@zkmake/three-meter 0.1.1 → 0.3.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.
@@ -1,3 +1,90 @@
1
+ //#region src/ui/theme.ts
2
+ const THEME_MODES = [
3
+ "dark",
4
+ "light",
5
+ "system"
6
+ ];
7
+ const DARK_SCHEME_QUERY = "(prefers-color-scheme: dark)";
8
+ const isThemeMode = (value) => typeof value === "string" && THEME_MODES.includes(value);
9
+ var HudTheme = class {
10
+ current;
11
+ userPick = null;
12
+ listeners = /* @__PURE__ */ new Set();
13
+ media = null;
14
+ onSchemeChange = () => this.notify();
15
+ constructor(mode = "system") {
16
+ this.current = mode;
17
+ this.watch();
18
+ }
19
+ /** What the consumer asked for. */
20
+ get mode() {
21
+ return this.current;
22
+ }
23
+ /** What the person using the HUD picked in the panel, or `null` for no pick. */
24
+ get override() {
25
+ return this.userPick;
26
+ }
27
+ /** The layer that applies: the user's pick if there is one, else the consumer's mode. */
28
+ get effective() {
29
+ return this.userPick ?? this.current;
30
+ }
31
+ /**
32
+ * `dark` or `light`, with `system` resolved against the OS preference.
33
+ * Where `matchMedia` is missing, `system` falls back to `dark`, the HUD's
34
+ * original palette.
35
+ */
36
+ get resolved() {
37
+ const effective = this.effective;
38
+ if (effective !== "system") return effective;
39
+ if (!this.media) return "dark";
40
+ return this.media.matches ? "dark" : "light";
41
+ }
42
+ setMode(mode) {
43
+ if (mode === this.current) return;
44
+ this.current = mode;
45
+ this.watch();
46
+ this.notify();
47
+ }
48
+ setOverride(mode) {
49
+ if (mode === this.userPick) return;
50
+ this.userPick = mode;
51
+ this.watch();
52
+ this.notify();
53
+ }
54
+ /** Fires on `setMode`, `setOverride` and, in `system`, whenever the OS preference flips. */
55
+ subscribe(listener) {
56
+ this.listeners.add(listener);
57
+ return () => {
58
+ this.listeners.delete(listener);
59
+ };
60
+ }
61
+ /** Stop tracking the OS preference and drop every subscriber. */
62
+ dispose() {
63
+ this.unwatch();
64
+ this.listeners.clear();
65
+ }
66
+ watch() {
67
+ if (this.effective !== "system") {
68
+ this.unwatch();
69
+ return;
70
+ }
71
+ if (this.media || typeof matchMedia !== "function") return;
72
+ this.media = matchMedia(DARK_SCHEME_QUERY);
73
+ this.media.addEventListener("change", this.onSchemeChange);
74
+ }
75
+ unwatch() {
76
+ this.media?.removeEventListener("change", this.onSchemeChange);
77
+ this.media = null;
78
+ }
79
+ notify() {
80
+ for (const listener of this.listeners) listener();
81
+ }
82
+ };
83
+ /** Paint the resolved theme onto an element for the stylesheet to key off. */
84
+ const applyTheme = (element, theme) => {
85
+ element.dataset.theme = theme.resolved;
86
+ };
87
+ //#endregion
1
88
  //#region src/ui/hud-settings.ts
2
89
  const DEFAULT_STORAGE_KEY = "three-meter";
3
90
  const DEFAULT_DIM = false;
@@ -38,6 +125,14 @@ var HudSettings = class {
38
125
  this.state.dim = enabled;
39
126
  this.persistAndNotify();
40
127
  }
128
+ /** The user's pick in the panel; `null` means none, so the consumer's option applies. */
129
+ get theme() {
130
+ return this.state.theme;
131
+ }
132
+ setTheme(mode) {
133
+ this.state.theme = mode;
134
+ this.persistAndNotify();
135
+ }
41
136
  isNumberEnabled(key) {
42
137
  return this.state.numbers.has(key);
43
138
  }
@@ -64,7 +159,8 @@ var HudSettings = class {
64
159
  const fallback = () => ({
65
160
  dim: this.defaults.dim,
66
161
  graphs: new Set(this.defaults.graphs),
67
- numbers: new Set(this.defaults.numbers)
162
+ numbers: new Set(this.defaults.numbers),
163
+ theme: null
68
164
  });
69
165
  if (this.storageKey === null) return fallback();
70
166
  try {
@@ -74,7 +170,8 @@ var HudSettings = class {
74
170
  return {
75
171
  dim: parsed.dim ?? this.defaults.dim,
76
172
  graphs: new Set((parsed.graphs ?? this.defaults.graphs).filter(isTimingMetric)),
77
- numbers: new Set(parsed.numbers ?? this.defaults.numbers)
173
+ numbers: new Set(parsed.numbers ?? this.defaults.numbers),
174
+ theme: isThemeMode(parsed.theme) ? parsed.theme : null
78
175
  };
79
176
  } catch {
80
177
  return fallback();
@@ -85,7 +182,8 @@ var HudSettings = class {
85
182
  localStorage.setItem(this.storageKey, JSON.stringify({
86
183
  dim: this.state.dim,
87
184
  graphs: [...this.state.graphs],
88
- numbers: [...this.state.numbers]
185
+ numbers: [...this.state.numbers],
186
+ theme: this.state.theme ?? void 0
89
187
  }));
90
188
  } catch {}
91
189
  for (const listener of this.listeners) listener();
@@ -344,7 +442,11 @@ const dockPanel = (element, options = {}) => {
344
442
  //#region src/ui/icons.ts
345
443
  const ICON_MARKUP = {
346
444
  blend: "<circle cx=\"9\" cy=\"9\" r=\"7\"/><circle cx=\"15\" cy=\"15\" r=\"7\"/>",
445
+ contrast: "<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 18a6 6 0 0 0 0-12v12z\"/>",
347
446
  grip: "<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>",
447
+ monitor: "<rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/><line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/>",
448
+ moon: "<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"/>",
449
+ sun: "<circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/><path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/><path d=\"M2 12h2\"/><path d=\"M20 12h2\"/><path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/>",
348
450
  sliders: "<line x1=\"21\" x2=\"14\" y1=\"4\" y2=\"4\"/><line x1=\"10\" x2=\"3\" y1=\"4\" y2=\"4\"/><line x1=\"21\" x2=\"12\" y1=\"12\" y2=\"12\"/><line x1=\"8\" x2=\"3\" y1=\"12\" y2=\"12\"/><line x1=\"21\" x2=\"16\" y1=\"20\" y2=\"20\"/><line x1=\"12\" x2=\"3\" y1=\"20\" y2=\"20\"/><line x1=\"14\" x2=\"14\" y1=\"2\" y2=\"6\"/><line x1=\"8\" x2=\"8\" y1=\"10\" y2=\"14\"/><line x1=\"16\" x2=\"16\" y1=\"18\" y2=\"22\"/>"
349
451
  };
350
452
  const createIcon = (name, className) => {
@@ -390,8 +492,14 @@ const TIMINGS = [
390
492
  label: "FPS",
391
493
  metric: "fps",
392
494
  style: {
393
- fill: "rgba(74, 222, 128, 0.15)",
394
- stroke: "#4ade80"
495
+ dark: {
496
+ fill: "rgba(74, 222, 128, 0.15)",
497
+ stroke: "#4ade80"
498
+ },
499
+ light: {
500
+ fill: "rgba(22, 163, 74, 0.12)",
501
+ stroke: "#16a34a"
502
+ }
395
503
  },
396
504
  unit: ""
397
505
  },
@@ -400,8 +508,14 @@ const TIMINGS = [
400
508
  label: "CPU",
401
509
  metric: "cpu",
402
510
  style: {
403
- fill: "rgba(96, 165, 250, 0.15)",
404
- stroke: "#60a5fa"
511
+ dark: {
512
+ fill: "rgba(96, 165, 250, 0.15)",
513
+ stroke: "#60a5fa"
514
+ },
515
+ light: {
516
+ fill: "rgba(37, 99, 235, 0.12)",
517
+ stroke: "#2563eb"
518
+ }
405
519
  },
406
520
  unit: "ms"
407
521
  },
@@ -410,12 +524,35 @@ const TIMINGS = [
410
524
  label: "GPU",
411
525
  metric: "gpu",
412
526
  style: {
413
- fill: "rgba(244, 114, 182, 0.15)",
414
- stroke: "#f472b6"
527
+ dark: {
528
+ fill: "rgba(244, 114, 182, 0.15)",
529
+ stroke: "#f472b6"
530
+ },
531
+ light: {
532
+ fill: "rgba(219, 39, 119, 0.12)",
533
+ stroke: "#db2777"
534
+ }
415
535
  },
416
536
  unit: "ms"
417
537
  }
418
538
  ];
539
+ const THEME_OPTIONS = [
540
+ {
541
+ icon: "sun",
542
+ label: "Light theme",
543
+ mode: "light"
544
+ },
545
+ {
546
+ icon: "monitor",
547
+ label: "Match the system theme",
548
+ mode: "system"
549
+ },
550
+ {
551
+ icon: "moon",
552
+ label: "Dark theme",
553
+ mode: "dark"
554
+ }
555
+ ];
419
556
  const formatCount = (value) => value.toLocaleString("en-US");
420
557
  const readTiming = (sample, metric) => {
421
558
  switch (metric) {
@@ -491,6 +628,9 @@ const NUMBERS = [
491
628
  var PerformanceView = class {
492
629
  element;
493
630
  settings;
631
+ theme;
632
+ ownsTheme;
633
+ unsubscribeTheme;
494
634
  monitor;
495
635
  minIntervalMs;
496
636
  mode;
@@ -500,6 +640,7 @@ var PerformanceView = class {
500
640
  statValueEls = /* @__PURE__ */ new Map();
501
641
  statCheckboxes = /* @__PURE__ */ new Map();
502
642
  dimCheckbox;
643
+ themeRadios = /* @__PURE__ */ new Map();
503
644
  hudGraphsEl;
504
645
  hudGridEl;
505
646
  hudGraphs = /* @__PURE__ */ new Map();
@@ -511,6 +652,9 @@ var PerformanceView = class {
511
652
  constructor(options) {
512
653
  this.monitor = options.monitor;
513
654
  this.settings = options.settings ?? new HudSettings();
655
+ this.ownsTheme = !options.theme;
656
+ this.theme = options.theme ?? new HudTheme();
657
+ this.theme.setOverride(this.settings.theme);
514
658
  this.mode = options.mode ?? "full";
515
659
  this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
516
660
  this.element = document.createElement("div");
@@ -519,7 +663,9 @@ var PerformanceView = class {
519
663
  this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());
520
664
  this.build();
521
665
  this.rebuildHud();
666
+ this.onThemeChanged();
522
667
  this.unsubscribe = this.settings.subscribe(() => this.onSelectionChanged());
668
+ this.unsubscribeTheme = this.theme.subscribe(() => this.onThemeChanged());
523
669
  }
524
670
  start() {
525
671
  if (this.rafId === null) this.rafId = requestAnimationFrame(this.frame);
@@ -542,6 +688,8 @@ var PerformanceView = class {
542
688
  dispose() {
543
689
  this.stop();
544
690
  this.unsubscribe();
691
+ this.unsubscribeTheme();
692
+ if (this.ownsTheme) this.theme.dispose();
545
693
  this.resizeObserver.disconnect();
546
694
  this.element.remove();
547
695
  }
@@ -562,7 +710,7 @@ var PerformanceView = class {
562
710
  const valueEl = this.graphValueEls.get(config.metric);
563
711
  valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, config.metric)) : "unavailable";
564
712
  const graph = this.graphCanvases.get(config.metric);
565
- drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(config.metric) : [], config.style);
713
+ drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(config.metric) : [], config.style[this.theme.resolved]);
566
714
  }
567
715
  for (const config of NUMBERS) this.statValueEls.get(config.key).textContent = config.read(sample);
568
716
  }
@@ -575,7 +723,7 @@ var PerformanceView = class {
575
723
  const config = TIMINGS.find((timing) => timing.metric === metric);
576
724
  const available = isTimingAvailable(sample, metric);
577
725
  graph.valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, metric)) : "—";
578
- drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(metric) : [], config.style);
726
+ drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(metric) : [], config.style[this.theme.resolved]);
579
727
  }
580
728
  }
581
729
  formatTiming(config, value) {
@@ -591,14 +739,37 @@ var PerformanceView = class {
591
739
  hud.append(this.hudGraphsEl, this.hudGridEl);
592
740
  const options = document.createElement("div");
593
741
  options.className = "perf-monitor__section perf-monitor__options";
742
+ const themeRow = document.createElement("div");
743
+ themeRow.className = "perf-monitor__row perf-monitor__row--static";
744
+ const themeLabel = document.createElement("span");
745
+ themeLabel.className = "perf-monitor__label";
746
+ themeLabel.textContent = "theme";
747
+ const segment = document.createElement("div");
748
+ segment.className = "perf-monitor__segment";
749
+ segment.setAttribute("role", "radiogroup");
750
+ segment.setAttribute("aria-label", "Panel theme");
751
+ for (const option of THEME_OPTIONS) {
752
+ const radio = document.createElement("button");
753
+ radio.type = "button";
754
+ radio.className = "perf-monitor__segment-option";
755
+ radio.setAttribute("role", "radio");
756
+ radio.setAttribute("aria-checked", "false");
757
+ radio.setAttribute("aria-label", option.label);
758
+ radio.title = option.label;
759
+ radio.append(createIcon(option.icon, "perf-monitor__icon"));
760
+ radio.addEventListener("click", () => this.settings.setTheme(option.mode));
761
+ this.themeRadios.set(option.mode, radio);
762
+ segment.append(radio);
763
+ }
764
+ themeRow.append(createIcon("contrast", "perf-monitor__icon"), themeLabel, segment);
594
765
  const dimRow = document.createElement("label");
595
766
  dimRow.className = "perf-monitor__row";
596
767
  this.dimCheckbox = this.buildCheckbox(this.settings.dim, "Dim the performance panel when the pointer leaves", (on) => this.settings.setDim(on));
597
768
  const dimLabel = document.createElement("span");
598
769
  dimLabel.className = "perf-monitor__label";
599
770
  dimLabel.textContent = "dim on leave";
600
- dimRow.append(this.dimCheckbox, createIcon("blend", "perf-monitor__icon"), dimLabel);
601
- options.append(dimRow);
771
+ dimRow.append(createIcon("blend", "perf-monitor__icon"), dimLabel, this.dimCheckbox);
772
+ options.append(themeRow, dimRow);
602
773
  const graphs = document.createElement("div");
603
774
  graphs.className = "perf-monitor__graphs";
604
775
  for (const config of TIMINGS) {
@@ -709,8 +880,14 @@ var PerformanceView = class {
709
880
  for (const [metric, checkbox] of this.graphCheckboxes) checkbox.checked = this.settings.isGraphEnabled(metric);
710
881
  for (const [key, checkbox] of this.statCheckboxes) checkbox.checked = this.settings.isNumberEnabled(key);
711
882
  this.dimCheckbox.checked = this.settings.dim;
883
+ this.theme.setOverride(this.settings.theme);
712
884
  this.rebuildHud();
713
885
  }
886
+ /** Repaint the palette and mark the radio matching what applies (pick, else consumer mode). */
887
+ onThemeChanged() {
888
+ applyTheme(this.element, this.theme);
889
+ for (const [mode, radio] of this.themeRadios) radio.setAttribute("aria-checked", String(mode === this.theme.effective));
890
+ }
714
891
  buildCheckbox(checked, title, onChange) {
715
892
  const checkbox = document.createElement("input");
716
893
  checkbox.type = "checkbox";
@@ -751,15 +928,42 @@ var PerformanceView = class {
751
928
  * injects itself once per document. `.perf-monitor` is the metrics card,
752
929
  * `.perf-hud` the floating host around it. Both namespaced so a host page can
753
930
  * override safely; the `--perf-*` custom properties are the theming surface.
931
+ *
932
+ * Both elements carry `data-theme="dark" | "light"` (see `HudTheme`) and each
933
+ * resolves its own tokens from it, so the card also themes when mounted on
934
+ * its own, outside a `.perf-hud`.
754
935
  */
755
936
  const STYLE_ATTRIBUTE = "data-three-meter";
756
937
  const PERF_HUD_STYLES = `
938
+ .perf-hud,
757
939
  .perf-monitor {
758
- --perf-bg: #16181d;
940
+ --perf-bg: rgba(22, 24, 29, 0.85);
759
941
  --perf-fg: #e6e8eb;
942
+ --perf-fg-dim: #c5c8ce;
760
943
  --perf-muted: #8b909a;
761
944
  --perf-row: rgba(255, 255, 255, 0.04);
945
+ --perf-border: rgba(255, 255, 255, 0.12);
946
+ --perf-accent: #60a5fa;
947
+ --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
948
+
949
+ color-scheme: dark;
950
+ }
951
+
952
+ .perf-hud[data-theme="light"],
953
+ .perf-monitor[data-theme="light"] {
954
+ --perf-bg: rgba(250, 250, 252, 0.88);
955
+ --perf-fg: #1a1c21;
956
+ --perf-fg-dim: #4b5058;
957
+ --perf-muted: #6b7079;
958
+ --perf-row: rgba(0, 0, 0, 0.05);
959
+ --perf-border: rgba(0, 0, 0, 0.12);
960
+ --perf-accent: #2563eb;
961
+ --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
762
962
 
963
+ color-scheme: light;
964
+ }
965
+
966
+ .perf-monitor {
763
967
  display: flex;
764
968
  flex-direction: column;
765
969
  gap: 10px;
@@ -858,7 +1062,55 @@ const PERF_HUD_STYLES = `
858
1062
 
859
1063
  .perf-monitor__options {
860
1064
  padding-bottom: 8px;
861
- border-bottom: 1px solid rgba(255, 255, 255, 0.12);
1065
+ border-bottom: 1px solid var(--perf-border);
1066
+ }
1067
+
1068
+ .perf-monitor__row--static {
1069
+ cursor: default;
1070
+ }
1071
+
1072
+ /* Light / system / dark, as a small segmented radio group. */
1073
+ .perf-monitor__segment {
1074
+ display: inline-flex;
1075
+ flex: none;
1076
+ gap: 1px;
1077
+ padding: 1px;
1078
+ border-radius: 4px;
1079
+ background: var(--perf-row);
1080
+ }
1081
+
1082
+ .perf-monitor__segment-option {
1083
+ display: flex;
1084
+ align-items: center;
1085
+ justify-content: center;
1086
+ width: 20px;
1087
+ height: 16px;
1088
+ padding: 0;
1089
+ border: 0;
1090
+ border-radius: 3px;
1091
+ background: transparent;
1092
+ color: var(--perf-muted);
1093
+ cursor: pointer;
1094
+ }
1095
+
1096
+ .perf-monitor__segment-option:hover {
1097
+ color: var(--perf-fg);
1098
+ }
1099
+
1100
+ .perf-monitor__segment-option[aria-checked="true"] {
1101
+ color: var(--perf-fg);
1102
+ background: var(--perf-border);
1103
+ }
1104
+
1105
+ .perf-monitor__segment-option:focus-visible {
1106
+ outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
1107
+ outline-offset: 1px;
1108
+ }
1109
+
1110
+ .perf-monitor__segment-option .perf-monitor__icon {
1111
+ width: 11px;
1112
+ height: 11px;
1113
+ color: inherit;
862
1114
  }
863
1115
 
864
1116
  .perf-monitor__checkbox {
@@ -866,7 +1118,7 @@ const PERF_HUD_STYLES = `
866
1118
  margin: 0;
867
1119
  width: 12px;
868
1120
  height: 12px;
869
- accent-color: #60a5fa;
1121
+ accent-color: var(--perf-accent);
870
1122
  cursor: pointer;
871
1123
  }
872
1124
 
@@ -964,9 +1216,9 @@ const PERF_HUD_STYLES = `
964
1216
  z-index: 1;
965
1217
  max-height: 70vh;
966
1218
  overflow: auto;
967
- background: rgba(22, 24, 29, 0.85);
1219
+ background: var(--perf-bg);
968
1220
  border-radius: 6px;
969
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
1221
+ box-shadow: var(--perf-shadow);
970
1222
  backdrop-filter: blur(2px);
971
1223
  pointer-events: auto;
972
1224
  transition: opacity 0.25s ease;
@@ -1027,9 +1279,9 @@ const PERF_HUD_STYLES = `
1027
1279
  padding: 0;
1028
1280
  border: 0;
1029
1281
  border-radius: 999px;
1030
- background: rgba(22, 24, 29, 0.85);
1031
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
1032
- color: #c5c8ce;
1282
+ background: var(--perf-bg);
1283
+ box-shadow: var(--perf-shadow);
1284
+ color: var(--perf-fg-dim);
1033
1285
  cursor: pointer;
1034
1286
  backdrop-filter: blur(2px);
1035
1287
  }
@@ -1050,8 +1302,8 @@ const PERF_HUD_STYLES = `
1050
1302
  .perf-hud__disc:hover,
1051
1303
  .perf-hud__disc:focus-visible,
1052
1304
  .perf-hud__disc[aria-pressed="true"] {
1053
- color: #e6e8eb;
1054
- outline: 1px solid rgba(96, 165, 250, 0.6);
1305
+ color: var(--perf-fg);
1306
+ outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
1055
1307
  outline-offset: 1px;
1056
1308
  }
1057
1309
  `;
@@ -1080,6 +1332,7 @@ const disc = (label, className, icon) => {
1080
1332
  const mountPerfHud = (monitor, options = {}) => {
1081
1333
  const storageKey = options.storageKey === void 0 ? DEFAULT_STORAGE_KEY : options.storageKey;
1082
1334
  const settings = options.settings ?? new HudSettings({ storageKey });
1335
+ const theme = new HudTheme(options.theme ?? "system");
1083
1336
  const parent = options.parent ?? document.body;
1084
1337
  if (options.injectStyles ?? true) injectStyles(parent.ownerDocument);
1085
1338
  const host = document.createElement("div");
@@ -1098,7 +1351,8 @@ const mountPerfHud = (monitor, options = {}) => {
1098
1351
  mode: options.mode ?? "compact",
1099
1352
  monitor,
1100
1353
  refreshHz: options.refreshHz,
1101
- settings
1354
+ settings,
1355
+ theme
1102
1356
  });
1103
1357
  host.append(hotspot, tools, view.element);
1104
1358
  parent.append(host);
@@ -1110,7 +1364,9 @@ const mountPerfHud = (monitor, options = {}) => {
1110
1364
  };
1111
1365
  applyMode();
1112
1366
  applyDim();
1367
+ applyTheme(host, theme);
1113
1368
  const unsubscribe = settings.subscribe(applyDim);
1369
+ const unsubscribeTheme = theme.subscribe(() => applyTheme(host, theme));
1114
1370
  const dock = dockPanel(host, {
1115
1371
  defaultPlacement: options.defaultPlacement,
1116
1372
  handle: grip,
@@ -1159,18 +1415,23 @@ const mountPerfHud = (monitor, options = {}) => {
1159
1415
  host.removeEventListener("pointerleave", onLeave);
1160
1416
  host.removeEventListener("pointerdown", onDown);
1161
1417
  unsubscribe();
1418
+ unsubscribeTheme();
1162
1419
  dock.dispose();
1163
1420
  view.dispose();
1421
+ theme.dispose();
1164
1422
  host.remove();
1165
1423
  },
1166
1424
  element: host,
1167
1425
  getMode: () => view.getMode(),
1168
1426
  setMode,
1427
+ getTheme: () => theme.mode,
1428
+ setTheme: (mode) => theme.setMode(mode),
1169
1429
  settings,
1430
+ theme,
1170
1431
  view
1171
1432
  };
1172
1433
  };
1173
1434
  //#endregion
1174
- export { dockPanel as a, PerformanceView as i, PERF_HUD_STYLES as n, DEFAULT_STORAGE_KEY as o, injectStyles as r, HudSettings as s, mountPerfHud as t };
1435
+ export { dockPanel as a, HudTheme as c, PerformanceView as i, THEME_MODES as l, PERF_HUD_STYLES as n, DEFAULT_STORAGE_KEY as o, injectStyles as r, HudSettings as s, mountPerfHud as t, isThemeMode as u };
1175
1436
 
1176
- //# sourceMappingURL=mount-perf-hud-CduRNym5.js.map
1437
+ //# sourceMappingURL=mount-perf-hud-CZNSZVYn.js.map