Skip to content

Commit 19a09bc

Browse files
fix(badge): make text-width fallback less aggressive for consistent sizing
1 parent 7f2fc1a commit 19a09bc

File tree

1 file changed

+35
-5
lines changed

1 file changed

+35
-5
lines changed

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

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ const COLORS = {
4141
white: '#ffffff',
4242
}
4343

44-
const CHAR_WIDTH = 7
45-
const SHIELDS_CHAR_WIDTH = 6
46-
4744
const BADGE_PADDING_X = 8
4845
const MIN_BADGE_TEXT_WIDTH = 40
4946
const SHIELDS_LABEL_PADDING_X = 5
@@ -53,6 +50,39 @@ const SHIELDS_FONT_SHORTHAND = 'normal normal 400 11px Verdana, Geneva, DejaVu S
5350

5451
let cachedCanvasContext: SKRSContext2D | null | undefined
5552

53+
const NARROW_CHARS = new Set([' ', '!', '"', "'", '(', ')', '*', ',', '-', '.', ':', ';', '|'])
54+
const MEDIUM_CHARS = new Set(['#', '$', '+', '/', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '}', '~'])
55+
56+
function estimateTextWidth(text: string, fallbackFont: 'default' | 'shieldsio'): number {
57+
let totalWidth = 0
58+
59+
for (const character of text) {
60+
if (NARROW_CHARS.has(character)) {
61+
totalWidth += 3
62+
continue
63+
}
64+
65+
if (MEDIUM_CHARS.has(character)) {
66+
totalWidth += 5
67+
continue
68+
}
69+
70+
if (/\d/.test(character)) {
71+
totalWidth += 6
72+
continue
73+
}
74+
75+
if (/[A-Z]/.test(character)) {
76+
totalWidth += 7
77+
continue
78+
}
79+
80+
totalWidth += fallbackFont === 'shieldsio' ? 5.5 : 6
81+
}
82+
83+
return Math.max(1, Math.round(totalWidth))
84+
}
85+
5686
function getCanvasContext(): SKRSContext2D | null {
5787
if (cachedCanvasContext !== undefined) {
5888
return cachedCanvasContext
@@ -90,7 +120,7 @@ function measureDefaultTextWidth(text: string): number {
90120
return Math.max(MIN_BADGE_TEXT_WIDTH, measuredWidth + BADGE_PADDING_X * 2)
91121
}
92122

93-
return Math.max(MIN_BADGE_TEXT_WIDTH, Math.round(text.length * CHAR_WIDTH) + BADGE_PADDING_X * 2)
123+
return Math.max(MIN_BADGE_TEXT_WIDTH, estimateTextWidth(text, 'default') + BADGE_PADDING_X * 2)
94124
}
95125

96126
function escapeXML(str: string): string {
@@ -125,7 +155,7 @@ function measureShieldsTextLength(text: string): number {
125155
return Math.max(1, measuredWidth)
126156
}
127157

128-
return Math.max(1, Math.round(text.length * SHIELDS_CHAR_WIDTH))
158+
return estimateTextWidth(text, 'shieldsio')
129159
}
130160

131161
function renderDefaultBadgeSvg(params: {

0 commit comments

Comments
 (0)