Wire markdown-svg to Dataface font config and vendored Inter
Problem
Board content markdown is rendered with mdsvg (measure / render in dataface/core/render/faces.py, sizing in dataface/core/compile/sizing.py). That path does not pass a font_path into SVGRenderer, so precise layout uses FontMeasurer.system_default() (host-dependent). _get_compact_style() sets compact spacing and theme colors but does not apply get_config().style.font_family or typography (content_font_size, content_line_height), so SVG font-family on text stays on mdsvg defaults (system-ui stack) while charts/HTML/export were moved to vendored Inter (dataface/core/render/font_support.py, default_config.yml). Wrapping and spacing can disagree with the labeled font; CI/Linux may measure with a different face than production charts.
Context
- Completed work:
tasks/workstreams/graph-library/tasks/implement-inter-as-primary-sans-in-dataface-charts.md(PR #737) wired Inter for Vega, HTML wrappers, tables, export — not the mdsvg markdown pipeline. - Vendored font:
dataface/core/render/fonts/InterVariable.ttfviaget_inter_font_path()infont_support.py. - mdsvg supports
SVGRenderer(font_path=..., mono_font_path=...)andStyle.font_family/base_font_size/line_heightfor output attributes and measurement alignment (libs/markdown-svg/src/mdsvg/renderer.py). - Top-level
mdsvg.render()/measure()currently constructSVGRendererwithoutfont_path; Dataface may need thin wrappers or a small mdsvg API extension so embedders pass font paths without duplicating renderer construction.
Possible Solutions
- Recommended: Centralize “mdsvg style + font paths” in one helper used by
faces.py,sizing.py, and any othermdsvgcall sites; pass vendored Inter (and a monospace file if we vendor one) intoSVGRenderer, and mapget_config().style+typographyintomdsvg.Styleso measure/render/SVG attributes match. - Extend mdsvg public
render/measureto accept optionalfont_path/mono_font_pathand forward toSVGRenderer— cleaner for embedders, small library change. - Rely on
download_google_font("Inter")at runtime — rejected: duplicates vendoring, needs network/caching policy, inconsistent with offline/CI.
Plan
- Audit all
mdsvgimport sites indataface/and tests. - Add helper (e.g. under
compile/sizing.pyorrender/) that returns(mdsvg.Style, font_path, mono_font_path)from resolved config + theme. - Use
SVGRendererwith those paths for markdown measure/render, or extend mdsvg convenience functions; define behavior when fonttools or variable font metrics are unavailable. - Add regression test: markdown block width/height stable with vendored Inter on Linux (or golden dimensions for a fixed string).
- Run
just task validateafter edits to this file.
Implementation Progress
- 2026-03-24: Task marked
in_progress. Manager dispatched worker viascripts/task-system/dispatchon branchcodex/wire-markdown-svg-to-dataface-font-config-and-vendored-inter, worktree.worktrees/wire-markdown-svg-to-dataface-font-config-and-vendored-inter. Monitor:scripts/task-system/dispatch-watch "Wire markdown-svg to Dataface font config and vendored Inter"; log:.tasks/dispatch-wire-markdown-svg-to-dataface-font-config-and-vendored-inter.log. - 2026-03-24: Implementation complete. Changes:
1. mdsvg API extended: Added
font_pathandmono_font_pathoptional params torender(),measure(),render_content(),render_blocks()inlibs/markdown-svg/src/mdsvg/renderer.py. These forward toSVGRendererso embedders don't duplicate renderer construction. 2._get_compact_style()wired to config: Now readsfont_family,content_font_size,content_line_heightfromget_config().style/get_config().typographyinstead of relying on mdsvg defaults. 3. Shared font accessordataface/core/fonts.py:get_inter_font_path()moved here so compile layer can import without crossing into render layer. Re-exported fromfont_support.py. 4. All call sites wired inline:faces.py::_render_content_svg,svg_utils.py::render_title,sizing.py::get_markdown_content_height,sizing.py::get_title_height— all pass vendored Inter font_path via_get_compact_style()+get_inter_font_path(). 5. Monospace: No vendored mono font; mdsvg falls back tomono_char_width_ratioheuristic (0.6). System mono measurer was considered but not needed since code blocks are rare in dashboard content and the heuristic is adequate. 6. Tests: 11 tests intests/core/test_font_wiring.pycovering style config wiring (including dark theme colors), mdsvg API font_path forwarding, title font-family in SVG output, and layout dimension stability/regression with tight tolerance. 7. CI:just ci-lintpasses clean (black, ruff, mypy). All core tests pass; 2 pre-existing flaky script tests unrelated to this change. - 2026-03-24: Review pass 1 — addressed all issues:
- Deleted
font_wiring.py(trivial wrapper that didn't earn its place per anti-slop) - Moved
get_inter_font_path()todataface/core/fonts.pyto fix compile→render layer violation - Inlined font config at all call sites (2 imports + 2 lines each)
- Tightened regression guard from 15-40px to 19-26px range
- Added dark theme color assertion test
QA Exploration
N/A — no browser UX change. This is a backend font-measurement wiring change; SVG output uses the same Inter font family that was already configured in default_config.yml. Visual output is consistent (font-family attribute now matches the measurement font).
- [x] QA exploration completed (or N/A for non-UI tasks)
Review Feedback
- Review pass 1: Deleted font_wiring.py wrapper, fixed compile→render import, tightened test assertions, added dark theme test. All issues resolved.
- 2026-03-24:
just reviewrerun after follow-up fixes returnedAPPROVED; no remaining blocking findings. - [x] Review cleared