Skip to content

feat(mcp): text-measurement primitive + 3 layout-prediction tools#22

Merged
andresdefi merged 2 commits into
mainfrom
feat/mcp-tier2-measurement
May 22, 2026
Merged

feat(mcp): text-measurement primitive + 3 layout-prediction tools#22
andresdefi merged 2 commits into
mainfrom
feat/mcp-tier2-measurement

Conversation

@andresdefi
Copy link
Copy Markdown
Owner

Summary

Adds a fontkit-backed text measurement utility plus three tools that use it to predict layout issues before render-time. All three eliminate the iterate-render-check loop the agent used to do manually.

Measurement primitive (packages/mcp/src/measure.ts)

  • measureText(opts) reads the bundled woff2 directly via fontkit, sums glyph advances, handles word-wrap to a max width. Returns { singleLineWidth, wrappedWidth, lineCount, height }. ~10ms per call. Caches parsed fonts; falls back to a 0.55-em heuristic if a font file isn't found.
  • estimateDeviceBounds(screen) returns canvas-% bounds of the primary device frame from deviceTop / deviceScale / deviceOffsetX using a 2:1 aspect approximation.

New tools

auto_fit_headline

Binary-searches the largest headline font size that fits in maxLines (default 2). Returns the suggested size + measurement metrics. Pass apply: true to write headlineSize via patch_screen in the same call.

check_text_overlap

Computes headline bounds from the current size + position, intersects with estimated device bounds, returns overlap status + a recommendation if they collide. Use as a pre-render sanity check.

predict_locale_overflow

Given a translated headline (e.g. for a non-English locale), predicts whether it overflows the text area at the current font size. If it doesn't fit, runs the auto-fit binary search internally and suggests a smaller size. Useful before committing translations — German / Russian / Finnish typically expand by 30-50%.

Live smoke test (against my own project)

auto_fit_headline screen 0 → suggested: 292px, lines=2, width=1095px
check_text_overlap screen 0 → overlaps: false
predict_locale_overflow screen 0 (long German) → fits: false, lines: 6 at 170px,
  suggested: 62px, "Translation overflows at 170px. Try size 62px to fit in 2 lines."

Tool count: 65 → 68

Test plan

  • pnpm typecheck clean
  • pnpm test — 535 / 535 passing
  • Live smoke test against running project produces sensible measurements
  • All 3 tools registered in tools/list

🤖 Generated with Claude Code

Adds a fontkit-backed text measurement utility and three tools built
on top of it. All three eliminate the iterate-render-check loop the
agent used to do manually.

New module `packages/mcp/src/measure.ts`:
- `measureText(opts)` — reads the bundled woff2 directly, sums glyph
  advances via fontkit, handles word-wrap to a max width. Returns
  singleLineWidth, wrappedWidth, lineCount, height. ~10ms per call.
  Caches parsed fonts; falls back to a 0.55-em heuristic if the font
  file isn't found.
- `estimateDeviceBounds(screen)` — canvas-% bounds of the primary
  device frame from deviceTop / deviceScale / deviceOffsetX, using a
  2:1 aspect approximation.

New tools:

- `auto_fit_headline(slug, index, text?, maxLines?, apply?)` — binary-
  searches the largest headline size that fits in `maxLines` (default
  2). Returns suggestedSize + measurement metrics. Pass apply:true to
  write headlineSize via patch_screen.

- `check_text_overlap(slug, index)` — computes headline bounds from
  current size + position, intersects with estimated device bounds,
  returns overlap status + a recommendation if they collide. Use as a
  pre-render sanity check.

- `predict_locale_overflow(slug, index, translatedText, maxLines?)` —
  given a translated headline, predicts whether it overflows the text
  area at the current font size. If it doesn't fit, runs the auto-fit
  binary search internally and suggests a smaller size. Useful before
  committing translations to a locale — German/Russian/Finnish often
  expand the headline by 30-50%.

Total tool count: 65 → 68.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread packages/mcp/src/tools/screen.ts Fixed
Switches `stripTagsFromHeadline` from an iterate-until-stable regex
to a character-by-character state machine. Same behavior (handles
nested constructs like `<<p>p>` safely), but CodeQL recognises the
state machine as a safe parser instead of flagging it as
incomplete-multi-character-sanitization on every PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@andresdefi andresdefi merged commit 73153f1 into main May 22, 2026
4 checks passed
@andresdefi andresdefi deleted the feat/mcp-tier2-measurement branch May 22, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants