@zkmake/three-meter 0.11.6 → 0.13.0

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 CHANGED
@@ -125,6 +125,7 @@ Canvas is its own React root. To run two canvases on one page, pass the same `st
125
125
  | `injectStyles` | `true` | Append the stylesheet once per document. |
126
126
  | `refreshHz` | `10` | Repaint rate. |
127
127
  | `budgets` | timing defaults | Limits past which a value turns amber. See [Budgets](#budgets). |
128
+ | `quality` | none | Adaptive quality to show. See [Adaptive quality](#adaptive-quality). |
128
129
  | `label` | `"Performance"` | Accessible name of the panel. |
129
130
 
130
131
  `PerformanceMonitor` and `PerfSampler` take `trackGPU` (default true), `gpuQueryPoolSize` (default 5),
@@ -182,6 +183,41 @@ hud.setBudgets({ gpu: null }); // null drops a default; false drops them all
182
183
 
183
184
  `PerfHud` takes the same `budgets` prop and applies changes live.
184
185
 
186
+ An app that keeps its budgets in one file with [three-audit](../three-audit)'s `defineBudgets`
187
+ passes that file as is. The HUD applies its `frame`: `fps` as `targetFps`, `calls` and
188
+ `triangles`. The same file holds the per-part budgets its tests and the `three-audit` CLI check.
189
+
190
+ ```ts
191
+ import budgets from "./budgets.ts"; // defineBudgets({ frame: { fps: 60, calls: 150, … }, parts })
192
+
193
+ mountPerfHud(monitor, { budgets });
194
+ ```
195
+
196
+ three-meter reads the file's shape (`AppBudgets`) and doesn't depend on three-audit.
197
+
198
+ ## Adaptive quality
199
+
200
+ A game that adapts its render quality at runtime with [three-quality](../three-quality) can show
201
+ what it's doing: pass the controller, or in React its store, as `quality`. The card gets one line
202
+ under the grid, `dpr 1.25 · adapting · shadows off`, and the full view a **Quality** row; hover
203
+ either for the level and the last window's p90 frame against the budget.
204
+
205
+ ```ts
206
+ const quality = adaptQuality(renderer); // @zkmake/three-quality
207
+
208
+ mountPerfHud(monitor, { quality });
209
+ ```
210
+
211
+ ```tsx
212
+ import { qualityStore } from "@zkmake/three-quality/react";
213
+
214
+ <PerfHud quality={qualityStore} />;
215
+ ```
216
+
217
+ three-meter reads the shape (`QualitySource`: a `state` with `dpr`, `phase`, `level`, `levels`
218
+ and `tiers`), so neither package depends on the other: the game ships three-quality and loads
219
+ three-meter behind `?debug`. `hud.setQuality(source)` swaps it later.
220
+
185
221
  ## Top costs
186
222
 
187
223
  The HUD tells you there are 400 draw calls; **top costs** tells you which objects they come from.
@@ -11,12 +11,28 @@ import { p as TimingMetric, t as PerformanceMonitor } from "./performance-monito
11
11
  * used, then 60 until one is detected.
12
12
  * Counts have no default, since what's reasonable depends on the scene. Pass
13
13
  * `null` for a key to drop its default, or `false` for no budgets at all.
14
+ *
15
+ * An app's budgets file (`AppBudgets`, from three-audit's `defineBudgets`) works too: its
16
+ * `frame` sets `targetFps`, `calls` and `triangles`.
14
17
  */
15
18
  type BudgetKey = "calls" | "cpu" | "fps" | "geometries" | "gpu" | "headroom" | "hitches" | "lines" | "low" | "p99" | "passes" | "points" | "refresh" | "shaders" | "textures" | "triangles";
16
19
  type Budgets = Partial<Record<BudgetKey, number | null>> & {
17
20
  /** The frame rate the defaults are derived from. Default: the detected refresh rate, else 60. */
18
21
  targetFps?: number;
19
22
  };
23
+ /**
24
+ * An app's budgets file, as `@zkmake/three-audit`'s `defineBudgets` returns it. Structural, so
25
+ * three-meter needs no dependency: only `frame` is read, its `fps` as `targetFps`.
26
+ */
27
+ type AppBudgets = {
28
+ readonly frame: {
29
+ readonly fps?: number | undefined;
30
+ readonly calls?: number | undefined;
31
+ readonly triangles?: number | undefined;
32
+ };
33
+ };
34
+ /** What the HUD's `budgets` option takes: its own budgets, or an app's budgets file. */
35
+ type BudgetsInput = Budgets | AppBudgets;
20
36
  type ResolvedBudgets = {
21
37
  /** Frame budget in ms, `1000 / targetFps`. */
22
38
  frameMs: number;
@@ -25,7 +41,7 @@ type ResolvedBudgets = {
25
41
  limits: Partial<Record<BudgetKey, number>>;
26
42
  };
27
43
  /** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */
28
- declare const resolveBudgets: (budgets: Budgets | false | undefined, detectedHz?: number | null) => ResolvedBudgets;
44
+ declare const resolveBudgets: (input: BudgetsInput | false | undefined, detectedHz?: number | null) => ResolvedBudgets;
29
45
  declare const EDGES: readonly ["left", "right", "top", "bottom"];
30
46
  type ScreenEdge = (typeof EDGES)[number];
31
47
  type Placement = {
@@ -144,6 +160,31 @@ declare class HudSettings {
144
160
  private load;
145
161
  private persistAndNotify;
146
162
  }
163
+ /**
164
+ * Adaptive quality, as the HUD shows it: one line, `dpr 1.25 · adapting · shadows off`. The source
165
+ * is `@zkmake/three-quality`'s controller (`adaptQuality`) or its React store (`qualityStore`),
166
+ * read by shape like `AppBudgets`, so three-meter needs no dependency and the runtime package none
167
+ * on this dev tool. Anything with a `state` of this shape works.
168
+ */
169
+ type QualityReading = {
170
+ /** The pixel ratio applied. */
171
+ readonly dpr: number;
172
+ /** What the controller is doing: `warming`, `adapting`, `steady`, `cpu-bound`, `pinned`. */
173
+ readonly phase: string;
174
+ /** 0 is the best rung. */
175
+ readonly level: number;
176
+ readonly levels: number;
177
+ /** The app's tiers by name, `true` while on. */
178
+ readonly tiers?: Readonly<Record<string, boolean>>;
179
+ /** The last window's 90th-percentile frame, ms. */
180
+ readonly p90Ms?: number | null;
181
+ readonly targetFps?: number;
182
+ };
183
+ /** What the HUD's `quality` option takes. `state` is read on each repaint; `null` shows `—`. */
184
+ type QualitySource = {
185
+ readonly state: QualityReading | null;
186
+ };
187
+ declare const formatQuality: (reading: QualityReading | null) => string;
147
188
  type PerformanceViewMode = "full" | "compact";
148
189
  type PerformanceViewOptions = {
149
190
  monitor: PerformanceMonitor;
@@ -153,9 +194,15 @@ type PerformanceViewOptions = {
153
194
  refreshHz?: number;
154
195
  /**
155
196
  * Limits past which a value turns amber. Timing budgets default from
156
- * `targetFps` (60); `false` turns them all off. See {@link Budgets}.
197
+ * `targetFps` (60); `false` turns them all off. See {@link Budgets}; an app's
198
+ * budgets file (`AppBudgets`) applies its `frame`.
199
+ */
200
+ budgets?: BudgetsInput | false;
201
+ /**
202
+ * Adaptive quality to show, one line in the card and a row in the list: three-quality's
203
+ * controller or React store, read by shape. See {@link QualitySource}.
157
204
  */
158
- budgets?: Budgets | false;
205
+ quality?: QualitySource;
159
206
  /** Share one between views to keep them in sync; a fresh instance otherwise. */
160
207
  settings?: HudSettings;
161
208
  /**
@@ -198,6 +245,12 @@ declare class PerformanceView {
198
245
  private budgets;
199
246
  private budgetsInput;
200
247
  private detectedHz;
248
+ private quality;
249
+ private lastQualityText;
250
+ private hudQualityEl;
251
+ private hudQualityValueEl;
252
+ private qualityRowEl;
253
+ private qualityValueEl;
201
254
  private readonly hider;
202
255
  private readonly hiddenRows;
203
256
  private rafId;
@@ -218,7 +271,9 @@ declare class PerformanceView {
218
271
  setMode(mode: PerformanceViewMode): void;
219
272
  getMode(): PerformanceViewMode;
220
273
  /** Replace the budgets; `false` turns them all off. Applies on the next paint. */
221
- setBudgets(budgets: Budgets | false | undefined): void;
274
+ setBudgets(budgets: BudgetsInput | false | undefined): void;
275
+ /** Show another quality source, or none. Applies on the next paint. */
276
+ setQuality(quality: QualitySource | undefined): void;
222
277
  dispose(): void;
223
278
  private frame;
224
279
  private render;
@@ -235,6 +290,9 @@ declare class PerformanceView {
235
290
  private costsNote;
236
291
  /** The monitor caches the environment once its backend settles; until then this re-reads. */
237
292
  private renderFooter;
293
+ /** One string for both the card's line and the list's row; the DOM is touched only on change. */
294
+ private renderQuality;
295
+ private applyQuality;
238
296
  /** Budgets follow the detected refresh rate unless `targetFps` was given. */
239
297
  private followRefresh;
240
298
  private renderHud;
@@ -279,11 +337,17 @@ type MountPerfHudOptions = {
279
337
  /** Repaint rate. Default 10. */
280
338
  refreshHz?: number;
281
339
  /**
282
- * Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`.
340
+ * Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`,
341
+ * or the app's budgets file (three-audit's `defineBudgets`), whose `frame` applies.
283
342
  * Timing budgets default from `targetFps` (60); `false` turns them all off.
284
343
  * Change later with the handle's `setBudgets`.
285
344
  */
286
- budgets?: Budgets | false;
345
+ budgets?: BudgetsInput | false;
346
+ /**
347
+ * Adaptive quality to show: `@zkmake/three-quality`'s controller (`adaptQuality(…)`) or its React
348
+ * store (`qualityStore`), read by shape, no dependency. Change later with `setQuality`.
349
+ */
350
+ quality?: QualitySource;
287
351
  /** Accessible name of the panel. Default `Performance`. */
288
352
  label?: string;
289
353
  };
@@ -304,9 +368,11 @@ type PerfHudHandle = {
304
368
  /** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */
305
369
  setTheme: (mode: ThemeMode) => void;
306
370
  /** Replace the budgets; `false` turns them all off. */
307
- setBudgets: (budgets: Budgets | false | undefined) => void;
371
+ setBudgets: (budgets: BudgetsInput | false | undefined) => void;
372
+ /** Show another quality source, or none. */
373
+ setQuality: (quality: QualitySource | undefined) => void;
308
374
  dispose: () => void;
309
375
  };
310
376
  declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
311
- 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 };
312
- //# sourceMappingURL=mount-perf-hud-C7CzkVqM.d.ts.map
377
+ export { BudgetsInput as A, DockPanelOptions as C, AppBudgets as D, dockPanel as E, resolveBudgets as M, BudgetKey as O, DockHandle as S, ScreenEdge as T, THEME_MODES as _, PerformanceViewMode as a, DefaultPlacement as b, QualitySource as c, HudDefaults as d, HudSelection as f, ResolvedTheme as g, HudTheme as h, PerformanceView as i, ResolvedBudgets as j, Budgets as k, formatQuality as l, HudSettingsOptions as m, PerfHudHandle as n, PerformanceViewOptions as o, HudSettings as p, mountPerfHud as r, QualityReading as s, MountPerfHudOptions as t, DEFAULT_STORAGE_KEY as u, ThemeMode as v, Placement as w, DockAlign as x, isThemeMode as y };
378
+ //# sourceMappingURL=mount-perf-hud-C0yuW5JI.d.ts.map
@@ -5,8 +5,18 @@ const FLOORS = /* @__PURE__ */ new Set([
5
5
  "headroom",
6
6
  "low"
7
7
  ]);
8
+ const isAppBudgets = (budgets) => "frame" in budgets;
9
+ /** An app's frame budget as the HUD's: only the limits it sets, so the rest keep their defaults. */
10
+ const fromApp = ({ frame }) => {
11
+ const budgets = {};
12
+ if (frame.fps !== void 0) budgets.targetFps = frame.fps;
13
+ if (frame.calls !== void 0) budgets.calls = frame.calls;
14
+ if (frame.triangles !== void 0) budgets.triangles = frame.triangles;
15
+ return budgets;
16
+ };
8
17
  /** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */
9
- const resolveBudgets = (budgets, detectedHz = null) => {
18
+ const resolveBudgets = (input, detectedHz = null) => {
19
+ const budgets = input && isAppBudgets(input) ? fromApp(input) : input;
10
20
  const targetFps = budgets && budgets.targetFps || detectedHz || DEFAULT_TARGET_FPS;
11
21
  const frameMs = 1e3 / targetFps;
12
22
  if (budgets === false) return {
@@ -959,6 +969,34 @@ const PERF_HUD_STYLES = `
959
969
  text-align: right;
960
970
  }
961
971
 
972
+ /* Adaptive quality (three-quality), under the grid: one muted line, the reading in the foreground. */
973
+ .perf-monitor__hud-quality {
974
+ display: flex;
975
+ align-items: center;
976
+ gap: 4px;
977
+ min-width: 0;
978
+ color: var(--perf-muted);
979
+ font-size: 10px;
980
+ }
981
+
982
+ .perf-monitor__hud-quality[hidden],
983
+ .perf-monitor__row[hidden] {
984
+ display: none;
985
+ }
986
+
987
+ .perf-monitor__hud-quality .perf-monitor__icon {
988
+ width: 11px;
989
+ height: 11px;
990
+ }
991
+
992
+ .perf-monitor__hud-quality-value {
993
+ overflow: hidden;
994
+ color: var(--perf-fg);
995
+ font-variant-numeric: tabular-nums lining-nums;
996
+ text-overflow: ellipsis;
997
+ white-space: nowrap;
998
+ }
999
+
962
1000
  .perf-monitor__footer {
963
1001
  display: flex;
964
1002
  align-items: flex-start;
@@ -1390,7 +1428,7 @@ const mountDevPanel = (options) => {
1390
1428
  };
1391
1429
  // package.json
1392
1430
  var name = "@zkmake/three-meter";
1393
- var version = "0.11.6";
1431
+ var version = "0.13.0";
1394
1432
  // src/ui/clipboard.ts
1395
1433
  /**
1396
1434
  * Copy text from a click handler. The async Clipboard API needs a secure
@@ -1501,6 +1539,23 @@ const METRIC_HELP = {
1501
1539
  textures: "Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.",
1502
1540
  triangles: "Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it."
1503
1541
  };
1542
+ // src/ui/quality.ts
1543
+ const ratio = (dpr) => Number(dpr.toFixed(2)).toString();
1544
+ const tiersOff = (reading) => Object.entries(reading.tiers ?? {}).filter(([, on]) => !on).map(([name]) => name);
1545
+ const formatQuality = (reading) => {
1546
+ if (!reading) return "—";
1547
+ const off = tiersOff(reading);
1548
+ return [
1549
+ `dpr ${ratio(reading.dpr)}`,
1550
+ reading.phase,
1551
+ off.length ? `${off.join(", ")} off` : ""
1552
+ ].filter(Boolean).join(" · ");
1553
+ };
1554
+ const qualityTitle = (reading) => {
1555
+ if (!reading) return "Adaptive quality: no controller mounted.";
1556
+ const frame = typeof reading.p90Ms === "number" ? `; p90 frame ${reading.p90Ms.toFixed(1)} ms${reading.targetFps ? ` against ${(1e3 / reading.targetFps).toFixed(1)} ms` : ""}` : "";
1557
+ return `Adaptive quality: level ${reading.level + 1} of ${reading.levels} (1 is the best)${frame}.`;
1558
+ };
1504
1559
  // src/ui/report.ts
1505
1560
  /**
1506
1561
  * A plain-text snapshot of the monitor for bug reports: versions, backend and
@@ -1898,6 +1953,12 @@ var PerformanceView = class {
1898
1953
  budgets;
1899
1954
  budgetsInput;
1900
1955
  detectedHz = null;
1956
+ quality;
1957
+ lastQualityText = "";
1958
+ hudQualityEl;
1959
+ hudQualityValueEl;
1960
+ qualityRowEl;
1961
+ qualityValueEl;
1901
1962
  hider = new Hider();
1902
1963
  hiddenRows = /* @__PURE__ */ new Map();
1903
1964
  rafId = null;
@@ -1922,11 +1983,13 @@ var PerformanceView = class {
1922
1983
  this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
1923
1984
  this.budgetsInput = options.budgets;
1924
1985
  this.budgets = resolveBudgets(options.budgets);
1986
+ this.quality = options.quality;
1925
1987
  this.element = document.createElement("div");
1926
1988
  this.element.className = "perf-monitor";
1927
1989
  this.applyModeClass();
1928
1990
  this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());
1929
1991
  this.build();
1992
+ this.applyQuality();
1930
1993
  this.applyInfoClass();
1931
1994
  this.rebuildHud();
1932
1995
  this.onThemeChanged();
@@ -1957,6 +2020,11 @@ var PerformanceView = class {
1957
2020
  this.budgets = resolveBudgets(budgets, this.detectedHz);
1958
2021
  this.applyBudgetTitles();
1959
2022
  }
2023
+ /** Show another quality source, or none. Applies on the next paint. */
2024
+ setQuality(quality) {
2025
+ this.quality = quality;
2026
+ this.applyQuality();
2027
+ }
1960
2028
  dispose() {
1961
2029
  this.stop();
1962
2030
  this.showAllHidden();
@@ -1976,6 +2044,7 @@ var PerformanceView = class {
1976
2044
  const sample = this.monitor.getSample();
1977
2045
  const stats = this.monitor.getFrameStats();
1978
2046
  this.followRefresh(stats.refreshHz);
2047
+ this.renderQuality();
1979
2048
  if (this.mode === "compact") {
1980
2049
  this.renderHud(sample, stats);
1981
2050
  if (this.settings.info) this.renderFooter();
@@ -2110,6 +2179,25 @@ var PerformanceView = class {
2110
2179
  this.footerGpuEl.title = gpu;
2111
2180
  this.footerHardwareEl.hidden = backend === "" && gpu === "";
2112
2181
  }
2182
+ /** One string for both the card's line and the list's row; the DOM is touched only on change. */
2183
+ renderQuality() {
2184
+ if (!this.quality) return;
2185
+ const reading = this.quality.state;
2186
+ const text = formatQuality(reading);
2187
+ const title = qualityTitle(reading);
2188
+ if (`${text}\n${title}` === this.lastQualityText) return;
2189
+ this.lastQualityText = `${text}\n${title}`;
2190
+ this.hudQualityValueEl.textContent = text;
2191
+ this.qualityValueEl.textContent = text;
2192
+ this.hudQualityEl.title = title;
2193
+ this.qualityRowEl.title = title;
2194
+ }
2195
+ applyQuality() {
2196
+ this.hudQualityEl.hidden = !this.quality;
2197
+ this.qualityRowEl.hidden = !this.quality;
2198
+ this.lastQualityText = "";
2199
+ this.renderQuality();
2200
+ }
2113
2201
  /** Budgets follow the detected refresh rate unless `targetFps` was given. */
2114
2202
  followRefresh(hz) {
2115
2203
  if (hz === this.detectedHz) return;
@@ -2161,7 +2249,12 @@ var PerformanceView = class {
2161
2249
  this.hudGraphsEl.className = "perf-monitor__hud-graphs";
2162
2250
  this.hudGridEl = document.createElement("div");
2163
2251
  this.hudGridEl.className = "perf-monitor__hud-grid";
2164
- hud.append(this.hudGraphsEl, this.hudGridEl);
2252
+ this.hudQualityEl = document.createElement("div");
2253
+ this.hudQualityEl.className = "perf-monitor__hud-quality";
2254
+ this.hudQualityValueEl = document.createElement("span");
2255
+ this.hudQualityValueEl.className = "perf-monitor__hud-quality-value";
2256
+ this.hudQualityEl.append(createIcon("sliders", "perf-monitor__icon"), this.hudQualityValueEl);
2257
+ hud.append(this.hudGraphsEl, this.hudGridEl, this.hudQualityEl);
2165
2258
  const options = document.createElement("div");
2166
2259
  options.className = "perf-monitor__section perf-monitor__options";
2167
2260
  const themeRow = document.createElement("div");
@@ -2236,6 +2329,15 @@ var PerformanceView = class {
2236
2329
  }
2237
2330
  const stats = document.createElement("div");
2238
2331
  stats.className = "perf-monitor__section";
2332
+ this.qualityRowEl = document.createElement("div");
2333
+ this.qualityRowEl.className = "perf-monitor__row perf-monitor__row--static";
2334
+ const qualityLabel = document.createElement("span");
2335
+ qualityLabel.className = "perf-monitor__label";
2336
+ qualityLabel.textContent = "Quality";
2337
+ this.qualityValueEl = document.createElement("span");
2338
+ this.qualityValueEl.className = "perf-monitor__value";
2339
+ this.qualityValueEl.textContent = "—";
2340
+ this.qualityRowEl.append(createIcon("sliders", "perf-monitor__icon"), qualityLabel, this.qualityValueEl);
2239
2341
  for (const config of NUMBERS) {
2240
2342
  const rowEl = document.createElement("label");
2241
2343
  rowEl.className = "perf-monitor__row";
@@ -2256,6 +2358,7 @@ var PerformanceView = class {
2256
2358
  rowEl.append(createIcon(config.icon, "perf-monitor__icon"), label, value, checkbox, hint);
2257
2359
  stats.append(rowEl);
2258
2360
  }
2361
+ stats.append(this.qualityRowEl);
2259
2362
  const footer = document.createElement("div");
2260
2363
  footer.className = "perf-monitor__footer";
2261
2364
  this.infoCheckbox = this.buildCheckbox(this.settings.info, "Show version and environment in HUD", (on) => this.settings.setInfo(on));
@@ -2492,6 +2595,7 @@ const mountPerfHud = (monitor, options = {}) => {
2492
2595
  budgets: options.budgets,
2493
2596
  mode: options.mode ?? "compact",
2494
2597
  monitor,
2598
+ quality: options.quality,
2495
2599
  refreshHz: options.refreshHz,
2496
2600
  settings,
2497
2601
  theme
@@ -2534,6 +2638,7 @@ const mountPerfHud = (monitor, options = {}) => {
2534
2638
  getMode: () => view.getMode(),
2535
2639
  setBudgets: (budgets) => view.setBudgets(budgets),
2536
2640
  setMode,
2641
+ setQuality: (quality) => view.setQuality(quality),
2537
2642
  getTheme: () => theme.mode,
2538
2643
  setTheme: (mode) => theme.setMode(mode),
2539
2644
  settings,
@@ -2541,6 +2646,6 @@ const mountPerfHud = (monitor, options = {}) => {
2541
2646
  view
2542
2647
  };
2543
2648
  };
2544
- export { PERF_HUD_STYLES as a, DEFAULT_STORAGE_KEY as c, THEME_MODES as d, isThemeMode as f, mountDevPanel as i, HudSettings as l, PerformanceView as n, injectStyles as o, resolveBudgets as p, formatReport as r, dockPanel as s, mountPerfHud as t, HudTheme as u };
2649
+ export { mountDevPanel as a, dockPanel as c, HudTheme as d, THEME_MODES as f, formatQuality as i, DEFAULT_STORAGE_KEY as l, resolveBudgets as m, PerformanceView as n, PERF_HUD_STYLES as o, isThemeMode as p, formatReport as r, injectStyles as s, mountPerfHud as t, HudSettings as u };
2545
2650
 
2546
- //# sourceMappingURL=mount-perf-hud-DezuAiPb.js.map
2651
+ //# sourceMappingURL=mount-perf-hud-DefBWWKu.js.map