@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.
- package/README.md +48 -12
- package/dist/{mount-perf-hud-Dw8KNQTt.d.ts → mount-perf-hud-B3wBLrcu.d.ts} +79 -2
- package/dist/{mount-perf-hud-CduRNym5.js → mount-perf-hud-CZNSZVYn.js} +287 -26
- package/dist/mount-perf-hud-CZNSZVYn.js.map +1 -0
- package/dist/react.d.ts +4 -3
- package/dist/react.js +18 -10
- package/dist/react.js.map +1 -1
- package/dist/styles.css +81 -10
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/package.json +1 -1
- package/dist/mount-perf-hud-CduRNym5.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ geometries, textures and programs, with a small dockable HUD. Zero dependencies.
|
|
|
8
8
|
bun add -d @zkmake/three-meter # or npm i -D / pnpm add -D
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Live demo: [three-meter.pages.dev](https://three-meter.pages.dev/), with a vanilla three and a
|
|
12
|
+
React Three Fiber take on the same scene. Add `?webgpu` for the WebGPU renderer and `?count=5000` to
|
|
13
|
+
load the scene up.
|
|
14
|
+
|
|
11
15
|
The sampler and the card are separate components, so toggling the HUD never remounts your canvas.
|
|
12
16
|
GPU time is measured on WebGPU as well as WebGL. The counters are draw calls, render passes and
|
|
13
17
|
resource counts, which is what you watch when a scene is instanced. The card docks to a screen edge,
|
|
@@ -68,18 +72,46 @@ Canvas is its own React root. To run two canvases on one page, pass the same `st
|
|
|
68
72
|
|
|
69
73
|
`mountPerfHud` and `PerfHud` take:
|
|
70
74
|
|
|
71
|
-
| Option | Default | Meaning
|
|
72
|
-
| ------------------ | ------------------ |
|
|
73
|
-
| `mode` | `"compact"` | `compact` is the card. `full` is the checkbox list that configures it.
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
75
|
+
| Option | Default | Meaning |
|
|
76
|
+
| ------------------ | ------------------ | --------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `mode` | `"compact"` | `compact` is the card. `full` is the checkbox list that configures it. |
|
|
78
|
+
| `theme` | `"system"` | `dark`, `light`, or `system` to follow `prefers-color-scheme` live. A pick in the panel wins. |
|
|
79
|
+
| `defaultPlacement` | `{ edge: "left" }` | First-visit dock, as `edge` plus `align` of `start`, `center` or `end`. A drag overrides it. |
|
|
80
|
+
| `storageKey` | `three-meter` | localStorage key for the selection and the dock. `null` disables persistence. |
|
|
81
|
+
| `parent` | `document.body` | Where the host element is appended. |
|
|
82
|
+
| `injectStyles` | `true` | Append the stylesheet once per document. |
|
|
83
|
+
| `refreshHz` | `10` | Repaint rate. |
|
|
79
84
|
|
|
80
85
|
`PerformanceMonitor` and `PerfSampler` take `trackGPU` (default true), `gpuQueryPoolSize` (default 5)
|
|
81
86
|
and `historySize` (default 120).
|
|
82
87
|
|
|
88
|
+
## Theme
|
|
89
|
+
|
|
90
|
+
The HUD ships a dark and a light palette. Two layers decide which shows:
|
|
91
|
+
|
|
92
|
+
1. **The panel's own pick.** The full view has a light / system / dark row. A pick there is kept
|
|
93
|
+
with the other settings under `storageKey` and wins while set.
|
|
94
|
+
2. **Your `theme` option.** `dark`, `light`, or `system` (the default), which follows the OS and
|
|
95
|
+
switches when it does. Applies whenever the person using the HUD hasn't picked anything.
|
|
96
|
+
|
|
97
|
+
Change your layer later from the handle, or from the `PerfHud` prop in React, which applies without
|
|
98
|
+
remounting:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const hud = mountPerfHud(monitor, { theme: "system" });
|
|
102
|
+
hud.setTheme("light"); // your layer: "dark" | "light" | "system"
|
|
103
|
+
hud.getTheme(); // your layer, as asked for
|
|
104
|
+
hud.settings.theme; // the panel's pick, or null
|
|
105
|
+
hud.settings.setTheme(null); // clear the pick so your layer applies again
|
|
106
|
+
hud.theme.effective; // whichever layer is in force
|
|
107
|
+
hud.theme.resolved; // "dark" | "light", what is on screen right now
|
|
108
|
+
hud.theme.subscribe(() => syncMyPageWith(hud.theme.resolved));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The resolved theme lands on `data-theme` of `.perf-hud` and `.perf-monitor`. To restyle either
|
|
112
|
+
palette, override the `--perf-*` custom properties (`bg`, `fg`, `fg-dim`, `muted`, `row`,
|
|
113
|
+
`border`, `accent`, `shadow`) on those selectors.
|
|
114
|
+
|
|
83
115
|
## GPU timing
|
|
84
116
|
|
|
85
117
|
On WebGL2 the monitor uses `EXT_disjoint_timer_query_webgl2`, which Chrome and Edge expose. Safari
|
|
@@ -89,9 +121,13 @@ On WebGPU, construct the renderer with `trackTimestamp: true`. Timings resolve a
|
|
|
89
121
|
|
|
90
122
|
## Examples
|
|
91
123
|
|
|
92
|
-
[`examples/
|
|
93
|
-
|
|
94
|
-
is
|
|
124
|
+
[`examples/site`](examples/site) is what runs at [three-meter.pages.dev](https://three-meter.pages.dev/):
|
|
125
|
+
one Vite app with both integrations of the same scene, swapped from the header.
|
|
126
|
+
[`src/demos/vanilla.ts`](examples/site/src/demos/vanilla.ts) is plain three with `mountPerfHud`;
|
|
127
|
+
[`src/demos/r3f.tsx`](examples/site/src/demos/r3f.tsx) is React Three Fiber with `PerfSampler` and
|
|
128
|
+
`PerfHud`. `?r3f` opens on Fiber, `?webgpu` uses `WebGPURenderer` in either, `?count=` scales the
|
|
129
|
+
scene. The header's theme toggle sets the page theme and the HUD's `theme` layer together; the row
|
|
130
|
+
inside the panel overrides the HUD alone. Run `bun run dev` inside it.
|
|
95
131
|
|
|
96
132
|
## Shipping it
|
|
97
133
|
|
|
@@ -105,7 +141,7 @@ your call. Gate the import in the app with a `?debug=` query, a build flag or a
|
|
|
105
141
|
|
|
106
142
|
While a monitor is attached, `renderer.info.autoReset` is off and `begin()` resets `info`. One
|
|
107
143
|
monitor per renderer. A second throws. `dispose()` restores everything. Theme with the `--perf-*`
|
|
108
|
-
custom properties on `.perf-monitor
|
|
144
|
+
custom properties on `.perf-hud` and `.perf-monitor`, or the `theme` option.
|
|
109
145
|
|
|
110
146
|
## Contributing
|
|
111
147
|
|
|
@@ -32,11 +32,58 @@ type DockHandle = {
|
|
|
32
32
|
*/
|
|
33
33
|
declare const dockPanel: (element: HTMLElement, options?: DockPanelOptions) => DockHandle;
|
|
34
34
|
//#endregion
|
|
35
|
+
//#region src/ui/theme.d.ts
|
|
36
|
+
/**
|
|
37
|
+
* Dark, light, or follow the OS. One instance per HUD: the floating host and
|
|
38
|
+
* the card inside it both paint `resolved` onto `data-theme`, and the
|
|
39
|
+
* stylesheet picks the palette from that attribute.
|
|
40
|
+
*
|
|
41
|
+
* Two layers. `mode` is what the consumer asked for (the `theme` option,
|
|
42
|
+
* `setTheme`). `override` is what the person using the HUD picked in the
|
|
43
|
+
* panel, kept in `HudSettings`; it wins while set. `effective` is whichever
|
|
44
|
+
* applies, and `system` there tracks `prefers-color-scheme` for as long as
|
|
45
|
+
* the instance lives.
|
|
46
|
+
*/
|
|
47
|
+
type ThemeMode = "dark" | "light" | "system";
|
|
48
|
+
type ResolvedTheme = Exclude<ThemeMode, "system">;
|
|
49
|
+
declare const THEME_MODES: readonly ThemeMode[];
|
|
50
|
+
declare const isThemeMode: (value: unknown) => value is ThemeMode;
|
|
51
|
+
declare class HudTheme {
|
|
52
|
+
private current;
|
|
53
|
+
private userPick;
|
|
54
|
+
private readonly listeners;
|
|
55
|
+
private media;
|
|
56
|
+
private readonly onSchemeChange;
|
|
57
|
+
constructor(mode?: ThemeMode);
|
|
58
|
+
/** What the consumer asked for. */
|
|
59
|
+
get mode(): ThemeMode;
|
|
60
|
+
/** What the person using the HUD picked in the panel, or `null` for no pick. */
|
|
61
|
+
get override(): ThemeMode | null;
|
|
62
|
+
/** The layer that applies: the user's pick if there is one, else the consumer's mode. */
|
|
63
|
+
get effective(): ThemeMode;
|
|
64
|
+
/**
|
|
65
|
+
* `dark` or `light`, with `system` resolved against the OS preference.
|
|
66
|
+
* Where `matchMedia` is missing, `system` falls back to `dark`, the HUD's
|
|
67
|
+
* original palette.
|
|
68
|
+
*/
|
|
69
|
+
get resolved(): ResolvedTheme;
|
|
70
|
+
setMode(mode: ThemeMode): void;
|
|
71
|
+
setOverride(mode: ThemeMode | null): void;
|
|
72
|
+
/** Fires on `setMode`, `setOverride` and, in `system`, whenever the OS preference flips. */
|
|
73
|
+
subscribe(listener: () => void): () => void;
|
|
74
|
+
/** Stop tracking the OS preference and drop every subscriber. */
|
|
75
|
+
dispose(): void;
|
|
76
|
+
private watch;
|
|
77
|
+
private unwatch;
|
|
78
|
+
private notify;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
35
81
|
//#region src/ui/hud-settings.d.ts
|
|
36
82
|
type HudSelection = {
|
|
37
83
|
dim: boolean;
|
|
38
84
|
graphs: ReadonlySet<TimingMetric>;
|
|
39
85
|
numbers: ReadonlySet<string>;
|
|
86
|
+
theme: ThemeMode | null;
|
|
40
87
|
};
|
|
41
88
|
type HudDefaults = {
|
|
42
89
|
dim?: boolean;
|
|
@@ -59,6 +106,9 @@ declare class HudSettings {
|
|
|
59
106
|
get selection(): HudSelection;
|
|
60
107
|
get dim(): boolean;
|
|
61
108
|
setDim(enabled: boolean): void;
|
|
109
|
+
/** The user's pick in the panel; `null` means none, so the consumer's option applies. */
|
|
110
|
+
get theme(): ThemeMode | null;
|
|
111
|
+
setTheme(mode: ThemeMode | null): void;
|
|
62
112
|
isNumberEnabled(key: string): boolean;
|
|
63
113
|
isGraphEnabled(metric: TimingMetric): boolean;
|
|
64
114
|
setNumberEnabled(key: string, enabled: boolean): void;
|
|
@@ -78,6 +128,11 @@ type PerformanceViewOptions = {
|
|
|
78
128
|
refreshHz?: number;
|
|
79
129
|
/** Share one between views to keep them in sync; a fresh instance otherwise. */
|
|
80
130
|
settings?: HudSettings;
|
|
131
|
+
/**
|
|
132
|
+
* Share the host's so the card and its chrome switch together. Otherwise a
|
|
133
|
+
* fresh `system` instance is created and disposed with the view.
|
|
134
|
+
*/
|
|
135
|
+
theme?: HudTheme;
|
|
81
136
|
};
|
|
82
137
|
/**
|
|
83
138
|
* Host-agnostic widget that paints {@link Sample}s. Mount {@link element}
|
|
@@ -88,6 +143,9 @@ type PerformanceViewOptions = {
|
|
|
88
143
|
declare class PerformanceView {
|
|
89
144
|
readonly element: HTMLDivElement;
|
|
90
145
|
readonly settings: HudSettings;
|
|
146
|
+
readonly theme: HudTheme;
|
|
147
|
+
private readonly ownsTheme;
|
|
148
|
+
private readonly unsubscribeTheme;
|
|
91
149
|
private readonly monitor;
|
|
92
150
|
private readonly minIntervalMs;
|
|
93
151
|
private mode;
|
|
@@ -97,6 +155,7 @@ declare class PerformanceView {
|
|
|
97
155
|
private readonly statValueEls;
|
|
98
156
|
private readonly statCheckboxes;
|
|
99
157
|
private dimCheckbox;
|
|
158
|
+
private readonly themeRadios;
|
|
100
159
|
private hudGraphsEl;
|
|
101
160
|
private hudGridEl;
|
|
102
161
|
private readonly hudGraphs;
|
|
@@ -118,6 +177,8 @@ declare class PerformanceView {
|
|
|
118
177
|
private build;
|
|
119
178
|
private rebuildHud;
|
|
120
179
|
private onSelectionChanged;
|
|
180
|
+
/** Repaint the palette and mark the radio matching what applies (pick, else consumer mode). */
|
|
181
|
+
private onThemeChanged;
|
|
121
182
|
private buildCheckbox;
|
|
122
183
|
private applyModeClass;
|
|
123
184
|
private resizeCanvases;
|
|
@@ -127,6 +188,12 @@ declare class PerformanceView {
|
|
|
127
188
|
type MountPerfHudOptions = {
|
|
128
189
|
/** Start compact (the card) or full (the checkbox list). Default `compact`. */
|
|
129
190
|
mode?: PerformanceViewMode;
|
|
191
|
+
/**
|
|
192
|
+
* `dark`, `light`, or `system` to follow the OS preference. Default
|
|
193
|
+
* `system`. Change it later with the handle's `setTheme`. A pick made in
|
|
194
|
+
* the panel's own theme row (kept in `settings`) sits on top of this.
|
|
195
|
+
*/
|
|
196
|
+
theme?: ThemeMode;
|
|
130
197
|
/** localStorage key for the selection and the dock. `null` disables persistence. */
|
|
131
198
|
storageKey?: string | null;
|
|
132
199
|
/** Bring your own; otherwise one is created from `storageKey`. */
|
|
@@ -146,11 +213,21 @@ type PerfHudHandle = {
|
|
|
146
213
|
element: HTMLDivElement;
|
|
147
214
|
view: PerformanceView;
|
|
148
215
|
settings: HudSettings;
|
|
216
|
+
/**
|
|
217
|
+
* `mode` is the consumer layer, `override` the panel's pick, `effective`
|
|
218
|
+
* whichever applies, `resolved` the `dark` / `light` on screen. Subscribe
|
|
219
|
+
* for changes, including the OS preference moving under `system`.
|
|
220
|
+
*/
|
|
221
|
+
theme: HudTheme;
|
|
149
222
|
getMode: () => PerformanceViewMode;
|
|
150
223
|
setMode: (mode: PerformanceViewMode) => void;
|
|
224
|
+
/** The consumer layer. The panel's pick, if any, is `settings.theme`. */
|
|
225
|
+
getTheme: () => ThemeMode;
|
|
226
|
+
/** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */
|
|
227
|
+
setTheme: (mode: ThemeMode) => void;
|
|
151
228
|
dispose: () => void;
|
|
152
229
|
};
|
|
153
230
|
declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
|
|
154
231
|
//#endregion
|
|
155
|
-
export { ScreenEdge as _, PerformanceViewMode as a, HudDefaults as c, HudSettingsOptions as d,
|
|
156
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
232
|
+
export { dockPanel as C, ScreenEdge as S, 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, Placement as x, DockHandle as y };
|
|
233
|
+
//# sourceMappingURL=mount-perf-hud-B3wBLrcu.d.ts.map
|