Skip to content

Commit a52779d

Browse files
committed
fix: improve badge text measurement accuracy
This PR improves badge appearance by using a more accurate method to measure text width. Instead of relying on a predefined character width, it utilizes the Canvas API to measure the actual width of the text, resulting in better alignment and spacing for badges with varying character widths. Speaking in code, this is how the old and new methods compare: { label: 'iiiiiiiiii', leftWidthOld: 86, leftWidth: 41 } { label: 'wwwwwwwwww', leftWidthOld: 86, leftWidth: 97 }
1 parent 6755779 commit a52779d

3 files changed

Lines changed: 167 additions & 11 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"@iconify-json/vscode-icons": "1.2.40",
6161
"@intlify/shared": "11.2.8",
6262
"@lunariajs/core": "https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@f07e1a3",
63+
"@napi-rs/canvas": "0.1.92",
6364
"@nuxt/a11y": "1.0.0-alpha.1",
6465
"@nuxt/fonts": "0.13.0",
6566
"@nuxt/scripts": "0.13.2",

pnpm-lock.yaml

Lines changed: 125 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/api/registry/badge/[type]/[...pkg].get.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as v from 'valibot'
2+
import { createCanvas, type SKRSContext2D } from '@napi-rs/canvas'
23
import { hash } from 'ohash'
34
import { createError, getRouterParam, getQuery, setHeader } from 'h3'
45
import { PackageRouteParamsSchema } from '#shared/schemas/package'
@@ -34,15 +35,47 @@ const COLORS = {
3435
white: '#ffffff',
3536
}
3637

37-
const DEFAULT_CHAR_WIDTH = 7
38-
const CHARS_WIDTH = {
39-
engines: 5.5,
38+
const CHAR_WIDTH = 7
39+
40+
const BADGE_PADDING_X = 8
41+
const MIN_BADGE_TEXT_WIDTH = 40
42+
43+
const BADGE_FONT_SHORTHAND = 'normal normal 400 11px Geist, system-ui, -apple-system, sans-serif'
44+
45+
let cachedCanvasContext: SKRSContext2D | null | undefined
46+
47+
function getCanvasContext(): SKRSContext2D | null {
48+
if (cachedCanvasContext !== undefined) {
49+
return cachedCanvasContext
50+
}
51+
52+
try {
53+
cachedCanvasContext = createCanvas(1, 1).getContext('2d')
54+
} catch {
55+
cachedCanvasContext = null
56+
}
57+
58+
return cachedCanvasContext
4059
}
4160

42-
function measureTextWidth(text: string, charWidth?: number): number {
43-
charWidth ??= DEFAULT_CHAR_WIDTH
44-
const paddingX = 8
45-
return Math.max(40, Math.round(text.length * charWidth) + paddingX * 2)
61+
function fallbackMeasureTextWidth(text: string): number {
62+
return Math.max(MIN_BADGE_TEXT_WIDTH, Math.round(text.length * CHAR_WIDTH) + BADGE_PADDING_X * 2)
63+
}
64+
65+
function measureTextWidth(text: string): number {
66+
const context = getCanvasContext()
67+
68+
if (context) {
69+
context.font = BADGE_FONT_SHORTHAND
70+
71+
const measuredWidth = context.measureText(text).width
72+
73+
if (!Number.isNaN(measuredWidth)) {
74+
return Math.max(MIN_BADGE_TEXT_WIDTH, Math.ceil(measuredWidth) + BADGE_PADDING_X * 2)
75+
}
76+
}
77+
78+
return fallbackMeasureTextWidth(text)
4679
}
4780

4881
function formatBytes(bytes: number): string {
@@ -296,10 +329,7 @@ export default defineCachedEventHandler(
296329
const finalLabelColor = rawLabelColor?.startsWith('#') ? rawLabelColor : `#${rawLabelColor}`
297330

298331
const leftWidth = finalLabel.trim().length === 0 ? 0 : measureTextWidth(finalLabel)
299-
const rightWidth = measureTextWidth(
300-
finalValue,
301-
CHARS_WIDTH[strategyKey as keyof typeof CHARS_WIDTH],
302-
)
332+
const rightWidth = measureTextWidth(finalValue)
303333
const totalWidth = leftWidth + rightWidth
304334
const height = 20
305335

0 commit comments

Comments
 (0)