@zkmake/three-meter 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -247,8 +247,10 @@ var HudSettings = class {
|
|
|
247
247
|
/**
|
|
248
248
|
* Dock a fixed-position panel to a screen edge. Dragging the handle snaps it
|
|
249
249
|
* to the nearest edge on drop; the edge and the offset along it persist under
|
|
250
|
-
* `<storageKey>:placement`.
|
|
251
|
-
*
|
|
250
|
+
* `<storageKey>:placement`. On a resize (the window's or the panel's own) it
|
|
251
|
+
* keeps its edge and slides along it to stay on screen; only a drop changes the
|
|
252
|
+
* edge. (Re-docking an overflowing box to its nearest edge flipped a panel that
|
|
253
|
+
* grew in a corner onto the side edge.)
|
|
252
254
|
*/
|
|
253
255
|
const INSET = 10;
|
|
254
256
|
/** Room for the stacked control discs on the inward side of the panel. */
|
|
@@ -279,39 +281,6 @@ const clampAlong = (edge, along, view, panel) => {
|
|
|
279
281
|
if (edge === "left" || edge === "right") return clamp(along, INSET, Math.max(INSET, view.height - panel.height - INSET));
|
|
280
282
|
return clamp(along, INSET, Math.max(INSET, view.width - panel.width - INSET - SIDE_CLEARANCE));
|
|
281
283
|
};
|
|
282
|
-
const ghostBox = (placement, panel, view) => {
|
|
283
|
-
switch (placement.edge) {
|
|
284
|
-
case "left": return {
|
|
285
|
-
height: panel.height,
|
|
286
|
-
left: INSET,
|
|
287
|
-
top: placement.along,
|
|
288
|
-
width: panel.width
|
|
289
|
-
};
|
|
290
|
-
case "right": return {
|
|
291
|
-
height: panel.height,
|
|
292
|
-
left: view.width - INSET - panel.width,
|
|
293
|
-
top: placement.along,
|
|
294
|
-
width: panel.width
|
|
295
|
-
};
|
|
296
|
-
case "top": return {
|
|
297
|
-
height: panel.height,
|
|
298
|
-
left: placement.along,
|
|
299
|
-
top: INSET,
|
|
300
|
-
width: panel.width
|
|
301
|
-
};
|
|
302
|
-
case "bottom": return {
|
|
303
|
-
height: panel.height,
|
|
304
|
-
left: placement.along,
|
|
305
|
-
top: view.height - INSET - panel.height,
|
|
306
|
-
width: panel.width
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
const centerOutside = (box, view) => {
|
|
311
|
-
const x = box.left + box.width / 2;
|
|
312
|
-
const y = box.top + box.height / 2;
|
|
313
|
-
return x < 0 || y < 0 || x > view.width || y > view.height;
|
|
314
|
-
};
|
|
315
284
|
const snapBoxToEdge = (box, view) => {
|
|
316
285
|
const distances = {
|
|
317
286
|
bottom: view.height - (box.top + box.height),
|
|
@@ -366,8 +335,6 @@ const writePlacement = (key, placement) => {
|
|
|
366
335
|
};
|
|
367
336
|
const resolvePlacement = (stored, preset, panel, view) => {
|
|
368
337
|
if (!stored) return initialPlacement(preset, view, panel);
|
|
369
|
-
const ghost = ghostBox(stored, panel, view);
|
|
370
|
-
if (centerOutside(ghost, view)) return snapBoxToEdge(ghost, view);
|
|
371
338
|
return {
|
|
372
339
|
along: Math.round(clampAlong(stored.edge, stored.along, view, panel)),
|
|
373
340
|
edge: stored.edge
|
|
@@ -1429,7 +1396,7 @@ const mountDevPanel = (options) => {
|
|
|
1429
1396
|
//#endregion
|
|
1430
1397
|
//#region package.json
|
|
1431
1398
|
var name = "@zkmake/three-meter";
|
|
1432
|
-
var version = "0.11.
|
|
1399
|
+
var version = "0.11.2";
|
|
1433
1400
|
//#endregion
|
|
1434
1401
|
//#region src/ui/clipboard.ts
|
|
1435
1402
|
/**
|
|
@@ -2591,4 +2558,4 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2591
2558
|
//#endregion
|
|
2592
2559
|
export { PERF_HUD_STYLES as a, DEFAULT_STORAGE_KEY as c, THEME_MODES as d, isThemeMode as f, mountDevPanel as i, HudSettings as l, PerformanceView as n, injectStyles as o, resolveBudgets as p, formatReport as r, dockPanel as s, mountPerfHud as t, HudTheme as u };
|
|
2593
2560
|
|
|
2594
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
2561
|
+
//# sourceMappingURL=mount-perf-hud-BB8btwk9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-perf-hud-BB8btwk9.js","names":[],"sources":["../src/ui/budgets.ts","../src/ui/theme.ts","../src/ui/hud-settings.ts","../src/ui/dock-panel.ts","../src/ui/icons.ts","../src/ui/styles.ts","../src/ui/dev-panel.ts","../package.json","../src/ui/clipboard.ts","../src/ui/format.ts","../src/ui/hider.ts","../src/ui/metric-help.ts","../src/ui/report.ts","../src/ui/sparkline.ts","../src/ui/performance-view.ts","../src/ui/mount-perf-hud.ts"],"sourcesContent":["/**\n * Budgets: the value each metric should stay within. A reading past its\n * budget turns amber in the card, and the FPS / CPU / GPU graphs draw the\n * line once the series reaches it. `fps`, `low` and `headroom` are floors\n * (worse below); everything else is a ceiling (worse above).\n *\n * Timing budgets default from `targetFps`: FPS at least 95% of it, 1% low at\n * least half, CPU and GPU within one frame, p99 within one and a half,\n * headroom at least zero. Without a `targetFps`, the detected refresh rate is\n * used, then 60 until one is detected.\n * Counts have no default, since what's reasonable depends on the scene. Pass\n * `null` for a key to drop its default, or `false` for no budgets at all.\n */\ntype BudgetKey =\n | \"calls\"\n | \"cpu\"\n | \"fps\"\n | \"geometries\"\n | \"gpu\"\n | \"headroom\"\n | \"hitches\"\n | \"lines\"\n | \"low\"\n | \"p99\"\n | \"passes\"\n | \"points\"\n | \"refresh\"\n | \"shaders\"\n | \"textures\"\n | \"triangles\";\n\ntype Budgets = Partial<Record<BudgetKey, number | null>> & {\n /** The frame rate the defaults are derived from. Default: the detected refresh rate, else 60. */\n targetFps?: number;\n};\n\ntype ResolvedBudgets = {\n /** Frame budget in ms, `1000 / targetFps`. */\n frameMs: number;\n /** The rate the timing budgets came from: `targetFps`, the detected refresh, or 60. */\n targetFps: number;\n limits: Partial<Record<BudgetKey, number>>;\n};\n\nconst DEFAULT_TARGET_FPS = 60;\nconst FLOORS = new Set<BudgetKey>([\"fps\", \"headroom\", \"low\"]);\n\n/** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */\nconst resolveBudgets = (\n budgets: Budgets | false | undefined,\n detectedHz: number | null = null,\n): ResolvedBudgets => {\n const targetFps = (budgets && budgets.targetFps) || detectedHz || DEFAULT_TARGET_FPS;\n const frameMs = 1000 / targetFps;\n\n if (budgets === false) {\n return { frameMs, limits: {}, targetFps };\n }\n\n const merged: Budgets = {\n cpu: frameMs,\n fps: targetFps * 0.95,\n gpu: frameMs,\n headroom: 0,\n low: targetFps / 2,\n p99: frameMs * 1.5,\n ...budgets,\n };\n const limits: Partial<Record<BudgetKey, number>> = {};\n\n for (const [key, limit] of Object.entries(merged)) {\n if (key !== \"targetFps\" && typeof limit === \"number\" && Number.isFinite(limit)) {\n limits[key as BudgetKey] = limit;\n }\n }\n\n return { frameMs, limits, targetFps };\n};\n\nconst isFloor = (key: BudgetKey) => FLOORS.has(key);\n\n/** `null` means the value isn't known yet (no frames, no GPU timer), which is never over. */\nconst isOverBudget = (resolved: ResolvedBudgets, key: BudgetKey, value: number | null) => {\n const limit = resolved.limits[key];\n\n if (limit === undefined || value === null) {\n return false;\n }\n\n return isFloor(key) ? value < limit : value > limit;\n};\n\nexport { isFloor, isOverBudget, resolveBudgets };\nexport type { BudgetKey, Budgets, ResolvedBudgets };\n","/**\n * Dark, light, or follow the OS. One instance per HUD: the floating host and\n * the card inside it both paint `resolved` onto `data-theme`, and the\n * stylesheet picks the palette from that attribute.\n *\n * Two layers. `mode` is what the consumer asked for (the `theme` option,\n * `setTheme`). `override` is what the person using the HUD picked in the\n * panel, kept in `HudSettings`; it wins while set. `effective` is whichever\n * applies, and `system` there tracks `prefers-color-scheme` for as long as\n * the instance lives.\n */\ntype ThemeMode = \"dark\" | \"light\" | \"system\";\ntype ResolvedTheme = Exclude<ThemeMode, \"system\">;\n\nconst THEME_MODES: readonly ThemeMode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_SCHEME_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isThemeMode = (value: unknown): value is ThemeMode =>\n typeof value === \"string\" && (THEME_MODES as readonly string[]).includes(value);\n\nclass HudTheme {\n private current: ThemeMode;\n private userPick: ThemeMode | null = null;\n private readonly listeners = new Set<() => void>();\n private media: MediaQueryList | null = null;\n private readonly onSchemeChange = () => this.notify();\n\n constructor(mode: ThemeMode = \"system\") {\n this.current = mode;\n this.watch();\n }\n\n /** What the consumer asked for. */\n get mode(): ThemeMode {\n return this.current;\n }\n\n /** What the person using the HUD picked in the panel, or `null` for no pick. */\n get override(): ThemeMode | null {\n return this.userPick;\n }\n\n /** The layer that applies: the user's pick if there is one, else the consumer's mode. */\n get effective(): ThemeMode {\n return this.userPick ?? this.current;\n }\n\n /**\n * `dark` or `light`, with `system` resolved against the OS preference.\n * Where `matchMedia` is missing, `system` falls back to `dark`, the HUD's\n * original palette.\n */\n get resolved(): ResolvedTheme {\n const effective = this.effective;\n\n if (effective !== \"system\") {\n return effective;\n }\n\n if (!this.media) {\n return \"dark\";\n }\n\n return this.media.matches ? \"dark\" : \"light\";\n }\n\n setMode(mode: ThemeMode) {\n if (mode === this.current) {\n return;\n }\n\n this.current = mode;\n this.watch();\n this.notify();\n }\n\n setOverride(mode: ThemeMode | null) {\n if (mode === this.userPick) {\n return;\n }\n\n this.userPick = mode;\n this.watch();\n this.notify();\n }\n\n /** Fires on `setMode`, `setOverride` and, in `system`, whenever the OS preference flips. */\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Stop tracking the OS preference and drop every subscriber. */\n dispose() {\n this.unwatch();\n this.listeners.clear();\n }\n\n private watch() {\n if (this.effective !== \"system\") {\n this.unwatch();\n\n return;\n }\n\n if (this.media || typeof matchMedia !== \"function\") {\n return;\n }\n\n this.media = matchMedia(DARK_SCHEME_QUERY);\n this.media.addEventListener(\"change\", this.onSchemeChange);\n }\n\n private unwatch() {\n this.media?.removeEventListener(\"change\", this.onSchemeChange);\n this.media = null;\n }\n\n private notify() {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\n/** Paint the resolved theme onto an element for the stylesheet to key off. */\nconst applyTheme = (element: HTMLElement, theme: HudTheme) => {\n element.dataset.theme = theme.resolved;\n};\n\nexport { applyTheme, HudTheme, isThemeMode, THEME_MODES };\nexport type { ResolvedTheme, ThemeMode };\n","/**\n * Which metrics the compact HUD shows, whether it shows the environment\n * footer, the dim-on-leave toggle, and the theme\n * the person using the HUD picked in the panel (`null` until they pick one,\n * so the consumer's `theme` option applies). One instance per HUD; the\n * full-mode controls and the compact card stay in sync through it. Persists\n * to `localStorage` under `storageKey` (pass `null` to keep it in memory only).\n */\nimport type { TimingMetric } from \"../core/types.ts\";\nimport { isThemeMode, type ThemeMode } from \"./theme.ts\";\n\ntype HudSelection = {\n dim: boolean;\n graphs: ReadonlySet<TimingMetric>;\n /** Version, three revision, backend and GPU footer in the compact HUD. */\n info: boolean;\n numbers: ReadonlySet<string>;\n theme: ThemeMode | null;\n};\n\ntype HudDefaults = {\n dim?: boolean;\n graphs?: readonly TimingMetric[];\n info?: boolean;\n numbers?: readonly string[];\n};\n\ntype HudSettingsOptions = {\n /** localStorage key. Default `three-meter`; `null` disables persistence. */\n storageKey?: string | null;\n /** What a first visit shows. */\n defaults?: HudDefaults;\n};\n\ntype MutableSelection = {\n dim: boolean;\n graphs: Set<TimingMetric>;\n info: boolean;\n numbers: Set<string>;\n theme: ThemeMode | null;\n};\n\nconst DEFAULT_STORAGE_KEY = \"three-meter\";\nconst DEFAULT_DIM = false;\nconst DEFAULT_INFO = false;\nconst DEFAULT_NUMBERS: readonly string[] = [\"fps\", \"calls\", \"cpu\", \"gpu\"];\nconst DEFAULT_GRAPHS: readonly TimingMetric[] = [];\nconst TIMING_METRICS = new Set<TimingMetric>([\"fps\", \"cpu\", \"gpu\"]);\n\nconst isTimingMetric = (value: string): value is TimingMetric =>\n TIMING_METRICS.has(value as TimingMetric);\n\nclass HudSettings {\n private readonly storageKey: string | null;\n private readonly defaults: Required<HudDefaults>;\n private readonly listeners = new Set<() => void>();\n private readonly state: MutableSelection;\n\n constructor(options: HudSettingsOptions = {}) {\n this.storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n this.defaults = {\n dim: options.defaults?.dim ?? DEFAULT_DIM,\n graphs: options.defaults?.graphs ?? DEFAULT_GRAPHS,\n info: options.defaults?.info ?? DEFAULT_INFO,\n numbers: options.defaults?.numbers ?? DEFAULT_NUMBERS,\n };\n this.state = this.load();\n }\n\n get selection(): HudSelection {\n return this.state;\n }\n\n get dim() {\n return this.state.dim;\n }\n\n setDim(enabled: boolean) {\n this.state.dim = enabled;\n this.persistAndNotify();\n }\n\n get info() {\n return this.state.info;\n }\n\n setInfo(enabled: boolean) {\n this.state.info = enabled;\n this.persistAndNotify();\n }\n\n /** The user's pick in the panel; `null` means none, so the consumer's option applies. */\n get theme() {\n return this.state.theme;\n }\n\n setTheme(mode: ThemeMode | null) {\n this.state.theme = mode;\n this.persistAndNotify();\n }\n\n isNumberEnabled(key: string) {\n return this.state.numbers.has(key);\n }\n\n isGraphEnabled(metric: TimingMetric) {\n return this.state.graphs.has(metric);\n }\n\n setNumberEnabled(key: string, enabled: boolean) {\n if (enabled) {\n this.state.numbers.add(key);\n } else {\n this.state.numbers.delete(key);\n }\n\n this.persistAndNotify();\n }\n\n setGraphEnabled(metric: TimingMetric, enabled: boolean) {\n if (enabled) {\n this.state.graphs.add(metric);\n } else {\n this.state.graphs.delete(metric);\n }\n\n this.persistAndNotify();\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private load(): MutableSelection {\n const fallback = (): MutableSelection => ({\n dim: this.defaults.dim,\n graphs: new Set(this.defaults.graphs),\n info: this.defaults.info,\n numbers: new Set(this.defaults.numbers),\n theme: null,\n });\n\n if (this.storageKey === null) {\n return fallback();\n }\n\n try {\n const raw = localStorage.getItem(this.storageKey);\n\n if (!raw) {\n return fallback();\n }\n\n const parsed = JSON.parse(raw) as {\n dim?: boolean;\n graphs?: string[];\n info?: boolean;\n numbers?: string[];\n theme?: unknown;\n };\n\n return {\n dim: parsed.dim ?? this.defaults.dim,\n graphs: new Set((parsed.graphs ?? this.defaults.graphs).filter(isTimingMetric)),\n info: parsed.info ?? this.defaults.info,\n numbers: new Set(parsed.numbers ?? this.defaults.numbers),\n theme: isThemeMode(parsed.theme) ? parsed.theme : null,\n };\n } catch {\n // Malformed or unavailable storage → defaults.\n return fallback();\n }\n }\n\n private persistAndNotify() {\n if (this.storageKey !== null) {\n try {\n localStorage.setItem(\n this.storageKey,\n JSON.stringify({\n dim: this.state.dim,\n graphs: [...this.state.graphs],\n info: this.state.info,\n numbers: [...this.state.numbers],\n // Omitted while null: JSON.stringify drops undefined.\n theme: this.state.theme ?? undefined,\n }),\n );\n } catch {\n // In-memory selection still drives the live views.\n }\n }\n\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\nexport { DEFAULT_STORAGE_KEY, HudSettings };\nexport type { HudDefaults, HudSelection, HudSettingsOptions };\n","/**\n * Dock a fixed-position panel to a screen edge. Dragging the handle snaps it\n * to the nearest edge on drop; the edge and the offset along it persist under\n * `<storageKey>:placement`. On a resize (the window's or the panel's own) it\n * keeps its edge and slides along it to stay on screen; only a drop changes the\n * edge. (Re-docking an overflowing box to its nearest edge flipped a panel that\n * grew in a corner onto the side edge.)\n */\nimport { DEFAULT_STORAGE_KEY } from \"./hud-settings.ts\";\n\nconst INSET = 10;\n/** Room for the stacked control discs on the inward side of the panel. */\nconst SIDE_CLEARANCE = 26;\nconst DRAG_THRESHOLD = 4;\nconst EDGES = [\"left\", \"right\", \"top\", \"bottom\"] as const;\n\ntype ScreenEdge = (typeof EDGES)[number];\n\ntype Placement = {\n /** Top (left/right) or left (top/bottom) of the host, in CSS pixels. */\n along: number;\n edge: ScreenEdge;\n};\n\ntype Box = {\n height: number;\n left: number;\n top: number;\n width: number;\n};\n\ntype DockAlign = \"start\" | \"center\" | \"end\";\n\ntype DefaultPlacement = {\n edge: ScreenEdge;\n /** Where along that edge on a first visit. Default `center`. */\n align?: DockAlign;\n};\n\ntype DockPanelOptions = {\n /** The drag grip. Without one the panel still docks and re-docks on resize. */\n handle?: HTMLElement | null;\n /** First-visit position, before anything is stored. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Shares the HUD's key; `null` disables persistence. Default `three-meter`. */\n storageKey?: string | null;\n};\n\ntype DockHandle = {\n /** Re-resolve against the current viewport and panel size (after a mode change, say). */\n refresh: () => Placement;\n dispose: () => void;\n};\n\nconst viewportBox = (): Box => ({\n height: window.innerHeight,\n left: 0,\n top: 0,\n width: window.innerWidth,\n});\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst panelBox = (element: HTMLElement): Box => {\n const rect = element.getBoundingClientRect();\n\n return {\n height: rect.height || element.offsetHeight,\n left: rect.left,\n top: rect.top,\n width: rect.width || element.offsetWidth,\n };\n};\n\nconst clampAlong = (edge: ScreenEdge, along: number, view: Box, panel: Box) => {\n if (edge === \"left\" || edge === \"right\") {\n return clamp(along, INSET, Math.max(INSET, view.height - panel.height - INSET));\n }\n\n // Tools sit on the right (inward) unless the panel is on the right edge.\n return clamp(along, INSET, Math.max(INSET, view.width - panel.width - INSET - SIDE_CLEARANCE));\n};\n\nconst snapBoxToEdge = (box: Box, view: Box): Placement => {\n const distances: Record<ScreenEdge, number> = {\n bottom: view.height - (box.top + box.height),\n left: box.left,\n right: view.width - (box.left + box.width),\n top: box.top,\n };\n\n let edge: ScreenEdge = \"left\";\n let nearest = Number.POSITIVE_INFINITY;\n\n for (const next of EDGES) {\n if (distances[next] < nearest) {\n nearest = distances[next];\n edge = next;\n }\n }\n\n const along = edge === \"left\" || edge === \"right\" ? box.top : box.left;\n\n return { along: Math.round(clampAlong(edge, along, view, box)), edge };\n};\n\nconst initialPlacement = (preset: DefaultPlacement, view: Box, panel: Box): Placement => {\n const { edge } = preset;\n const vertical = edge === \"left\" || edge === \"right\";\n const span = vertical ? view.height : view.width;\n const size = vertical ? panel.height : panel.width;\n const along =\n preset.align === \"start\" ? 0 : preset.align === \"end\" ? span - size : (span - size) / 2;\n\n return { along: Math.round(clampAlong(edge, along, view, panel)), edge };\n};\n\nconst isEdge = (value: unknown): value is ScreenEdge =>\n typeof value === \"string\" && (EDGES as readonly string[]).includes(value);\n\nconst readPlacement = (key: string | null): Placement | null => {\n if (key === null) {\n return null;\n }\n\n try {\n const raw = localStorage.getItem(key);\n\n if (!raw) {\n return null;\n }\n\n const parsed = JSON.parse(raw) as { along?: unknown; edge?: unknown };\n\n if (\n typeof parsed.along !== \"number\" ||\n !Number.isFinite(parsed.along) ||\n !isEdge(parsed.edge)\n ) {\n return null;\n }\n\n return { along: parsed.along, edge: parsed.edge };\n } catch {\n return null;\n }\n};\n\nconst writePlacement = (key: string | null, placement: Placement) => {\n if (key === null) {\n return;\n }\n\n try {\n localStorage.setItem(key, JSON.stringify(placement));\n } catch {\n // Best-effort; the live dock still applies.\n }\n};\n\nconst resolvePlacement = (\n stored: Placement | null,\n preset: DefaultPlacement,\n panel: Box,\n view: Box,\n): Placement => {\n if (!stored) {\n return initialPlacement(preset, view, panel);\n }\n\n return {\n along: Math.round(clampAlong(stored.edge, stored.along, view, panel)),\n edge: stored.edge,\n };\n};\n\nconst paintPlacement = (element: HTMLElement, placement: Placement) => {\n element.dataset.edge = placement.edge;\n element.style.left = \"\";\n element.style.right = \"\";\n element.style.top = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n\n switch (placement.edge) {\n case \"left\":\n element.style.left = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"right\":\n element.style.right = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"top\":\n element.style.top = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n case \"bottom\":\n element.style.bottom = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n }\n};\n\n/**\n * Dock `element` from storage (or the left centre), re-dock on resize, and\n * snap to the nearest edge when the handle drops.\n */\nconst dockPanel = (element: HTMLElement, options: DockPanelOptions = {}): DockHandle => {\n const baseKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const key = baseKey === null ? null : `${baseKey}:placement`;\n const handle = options.handle ?? null;\n const preset = options.defaultPlacement ?? { edge: \"left\" };\n\n const apply = (): Placement => {\n const stored = readPlacement(key);\n const view = viewportBox();\n\n if (element.classList.contains(\"is-dragging\")) {\n return stored ?? initialPlacement(preset, view, panelBox(element));\n }\n\n const panel = panelBox(element);\n\n if (panel.width === 0 || panel.height === 0) {\n return stored ?? initialPlacement(preset, view, panel);\n }\n\n const next = resolvePlacement(stored, preset, panel, view);\n paintPlacement(element, next);\n\n if (!stored || stored.edge !== next.edge || stored.along !== next.along) {\n writePlacement(key, next);\n }\n\n return next;\n };\n\n apply();\n requestAnimationFrame(apply);\n\n const onResize = () => apply();\n window.addEventListener(\"resize\", onResize);\n\n const resizeObserver = new ResizeObserver(() => apply());\n resizeObserver.observe(element);\n\n const disposeBase = () => {\n window.removeEventListener(\"resize\", onResize);\n resizeObserver.disconnect();\n };\n\n if (!handle) {\n return { dispose: disposeBase, refresh: apply };\n }\n\n let pointerId: number | null = null;\n let originX = 0;\n let originY = 0;\n let grabX = 0;\n let grabY = 0;\n let dragging = false;\n\n const finish = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n if (dragging) {\n const next = snapBoxToEdge(panelBox(element), viewportBox());\n paintPlacement(element, next);\n writePlacement(key, next);\n }\n\n dragging = false;\n pointerId = null;\n element.classList.remove(\"is-dragging\");\n\n try {\n handle.releasePointerCapture(event.pointerId);\n } catch {\n // Capture was never set.\n }\n };\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) {\n return;\n }\n\n const box = panelBox(element);\n pointerId = event.pointerId;\n originX = event.clientX;\n originY = event.clientY;\n grabX = event.clientX - box.left;\n grabY = event.clientY - box.top;\n dragging = false;\n handle.setPointerCapture(event.pointerId);\n };\n\n const onPointerMove = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n const dx = event.clientX - originX;\n const dy = event.clientY - originY;\n\n if (!dragging && dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) {\n return;\n }\n\n dragging = true;\n element.classList.add(\"is-dragging\");\n element.style.right = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n element.style.left = `${event.clientX - grabX}px`;\n element.style.top = `${event.clientY - grabY}px`;\n };\n\n handle.addEventListener(\"pointerdown\", onPointerDown);\n handle.addEventListener(\"pointermove\", onPointerMove);\n handle.addEventListener(\"pointerup\", finish);\n handle.addEventListener(\"pointercancel\", finish);\n\n return {\n dispose: () => {\n disposeBase();\n handle.removeEventListener(\"pointerdown\", onPointerDown);\n handle.removeEventListener(\"pointermove\", onPointerMove);\n handle.removeEventListener(\"pointerup\", finish);\n handle.removeEventListener(\"pointercancel\", finish);\n },\n refresh: apply,\n };\n};\n\nexport { dockPanel };\nexport type { DefaultPlacement, DockAlign, DockHandle, DockPanelOptions, Placement, ScreenEdge };\n","/**\n * The Lucide marks the HUD chrome uses, inlined so the package carries no\n * icon dependency. The markup is static, so nothing user-supplied reaches it.\n */\ntype IconName =\n | \"activity\"\n | \"blend\"\n | \"box\"\n | \"circleDot\"\n | \"clipboard\"\n | \"contrast\"\n | \"cpu\"\n | \"eye\"\n | \"eyeOff\"\n | \"flame\"\n | \"gauge\"\n | \"grip\"\n | \"help\"\n | \"hourglass\"\n | \"image\"\n | \"info\"\n | \"layers\"\n | \"monitor\"\n | \"moon\"\n | \"palette\"\n | \"repeat\"\n | \"sliders\"\n | \"spline\"\n | \"sun\"\n | \"timer\"\n | \"trendingDown\"\n | \"triangle\"\n | \"zap\";\n\nconst ICON_MARKUP: Record<IconName, string> = {\n eye:\n '<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/>' +\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n eyeOff:\n '<path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\"/>' +\n '<path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\"/>' +\n '<path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\"/>' +\n '<path d=\"m2 2 20 20\"/>',\n hourglass:\n '<path d=\"M5 22h14\"/><path d=\"M5 2h14\"/>' +\n '<path d=\"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\"/>' +\n '<path d=\"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\"/>',\n flame:\n '<path d=\"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\"/>',\n activity:\n '<path d=\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\"/>',\n box:\n '<path d=\"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\"/>' +\n '<path d=\"m3.3 7 8.7 5 8.7-5\"/><path d=\"M12 22V12\"/>',\n circleDot: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/>',\n cpu:\n '<rect width=\"16\" height=\"16\" x=\"4\" y=\"4\" rx=\"2\"/><rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" rx=\"1\"/>' +\n '<path d=\"M15 2v2\"/><path d=\"M15 20v2\"/><path d=\"M2 15h2\"/><path d=\"M2 9h2\"/>' +\n '<path d=\"M20 15h2\"/><path d=\"M20 9h2\"/><path d=\"M9 2v2\"/><path d=\"M9 20v2\"/>',\n gauge: '<path d=\"m12 14 4-4\"/><path d=\"M3.34 19a10 10 0 1 1 17.32 0\"/>',\n image:\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/>' +\n '<path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/>',\n layers:\n '<path d=\"m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z\"/>' +\n '<path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\"/>' +\n '<path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\"/>',\n palette:\n '<circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\"/>',\n repeat:\n '<path d=\"m17 2 4 4-4 4\"/><path d=\"M3 11v-1a4 4 0 0 1 4-4h14\"/>' +\n '<path d=\"m7 22-4-4 4-4\"/><path d=\"M21 13v1a4 4 0 0 1-4 4H3\"/>',\n spline:\n '<circle cx=\"19\" cy=\"5\" r=\"2\"/><circle cx=\"5\" cy=\"19\" r=\"2\"/><path d=\"M5 17A12 12 0 0 1 17 5\"/>',\n timer:\n '<line x1=\"10\" x2=\"14\" y1=\"2\" y2=\"2\"/><line x1=\"12\" x2=\"15\" y1=\"14\" y2=\"11\"/><circle cx=\"12\" cy=\"14\" r=\"8\"/>',\n trendingDown:\n '<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>',\n triangle: '<path d=\"M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\"/>',\n zap: '<path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\"/>',\n blend: '<circle cx=\"9\" cy=\"9\" r=\"7\"/><circle cx=\"15\" cy=\"15\" r=\"7\"/>',\n clipboard:\n '<rect width=\"8\" height=\"4\" x=\"8\" y=\"2\" rx=\"1\" ry=\"1\"/>' +\n '<path d=\"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\"/>',\n contrast: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 18a6 6 0 0 0 0-12v12z\"/>',\n grip:\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/>' +\n '<circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>',\n help:\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/>' +\n '<path d=\"M12 17h.01\"/>',\n monitor:\n '<rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/>' +\n '<line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/>',\n moon: '<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"/>',\n sun:\n '<circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/>' +\n '<path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/>' +\n '<path d=\"M2 12h2\"/><path d=\"M20 12h2\"/>' +\n '<path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/>',\n sliders:\n '<line x1=\"21\" x2=\"14\" y1=\"4\" y2=\"4\"/><line x1=\"10\" x2=\"3\" y1=\"4\" y2=\"4\"/>' +\n '<line x1=\"21\" x2=\"12\" y1=\"12\" y2=\"12\"/><line x1=\"8\" x2=\"3\" y1=\"12\" y2=\"12\"/>' +\n '<line x1=\"21\" x2=\"16\" y1=\"20\" y2=\"20\"/><line x1=\"12\" x2=\"3\" y1=\"20\" y2=\"20\"/>' +\n '<line x1=\"14\" x2=\"14\" y1=\"2\" y2=\"6\"/><line x1=\"8\" x2=\"8\" y1=\"10\" y2=\"14\"/>' +\n '<line x1=\"16\" x2=\"16\" y1=\"18\" y2=\"22\"/>',\n};\n\nconst createIcon = (name: IconName, className: string) => {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.setAttribute(\"class\", className);\n svg.innerHTML = ICON_MARKUP[name];\n\n return svg;\n};\n\nexport { createIcon };\nexport type { IconName };\n","/**\n * The HUD stylesheet as a string, so the package works without a bundler and\n * injects itself once per document. `.perf-monitor` is the metrics card,\n * `.perf-hud` the floating host around it. Both namespaced so a host page can\n * override safely; the `--perf-*` custom properties are the theming surface.\n *\n * Both elements carry `data-theme=\"dark\" | \"light\"` (see `HudTheme`) and each\n * resolves its own tokens from it, so the card also themes when mounted on\n * its own, outside a `.perf-hud`.\n */\nconst STYLE_ATTRIBUTE = \"data-three-meter\";\n\nconst PERF_HUD_STYLES = `\n.perf-hud,\n.perf-monitor {\n --perf-bg: rgba(22, 24, 29, 0.85);\n --perf-fg: #e6e8eb;\n --perf-fg-dim: #c5c8ce;\n --perf-muted: #8b909a;\n --perf-row: rgba(255, 255, 255, 0.04);\n --perf-border: rgba(255, 255, 255, 0.12);\n --perf-accent: #60a5fa;\n --perf-warn: #f59e0b;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);\n\n color-scheme: dark;\n}\n\n.perf-hud[data-theme=\"light\"],\n.perf-monitor[data-theme=\"light\"] {\n --perf-bg: rgba(250, 250, 252, 0.88);\n --perf-fg: #1a1c21;\n --perf-fg-dim: #4b5058;\n --perf-muted: #6b7079;\n --perf-row: rgba(0, 0, 0, 0.05);\n --perf-border: rgba(0, 0, 0, 0.12);\n --perf-accent: #2563eb;\n --perf-warn: #b45309;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n\n color-scheme: light;\n}\n\n.perf-monitor {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 8px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.4;\n color: var(--perf-fg);\n}\n\n.perf-monitor__graphs {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.perf-monitor__graph {\n position: relative;\n height: 40px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__canvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.perf-monitor__graph-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px;\n pointer-events: none;\n}\n\n.perf-monitor__graph-head {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.perf-monitor__graph-overlay--toggle {\n pointer-events: auto;\n cursor: pointer;\n}\n\n.perf-monitor__graph-label {\n color: var(--perf-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-size: 10px;\n}\n\n.perf-monitor__graph-value {\n min-width: 6ch;\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__section {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.perf-monitor__row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n/* Hidden until \"explain metrics\" is on; then the row wraps it under the label. */\n.perf-monitor__hint {\n display: none;\n flex-basis: 100%;\n padding: 0 0 2px 18px;\n color: var(--perf-muted);\n font-size: 10px;\n line-height: 1.35;\n opacity: 0.85;\n}\n\n.perf-monitor--explain .perf-monitor__hint {\n display: block;\n}\n\n.perf-monitor--explain .perf-monitor__section .perf-monitor__row {\n flex-wrap: wrap;\n}\n\n.perf-monitor__row:nth-child(even) {\n background: var(--perf-row);\n}\n\n.perf-monitor__label {\n flex: 1;\n color: var(--perf-muted);\n}\n\n.perf-monitor__icon {\n flex: none;\n width: 12px;\n height: 12px;\n color: var(--perf-muted);\n}\n\n.perf-monitor__options {\n padding-bottom: 8px;\n border-bottom: 1px solid var(--perf-border);\n}\n\n.perf-monitor__row--static {\n cursor: default;\n}\n\n/* Light / system / dark, as a small segmented radio group. */\n.perf-monitor__segment {\n display: inline-flex;\n flex: none;\n gap: 1px;\n padding: 1px;\n border-radius: 4px;\n background: var(--perf-row);\n}\n\n.perf-monitor__segment-option {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 16px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n}\n\n.perf-monitor__segment-option:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__segment-option[aria-checked=\"true\"] {\n color: var(--perf-fg);\n background: var(--perf-border);\n}\n\n.perf-monitor__segment-option:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__segment-option .perf-monitor__icon {\n width: 11px;\n height: 11px;\n color: inherit;\n}\n\n.perf-monitor__button {\n flex: none;\n min-width: 7ch;\n padding: 0 6px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n background: var(--perf-row);\n color: var(--perf-fg-dim);\n font: inherit;\n cursor: pointer;\n}\n\n.perf-monitor__button:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__button:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Top costs: name, calls, triangles. Rows are buttons that log their objects. */\n.perf-monitor__costs-body {\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding-top: 2px;\n}\n\n.perf-monitor__costs-body[hidden] {\n display: none;\n}\n\n.perf-monitor__cost {\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch 14px;\n gap: 6px;\n align-items: baseline;\n padding: 2px 6px;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: default;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\n}\n\n.perf-monitor__cost.is-hidden .perf-monitor__cost-name {\n opacity: 0.6;\n text-decoration: line-through;\n}\n\n/* What hiding saved, across the calls and tris columns. */\n.perf-monitor__cost-saved {\n grid-column: 2 / span 2;\n color: var(--perf-accent);\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n white-space: nowrap;\n}\n\n.perf-monitor__cost-eye {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n align-self: center;\n}\n\n.perf-monitor__cost-eye .perf-monitor__icon {\n width: 12px;\n height: 12px;\n color: inherit;\n}\n\n.perf-monitor__cost-eye:hover,\n.perf-monitor__cost-eye[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-eye:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: -1px;\n}\n\n.perf-monitor__cost--head {\n align-items: center;\n cursor: default;\n}\n\n/* The name is a button: a click logs the row's objects. */\n.perf-monitor__cost-name {\n min-width: 0;\n padding: 0;\n border: 0;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__cost-name:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-name:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost-copies {\n color: var(--perf-muted);\n}\n\n.perf-monitor__cost-value {\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__cost-sort {\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--perf-muted);\n font: inherit;\n font-size: 10px;\n text-align: right;\n cursor: pointer;\n}\n\n.perf-monitor__cost-sort[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.perf-monitor__segment-option--text {\n width: auto;\n padding: 0 5px;\n font: inherit;\n font-size: 10px;\n}\n\n.perf-monitor__costs-note {\n padding: 2px 6px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__checkbox {\n flex: none;\n margin: 0;\n width: 12px;\n height: 12px;\n accent-color: var(--perf-accent);\n cursor: pointer;\n}\n\n.perf-monitor__value {\n min-width: 7ch;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__hud {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 2px;\n}\n\n.perf-monitor__hud-graphs {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.perf-monitor__hud-graphs:empty {\n display: none;\n}\n\n.perf-monitor__hud-graph {\n position: relative;\n height: 28px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__hud-grid {\n display: grid;\n grid-template-columns: auto 6ch auto 6ch;\n align-items: baseline;\n gap: 2px 8px;\n}\n\n.perf-monitor__hud-grid:empty {\n display: none;\n}\n\n.perf-monitor__hud-item {\n display: contents;\n}\n\n.perf-monitor__hud-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__hud-label .perf-monitor__icon,\n.perf-monitor__hud-graph .perf-monitor__icon {\n width: 11px;\n height: 11px;\n}\n\n.perf-monitor__hud-value {\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__footer {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 8px 6px 0;\n border-top: 1px solid var(--perf-border);\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__footer-body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row[hidden],\n.perf-monitor__badge[hidden] {\n display: none;\n}\n\n.perf-monitor__footer-gpu {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__badge {\n flex: none;\n padding: 0 5px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n color: var(--perf-fg-dim);\n letter-spacing: 0.04em;\n}\n\n.perf-monitor__badge.is-fallback {\n border-color: var(--perf-warn);\n color: var(--perf-warn);\n}\n\n/* Past its budget. The value's tooltip names the budget. */\n.perf-monitor__value.is-over,\n.perf-monitor__hud-value.is-over,\n.perf-monitor__graph-value.is-over {\n color: var(--perf-warn);\n}\n\n.perf-monitor__link {\n color: inherit;\n text-decoration: none;\n}\n\n.perf-monitor__link:hover,\n.perf-monitor__link:focus-visible {\n color: var(--perf-accent);\n text-decoration: underline;\n}\n\n.perf-monitor--full .perf-monitor__hud {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__graphs,\n.perf-monitor--compact .perf-monitor__section,\n.perf-monitor--compact:not(.perf-monitor--info) .perf-monitor__footer,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__checkbox,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__icon {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__footer {\n padding: 6px 2px 2px;\n}\n\n.perf-monitor--compact {\n padding: 4px 8px;\n}\n\n.perf-hud {\n position: fixed;\n z-index: 9999;\n /* Safari resolves width:100% canvases against the viewport on a fixed\n host with auto width. Shrink-wrap so expand stays a card, not the window. */\n width: max-content;\n max-width: min(20rem, calc(100vw - 48px));\n overflow: visible;\n background: transparent;\n pointer-events: none;\n user-select: none;\n touch-action: none;\n}\n\n.perf-hud--full .perf-hud__card > .perf-monitor {\n width: 16rem;\n max-width: 100%;\n}\n\n/* Room for the hints, so most fit on two or three lines. Plus padding, this fills the host's 20rem cap. */\n.perf-hud--full .perf-hud__card > .perf-monitor--explain {\n width: 19rem;\n}\n\n/* The card every dev panel sits in (\\`mountDevPanel\\`): the brand label, then the panel's content. */\n.perf-hud__card {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n max-height: 70vh;\n overflow: hidden;\n background: var(--perf-bg);\n border-radius: 6px;\n box-shadow: var(--perf-shadow);\n backdrop-filter: blur(2px);\n pointer-events: auto;\n transition: opacity 0.25s ease;\n}\n\n.perf-hud__card > .perf-monitor {\n min-height: 0;\n overflow: auto;\n}\n\n.perf-hud--dim:not(.is-awake):not(.is-dragging) > .perf-hud__card {\n opacity: 0.32;\n}\n\n.perf-hud__brand {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px 0;\n color: var(--perf-muted);\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.08em;\n line-height: 1;\n text-transform: uppercase;\n}\n\n/* Alone in the card: pad it all round. */\n.perf-hud__brand:last-child {\n padding-bottom: 6px;\n}\n\n.perf-hud__brand-name {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.perf-hud__brand-name::before {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--perf-accent);\n content: \"\";\n}\n\n.perf-hud__brand-detail {\n margin-left: auto;\n font-weight: 400;\n letter-spacing: 0.02em;\n text-transform: none;\n}\n\n.perf-hud__brand-detail:empty {\n display: none;\n}\n\n.perf-hud__hotspot {\n position: absolute;\n z-index: 0;\n inset: -20px;\n pointer-events: auto;\n}\n\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__hotspot {\n right: -46px;\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__hotspot {\n left: -46px;\n}\n\n.perf-hud__tools {\n position: absolute;\n top: 0;\n z-index: 2;\n display: flex;\n flex-direction: column;\n gap: 4px;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n}\n\n.perf-hud.is-awake .perf-hud__tools,\n.perf-hud.is-dragging .perf-hud__tools {\n opacity: 1;\n pointer-events: auto;\n}\n\n/* Inward side: right by default, left when docked to the right edge. */\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__tools {\n left: calc(100% + 6px);\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__tools {\n right: calc(100% + 6px);\n}\n\n.perf-hud__disc {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--perf-bg);\n box-shadow: var(--perf-shadow);\n color: var(--perf-fg-dim);\n cursor: pointer;\n backdrop-filter: blur(2px);\n}\n\n.perf-hud__icon {\n width: 12px;\n height: 12px;\n}\n\n.perf-hud__drag {\n cursor: grab;\n}\n\n.perf-hud.is-dragging .perf-hud__drag {\n cursor: grabbing;\n}\n\n/* The ring is only ever hover or keyboard focus, never state. */\n.perf-hud__disc:hover,\n.perf-hud__disc:focus-visible {\n color: var(--perf-fg);\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Dim on is a solid accent disc, off a plain one, so the state reads without hovering and\n hover keeps its own look on top of either. After the hover rule, so on stays on under it. */\n.perf-hud__dim[aria-pressed=\"true\"] {\n background: var(--perf-accent);\n color: var(--perf-bg);\n}\n`;\n\n/**\n * Append the stylesheet to `target`'s head, once. Call before mounting a view\n * by hand; {@link mountPerfHud} does it for you unless told not to.\n */\nconst injectStyles = (target: Document = document) => {\n if (target.head.querySelector(`style[${STYLE_ATTRIBUTE}]`)) {\n return;\n }\n\n const style = target.createElement(\"style\");\n style.setAttribute(STYLE_ATTRIBUTE, \"\");\n style.textContent = PERF_HUD_STYLES;\n target.head.append(style);\n};\n\nexport { injectStyles, PERF_HUD_STYLES };\n","/**\n * The frame every zkmake dev panel shares, so they look and handle alike: a fixed host docked to a\n * screen edge, a card with a brand label on top and the panel's content under it, and three discs\n * on the inward side — drag grip, expand/compact, dim on leave — that stay hidden until the pointer\n * is near or someone taps. `mountPerfHud` is one; `@zkmake/three-textures`' panel is another.\n */\nimport { type DefaultPlacement, dockPanel, type ScreenEdge } from \"./dock-panel.ts\";\nimport { createIcon } from \"./icons.ts\";\nimport { injectStyles } from \"./styles.ts\";\nimport { applyTheme, type HudTheme } from \"./theme.ts\";\n\nconst TOUCH_LINGER_MS = 2500;\n\ntype DevPanelDim = {\n get: () => boolean;\n set: (on: boolean) => void;\n subscribe: (listener: () => void) => () => void;\n};\n\ntype MountDevPanelOptions = {\n /** The panel's body, placed in the card under the brand label. */\n content: HTMLElement;\n /** The label on top of the card, naming the tool: `three-meter`, `three-textures`. */\n brand: string;\n /** Accessible name of the panel. */\n label: string;\n theme: HudTheme;\n /** Base key for the dock placement (`<key>:placement`) and the default dim flag. `null`: not kept. */\n storageKey: string | null;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** The expand/compact disc: its label, and what a press does. */\n onToggle: () => void;\n toggleLabel?: string;\n /** Where dim-on-leave lives. Default a flag of its own under `<storageKey>:dim`, off. */\n dim?: DevPanelDim;\n /** The panel docked to another edge (a drop or a resize). */\n onEdgeChange?: (edge: ScreenEdge) => void;\n};\n\ntype DevPanelHandle = {\n element: HTMLDivElement;\n card: HTMLDivElement;\n /** The brand row's right-hand slot: a count, a status. */\n detail: HTMLSpanElement;\n /** Re-dock against the current size, after the card grows or shrinks. */\n refresh: () => void;\n dispose: () => void;\n};\n\nconst disc = (label: string, className: string, icon: \"blend\" | \"grip\" | \"sliders\") => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = `perf-hud__disc ${className}`.trim();\n button.setAttribute(\"aria-label\", label);\n button.title = label;\n button.append(createIcon(icon, \"perf-hud__icon\"));\n\n return button;\n};\n\n/** A dim flag in localStorage, for panels with no settings of their own. */\nconst storedDim = (key: string | null): DevPanelDim => {\n const listeners = new Set<() => void>();\n let on = false;\n\n try {\n on = key !== null && localStorage.getItem(key) === \"1\";\n } catch {\n // No storage (private mode): the flag lasts this page.\n }\n\n return {\n get: () => on,\n set: (next) => {\n on = next;\n\n try {\n if (key !== null) {\n localStorage.setItem(key, next ? \"1\" : \"0\");\n }\n } catch {\n // As above.\n }\n\n for (const listener of listeners) {\n listener();\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nconst mountDevPanel = (options: MountDevPanelOptions): DevPanelHandle => {\n const parent = options.parent ?? document.body;\n const dim =\n options.dim ?? storedDim(options.storageKey === null ? null : `${options.storageKey}:dim`);\n\n if (options.injectStyles ?? true) {\n injectStyles(parent.ownerDocument);\n }\n\n const host = document.createElement(\"div\");\n host.className = \"perf-hud\";\n host.dataset.edge = \"left\";\n host.setAttribute(\"aria-label\", options.label);\n\n const hotspot = document.createElement(\"div\");\n hotspot.className = \"perf-hud__hotspot\";\n hotspot.setAttribute(\"aria-hidden\", \"true\");\n\n const tools = document.createElement(\"div\");\n tools.className = \"perf-hud__tools\";\n const grip = disc(`Drag the ${options.brand} panel`, \"perf-hud__drag\", \"grip\");\n const toggle = disc(options.toggleLabel ?? \"Expand or compact the panel\", \"\", \"sliders\");\n const dimDisc = disc(\"Dim the panel when the pointer leaves\", \"perf-hud__dim\", \"blend\");\n dimDisc.addEventListener(\"click\", () => dim.set(!dim.get()));\n toggle.addEventListener(\"click\", () => options.onToggle());\n tools.append(grip, toggle, dimDisc);\n\n const card = document.createElement(\"div\");\n card.className = \"perf-hud__card\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"perf-hud__brand\";\n\n const name = document.createElement(\"span\");\n name.className = \"perf-hud__brand-name\";\n name.textContent = options.brand;\n\n const detail = document.createElement(\"span\");\n detail.className = \"perf-hud__brand-detail\";\n brand.append(name, detail);\n card.append(brand, options.content);\n\n host.append(hotspot, tools, card);\n parent.append(host);\n\n const applyDim = () => {\n host.classList.toggle(\"perf-hud--dim\", dim.get());\n dimDisc.setAttribute(\"aria-pressed\", String(dim.get()));\n dimDisc.title = `Dim on leave: ${dim.get() ? \"on\" : \"off\"}`;\n };\n\n applyDim();\n applyTheme(host, options.theme);\n const unsubscribeDim = dim.subscribe(applyDim);\n const unsubscribeTheme = options.theme.subscribe(() => applyTheme(host, options.theme));\n const dock = dockPanel(host, {\n defaultPlacement: options.defaultPlacement,\n handle: grip,\n storageKey: options.storageKey,\n });\n\n // `dockPanel` writes the edge onto `data-edge`; pass changes on.\n let edge = host.dataset.edge as ScreenEdge;\n const edgeWatch = new MutationObserver(() => {\n const next = host.dataset.edge as ScreenEdge;\n\n if (next !== edge) {\n edge = next;\n options.onEdgeChange?.(edge);\n }\n });\n\n edgeWatch.observe(host, { attributes: true, attributeFilter: [\"data-edge\"] });\n options.onEdgeChange?.(edge);\n\n // Wake on approach, sleep on leave; a touch lingers so the discs can be tapped.\n let hideTimer = 0;\n\n const sleep = () => {\n if (host.classList.contains(\"is-dragging\")) {\n return;\n }\n\n host.classList.remove(\"is-awake\");\n };\n\n const wake = (linger: boolean) => {\n host.classList.add(\"is-awake\");\n window.clearTimeout(hideTimer);\n\n if (linger) {\n hideTimer = window.setTimeout(sleep, TOUCH_LINGER_MS);\n }\n };\n\n const onEnter = () => {\n wake(false);\n };\n\n const onLeave = (event: PointerEvent) => {\n if (host.contains(event.relatedTarget as Node | null)) {\n return;\n }\n\n if (event.pointerType === \"touch\" || event.pointerType === \"pen\") {\n wake(true);\n\n return;\n }\n\n sleep();\n };\n\n const onDown = (event: PointerEvent) => {\n wake(event.pointerType !== \"mouse\");\n };\n\n host.addEventListener(\"pointerenter\", onEnter);\n host.addEventListener(\"pointerleave\", onLeave);\n host.addEventListener(\"pointerdown\", onDown);\n\n return {\n element: host,\n card,\n detail,\n refresh: () => {\n requestAnimationFrame(() => dock.refresh());\n },\n dispose: () => {\n window.clearTimeout(hideTimer);\n edgeWatch.disconnect();\n host.removeEventListener(\"pointerenter\", onEnter);\n host.removeEventListener(\"pointerleave\", onLeave);\n host.removeEventListener(\"pointerdown\", onDown);\n unsubscribeDim();\n unsubscribeTheme();\n dock.dispose();\n host.remove();\n },\n };\n};\n\nexport { mountDevPanel };\nexport type { DevPanelDim, DevPanelHandle, MountDevPanelOptions };\n","","/**\n * Copy text from a click handler. The async Clipboard API needs a secure\n * context and the `clipboard-write` permission; a dev server opened over a LAN\n * IP has neither, and some embeds deny the permission. The fallback is the old\n * select-a-textarea `execCommand(\"copy\")`, still honoured inside a user gesture.\n */\nconst copyWithSelection = (text: string) => {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n textarea.style.pointerEvents = \"none\";\n\n const previous = document.activeElement as HTMLElement | null;\n document.body.append(textarea);\n textarea.select();\n\n try {\n return document.execCommand(\"copy\");\n } catch {\n return false;\n } finally {\n textarea.remove();\n previous?.focus?.();\n }\n};\n\nconst copyText = async (text: string): Promise<boolean> => {\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return copyWithSelection(text);\n }\n};\n\nexport { copyText };\n","/**\n * Number formatting for the card. Counts stay exact with thousands separators\n * while they fit the compact grid's 6ch value column (`99,999`), then switch\n * to compact notation (`250K`, `1.4M`, `12.3M`, `1.2B`) so a triangle count in\n * the millions can't run into the neighbouring label.\n */\nconst COMPACT_FROM = 100_000;\n\nconst exact = new Intl.NumberFormat(\"en-US\");\nconst compact = new Intl.NumberFormat(\"en-US\", {\n maximumFractionDigits: 1,\n notation: \"compact\",\n});\n\nconst formatCount = (value: number) =>\n Math.abs(value) < COMPACT_FROM ? exact.format(value) : compact.format(value);\n\nexport { formatCount };\n","/**\n * Hides groups of scene objects to measure what they cost, and puts every one\n * back exactly as it was. Objects are three objects, read structurally: only\n * `visible` is touched.\n *\n * An object can sit in two hidden groups at once (a mesh row and its\n * material's row); it stays hidden until the last group that holds it is\n * shown again. Objects that were already invisible stay invisible.\n */\ntype Hideable = { visible?: boolean };\n\nclass Hider {\n private readonly groups = new Map<string, Hideable[]>();\n /** Each hidden object's `visible` from before the first group hid it. */\n private readonly original = new Map<Hideable, boolean>();\n\n hide(key: string, objects: readonly unknown[]) {\n if (this.groups.has(key)) {\n return;\n }\n\n const hideable = objects.filter(\n (object): object is Hideable => typeof object === \"object\" && object !== null,\n );\n this.groups.set(key, hideable);\n\n for (const object of hideable) {\n if (!this.original.has(object)) {\n this.original.set(object, object.visible !== false);\n }\n\n object.visible = false;\n }\n }\n\n show(key: string) {\n const objects = this.groups.get(key);\n\n if (!objects) {\n return;\n }\n\n this.groups.delete(key);\n\n for (const object of objects) {\n if (!this.heldElsewhere(object)) {\n object.visible = this.original.get(object) ?? true;\n this.original.delete(object);\n }\n }\n }\n\n isHidden(key: string) {\n return this.groups.has(key);\n }\n\n /** Hidden group keys, in the order they were hidden. */\n keys() {\n return [...this.groups.keys()];\n }\n\n restoreAll() {\n for (const key of this.keys()) {\n this.show(key);\n }\n }\n\n private heldElsewhere(object: Hideable) {\n for (const objects of this.groups.values()) {\n if (objects.includes(object)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport { Hider };\n","/**\n * One plain-language line per metric: what it is, and what a bad reading\n * means. Shown as the label's tooltip and, with \"explain metrics\" on, under\n * each row. Keyed like the stat rows; the graphs reuse `fps`, `cpu`, `gpu`.\n */\nconst METRIC_HELP: Record<string, string> = {\n calls:\n \"Separate draw commands sent to the GPU this frame. Fewer is better: merge or instance meshes that share a material.\",\n cpu: \"JavaScript time per frame: your update code plus three.js preparing the render. It has to fit the frame budget (16.7 ms at 60 FPS).\",\n fps: \"Frames drawn per second. Higher is smoother; most displays top out at 60 or 120.\",\n geometries:\n \"Geometries held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n headroom:\n \"Spare time left in each frame: the frame budget minus whichever of CPU and GPU took longer. Below zero, frames miss the display's refresh.\",\n refresh:\n \"The rate the loop runs at when it keeps up: the display's refresh rate, or the app's own cap. Budgets use it unless you set targetFps.\",\n gpu: \"Time the graphics card spent drawing the frame. High means shaders, overdraw or resolution are the bottleneck.\",\n hitches:\n \"Frames that took over twice as long as usual, in the last 1,000 frames. Each one is a visible stutter.\",\n lines: \"Line segments drawn this frame.\",\n low: \"Frame rate during the slowest 1% of frames. Far below FPS means stutter the average hides.\",\n p99: \"99% of frames finished within this many ms. Above the frame budget (16.7 ms at 60 FPS) means dropped frames.\",\n passes:\n \"Times renderer.render() ran this frame. Shadows, post-processing and extra cameras each add passes.\",\n points: \"Points drawn this frame.\",\n shaders:\n \"Compiled shader programs. A jump mid-session usually means a stutter while a new material compiled.\",\n textures:\n \"Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n triangles:\n \"Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it.\",\n};\n\nexport { METRIC_HELP };\n","/**\n * A plain-text snapshot of the monitor for bug reports: versions, backend and\n * GPU, the current sample, stutter stats, the scene's top meshes and the page. Fenced as a `text`\n * code block so GitHub and Slack keep the line breaks.\n */\nimport { version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { Environment, FrameStats, Sample, SceneCost } from \"../core/types.ts\";\nimport { formatCount } from \"./format.ts\";\n\ntype ReportPage = {\n dpr: number;\n height: number;\n userAgent: string;\n width: number;\n};\n\ntype ReportData = {\n /** The top meshes go in when given. */\n cost?: SceneCost | null;\n environment: Environment;\n page: ReportPage | null;\n sample: Sample;\n stats: FrameStats;\n};\n\nconst BACKEND_LABELS = { webgl: \"WebGL\", webgl2: \"WebGL2\", webgpu: \"WebGPU\" } as const;\n\n/** `WebGPU`, `WebGL2`, `WebGL2 fallback`, or `\"\"` before the backend settles. */\nconst backendLabel = (environment: Environment) => {\n if (!environment.backend) {\n return \"\";\n }\n\n const label = BACKEND_LABELS[environment.backend];\n\n return environment.fallback ? `${label} fallback` : label;\n};\n\nconst ms = (value: number) => `${value.toFixed(1)} ms`;\n\n/** Top meshes by draw calls, three at most: `Top: cubes ×2,000 (1 call, 24K tris) · …`. */\nconst topMeshes = (cost: SceneCost) =>\n `Top: ${cost.meshes\n .slice(0, 3)\n .map((entry) => {\n const copies = entry.instances > 1 ? ` ×${formatCount(entry.instances)}` : \"\";\n const calls = `${formatCount(entry.calls)} ${entry.calls === 1 ? \"call\" : \"calls\"}`;\n\n return `${entry.label}${copies} (${calls}, ${formatCount(entry.triangles)} tris)`;\n })\n .join(\" · \")}`;\n\nconst renderReport = ({ cost, environment, page, sample, stats }: ReportData): string => {\n const { render, resources } = sample;\n const heading = [\n `three-meter v${version}`,\n environment.three ? `three r${environment.three}` : \"\",\n backendLabel(environment),\n environment.gpu ?? \"\",\n ].filter(Boolean);\n\n const frames =\n stats.frames === 0\n ? `FPS ${Math.round(sample.fps)}`\n : [\n `FPS ${Math.round(sample.fps)}`,\n `1% low ${Math.round(stats.lowFps)}`,\n `p99 ${ms(stats.p99Ms)}`,\n `hitches ${formatCount(stats.hitches)} / ${formatCount(stats.frames)} frames`,\n ].join(\" · \");\n\n const timing = [\n `CPU ${ms(sample.cpu)}`,\n `GPU ${sample.gpu.available ? ms(sample.gpu.ms) : \"unavailable\"}`,\n ];\n\n // Headroom against the detected refresh: spare time before the frame misses its slot.\n if (stats.refreshHz) {\n const busiest = Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0);\n timing.push(\n `refresh ${stats.refreshHz} Hz`,\n `headroom ${ms(1000 / stats.refreshHz - busiest)}`,\n );\n }\n\n const lines = [\n heading.join(\" · \"),\n frames,\n timing.join(\" · \"),\n [\n `Calls ${formatCount(render.calls)}`,\n `passes ${formatCount(render.passes)}`,\n `triangles ${formatCount(render.triangles)}`,\n `lines ${formatCount(render.lines)}`,\n `points ${formatCount(render.points)}`,\n ].join(\" · \"),\n [\n `Geometries ${formatCount(resources.geometries)}`,\n `textures ${formatCount(resources.textures)}`,\n `shaders ${formatCount(resources.programs)}`,\n ].join(\" · \"),\n ];\n\n if (cost && cost.meshes.length > 0) {\n lines.push(topMeshes(cost));\n }\n\n if (page) {\n lines.push(`Viewport ${page.width}×${page.height} @${page.dpr}x`, page.userAgent);\n }\n\n return [\"```text\", ...lines, \"```\"].join(\"\\n\");\n};\n\nconst readPage = (): ReportPage | null =>\n typeof window === \"undefined\"\n ? null\n : {\n dpr: window.devicePixelRatio || 1,\n height: window.innerHeight,\n userAgent: navigator.userAgent,\n width: window.innerWidth,\n };\n\n/** The report for `monitor` right now, as Markdown ready to paste into an issue. */\nconst formatReport = (monitor: PerformanceMonitor): string =>\n renderReport({\n cost: monitor.getSceneCost(),\n environment: monitor.getEnvironment(),\n page: readPage(),\n sample: monitor.getSample(),\n stats: monitor.getFrameStats(),\n });\n\nexport { backendLabel, formatReport, renderReport };\nexport type { ReportData, ReportPage };\n","/**\n * Minimal canvas sparkline. The caller owns the canvas, its size, and any\n * device-pixel-ratio transform; this just paints a filled line series in\n * logical (CSS) pixel coordinates. An optional guide (a budget) is drawn as a\n * dashed line when it falls inside the series' range. The scale never\n * stretches to fit it, so a CPU series at 0.2 ms keeps its shape under a\n * 16.7 ms budget and the line appears once the series gets near.\n */\ntype SparklineStyle = {\n fill: string;\n stroke: string;\n};\n\ntype SparklineGuide = {\n color: string;\n value: number;\n};\n\nfunction drawSparkline(\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number,\n values: readonly number[],\n style: SparklineStyle,\n guide?: SparklineGuide,\n) {\n ctx.clearRect(0, 0, width, height);\n\n if (values.length < 2) {\n return;\n }\n\n let max = 0;\n\n for (const value of values) {\n if (value > max) {\n max = value;\n }\n }\n\n const range = max * 1.1 || 1;\n const stepX = width / (values.length - 1);\n const toY = (value: number) => height - (value / range) * height;\n\n ctx.beginPath();\n ctx.moveTo(0, toY(values[0]!));\n\n for (let index = 1; index < values.length; index += 1) {\n ctx.lineTo(index * stepX, toY(values[index]!));\n }\n\n ctx.lineWidth = 1;\n ctx.strokeStyle = style.stroke;\n ctx.stroke();\n\n ctx.lineTo(width, height);\n ctx.lineTo(0, height);\n ctx.closePath();\n ctx.fillStyle = style.fill;\n ctx.fill();\n\n if (guide && guide.value > 0 && guide.value <= range) {\n // Half-pixel offset keeps a 1px line crisp.\n const y = Math.round(toY(guide.value)) + 0.5;\n ctx.save();\n ctx.setLineDash([3, 3]);\n ctx.strokeStyle = guide.color;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(width, y);\n ctx.stroke();\n ctx.restore();\n }\n}\n\nexport { drawSparkline };\nexport type { SparklineGuide, SparklineStyle };\n","import { name, version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { CostEntry, FrameStats, Sample, TimingMetric } from \"../core/types.ts\";\nimport {\n type BudgetKey,\n type Budgets,\n isFloor,\n isOverBudget,\n type ResolvedBudgets,\n resolveBudgets,\n} from \"./budgets.ts\";\nimport { copyText } from \"./clipboard.ts\";\nimport { formatCount } from \"./format.ts\";\nimport { Hider } from \"./hider.ts\";\nimport { HudSettings } from \"./hud-settings.ts\";\nimport { createIcon, type IconName } from \"./icons.ts\";\nimport { METRIC_HELP } from \"./metric-help.ts\";\nimport { backendLabel, formatReport } from \"./report.ts\";\nimport { drawSparkline, type SparklineStyle } from \"./sparkline.ts\";\nimport { applyTheme, HudTheme, type ResolvedTheme, type ThemeMode } from \"./theme.ts\";\n\ntype PerformanceViewMode = \"full\" | \"compact\";\n\ntype PerformanceViewOptions = {\n monitor: PerformanceMonitor;\n /** `full` is the checkbox list; `compact` the card it configures. Default `full`. */\n mode?: PerformanceViewMode;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber. Timing budgets default from\n * `targetFps` (60); `false` turns them all off. See {@link Budgets}.\n */\n budgets?: Budgets | false;\n /** Share one between views to keep them in sync; a fresh instance otherwise. */\n settings?: HudSettings;\n /**\n * Share the host's so the card and its chrome switch together. Otherwise a\n * fresh `system` instance is created and disposed with the view.\n */\n theme?: HudTheme;\n};\n\ntype TimingConfig = {\n format: (value: number) => string;\n icon: IconName;\n label: string;\n metric: TimingMetric;\n /** Canvas can't read CSS custom properties, so the sparkline palette lives here per theme. */\n style: Record<ResolvedTheme, SparklineStyle>;\n unit: string;\n};\n\ntype NumberConfig = {\n format: (value: number) => string;\n icon: IconName;\n key: BudgetKey;\n label: string;\n unit?: \"ms\" | \"Hz\";\n /**\n * `null` until known: no frames yet, or no GPU timer. `frameMs` is the frame\n * budget the timing budgets came from (target or detected refresh).\n */\n value: (sample: Sample, stats: FrameStats, frameMs: number) => number | null;\n};\n\nconst TIMINGS: TimingConfig[] = [\n {\n format: (value) => Math.round(value).toString(),\n icon: \"gauge\",\n label: \"FPS\",\n metric: \"fps\",\n style: {\n dark: { fill: \"rgba(74, 222, 128, 0.15)\", stroke: \"#4ade80\" },\n light: { fill: \"rgba(22, 163, 74, 0.12)\", stroke: \"#16a34a\" },\n },\n unit: \"\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"cpu\",\n label: \"CPU\",\n metric: \"cpu\",\n style: {\n dark: { fill: \"rgba(96, 165, 250, 0.15)\", stroke: \"#60a5fa\" },\n light: { fill: \"rgba(37, 99, 235, 0.12)\", stroke: \"#2563eb\" },\n },\n unit: \"ms\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"zap\",\n label: \"GPU\",\n metric: \"gpu\",\n style: {\n dark: { fill: \"rgba(244, 114, 182, 0.15)\", stroke: \"#f472b6\" },\n light: { fill: \"rgba(219, 39, 119, 0.12)\", stroke: \"#db2777\" },\n },\n unit: \"ms\",\n },\n];\n\nconst THEME_OPTIONS: { icon: \"sun\" | \"monitor\" | \"moon\"; label: string; mode: ThemeMode }[] = [\n { icon: \"sun\", label: \"Light theme\", mode: \"light\" },\n { icon: \"monitor\", label: \"Match the system theme\", mode: \"system\" },\n { icon: \"moon\", label: \"Dark theme\", mode: \"dark\" },\n];\n\n/** The monorepo tags each package's releases `@zkmake/three-meter@0.9.2`. */\nconst RELEASE_URL = `https://github.com/zkmake/three-kit/releases/tag/${encodeURIComponent(`${name}@${version}`)}`;\n\n/** How long the report button says `copied` / `failed` before reading `copy` again. */\nconst COPY_FEEDBACK_MS = 1500;\n\n/** Walking the scene graph isn't free; the top-costs list refreshes at 2 Hz while open. */\nconst COSTS_INTERVAL_MS = 500;\nconst COSTS_ROWS = 5;\n\ntype CostGroup = \"meshes\" | \"materials\";\ntype CostSort = \"calls\" | \"triangles\";\n\nconst COST_GROUPS: { group: CostGroup; label: string }[] = [\n { group: \"meshes\", label: \"meshes\" },\n { group: \"materials\", label: \"materials\" },\n];\n\nconst COSTS_HELP =\n \"The scene's biggest draw costs in the main render pass, estimated from the scene graph. Hidden and off-screen objects are left out; shadow and post-processing passes aren't counted. Click a row to log its objects to the console; use the eye to hide them and measure the time they cost.\";\n\n/** Frames averaged on each side of hiding a row. */\nconst MEASURE_FRAMES = 30;\n/** How long after hiding before the saving is read, so the average holds only frames without the row. */\nconst MEASURE_SETTLE_MS = 700;\n\ntype HiddenRow = {\n /** GPU ms when a GPU timer exists, else CPU ms: whichever the saving is measured in. */\n baseline: number;\n entry: CostEntry;\n hiddenAt: number;\n metric: \"cpu\" | \"gpu\";\n};\n\nconst meanOfLast = (values: readonly number[], count: number) => {\n const tail = values.slice(-count);\n\n return tail.length === 0 ? 0 : tail.reduce((sum, value) => sum + value, 0) / tail.length;\n};\n\n/** `×2,000` for instances or copies; nothing for a single object. */\nconst copiesOf = (entry: CostEntry) =>\n entry.instances > 1 ? `×${formatCount(entry.instances)}` : \"\";\n\nconst costTitle = (entry: CostEntry) => {\n const objects = entry.objects.length;\n const shape =\n entry.instances > objects\n ? `${formatCount(entry.instances)} instances in ${objects === 1 ? \"one object\" : `${objects} objects`}`\n : `${formatCount(objects)} ${objects === 1 ? \"object\" : \"separate objects\"}`;\n\n return `${entry.label}: ${shape}, ${formatCount(entry.calls)} draw ${entry.calls === 1 ? \"call\" : \"calls\"}, ${formatCount(entry.triangles)} triangles. Click to log to the console.`;\n};\n\nconst readTiming = (sample: Sample, metric: TimingMetric) => {\n switch (metric) {\n case \"fps\":\n return sample.fps;\n case \"cpu\":\n return sample.cpu;\n case \"gpu\":\n return sample.gpu.ms;\n }\n};\n\nconst isTimingAvailable = (sample: Sample, metric: TimingMetric) =>\n metric !== \"gpu\" || sample.gpu.available;\n\nconst whole = (value: number) => Math.round(value).toString();\nconst tenths = (value: number) => value.toFixed(1);\n\nconst NUMBERS: NumberConfig[] = [\n { format: whole, icon: \"gauge\", key: \"fps\", label: \"FPS\", value: (sample) => sample.fps || null },\n {\n format: formatCount,\n icon: \"layers\",\n key: \"calls\",\n label: \"Calls\",\n value: (sample) => sample.render.calls,\n },\n {\n format: tenths,\n icon: \"cpu\",\n key: \"cpu\",\n label: \"CPU\",\n unit: \"ms\",\n value: (sample) => sample.cpu,\n },\n {\n format: tenths,\n icon: \"zap\",\n key: \"gpu\",\n label: \"GPU\",\n unit: \"ms\",\n value: (sample) => (sample.gpu.available ? sample.gpu.ms : null),\n },\n {\n format: tenths,\n icon: \"hourglass\",\n key: \"headroom\",\n label: \"Headroom\",\n unit: \"ms\",\n // Spare time in the frame: the budget minus whichever of CPU and GPU took longer.\n value: (sample, _stats, frameMs) =>\n sample.fps ? frameMs - Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0) : null,\n },\n {\n format: (value) => `${Math.round(value)} Hz`,\n icon: \"monitor\",\n key: \"refresh\",\n label: \"Refresh\",\n unit: \"Hz\",\n value: (_sample, stats) => stats.refreshHz,\n },\n {\n format: whole,\n icon: \"trendingDown\",\n key: \"low\",\n label: \"1% low\",\n value: (_sample, stats) => (stats.frames ? stats.lowFps : null),\n },\n {\n format: tenths,\n icon: \"timer\",\n key: \"p99\",\n label: \"Frame p99\",\n unit: \"ms\",\n value: (_sample, stats) => (stats.frames ? stats.p99Ms : null),\n },\n {\n format: formatCount,\n icon: \"activity\",\n key: \"hitches\",\n label: \"Hitches\",\n value: (_sample, stats) => (stats.frames ? stats.hitches : null),\n },\n {\n format: formatCount,\n icon: \"triangle\",\n key: \"triangles\",\n label: \"Triangles\",\n value: (sample) => sample.render.triangles,\n },\n {\n format: formatCount,\n icon: \"spline\",\n key: \"lines\",\n label: \"Lines\",\n value: (sample) => sample.render.lines,\n },\n {\n format: formatCount,\n icon: \"circleDot\",\n key: \"points\",\n label: \"Points\",\n value: (sample) => sample.render.points,\n },\n {\n format: formatCount,\n icon: \"repeat\",\n key: \"passes\",\n label: \"Render passes\",\n value: (sample) => sample.render.passes,\n },\n {\n format: formatCount,\n icon: \"box\",\n key: \"geometries\",\n label: \"Geometries\",\n value: (sample) => sample.resources.geometries,\n },\n {\n format: formatCount,\n icon: \"image\",\n key: \"textures\",\n label: \"Textures\",\n value: (sample) => sample.resources.textures,\n },\n {\n format: formatCount,\n icon: \"palette\",\n key: \"shaders\",\n label: \"Shaders\",\n value: (sample) => sample.resources.programs,\n },\n];\n\nconst NUMBER_BY_KEY = new Map(NUMBERS.map((config) => [config.key, config]));\n\n/** Canvas can't read CSS custom properties; the budget line's amber per theme. */\nconst GUIDE_COLOR: Record<ResolvedTheme, string> = {\n dark: \"rgba(245, 158, 11, 0.75)\",\n light: \"rgba(180, 83, 9, 0.7)\",\n};\n\n/** `Budget: at most 16.7 ms` / `at least 57`, or `\"\"` without one. */\nconst budgetTitle = (budgets: ResolvedBudgets, key: BudgetKey) => {\n const limit = budgets.limits[key];\n const config = NUMBER_BY_KEY.get(key);\n\n if (limit === undefined || !config) {\n return \"\";\n }\n\n const bound = isFloor(key) ? \"at least\" : \"at most\";\n\n return `Budget: ${bound} ${config.format(limit)}${config.unit ? ` ${config.unit}` : \"\"}`;\n};\n\n/** The sample value a timing graph shows, `null` when not known. */\nconst timingValue = (sample: Sample, metric: TimingMetric) => {\n if (!isTimingAvailable(sample, metric)) {\n return null;\n }\n\n const value = readTiming(sample, metric);\n\n return metric === \"fps\" && value === 0 ? null : value;\n};\n\ntype GraphCanvas = {\n canvas: HTMLCanvasElement;\n ctx: CanvasRenderingContext2D;\n height: number;\n width: number;\n};\n\ntype HudGraph = GraphCanvas & {\n valueEl: HTMLElement;\n};\n\n/**\n * Host-agnostic widget that paints {@link Sample}s. Mount {@link element}\n * anywhere; the view drives its own throttled repaint. Compact is the floating\n * HUD; full is the checkbox list that chooses what compact shows. Styles are\n * not injected here. Call `injectStyles()` or use `mountPerfHud`.\n */\nclass PerformanceView {\n readonly element: HTMLDivElement;\n readonly settings: HudSettings;\n readonly theme: HudTheme;\n\n private readonly ownsTheme: boolean;\n private readonly unsubscribeTheme: () => void;\n private readonly monitor: PerformanceMonitor;\n private readonly minIntervalMs: number;\n private mode: PerformanceViewMode;\n private readonly graphValueEls = new Map<TimingMetric, HTMLElement>();\n private readonly graphCanvases = new Map<TimingMetric, GraphCanvas>();\n private readonly graphCheckboxes = new Map<TimingMetric, HTMLInputElement>();\n private readonly statValueEls = new Map<BudgetKey, HTMLElement>();\n private readonly statCheckboxes = new Map<BudgetKey, HTMLInputElement>();\n private infoCheckbox!: HTMLInputElement;\n private readonly themeRadios = new Map<ThemeMode, HTMLButtonElement>();\n private footerThreeEl!: HTMLElement;\n private footerHardwareEl!: HTMLElement;\n private footerBackendEl!: HTMLElement;\n private footerGpuEl!: HTMLElement;\n private lastEnvironmentKey = \"\";\n private hudGraphsEl!: HTMLElement;\n private hudGridEl!: HTMLElement;\n private readonly hudGraphs = new Map<TimingMetric, HudGraph>();\n private readonly hudNumberEls = new Map<BudgetKey, HTMLElement>();\n private budgets: ResolvedBudgets;\n private budgetsInput: Budgets | false | undefined;\n private detectedHz: number | null = null;\n private readonly hider = new Hider();\n private readonly hiddenRows = new Map<string, HiddenRow>();\n private rafId: number | null = null;\n private lastPaintAt = 0;\n private readonly resizeObserver: ResizeObserver;\n private readonly unsubscribe: () => void;\n private costsOpen = false;\n private costGroup: CostGroup = \"meshes\";\n private costSort: CostSort = \"calls\";\n private lastCostsAt = -Infinity;\n private costsBodyEl!: HTMLElement;\n private costsListEl!: HTMLElement;\n private readonly costGroupRadios = new Map<CostGroup, HTMLButtonElement>();\n private readonly costSortButtons = new Map<CostSort, HTMLButtonElement>();\n\n constructor(options: PerformanceViewOptions) {\n this.monitor = options.monitor;\n this.settings = options.settings ?? new HudSettings();\n this.ownsTheme = !options.theme;\n this.theme = options.theme ?? new HudTheme();\n // The panel's pick, restored from storage, sits on top of the consumer's mode.\n this.theme.setOverride(this.settings.theme);\n this.mode = options.mode ?? \"full\";\n this.minIntervalMs = 1000 / (options.refreshHz ?? 10);\n this.budgetsInput = options.budgets;\n this.budgets = resolveBudgets(options.budgets);\n this.element = document.createElement(\"div\");\n this.element.className = \"perf-monitor\";\n this.applyModeClass();\n this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());\n this.build();\n this.applyInfoClass();\n this.rebuildHud();\n this.onThemeChanged();\n this.unsubscribe = this.settings.subscribe(() => this.onSelectionChanged());\n this.unsubscribeTheme = this.theme.subscribe(() => this.onThemeChanged());\n }\n\n start() {\n if (this.rafId === null) {\n this.rafId = requestAnimationFrame(this.frame);\n }\n }\n\n stop() {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n setMode(mode: PerformanceViewMode) {\n if (mode === this.mode) {\n return;\n }\n\n this.mode = mode;\n this.applyModeClass();\n this.resizeCanvases();\n }\n\n getMode() {\n return this.mode;\n }\n\n /** Replace the budgets; `false` turns them all off. Applies on the next paint. */\n setBudgets(budgets: Budgets | false | undefined) {\n this.budgetsInput = budgets;\n this.budgets = resolveBudgets(budgets, this.detectedHz);\n this.applyBudgetTitles();\n }\n\n dispose() {\n this.stop();\n this.showAllHidden();\n this.unsubscribe();\n this.unsubscribeTheme();\n\n if (this.ownsTheme) {\n this.theme.dispose();\n }\n\n this.resizeObserver.disconnect();\n this.element.remove();\n }\n\n private frame = (now: number) => {\n this.rafId = requestAnimationFrame(this.frame);\n\n if (now - this.lastPaintAt < this.minIntervalMs) {\n return;\n }\n\n this.lastPaintAt = now;\n this.render();\n };\n\n private render() {\n const sample = this.monitor.getSample();\n const stats = this.monitor.getFrameStats();\n this.followRefresh(stats.refreshHz);\n\n if (this.mode === \"compact\") {\n this.renderHud(sample, stats);\n\n if (this.settings.info) {\n this.renderFooter();\n }\n\n return;\n }\n\n for (const config of TIMINGS) {\n this.paintGraph(\n config,\n sample,\n this.graphValueEls.get(config.metric)!,\n this.graphCanvases.get(config.metric)!,\n \"unavailable\",\n );\n }\n\n for (const config of NUMBERS) {\n this.paintNumber(config, this.statValueEls.get(config.key)!, sample, stats);\n }\n\n if (this.costsOpen && performance.now() - this.lastCostsAt >= COSTS_INTERVAL_MS) {\n this.renderCosts();\n }\n\n this.renderFooter();\n }\n\n private renderCosts() {\n this.lastCostsAt = performance.now();\n const cost = this.monitor.getSceneCost();\n // Hidden rows stay listed, first, so they can be shown again and read their saving.\n const hidden = [...this.hiddenRows.values()].map((row) => this.hiddenCostRow(row));\n\n if (!cost) {\n this.costsListEl.replaceChildren(...hidden, this.costsNote(\"No scene rendered yet.\"));\n\n return;\n }\n\n const entries = (this.costGroup === \"meshes\" ? cost.meshes : cost.materials).filter(\n (entry) => !this.hiddenRows.has(entry.key),\n );\n\n if (this.costSort === \"triangles\") {\n entries.sort((a, b) => b.triangles - a.triangles || b.calls - a.calls);\n }\n\n if (entries.length === 0 && hidden.length === 0) {\n this.costsListEl.replaceChildren(this.costsNote(\"Nothing drawn.\"));\n\n return;\n }\n\n const rows = entries.slice(0, COSTS_ROWS).map((entry) => {\n const row = this.costRowShell(entry);\n const calls = document.createElement(\"span\");\n calls.className = \"perf-monitor__cost-value\";\n calls.textContent = formatCount(entry.calls);\n\n const triangles = document.createElement(\"span\");\n triangles.className = \"perf-monitor__cost-value\";\n triangles.textContent = formatCount(entry.triangles);\n\n row.append(calls, triangles, this.eyeButton(entry, false));\n\n return row;\n });\n\n this.costsListEl.replaceChildren(...hidden, ...rows);\n }\n\n /** Name button (logs the objects) in a row; the caller appends the value cells and the eye. */\n private costRowShell(entry: CostEntry) {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__cost\";\n\n const name = document.createElement(\"button\");\n name.type = \"button\";\n name.className = \"perf-monitor__cost-name\";\n name.title = costTitle(entry);\n name.textContent = entry.label;\n\n const copies = copiesOf(entry);\n\n if (copies) {\n const suffix = document.createElement(\"span\");\n suffix.className = \"perf-monitor__cost-copies\";\n suffix.textContent = ` ${copies}`;\n name.append(suffix);\n }\n\n name.addEventListener(\"click\", () => {\n // oxlint-disable-next-line no-console -- handing the objects to devtools is the feature\n console.log(`three-meter: ${entry.label}`, entry.objects);\n });\n\n row.append(name);\n\n return row;\n }\n\n /** A hidden row: what hiding it saved, measured from the timing history. */\n private hiddenCostRow(hidden: HiddenRow) {\n const row = this.costRowShell(hidden.entry);\n row.classList.add(\"is-hidden\");\n\n const saved = document.createElement(\"span\");\n saved.className = \"perf-monitor__cost-saved\";\n const label = hidden.metric.toUpperCase();\n\n if (performance.now() - hidden.hiddenAt < MEASURE_SETTLE_MS) {\n saved.textContent = \"measuring…\";\n } else {\n const now = meanOfLast(this.monitor.getHistory(hidden.metric), MEASURE_FRAMES);\n const delta = hidden.baseline - now;\n saved.textContent = `${delta >= 0 ? \"−\" : \"+\"}${Math.abs(delta).toFixed(1)} ms ${label}`;\n saved.title = `${label} time went from ${hidden.baseline.toFixed(1)} ms to ${now.toFixed(1)} ms with this hidden, averaged over ${MEASURE_FRAMES} frames each side.`;\n }\n\n row.append(saved, this.eyeButton(hidden.entry, true));\n\n return row;\n }\n\n private eyeButton(entry: CostEntry, hidden: boolean) {\n const eye = document.createElement(\"button\");\n eye.type = \"button\";\n eye.className = \"perf-monitor__cost-eye\";\n eye.setAttribute(\"aria-pressed\", String(hidden));\n const label = hidden ? `Show ${entry.label} again` : `Hide ${entry.label} to measure its cost`;\n eye.title = label;\n eye.setAttribute(\"aria-label\", label);\n eye.append(createIcon(hidden ? \"eyeOff\" : \"eye\", \"perf-monitor__icon\"));\n eye.addEventListener(\"click\", () => (hidden ? this.showRow(entry.key) : this.hideRow(entry)));\n\n return eye;\n }\n\n private hideRow(entry: CostEntry) {\n const sample = this.monitor.getSample();\n const metric = sample.gpu.available ? \"gpu\" : \"cpu\";\n\n this.hiddenRows.set(entry.key, {\n baseline: meanOfLast(this.monitor.getHistory(metric), MEASURE_FRAMES),\n entry,\n hiddenAt: performance.now(),\n metric,\n });\n // The whole scene's matches, not just the on-screen ones in the row, so copies that come\n // into view while hidden stay hidden.\n const all = this.monitor.getObjectsForKey(entry.key);\n this.hider.hide(entry.key, all.length > 0 ? all : entry.objects);\n this.renderCosts();\n }\n\n private showRow(key: string) {\n this.hider.show(key);\n this.hiddenRows.delete(key);\n this.renderCosts();\n }\n\n /** Everything hidden goes back: on closing top costs and on dispose. */\n private showAllHidden() {\n this.hider.restoreAll();\n this.hiddenRows.clear();\n }\n\n private costsNote(text: string) {\n const note = document.createElement(\"div\");\n note.className = \"perf-monitor__costs-note\";\n note.textContent = text;\n\n return note;\n }\n\n /** The monitor caches the environment once its backend settles; until then this re-reads. */\n private renderFooter() {\n const environment = this.monitor.getEnvironment();\n const three = environment.three ? `three r${environment.three}` : \"\";\n const backend = backendLabel(environment);\n const gpu = environment.gpu ?? \"\";\n const key = `${three}\\n${backend}\\n${gpu}`;\n\n if (key === this.lastEnvironmentKey) {\n return;\n }\n\n this.lastEnvironmentKey = key;\n this.footerThreeEl.textContent = three;\n this.footerBackendEl.textContent = backend;\n this.footerBackendEl.hidden = backend === \"\";\n this.footerBackendEl.title = environment.fallback\n ? \"WebGPURenderer couldn't get WebGPU and fell back to WebGL2\"\n : \"\";\n this.footerBackendEl.classList.toggle(\"is-fallback\", environment.fallback);\n this.footerGpuEl.textContent = gpu;\n this.footerGpuEl.title = gpu;\n this.footerHardwareEl.hidden = backend === \"\" && gpu === \"\";\n }\n\n /** Budgets follow the detected refresh rate unless `targetFps` was given. */\n private followRefresh(hz: number | null) {\n if (hz === this.detectedHz) {\n return;\n }\n\n this.detectedHz = hz;\n this.budgets = resolveBudgets(this.budgetsInput, hz);\n this.applyBudgetTitles();\n }\n\n private renderHud(sample: Sample, stats: FrameStats) {\n for (const [key, element] of this.hudNumberEls) {\n const config = NUMBER_BY_KEY.get(key);\n\n if (config) {\n this.paintNumber(config, element, sample, stats);\n }\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n const config = TIMINGS.find((timing) => timing.metric === metric)!;\n this.paintGraph(config, sample, graph.valueEl, graph, \"—\");\n }\n }\n\n private paintNumber(\n config: NumberConfig,\n element: HTMLElement,\n sample: Sample,\n stats: FrameStats,\n ) {\n const value = config.value(sample, stats, this.budgets.frameMs);\n element.textContent = value === null ? \"—\" : config.format(value);\n element.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.key, value));\n }\n\n /** Value and sparkline, with the budget line drawn once the series reaches it. */\n private paintGraph(\n config: TimingConfig,\n sample: Sample,\n valueEl: HTMLElement,\n graph: GraphCanvas,\n missing: string,\n ) {\n const value = timingValue(sample, config.metric);\n const available = isTimingAvailable(sample, config.metric);\n const limit = this.budgets.limits[config.metric];\n\n valueEl.textContent = available\n ? this.formatTiming(config, readTiming(sample, config.metric))\n : missing;\n valueEl.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.metric, value));\n\n drawSparkline(\n graph.ctx,\n graph.width,\n graph.height,\n available ? this.monitor.getHistory(config.metric) : [],\n config.style[this.theme.resolved],\n limit === undefined ? undefined : { color: GUIDE_COLOR[this.theme.resolved], value: limit },\n );\n }\n\n /** Each value's tooltip names its budget, so amber explains itself. */\n private applyBudgetTitles() {\n for (const [key, element] of this.statValueEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [key, element] of this.hudNumberEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [metric, element] of this.graphValueEls) {\n element.title = budgetTitle(this.budgets, metric);\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n graph.valueEl.title = budgetTitle(this.budgets, metric);\n }\n }\n\n private formatTiming(config: TimingConfig, value: number) {\n return `${config.format(value)}${config.unit ? ` ${config.unit}` : \"\"}`;\n }\n\n private build() {\n const hud = document.createElement(\"div\");\n hud.className = \"perf-monitor__hud\";\n this.hudGraphsEl = document.createElement(\"div\");\n this.hudGraphsEl.className = \"perf-monitor__hud-graphs\";\n this.hudGridEl = document.createElement(\"div\");\n this.hudGridEl.className = \"perf-monitor__hud-grid\";\n hud.append(this.hudGraphsEl, this.hudGridEl);\n\n // Option rows read icon, label, control; the stat rows below keep the\n // checkbox first, so the two groups scan differently on purpose.\n const options = document.createElement(\"div\");\n options.className = \"perf-monitor__section perf-monitor__options\";\n\n const themeRow = document.createElement(\"div\");\n themeRow.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const themeLabel = document.createElement(\"span\");\n themeLabel.className = \"perf-monitor__label\";\n themeLabel.textContent = \"theme\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Panel theme\");\n\n for (const option of THEME_OPTIONS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", \"false\");\n radio.setAttribute(\"aria-label\", option.label);\n radio.title = option.label;\n radio.append(createIcon(option.icon, \"perf-monitor__icon\"));\n radio.addEventListener(\"click\", () => this.settings.setTheme(option.mode));\n this.themeRadios.set(option.mode, radio);\n segment.append(radio);\n }\n\n themeRow.append(createIcon(\"contrast\", \"perf-monitor__icon\"), themeLabel, segment);\n\n // View-local, not a setting: it's a way to learn the panel, not a preference.\n const explainRow = document.createElement(\"label\");\n explainRow.className = \"perf-monitor__row\";\n\n const explainCheckbox = this.buildCheckbox(false, \"Explain each metric\", (on) =>\n this.element.classList.toggle(\"perf-monitor--explain\", on),\n );\n\n const explainLabel = document.createElement(\"span\");\n explainLabel.className = \"perf-monitor__label\";\n explainLabel.textContent = \"explain metrics\";\n\n explainRow.append(createIcon(\"help\", \"perf-monitor__icon\"), explainLabel, explainCheckbox);\n // Dim on leave is a disc on the floating HUD (`mountPerfHud`), beside drag and expand.\n options.append(themeRow, explainRow, this.buildReportRow());\n\n const graphs = document.createElement(\"div\");\n graphs.className = \"perf-monitor__graphs\";\n\n for (const config of TIMINGS) {\n const graph = document.createElement(\"div\");\n graph.className = \"perf-monitor__graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n // The whole overlay is the label, so a click anywhere on the graph toggles it.\n const overlay = document.createElement(\"label\");\n overlay.className = \"perf-monitor__graph-overlay perf-monitor__graph-overlay--toggle\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isGraphEnabled(config.metric),\n `Show ${config.label} graph in HUD`,\n (on) => this.settings.setGraphEnabled(config.metric, on),\n );\n this.graphCheckboxes.set(config.metric, checkbox);\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n this.graphValueEls.set(config.metric, value);\n\n const tail = document.createElement(\"span\");\n tail.className = \"perf-monitor__graph-head\";\n tail.append(value, checkbox);\n\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n overlay.append(head, tail);\n graph.append(canvas, overlay);\n graphs.append(graph);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.graphCanvases.set(config.metric, { canvas, ctx, height: 0, width: 0 });\n this.resizeObserver.observe(graph);\n }\n }\n\n const stats = document.createElement(\"div\");\n stats.className = \"perf-monitor__section\";\n\n for (const config of NUMBERS) {\n const rowEl = document.createElement(\"label\");\n rowEl.className = \"perf-monitor__row\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isNumberEnabled(config.key),\n `Show ${config.label} in HUD`,\n (on) => this.settings.setNumberEnabled(config.key, on),\n );\n this.statCheckboxes.set(config.key, checkbox);\n\n const help = METRIC_HELP[config.key] ?? \"\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = config.label;\n label.title = help;\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__value\";\n value.textContent = \"—\";\n this.statValueEls.set(config.key, value);\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = help;\n\n rowEl.append(createIcon(config.icon, \"perf-monitor__icon\"), label, value, checkbox, hint);\n stats.append(rowEl);\n }\n\n // Icon, rows, checkbox like the stat rows; icon and checkbox hide in compact, the rows don't.\n const footer = document.createElement(\"div\");\n footer.className = \"perf-monitor__footer\";\n\n this.infoCheckbox = this.buildCheckbox(\n this.settings.info,\n \"Show version and environment in HUD\",\n (on) => this.settings.setInfo(on),\n );\n\n const footerBody = document.createElement(\"div\");\n footerBody.className = \"perf-monitor__footer-body\";\n\n // Software on the first row, hardware on the second.\n const softwareRow = document.createElement(\"div\");\n softwareRow.className = \"perf-monitor__footer-row\";\n\n const release = document.createElement(\"a\");\n release.className = \"perf-monitor__link\";\n release.href = RELEASE_URL;\n release.target = \"_blank\";\n release.rel = \"noopener noreferrer\";\n release.title = `Release notes for v${version}`;\n release.textContent = `three-meter v${version}`;\n\n this.footerThreeEl = document.createElement(\"span\");\n softwareRow.append(release, this.footerThreeEl);\n\n this.footerHardwareEl = document.createElement(\"div\");\n this.footerHardwareEl.className = \"perf-monitor__footer-row\";\n this.footerHardwareEl.hidden = true;\n\n this.footerBackendEl = document.createElement(\"span\");\n this.footerBackendEl.className = \"perf-monitor__badge\";\n\n this.footerGpuEl = document.createElement(\"span\");\n this.footerGpuEl.className = \"perf-monitor__footer-gpu\";\n\n this.footerHardwareEl.append(this.footerBackendEl, this.footerGpuEl);\n footerBody.append(softwareRow, this.footerHardwareEl);\n footer.append(createIcon(\"info\", \"perf-monitor__icon\"), footerBody, this.infoCheckbox);\n\n this.element.append(hud, options, graphs, stats, this.buildCosts(), footer);\n }\n\n private rebuildHud() {\n const { selection } = this.settings;\n\n for (const graph of this.hudGraphs.values()) {\n this.resizeObserver.unobserve(graph.canvas.parentElement ?? graph.canvas);\n }\n\n this.hudGraphs.clear();\n this.hudNumberEls.clear();\n this.hudGraphsEl.replaceChildren();\n this.hudGridEl.replaceChildren();\n\n for (const config of TIMINGS) {\n if (!selection.graphs.has(config.metric)) {\n continue;\n }\n\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__hud-graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"perf-monitor__graph-overlay\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n\n overlay.append(head, value);\n row.append(canvas, overlay);\n this.hudGraphsEl.append(row);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.hudGraphs.set(config.metric, { canvas, ctx, height: 0, valueEl: value, width: 0 });\n this.resizeObserver.observe(row);\n }\n }\n\n for (const config of NUMBERS) {\n if (!selection.numbers.has(config.key)) {\n continue;\n }\n\n const item = document.createElement(\"div\");\n item.className = \"perf-monitor__hud-item\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__hud-label\";\n label.title = METRIC_HELP[config.key] ?? \"\";\n label.append(createIcon(config.icon, \"perf-monitor__icon\"), config.label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__hud-value\";\n value.textContent = \"—\";\n\n item.append(label, value);\n this.hudGridEl.append(item);\n this.hudNumberEls.set(config.key, value);\n }\n\n this.applyBudgetTitles();\n this.resizeCanvases();\n }\n\n private onSelectionChanged() {\n for (const [metric, checkbox] of this.graphCheckboxes) {\n checkbox.checked = this.settings.isGraphEnabled(metric);\n }\n\n for (const [key, checkbox] of this.statCheckboxes) {\n checkbox.checked = this.settings.isNumberEnabled(key);\n }\n\n this.infoCheckbox.checked = this.settings.info;\n this.applyInfoClass();\n this.theme.setOverride(this.settings.theme);\n this.rebuildHud();\n }\n\n /** Repaint the palette and mark the radio matching what applies (pick, else consumer mode). */\n private onThemeChanged() {\n applyTheme(this.element, this.theme);\n\n for (const [mode, radio] of this.themeRadios) {\n radio.setAttribute(\"aria-checked\", String(mode === this.theme.effective));\n }\n }\n\n /** \"top costs\": a toggle row, then meshes / materials and a sortable top five. */\n private buildCosts() {\n const section = document.createElement(\"div\");\n section.className = \"perf-monitor__section perf-monitor__costs\";\n\n const toggleRow = document.createElement(\"label\");\n toggleRow.className = \"perf-monitor__row\";\n toggleRow.title = COSTS_HELP;\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"top costs\";\n\n const checkbox = this.buildCheckbox(false, \"Show the scene's biggest draw costs\", (on) => {\n this.costsOpen = on;\n this.costsBodyEl.hidden = !on;\n\n if (!on) {\n this.showAllHidden();\n }\n\n if (on) {\n this.renderCosts();\n }\n });\n\n toggleRow.append(createIcon(\"flame\", \"perf-monitor__icon\"), label, checkbox);\n\n this.costsBodyEl = document.createElement(\"div\");\n this.costsBodyEl.className = \"perf-monitor__costs-body\";\n this.costsBodyEl.hidden = true;\n\n const head = document.createElement(\"div\");\n head.className = \"perf-monitor__cost perf-monitor__cost--head\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Group costs by\");\n\n for (const option of COST_GROUPS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option perf-monitor__segment-option--text\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", String(option.group === this.costGroup));\n radio.textContent = option.label;\n radio.addEventListener(\"click\", () => {\n this.costGroup = option.group;\n\n for (const [group, button] of this.costGroupRadios) {\n button.setAttribute(\"aria-checked\", String(group === option.group));\n }\n\n this.renderCosts();\n });\n this.costGroupRadios.set(option.group, radio);\n segment.append(radio);\n }\n\n head.append(segment);\n\n for (const sort of [\"calls\", \"triangles\"] as const) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__cost-sort\";\n button.textContent = sort === \"calls\" ? \"calls\" : \"tris\";\n button.title = `Sort by ${sort === \"calls\" ? \"draw calls\" : \"triangles\"}`;\n button.setAttribute(\"aria-pressed\", String(sort === this.costSort));\n button.addEventListener(\"click\", () => {\n this.costSort = sort;\n\n for (const [key, other] of this.costSortButtons) {\n other.setAttribute(\"aria-pressed\", String(key === sort));\n }\n\n this.renderCosts();\n });\n this.costSortButtons.set(sort, button);\n head.append(button);\n }\n\n // Keeps the header's columns over the rows', which end in an eye button.\n head.append(document.createElement(\"span\"));\n\n this.costsListEl = document.createElement(\"div\");\n this.costsListEl.className = \"perf-monitor__costs-list\";\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = COSTS_HELP;\n\n this.costsBodyEl.append(head, this.costsListEl, hint);\n section.append(toggleRow, this.costsBodyEl);\n\n return section;\n }\n\n private buildReportRow() {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"report\";\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__button\";\n button.title = \"Copy versions, GPU and current metrics as Markdown for a bug report\";\n button.textContent = \"copy\";\n\n let resetTimer = 0;\n\n const flash = (text: string) => {\n button.textContent = text;\n window.clearTimeout(resetTimer);\n resetTimer = window.setTimeout(() => {\n button.textContent = \"copy\";\n }, COPY_FEEDBACK_MS);\n };\n\n button.addEventListener(\"click\", () => {\n void copyText(formatReport(this.monitor)).then((copied) =>\n flash(copied ? \"copied\" : \"failed\"),\n );\n });\n\n row.append(createIcon(\"clipboard\", \"perf-monitor__icon\"), label, button);\n\n return row;\n }\n\n private buildCheckbox(checked: boolean, title: string, onChange: (enabled: boolean) => void) {\n const checkbox = document.createElement(\"input\");\n checkbox.type = \"checkbox\";\n checkbox.className = \"perf-monitor__checkbox\";\n checkbox.checked = checked;\n checkbox.title = title;\n checkbox.setAttribute(\"aria-label\", title);\n checkbox.addEventListener(\"change\", () => onChange(checkbox.checked));\n\n return checkbox;\n }\n\n private applyInfoClass() {\n this.element.classList.toggle(\"perf-monitor--info\", this.settings.info);\n }\n\n private applyModeClass() {\n this.element.classList.toggle(\"perf-monitor--compact\", this.mode === \"compact\");\n this.element.classList.toggle(\"perf-monitor--full\", this.mode === \"full\");\n }\n\n private resizeCanvases() {\n const dpr = window.devicePixelRatio || 1;\n\n const sizeAll = (graphs: Iterable<GraphCanvas>) => {\n for (const graph of graphs) {\n const { canvas, ctx } = graph;\n const width = canvas.clientWidth;\n const height = canvas.clientHeight;\n\n if (width === 0 || height === 0) {\n continue;\n }\n\n canvas.width = Math.round(width * dpr);\n canvas.height = Math.round(height * dpr);\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n graph.width = width;\n graph.height = height;\n }\n };\n\n sizeAll(this.graphCanvases.values());\n sizeAll(this.hudGraphs.values());\n }\n}\n\nexport { PerformanceView };\nexport type { PerformanceViewMode, PerformanceViewOptions };\n","/**\n * The floating HUD: the metrics card in the shared dev-panel frame\n * (`mountDevPanel`: docked host, brand label, drag / compact-full / dim discs).\n * The dim disc flips `settings.dim`; the palette comes from `theme`. This is what the React\n * `PerfHud` wraps; a vanilla three app calls it directly.\n */\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { Budgets } from \"./budgets.ts\";\nimport { mountDevPanel } from \"./dev-panel.ts\";\nimport type { DefaultPlacement } from \"./dock-panel.ts\";\nimport { DEFAULT_STORAGE_KEY, HudSettings } from \"./hud-settings.ts\";\nimport { PerformanceView, type PerformanceViewMode } from \"./performance-view.ts\";\nimport { HudTheme, type ThemeMode } from \"./theme.ts\";\n\ntype MountPerfHudOptions = {\n /** Start compact (the card) or full (the checkbox list). Default `compact`. */\n mode?: PerformanceViewMode;\n /**\n * `dark`, `light`, or `system` to follow the OS preference. Default\n * `system`. Change it later with the handle's `setTheme`. A pick made in\n * the panel's own theme row (kept in `settings`) sits on top of this.\n */\n theme?: ThemeMode;\n /** localStorage key for the selection and the dock. `null` disables persistence. */\n storageKey?: string | null;\n /** Bring your own; otherwise one is created from `storageKey`. */\n settings?: HudSettings;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host element goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`.\n * Timing budgets default from `targetFps` (60); `false` turns them all off.\n * Change later with the handle's `setBudgets`.\n */\n budgets?: Budgets | false;\n /** Accessible name of the panel. Default `Performance`. */\n label?: string;\n};\n\ntype PerfHudHandle = {\n element: HTMLDivElement;\n view: PerformanceView;\n settings: HudSettings;\n /**\n * `mode` is the consumer layer, `override` the panel's pick, `effective`\n * whichever applies, `resolved` the `dark` / `light` on screen. Subscribe\n * for changes, including the OS preference moving under `system`.\n */\n theme: HudTheme;\n getMode: () => PerformanceViewMode;\n setMode: (mode: PerformanceViewMode) => void;\n /** The consumer layer. The panel's pick, if any, is `settings.theme`. */\n getTheme: () => ThemeMode;\n /** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */\n setTheme: (mode: ThemeMode) => void;\n /** Replace the budgets; `false` turns them all off. */\n setBudgets: (budgets: Budgets | false | undefined) => void;\n dispose: () => void;\n};\n\nconst mountPerfHud = (\n monitor: PerformanceMonitor,\n options: MountPerfHudOptions = {},\n): PerfHudHandle => {\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const settings = options.settings ?? new HudSettings({ storageKey });\n const theme = new HudTheme(options.theme ?? \"system\");\n\n const view = new PerformanceView({\n budgets: options.budgets,\n mode: options.mode ?? \"compact\",\n monitor,\n refreshHz: options.refreshHz,\n settings,\n theme,\n });\n\n // After the view: constructing it restores the panel's stored theme pick.\n const panel = mountDevPanel({\n brand: \"three-meter\",\n content: view.element,\n defaultPlacement: options.defaultPlacement,\n dim: {\n get: () => settings.dim,\n set: (on) => settings.setDim(on),\n subscribe: (listener) => settings.subscribe(listener),\n },\n injectStyles: options.injectStyles,\n label: options.label ?? \"Performance\",\n onToggle: () => setMode(view.getMode() === \"compact\" ? \"full\" : \"compact\"),\n parent: options.parent,\n storageKey,\n theme,\n toggleLabel: \"Toggle full performance metrics\",\n });\n const host = panel.element;\n\n const applyMode = () => {\n host.classList.toggle(\"perf-hud--full\", view.getMode() === \"full\");\n };\n\n const setMode = (mode: PerformanceViewMode) => {\n view.setMode(mode);\n applyMode();\n panel.refresh();\n };\n\n applyMode();\n view.start();\n\n return {\n dispose: () => {\n panel.dispose();\n view.dispose();\n theme.dispose();\n },\n element: host,\n getMode: () => view.getMode(),\n setBudgets: (budgets) => view.setBudgets(budgets),\n setMode,\n getTheme: () => theme.mode,\n setTheme: (mode: ThemeMode) => theme.setMode(mode),\n settings,\n theme,\n view,\n };\n};\n\nexport { mountPerfHud };\nexport type { MountPerfHudOptions, PerfHudHandle };\n"],"mappings":"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"}
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PerformanceMonitor } from "./performance-monitor-BqnoiLEs.js";
|
|
2
|
-
import { t as mountPerfHud } from "./mount-perf-hud-
|
|
2
|
+
import { t as mountPerfHud } from "./mount-perf-hud-BB8btwk9.js";
|
|
3
3
|
import { useEffect, useRef } from "react";
|
|
4
4
|
import { useFrame, useThree } from "@react-three/fiber";
|
|
5
5
|
//#region src/react/monitor-store.ts
|
package/dist/ui.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PERF_HUD_STYLES, c as DEFAULT_STORAGE_KEY, d as THEME_MODES, f as isThemeMode, i as mountDevPanel, l as HudSettings, n as PerformanceView, o as injectStyles, p as resolveBudgets, r as formatReport, s as dockPanel, t as mountPerfHud, u as HudTheme } from "./mount-perf-hud-
|
|
1
|
+
import { a as PERF_HUD_STYLES, c as DEFAULT_STORAGE_KEY, d as THEME_MODES, f as isThemeMode, i as mountDevPanel, l as HudSettings, n as PerformanceView, o as injectStyles, p as resolveBudgets, r as formatReport, s as dockPanel, t as mountPerfHud, u as HudTheme } from "./mount-perf-hud-BB8btwk9.js";
|
|
2
2
|
export { DEFAULT_STORAGE_KEY, HudSettings, HudTheme, PERF_HUD_STYLES, PerformanceView, THEME_MODES, dockPanel, formatReport, injectStyles, isThemeMode, mountDevPanel, mountPerfHud, resolveBudgets };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mount-perf-hud-JJIFHJCt.js","names":[],"sources":["../src/ui/budgets.ts","../src/ui/theme.ts","../src/ui/hud-settings.ts","../src/ui/dock-panel.ts","../src/ui/icons.ts","../src/ui/styles.ts","../src/ui/dev-panel.ts","../package.json","../src/ui/clipboard.ts","../src/ui/format.ts","../src/ui/hider.ts","../src/ui/metric-help.ts","../src/ui/report.ts","../src/ui/sparkline.ts","../src/ui/performance-view.ts","../src/ui/mount-perf-hud.ts"],"sourcesContent":["/**\n * Budgets: the value each metric should stay within. A reading past its\n * budget turns amber in the card, and the FPS / CPU / GPU graphs draw the\n * line once the series reaches it. `fps`, `low` and `headroom` are floors\n * (worse below); everything else is a ceiling (worse above).\n *\n * Timing budgets default from `targetFps`: FPS at least 95% of it, 1% low at\n * least half, CPU and GPU within one frame, p99 within one and a half,\n * headroom at least zero. Without a `targetFps`, the detected refresh rate is\n * used, then 60 until one is detected.\n * Counts have no default, since what's reasonable depends on the scene. Pass\n * `null` for a key to drop its default, or `false` for no budgets at all.\n */\ntype BudgetKey =\n | \"calls\"\n | \"cpu\"\n | \"fps\"\n | \"geometries\"\n | \"gpu\"\n | \"headroom\"\n | \"hitches\"\n | \"lines\"\n | \"low\"\n | \"p99\"\n | \"passes\"\n | \"points\"\n | \"refresh\"\n | \"shaders\"\n | \"textures\"\n | \"triangles\";\n\ntype Budgets = Partial<Record<BudgetKey, number | null>> & {\n /** The frame rate the defaults are derived from. Default: the detected refresh rate, else 60. */\n targetFps?: number;\n};\n\ntype ResolvedBudgets = {\n /** Frame budget in ms, `1000 / targetFps`. */\n frameMs: number;\n /** The rate the timing budgets came from: `targetFps`, the detected refresh, or 60. */\n targetFps: number;\n limits: Partial<Record<BudgetKey, number>>;\n};\n\nconst DEFAULT_TARGET_FPS = 60;\nconst FLOORS = new Set<BudgetKey>([\"fps\", \"headroom\", \"low\"]);\n\n/** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */\nconst resolveBudgets = (\n budgets: Budgets | false | undefined,\n detectedHz: number | null = null,\n): ResolvedBudgets => {\n const targetFps = (budgets && budgets.targetFps) || detectedHz || DEFAULT_TARGET_FPS;\n const frameMs = 1000 / targetFps;\n\n if (budgets === false) {\n return { frameMs, limits: {}, targetFps };\n }\n\n const merged: Budgets = {\n cpu: frameMs,\n fps: targetFps * 0.95,\n gpu: frameMs,\n headroom: 0,\n low: targetFps / 2,\n p99: frameMs * 1.5,\n ...budgets,\n };\n const limits: Partial<Record<BudgetKey, number>> = {};\n\n for (const [key, limit] of Object.entries(merged)) {\n if (key !== \"targetFps\" && typeof limit === \"number\" && Number.isFinite(limit)) {\n limits[key as BudgetKey] = limit;\n }\n }\n\n return { frameMs, limits, targetFps };\n};\n\nconst isFloor = (key: BudgetKey) => FLOORS.has(key);\n\n/** `null` means the value isn't known yet (no frames, no GPU timer), which is never over. */\nconst isOverBudget = (resolved: ResolvedBudgets, key: BudgetKey, value: number | null) => {\n const limit = resolved.limits[key];\n\n if (limit === undefined || value === null) {\n return false;\n }\n\n return isFloor(key) ? value < limit : value > limit;\n};\n\nexport { isFloor, isOverBudget, resolveBudgets };\nexport type { BudgetKey, Budgets, ResolvedBudgets };\n","/**\n * Dark, light, or follow the OS. One instance per HUD: the floating host and\n * the card inside it both paint `resolved` onto `data-theme`, and the\n * stylesheet picks the palette from that attribute.\n *\n * Two layers. `mode` is what the consumer asked for (the `theme` option,\n * `setTheme`). `override` is what the person using the HUD picked in the\n * panel, kept in `HudSettings`; it wins while set. `effective` is whichever\n * applies, and `system` there tracks `prefers-color-scheme` for as long as\n * the instance lives.\n */\ntype ThemeMode = \"dark\" | \"light\" | \"system\";\ntype ResolvedTheme = Exclude<ThemeMode, \"system\">;\n\nconst THEME_MODES: readonly ThemeMode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_SCHEME_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isThemeMode = (value: unknown): value is ThemeMode =>\n typeof value === \"string\" && (THEME_MODES as readonly string[]).includes(value);\n\nclass HudTheme {\n private current: ThemeMode;\n private userPick: ThemeMode | null = null;\n private readonly listeners = new Set<() => void>();\n private media: MediaQueryList | null = null;\n private readonly onSchemeChange = () => this.notify();\n\n constructor(mode: ThemeMode = \"system\") {\n this.current = mode;\n this.watch();\n }\n\n /** What the consumer asked for. */\n get mode(): ThemeMode {\n return this.current;\n }\n\n /** What the person using the HUD picked in the panel, or `null` for no pick. */\n get override(): ThemeMode | null {\n return this.userPick;\n }\n\n /** The layer that applies: the user's pick if there is one, else the consumer's mode. */\n get effective(): ThemeMode {\n return this.userPick ?? this.current;\n }\n\n /**\n * `dark` or `light`, with `system` resolved against the OS preference.\n * Where `matchMedia` is missing, `system` falls back to `dark`, the HUD's\n * original palette.\n */\n get resolved(): ResolvedTheme {\n const effective = this.effective;\n\n if (effective !== \"system\") {\n return effective;\n }\n\n if (!this.media) {\n return \"dark\";\n }\n\n return this.media.matches ? \"dark\" : \"light\";\n }\n\n setMode(mode: ThemeMode) {\n if (mode === this.current) {\n return;\n }\n\n this.current = mode;\n this.watch();\n this.notify();\n }\n\n setOverride(mode: ThemeMode | null) {\n if (mode === this.userPick) {\n return;\n }\n\n this.userPick = mode;\n this.watch();\n this.notify();\n }\n\n /** Fires on `setMode`, `setOverride` and, in `system`, whenever the OS preference flips. */\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Stop tracking the OS preference and drop every subscriber. */\n dispose() {\n this.unwatch();\n this.listeners.clear();\n }\n\n private watch() {\n if (this.effective !== \"system\") {\n this.unwatch();\n\n return;\n }\n\n if (this.media || typeof matchMedia !== \"function\") {\n return;\n }\n\n this.media = matchMedia(DARK_SCHEME_QUERY);\n this.media.addEventListener(\"change\", this.onSchemeChange);\n }\n\n private unwatch() {\n this.media?.removeEventListener(\"change\", this.onSchemeChange);\n this.media = null;\n }\n\n private notify() {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\n/** Paint the resolved theme onto an element for the stylesheet to key off. */\nconst applyTheme = (element: HTMLElement, theme: HudTheme) => {\n element.dataset.theme = theme.resolved;\n};\n\nexport { applyTheme, HudTheme, isThemeMode, THEME_MODES };\nexport type { ResolvedTheme, ThemeMode };\n","/**\n * Which metrics the compact HUD shows, whether it shows the environment\n * footer, the dim-on-leave toggle, and the theme\n * the person using the HUD picked in the panel (`null` until they pick one,\n * so the consumer's `theme` option applies). One instance per HUD; the\n * full-mode controls and the compact card stay in sync through it. Persists\n * to `localStorage` under `storageKey` (pass `null` to keep it in memory only).\n */\nimport type { TimingMetric } from \"../core/types.ts\";\nimport { isThemeMode, type ThemeMode } from \"./theme.ts\";\n\ntype HudSelection = {\n dim: boolean;\n graphs: ReadonlySet<TimingMetric>;\n /** Version, three revision, backend and GPU footer in the compact HUD. */\n info: boolean;\n numbers: ReadonlySet<string>;\n theme: ThemeMode | null;\n};\n\ntype HudDefaults = {\n dim?: boolean;\n graphs?: readonly TimingMetric[];\n info?: boolean;\n numbers?: readonly string[];\n};\n\ntype HudSettingsOptions = {\n /** localStorage key. Default `three-meter`; `null` disables persistence. */\n storageKey?: string | null;\n /** What a first visit shows. */\n defaults?: HudDefaults;\n};\n\ntype MutableSelection = {\n dim: boolean;\n graphs: Set<TimingMetric>;\n info: boolean;\n numbers: Set<string>;\n theme: ThemeMode | null;\n};\n\nconst DEFAULT_STORAGE_KEY = \"three-meter\";\nconst DEFAULT_DIM = false;\nconst DEFAULT_INFO = false;\nconst DEFAULT_NUMBERS: readonly string[] = [\"fps\", \"calls\", \"cpu\", \"gpu\"];\nconst DEFAULT_GRAPHS: readonly TimingMetric[] = [];\nconst TIMING_METRICS = new Set<TimingMetric>([\"fps\", \"cpu\", \"gpu\"]);\n\nconst isTimingMetric = (value: string): value is TimingMetric =>\n TIMING_METRICS.has(value as TimingMetric);\n\nclass HudSettings {\n private readonly storageKey: string | null;\n private readonly defaults: Required<HudDefaults>;\n private readonly listeners = new Set<() => void>();\n private readonly state: MutableSelection;\n\n constructor(options: HudSettingsOptions = {}) {\n this.storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n this.defaults = {\n dim: options.defaults?.dim ?? DEFAULT_DIM,\n graphs: options.defaults?.graphs ?? DEFAULT_GRAPHS,\n info: options.defaults?.info ?? DEFAULT_INFO,\n numbers: options.defaults?.numbers ?? DEFAULT_NUMBERS,\n };\n this.state = this.load();\n }\n\n get selection(): HudSelection {\n return this.state;\n }\n\n get dim() {\n return this.state.dim;\n }\n\n setDim(enabled: boolean) {\n this.state.dim = enabled;\n this.persistAndNotify();\n }\n\n get info() {\n return this.state.info;\n }\n\n setInfo(enabled: boolean) {\n this.state.info = enabled;\n this.persistAndNotify();\n }\n\n /** The user's pick in the panel; `null` means none, so the consumer's option applies. */\n get theme() {\n return this.state.theme;\n }\n\n setTheme(mode: ThemeMode | null) {\n this.state.theme = mode;\n this.persistAndNotify();\n }\n\n isNumberEnabled(key: string) {\n return this.state.numbers.has(key);\n }\n\n isGraphEnabled(metric: TimingMetric) {\n return this.state.graphs.has(metric);\n }\n\n setNumberEnabled(key: string, enabled: boolean) {\n if (enabled) {\n this.state.numbers.add(key);\n } else {\n this.state.numbers.delete(key);\n }\n\n this.persistAndNotify();\n }\n\n setGraphEnabled(metric: TimingMetric, enabled: boolean) {\n if (enabled) {\n this.state.graphs.add(metric);\n } else {\n this.state.graphs.delete(metric);\n }\n\n this.persistAndNotify();\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private load(): MutableSelection {\n const fallback = (): MutableSelection => ({\n dim: this.defaults.dim,\n graphs: new Set(this.defaults.graphs),\n info: this.defaults.info,\n numbers: new Set(this.defaults.numbers),\n theme: null,\n });\n\n if (this.storageKey === null) {\n return fallback();\n }\n\n try {\n const raw = localStorage.getItem(this.storageKey);\n\n if (!raw) {\n return fallback();\n }\n\n const parsed = JSON.parse(raw) as {\n dim?: boolean;\n graphs?: string[];\n info?: boolean;\n numbers?: string[];\n theme?: unknown;\n };\n\n return {\n dim: parsed.dim ?? this.defaults.dim,\n graphs: new Set((parsed.graphs ?? this.defaults.graphs).filter(isTimingMetric)),\n info: parsed.info ?? this.defaults.info,\n numbers: new Set(parsed.numbers ?? this.defaults.numbers),\n theme: isThemeMode(parsed.theme) ? parsed.theme : null,\n };\n } catch {\n // Malformed or unavailable storage → defaults.\n return fallback();\n }\n }\n\n private persistAndNotify() {\n if (this.storageKey !== null) {\n try {\n localStorage.setItem(\n this.storageKey,\n JSON.stringify({\n dim: this.state.dim,\n graphs: [...this.state.graphs],\n info: this.state.info,\n numbers: [...this.state.numbers],\n // Omitted while null: JSON.stringify drops undefined.\n theme: this.state.theme ?? undefined,\n }),\n );\n } catch {\n // In-memory selection still drives the live views.\n }\n }\n\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\nexport { DEFAULT_STORAGE_KEY, HudSettings };\nexport type { HudDefaults, HudSelection, HudSettingsOptions };\n","/**\n * Dock a fixed-position panel to a screen edge. Dragging the handle snaps it\n * to the nearest edge on drop; the edge and the offset along it persist under\n * `<storageKey>:placement`. A stored box whose centre sits outside the current\n * viewport is re-docked to whichever edge that box is closest to.\n */\nimport { DEFAULT_STORAGE_KEY } from \"./hud-settings.ts\";\n\nconst INSET = 10;\n/** Room for the stacked control discs on the inward side of the panel. */\nconst SIDE_CLEARANCE = 26;\nconst DRAG_THRESHOLD = 4;\nconst EDGES = [\"left\", \"right\", \"top\", \"bottom\"] as const;\n\ntype ScreenEdge = (typeof EDGES)[number];\n\ntype Placement = {\n /** Top (left/right) or left (top/bottom) of the host, in CSS pixels. */\n along: number;\n edge: ScreenEdge;\n};\n\ntype Box = {\n height: number;\n left: number;\n top: number;\n width: number;\n};\n\ntype DockAlign = \"start\" | \"center\" | \"end\";\n\ntype DefaultPlacement = {\n edge: ScreenEdge;\n /** Where along that edge on a first visit. Default `center`. */\n align?: DockAlign;\n};\n\ntype DockPanelOptions = {\n /** The drag grip. Without one the panel still docks and re-docks on resize. */\n handle?: HTMLElement | null;\n /** First-visit position, before anything is stored. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Shares the HUD's key; `null` disables persistence. Default `three-meter`. */\n storageKey?: string | null;\n};\n\ntype DockHandle = {\n /** Re-resolve against the current viewport and panel size (after a mode change, say). */\n refresh: () => Placement;\n dispose: () => void;\n};\n\nconst viewportBox = (): Box => ({\n height: window.innerHeight,\n left: 0,\n top: 0,\n width: window.innerWidth,\n});\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst panelBox = (element: HTMLElement): Box => {\n const rect = element.getBoundingClientRect();\n\n return {\n height: rect.height || element.offsetHeight,\n left: rect.left,\n top: rect.top,\n width: rect.width || element.offsetWidth,\n };\n};\n\nconst clampAlong = (edge: ScreenEdge, along: number, view: Box, panel: Box) => {\n if (edge === \"left\" || edge === \"right\") {\n return clamp(along, INSET, Math.max(INSET, view.height - panel.height - INSET));\n }\n\n // Tools sit on the right (inward) unless the panel is on the right edge.\n return clamp(along, INSET, Math.max(INSET, view.width - panel.width - INSET - SIDE_CLEARANCE));\n};\n\nconst ghostBox = (placement: Placement, panel: Box, view: Box): Box => {\n switch (placement.edge) {\n case \"left\":\n return { height: panel.height, left: INSET, top: placement.along, width: panel.width };\n case \"right\":\n return {\n height: panel.height,\n left: view.width - INSET - panel.width,\n top: placement.along,\n width: panel.width,\n };\n case \"top\":\n return { height: panel.height, left: placement.along, top: INSET, width: panel.width };\n case \"bottom\":\n return {\n height: panel.height,\n left: placement.along,\n top: view.height - INSET - panel.height,\n width: panel.width,\n };\n }\n};\n\nconst centerOutside = (box: Box, view: Box) => {\n const x = box.left + box.width / 2;\n const y = box.top + box.height / 2;\n\n return x < 0 || y < 0 || x > view.width || y > view.height;\n};\n\nconst snapBoxToEdge = (box: Box, view: Box): Placement => {\n const distances: Record<ScreenEdge, number> = {\n bottom: view.height - (box.top + box.height),\n left: box.left,\n right: view.width - (box.left + box.width),\n top: box.top,\n };\n\n let edge: ScreenEdge = \"left\";\n let nearest = Number.POSITIVE_INFINITY;\n\n for (const next of EDGES) {\n if (distances[next] < nearest) {\n nearest = distances[next];\n edge = next;\n }\n }\n\n const along = edge === \"left\" || edge === \"right\" ? box.top : box.left;\n\n return { along: Math.round(clampAlong(edge, along, view, box)), edge };\n};\n\nconst initialPlacement = (preset: DefaultPlacement, view: Box, panel: Box): Placement => {\n const { edge } = preset;\n const vertical = edge === \"left\" || edge === \"right\";\n const span = vertical ? view.height : view.width;\n const size = vertical ? panel.height : panel.width;\n const along =\n preset.align === \"start\" ? 0 : preset.align === \"end\" ? span - size : (span - size) / 2;\n\n return { along: Math.round(clampAlong(edge, along, view, panel)), edge };\n};\n\nconst isEdge = (value: unknown): value is ScreenEdge =>\n typeof value === \"string\" && (EDGES as readonly string[]).includes(value);\n\nconst readPlacement = (key: string | null): Placement | null => {\n if (key === null) {\n return null;\n }\n\n try {\n const raw = localStorage.getItem(key);\n\n if (!raw) {\n return null;\n }\n\n const parsed = JSON.parse(raw) as { along?: unknown; edge?: unknown };\n\n if (\n typeof parsed.along !== \"number\" ||\n !Number.isFinite(parsed.along) ||\n !isEdge(parsed.edge)\n ) {\n return null;\n }\n\n return { along: parsed.along, edge: parsed.edge };\n } catch {\n return null;\n }\n};\n\nconst writePlacement = (key: string | null, placement: Placement) => {\n if (key === null) {\n return;\n }\n\n try {\n localStorage.setItem(key, JSON.stringify(placement));\n } catch {\n // Best-effort; the live dock still applies.\n }\n};\n\nconst resolvePlacement = (\n stored: Placement | null,\n preset: DefaultPlacement,\n panel: Box,\n view: Box,\n): Placement => {\n if (!stored) {\n return initialPlacement(preset, view, panel);\n }\n\n const ghost = ghostBox(stored, panel, view);\n\n if (centerOutside(ghost, view)) {\n return snapBoxToEdge(ghost, view);\n }\n\n return {\n along: Math.round(clampAlong(stored.edge, stored.along, view, panel)),\n edge: stored.edge,\n };\n};\n\nconst paintPlacement = (element: HTMLElement, placement: Placement) => {\n element.dataset.edge = placement.edge;\n element.style.left = \"\";\n element.style.right = \"\";\n element.style.top = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n\n switch (placement.edge) {\n case \"left\":\n element.style.left = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"right\":\n element.style.right = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"top\":\n element.style.top = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n case \"bottom\":\n element.style.bottom = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n }\n};\n\n/**\n * Dock `element` from storage (or the left centre), re-dock on resize, and\n * snap to the nearest edge when the handle drops.\n */\nconst dockPanel = (element: HTMLElement, options: DockPanelOptions = {}): DockHandle => {\n const baseKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const key = baseKey === null ? null : `${baseKey}:placement`;\n const handle = options.handle ?? null;\n const preset = options.defaultPlacement ?? { edge: \"left\" };\n\n const apply = (): Placement => {\n const stored = readPlacement(key);\n const view = viewportBox();\n\n if (element.classList.contains(\"is-dragging\")) {\n return stored ?? initialPlacement(preset, view, panelBox(element));\n }\n\n const panel = panelBox(element);\n\n if (panel.width === 0 || panel.height === 0) {\n return stored ?? initialPlacement(preset, view, panel);\n }\n\n const next = resolvePlacement(stored, preset, panel, view);\n paintPlacement(element, next);\n\n if (!stored || stored.edge !== next.edge || stored.along !== next.along) {\n writePlacement(key, next);\n }\n\n return next;\n };\n\n apply();\n requestAnimationFrame(apply);\n\n const onResize = () => apply();\n window.addEventListener(\"resize\", onResize);\n\n const resizeObserver = new ResizeObserver(() => apply());\n resizeObserver.observe(element);\n\n const disposeBase = () => {\n window.removeEventListener(\"resize\", onResize);\n resizeObserver.disconnect();\n };\n\n if (!handle) {\n return { dispose: disposeBase, refresh: apply };\n }\n\n let pointerId: number | null = null;\n let originX = 0;\n let originY = 0;\n let grabX = 0;\n let grabY = 0;\n let dragging = false;\n\n const finish = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n if (dragging) {\n const next = snapBoxToEdge(panelBox(element), viewportBox());\n paintPlacement(element, next);\n writePlacement(key, next);\n }\n\n dragging = false;\n pointerId = null;\n element.classList.remove(\"is-dragging\");\n\n try {\n handle.releasePointerCapture(event.pointerId);\n } catch {\n // Capture was never set.\n }\n };\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) {\n return;\n }\n\n const box = panelBox(element);\n pointerId = event.pointerId;\n originX = event.clientX;\n originY = event.clientY;\n grabX = event.clientX - box.left;\n grabY = event.clientY - box.top;\n dragging = false;\n handle.setPointerCapture(event.pointerId);\n };\n\n const onPointerMove = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n const dx = event.clientX - originX;\n const dy = event.clientY - originY;\n\n if (!dragging && dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) {\n return;\n }\n\n dragging = true;\n element.classList.add(\"is-dragging\");\n element.style.right = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n element.style.left = `${event.clientX - grabX}px`;\n element.style.top = `${event.clientY - grabY}px`;\n };\n\n handle.addEventListener(\"pointerdown\", onPointerDown);\n handle.addEventListener(\"pointermove\", onPointerMove);\n handle.addEventListener(\"pointerup\", finish);\n handle.addEventListener(\"pointercancel\", finish);\n\n return {\n dispose: () => {\n disposeBase();\n handle.removeEventListener(\"pointerdown\", onPointerDown);\n handle.removeEventListener(\"pointermove\", onPointerMove);\n handle.removeEventListener(\"pointerup\", finish);\n handle.removeEventListener(\"pointercancel\", finish);\n },\n refresh: apply,\n };\n};\n\nexport { dockPanel };\nexport type { DefaultPlacement, DockAlign, DockHandle, DockPanelOptions, Placement, ScreenEdge };\n","/**\n * The Lucide marks the HUD chrome uses, inlined so the package carries no\n * icon dependency. The markup is static, so nothing user-supplied reaches it.\n */\ntype IconName =\n | \"activity\"\n | \"blend\"\n | \"box\"\n | \"circleDot\"\n | \"clipboard\"\n | \"contrast\"\n | \"cpu\"\n | \"eye\"\n | \"eyeOff\"\n | \"flame\"\n | \"gauge\"\n | \"grip\"\n | \"help\"\n | \"hourglass\"\n | \"image\"\n | \"info\"\n | \"layers\"\n | \"monitor\"\n | \"moon\"\n | \"palette\"\n | \"repeat\"\n | \"sliders\"\n | \"spline\"\n | \"sun\"\n | \"timer\"\n | \"trendingDown\"\n | \"triangle\"\n | \"zap\";\n\nconst ICON_MARKUP: Record<IconName, string> = {\n eye:\n '<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/>' +\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n eyeOff:\n '<path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\"/>' +\n '<path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\"/>' +\n '<path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\"/>' +\n '<path d=\"m2 2 20 20\"/>',\n hourglass:\n '<path d=\"M5 22h14\"/><path d=\"M5 2h14\"/>' +\n '<path d=\"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\"/>' +\n '<path d=\"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\"/>',\n flame:\n '<path d=\"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\"/>',\n activity:\n '<path d=\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\"/>',\n box:\n '<path d=\"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\"/>' +\n '<path d=\"m3.3 7 8.7 5 8.7-5\"/><path d=\"M12 22V12\"/>',\n circleDot: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/>',\n cpu:\n '<rect width=\"16\" height=\"16\" x=\"4\" y=\"4\" rx=\"2\"/><rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" rx=\"1\"/>' +\n '<path d=\"M15 2v2\"/><path d=\"M15 20v2\"/><path d=\"M2 15h2\"/><path d=\"M2 9h2\"/>' +\n '<path d=\"M20 15h2\"/><path d=\"M20 9h2\"/><path d=\"M9 2v2\"/><path d=\"M9 20v2\"/>',\n gauge: '<path d=\"m12 14 4-4\"/><path d=\"M3.34 19a10 10 0 1 1 17.32 0\"/>',\n image:\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/>' +\n '<path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/>',\n layers:\n '<path d=\"m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z\"/>' +\n '<path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\"/>' +\n '<path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\"/>',\n palette:\n '<circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\"/>',\n repeat:\n '<path d=\"m17 2 4 4-4 4\"/><path d=\"M3 11v-1a4 4 0 0 1 4-4h14\"/>' +\n '<path d=\"m7 22-4-4 4-4\"/><path d=\"M21 13v1a4 4 0 0 1-4 4H3\"/>',\n spline:\n '<circle cx=\"19\" cy=\"5\" r=\"2\"/><circle cx=\"5\" cy=\"19\" r=\"2\"/><path d=\"M5 17A12 12 0 0 1 17 5\"/>',\n timer:\n '<line x1=\"10\" x2=\"14\" y1=\"2\" y2=\"2\"/><line x1=\"12\" x2=\"15\" y1=\"14\" y2=\"11\"/><circle cx=\"12\" cy=\"14\" r=\"8\"/>',\n trendingDown:\n '<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>',\n triangle: '<path d=\"M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\"/>',\n zap: '<path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\"/>',\n blend: '<circle cx=\"9\" cy=\"9\" r=\"7\"/><circle cx=\"15\" cy=\"15\" r=\"7\"/>',\n clipboard:\n '<rect width=\"8\" height=\"4\" x=\"8\" y=\"2\" rx=\"1\" ry=\"1\"/>' +\n '<path d=\"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\"/>',\n contrast: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 18a6 6 0 0 0 0-12v12z\"/>',\n grip:\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/>' +\n '<circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>',\n help:\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/>' +\n '<path d=\"M12 17h.01\"/>',\n monitor:\n '<rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/>' +\n '<line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/>',\n moon: '<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"/>',\n sun:\n '<circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/>' +\n '<path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/>' +\n '<path d=\"M2 12h2\"/><path d=\"M20 12h2\"/>' +\n '<path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/>',\n sliders:\n '<line x1=\"21\" x2=\"14\" y1=\"4\" y2=\"4\"/><line x1=\"10\" x2=\"3\" y1=\"4\" y2=\"4\"/>' +\n '<line x1=\"21\" x2=\"12\" y1=\"12\" y2=\"12\"/><line x1=\"8\" x2=\"3\" y1=\"12\" y2=\"12\"/>' +\n '<line x1=\"21\" x2=\"16\" y1=\"20\" y2=\"20\"/><line x1=\"12\" x2=\"3\" y1=\"20\" y2=\"20\"/>' +\n '<line x1=\"14\" x2=\"14\" y1=\"2\" y2=\"6\"/><line x1=\"8\" x2=\"8\" y1=\"10\" y2=\"14\"/>' +\n '<line x1=\"16\" x2=\"16\" y1=\"18\" y2=\"22\"/>',\n};\n\nconst createIcon = (name: IconName, className: string) => {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.setAttribute(\"class\", className);\n svg.innerHTML = ICON_MARKUP[name];\n\n return svg;\n};\n\nexport { createIcon };\nexport type { IconName };\n","/**\n * The HUD stylesheet as a string, so the package works without a bundler and\n * injects itself once per document. `.perf-monitor` is the metrics card,\n * `.perf-hud` the floating host around it. Both namespaced so a host page can\n * override safely; the `--perf-*` custom properties are the theming surface.\n *\n * Both elements carry `data-theme=\"dark\" | \"light\"` (see `HudTheme`) and each\n * resolves its own tokens from it, so the card also themes when mounted on\n * its own, outside a `.perf-hud`.\n */\nconst STYLE_ATTRIBUTE = \"data-three-meter\";\n\nconst PERF_HUD_STYLES = `\n.perf-hud,\n.perf-monitor {\n --perf-bg: rgba(22, 24, 29, 0.85);\n --perf-fg: #e6e8eb;\n --perf-fg-dim: #c5c8ce;\n --perf-muted: #8b909a;\n --perf-row: rgba(255, 255, 255, 0.04);\n --perf-border: rgba(255, 255, 255, 0.12);\n --perf-accent: #60a5fa;\n --perf-warn: #f59e0b;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);\n\n color-scheme: dark;\n}\n\n.perf-hud[data-theme=\"light\"],\n.perf-monitor[data-theme=\"light\"] {\n --perf-bg: rgba(250, 250, 252, 0.88);\n --perf-fg: #1a1c21;\n --perf-fg-dim: #4b5058;\n --perf-muted: #6b7079;\n --perf-row: rgba(0, 0, 0, 0.05);\n --perf-border: rgba(0, 0, 0, 0.12);\n --perf-accent: #2563eb;\n --perf-warn: #b45309;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n\n color-scheme: light;\n}\n\n.perf-monitor {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 8px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.4;\n color: var(--perf-fg);\n}\n\n.perf-monitor__graphs {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.perf-monitor__graph {\n position: relative;\n height: 40px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__canvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.perf-monitor__graph-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px;\n pointer-events: none;\n}\n\n.perf-monitor__graph-head {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.perf-monitor__graph-overlay--toggle {\n pointer-events: auto;\n cursor: pointer;\n}\n\n.perf-monitor__graph-label {\n color: var(--perf-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-size: 10px;\n}\n\n.perf-monitor__graph-value {\n min-width: 6ch;\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__section {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.perf-monitor__row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n/* Hidden until \"explain metrics\" is on; then the row wraps it under the label. */\n.perf-monitor__hint {\n display: none;\n flex-basis: 100%;\n padding: 0 0 2px 18px;\n color: var(--perf-muted);\n font-size: 10px;\n line-height: 1.35;\n opacity: 0.85;\n}\n\n.perf-monitor--explain .perf-monitor__hint {\n display: block;\n}\n\n.perf-monitor--explain .perf-monitor__section .perf-monitor__row {\n flex-wrap: wrap;\n}\n\n.perf-monitor__row:nth-child(even) {\n background: var(--perf-row);\n}\n\n.perf-monitor__label {\n flex: 1;\n color: var(--perf-muted);\n}\n\n.perf-monitor__icon {\n flex: none;\n width: 12px;\n height: 12px;\n color: var(--perf-muted);\n}\n\n.perf-monitor__options {\n padding-bottom: 8px;\n border-bottom: 1px solid var(--perf-border);\n}\n\n.perf-monitor__row--static {\n cursor: default;\n}\n\n/* Light / system / dark, as a small segmented radio group. */\n.perf-monitor__segment {\n display: inline-flex;\n flex: none;\n gap: 1px;\n padding: 1px;\n border-radius: 4px;\n background: var(--perf-row);\n}\n\n.perf-monitor__segment-option {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 16px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n}\n\n.perf-monitor__segment-option:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__segment-option[aria-checked=\"true\"] {\n color: var(--perf-fg);\n background: var(--perf-border);\n}\n\n.perf-monitor__segment-option:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__segment-option .perf-monitor__icon {\n width: 11px;\n height: 11px;\n color: inherit;\n}\n\n.perf-monitor__button {\n flex: none;\n min-width: 7ch;\n padding: 0 6px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n background: var(--perf-row);\n color: var(--perf-fg-dim);\n font: inherit;\n cursor: pointer;\n}\n\n.perf-monitor__button:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__button:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Top costs: name, calls, triangles. Rows are buttons that log their objects. */\n.perf-monitor__costs-body {\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding-top: 2px;\n}\n\n.perf-monitor__costs-body[hidden] {\n display: none;\n}\n\n.perf-monitor__cost {\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch 14px;\n gap: 6px;\n align-items: baseline;\n padding: 2px 6px;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: default;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\n}\n\n.perf-monitor__cost.is-hidden .perf-monitor__cost-name {\n opacity: 0.6;\n text-decoration: line-through;\n}\n\n/* What hiding saved, across the calls and tris columns. */\n.perf-monitor__cost-saved {\n grid-column: 2 / span 2;\n color: var(--perf-accent);\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n white-space: nowrap;\n}\n\n.perf-monitor__cost-eye {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n align-self: center;\n}\n\n.perf-monitor__cost-eye .perf-monitor__icon {\n width: 12px;\n height: 12px;\n color: inherit;\n}\n\n.perf-monitor__cost-eye:hover,\n.perf-monitor__cost-eye[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-eye:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: -1px;\n}\n\n.perf-monitor__cost--head {\n align-items: center;\n cursor: default;\n}\n\n/* The name is a button: a click logs the row's objects. */\n.perf-monitor__cost-name {\n min-width: 0;\n padding: 0;\n border: 0;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__cost-name:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-name:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost-copies {\n color: var(--perf-muted);\n}\n\n.perf-monitor__cost-value {\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__cost-sort {\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--perf-muted);\n font: inherit;\n font-size: 10px;\n text-align: right;\n cursor: pointer;\n}\n\n.perf-monitor__cost-sort[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.perf-monitor__segment-option--text {\n width: auto;\n padding: 0 5px;\n font: inherit;\n font-size: 10px;\n}\n\n.perf-monitor__costs-note {\n padding: 2px 6px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__checkbox {\n flex: none;\n margin: 0;\n width: 12px;\n height: 12px;\n accent-color: var(--perf-accent);\n cursor: pointer;\n}\n\n.perf-monitor__value {\n min-width: 7ch;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__hud {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 2px;\n}\n\n.perf-monitor__hud-graphs {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.perf-monitor__hud-graphs:empty {\n display: none;\n}\n\n.perf-monitor__hud-graph {\n position: relative;\n height: 28px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__hud-grid {\n display: grid;\n grid-template-columns: auto 6ch auto 6ch;\n align-items: baseline;\n gap: 2px 8px;\n}\n\n.perf-monitor__hud-grid:empty {\n display: none;\n}\n\n.perf-monitor__hud-item {\n display: contents;\n}\n\n.perf-monitor__hud-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__hud-label .perf-monitor__icon,\n.perf-monitor__hud-graph .perf-monitor__icon {\n width: 11px;\n height: 11px;\n}\n\n.perf-monitor__hud-value {\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__footer {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 8px 6px 0;\n border-top: 1px solid var(--perf-border);\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__footer-body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row[hidden],\n.perf-monitor__badge[hidden] {\n display: none;\n}\n\n.perf-monitor__footer-gpu {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__badge {\n flex: none;\n padding: 0 5px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n color: var(--perf-fg-dim);\n letter-spacing: 0.04em;\n}\n\n.perf-monitor__badge.is-fallback {\n border-color: var(--perf-warn);\n color: var(--perf-warn);\n}\n\n/* Past its budget. The value's tooltip names the budget. */\n.perf-monitor__value.is-over,\n.perf-monitor__hud-value.is-over,\n.perf-monitor__graph-value.is-over {\n color: var(--perf-warn);\n}\n\n.perf-monitor__link {\n color: inherit;\n text-decoration: none;\n}\n\n.perf-monitor__link:hover,\n.perf-monitor__link:focus-visible {\n color: var(--perf-accent);\n text-decoration: underline;\n}\n\n.perf-monitor--full .perf-monitor__hud {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__graphs,\n.perf-monitor--compact .perf-monitor__section,\n.perf-monitor--compact:not(.perf-monitor--info) .perf-monitor__footer,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__checkbox,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__icon {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__footer {\n padding: 6px 2px 2px;\n}\n\n.perf-monitor--compact {\n padding: 4px 8px;\n}\n\n.perf-hud {\n position: fixed;\n z-index: 9999;\n /* Safari resolves width:100% canvases against the viewport on a fixed\n host with auto width. Shrink-wrap so expand stays a card, not the window. */\n width: max-content;\n max-width: min(20rem, calc(100vw - 48px));\n overflow: visible;\n background: transparent;\n pointer-events: none;\n user-select: none;\n touch-action: none;\n}\n\n.perf-hud--full .perf-hud__card > .perf-monitor {\n width: 16rem;\n max-width: 100%;\n}\n\n/* Room for the hints, so most fit on two or three lines. Plus padding, this fills the host's 20rem cap. */\n.perf-hud--full .perf-hud__card > .perf-monitor--explain {\n width: 19rem;\n}\n\n/* The card every dev panel sits in (\\`mountDevPanel\\`): the brand label, then the panel's content. */\n.perf-hud__card {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n max-height: 70vh;\n overflow: hidden;\n background: var(--perf-bg);\n border-radius: 6px;\n box-shadow: var(--perf-shadow);\n backdrop-filter: blur(2px);\n pointer-events: auto;\n transition: opacity 0.25s ease;\n}\n\n.perf-hud__card > .perf-monitor {\n min-height: 0;\n overflow: auto;\n}\n\n.perf-hud--dim:not(.is-awake):not(.is-dragging) > .perf-hud__card {\n opacity: 0.32;\n}\n\n.perf-hud__brand {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px 0;\n color: var(--perf-muted);\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.08em;\n line-height: 1;\n text-transform: uppercase;\n}\n\n/* Alone in the card: pad it all round. */\n.perf-hud__brand:last-child {\n padding-bottom: 6px;\n}\n\n.perf-hud__brand-name {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.perf-hud__brand-name::before {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--perf-accent);\n content: \"\";\n}\n\n.perf-hud__brand-detail {\n margin-left: auto;\n font-weight: 400;\n letter-spacing: 0.02em;\n text-transform: none;\n}\n\n.perf-hud__brand-detail:empty {\n display: none;\n}\n\n.perf-hud__hotspot {\n position: absolute;\n z-index: 0;\n inset: -20px;\n pointer-events: auto;\n}\n\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__hotspot {\n right: -46px;\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__hotspot {\n left: -46px;\n}\n\n.perf-hud__tools {\n position: absolute;\n top: 0;\n z-index: 2;\n display: flex;\n flex-direction: column;\n gap: 4px;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n}\n\n.perf-hud.is-awake .perf-hud__tools,\n.perf-hud.is-dragging .perf-hud__tools {\n opacity: 1;\n pointer-events: auto;\n}\n\n/* Inward side: right by default, left when docked to the right edge. */\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__tools {\n left: calc(100% + 6px);\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__tools {\n right: calc(100% + 6px);\n}\n\n.perf-hud__disc {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--perf-bg);\n box-shadow: var(--perf-shadow);\n color: var(--perf-fg-dim);\n cursor: pointer;\n backdrop-filter: blur(2px);\n}\n\n.perf-hud__icon {\n width: 12px;\n height: 12px;\n}\n\n.perf-hud__drag {\n cursor: grab;\n}\n\n.perf-hud.is-dragging .perf-hud__drag {\n cursor: grabbing;\n}\n\n/* The ring is only ever hover or keyboard focus, never state. */\n.perf-hud__disc:hover,\n.perf-hud__disc:focus-visible {\n color: var(--perf-fg);\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Dim on is a solid accent disc, off a plain one, so the state reads without hovering and\n hover keeps its own look on top of either. After the hover rule, so on stays on under it. */\n.perf-hud__dim[aria-pressed=\"true\"] {\n background: var(--perf-accent);\n color: var(--perf-bg);\n}\n`;\n\n/**\n * Append the stylesheet to `target`'s head, once. Call before mounting a view\n * by hand; {@link mountPerfHud} does it for you unless told not to.\n */\nconst injectStyles = (target: Document = document) => {\n if (target.head.querySelector(`style[${STYLE_ATTRIBUTE}]`)) {\n return;\n }\n\n const style = target.createElement(\"style\");\n style.setAttribute(STYLE_ATTRIBUTE, \"\");\n style.textContent = PERF_HUD_STYLES;\n target.head.append(style);\n};\n\nexport { injectStyles, PERF_HUD_STYLES };\n","/**\n * The frame every zkmake dev panel shares, so they look and handle alike: a fixed host docked to a\n * screen edge, a card with a brand label on top and the panel's content under it, and three discs\n * on the inward side — drag grip, expand/compact, dim on leave — that stay hidden until the pointer\n * is near or someone taps. `mountPerfHud` is one; `@zkmake/three-textures`' panel is another.\n */\nimport { type DefaultPlacement, dockPanel, type ScreenEdge } from \"./dock-panel.ts\";\nimport { createIcon } from \"./icons.ts\";\nimport { injectStyles } from \"./styles.ts\";\nimport { applyTheme, type HudTheme } from \"./theme.ts\";\n\nconst TOUCH_LINGER_MS = 2500;\n\ntype DevPanelDim = {\n get: () => boolean;\n set: (on: boolean) => void;\n subscribe: (listener: () => void) => () => void;\n};\n\ntype MountDevPanelOptions = {\n /** The panel's body, placed in the card under the brand label. */\n content: HTMLElement;\n /** The label on top of the card, naming the tool: `three-meter`, `three-textures`. */\n brand: string;\n /** Accessible name of the panel. */\n label: string;\n theme: HudTheme;\n /** Base key for the dock placement (`<key>:placement`) and the default dim flag. `null`: not kept. */\n storageKey: string | null;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** The expand/compact disc: its label, and what a press does. */\n onToggle: () => void;\n toggleLabel?: string;\n /** Where dim-on-leave lives. Default a flag of its own under `<storageKey>:dim`, off. */\n dim?: DevPanelDim;\n /** The panel docked to another edge (a drop or a resize). */\n onEdgeChange?: (edge: ScreenEdge) => void;\n};\n\ntype DevPanelHandle = {\n element: HTMLDivElement;\n card: HTMLDivElement;\n /** The brand row's right-hand slot: a count, a status. */\n detail: HTMLSpanElement;\n /** Re-dock against the current size, after the card grows or shrinks. */\n refresh: () => void;\n dispose: () => void;\n};\n\nconst disc = (label: string, className: string, icon: \"blend\" | \"grip\" | \"sliders\") => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = `perf-hud__disc ${className}`.trim();\n button.setAttribute(\"aria-label\", label);\n button.title = label;\n button.append(createIcon(icon, \"perf-hud__icon\"));\n\n return button;\n};\n\n/** A dim flag in localStorage, for panels with no settings of their own. */\nconst storedDim = (key: string | null): DevPanelDim => {\n const listeners = new Set<() => void>();\n let on = false;\n\n try {\n on = key !== null && localStorage.getItem(key) === \"1\";\n } catch {\n // No storage (private mode): the flag lasts this page.\n }\n\n return {\n get: () => on,\n set: (next) => {\n on = next;\n\n try {\n if (key !== null) {\n localStorage.setItem(key, next ? \"1\" : \"0\");\n }\n } catch {\n // As above.\n }\n\n for (const listener of listeners) {\n listener();\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nconst mountDevPanel = (options: MountDevPanelOptions): DevPanelHandle => {\n const parent = options.parent ?? document.body;\n const dim =\n options.dim ?? storedDim(options.storageKey === null ? null : `${options.storageKey}:dim`);\n\n if (options.injectStyles ?? true) {\n injectStyles(parent.ownerDocument);\n }\n\n const host = document.createElement(\"div\");\n host.className = \"perf-hud\";\n host.dataset.edge = \"left\";\n host.setAttribute(\"aria-label\", options.label);\n\n const hotspot = document.createElement(\"div\");\n hotspot.className = \"perf-hud__hotspot\";\n hotspot.setAttribute(\"aria-hidden\", \"true\");\n\n const tools = document.createElement(\"div\");\n tools.className = \"perf-hud__tools\";\n const grip = disc(`Drag the ${options.brand} panel`, \"perf-hud__drag\", \"grip\");\n const toggle = disc(options.toggleLabel ?? \"Expand or compact the panel\", \"\", \"sliders\");\n const dimDisc = disc(\"Dim the panel when the pointer leaves\", \"perf-hud__dim\", \"blend\");\n dimDisc.addEventListener(\"click\", () => dim.set(!dim.get()));\n toggle.addEventListener(\"click\", () => options.onToggle());\n tools.append(grip, toggle, dimDisc);\n\n const card = document.createElement(\"div\");\n card.className = \"perf-hud__card\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"perf-hud__brand\";\n\n const name = document.createElement(\"span\");\n name.className = \"perf-hud__brand-name\";\n name.textContent = options.brand;\n\n const detail = document.createElement(\"span\");\n detail.className = \"perf-hud__brand-detail\";\n brand.append(name, detail);\n card.append(brand, options.content);\n\n host.append(hotspot, tools, card);\n parent.append(host);\n\n const applyDim = () => {\n host.classList.toggle(\"perf-hud--dim\", dim.get());\n dimDisc.setAttribute(\"aria-pressed\", String(dim.get()));\n dimDisc.title = `Dim on leave: ${dim.get() ? \"on\" : \"off\"}`;\n };\n\n applyDim();\n applyTheme(host, options.theme);\n const unsubscribeDim = dim.subscribe(applyDim);\n const unsubscribeTheme = options.theme.subscribe(() => applyTheme(host, options.theme));\n const dock = dockPanel(host, {\n defaultPlacement: options.defaultPlacement,\n handle: grip,\n storageKey: options.storageKey,\n });\n\n // `dockPanel` writes the edge onto `data-edge`; pass changes on.\n let edge = host.dataset.edge as ScreenEdge;\n const edgeWatch = new MutationObserver(() => {\n const next = host.dataset.edge as ScreenEdge;\n\n if (next !== edge) {\n edge = next;\n options.onEdgeChange?.(edge);\n }\n });\n\n edgeWatch.observe(host, { attributes: true, attributeFilter: [\"data-edge\"] });\n options.onEdgeChange?.(edge);\n\n // Wake on approach, sleep on leave; a touch lingers so the discs can be tapped.\n let hideTimer = 0;\n\n const sleep = () => {\n if (host.classList.contains(\"is-dragging\")) {\n return;\n }\n\n host.classList.remove(\"is-awake\");\n };\n\n const wake = (linger: boolean) => {\n host.classList.add(\"is-awake\");\n window.clearTimeout(hideTimer);\n\n if (linger) {\n hideTimer = window.setTimeout(sleep, TOUCH_LINGER_MS);\n }\n };\n\n const onEnter = () => {\n wake(false);\n };\n\n const onLeave = (event: PointerEvent) => {\n if (host.contains(event.relatedTarget as Node | null)) {\n return;\n }\n\n if (event.pointerType === \"touch\" || event.pointerType === \"pen\") {\n wake(true);\n\n return;\n }\n\n sleep();\n };\n\n const onDown = (event: PointerEvent) => {\n wake(event.pointerType !== \"mouse\");\n };\n\n host.addEventListener(\"pointerenter\", onEnter);\n host.addEventListener(\"pointerleave\", onLeave);\n host.addEventListener(\"pointerdown\", onDown);\n\n return {\n element: host,\n card,\n detail,\n refresh: () => {\n requestAnimationFrame(() => dock.refresh());\n },\n dispose: () => {\n window.clearTimeout(hideTimer);\n edgeWatch.disconnect();\n host.removeEventListener(\"pointerenter\", onEnter);\n host.removeEventListener(\"pointerleave\", onLeave);\n host.removeEventListener(\"pointerdown\", onDown);\n unsubscribeDim();\n unsubscribeTheme();\n dock.dispose();\n host.remove();\n },\n };\n};\n\nexport { mountDevPanel };\nexport type { DevPanelDim, DevPanelHandle, MountDevPanelOptions };\n","","/**\n * Copy text from a click handler. The async Clipboard API needs a secure\n * context and the `clipboard-write` permission; a dev server opened over a LAN\n * IP has neither, and some embeds deny the permission. The fallback is the old\n * select-a-textarea `execCommand(\"copy\")`, still honoured inside a user gesture.\n */\nconst copyWithSelection = (text: string) => {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n textarea.style.pointerEvents = \"none\";\n\n const previous = document.activeElement as HTMLElement | null;\n document.body.append(textarea);\n textarea.select();\n\n try {\n return document.execCommand(\"copy\");\n } catch {\n return false;\n } finally {\n textarea.remove();\n previous?.focus?.();\n }\n};\n\nconst copyText = async (text: string): Promise<boolean> => {\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return copyWithSelection(text);\n }\n};\n\nexport { copyText };\n","/**\n * Number formatting for the card. Counts stay exact with thousands separators\n * while they fit the compact grid's 6ch value column (`99,999`), then switch\n * to compact notation (`250K`, `1.4M`, `12.3M`, `1.2B`) so a triangle count in\n * the millions can't run into the neighbouring label.\n */\nconst COMPACT_FROM = 100_000;\n\nconst exact = new Intl.NumberFormat(\"en-US\");\nconst compact = new Intl.NumberFormat(\"en-US\", {\n maximumFractionDigits: 1,\n notation: \"compact\",\n});\n\nconst formatCount = (value: number) =>\n Math.abs(value) < COMPACT_FROM ? exact.format(value) : compact.format(value);\n\nexport { formatCount };\n","/**\n * Hides groups of scene objects to measure what they cost, and puts every one\n * back exactly as it was. Objects are three objects, read structurally: only\n * `visible` is touched.\n *\n * An object can sit in two hidden groups at once (a mesh row and its\n * material's row); it stays hidden until the last group that holds it is\n * shown again. Objects that were already invisible stay invisible.\n */\ntype Hideable = { visible?: boolean };\n\nclass Hider {\n private readonly groups = new Map<string, Hideable[]>();\n /** Each hidden object's `visible` from before the first group hid it. */\n private readonly original = new Map<Hideable, boolean>();\n\n hide(key: string, objects: readonly unknown[]) {\n if (this.groups.has(key)) {\n return;\n }\n\n const hideable = objects.filter(\n (object): object is Hideable => typeof object === \"object\" && object !== null,\n );\n this.groups.set(key, hideable);\n\n for (const object of hideable) {\n if (!this.original.has(object)) {\n this.original.set(object, object.visible !== false);\n }\n\n object.visible = false;\n }\n }\n\n show(key: string) {\n const objects = this.groups.get(key);\n\n if (!objects) {\n return;\n }\n\n this.groups.delete(key);\n\n for (const object of objects) {\n if (!this.heldElsewhere(object)) {\n object.visible = this.original.get(object) ?? true;\n this.original.delete(object);\n }\n }\n }\n\n isHidden(key: string) {\n return this.groups.has(key);\n }\n\n /** Hidden group keys, in the order they were hidden. */\n keys() {\n return [...this.groups.keys()];\n }\n\n restoreAll() {\n for (const key of this.keys()) {\n this.show(key);\n }\n }\n\n private heldElsewhere(object: Hideable) {\n for (const objects of this.groups.values()) {\n if (objects.includes(object)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport { Hider };\n","/**\n * One plain-language line per metric: what it is, and what a bad reading\n * means. Shown as the label's tooltip and, with \"explain metrics\" on, under\n * each row. Keyed like the stat rows; the graphs reuse `fps`, `cpu`, `gpu`.\n */\nconst METRIC_HELP: Record<string, string> = {\n calls:\n \"Separate draw commands sent to the GPU this frame. Fewer is better: merge or instance meshes that share a material.\",\n cpu: \"JavaScript time per frame: your update code plus three.js preparing the render. It has to fit the frame budget (16.7 ms at 60 FPS).\",\n fps: \"Frames drawn per second. Higher is smoother; most displays top out at 60 or 120.\",\n geometries:\n \"Geometries held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n headroom:\n \"Spare time left in each frame: the frame budget minus whichever of CPU and GPU took longer. Below zero, frames miss the display's refresh.\",\n refresh:\n \"The rate the loop runs at when it keeps up: the display's refresh rate, or the app's own cap. Budgets use it unless you set targetFps.\",\n gpu: \"Time the graphics card spent drawing the frame. High means shaders, overdraw or resolution are the bottleneck.\",\n hitches:\n \"Frames that took over twice as long as usual, in the last 1,000 frames. Each one is a visible stutter.\",\n lines: \"Line segments drawn this frame.\",\n low: \"Frame rate during the slowest 1% of frames. Far below FPS means stutter the average hides.\",\n p99: \"99% of frames finished within this many ms. Above the frame budget (16.7 ms at 60 FPS) means dropped frames.\",\n passes:\n \"Times renderer.render() ran this frame. Shadows, post-processing and extra cameras each add passes.\",\n points: \"Points drawn this frame.\",\n shaders:\n \"Compiled shader programs. A jump mid-session usually means a stutter while a new material compiled.\",\n textures:\n \"Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n triangles:\n \"Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it.\",\n};\n\nexport { METRIC_HELP };\n","/**\n * A plain-text snapshot of the monitor for bug reports: versions, backend and\n * GPU, the current sample, stutter stats, the scene's top meshes and the page. Fenced as a `text`\n * code block so GitHub and Slack keep the line breaks.\n */\nimport { version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { Environment, FrameStats, Sample, SceneCost } from \"../core/types.ts\";\nimport { formatCount } from \"./format.ts\";\n\ntype ReportPage = {\n dpr: number;\n height: number;\n userAgent: string;\n width: number;\n};\n\ntype ReportData = {\n /** The top meshes go in when given. */\n cost?: SceneCost | null;\n environment: Environment;\n page: ReportPage | null;\n sample: Sample;\n stats: FrameStats;\n};\n\nconst BACKEND_LABELS = { webgl: \"WebGL\", webgl2: \"WebGL2\", webgpu: \"WebGPU\" } as const;\n\n/** `WebGPU`, `WebGL2`, `WebGL2 fallback`, or `\"\"` before the backend settles. */\nconst backendLabel = (environment: Environment) => {\n if (!environment.backend) {\n return \"\";\n }\n\n const label = BACKEND_LABELS[environment.backend];\n\n return environment.fallback ? `${label} fallback` : label;\n};\n\nconst ms = (value: number) => `${value.toFixed(1)} ms`;\n\n/** Top meshes by draw calls, three at most: `Top: cubes ×2,000 (1 call, 24K tris) · …`. */\nconst topMeshes = (cost: SceneCost) =>\n `Top: ${cost.meshes\n .slice(0, 3)\n .map((entry) => {\n const copies = entry.instances > 1 ? ` ×${formatCount(entry.instances)}` : \"\";\n const calls = `${formatCount(entry.calls)} ${entry.calls === 1 ? \"call\" : \"calls\"}`;\n\n return `${entry.label}${copies} (${calls}, ${formatCount(entry.triangles)} tris)`;\n })\n .join(\" · \")}`;\n\nconst renderReport = ({ cost, environment, page, sample, stats }: ReportData): string => {\n const { render, resources } = sample;\n const heading = [\n `three-meter v${version}`,\n environment.three ? `three r${environment.three}` : \"\",\n backendLabel(environment),\n environment.gpu ?? \"\",\n ].filter(Boolean);\n\n const frames =\n stats.frames === 0\n ? `FPS ${Math.round(sample.fps)}`\n : [\n `FPS ${Math.round(sample.fps)}`,\n `1% low ${Math.round(stats.lowFps)}`,\n `p99 ${ms(stats.p99Ms)}`,\n `hitches ${formatCount(stats.hitches)} / ${formatCount(stats.frames)} frames`,\n ].join(\" · \");\n\n const timing = [\n `CPU ${ms(sample.cpu)}`,\n `GPU ${sample.gpu.available ? ms(sample.gpu.ms) : \"unavailable\"}`,\n ];\n\n // Headroom against the detected refresh: spare time before the frame misses its slot.\n if (stats.refreshHz) {\n const busiest = Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0);\n timing.push(\n `refresh ${stats.refreshHz} Hz`,\n `headroom ${ms(1000 / stats.refreshHz - busiest)}`,\n );\n }\n\n const lines = [\n heading.join(\" · \"),\n frames,\n timing.join(\" · \"),\n [\n `Calls ${formatCount(render.calls)}`,\n `passes ${formatCount(render.passes)}`,\n `triangles ${formatCount(render.triangles)}`,\n `lines ${formatCount(render.lines)}`,\n `points ${formatCount(render.points)}`,\n ].join(\" · \"),\n [\n `Geometries ${formatCount(resources.geometries)}`,\n `textures ${formatCount(resources.textures)}`,\n `shaders ${formatCount(resources.programs)}`,\n ].join(\" · \"),\n ];\n\n if (cost && cost.meshes.length > 0) {\n lines.push(topMeshes(cost));\n }\n\n if (page) {\n lines.push(`Viewport ${page.width}×${page.height} @${page.dpr}x`, page.userAgent);\n }\n\n return [\"```text\", ...lines, \"```\"].join(\"\\n\");\n};\n\nconst readPage = (): ReportPage | null =>\n typeof window === \"undefined\"\n ? null\n : {\n dpr: window.devicePixelRatio || 1,\n height: window.innerHeight,\n userAgent: navigator.userAgent,\n width: window.innerWidth,\n };\n\n/** The report for `monitor` right now, as Markdown ready to paste into an issue. */\nconst formatReport = (monitor: PerformanceMonitor): string =>\n renderReport({\n cost: monitor.getSceneCost(),\n environment: monitor.getEnvironment(),\n page: readPage(),\n sample: monitor.getSample(),\n stats: monitor.getFrameStats(),\n });\n\nexport { backendLabel, formatReport, renderReport };\nexport type { ReportData, ReportPage };\n","/**\n * Minimal canvas sparkline. The caller owns the canvas, its size, and any\n * device-pixel-ratio transform; this just paints a filled line series in\n * logical (CSS) pixel coordinates. An optional guide (a budget) is drawn as a\n * dashed line when it falls inside the series' range. The scale never\n * stretches to fit it, so a CPU series at 0.2 ms keeps its shape under a\n * 16.7 ms budget and the line appears once the series gets near.\n */\ntype SparklineStyle = {\n fill: string;\n stroke: string;\n};\n\ntype SparklineGuide = {\n color: string;\n value: number;\n};\n\nfunction drawSparkline(\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number,\n values: readonly number[],\n style: SparklineStyle,\n guide?: SparklineGuide,\n) {\n ctx.clearRect(0, 0, width, height);\n\n if (values.length < 2) {\n return;\n }\n\n let max = 0;\n\n for (const value of values) {\n if (value > max) {\n max = value;\n }\n }\n\n const range = max * 1.1 || 1;\n const stepX = width / (values.length - 1);\n const toY = (value: number) => height - (value / range) * height;\n\n ctx.beginPath();\n ctx.moveTo(0, toY(values[0]!));\n\n for (let index = 1; index < values.length; index += 1) {\n ctx.lineTo(index * stepX, toY(values[index]!));\n }\n\n ctx.lineWidth = 1;\n ctx.strokeStyle = style.stroke;\n ctx.stroke();\n\n ctx.lineTo(width, height);\n ctx.lineTo(0, height);\n ctx.closePath();\n ctx.fillStyle = style.fill;\n ctx.fill();\n\n if (guide && guide.value > 0 && guide.value <= range) {\n // Half-pixel offset keeps a 1px line crisp.\n const y = Math.round(toY(guide.value)) + 0.5;\n ctx.save();\n ctx.setLineDash([3, 3]);\n ctx.strokeStyle = guide.color;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(width, y);\n ctx.stroke();\n ctx.restore();\n }\n}\n\nexport { drawSparkline };\nexport type { SparklineGuide, SparklineStyle };\n","import { name, version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { CostEntry, FrameStats, Sample, TimingMetric } from \"../core/types.ts\";\nimport {\n type BudgetKey,\n type Budgets,\n isFloor,\n isOverBudget,\n type ResolvedBudgets,\n resolveBudgets,\n} from \"./budgets.ts\";\nimport { copyText } from \"./clipboard.ts\";\nimport { formatCount } from \"./format.ts\";\nimport { Hider } from \"./hider.ts\";\nimport { HudSettings } from \"./hud-settings.ts\";\nimport { createIcon, type IconName } from \"./icons.ts\";\nimport { METRIC_HELP } from \"./metric-help.ts\";\nimport { backendLabel, formatReport } from \"./report.ts\";\nimport { drawSparkline, type SparklineStyle } from \"./sparkline.ts\";\nimport { applyTheme, HudTheme, type ResolvedTheme, type ThemeMode } from \"./theme.ts\";\n\ntype PerformanceViewMode = \"full\" | \"compact\";\n\ntype PerformanceViewOptions = {\n monitor: PerformanceMonitor;\n /** `full` is the checkbox list; `compact` the card it configures. Default `full`. */\n mode?: PerformanceViewMode;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber. Timing budgets default from\n * `targetFps` (60); `false` turns them all off. See {@link Budgets}.\n */\n budgets?: Budgets | false;\n /** Share one between views to keep them in sync; a fresh instance otherwise. */\n settings?: HudSettings;\n /**\n * Share the host's so the card and its chrome switch together. Otherwise a\n * fresh `system` instance is created and disposed with the view.\n */\n theme?: HudTheme;\n};\n\ntype TimingConfig = {\n format: (value: number) => string;\n icon: IconName;\n label: string;\n metric: TimingMetric;\n /** Canvas can't read CSS custom properties, so the sparkline palette lives here per theme. */\n style: Record<ResolvedTheme, SparklineStyle>;\n unit: string;\n};\n\ntype NumberConfig = {\n format: (value: number) => string;\n icon: IconName;\n key: BudgetKey;\n label: string;\n unit?: \"ms\" | \"Hz\";\n /**\n * `null` until known: no frames yet, or no GPU timer. `frameMs` is the frame\n * budget the timing budgets came from (target or detected refresh).\n */\n value: (sample: Sample, stats: FrameStats, frameMs: number) => number | null;\n};\n\nconst TIMINGS: TimingConfig[] = [\n {\n format: (value) => Math.round(value).toString(),\n icon: \"gauge\",\n label: \"FPS\",\n metric: \"fps\",\n style: {\n dark: { fill: \"rgba(74, 222, 128, 0.15)\", stroke: \"#4ade80\" },\n light: { fill: \"rgba(22, 163, 74, 0.12)\", stroke: \"#16a34a\" },\n },\n unit: \"\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"cpu\",\n label: \"CPU\",\n metric: \"cpu\",\n style: {\n dark: { fill: \"rgba(96, 165, 250, 0.15)\", stroke: \"#60a5fa\" },\n light: { fill: \"rgba(37, 99, 235, 0.12)\", stroke: \"#2563eb\" },\n },\n unit: \"ms\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"zap\",\n label: \"GPU\",\n metric: \"gpu\",\n style: {\n dark: { fill: \"rgba(244, 114, 182, 0.15)\", stroke: \"#f472b6\" },\n light: { fill: \"rgba(219, 39, 119, 0.12)\", stroke: \"#db2777\" },\n },\n unit: \"ms\",\n },\n];\n\nconst THEME_OPTIONS: { icon: \"sun\" | \"monitor\" | \"moon\"; label: string; mode: ThemeMode }[] = [\n { icon: \"sun\", label: \"Light theme\", mode: \"light\" },\n { icon: \"monitor\", label: \"Match the system theme\", mode: \"system\" },\n { icon: \"moon\", label: \"Dark theme\", mode: \"dark\" },\n];\n\n/** The monorepo tags each package's releases `@zkmake/three-meter@0.9.2`. */\nconst RELEASE_URL = `https://github.com/zkmake/three-kit/releases/tag/${encodeURIComponent(`${name}@${version}`)}`;\n\n/** How long the report button says `copied` / `failed` before reading `copy` again. */\nconst COPY_FEEDBACK_MS = 1500;\n\n/** Walking the scene graph isn't free; the top-costs list refreshes at 2 Hz while open. */\nconst COSTS_INTERVAL_MS = 500;\nconst COSTS_ROWS = 5;\n\ntype CostGroup = \"meshes\" | \"materials\";\ntype CostSort = \"calls\" | \"triangles\";\n\nconst COST_GROUPS: { group: CostGroup; label: string }[] = [\n { group: \"meshes\", label: \"meshes\" },\n { group: \"materials\", label: \"materials\" },\n];\n\nconst COSTS_HELP =\n \"The scene's biggest draw costs in the main render pass, estimated from the scene graph. Hidden and off-screen objects are left out; shadow and post-processing passes aren't counted. Click a row to log its objects to the console; use the eye to hide them and measure the time they cost.\";\n\n/** Frames averaged on each side of hiding a row. */\nconst MEASURE_FRAMES = 30;\n/** How long after hiding before the saving is read, so the average holds only frames without the row. */\nconst MEASURE_SETTLE_MS = 700;\n\ntype HiddenRow = {\n /** GPU ms when a GPU timer exists, else CPU ms: whichever the saving is measured in. */\n baseline: number;\n entry: CostEntry;\n hiddenAt: number;\n metric: \"cpu\" | \"gpu\";\n};\n\nconst meanOfLast = (values: readonly number[], count: number) => {\n const tail = values.slice(-count);\n\n return tail.length === 0 ? 0 : tail.reduce((sum, value) => sum + value, 0) / tail.length;\n};\n\n/** `×2,000` for instances or copies; nothing for a single object. */\nconst copiesOf = (entry: CostEntry) =>\n entry.instances > 1 ? `×${formatCount(entry.instances)}` : \"\";\n\nconst costTitle = (entry: CostEntry) => {\n const objects = entry.objects.length;\n const shape =\n entry.instances > objects\n ? `${formatCount(entry.instances)} instances in ${objects === 1 ? \"one object\" : `${objects} objects`}`\n : `${formatCount(objects)} ${objects === 1 ? \"object\" : \"separate objects\"}`;\n\n return `${entry.label}: ${shape}, ${formatCount(entry.calls)} draw ${entry.calls === 1 ? \"call\" : \"calls\"}, ${formatCount(entry.triangles)} triangles. Click to log to the console.`;\n};\n\nconst readTiming = (sample: Sample, metric: TimingMetric) => {\n switch (metric) {\n case \"fps\":\n return sample.fps;\n case \"cpu\":\n return sample.cpu;\n case \"gpu\":\n return sample.gpu.ms;\n }\n};\n\nconst isTimingAvailable = (sample: Sample, metric: TimingMetric) =>\n metric !== \"gpu\" || sample.gpu.available;\n\nconst whole = (value: number) => Math.round(value).toString();\nconst tenths = (value: number) => value.toFixed(1);\n\nconst NUMBERS: NumberConfig[] = [\n { format: whole, icon: \"gauge\", key: \"fps\", label: \"FPS\", value: (sample) => sample.fps || null },\n {\n format: formatCount,\n icon: \"layers\",\n key: \"calls\",\n label: \"Calls\",\n value: (sample) => sample.render.calls,\n },\n {\n format: tenths,\n icon: \"cpu\",\n key: \"cpu\",\n label: \"CPU\",\n unit: \"ms\",\n value: (sample) => sample.cpu,\n },\n {\n format: tenths,\n icon: \"zap\",\n key: \"gpu\",\n label: \"GPU\",\n unit: \"ms\",\n value: (sample) => (sample.gpu.available ? sample.gpu.ms : null),\n },\n {\n format: tenths,\n icon: \"hourglass\",\n key: \"headroom\",\n label: \"Headroom\",\n unit: \"ms\",\n // Spare time in the frame: the budget minus whichever of CPU and GPU took longer.\n value: (sample, _stats, frameMs) =>\n sample.fps ? frameMs - Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0) : null,\n },\n {\n format: (value) => `${Math.round(value)} Hz`,\n icon: \"monitor\",\n key: \"refresh\",\n label: \"Refresh\",\n unit: \"Hz\",\n value: (_sample, stats) => stats.refreshHz,\n },\n {\n format: whole,\n icon: \"trendingDown\",\n key: \"low\",\n label: \"1% low\",\n value: (_sample, stats) => (stats.frames ? stats.lowFps : null),\n },\n {\n format: tenths,\n icon: \"timer\",\n key: \"p99\",\n label: \"Frame p99\",\n unit: \"ms\",\n value: (_sample, stats) => (stats.frames ? stats.p99Ms : null),\n },\n {\n format: formatCount,\n icon: \"activity\",\n key: \"hitches\",\n label: \"Hitches\",\n value: (_sample, stats) => (stats.frames ? stats.hitches : null),\n },\n {\n format: formatCount,\n icon: \"triangle\",\n key: \"triangles\",\n label: \"Triangles\",\n value: (sample) => sample.render.triangles,\n },\n {\n format: formatCount,\n icon: \"spline\",\n key: \"lines\",\n label: \"Lines\",\n value: (sample) => sample.render.lines,\n },\n {\n format: formatCount,\n icon: \"circleDot\",\n key: \"points\",\n label: \"Points\",\n value: (sample) => sample.render.points,\n },\n {\n format: formatCount,\n icon: \"repeat\",\n key: \"passes\",\n label: \"Render passes\",\n value: (sample) => sample.render.passes,\n },\n {\n format: formatCount,\n icon: \"box\",\n key: \"geometries\",\n label: \"Geometries\",\n value: (sample) => sample.resources.geometries,\n },\n {\n format: formatCount,\n icon: \"image\",\n key: \"textures\",\n label: \"Textures\",\n value: (sample) => sample.resources.textures,\n },\n {\n format: formatCount,\n icon: \"palette\",\n key: \"shaders\",\n label: \"Shaders\",\n value: (sample) => sample.resources.programs,\n },\n];\n\nconst NUMBER_BY_KEY = new Map(NUMBERS.map((config) => [config.key, config]));\n\n/** Canvas can't read CSS custom properties; the budget line's amber per theme. */\nconst GUIDE_COLOR: Record<ResolvedTheme, string> = {\n dark: \"rgba(245, 158, 11, 0.75)\",\n light: \"rgba(180, 83, 9, 0.7)\",\n};\n\n/** `Budget: at most 16.7 ms` / `at least 57`, or `\"\"` without one. */\nconst budgetTitle = (budgets: ResolvedBudgets, key: BudgetKey) => {\n const limit = budgets.limits[key];\n const config = NUMBER_BY_KEY.get(key);\n\n if (limit === undefined || !config) {\n return \"\";\n }\n\n const bound = isFloor(key) ? \"at least\" : \"at most\";\n\n return `Budget: ${bound} ${config.format(limit)}${config.unit ? ` ${config.unit}` : \"\"}`;\n};\n\n/** The sample value a timing graph shows, `null` when not known. */\nconst timingValue = (sample: Sample, metric: TimingMetric) => {\n if (!isTimingAvailable(sample, metric)) {\n return null;\n }\n\n const value = readTiming(sample, metric);\n\n return metric === \"fps\" && value === 0 ? null : value;\n};\n\ntype GraphCanvas = {\n canvas: HTMLCanvasElement;\n ctx: CanvasRenderingContext2D;\n height: number;\n width: number;\n};\n\ntype HudGraph = GraphCanvas & {\n valueEl: HTMLElement;\n};\n\n/**\n * Host-agnostic widget that paints {@link Sample}s. Mount {@link element}\n * anywhere; the view drives its own throttled repaint. Compact is the floating\n * HUD; full is the checkbox list that chooses what compact shows. Styles are\n * not injected here. Call `injectStyles()` or use `mountPerfHud`.\n */\nclass PerformanceView {\n readonly element: HTMLDivElement;\n readonly settings: HudSettings;\n readonly theme: HudTheme;\n\n private readonly ownsTheme: boolean;\n private readonly unsubscribeTheme: () => void;\n private readonly monitor: PerformanceMonitor;\n private readonly minIntervalMs: number;\n private mode: PerformanceViewMode;\n private readonly graphValueEls = new Map<TimingMetric, HTMLElement>();\n private readonly graphCanvases = new Map<TimingMetric, GraphCanvas>();\n private readonly graphCheckboxes = new Map<TimingMetric, HTMLInputElement>();\n private readonly statValueEls = new Map<BudgetKey, HTMLElement>();\n private readonly statCheckboxes = new Map<BudgetKey, HTMLInputElement>();\n private infoCheckbox!: HTMLInputElement;\n private readonly themeRadios = new Map<ThemeMode, HTMLButtonElement>();\n private footerThreeEl!: HTMLElement;\n private footerHardwareEl!: HTMLElement;\n private footerBackendEl!: HTMLElement;\n private footerGpuEl!: HTMLElement;\n private lastEnvironmentKey = \"\";\n private hudGraphsEl!: HTMLElement;\n private hudGridEl!: HTMLElement;\n private readonly hudGraphs = new Map<TimingMetric, HudGraph>();\n private readonly hudNumberEls = new Map<BudgetKey, HTMLElement>();\n private budgets: ResolvedBudgets;\n private budgetsInput: Budgets | false | undefined;\n private detectedHz: number | null = null;\n private readonly hider = new Hider();\n private readonly hiddenRows = new Map<string, HiddenRow>();\n private rafId: number | null = null;\n private lastPaintAt = 0;\n private readonly resizeObserver: ResizeObserver;\n private readonly unsubscribe: () => void;\n private costsOpen = false;\n private costGroup: CostGroup = \"meshes\";\n private costSort: CostSort = \"calls\";\n private lastCostsAt = -Infinity;\n private costsBodyEl!: HTMLElement;\n private costsListEl!: HTMLElement;\n private readonly costGroupRadios = new Map<CostGroup, HTMLButtonElement>();\n private readonly costSortButtons = new Map<CostSort, HTMLButtonElement>();\n\n constructor(options: PerformanceViewOptions) {\n this.monitor = options.monitor;\n this.settings = options.settings ?? new HudSettings();\n this.ownsTheme = !options.theme;\n this.theme = options.theme ?? new HudTheme();\n // The panel's pick, restored from storage, sits on top of the consumer's mode.\n this.theme.setOverride(this.settings.theme);\n this.mode = options.mode ?? \"full\";\n this.minIntervalMs = 1000 / (options.refreshHz ?? 10);\n this.budgetsInput = options.budgets;\n this.budgets = resolveBudgets(options.budgets);\n this.element = document.createElement(\"div\");\n this.element.className = \"perf-monitor\";\n this.applyModeClass();\n this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());\n this.build();\n this.applyInfoClass();\n this.rebuildHud();\n this.onThemeChanged();\n this.unsubscribe = this.settings.subscribe(() => this.onSelectionChanged());\n this.unsubscribeTheme = this.theme.subscribe(() => this.onThemeChanged());\n }\n\n start() {\n if (this.rafId === null) {\n this.rafId = requestAnimationFrame(this.frame);\n }\n }\n\n stop() {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n setMode(mode: PerformanceViewMode) {\n if (mode === this.mode) {\n return;\n }\n\n this.mode = mode;\n this.applyModeClass();\n this.resizeCanvases();\n }\n\n getMode() {\n return this.mode;\n }\n\n /** Replace the budgets; `false` turns them all off. Applies on the next paint. */\n setBudgets(budgets: Budgets | false | undefined) {\n this.budgetsInput = budgets;\n this.budgets = resolveBudgets(budgets, this.detectedHz);\n this.applyBudgetTitles();\n }\n\n dispose() {\n this.stop();\n this.showAllHidden();\n this.unsubscribe();\n this.unsubscribeTheme();\n\n if (this.ownsTheme) {\n this.theme.dispose();\n }\n\n this.resizeObserver.disconnect();\n this.element.remove();\n }\n\n private frame = (now: number) => {\n this.rafId = requestAnimationFrame(this.frame);\n\n if (now - this.lastPaintAt < this.minIntervalMs) {\n return;\n }\n\n this.lastPaintAt = now;\n this.render();\n };\n\n private render() {\n const sample = this.monitor.getSample();\n const stats = this.monitor.getFrameStats();\n this.followRefresh(stats.refreshHz);\n\n if (this.mode === \"compact\") {\n this.renderHud(sample, stats);\n\n if (this.settings.info) {\n this.renderFooter();\n }\n\n return;\n }\n\n for (const config of TIMINGS) {\n this.paintGraph(\n config,\n sample,\n this.graphValueEls.get(config.metric)!,\n this.graphCanvases.get(config.metric)!,\n \"unavailable\",\n );\n }\n\n for (const config of NUMBERS) {\n this.paintNumber(config, this.statValueEls.get(config.key)!, sample, stats);\n }\n\n if (this.costsOpen && performance.now() - this.lastCostsAt >= COSTS_INTERVAL_MS) {\n this.renderCosts();\n }\n\n this.renderFooter();\n }\n\n private renderCosts() {\n this.lastCostsAt = performance.now();\n const cost = this.monitor.getSceneCost();\n // Hidden rows stay listed, first, so they can be shown again and read their saving.\n const hidden = [...this.hiddenRows.values()].map((row) => this.hiddenCostRow(row));\n\n if (!cost) {\n this.costsListEl.replaceChildren(...hidden, this.costsNote(\"No scene rendered yet.\"));\n\n return;\n }\n\n const entries = (this.costGroup === \"meshes\" ? cost.meshes : cost.materials).filter(\n (entry) => !this.hiddenRows.has(entry.key),\n );\n\n if (this.costSort === \"triangles\") {\n entries.sort((a, b) => b.triangles - a.triangles || b.calls - a.calls);\n }\n\n if (entries.length === 0 && hidden.length === 0) {\n this.costsListEl.replaceChildren(this.costsNote(\"Nothing drawn.\"));\n\n return;\n }\n\n const rows = entries.slice(0, COSTS_ROWS).map((entry) => {\n const row = this.costRowShell(entry);\n const calls = document.createElement(\"span\");\n calls.className = \"perf-monitor__cost-value\";\n calls.textContent = formatCount(entry.calls);\n\n const triangles = document.createElement(\"span\");\n triangles.className = \"perf-monitor__cost-value\";\n triangles.textContent = formatCount(entry.triangles);\n\n row.append(calls, triangles, this.eyeButton(entry, false));\n\n return row;\n });\n\n this.costsListEl.replaceChildren(...hidden, ...rows);\n }\n\n /** Name button (logs the objects) in a row; the caller appends the value cells and the eye. */\n private costRowShell(entry: CostEntry) {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__cost\";\n\n const name = document.createElement(\"button\");\n name.type = \"button\";\n name.className = \"perf-monitor__cost-name\";\n name.title = costTitle(entry);\n name.textContent = entry.label;\n\n const copies = copiesOf(entry);\n\n if (copies) {\n const suffix = document.createElement(\"span\");\n suffix.className = \"perf-monitor__cost-copies\";\n suffix.textContent = ` ${copies}`;\n name.append(suffix);\n }\n\n name.addEventListener(\"click\", () => {\n // oxlint-disable-next-line no-console -- handing the objects to devtools is the feature\n console.log(`three-meter: ${entry.label}`, entry.objects);\n });\n\n row.append(name);\n\n return row;\n }\n\n /** A hidden row: what hiding it saved, measured from the timing history. */\n private hiddenCostRow(hidden: HiddenRow) {\n const row = this.costRowShell(hidden.entry);\n row.classList.add(\"is-hidden\");\n\n const saved = document.createElement(\"span\");\n saved.className = \"perf-monitor__cost-saved\";\n const label = hidden.metric.toUpperCase();\n\n if (performance.now() - hidden.hiddenAt < MEASURE_SETTLE_MS) {\n saved.textContent = \"measuring…\";\n } else {\n const now = meanOfLast(this.monitor.getHistory(hidden.metric), MEASURE_FRAMES);\n const delta = hidden.baseline - now;\n saved.textContent = `${delta >= 0 ? \"−\" : \"+\"}${Math.abs(delta).toFixed(1)} ms ${label}`;\n saved.title = `${label} time went from ${hidden.baseline.toFixed(1)} ms to ${now.toFixed(1)} ms with this hidden, averaged over ${MEASURE_FRAMES} frames each side.`;\n }\n\n row.append(saved, this.eyeButton(hidden.entry, true));\n\n return row;\n }\n\n private eyeButton(entry: CostEntry, hidden: boolean) {\n const eye = document.createElement(\"button\");\n eye.type = \"button\";\n eye.className = \"perf-monitor__cost-eye\";\n eye.setAttribute(\"aria-pressed\", String(hidden));\n const label = hidden ? `Show ${entry.label} again` : `Hide ${entry.label} to measure its cost`;\n eye.title = label;\n eye.setAttribute(\"aria-label\", label);\n eye.append(createIcon(hidden ? \"eyeOff\" : \"eye\", \"perf-monitor__icon\"));\n eye.addEventListener(\"click\", () => (hidden ? this.showRow(entry.key) : this.hideRow(entry)));\n\n return eye;\n }\n\n private hideRow(entry: CostEntry) {\n const sample = this.monitor.getSample();\n const metric = sample.gpu.available ? \"gpu\" : \"cpu\";\n\n this.hiddenRows.set(entry.key, {\n baseline: meanOfLast(this.monitor.getHistory(metric), MEASURE_FRAMES),\n entry,\n hiddenAt: performance.now(),\n metric,\n });\n // The whole scene's matches, not just the on-screen ones in the row, so copies that come\n // into view while hidden stay hidden.\n const all = this.monitor.getObjectsForKey(entry.key);\n this.hider.hide(entry.key, all.length > 0 ? all : entry.objects);\n this.renderCosts();\n }\n\n private showRow(key: string) {\n this.hider.show(key);\n this.hiddenRows.delete(key);\n this.renderCosts();\n }\n\n /** Everything hidden goes back: on closing top costs and on dispose. */\n private showAllHidden() {\n this.hider.restoreAll();\n this.hiddenRows.clear();\n }\n\n private costsNote(text: string) {\n const note = document.createElement(\"div\");\n note.className = \"perf-monitor__costs-note\";\n note.textContent = text;\n\n return note;\n }\n\n /** The monitor caches the environment once its backend settles; until then this re-reads. */\n private renderFooter() {\n const environment = this.monitor.getEnvironment();\n const three = environment.three ? `three r${environment.three}` : \"\";\n const backend = backendLabel(environment);\n const gpu = environment.gpu ?? \"\";\n const key = `${three}\\n${backend}\\n${gpu}`;\n\n if (key === this.lastEnvironmentKey) {\n return;\n }\n\n this.lastEnvironmentKey = key;\n this.footerThreeEl.textContent = three;\n this.footerBackendEl.textContent = backend;\n this.footerBackendEl.hidden = backend === \"\";\n this.footerBackendEl.title = environment.fallback\n ? \"WebGPURenderer couldn't get WebGPU and fell back to WebGL2\"\n : \"\";\n this.footerBackendEl.classList.toggle(\"is-fallback\", environment.fallback);\n this.footerGpuEl.textContent = gpu;\n this.footerGpuEl.title = gpu;\n this.footerHardwareEl.hidden = backend === \"\" && gpu === \"\";\n }\n\n /** Budgets follow the detected refresh rate unless `targetFps` was given. */\n private followRefresh(hz: number | null) {\n if (hz === this.detectedHz) {\n return;\n }\n\n this.detectedHz = hz;\n this.budgets = resolveBudgets(this.budgetsInput, hz);\n this.applyBudgetTitles();\n }\n\n private renderHud(sample: Sample, stats: FrameStats) {\n for (const [key, element] of this.hudNumberEls) {\n const config = NUMBER_BY_KEY.get(key);\n\n if (config) {\n this.paintNumber(config, element, sample, stats);\n }\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n const config = TIMINGS.find((timing) => timing.metric === metric)!;\n this.paintGraph(config, sample, graph.valueEl, graph, \"—\");\n }\n }\n\n private paintNumber(\n config: NumberConfig,\n element: HTMLElement,\n sample: Sample,\n stats: FrameStats,\n ) {\n const value = config.value(sample, stats, this.budgets.frameMs);\n element.textContent = value === null ? \"—\" : config.format(value);\n element.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.key, value));\n }\n\n /** Value and sparkline, with the budget line drawn once the series reaches it. */\n private paintGraph(\n config: TimingConfig,\n sample: Sample,\n valueEl: HTMLElement,\n graph: GraphCanvas,\n missing: string,\n ) {\n const value = timingValue(sample, config.metric);\n const available = isTimingAvailable(sample, config.metric);\n const limit = this.budgets.limits[config.metric];\n\n valueEl.textContent = available\n ? this.formatTiming(config, readTiming(sample, config.metric))\n : missing;\n valueEl.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.metric, value));\n\n drawSparkline(\n graph.ctx,\n graph.width,\n graph.height,\n available ? this.monitor.getHistory(config.metric) : [],\n config.style[this.theme.resolved],\n limit === undefined ? undefined : { color: GUIDE_COLOR[this.theme.resolved], value: limit },\n );\n }\n\n /** Each value's tooltip names its budget, so amber explains itself. */\n private applyBudgetTitles() {\n for (const [key, element] of this.statValueEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [key, element] of this.hudNumberEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [metric, element] of this.graphValueEls) {\n element.title = budgetTitle(this.budgets, metric);\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n graph.valueEl.title = budgetTitle(this.budgets, metric);\n }\n }\n\n private formatTiming(config: TimingConfig, value: number) {\n return `${config.format(value)}${config.unit ? ` ${config.unit}` : \"\"}`;\n }\n\n private build() {\n const hud = document.createElement(\"div\");\n hud.className = \"perf-monitor__hud\";\n this.hudGraphsEl = document.createElement(\"div\");\n this.hudGraphsEl.className = \"perf-monitor__hud-graphs\";\n this.hudGridEl = document.createElement(\"div\");\n this.hudGridEl.className = \"perf-monitor__hud-grid\";\n hud.append(this.hudGraphsEl, this.hudGridEl);\n\n // Option rows read icon, label, control; the stat rows below keep the\n // checkbox first, so the two groups scan differently on purpose.\n const options = document.createElement(\"div\");\n options.className = \"perf-monitor__section perf-monitor__options\";\n\n const themeRow = document.createElement(\"div\");\n themeRow.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const themeLabel = document.createElement(\"span\");\n themeLabel.className = \"perf-monitor__label\";\n themeLabel.textContent = \"theme\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Panel theme\");\n\n for (const option of THEME_OPTIONS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", \"false\");\n radio.setAttribute(\"aria-label\", option.label);\n radio.title = option.label;\n radio.append(createIcon(option.icon, \"perf-monitor__icon\"));\n radio.addEventListener(\"click\", () => this.settings.setTheme(option.mode));\n this.themeRadios.set(option.mode, radio);\n segment.append(radio);\n }\n\n themeRow.append(createIcon(\"contrast\", \"perf-monitor__icon\"), themeLabel, segment);\n\n // View-local, not a setting: it's a way to learn the panel, not a preference.\n const explainRow = document.createElement(\"label\");\n explainRow.className = \"perf-monitor__row\";\n\n const explainCheckbox = this.buildCheckbox(false, \"Explain each metric\", (on) =>\n this.element.classList.toggle(\"perf-monitor--explain\", on),\n );\n\n const explainLabel = document.createElement(\"span\");\n explainLabel.className = \"perf-monitor__label\";\n explainLabel.textContent = \"explain metrics\";\n\n explainRow.append(createIcon(\"help\", \"perf-monitor__icon\"), explainLabel, explainCheckbox);\n // Dim on leave is a disc on the floating HUD (`mountPerfHud`), beside drag and expand.\n options.append(themeRow, explainRow, this.buildReportRow());\n\n const graphs = document.createElement(\"div\");\n graphs.className = \"perf-monitor__graphs\";\n\n for (const config of TIMINGS) {\n const graph = document.createElement(\"div\");\n graph.className = \"perf-monitor__graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n // The whole overlay is the label, so a click anywhere on the graph toggles it.\n const overlay = document.createElement(\"label\");\n overlay.className = \"perf-monitor__graph-overlay perf-monitor__graph-overlay--toggle\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isGraphEnabled(config.metric),\n `Show ${config.label} graph in HUD`,\n (on) => this.settings.setGraphEnabled(config.metric, on),\n );\n this.graphCheckboxes.set(config.metric, checkbox);\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n this.graphValueEls.set(config.metric, value);\n\n const tail = document.createElement(\"span\");\n tail.className = \"perf-monitor__graph-head\";\n tail.append(value, checkbox);\n\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n overlay.append(head, tail);\n graph.append(canvas, overlay);\n graphs.append(graph);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.graphCanvases.set(config.metric, { canvas, ctx, height: 0, width: 0 });\n this.resizeObserver.observe(graph);\n }\n }\n\n const stats = document.createElement(\"div\");\n stats.className = \"perf-monitor__section\";\n\n for (const config of NUMBERS) {\n const rowEl = document.createElement(\"label\");\n rowEl.className = \"perf-monitor__row\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isNumberEnabled(config.key),\n `Show ${config.label} in HUD`,\n (on) => this.settings.setNumberEnabled(config.key, on),\n );\n this.statCheckboxes.set(config.key, checkbox);\n\n const help = METRIC_HELP[config.key] ?? \"\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = config.label;\n label.title = help;\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__value\";\n value.textContent = \"—\";\n this.statValueEls.set(config.key, value);\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = help;\n\n rowEl.append(createIcon(config.icon, \"perf-monitor__icon\"), label, value, checkbox, hint);\n stats.append(rowEl);\n }\n\n // Icon, rows, checkbox like the stat rows; icon and checkbox hide in compact, the rows don't.\n const footer = document.createElement(\"div\");\n footer.className = \"perf-monitor__footer\";\n\n this.infoCheckbox = this.buildCheckbox(\n this.settings.info,\n \"Show version and environment in HUD\",\n (on) => this.settings.setInfo(on),\n );\n\n const footerBody = document.createElement(\"div\");\n footerBody.className = \"perf-monitor__footer-body\";\n\n // Software on the first row, hardware on the second.\n const softwareRow = document.createElement(\"div\");\n softwareRow.className = \"perf-monitor__footer-row\";\n\n const release = document.createElement(\"a\");\n release.className = \"perf-monitor__link\";\n release.href = RELEASE_URL;\n release.target = \"_blank\";\n release.rel = \"noopener noreferrer\";\n release.title = `Release notes for v${version}`;\n release.textContent = `three-meter v${version}`;\n\n this.footerThreeEl = document.createElement(\"span\");\n softwareRow.append(release, this.footerThreeEl);\n\n this.footerHardwareEl = document.createElement(\"div\");\n this.footerHardwareEl.className = \"perf-monitor__footer-row\";\n this.footerHardwareEl.hidden = true;\n\n this.footerBackendEl = document.createElement(\"span\");\n this.footerBackendEl.className = \"perf-monitor__badge\";\n\n this.footerGpuEl = document.createElement(\"span\");\n this.footerGpuEl.className = \"perf-monitor__footer-gpu\";\n\n this.footerHardwareEl.append(this.footerBackendEl, this.footerGpuEl);\n footerBody.append(softwareRow, this.footerHardwareEl);\n footer.append(createIcon(\"info\", \"perf-monitor__icon\"), footerBody, this.infoCheckbox);\n\n this.element.append(hud, options, graphs, stats, this.buildCosts(), footer);\n }\n\n private rebuildHud() {\n const { selection } = this.settings;\n\n for (const graph of this.hudGraphs.values()) {\n this.resizeObserver.unobserve(graph.canvas.parentElement ?? graph.canvas);\n }\n\n this.hudGraphs.clear();\n this.hudNumberEls.clear();\n this.hudGraphsEl.replaceChildren();\n this.hudGridEl.replaceChildren();\n\n for (const config of TIMINGS) {\n if (!selection.graphs.has(config.metric)) {\n continue;\n }\n\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__hud-graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"perf-monitor__graph-overlay\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n\n overlay.append(head, value);\n row.append(canvas, overlay);\n this.hudGraphsEl.append(row);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.hudGraphs.set(config.metric, { canvas, ctx, height: 0, valueEl: value, width: 0 });\n this.resizeObserver.observe(row);\n }\n }\n\n for (const config of NUMBERS) {\n if (!selection.numbers.has(config.key)) {\n continue;\n }\n\n const item = document.createElement(\"div\");\n item.className = \"perf-monitor__hud-item\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__hud-label\";\n label.title = METRIC_HELP[config.key] ?? \"\";\n label.append(createIcon(config.icon, \"perf-monitor__icon\"), config.label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__hud-value\";\n value.textContent = \"—\";\n\n item.append(label, value);\n this.hudGridEl.append(item);\n this.hudNumberEls.set(config.key, value);\n }\n\n this.applyBudgetTitles();\n this.resizeCanvases();\n }\n\n private onSelectionChanged() {\n for (const [metric, checkbox] of this.graphCheckboxes) {\n checkbox.checked = this.settings.isGraphEnabled(metric);\n }\n\n for (const [key, checkbox] of this.statCheckboxes) {\n checkbox.checked = this.settings.isNumberEnabled(key);\n }\n\n this.infoCheckbox.checked = this.settings.info;\n this.applyInfoClass();\n this.theme.setOverride(this.settings.theme);\n this.rebuildHud();\n }\n\n /** Repaint the palette and mark the radio matching what applies (pick, else consumer mode). */\n private onThemeChanged() {\n applyTheme(this.element, this.theme);\n\n for (const [mode, radio] of this.themeRadios) {\n radio.setAttribute(\"aria-checked\", String(mode === this.theme.effective));\n }\n }\n\n /** \"top costs\": a toggle row, then meshes / materials and a sortable top five. */\n private buildCosts() {\n const section = document.createElement(\"div\");\n section.className = \"perf-monitor__section perf-monitor__costs\";\n\n const toggleRow = document.createElement(\"label\");\n toggleRow.className = \"perf-monitor__row\";\n toggleRow.title = COSTS_HELP;\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"top costs\";\n\n const checkbox = this.buildCheckbox(false, \"Show the scene's biggest draw costs\", (on) => {\n this.costsOpen = on;\n this.costsBodyEl.hidden = !on;\n\n if (!on) {\n this.showAllHidden();\n }\n\n if (on) {\n this.renderCosts();\n }\n });\n\n toggleRow.append(createIcon(\"flame\", \"perf-monitor__icon\"), label, checkbox);\n\n this.costsBodyEl = document.createElement(\"div\");\n this.costsBodyEl.className = \"perf-monitor__costs-body\";\n this.costsBodyEl.hidden = true;\n\n const head = document.createElement(\"div\");\n head.className = \"perf-monitor__cost perf-monitor__cost--head\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Group costs by\");\n\n for (const option of COST_GROUPS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option perf-monitor__segment-option--text\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", String(option.group === this.costGroup));\n radio.textContent = option.label;\n radio.addEventListener(\"click\", () => {\n this.costGroup = option.group;\n\n for (const [group, button] of this.costGroupRadios) {\n button.setAttribute(\"aria-checked\", String(group === option.group));\n }\n\n this.renderCosts();\n });\n this.costGroupRadios.set(option.group, radio);\n segment.append(radio);\n }\n\n head.append(segment);\n\n for (const sort of [\"calls\", \"triangles\"] as const) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__cost-sort\";\n button.textContent = sort === \"calls\" ? \"calls\" : \"tris\";\n button.title = `Sort by ${sort === \"calls\" ? \"draw calls\" : \"triangles\"}`;\n button.setAttribute(\"aria-pressed\", String(sort === this.costSort));\n button.addEventListener(\"click\", () => {\n this.costSort = sort;\n\n for (const [key, other] of this.costSortButtons) {\n other.setAttribute(\"aria-pressed\", String(key === sort));\n }\n\n this.renderCosts();\n });\n this.costSortButtons.set(sort, button);\n head.append(button);\n }\n\n // Keeps the header's columns over the rows', which end in an eye button.\n head.append(document.createElement(\"span\"));\n\n this.costsListEl = document.createElement(\"div\");\n this.costsListEl.className = \"perf-monitor__costs-list\";\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = COSTS_HELP;\n\n this.costsBodyEl.append(head, this.costsListEl, hint);\n section.append(toggleRow, this.costsBodyEl);\n\n return section;\n }\n\n private buildReportRow() {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"report\";\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__button\";\n button.title = \"Copy versions, GPU and current metrics as Markdown for a bug report\";\n button.textContent = \"copy\";\n\n let resetTimer = 0;\n\n const flash = (text: string) => {\n button.textContent = text;\n window.clearTimeout(resetTimer);\n resetTimer = window.setTimeout(() => {\n button.textContent = \"copy\";\n }, COPY_FEEDBACK_MS);\n };\n\n button.addEventListener(\"click\", () => {\n void copyText(formatReport(this.monitor)).then((copied) =>\n flash(copied ? \"copied\" : \"failed\"),\n );\n });\n\n row.append(createIcon(\"clipboard\", \"perf-monitor__icon\"), label, button);\n\n return row;\n }\n\n private buildCheckbox(checked: boolean, title: string, onChange: (enabled: boolean) => void) {\n const checkbox = document.createElement(\"input\");\n checkbox.type = \"checkbox\";\n checkbox.className = \"perf-monitor__checkbox\";\n checkbox.checked = checked;\n checkbox.title = title;\n checkbox.setAttribute(\"aria-label\", title);\n checkbox.addEventListener(\"change\", () => onChange(checkbox.checked));\n\n return checkbox;\n }\n\n private applyInfoClass() {\n this.element.classList.toggle(\"perf-monitor--info\", this.settings.info);\n }\n\n private applyModeClass() {\n this.element.classList.toggle(\"perf-monitor--compact\", this.mode === \"compact\");\n this.element.classList.toggle(\"perf-monitor--full\", this.mode === \"full\");\n }\n\n private resizeCanvases() {\n const dpr = window.devicePixelRatio || 1;\n\n const sizeAll = (graphs: Iterable<GraphCanvas>) => {\n for (const graph of graphs) {\n const { canvas, ctx } = graph;\n const width = canvas.clientWidth;\n const height = canvas.clientHeight;\n\n if (width === 0 || height === 0) {\n continue;\n }\n\n canvas.width = Math.round(width * dpr);\n canvas.height = Math.round(height * dpr);\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n graph.width = width;\n graph.height = height;\n }\n };\n\n sizeAll(this.graphCanvases.values());\n sizeAll(this.hudGraphs.values());\n }\n}\n\nexport { PerformanceView };\nexport type { PerformanceViewMode, PerformanceViewOptions };\n","/**\n * The floating HUD: the metrics card in the shared dev-panel frame\n * (`mountDevPanel`: docked host, brand label, drag / compact-full / dim discs).\n * The dim disc flips `settings.dim`; the palette comes from `theme`. This is what the React\n * `PerfHud` wraps; a vanilla three app calls it directly.\n */\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { Budgets } from \"./budgets.ts\";\nimport { mountDevPanel } from \"./dev-panel.ts\";\nimport type { DefaultPlacement } from \"./dock-panel.ts\";\nimport { DEFAULT_STORAGE_KEY, HudSettings } from \"./hud-settings.ts\";\nimport { PerformanceView, type PerformanceViewMode } from \"./performance-view.ts\";\nimport { HudTheme, type ThemeMode } from \"./theme.ts\";\n\ntype MountPerfHudOptions = {\n /** Start compact (the card) or full (the checkbox list). Default `compact`. */\n mode?: PerformanceViewMode;\n /**\n * `dark`, `light`, or `system` to follow the OS preference. Default\n * `system`. Change it later with the handle's `setTheme`. A pick made in\n * the panel's own theme row (kept in `settings`) sits on top of this.\n */\n theme?: ThemeMode;\n /** localStorage key for the selection and the dock. `null` disables persistence. */\n storageKey?: string | null;\n /** Bring your own; otherwise one is created from `storageKey`. */\n settings?: HudSettings;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host element goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`.\n * Timing budgets default from `targetFps` (60); `false` turns them all off.\n * Change later with the handle's `setBudgets`.\n */\n budgets?: Budgets | false;\n /** Accessible name of the panel. Default `Performance`. */\n label?: string;\n};\n\ntype PerfHudHandle = {\n element: HTMLDivElement;\n view: PerformanceView;\n settings: HudSettings;\n /**\n * `mode` is the consumer layer, `override` the panel's pick, `effective`\n * whichever applies, `resolved` the `dark` / `light` on screen. Subscribe\n * for changes, including the OS preference moving under `system`.\n */\n theme: HudTheme;\n getMode: () => PerformanceViewMode;\n setMode: (mode: PerformanceViewMode) => void;\n /** The consumer layer. The panel's pick, if any, is `settings.theme`. */\n getTheme: () => ThemeMode;\n /** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */\n setTheme: (mode: ThemeMode) => void;\n /** Replace the budgets; `false` turns them all off. */\n setBudgets: (budgets: Budgets | false | undefined) => void;\n dispose: () => void;\n};\n\nconst mountPerfHud = (\n monitor: PerformanceMonitor,\n options: MountPerfHudOptions = {},\n): PerfHudHandle => {\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const settings = options.settings ?? new HudSettings({ storageKey });\n const theme = new HudTheme(options.theme ?? \"system\");\n\n const view = new PerformanceView({\n budgets: options.budgets,\n mode: options.mode ?? \"compact\",\n monitor,\n refreshHz: options.refreshHz,\n settings,\n theme,\n });\n\n // After the view: constructing it restores the panel's stored theme pick.\n const panel = mountDevPanel({\n brand: \"three-meter\",\n content: view.element,\n defaultPlacement: options.defaultPlacement,\n dim: {\n get: () => settings.dim,\n set: (on) => settings.setDim(on),\n subscribe: (listener) => settings.subscribe(listener),\n },\n injectStyles: options.injectStyles,\n label: options.label ?? \"Performance\",\n onToggle: () => setMode(view.getMode() === \"compact\" ? \"full\" : \"compact\"),\n parent: options.parent,\n storageKey,\n theme,\n toggleLabel: \"Toggle full performance metrics\",\n });\n const host = panel.element;\n\n const applyMode = () => {\n host.classList.toggle(\"perf-hud--full\", view.getMode() === \"full\");\n };\n\n const setMode = (mode: PerformanceViewMode) => {\n view.setMode(mode);\n applyMode();\n panel.refresh();\n };\n\n applyMode();\n view.start();\n\n return {\n dispose: () => {\n panel.dispose();\n view.dispose();\n theme.dispose();\n },\n element: host,\n getMode: () => view.getMode(),\n setBudgets: (budgets) => view.setBudgets(budgets),\n setMode,\n getTheme: () => theme.mode,\n setTheme: (mode: ThemeMode) => theme.setMode(mode),\n settings,\n theme,\n view,\n };\n};\n\nexport { mountPerfHud };\nexport type { MountPerfHudOptions, PerfHudHandle };\n"],"mappings":"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"}
|