feat(sketch): deploy-time flag to prune 13MB CJK canvas font - #9088
Conversation
UX Review (Fable 5) — ✅ PASSUX-level review of This PR changes only UX-Verdict: PASS Build-config-only change with no user-visible surface; the deployer-facing warning names what happened, the consequence, and the undo. [UX-REVIEWED] 7d882ae |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Both candidate findings are checked. Final review: Design-Verdict: PASS Measured, default-off, one-file filter over an existing enumeration — the proportionate shape for a deploy-time size lever, with dev/build parity handled. Suggestions
[DESIGN-REVIEWED] 7d882ae |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
bolichen97
left a comment
There was a problem hiding this comment.
Tech Lead review — approved.
Default is opt-in, verified. const pruneCjk = process.env.KIROCREW_PRUNE_SKETCH_CJK_FONT === '1' is a strict equality against '1', so unset / empty / 0 / true all evaluate false and isPruned() short-circuits before the family lookup. listFonts() therefore emits all 234 files exactly as today, and the dev-server route serves all of them. No existing build changes and no zh/ja/ko user loses the Xiaolai hand-drawn family unless a deployer deliberately sets the variable.
No i18n interaction. The pruned asset is an Excalidraw canvas font served from dist/vendor/excalidraw/fonts/**. It is not part of the dashboard's 12-locale i18n path — no locale catalog, no t() key, no dashboard typography stack references it — so dashboard CJK text rendering across all 12 languages is untouched in both flag states. The only consequence, and only under an explicit opt-in, is that sketch-canvas CJK text falls back to a system font: style loss, not legibility loss.
Failure mode is fail-safe. If a future @excalidraw/excalidraw upgrade renames the family, PRUNABLE_CJK_FONT_FAMILIES stops matching and the build ships every font — i.e. the flag goes inert in the direction that keeps CJK users whole, never in the direction that breaks them.
Scope and signals. One file, +40/-1, no new dependency and no new configuration layer — a filename predicate over the enumeration the plugin already walks, plus dev/build parity via the same isPruned(). All 5 AI review lanes clean on 7d882ae (GPT no findings, Opus no findings, UX PASS, Design PASS, First Principles success), PR Readiness success, no unresolved review threads, readiness: passed postdates the head commit.
Design Review's two suggestions are advisory and I am not blocking on them: deriving the warning's family/size from what listFonts() actually skipped instead of the hardcoded ~12.67 MB, and cataloging the flag in website/docs/extension-seams.md alongside the other build-time KIROCREW_* seams. The second is the one that matters for discoverability — worth a follow-up so the size-sensitive deployer this PR serves can find the lever. The runtime-notice product question stays deferred on #8091, correctly, since a default-off reversible build flag does not force that call.
What is the problem?
The sketch pad (#8041) self-hosts Excalidraw's canvas fonts so no CDN is contacted, emitting them at build time via
excalidrawFontsPlugin()inwebsite/vite.config.ts. Measured at main31cd87f2(package pin@excalidraw/excalidraw@0.18.1): the plugin emits 13,107,068 bytes across 234 files with no filter, of which the Xiaolai CJK handwriting family is 12,667,492 bytes across 209 files -- 96.6% of the payload. Every other family combined is 0.44 MB. A size-sensitive deployment has no way to opt out of the 12.67 MB CJK family.Why this matters to the user
Two different users are in tension. A zh/ja/ko user needs Xiaolai to sketch with hand-drawn CJK text; without it Excalidraw substitutes a system font, losing the hand-drawn style. A deployer building a size-sensitive image wants the 12.67 MB gone. Today only the first is served, and there is no lever for the second.
How the fix solves it
A single build-time env var,
KIROCREW_PRUNE_SKETCH_CJK_FONT, gates a filename filter over the families the plugin already enumerates -- no new configuration layer, no new dependency, one file.editionExtensionPlugin,KIROCREW_EDITION_DIR/KIROCREW_ALLOW_EDITION) handles branding and asset overlays but has no font hook, so a newKIROCREW_*var in the same family is the minimal addition.Still needing a human, and deliberately NOT decided here: what a pruned build should show CJK users at runtime (accept silent system fallback, or surface a notice). Defaulting the flag off decouples that product-policy call from this reversible build option. This re-triage was routed to needs-human by dwu96 (2026-09-03T20:37Z), whose read that "neither branch is derivable from the measurement" still holds -- this PR ships only the reversible, default-off half.
What tests we did
Verified by real
vite buildruns in the worktree against the installed0.18.1package (fullnpm run buildwas not runnable here: itstsc -bstep fails on missing@storybook/react-vitestubs in the borrowed node_modules, all errors undersrc/stories/, none invite.config.ts; ranvite builddirectly):KIROCREW_PRUNE_SKETCH_CJK_FONT=1: 25 font files emitted (234 minus Xiaolai's 209), 0 Xiaolai, 8 other families intact, and the build-time warning fired once.Other suggestions
The open runtime-signal question above is worth its own issue once a maintainer picks the acceptable-degradation bar; it is not blocked by this flag.
Pattern harvest
Rule candidate: when an asset-emitting build plugin enumerates a set (fonts, locales, vendored files), a deployment-time prune is a filename filter over what it already lists plus a loud build-time warning, defaulted off -- not a new config layer. This keeps the size lever reversible and the policy question (what the pruned build shows the user) separable from the code.
Refs #8091