@zkmake/three-meter 0.8.0 → 0.9.1
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.
- package/README.md +26 -8
- package/dist/{mount-perf-hud-DkSNfZmB.d.ts → mount-perf-hud-BKpk62Pp.d.ts} +1 -2
- package/dist/{mount-perf-hud-B_cmTG2M.js → mount-perf-hud-CebnfB8t.js} +32 -17
- package/dist/mount-perf-hud-CebnfB8t.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/styles.css +18 -2
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +1 -1
- package/package.json +1 -1
- package/dist/mount-perf-hud-B_cmTG2M.js.map +0 -1
package/README.md
CHANGED
|
@@ -13,13 +13,31 @@ Live demo: [three-meter.pages.dev](https://three-meter.pages.dev/), with a vanil
|
|
|
13
13
|
React Three Fiber take on the same scene. Add `?webgpu` for the WebGPU renderer and `?count=30000` to
|
|
14
14
|
load the scene up past the demo's triangle budget.
|
|
15
15
|
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
<table align="center">
|
|
17
|
+
<tr>
|
|
18
|
+
<th></th>
|
|
19
|
+
<th>Expanded</th>
|
|
20
|
+
<th>Collapsed</th>
|
|
21
|
+
</tr>
|
|
22
|
+
<tr>
|
|
23
|
+
<th>Dark</th>
|
|
24
|
+
<td valign="top">
|
|
25
|
+
<img src="https://raw.githubusercontent.com/zkmake/three-meter/main/docs/hud-full.png" width="318" alt="The HUD in full mode, dark theme. Option rows for theme (light, system, dark), explain metrics and a copy-report button; FPS, CPU and GPU sparklines with live values, the FPS graph showing a dashed budget line; then one row per metric, each with an icon, its value and a checkbox choosing whether the compact card shows it (FPS, calls, CPU, GPU, 1% low, frame p99, hitches, triangles, lines, points, render passes, geometries, textures, shaders), with the triangle count in amber for being over its budget; a top costs toggle; and a footer with the three-meter version, the three revision, a WebGL2 backend badge and the GPU name. Three discs beside it: a drag grip, the compact/full toggle and dim on leave." />
|
|
26
|
+
</td>
|
|
27
|
+
<td valign="top">
|
|
28
|
+
<img src="https://raw.githubusercontent.com/zkmake/three-meter/main/docs/hud-compact.png" width="314" alt="The HUD collapsed, dark theme: FPS and GPU sparklines with a dashed budget line on the FPS graph, a two-column grid of metrics with icons (FPS, calls, CPU, GPU, 1% low, frame p99, triangles in amber for being over budget, geometries, textures, shaders), and the footer with versions, backend and GPU. Three discs beside it: drag, expand and dim on leave." />
|
|
29
|
+
</td>
|
|
30
|
+
</tr>
|
|
31
|
+
<tr>
|
|
32
|
+
<th>Light</th>
|
|
33
|
+
<td valign="top">
|
|
34
|
+
<img src="https://raw.githubusercontent.com/zkmake/three-meter/main/docs/hud-full-light.png" width="318" alt="The HUD in full mode, light theme. Option rows for theme (light, system, dark), explain metrics and a copy-report button; FPS, CPU and GPU sparklines with live values, the FPS graph showing a dashed budget line; then one row per metric, each with an icon, its value and a checkbox choosing whether the compact card shows it (FPS, calls, CPU, GPU, 1% low, frame p99, hitches, triangles, lines, points, render passes, geometries, textures, shaders), with the triangle count in amber for being over its budget; a top costs toggle; and a footer with the three-meter version, the three revision, a WebGL2 backend badge and the GPU name. Three discs beside it: a drag grip, the compact/full toggle and dim on leave." />
|
|
35
|
+
</td>
|
|
36
|
+
<td valign="top">
|
|
37
|
+
<img src="https://raw.githubusercontent.com/zkmake/three-meter/main/docs/hud-compact-light.png" width="314" alt="The HUD collapsed, light theme: FPS and GPU sparklines with a dashed budget line on the FPS graph, a two-column grid of metrics with icons (FPS, calls, CPU, GPU, 1% low, frame p99, triangles in amber for being over budget, geometries, textures, shaders), and the footer with versions, backend and GPU. Three discs beside it: drag, expand and dim on leave." />
|
|
38
|
+
</td>
|
|
39
|
+
</tr>
|
|
40
|
+
</table>
|
|
23
41
|
|
|
24
42
|
- **Spot problems at a glance.** Values past their budget turn amber, and every metric explains
|
|
25
43
|
itself on hover, so you don't need to know what a p99 is to see that one is bad.
|
|
@@ -32,7 +50,7 @@ load the scene up past the demo's triangle budget.
|
|
|
32
50
|
- **One-click bug reports.** Copy versions, backend, GPU and the current numbers as Markdown.
|
|
33
51
|
- **Stays out of the way.** The sampler and the card are separate components, so toggling the HUD
|
|
34
52
|
never remounts your canvas. The card docks to a screen edge, remembers where you put it, and hides
|
|
35
|
-
its controls until the pointer comes near.
|
|
53
|
+
its controls until the pointer comes near; a third disc there dims it when the pointer leaves.
|
|
36
54
|
|
|
37
55
|
## Entry points
|
|
38
56
|
|
|
@@ -188,7 +188,6 @@ declare class PerformanceView {
|
|
|
188
188
|
private readonly graphCheckboxes;
|
|
189
189
|
private readonly statValueEls;
|
|
190
190
|
private readonly statCheckboxes;
|
|
191
|
-
private dimCheckbox;
|
|
192
191
|
private infoCheckbox;
|
|
193
192
|
private readonly themeRadios;
|
|
194
193
|
private footerThreeEl;
|
|
@@ -302,4 +301,4 @@ type PerfHudHandle = {
|
|
|
302
301
|
declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
|
|
303
302
|
//#endregion
|
|
304
303
|
export { dockPanel as C, resolveBudgets as D, ResolvedBudgets as E, ScreenEdge as S, Budgets as T, DefaultPlacement as _, PerformanceViewMode as a, DockPanelOptions as b, HudDefaults as c, HudSettingsOptions as d, HudTheme as f, isThemeMode as g, ThemeMode as h, PerformanceView as i, HudSelection as l, THEME_MODES as m, PerfHudHandle as n, PerformanceViewOptions as o, ResolvedTheme as p, mountPerfHud as r, DEFAULT_STORAGE_KEY as s, MountPerfHudOptions as t, HudSettings as u, DockAlign as v, BudgetKey as w, Placement as x, DockHandle as y };
|
|
305
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
304
|
+
//# sourceMappingURL=mount-perf-hud-BKpk62Pp.d.ts.map
|
|
@@ -527,7 +527,7 @@ const createIcon = (name, className) => {
|
|
|
527
527
|
};
|
|
528
528
|
//#endregion
|
|
529
529
|
//#region package.json
|
|
530
|
-
var version = "0.
|
|
530
|
+
var version = "0.9.1";
|
|
531
531
|
//#endregion
|
|
532
532
|
//#region src/ui/clipboard.ts
|
|
533
533
|
/**
|
|
@@ -957,7 +957,6 @@ var PerformanceView = class {
|
|
|
957
957
|
graphCheckboxes = /* @__PURE__ */ new Map();
|
|
958
958
|
statValueEls = /* @__PURE__ */ new Map();
|
|
959
959
|
statCheckboxes = /* @__PURE__ */ new Map();
|
|
960
|
-
dimCheckbox;
|
|
961
960
|
infoCheckbox;
|
|
962
961
|
themeRadios = /* @__PURE__ */ new Map();
|
|
963
962
|
footerThreeEl;
|
|
@@ -1189,13 +1188,6 @@ var PerformanceView = class {
|
|
|
1189
1188
|
segment.append(radio);
|
|
1190
1189
|
}
|
|
1191
1190
|
themeRow.append(createIcon("contrast", "perf-monitor__icon"), themeLabel, segment);
|
|
1192
|
-
const dimRow = document.createElement("label");
|
|
1193
|
-
dimRow.className = "perf-monitor__row";
|
|
1194
|
-
this.dimCheckbox = this.buildCheckbox(this.settings.dim, "Dim the performance panel when the pointer leaves", (on) => this.settings.setDim(on));
|
|
1195
|
-
const dimLabel = document.createElement("span");
|
|
1196
|
-
dimLabel.className = "perf-monitor__label";
|
|
1197
|
-
dimLabel.textContent = "dim on leave";
|
|
1198
|
-
dimRow.append(createIcon("blend", "perf-monitor__icon"), dimLabel, this.dimCheckbox);
|
|
1199
1191
|
const explainRow = document.createElement("label");
|
|
1200
1192
|
explainRow.className = "perf-monitor__row";
|
|
1201
1193
|
const explainCheckbox = this.buildCheckbox(false, "Explain each metric", (on) => this.element.classList.toggle("perf-monitor--explain", on));
|
|
@@ -1203,7 +1195,7 @@ var PerformanceView = class {
|
|
|
1203
1195
|
explainLabel.className = "perf-monitor__label";
|
|
1204
1196
|
explainLabel.textContent = "explain metrics";
|
|
1205
1197
|
explainRow.append(createIcon("help", "perf-monitor__icon"), explainLabel, explainCheckbox);
|
|
1206
|
-
options.append(themeRow,
|
|
1198
|
+
options.append(themeRow, explainRow, this.buildReportRow());
|
|
1207
1199
|
const graphs = document.createElement("div");
|
|
1208
1200
|
graphs.className = "perf-monitor__graphs";
|
|
1209
1201
|
for (const config of TIMINGS) {
|
|
@@ -1312,10 +1304,13 @@ var PerformanceView = class {
|
|
|
1312
1304
|
label.className = "perf-monitor__graph-label";
|
|
1313
1305
|
label.textContent = config.label;
|
|
1314
1306
|
label.title = METRIC_HELP[config.metric] ?? "";
|
|
1307
|
+
const head = document.createElement("span");
|
|
1308
|
+
head.className = "perf-monitor__graph-head";
|
|
1309
|
+
head.append(createIcon(config.icon, "perf-monitor__icon"), label);
|
|
1315
1310
|
const value = document.createElement("span");
|
|
1316
1311
|
value.className = "perf-monitor__graph-value";
|
|
1317
1312
|
value.textContent = "—";
|
|
1318
|
-
overlay.append(
|
|
1313
|
+
overlay.append(head, value);
|
|
1319
1314
|
row.append(canvas, overlay);
|
|
1320
1315
|
this.hudGraphsEl.append(row);
|
|
1321
1316
|
const ctx = canvas.getContext("2d");
|
|
@@ -1336,8 +1331,8 @@ var PerformanceView = class {
|
|
|
1336
1331
|
item.className = "perf-monitor__hud-item";
|
|
1337
1332
|
const label = document.createElement("span");
|
|
1338
1333
|
label.className = "perf-monitor__hud-label";
|
|
1339
|
-
label.textContent = config.label;
|
|
1340
1334
|
label.title = METRIC_HELP[config.key] ?? "";
|
|
1335
|
+
label.append(createIcon(config.icon, "perf-monitor__icon"), config.label);
|
|
1341
1336
|
const value = document.createElement("span");
|
|
1342
1337
|
value.className = "perf-monitor__hud-value";
|
|
1343
1338
|
value.textContent = "—";
|
|
@@ -1351,7 +1346,6 @@ var PerformanceView = class {
|
|
|
1351
1346
|
onSelectionChanged() {
|
|
1352
1347
|
for (const [metric, checkbox] of this.graphCheckboxes) checkbox.checked = this.settings.isGraphEnabled(metric);
|
|
1353
1348
|
for (const [key, checkbox] of this.statCheckboxes) checkbox.checked = this.settings.isNumberEnabled(key);
|
|
1354
|
-
this.dimCheckbox.checked = this.settings.dim;
|
|
1355
1349
|
this.infoCheckbox.checked = this.settings.info;
|
|
1356
1350
|
this.applyInfoClass();
|
|
1357
1351
|
this.theme.setOverride(this.settings.theme);
|
|
@@ -1869,10 +1863,19 @@ const PERF_HUD_STYLES = `
|
|
|
1869
1863
|
}
|
|
1870
1864
|
|
|
1871
1865
|
.perf-monitor__hud-label {
|
|
1866
|
+
display: inline-flex;
|
|
1867
|
+
align-items: center;
|
|
1868
|
+
gap: 4px;
|
|
1872
1869
|
color: var(--perf-muted);
|
|
1873
1870
|
font-size: 10px;
|
|
1874
1871
|
}
|
|
1875
1872
|
|
|
1873
|
+
.perf-monitor__hud-label .perf-monitor__icon,
|
|
1874
|
+
.perf-monitor__hud-graph .perf-monitor__icon {
|
|
1875
|
+
width: 11px;
|
|
1876
|
+
height: 11px;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1876
1879
|
.perf-monitor__hud-value {
|
|
1877
1880
|
font-weight: 600;
|
|
1878
1881
|
font-variant-numeric: tabular-nums lining-nums;
|
|
@@ -2082,13 +2085,20 @@ const PERF_HUD_STYLES = `
|
|
|
2082
2085
|
cursor: grabbing;
|
|
2083
2086
|
}
|
|
2084
2087
|
|
|
2088
|
+
/* The ring is only ever hover or keyboard focus, never state. */
|
|
2085
2089
|
.perf-hud__disc:hover,
|
|
2086
|
-
.perf-hud__disc:focus-visible
|
|
2087
|
-
.perf-hud__disc[aria-pressed="true"] {
|
|
2090
|
+
.perf-hud__disc:focus-visible {
|
|
2088
2091
|
color: var(--perf-fg);
|
|
2089
2092
|
outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
|
|
2090
2093
|
outline-offset: 1px;
|
|
2091
2094
|
}
|
|
2095
|
+
|
|
2096
|
+
/* Dim on is a solid accent disc, off a plain one, so the state reads without hovering and
|
|
2097
|
+
hover keeps its own look on top of either. After the hover rule, so on stays on under it. */
|
|
2098
|
+
.perf-hud__dim[aria-pressed="true"] {
|
|
2099
|
+
background: var(--perf-accent);
|
|
2100
|
+
color: var(--perf-bg);
|
|
2101
|
+
}
|
|
2092
2102
|
`;
|
|
2093
2103
|
/**
|
|
2094
2104
|
* Append the stylesheet to `target`'s head, once. Call before mounting a view
|
|
@@ -2109,6 +2119,7 @@ const disc = (label, className, icon) => {
|
|
|
2109
2119
|
button.type = "button";
|
|
2110
2120
|
button.className = `perf-hud__disc ${className}`.trim();
|
|
2111
2121
|
button.setAttribute("aria-label", label);
|
|
2122
|
+
button.title = label;
|
|
2112
2123
|
button.append(createIcon(icon, "perf-hud__icon"));
|
|
2113
2124
|
return button;
|
|
2114
2125
|
};
|
|
@@ -2129,7 +2140,9 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2129
2140
|
tools.className = "perf-hud__tools";
|
|
2130
2141
|
const grip = disc("Drag performance panel", "perf-hud__drag", "grip");
|
|
2131
2142
|
const toggle = disc("Toggle full performance metrics", "", "sliders");
|
|
2132
|
-
|
|
2143
|
+
const dim = disc("Dim the panel when the pointer leaves", "perf-hud__dim", "blend");
|
|
2144
|
+
dim.addEventListener("click", () => settings.setDim(!settings.dim));
|
|
2145
|
+
tools.append(grip, toggle, dim);
|
|
2133
2146
|
const view = new PerformanceView({
|
|
2134
2147
|
budgets: options.budgets,
|
|
2135
2148
|
mode: options.mode ?? "compact",
|
|
@@ -2145,6 +2158,8 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2145
2158
|
};
|
|
2146
2159
|
const applyDim = () => {
|
|
2147
2160
|
host.classList.toggle("perf-hud--dim", settings.dim);
|
|
2161
|
+
dim.setAttribute("aria-pressed", String(settings.dim));
|
|
2162
|
+
dim.title = `Dim on leave: ${settings.dim ? "on" : "off"}`;
|
|
2148
2163
|
};
|
|
2149
2164
|
applyMode();
|
|
2150
2165
|
applyDim();
|
|
@@ -2219,4 +2234,4 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2219
2234
|
//#endregion
|
|
2220
2235
|
export { formatReport as a, HudSettings as c, isThemeMode as d, resolveBudgets as f, PerformanceView as i, HudTheme as l, PERF_HUD_STYLES as n, dockPanel as o, injectStyles as r, DEFAULT_STORAGE_KEY as s, mountPerfHud as t, THEME_MODES as u };
|
|
2221
2236
|
|
|
2222
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
2237
|
+
//# sourceMappingURL=mount-perf-hud-CebnfB8t.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-perf-hud-CebnfB8t.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","../package.json","../src/ui/clipboard.ts","../src/ui/format.ts","../src/ui/metric-help.ts","../src/ui/report.ts","../src/ui/sparkline.ts","../src/ui/performance-view.ts","../src/ui/styles.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` and `low` are floors (worse below);\n * everything else is a ceiling (worse above).\n *\n * Timing budgets default from `targetFps` (60): FPS at least 95% of it, 1%\n * low at least half, CPU and GPU within one frame, p99 within one and a half.\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 | \"hitches\"\n | \"lines\"\n | \"low\"\n | \"p99\"\n | \"passes\"\n | \"points\"\n | \"shaders\"\n | \"textures\"\n | \"triangles\";\n\ntype Budgets = Partial<Record<BudgetKey, number | null>> & {\n /** The frame rate the defaults are derived from. Default 60. */\n targetFps?: number;\n};\n\ntype ResolvedBudgets = {\n /** Frame budget in ms, `1000 / targetFps`. */\n frameMs: number;\n limits: Partial<Record<BudgetKey, number>>;\n};\n\nconst DEFAULT_TARGET_FPS = 60;\nconst FLOORS = new Set<BudgetKey>([\"fps\", \"low\"]);\n\nconst resolveBudgets = (budgets: Budgets | false | undefined): ResolvedBudgets => {\n const targetFps = (budgets && budgets.targetFps) || DEFAULT_TARGET_FPS;\n const frameMs = 1000 / targetFps;\n\n if (budgets === false) {\n return { frameMs, limits: {} };\n }\n\n const merged: Budgets = {\n cpu: frameMs,\n fps: targetFps * 0.95,\n gpu: frameMs,\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 };\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 | \"flame\"\n | \"gauge\"\n | \"grip\"\n | \"help\"\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 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 * 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 * 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 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 lines = [\n heading.join(\" · \"),\n frames,\n `CPU ${ms(sample.cpu)} · GPU ${sample.gpu.available ? ms(sample.gpu.ms) : \"unavailable\"}`,\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 { 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 { 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\";\n /** `null` until known: no frames yet, or no GPU timer. */\n value: (sample: Sample, stats: FrameStats) => 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\nconst RELEASE_URL = `https://github.com/zkmake/three-meter/releases/tag/v${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.\";\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: 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 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.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.budgets = resolveBudgets(budgets);\n this.applyBudgetTitles();\n }\n\n dispose() {\n this.stop();\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\n if (this.mode === \"compact\") {\n this.renderHud(sample);\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 const stats = this.monitor.getFrameStats();\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\n if (!cost) {\n this.costsListEl.replaceChildren(this.costsNote(\"No scene rendered yet.\"));\n\n return;\n }\n\n const entries = [...(this.costGroup === \"meshes\" ? cost.meshes : cost.materials)];\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) {\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 = document.createElement(\"button\");\n row.type = \"button\";\n row.className = \"perf-monitor__cost\";\n row.title = costTitle(entry);\n\n const name = document.createElement(\"span\");\n name.className = \"perf-monitor__cost-name\";\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 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(name, calls, triangles);\n row.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 return row;\n });\n\n this.costsListEl.replaceChildren(...rows);\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 private renderHud(sample: Sample) {\n const stats = this.hudNumberEls.size > 0 ? this.monitor.getFrameStats() : null;\n\n for (const [key, element] of this.hudNumberEls) {\n const config = NUMBER_BY_KEY.get(key);\n\n if (config && stats) {\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);\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.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 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 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 /* Rows are buttons, which shrink to their content without a width. */\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch;\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: pointer;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\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.perf-monitor__cost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\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-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-monitor--explain {\n width: 19rem;\n}\n\n.perf-hud > .perf-monitor {\n position: relative;\n z-index: 1;\n max-height: 70vh;\n overflow: auto;\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--dim:not(.is-awake):not(.is-dragging) > .perf-monitor {\n opacity: 0.32;\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 floating HUD: a fixed host docked to a screen edge, the metrics card\n * inside it, and three discs on the inward side (drag grip, compact/full\n * toggle, dim on leave) that stay hidden until the pointer is near or someone\n * taps. 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 { type DefaultPlacement, dockPanel } from \"./dock-panel.ts\";\nimport { DEFAULT_STORAGE_KEY, HudSettings } from \"./hud-settings.ts\";\nimport { createIcon } from \"./icons.ts\";\nimport { PerformanceView, type PerformanceViewMode } from \"./performance-view.ts\";\nimport { injectStyles } from \"./styles.ts\";\nimport { applyTheme, HudTheme, type ThemeMode } from \"./theme.ts\";\n\nconst TOUCH_LINGER_MS = 2500;\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 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\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 const parent = options.parent ?? document.body;\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 ?? \"Performance\");\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 performance panel\", \"perf-hud__drag\", \"grip\");\n const toggle = disc(\"Toggle full performance metrics\", \"\", \"sliders\");\n const dim = disc(\"Dim the panel when the pointer leaves\", \"perf-hud__dim\", \"blend\");\n dim.addEventListener(\"click\", () => settings.setDim(!settings.dim));\n tools.append(grip, toggle, dim);\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 host.append(hotspot, tools, view.element);\n parent.append(host);\n\n const applyMode = () => {\n host.classList.toggle(\"perf-hud--full\", view.getMode() === \"full\");\n };\n\n const applyDim = () => {\n host.classList.toggle(\"perf-hud--dim\", settings.dim);\n dim.setAttribute(\"aria-pressed\", String(settings.dim));\n dim.title = `Dim on leave: ${settings.dim ? \"on\" : \"off\"}`;\n };\n\n applyMode();\n applyDim();\n // After the view: constructing it restores the panel's stored theme pick.\n applyTheme(host, theme);\n const unsubscribe = settings.subscribe(applyDim);\n const unsubscribeTheme = theme.subscribe(() => applyTheme(host, theme));\n const dock = dockPanel(host, {\n defaultPlacement: options.defaultPlacement,\n handle: grip,\n storageKey,\n });\n\n const setMode = (mode: PerformanceViewMode) => {\n view.setMode(mode);\n applyMode();\n requestAnimationFrame(() => dock.refresh());\n };\n\n toggle.addEventListener(\"click\", () => {\n setMode(view.getMode() === \"compact\" ? \"full\" : \"compact\");\n });\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 view.start();\n\n return {\n dispose: () => {\n window.clearTimeout(hideTimer);\n host.removeEventListener(\"pointerenter\", onEnter);\n host.removeEventListener(\"pointerleave\", onLeave);\n host.removeEventListener(\"pointerdown\", onDown);\n unsubscribe();\n unsubscribeTheme();\n dock.dispose();\n view.dispose();\n theme.dispose();\n host.remove();\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.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { s as PerformanceMonitorOptions, t as PerformanceMonitor } from "./performance-monitor-DgHxeQ2X.js";
|
|
2
|
-
import { t as MountPerfHudOptions } from "./mount-perf-hud-
|
|
2
|
+
import { t as MountPerfHudOptions } from "./mount-perf-hud-BKpk62Pp.js";
|
|
3
3
|
//#region src/react/monitor-store.d.ts
|
|
4
4
|
type MonitorListener = (monitor: PerformanceMonitor | null) => void;
|
|
5
5
|
type PerfMonitorStore = {
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PerformanceMonitor } from "./performance-monitor-DsBp13ts.js";
|
|
2
|
-
import { t as mountPerfHud } from "./mount-perf-hud-
|
|
2
|
+
import { t as mountPerfHud } from "./mount-perf-hud-CebnfB8t.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/styles.css
CHANGED
|
@@ -365,10 +365,19 @@
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
.perf-monitor__hud-label {
|
|
368
|
+
display: inline-flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
gap: 4px;
|
|
368
371
|
color: var(--perf-muted);
|
|
369
372
|
font-size: 10px;
|
|
370
373
|
}
|
|
371
374
|
|
|
375
|
+
.perf-monitor__hud-label .perf-monitor__icon,
|
|
376
|
+
.perf-monitor__hud-graph .perf-monitor__icon {
|
|
377
|
+
width: 11px;
|
|
378
|
+
height: 11px;
|
|
379
|
+
}
|
|
380
|
+
|
|
372
381
|
.perf-monitor__hud-value {
|
|
373
382
|
font-weight: 600;
|
|
374
383
|
font-variant-numeric: tabular-nums lining-nums;
|
|
@@ -578,10 +587,17 @@
|
|
|
578
587
|
cursor: grabbing;
|
|
579
588
|
}
|
|
580
589
|
|
|
590
|
+
/* The ring is only ever hover or keyboard focus, never state. */
|
|
581
591
|
.perf-hud__disc:hover,
|
|
582
|
-
.perf-hud__disc:focus-visible
|
|
583
|
-
.perf-hud__disc[aria-pressed="true"] {
|
|
592
|
+
.perf-hud__disc:focus-visible {
|
|
584
593
|
color: var(--perf-fg);
|
|
585
594
|
outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
|
|
586
595
|
outline-offset: 1px;
|
|
587
596
|
}
|
|
597
|
+
|
|
598
|
+
/* Dim on is a solid accent disc, off a plain one, so the state reads without hovering and
|
|
599
|
+
hover keeps its own look on top of either. After the hover rule, so on stays on under it. */
|
|
600
|
+
.perf-hud__dim[aria-pressed="true"] {
|
|
601
|
+
background: var(--perf-accent);
|
|
602
|
+
color: var(--perf-bg);
|
|
603
|
+
}
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { t as PerformanceMonitor } from "./performance-monitor-DgHxeQ2X.js";
|
|
2
|
-
import { C as dockPanel, D as resolveBudgets, E as ResolvedBudgets, S as ScreenEdge, T as Budgets, _ as DefaultPlacement, a as PerformanceViewMode, b as DockPanelOptions, c as HudDefaults, d as HudSettingsOptions, f as HudTheme, g as isThemeMode, h as ThemeMode, i as PerformanceView, l as HudSelection, m as THEME_MODES, n as PerfHudHandle, o as PerformanceViewOptions, p as ResolvedTheme, r as mountPerfHud, s as DEFAULT_STORAGE_KEY, t as MountPerfHudOptions, u as HudSettings, v as DockAlign, w as BudgetKey, x as Placement, y as DockHandle } from "./mount-perf-hud-
|
|
2
|
+
import { C as dockPanel, D as resolveBudgets, E as ResolvedBudgets, S as ScreenEdge, T as Budgets, _ as DefaultPlacement, a as PerformanceViewMode, b as DockPanelOptions, c as HudDefaults, d as HudSettingsOptions, f as HudTheme, g as isThemeMode, h as ThemeMode, i as PerformanceView, l as HudSelection, m as THEME_MODES, n as PerfHudHandle, o as PerformanceViewOptions, p as ResolvedTheme, r as mountPerfHud, s as DEFAULT_STORAGE_KEY, t as MountPerfHudOptions, u as HudSettings, v as DockAlign, w as BudgetKey, x as Placement, y as DockHandle } from "./mount-perf-hud-BKpk62Pp.js";
|
|
3
3
|
//#region src/ui/report.d.ts
|
|
4
4
|
/** The report for `monitor` right now, as Markdown ready to paste into an issue. */
|
|
5
5
|
declare const formatReport: (monitor: PerformanceMonitor) => string;
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/ui/styles.d.ts
|
|
8
|
-
declare const 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 /* Rows are buttons, which shrink to their content without a width. */\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch;\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: pointer;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\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.perf-monitor__cost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\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 color: var(--perf-muted);\n font-size: 10px;\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-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-monitor--explain {\n width: 19rem;\n}\n\n.perf-hud > .perf-monitor {\n position: relative;\n z-index: 1;\n max-height: 70vh;\n overflow: auto;\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--dim:not(.is-awake):not(.is-dragging) > .perf-monitor {\n opacity: 0.32;\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.perf-hud__disc:hover,\n.perf-hud__disc:focus-visible,\n.perf-hud__disc[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n";
|
|
8
|
+
declare const 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 /* Rows are buttons, which shrink to their content without a width. */\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch;\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: pointer;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\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.perf-monitor__cost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\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-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-monitor--explain {\n width: 19rem;\n}\n\n.perf-hud > .perf-monitor {\n position: relative;\n z-index: 1;\n max-height: 70vh;\n overflow: auto;\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--dim:not(.is-awake):not(.is-dragging) > .perf-monitor {\n opacity: 0.32;\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";
|
|
9
9
|
/**
|
|
10
10
|
* Append the stylesheet to `target`'s head, once. Call before mounting a view
|
|
11
11
|
* by hand; {@link mountPerfHud} does it for you unless told not to.
|
package/dist/ui.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as formatReport, c as HudSettings, d as isThemeMode, f as resolveBudgets, i as PerformanceView, l as HudTheme, n as PERF_HUD_STYLES, o as dockPanel, r as injectStyles, s as DEFAULT_STORAGE_KEY, t as mountPerfHud, u as THEME_MODES } from "./mount-perf-hud-
|
|
1
|
+
import { a as formatReport, c as HudSettings, d as isThemeMode, f as resolveBudgets, i as PerformanceView, l as HudTheme, n as PERF_HUD_STYLES, o as dockPanel, r as injectStyles, s as DEFAULT_STORAGE_KEY, t as mountPerfHud, u as THEME_MODES } from "./mount-perf-hud-CebnfB8t.js";
|
|
2
2
|
export { DEFAULT_STORAGE_KEY, HudSettings, HudTheme, PERF_HUD_STYLES, PerformanceView, THEME_MODES, dockPanel, formatReport, injectStyles, isThemeMode, mountPerfHud, resolveBudgets };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mount-perf-hud-B_cmTG2M.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","../package.json","../src/ui/clipboard.ts","../src/ui/format.ts","../src/ui/metric-help.ts","../src/ui/report.ts","../src/ui/sparkline.ts","../src/ui/performance-view.ts","../src/ui/styles.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` and `low` are floors (worse below);\n * everything else is a ceiling (worse above).\n *\n * Timing budgets default from `targetFps` (60): FPS at least 95% of it, 1%\n * low at least half, CPU and GPU within one frame, p99 within one and a half.\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 | \"hitches\"\n | \"lines\"\n | \"low\"\n | \"p99\"\n | \"passes\"\n | \"points\"\n | \"shaders\"\n | \"textures\"\n | \"triangles\";\n\ntype Budgets = Partial<Record<BudgetKey, number | null>> & {\n /** The frame rate the defaults are derived from. Default 60. */\n targetFps?: number;\n};\n\ntype ResolvedBudgets = {\n /** Frame budget in ms, `1000 / targetFps`. */\n frameMs: number;\n limits: Partial<Record<BudgetKey, number>>;\n};\n\nconst DEFAULT_TARGET_FPS = 60;\nconst FLOORS = new Set<BudgetKey>([\"fps\", \"low\"]);\n\nconst resolveBudgets = (budgets: Budgets | false | undefined): ResolvedBudgets => {\n const targetFps = (budgets && budgets.targetFps) || DEFAULT_TARGET_FPS;\n const frameMs = 1000 / targetFps;\n\n if (budgets === false) {\n return { frameMs, limits: {} };\n }\n\n const merged: Budgets = {\n cpu: frameMs,\n fps: targetFps * 0.95,\n gpu: frameMs,\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 };\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 | \"flame\"\n | \"gauge\"\n | \"grip\"\n | \"help\"\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 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 * 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 * 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 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 lines = [\n heading.join(\" · \"),\n frames,\n `CPU ${ms(sample.cpu)} · GPU ${sample.gpu.available ? ms(sample.gpu.ms) : \"unavailable\"}`,\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 { 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 { 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\";\n /** `null` until known: no frames yet, or no GPU timer. */\n value: (sample: Sample, stats: FrameStats) => 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\nconst RELEASE_URL = `https://github.com/zkmake/three-meter/releases/tag/v${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.\";\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: 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 dimCheckbox!: 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 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.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.budgets = resolveBudgets(budgets);\n this.applyBudgetTitles();\n }\n\n dispose() {\n this.stop();\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\n if (this.mode === \"compact\") {\n this.renderHud(sample);\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 const stats = this.monitor.getFrameStats();\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\n if (!cost) {\n this.costsListEl.replaceChildren(this.costsNote(\"No scene rendered yet.\"));\n\n return;\n }\n\n const entries = [...(this.costGroup === \"meshes\" ? cost.meshes : cost.materials)];\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) {\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 = document.createElement(\"button\");\n row.type = \"button\";\n row.className = \"perf-monitor__cost\";\n row.title = costTitle(entry);\n\n const name = document.createElement(\"span\");\n name.className = \"perf-monitor__cost-name\";\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 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(name, calls, triangles);\n row.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 return row;\n });\n\n this.costsListEl.replaceChildren(...rows);\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 private renderHud(sample: Sample) {\n const stats = this.hudNumberEls.size > 0 ? this.monitor.getFrameStats() : null;\n\n for (const [key, element] of this.hudNumberEls) {\n const config = NUMBER_BY_KEY.get(key);\n\n if (config && stats) {\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);\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 const dimRow = document.createElement(\"label\");\n dimRow.className = \"perf-monitor__row\";\n\n this.dimCheckbox = this.buildCheckbox(\n this.settings.dim,\n \"Dim the performance panel when the pointer leaves\",\n (on) => this.settings.setDim(on),\n );\n\n const dimLabel = document.createElement(\"span\");\n dimLabel.className = \"perf-monitor__label\";\n dimLabel.textContent = \"dim on leave\";\n\n dimRow.append(createIcon(\"blend\", \"perf-monitor__icon\"), dimLabel, this.dimCheckbox);\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 options.append(themeRow, dimRow, 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 value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n\n overlay.append(label, 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.textContent = config.label;\n label.title = METRIC_HELP[config.key] ?? \"\";\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.dimCheckbox.checked = this.settings.dim;\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.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 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 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 /* Rows are buttons, which shrink to their content without a width. */\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch;\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: pointer;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\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.perf-monitor__cost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\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 color: var(--perf-muted);\n font-size: 10px;\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-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-monitor--explain {\n width: 19rem;\n}\n\n.perf-hud > .perf-monitor {\n position: relative;\n z-index: 1;\n max-height: 70vh;\n overflow: auto;\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--dim:not(.is-awake):not(.is-dragging) > .perf-monitor {\n opacity: 0.32;\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.perf-hud__disc:hover,\n.perf-hud__disc:focus-visible,\n.perf-hud__disc[aria-pressed=\"true\"] {\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\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 floating HUD: a fixed host docked to a screen edge, the metrics card\n * inside it, and two discs on the inward side (drag grip, compact/full toggle)\n * that stay hidden until the pointer is near or someone taps. Dim-on-leave\n * comes from the settings; the palette 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 { type DefaultPlacement, dockPanel } from \"./dock-panel.ts\";\nimport { DEFAULT_STORAGE_KEY, HudSettings } from \"./hud-settings.ts\";\nimport { createIcon } from \"./icons.ts\";\nimport { PerformanceView, type PerformanceViewMode } from \"./performance-view.ts\";\nimport { injectStyles } from \"./styles.ts\";\nimport { applyTheme, HudTheme, type ThemeMode } from \"./theme.ts\";\n\nconst TOUCH_LINGER_MS = 2500;\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 disc = (label: string, className: string, icon: \"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.append(createIcon(icon, \"perf-hud__icon\"));\n\n return button;\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 const parent = options.parent ?? document.body;\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 ?? \"Performance\");\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 performance panel\", \"perf-hud__drag\", \"grip\");\n const toggle = disc(\"Toggle full performance metrics\", \"\", \"sliders\");\n tools.append(grip, toggle);\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 host.append(hotspot, tools, view.element);\n parent.append(host);\n\n const applyMode = () => {\n host.classList.toggle(\"perf-hud--full\", view.getMode() === \"full\");\n };\n\n const applyDim = () => {\n host.classList.toggle(\"perf-hud--dim\", settings.dim);\n };\n\n applyMode();\n applyDim();\n // After the view: constructing it restores the panel's stored theme pick.\n applyTheme(host, theme);\n const unsubscribe = settings.subscribe(applyDim);\n const unsubscribeTheme = theme.subscribe(() => applyTheme(host, theme));\n const dock = dockPanel(host, {\n defaultPlacement: options.defaultPlacement,\n handle: grip,\n storageKey,\n });\n\n const setMode = (mode: PerformanceViewMode) => {\n view.setMode(mode);\n applyMode();\n requestAnimationFrame(() => dock.refresh());\n };\n\n toggle.addEventListener(\"click\", () => {\n setMode(view.getMode() === \"compact\" ? \"full\" : \"compact\");\n });\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 view.start();\n\n return {\n dispose: () => {\n window.clearTimeout(hideTimer);\n host.removeEventListener(\"pointerenter\", onEnter);\n host.removeEventListener(\"pointerleave\", onLeave);\n host.removeEventListener(\"pointerdown\", onDown);\n unsubscribe();\n unsubscribeTheme();\n dock.dispose();\n view.dispose();\n theme.dispose();\n host.remove();\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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"}
|