Skip to content

Commit 1fd2d53

Browse files
MathurAditya724autofix-ci[bot]ghostdevv
authored
fix(ui): reduce badge width overestimation in fallback text measurement (#2199)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Willow (GHOST) <git@willow.sh>
1 parent 14538da commit 1fd2d53

File tree

1 file changed

+79
-7
lines changed

1 file changed

+79
-7
lines changed

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

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,87 @@ const COLORS = {
4040
white: '#ffffff',
4141
}
4242

43-
const CHAR_WIDTH = 7
44-
const SHIELDS_CHAR_WIDTH = 6
45-
4643
const BADGE_PADDING_X = 8
4744
const MIN_BADGE_TEXT_WIDTH = 40
45+
const FALLBACK_VALUE_EXTRA_PADDING_X = 8
4846
const SHIELDS_LABEL_PADDING_X = 5
4947

5048
const BADGE_FONT_SHORTHAND = 'normal normal 400 11px Geist, system-ui, -apple-system, sans-serif'
5149
const SHIELDS_FONT_SHORTHAND = 'normal normal 400 11px Verdana, Geneva, DejaVu Sans, sans-serif'
5250

5351
let cachedCanvasContext: SKRSContext2D | null | undefined
5452

53+
const NARROW_CHARS = new Set([' ', '!', '"', "'", '(', ')', '*', ',', '-', '.', ':', ';', '|'])
54+
const MEDIUM_CHARS = new Set([
55+
'#',
56+
'$',
57+
'+',
58+
'/',
59+
'<',
60+
'=',
61+
'>',
62+
'?',
63+
'@',
64+
'[',
65+
'\\',
66+
']',
67+
'^',
68+
'_',
69+
'`',
70+
'{',
71+
'}',
72+
'~',
73+
])
74+
75+
const FALLBACK_WIDTHS = {
76+
default: {
77+
narrow: 3,
78+
medium: 5,
79+
digit: 6,
80+
uppercase: 7,
81+
other: 6,
82+
},
83+
shieldsio: {
84+
narrow: 3,
85+
medium: 5,
86+
digit: 6,
87+
uppercase: 7,
88+
other: 5.5,
89+
},
90+
} as const
91+
92+
function estimateTextWidth(text: string, fallbackFont: 'default' | 'shieldsio'): number {
93+
// Heuristic coefficients tuned to keep fallback rendering close to canvas metrics.
94+
const widths = FALLBACK_WIDTHS[fallbackFont]
95+
let totalWidth = 0
96+
97+
for (const character of text) {
98+
if (NARROW_CHARS.has(character)) {
99+
totalWidth += widths.narrow
100+
continue
101+
}
102+
103+
if (MEDIUM_CHARS.has(character)) {
104+
totalWidth += widths.medium
105+
continue
106+
}
107+
108+
if (/\d/.test(character)) {
109+
totalWidth += widths.digit
110+
continue
111+
}
112+
113+
if (/[A-Z]/.test(character)) {
114+
totalWidth += widths.uppercase
115+
continue
116+
}
117+
118+
totalWidth += widths.other
119+
}
120+
121+
return Math.max(1, Math.round(totalWidth))
122+
}
123+
55124
function getCanvasContext(): SKRSContext2D | null {
56125
if (cachedCanvasContext !== undefined) {
57126
return cachedCanvasContext
@@ -82,14 +151,17 @@ function measureTextWidth(text: string, font: string): number | null {
82151
return null
83152
}
84153

85-
function measureDefaultTextWidth(text: string): number {
154+
function measureDefaultTextWidth(text: string, fallbackExtraPadding = 0): number {
86155
const measuredWidth = measureTextWidth(text, BADGE_FONT_SHORTHAND)
87156

88157
if (measuredWidth !== null) {
89158
return Math.max(MIN_BADGE_TEXT_WIDTH, measuredWidth + BADGE_PADDING_X * 2)
90159
}
91160

92-
return Math.max(MIN_BADGE_TEXT_WIDTH, Math.round(text.length * CHAR_WIDTH) + BADGE_PADDING_X * 2)
161+
return Math.max(
162+
MIN_BADGE_TEXT_WIDTH,
163+
estimateTextWidth(text, 'default') + BADGE_PADDING_X * 2 + fallbackExtraPadding,
164+
)
93165
}
94166

95167
function escapeXML(str: string): string {
@@ -124,7 +196,7 @@ function measureShieldsTextLength(text: string): number {
124196
return Math.max(1, measuredWidth)
125197
}
126198

127-
return Math.max(1, Math.round(text.length * SHIELDS_CHAR_WIDTH))
199+
return estimateTextWidth(text, 'shieldsio')
128200
}
129201

130202
function renderDefaultBadgeSvg(params: {
@@ -138,7 +210,7 @@ function renderDefaultBadgeSvg(params: {
138210
const { finalColor, finalLabel, finalLabelColor, finalValue, labelTextColor, valueTextColor } =
139211
params
140212
const leftWidth = finalLabel.trim().length === 0 ? 0 : measureDefaultTextWidth(finalLabel)
141-
const rightWidth = measureDefaultTextWidth(finalValue)
213+
const rightWidth = measureDefaultTextWidth(finalValue, FALLBACK_VALUE_EXTRA_PADDING_X)
142214
const totalWidth = leftWidth + rightWidth
143215
const height = 20
144216
const escapedLabel = escapeXML(finalLabel)

0 commit comments

Comments
 (0)