|
1 | 1 | import * as v from 'valibot' |
| 2 | +import { createCanvas, type SKRSContext2D } from '@napi-rs/canvas' |
2 | 3 | import { hash } from 'ohash' |
3 | 4 | import { createError, getRouterParam, getQuery, setHeader } from 'h3' |
4 | 5 | import { PackageRouteParamsSchema } from '#shared/schemas/package' |
@@ -34,15 +35,47 @@ const COLORS = { |
34 | 35 | white: '#ffffff', |
35 | 36 | } |
36 | 37 |
|
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 |
40 | 59 | } |
41 | 60 |
|
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) |
46 | 79 | } |
47 | 80 |
|
48 | 81 | function formatBytes(bytes: number): string { |
@@ -300,10 +333,7 @@ export default defineCachedEventHandler( |
300 | 333 | const finalLabelColor = rawLabelColor?.startsWith('#') ? rawLabelColor : `#${rawLabelColor}` |
301 | 334 |
|
302 | 335 | const leftWidth = finalLabel.trim().length === 0 ? 0 : measureTextWidth(finalLabel) |
303 | | - const rightWidth = measureTextWidth( |
304 | | - finalValue, |
305 | | - CHARS_WIDTH[strategyKey as keyof typeof CHARS_WIDTH], |
306 | | - ) |
| 336 | + const rightWidth = measureTextWidth(finalValue) |
307 | 337 | const totalWidth = leftWidth + rightWidth |
308 | 338 | const height = 20 |
309 | 339 |
|
|
0 commit comments