fix(ui): register Geist font with canvas to fix badge width measurement#2196
fix(ui): register Geist font with canvas to fix badge width measurement#2196howwohmm wants to merge 1 commit intonpmx-dev:mainfrom
Conversation
The Geist font was not registered with @napi-rs/canvas, causing text measurement to use a wider system fallback font in production. This produced badges with excessive whitespace, especially for longer text. Register the Geist font from server assets so canvas measures text with the same font the SVG specifies for rendering. Closes npmx-dev#2187 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
📝 WalkthroughWalkthroughThe pull request adds server-side font asset configuration and Geist font registration for badge generation. The Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
server/api/registry/badge/[type]/[...pkg].get.ts (1)
55-73: Minor race condition on concurrent requests.Multiple simultaneous requests before registration completes will all enter the
tryblock and attempt registration. Whilst likely benign (redundant work rather than corruption), a Promise-based singleton pattern avoids repeated font loads.🔧 Suggested fix using Promise singleton
-let fontRegistered = false +let fontRegistrationPromise: Promise<void> | null = null -async function registerGeistFont(): Promise<void> { - if (fontRegistered) return +function registerGeistFont(): Promise<void> { + if (fontRegistrationPromise) return fontRegistrationPromise - try { - const fontStorage = useStorage('assets:fonts') - const fontData = await fontStorage.getItemRaw('Geist-Regular.ttf') - - if (fontData) { - const buffer = fontData instanceof Buffer ? fontData : Buffer.from(fontData as ArrayBuffer) - GlobalFonts.register(buffer, 'Geist') + fontRegistrationPromise = (async () => { + try { + const fontStorage = useStorage('assets:fonts') + const fontData = await fontStorage.getItemRaw('Geist-Regular.ttf') + + if (fontData) { + const buffer = fontData instanceof Buffer ? fontData : Buffer.from(fontData as ArrayBuffer) + GlobalFonts.register(buffer, 'Geist') + } + } catch { + // font registration is best-effort; fallback measurement will be used } - } catch { - // font registration is best-effort; fallback measurement will be used - } + })() - fontRegistered = true + return fontRegistrationPromise }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9c55b5f7-3bc7-4932-908c-fe4e89a53437
📒 Files selected for processing (2)
nuxt.config.tsserver/api/registry/badge/[type]/[...pkg].get.ts
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Summary
@napi-rs/canvasGlobalFontsso server-side text measurement uses the same font the SVG specifies for browser renderingserverAssetsconfig in Nitro to exposepublic/fonts/to server-side code viauseStorageRoot cause
The badge SVG specifies
font-family="Geist, system-ui, ..."but@napi-rs/canvaswas never given the Geist font file. In production, canvas measured text with a wider system fallback font, producing oversizedwidthattributes on the SVG. The browser then rendered the text narrower (using Geist or system-ui), leaving visible whitespace gaps — especially noticeable for longer badge values.Locally, the Geist font may be installed system-wide, which is why
pnpm devproduced correct-looking badges.Closes #2187
Test plan
/api/registry/badge/engines/vitestno longer has excessive whitespacestyle=shieldsiobadges are unaffected🤖 Generated with Claude Code