@three-flatland/devtools 0.1.0-alpha.3
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/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/buffers-modal.cjs +491 -0
- package/dist/buffers-modal.cjs.map +1 -0
- package/dist/buffers-modal.d.cts +62 -0
- package/dist/buffers-modal.d.ts +62 -0
- package/dist/buffers-modal.js +467 -0
- package/dist/buffers-modal.js.map +1 -0
- package/dist/buffers-view.cjs +269 -0
- package/dist/buffers-view.cjs.map +1 -0
- package/dist/buffers-view.d.cts +39 -0
- package/dist/buffers-view.d.ts +39 -0
- package/dist/buffers-view.js +245 -0
- package/dist/buffers-view.js.map +1 -0
- package/dist/create-pane.cjs +203 -0
- package/dist/create-pane.cjs.map +1 -0
- package/dist/create-pane.d.cts +66 -0
- package/dist/create-pane.d.ts +66 -0
- package/dist/create-pane.js +178 -0
- package/dist/create-pane.js.map +1 -0
- package/dist/dashboard/app.tsx +42 -0
- package/dist/dashboard/client.ts +21 -0
- package/dist/dashboard/export.ts +84 -0
- package/dist/dashboard/hooks.ts +75 -0
- package/dist/dashboard/index.html +1149 -0
- package/dist/dashboard/index.tsx +18 -0
- package/dist/dashboard/panels/batches.tsx +282 -0
- package/dist/dashboard/panels/buffers.tsx +425 -0
- package/dist/dashboard/panels/env.tsx +85 -0
- package/dist/dashboard/panels/header-stats.tsx +30 -0
- package/dist/dashboard/panels/producer-select.tsx +81 -0
- package/dist/dashboard/panels/protocol-log.tsx +743 -0
- package/dist/dashboard/panels/registry.tsx +642 -0
- package/dist/dashboard/panels/stats.tsx +431 -0
- package/dist/dashboard/protocol-store.ts +312 -0
- package/dist/dashboard/tsconfig.json +19 -0
- package/dist/dashboard/vendor/hooks.module.js +1 -0
- package/dist/dashboard/vendor/jsx-runtime.js +22 -0
- package/dist/dashboard/vendor/preact.module.js +12 -0
- package/dist/devtools-client.cjs +870 -0
- package/dist/devtools-client.cjs.map +1 -0
- package/dist/devtools-client.d.cts +362 -0
- package/dist/devtools-client.d.ts +362 -0
- package/dist/devtools-client.js +852 -0
- package/dist/devtools-client.js.map +1 -0
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/perf-trace.cjs +103 -0
- package/dist/perf-trace.cjs.map +1 -0
- package/dist/perf-trace.d.cts +27 -0
- package/dist/perf-trace.d.ts +27 -0
- package/dist/perf-trace.js +79 -0
- package/dist/perf-trace.js.map +1 -0
- package/dist/plugins.cjs +44 -0
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +7 -0
- package/dist/plugins.d.ts +7 -0
- package/dist/plugins.js +9 -0
- package/dist/plugins.js.map +1 -0
- package/dist/provider-switcher.cjs +95 -0
- package/dist/provider-switcher.cjs.map +1 -0
- package/dist/provider-switcher.d.cts +18 -0
- package/dist/provider-switcher.d.ts +18 -0
- package/dist/provider-switcher.js +71 -0
- package/dist/provider-switcher.js.map +1 -0
- package/dist/react/devtools-provider.cjs +59 -0
- package/dist/react/devtools-provider.cjs.map +1 -0
- package/dist/react/devtools-provider.d.cts +39 -0
- package/dist/react/devtools-provider.d.ts +39 -0
- package/dist/react/devtools-provider.js +38 -0
- package/dist/react/devtools-provider.js.map +1 -0
- package/dist/react/use-fps-graph.cjs +49 -0
- package/dist/react/use-fps-graph.cjs.map +1 -0
- package/dist/react/use-fps-graph.d.cts +15 -0
- package/dist/react/use-fps-graph.d.ts +15 -0
- package/dist/react/use-fps-graph.js +25 -0
- package/dist/react/use-fps-graph.js.map +1 -0
- package/dist/react/use-pane-button.cjs +58 -0
- package/dist/react/use-pane-button.cjs.map +1 -0
- package/dist/react/use-pane-button.d.cts +10 -0
- package/dist/react/use-pane-button.d.ts +10 -0
- package/dist/react/use-pane-button.js +34 -0
- package/dist/react/use-pane-button.js.map +1 -0
- package/dist/react/use-pane-folder.cjs +48 -0
- package/dist/react/use-pane-folder.cjs.map +1 -0
- package/dist/react/use-pane-folder.d.cts +15 -0
- package/dist/react/use-pane-folder.d.ts +15 -0
- package/dist/react/use-pane-folder.js +24 -0
- package/dist/react/use-pane-folder.js.map +1 -0
- package/dist/react/use-pane-input.cjs +70 -0
- package/dist/react/use-pane-input.cjs.map +1 -0
- package/dist/react/use-pane-input.d.cts +44 -0
- package/dist/react/use-pane-input.d.ts +44 -0
- package/dist/react/use-pane-input.js +46 -0
- package/dist/react/use-pane-input.js.map +1 -0
- package/dist/react/use-pane-radio-grid.cjs +78 -0
- package/dist/react/use-pane-radio-grid.cjs.map +1 -0
- package/dist/react/use-pane-radio-grid.d.cts +35 -0
- package/dist/react/use-pane-radio-grid.d.ts +35 -0
- package/dist/react/use-pane-radio-grid.js +54 -0
- package/dist/react/use-pane-radio-grid.js.map +1 -0
- package/dist/react/use-pane.cjs +45 -0
- package/dist/react/use-pane.cjs.map +1 -0
- package/dist/react/use-pane.d.cts +40 -0
- package/dist/react/use-pane.d.ts +40 -0
- package/dist/react/use-pane.js +21 -0
- package/dist/react/use-pane.js.map +1 -0
- package/dist/react.cjs +50 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +12 -0
- package/dist/react.d.ts +12 -0
- package/dist/react.js +19 -0
- package/dist/react.js.map +1 -0
- package/dist/registry-view.cjs +371 -0
- package/dist/registry-view.cjs.map +1 -0
- package/dist/registry-view.d.cts +24 -0
- package/dist/registry-view.d.ts +24 -0
- package/dist/registry-view.js +347 -0
- package/dist/registry-view.js.map +1 -0
- package/dist/stats-graph.cjs +297 -0
- package/dist/stats-graph.cjs.map +1 -0
- package/dist/stats-graph.d.cts +40 -0
- package/dist/stats-graph.d.ts +40 -0
- package/dist/stats-graph.js +273 -0
- package/dist/stats-graph.js.map +1 -0
- package/dist/stats-row.cjs +153 -0
- package/dist/stats-row.cjs.map +1 -0
- package/dist/stats-row.d.cts +24 -0
- package/dist/stats-row.d.ts +24 -0
- package/dist/stats-row.js +129 -0
- package/dist/stats-row.js.map +1 -0
- package/dist/theme.cjs +260 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +10 -0
- package/dist/theme.d.ts +10 -0
- package/dist/theme.js +235 -0
- package/dist/theme.js.map +1 -0
- package/dist/vite-plugin.cjs +113 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.cts +9 -0
- package/dist/vite-plugin.d.ts +9 -0
- package/dist/vite-plugin.js +88 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
+
<title>three-flatland devtools</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
color-scheme: dark;
|
|
10
|
+
--bg: #0e1016;
|
|
11
|
+
--bg-elev: #14171e;
|
|
12
|
+
--bg-elev-2: #1b1f29;
|
|
13
|
+
--border: #2a2e37;
|
|
14
|
+
--fg: #e4e6eb;
|
|
15
|
+
--fg-dim: #9aa0ab;
|
|
16
|
+
--accent: #5eb0ff;
|
|
17
|
+
--live: #5ed38a;
|
|
18
|
+
}
|
|
19
|
+
html, body { height: 100%; margin: 0; overflow: hidden; }
|
|
20
|
+
body {
|
|
21
|
+
background: var(--bg);
|
|
22
|
+
color: var(--fg);
|
|
23
|
+
font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
24
|
+
}
|
|
25
|
+
button { font: inherit; color: inherit; }
|
|
26
|
+
|
|
27
|
+
/* Shared scroll styling across every scrollable region in the
|
|
28
|
+
* dashboard — thin scrollbars, muted thumbs, contain-by-default so
|
|
29
|
+
* wheel events don't chain into ancestor containers. */
|
|
30
|
+
.dashboard-root * {
|
|
31
|
+
scrollbar-width: thin;
|
|
32
|
+
scrollbar-color: rgba(255, 255, 255, 0.12) transparent;
|
|
33
|
+
}
|
|
34
|
+
.dashboard-root *::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
35
|
+
.dashboard-root *::-webkit-scrollbar-track { background: transparent; }
|
|
36
|
+
.dashboard-root *::-webkit-scrollbar-thumb {
|
|
37
|
+
background: rgba(255, 255, 255, 0.12);
|
|
38
|
+
border-radius: 4px;
|
|
39
|
+
}
|
|
40
|
+
.dashboard-root *::-webkit-scrollbar-thumb:hover {
|
|
41
|
+
background: rgba(255, 255, 255, 0.2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Lock the root to the viewport — no page-level scrolling, each
|
|
45
|
+
* panel owns its own scroll region. `100dvh` (with a `100vh`
|
|
46
|
+
* fallback) keeps mobile Safari's dynamic toolbar from clipping the
|
|
47
|
+
* bottom panel. `min-height: 0` lets descendant flex items shrink
|
|
48
|
+
* below their content. */
|
|
49
|
+
.dashboard-root {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
height: 100vh;
|
|
53
|
+
height: 100dvh;
|
|
54
|
+
min-height: 0;
|
|
55
|
+
}
|
|
56
|
+
.dashboard-header {
|
|
57
|
+
padding: 6px 12px;
|
|
58
|
+
border-bottom: 1px solid var(--border);
|
|
59
|
+
background: var(--bg-elev);
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 12px;
|
|
63
|
+
}
|
|
64
|
+
.brand { font-weight: 600; letter-spacing: 0.02em; flex: 0 0 auto; }
|
|
65
|
+
.dashboard-main { flex: 1; display: flex; min-height: 0; }
|
|
66
|
+
.dashboard-content {
|
|
67
|
+
flex: 1;
|
|
68
|
+
padding: 8px;
|
|
69
|
+
min-width: 0;
|
|
70
|
+
min-height: 0;
|
|
71
|
+
/* Column flex so the stats strip sits on top and the split grows
|
|
72
|
+
* to absorb the remaining vertical space. No outer overflow —
|
|
73
|
+
* each panel manages its own scrolling so the page doesn't
|
|
74
|
+
* expand past the viewport. */
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
gap: 8px;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
}
|
|
80
|
+
/*
|
|
81
|
+
* Main content split: on wide viewports, buffers + registry live in
|
|
82
|
+
* a left column while protocol takes the right. Below ~1150px (too
|
|
83
|
+
* narrow for both a fullscreen canvas and a usable protocol list)
|
|
84
|
+
* they fall back into one stacked column.
|
|
85
|
+
*/
|
|
86
|
+
.dashboard-split {
|
|
87
|
+
display: flex;
|
|
88
|
+
gap: 8px;
|
|
89
|
+
min-height: 0;
|
|
90
|
+
/* Let the split stretch vertically so the protocol column can
|
|
91
|
+
* actually grow — otherwise it just hugs its min-height. */
|
|
92
|
+
flex: 1;
|
|
93
|
+
}
|
|
94
|
+
.dashboard-split-col {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
min-width: 0;
|
|
99
|
+
min-height: 0;
|
|
100
|
+
flex: 1;
|
|
101
|
+
}
|
|
102
|
+
.producer-select { position: relative; }
|
|
103
|
+
.producer-button {
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
gap: 8px;
|
|
107
|
+
padding: 4px 10px;
|
|
108
|
+
background: var(--bg);
|
|
109
|
+
color: var(--fg);
|
|
110
|
+
border: 1px solid var(--border);
|
|
111
|
+
border-radius: 3px;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
min-width: 220px;
|
|
114
|
+
}
|
|
115
|
+
.producer-button:disabled { opacity: 0.5; cursor: default; }
|
|
116
|
+
.producer-button-open { border-color: var(--accent); }
|
|
117
|
+
.producer-button .producer-label {
|
|
118
|
+
flex: 1;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
text-overflow: ellipsis;
|
|
121
|
+
white-space: nowrap;
|
|
122
|
+
text-align: left;
|
|
123
|
+
}
|
|
124
|
+
.producer-button .producer-kind {
|
|
125
|
+
font-size: 9px;
|
|
126
|
+
text-transform: uppercase;
|
|
127
|
+
color: var(--fg-dim);
|
|
128
|
+
letter-spacing: 0.06em;
|
|
129
|
+
}
|
|
130
|
+
.producer-caret { color: var(--fg-dim); font-size: 8px; }
|
|
131
|
+
.producer-menu {
|
|
132
|
+
position: absolute;
|
|
133
|
+
top: calc(100% + 4px);
|
|
134
|
+
left: 0;
|
|
135
|
+
right: 0;
|
|
136
|
+
margin: 0;
|
|
137
|
+
padding: 4px 0;
|
|
138
|
+
list-style: none;
|
|
139
|
+
background: var(--bg-elev-2);
|
|
140
|
+
border: 1px solid var(--border);
|
|
141
|
+
border-radius: 3px;
|
|
142
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
143
|
+
z-index: 100;
|
|
144
|
+
max-height: 320px;
|
|
145
|
+
overflow-y: auto;
|
|
146
|
+
}
|
|
147
|
+
.producer-option {
|
|
148
|
+
display: grid;
|
|
149
|
+
grid-template-columns: 16px 1fr auto;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
align-items: center;
|
|
152
|
+
width: 100%;
|
|
153
|
+
padding: 4px 10px;
|
|
154
|
+
background: transparent;
|
|
155
|
+
color: var(--fg);
|
|
156
|
+
border: none;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
text-align: left;
|
|
159
|
+
}
|
|
160
|
+
.producer-option:hover { background: rgba(255, 255, 255, 0.04); }
|
|
161
|
+
.producer-option-active { color: var(--accent); }
|
|
162
|
+
.producer-check { text-align: center; color: var(--accent); }
|
|
163
|
+
|
|
164
|
+
.header-stats {
|
|
165
|
+
display: inline-flex;
|
|
166
|
+
gap: 12px;
|
|
167
|
+
margin-left: auto;
|
|
168
|
+
color: var(--fg-dim);
|
|
169
|
+
font-variant-numeric: tabular-nums;
|
|
170
|
+
font-size: 11px;
|
|
171
|
+
}
|
|
172
|
+
.header-stats span[title] { cursor: help; }
|
|
173
|
+
/* Match protocol-log direction arrow palette: green in, amber out. */
|
|
174
|
+
.header-arrow-in { color: #5ed38a; font-weight: 700; }
|
|
175
|
+
.header-arrow-out { color: #f0b429; font-weight: 700; }
|
|
176
|
+
|
|
177
|
+
.panel {
|
|
178
|
+
background: var(--bg-elev-2);
|
|
179
|
+
border: 1px solid var(--border);
|
|
180
|
+
border-radius: 4px;
|
|
181
|
+
/* Gap-based spacing is owned by the layout containers now; the
|
|
182
|
+
* per-panel margin would stack with `gap` and push total height
|
|
183
|
+
* past the viewport, causing the log to grow the page down. */
|
|
184
|
+
}
|
|
185
|
+
.panel-header {
|
|
186
|
+
padding: 6px 10px;
|
|
187
|
+
font-size: 11px;
|
|
188
|
+
text-transform: uppercase;
|
|
189
|
+
letter-spacing: 0.06em;
|
|
190
|
+
color: var(--fg-dim);
|
|
191
|
+
border-bottom: 1px solid var(--border);
|
|
192
|
+
background: rgba(255, 255, 255, 0.02);
|
|
193
|
+
}
|
|
194
|
+
.panel-body { padding: 8px 10px; }
|
|
195
|
+
.panel-empty { margin: 4px 0; color: var(--fg-dim); }
|
|
196
|
+
|
|
197
|
+
.producers-list { list-style: none; margin: 0; padding: 0; }
|
|
198
|
+
.producers-list li + li { margin-top: 2px; }
|
|
199
|
+
.producer-row {
|
|
200
|
+
display: grid;
|
|
201
|
+
grid-template-columns: auto 1fr auto;
|
|
202
|
+
gap: 8px;
|
|
203
|
+
align-items: center;
|
|
204
|
+
width: 100%;
|
|
205
|
+
padding: 6px 8px;
|
|
206
|
+
background: transparent;
|
|
207
|
+
border: 1px solid transparent;
|
|
208
|
+
border-radius: 3px;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
text-align: left;
|
|
211
|
+
}
|
|
212
|
+
.producer-row:hover { background: rgba(255, 255, 255, 0.03); }
|
|
213
|
+
.producer-row-selected {
|
|
214
|
+
background: rgba(94, 176, 255, 0.08);
|
|
215
|
+
border-color: rgba(94, 176, 255, 0.3);
|
|
216
|
+
}
|
|
217
|
+
.producer-kind {
|
|
218
|
+
font-size: 10px;
|
|
219
|
+
text-transform: uppercase;
|
|
220
|
+
color: var(--fg-dim);
|
|
221
|
+
}
|
|
222
|
+
.producer-label { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
223
|
+
.dot {
|
|
224
|
+
width: 8px;
|
|
225
|
+
height: 8px;
|
|
226
|
+
border-radius: 50%;
|
|
227
|
+
background: #3a3f4b;
|
|
228
|
+
}
|
|
229
|
+
.dot-live { background: var(--live); box-shadow: 0 0 6px rgba(94, 211, 138, 0.6); }
|
|
230
|
+
|
|
231
|
+
.stats-strip {
|
|
232
|
+
display: grid;
|
|
233
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
234
|
+
gap: 4px;
|
|
235
|
+
/* Keep top/bottom padding in line with other panels so the gap
|
|
236
|
+
* between Stats and the panel beneath doesn't read larger than
|
|
237
|
+
* the inter-panel gap elsewhere. */
|
|
238
|
+
padding: 4px 10px;
|
|
239
|
+
}
|
|
240
|
+
.stat {
|
|
241
|
+
position: relative;
|
|
242
|
+
display: flex;
|
|
243
|
+
flex-direction: column;
|
|
244
|
+
gap: 2px;
|
|
245
|
+
padding: 6px 10px;
|
|
246
|
+
background: var(--bg);
|
|
247
|
+
border: 1px solid transparent;
|
|
248
|
+
border-radius: 3px;
|
|
249
|
+
overflow: hidden;
|
|
250
|
+
min-height: 52px;
|
|
251
|
+
cursor: pointer;
|
|
252
|
+
text-align: left;
|
|
253
|
+
color: inherit;
|
|
254
|
+
font: inherit;
|
|
255
|
+
appearance: none;
|
|
256
|
+
-webkit-appearance: none;
|
|
257
|
+
}
|
|
258
|
+
.stat:focus { outline: none; }
|
|
259
|
+
.stat:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
|
|
260
|
+
.stat-canvas {
|
|
261
|
+
position: absolute;
|
|
262
|
+
inset: 0;
|
|
263
|
+
width: 100%;
|
|
264
|
+
height: 100%;
|
|
265
|
+
pointer-events: none;
|
|
266
|
+
opacity: 0.6;
|
|
267
|
+
}
|
|
268
|
+
.stat-label {
|
|
269
|
+
position: relative;
|
|
270
|
+
font-size: 10px;
|
|
271
|
+
text-transform: uppercase;
|
|
272
|
+
letter-spacing: 0.06em;
|
|
273
|
+
color: var(--stat-color, var(--fg-dim));
|
|
274
|
+
}
|
|
275
|
+
.stat-value {
|
|
276
|
+
position: relative;
|
|
277
|
+
font-size: 15px;
|
|
278
|
+
font-variant-numeric: tabular-nums;
|
|
279
|
+
font-weight: 500;
|
|
280
|
+
}
|
|
281
|
+
.stat:hover { background: rgba(255, 255, 255, 0.02); }
|
|
282
|
+
.stat-selected { box-shadow: inset 0 0 0 1px var(--stat-color, var(--accent)); }
|
|
283
|
+
|
|
284
|
+
.stat-detail {
|
|
285
|
+
margin: 0 10px 8px;
|
|
286
|
+
padding: 8px 10px;
|
|
287
|
+
background: var(--bg);
|
|
288
|
+
border: 1px solid var(--border);
|
|
289
|
+
border-top: 2px solid var(--stat-color, var(--accent));
|
|
290
|
+
border-radius: 3px;
|
|
291
|
+
}
|
|
292
|
+
.stat-detail-header {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 12px;
|
|
296
|
+
margin-bottom: 6px;
|
|
297
|
+
}
|
|
298
|
+
.stat-detail-label {
|
|
299
|
+
font-size: 11px;
|
|
300
|
+
text-transform: uppercase;
|
|
301
|
+
letter-spacing: 0.06em;
|
|
302
|
+
color: var(--stat-color, var(--fg));
|
|
303
|
+
}
|
|
304
|
+
.stat-detail-samples { font-size: 10px; color: var(--fg-dim); }
|
|
305
|
+
.stat-detail-close {
|
|
306
|
+
margin-left: auto;
|
|
307
|
+
background: transparent;
|
|
308
|
+
border: none;
|
|
309
|
+
color: var(--fg-dim);
|
|
310
|
+
cursor: pointer;
|
|
311
|
+
font-size: 16px;
|
|
312
|
+
line-height: 1;
|
|
313
|
+
padding: 0 4px;
|
|
314
|
+
}
|
|
315
|
+
.stat-detail-close:hover { color: var(--fg); }
|
|
316
|
+
.stat-detail-metrics {
|
|
317
|
+
display: grid;
|
|
318
|
+
grid-template-columns: repeat(6, 1fr);
|
|
319
|
+
gap: 4px;
|
|
320
|
+
margin-bottom: 6px;
|
|
321
|
+
}
|
|
322
|
+
.stat-metric {
|
|
323
|
+
display: flex;
|
|
324
|
+
flex-direction: column;
|
|
325
|
+
gap: 2px;
|
|
326
|
+
padding: 4px 6px;
|
|
327
|
+
background: var(--bg-elev-2);
|
|
328
|
+
border-radius: 2px;
|
|
329
|
+
}
|
|
330
|
+
.stat-metric-label {
|
|
331
|
+
font-size: 9px;
|
|
332
|
+
text-transform: uppercase;
|
|
333
|
+
color: var(--fg-dim);
|
|
334
|
+
letter-spacing: 0.06em;
|
|
335
|
+
}
|
|
336
|
+
.stat-metric-value {
|
|
337
|
+
font-size: 13px;
|
|
338
|
+
font-variant-numeric: tabular-nums;
|
|
339
|
+
}
|
|
340
|
+
.stat-histo {
|
|
341
|
+
display: block;
|
|
342
|
+
width: 100%;
|
|
343
|
+
height: 80px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.protocol-panel {
|
|
347
|
+
display: flex;
|
|
348
|
+
flex-direction: column;
|
|
349
|
+
min-height: 0;
|
|
350
|
+
/* Fill the column; scroll internally via .protocol-body so the
|
|
351
|
+
* log can't push the page taller as messages accumulate. */
|
|
352
|
+
flex: 1;
|
|
353
|
+
}
|
|
354
|
+
.protocol-header {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
gap: 8px;
|
|
358
|
+
text-transform: none;
|
|
359
|
+
letter-spacing: 0;
|
|
360
|
+
}
|
|
361
|
+
.protocol-header > span:first-child {
|
|
362
|
+
font-size: 11px;
|
|
363
|
+
text-transform: uppercase;
|
|
364
|
+
letter-spacing: 0.06em;
|
|
365
|
+
flex: 0 0 auto;
|
|
366
|
+
}
|
|
367
|
+
.protocol-filter {
|
|
368
|
+
flex: 1;
|
|
369
|
+
min-width: 0;
|
|
370
|
+
background: var(--bg);
|
|
371
|
+
color: var(--fg);
|
|
372
|
+
border: 1px solid var(--border);
|
|
373
|
+
border-radius: 3px;
|
|
374
|
+
padding: 3px 6px;
|
|
375
|
+
font: inherit;
|
|
376
|
+
}
|
|
377
|
+
.protocol-btn {
|
|
378
|
+
background: var(--bg);
|
|
379
|
+
color: var(--fg-dim);
|
|
380
|
+
border: 1px solid var(--border);
|
|
381
|
+
border-radius: 3px;
|
|
382
|
+
padding: 3px 8px;
|
|
383
|
+
cursor: pointer;
|
|
384
|
+
}
|
|
385
|
+
.protocol-btn:hover { color: var(--fg); }
|
|
386
|
+
.protocol-btn-on { color: var(--accent); border-color: rgba(94, 176, 255, 0.5); }
|
|
387
|
+
.protocol-btn[disabled] { opacity: 0.4; cursor: default; }
|
|
388
|
+
.protocol-match-count {
|
|
389
|
+
font-size: 10px;
|
|
390
|
+
color: var(--fg-dim);
|
|
391
|
+
font-variant-numeric: tabular-nums;
|
|
392
|
+
padding: 0 6px;
|
|
393
|
+
background: rgba(94, 176, 255, 0.1);
|
|
394
|
+
border: 1px solid rgba(94, 176, 255, 0.3);
|
|
395
|
+
border-radius: 3px;
|
|
396
|
+
line-height: 18px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.protocol-layout { flex: 1; display: flex; min-height: 0; }
|
|
400
|
+
.protocol-body {
|
|
401
|
+
flex: 1;
|
|
402
|
+
min-width: 0;
|
|
403
|
+
/* `min-height: 0` lets the flex item contract below its
|
|
404
|
+
* virtualization spacer and actually activate the scroll. */
|
|
405
|
+
min-height: 0;
|
|
406
|
+
overflow: auto;
|
|
407
|
+
padding: 0;
|
|
408
|
+
position: relative;
|
|
409
|
+
/* `auto` so scroll gestures chain to the page when the log is
|
|
410
|
+
* already at its top / bottom edge — matches how native mobile
|
|
411
|
+
* nested scroll containers behave. */
|
|
412
|
+
overscroll-behavior: auto;
|
|
413
|
+
scrollbar-width: thin;
|
|
414
|
+
scrollbar-color: var(--border) transparent;
|
|
415
|
+
}
|
|
416
|
+
.protocol-body::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
417
|
+
.protocol-body::-webkit-scrollbar-track { background: transparent; }
|
|
418
|
+
.protocol-body::-webkit-scrollbar-thumb {
|
|
419
|
+
background: rgba(255, 255, 255, 0.12);
|
|
420
|
+
border-radius: 4px;
|
|
421
|
+
}
|
|
422
|
+
.protocol-body::-webkit-scrollbar-thumb:hover {
|
|
423
|
+
background: rgba(255, 255, 255, 0.2);
|
|
424
|
+
}
|
|
425
|
+
.protocol-spacer { position: relative; width: 100%; }
|
|
426
|
+
.protocol-row {
|
|
427
|
+
position: absolute;
|
|
428
|
+
top: 0;
|
|
429
|
+
left: 0;
|
|
430
|
+
right: 0;
|
|
431
|
+
display: grid;
|
|
432
|
+
grid-template-columns: 14px 90px 60px 120px 1fr 56px;
|
|
433
|
+
gap: 8px;
|
|
434
|
+
padding: 0 10px;
|
|
435
|
+
background: transparent;
|
|
436
|
+
border: none;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
text-align: left;
|
|
439
|
+
font-size: 11px;
|
|
440
|
+
font-variant-numeric: tabular-nums;
|
|
441
|
+
align-items: center;
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
will-change: transform;
|
|
444
|
+
}
|
|
445
|
+
.protocol-frame { color: var(--fg-dim); font-size: 10px; }
|
|
446
|
+
.protocol-size { color: var(--fg-dim); text-align: right; }
|
|
447
|
+
.protocol-row-hover { background: rgba(255, 255, 255, 0.04); }
|
|
448
|
+
.protocol-row-selected {
|
|
449
|
+
background: rgba(94, 176, 255, 0.1) !important;
|
|
450
|
+
box-shadow: inset 2px 0 0 var(--accent);
|
|
451
|
+
}
|
|
452
|
+
.protocol-row-placeholder {
|
|
453
|
+
color: rgba(255, 255, 255, 0.2);
|
|
454
|
+
}
|
|
455
|
+
.protocol-dir { font-weight: 700; }
|
|
456
|
+
.protocol-row-in .protocol-dir { color: #5ed38a; }
|
|
457
|
+
.protocol-row-out .protocol-dir { color: #f0b429; }
|
|
458
|
+
.protocol-time { color: var(--fg-dim); }
|
|
459
|
+
.protocol-type { color: var(--fg); }
|
|
460
|
+
.protocol-tag {
|
|
461
|
+
color: var(--fg-dim);
|
|
462
|
+
overflow: hidden;
|
|
463
|
+
text-overflow: ellipsis;
|
|
464
|
+
white-space: nowrap;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.protocol-detail {
|
|
468
|
+
flex: 0 0 420px;
|
|
469
|
+
display: flex;
|
|
470
|
+
flex-direction: column;
|
|
471
|
+
border-left: 1px solid var(--border);
|
|
472
|
+
background: var(--bg);
|
|
473
|
+
min-width: 0;
|
|
474
|
+
}
|
|
475
|
+
.protocol-detail-header {
|
|
476
|
+
display: flex;
|
|
477
|
+
align-items: center;
|
|
478
|
+
justify-content: space-between;
|
|
479
|
+
gap: 8px;
|
|
480
|
+
padding: 6px 10px;
|
|
481
|
+
border-bottom: 1px solid var(--border);
|
|
482
|
+
}
|
|
483
|
+
.protocol-detail-title {
|
|
484
|
+
overflow: hidden;
|
|
485
|
+
text-overflow: ellipsis;
|
|
486
|
+
white-space: nowrap;
|
|
487
|
+
}
|
|
488
|
+
.protocol-detail-meta {
|
|
489
|
+
display: flex;
|
|
490
|
+
gap: 12px;
|
|
491
|
+
padding: 4px 10px;
|
|
492
|
+
font-size: 10px;
|
|
493
|
+
color: var(--fg-dim);
|
|
494
|
+
border-bottom: 1px solid var(--border);
|
|
495
|
+
}
|
|
496
|
+
.protocol-detail-body {
|
|
497
|
+
flex: 1;
|
|
498
|
+
margin: 0;
|
|
499
|
+
padding: 8px 10px;
|
|
500
|
+
overflow: auto;
|
|
501
|
+
font-size: 11px;
|
|
502
|
+
color: var(--fg-dim);
|
|
503
|
+
white-space: pre;
|
|
504
|
+
}
|
|
505
|
+
.json-key { color: #8ab4ff; }
|
|
506
|
+
.json-str { color: #c3e88d; }
|
|
507
|
+
.json-num { color: #f78c6c; }
|
|
508
|
+
.json-bool { color: #c792ea; }
|
|
509
|
+
.json-null { color: #89ddff; font-style: italic; }
|
|
510
|
+
|
|
511
|
+
.registry-panel {
|
|
512
|
+
display: flex;
|
|
513
|
+
flex-direction: column;
|
|
514
|
+
flex: 2;
|
|
515
|
+
min-height: 0;
|
|
516
|
+
}
|
|
517
|
+
.registry-header {
|
|
518
|
+
display: flex;
|
|
519
|
+
align-items: center;
|
|
520
|
+
gap: 8px;
|
|
521
|
+
text-transform: none;
|
|
522
|
+
letter-spacing: 0;
|
|
523
|
+
}
|
|
524
|
+
.registry-header > span:first-child {
|
|
525
|
+
font-size: 11px;
|
|
526
|
+
text-transform: uppercase;
|
|
527
|
+
letter-spacing: 0.06em;
|
|
528
|
+
}
|
|
529
|
+
.registry-count {
|
|
530
|
+
font-size: 10px;
|
|
531
|
+
color: var(--fg-dim);
|
|
532
|
+
font-variant-numeric: tabular-nums;
|
|
533
|
+
}
|
|
534
|
+
.registry-layout { flex: 1; display: flex; min-height: 0; min-width: 0; }
|
|
535
|
+
.registry-list {
|
|
536
|
+
list-style: none;
|
|
537
|
+
margin: 0;
|
|
538
|
+
padding: 4px 0;
|
|
539
|
+
overflow-y: auto;
|
|
540
|
+
flex: 0 0 260px;
|
|
541
|
+
border-right: 1px solid var(--border);
|
|
542
|
+
background: var(--bg-elev-2);
|
|
543
|
+
}
|
|
544
|
+
.registry-row {
|
|
545
|
+
display: grid;
|
|
546
|
+
grid-template-columns: auto 1fr auto auto;
|
|
547
|
+
gap: 8px;
|
|
548
|
+
align-items: center;
|
|
549
|
+
width: 100%;
|
|
550
|
+
padding: 4px 10px;
|
|
551
|
+
background: transparent;
|
|
552
|
+
border: none;
|
|
553
|
+
cursor: pointer;
|
|
554
|
+
text-align: left;
|
|
555
|
+
font-size: 11px;
|
|
556
|
+
color: var(--fg);
|
|
557
|
+
}
|
|
558
|
+
.registry-sparkline { image-rendering: pixelated; }
|
|
559
|
+
.registry-viz-wrap {
|
|
560
|
+
/* Wrapper supplies the horizontal breathing room; the canvas
|
|
561
|
+
* below expands to fill it. Previous version applied `margin: 0
|
|
562
|
+
* 10px` directly to the canvas alongside `width: 100%`, which
|
|
563
|
+
* added up to `100% + 20px` and pushed the graph past the panel
|
|
564
|
+
* border on every render. */
|
|
565
|
+
padding: 0 10px;
|
|
566
|
+
box-sizing: border-box;
|
|
567
|
+
width: 100%;
|
|
568
|
+
}
|
|
569
|
+
.registry-viz-stack {
|
|
570
|
+
position: relative;
|
|
571
|
+
width: 100%;
|
|
572
|
+
height: 80px;
|
|
573
|
+
}
|
|
574
|
+
.registry-viz {
|
|
575
|
+
display: block;
|
|
576
|
+
width: 100%;
|
|
577
|
+
height: 100%;
|
|
578
|
+
background: rgba(0, 2, 28, 0.35);
|
|
579
|
+
border-radius: 2px;
|
|
580
|
+
}
|
|
581
|
+
.registry-viz-stack { cursor: crosshair; }
|
|
582
|
+
.registry-viz-cursor-line {
|
|
583
|
+
position: absolute;
|
|
584
|
+
top: 0;
|
|
585
|
+
bottom: 0;
|
|
586
|
+
width: 1px;
|
|
587
|
+
background: rgba(255, 255, 255, 0.9);
|
|
588
|
+
pointer-events: none;
|
|
589
|
+
display: none;
|
|
590
|
+
/* Imperative positioning driven directly by mousemove — no React
|
|
591
|
+
* re-render per frame, just a style.left update. */
|
|
592
|
+
will-change: left;
|
|
593
|
+
}
|
|
594
|
+
.registry-row:hover { background: rgba(255, 255, 255, 0.03); }
|
|
595
|
+
.registry-row-selected {
|
|
596
|
+
background: rgba(94, 176, 255, 0.1);
|
|
597
|
+
box-shadow: inset 2px 0 0 var(--accent);
|
|
598
|
+
}
|
|
599
|
+
.registry-kind {
|
|
600
|
+
font-size: 9px;
|
|
601
|
+
text-transform: uppercase;
|
|
602
|
+
color: var(--fg-dim);
|
|
603
|
+
letter-spacing: 0.04em;
|
|
604
|
+
}
|
|
605
|
+
.registry-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
606
|
+
.registry-count-pill {
|
|
607
|
+
color: var(--fg-dim);
|
|
608
|
+
font-variant-numeric: tabular-nums;
|
|
609
|
+
font-size: 10px;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.registry-detail {
|
|
613
|
+
flex: 1;
|
|
614
|
+
min-width: 0;
|
|
615
|
+
display: flex;
|
|
616
|
+
flex-direction: column;
|
|
617
|
+
background: var(--bg);
|
|
618
|
+
}
|
|
619
|
+
.registry-detail-header {
|
|
620
|
+
display: flex;
|
|
621
|
+
gap: 8px;
|
|
622
|
+
padding: 6px 10px;
|
|
623
|
+
border-bottom: 1px solid var(--border);
|
|
624
|
+
align-items: baseline;
|
|
625
|
+
}
|
|
626
|
+
.registry-detail-name { font-weight: 600; }
|
|
627
|
+
.registry-detail-label { color: var(--fg-dim); font-size: 11px; }
|
|
628
|
+
.registry-detail-meta {
|
|
629
|
+
display: flex;
|
|
630
|
+
gap: 12px;
|
|
631
|
+
padding: 4px 10px;
|
|
632
|
+
font-size: 10px;
|
|
633
|
+
color: var(--fg-dim);
|
|
634
|
+
border-bottom: 1px solid var(--border);
|
|
635
|
+
}
|
|
636
|
+
/* Flex-column so the inner `.sample-grid-scroller` can `flex: 1` +
|
|
637
|
+
* overflow inside. Previously `overflow: auto` on this element
|
|
638
|
+
* itself made it the scroller, while the inner scroller had no
|
|
639
|
+
* flex context and sized to content — `scrollTo` on the inner was
|
|
640
|
+
* a no-op because the outer was what actually scrolled. */
|
|
641
|
+
.registry-detail-body {
|
|
642
|
+
flex: 1;
|
|
643
|
+
min-height: 0;
|
|
644
|
+
display: flex;
|
|
645
|
+
flex-direction: column;
|
|
646
|
+
padding: 6px 10px;
|
|
647
|
+
font-size: 11px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.sample-grid-scroller {
|
|
651
|
+
flex: 1;
|
|
652
|
+
min-height: 0;
|
|
653
|
+
overflow: auto;
|
|
654
|
+
}
|
|
655
|
+
.sample-grid {
|
|
656
|
+
position: relative;
|
|
657
|
+
/* Fixed-width decimal "hex-viewer"-style grid — monospace font,
|
|
658
|
+
* tabular figures, and each cell padded to the same char count by
|
|
659
|
+
* the formatter. Raw value lives in each cell's `title` so hover
|
|
660
|
+
* shows full precision. */
|
|
661
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
662
|
+
font-variant-numeric: tabular-nums;
|
|
663
|
+
white-space: pre;
|
|
664
|
+
}
|
|
665
|
+
.sample-row {
|
|
666
|
+
position: absolute;
|
|
667
|
+
left: 0;
|
|
668
|
+
right: 0;
|
|
669
|
+
display: grid;
|
|
670
|
+
/* 8-char cell + 1 char padding via ch units so columns line up
|
|
671
|
+
* regardless of zoom / font-size. */
|
|
672
|
+
grid-template-columns: 5ch repeat(8, 9ch);
|
|
673
|
+
gap: 6px;
|
|
674
|
+
align-items: center;
|
|
675
|
+
will-change: transform;
|
|
676
|
+
}
|
|
677
|
+
.sample-index { color: var(--fg-dim); font-size: 10px; text-align: right; padding-right: 4px; }
|
|
678
|
+
.sample-cell {
|
|
679
|
+
text-align: right;
|
|
680
|
+
color: var(--fg);
|
|
681
|
+
padding: 1px 4px;
|
|
682
|
+
border-radius: 2px;
|
|
683
|
+
cursor: pointer;
|
|
684
|
+
}
|
|
685
|
+
.sample-cell:hover { background: rgba(255, 255, 255, 0.04); }
|
|
686
|
+
.sample-cell-cursor {
|
|
687
|
+
background: rgba(255, 255, 255, 0.12);
|
|
688
|
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
|
|
689
|
+
}
|
|
690
|
+
.registry-cursor-label {
|
|
691
|
+
color: var(--fg);
|
|
692
|
+
background: rgba(255, 255, 255, 0.08);
|
|
693
|
+
border-radius: 2px;
|
|
694
|
+
padding: 0 4px;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.buffers-panel {
|
|
698
|
+
display: flex;
|
|
699
|
+
flex-direction: column;
|
|
700
|
+
/* Share the column with Registry — 3:2 split is a good default
|
|
701
|
+
* for the canvas/preview + entry list rhythm. Both need
|
|
702
|
+
* `min-height: 0` so their internal overflow containers can
|
|
703
|
+
* actually scroll instead of expanding the panel. */
|
|
704
|
+
flex: 3;
|
|
705
|
+
min-height: 0;
|
|
706
|
+
}
|
|
707
|
+
.buffers-header {
|
|
708
|
+
display: flex;
|
|
709
|
+
align-items: center;
|
|
710
|
+
gap: 8px;
|
|
711
|
+
text-transform: none;
|
|
712
|
+
letter-spacing: 0;
|
|
713
|
+
}
|
|
714
|
+
.buffers-header > span:first-child {
|
|
715
|
+
font-size: 11px;
|
|
716
|
+
text-transform: uppercase;
|
|
717
|
+
letter-spacing: 0.06em;
|
|
718
|
+
}
|
|
719
|
+
.buffers-layout { flex: 1; display: flex; min-height: 0; min-width: 0; }
|
|
720
|
+
.buffers-list {
|
|
721
|
+
list-style: none;
|
|
722
|
+
margin: 0;
|
|
723
|
+
padding: 4px 0;
|
|
724
|
+
overflow-y: auto;
|
|
725
|
+
flex: 0 0 260px;
|
|
726
|
+
border-right: 1px solid var(--border);
|
|
727
|
+
background: var(--bg-elev-2);
|
|
728
|
+
}
|
|
729
|
+
.buffers-row {
|
|
730
|
+
display: flex;
|
|
731
|
+
flex-direction: column;
|
|
732
|
+
gap: 2px;
|
|
733
|
+
width: 100%;
|
|
734
|
+
padding: 6px 10px;
|
|
735
|
+
background: transparent;
|
|
736
|
+
border: none;
|
|
737
|
+
cursor: pointer;
|
|
738
|
+
text-align: left;
|
|
739
|
+
font-size: 11px;
|
|
740
|
+
color: var(--fg);
|
|
741
|
+
}
|
|
742
|
+
.buffers-row:hover { background: rgba(255, 255, 255, 0.03); }
|
|
743
|
+
.buffers-row-selected {
|
|
744
|
+
background: rgba(94, 176, 255, 0.1);
|
|
745
|
+
box-shadow: inset 2px 0 0 var(--accent);
|
|
746
|
+
}
|
|
747
|
+
.buffers-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
748
|
+
.buffers-meta {
|
|
749
|
+
color: var(--fg-dim);
|
|
750
|
+
font-size: 10px;
|
|
751
|
+
font-variant-numeric: tabular-nums;
|
|
752
|
+
}
|
|
753
|
+
.buffers-stage {
|
|
754
|
+
flex: 1;
|
|
755
|
+
min-width: 0;
|
|
756
|
+
position: relative;
|
|
757
|
+
background: var(--bg);
|
|
758
|
+
display: flex;
|
|
759
|
+
align-items: center;
|
|
760
|
+
justify-content: center;
|
|
761
|
+
padding: 8px;
|
|
762
|
+
overflow: hidden;
|
|
763
|
+
}
|
|
764
|
+
.buffers-canvas {
|
|
765
|
+
image-rendering: pixelated;
|
|
766
|
+
display: block;
|
|
767
|
+
background: rgba(0, 2, 28, 0.6);
|
|
768
|
+
border-radius: 2px;
|
|
769
|
+
}
|
|
770
|
+
.buffers-info {
|
|
771
|
+
position: absolute;
|
|
772
|
+
bottom: 8px;
|
|
773
|
+
left: 12px;
|
|
774
|
+
right: 12px;
|
|
775
|
+
display: flex;
|
|
776
|
+
gap: 12px;
|
|
777
|
+
font-size: 10px;
|
|
778
|
+
color: var(--fg-dim);
|
|
779
|
+
background: rgba(0, 0, 0, 0.5);
|
|
780
|
+
border-radius: 3px;
|
|
781
|
+
padding: 3px 8px;
|
|
782
|
+
pointer-events: none;
|
|
783
|
+
}
|
|
784
|
+
.buffers-warn { color: #f0b429; }
|
|
785
|
+
.buffers-close {
|
|
786
|
+
position: absolute;
|
|
787
|
+
top: 6px;
|
|
788
|
+
right: 8px;
|
|
789
|
+
background: rgba(0, 0, 0, 0.5);
|
|
790
|
+
color: var(--fg-dim);
|
|
791
|
+
border: 1px solid var(--border);
|
|
792
|
+
border-radius: 3px;
|
|
793
|
+
cursor: pointer;
|
|
794
|
+
padding: 0 6px;
|
|
795
|
+
line-height: 18px;
|
|
796
|
+
font-size: 14px;
|
|
797
|
+
}
|
|
798
|
+
.buffers-close:hover { color: var(--fg); background: rgba(0, 0, 0, 0.7); }
|
|
799
|
+
|
|
800
|
+
.env-popover { position: relative; }
|
|
801
|
+
.env-trigger {
|
|
802
|
+
display: inline-flex;
|
|
803
|
+
align-items: center;
|
|
804
|
+
gap: 6px;
|
|
805
|
+
padding: 4px 10px;
|
|
806
|
+
background: var(--bg);
|
|
807
|
+
color: var(--fg);
|
|
808
|
+
border: 1px solid var(--border);
|
|
809
|
+
border-radius: 3px;
|
|
810
|
+
cursor: pointer;
|
|
811
|
+
font-size: 11px;
|
|
812
|
+
}
|
|
813
|
+
.env-trigger-open { border-color: var(--accent); }
|
|
814
|
+
.env-trigger-summary { color: var(--fg-dim); }
|
|
815
|
+
.env-caret { color: var(--fg-dim); font-size: 8px; }
|
|
816
|
+
.env-menu {
|
|
817
|
+
position: absolute;
|
|
818
|
+
top: calc(100% + 6px);
|
|
819
|
+
right: 0;
|
|
820
|
+
min-width: 280px;
|
|
821
|
+
padding: 8px 12px;
|
|
822
|
+
background: var(--bg-elev-2);
|
|
823
|
+
border: 1px solid var(--border);
|
|
824
|
+
border-radius: 3px;
|
|
825
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
826
|
+
z-index: 100;
|
|
827
|
+
}
|
|
828
|
+
.env-row {
|
|
829
|
+
display: flex;
|
|
830
|
+
justify-content: space-between;
|
|
831
|
+
gap: 16px;
|
|
832
|
+
padding: 2px 0;
|
|
833
|
+
font-size: 11px;
|
|
834
|
+
}
|
|
835
|
+
.env-label { color: var(--fg-dim); }
|
|
836
|
+
.env-value { font-variant-numeric: tabular-nums; color: var(--fg); }
|
|
837
|
+
|
|
838
|
+
.buffers-canvas {
|
|
839
|
+
transform-origin: center center;
|
|
840
|
+
will-change: transform;
|
|
841
|
+
}
|
|
842
|
+
.buffers-zoom { margin-left: auto; color: var(--fg-dim); }
|
|
843
|
+
.buffers-probe {
|
|
844
|
+
position: absolute;
|
|
845
|
+
top: 6px;
|
|
846
|
+
left: 8px;
|
|
847
|
+
display: inline-flex;
|
|
848
|
+
align-items: center;
|
|
849
|
+
gap: 8px;
|
|
850
|
+
padding: 3px 8px;
|
|
851
|
+
background: rgba(0, 0, 0, 0.6);
|
|
852
|
+
border: 1px solid var(--border);
|
|
853
|
+
border-radius: 3px;
|
|
854
|
+
color: var(--fg);
|
|
855
|
+
font-size: 10px;
|
|
856
|
+
font-variant-numeric: tabular-nums;
|
|
857
|
+
pointer-events: none;
|
|
858
|
+
}
|
|
859
|
+
.probe-chip {
|
|
860
|
+
width: 10px;
|
|
861
|
+
height: 10px;
|
|
862
|
+
border-radius: 2px;
|
|
863
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
864
|
+
}
|
|
865
|
+
.buffers-reset {
|
|
866
|
+
position: absolute;
|
|
867
|
+
top: 6px;
|
|
868
|
+
right: 38px;
|
|
869
|
+
background: rgba(0, 0, 0, 0.5);
|
|
870
|
+
color: var(--fg-dim);
|
|
871
|
+
border: 1px solid var(--border);
|
|
872
|
+
border-radius: 3px;
|
|
873
|
+
cursor: pointer;
|
|
874
|
+
padding: 0 6px;
|
|
875
|
+
line-height: 18px;
|
|
876
|
+
font-size: 14px;
|
|
877
|
+
}
|
|
878
|
+
.buffers-reset:hover { color: var(--fg); background: rgba(0, 0, 0, 0.7); }
|
|
879
|
+
.buffers-stage { user-select: none; }
|
|
880
|
+
|
|
881
|
+
.protocol-dir-toggle { display: inline-flex; gap: 2px; }
|
|
882
|
+
.protocol-filter-menu-wrap { position: relative; }
|
|
883
|
+
.protocol-filter-menu {
|
|
884
|
+
position: absolute;
|
|
885
|
+
top: calc(100% + 6px);
|
|
886
|
+
right: 0;
|
|
887
|
+
min-width: 260px;
|
|
888
|
+
padding: 8px;
|
|
889
|
+
background: var(--bg-elev-2);
|
|
890
|
+
border: 1px solid var(--border);
|
|
891
|
+
border-radius: 3px;
|
|
892
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
893
|
+
z-index: 100;
|
|
894
|
+
}
|
|
895
|
+
.protocol-filter-menu-section { margin-bottom: 10px; }
|
|
896
|
+
.protocol-filter-menu-section:last-child { margin-bottom: 0; }
|
|
897
|
+
.protocol-filter-menu-title {
|
|
898
|
+
display: flex;
|
|
899
|
+
justify-content: space-between;
|
|
900
|
+
align-items: center;
|
|
901
|
+
font-size: 10px;
|
|
902
|
+
text-transform: uppercase;
|
|
903
|
+
color: var(--fg-dim);
|
|
904
|
+
letter-spacing: 0.06em;
|
|
905
|
+
margin-bottom: 4px;
|
|
906
|
+
}
|
|
907
|
+
.protocol-filter-menu-clear {
|
|
908
|
+
padding: 0 6px;
|
|
909
|
+
font-size: 10px;
|
|
910
|
+
}
|
|
911
|
+
.protocol-filter-menu-value {
|
|
912
|
+
display: inline-block;
|
|
913
|
+
margin-left: 8px;
|
|
914
|
+
font-size: 11px;
|
|
915
|
+
color: var(--fg-dim);
|
|
916
|
+
font-variant-numeric: tabular-nums;
|
|
917
|
+
}
|
|
918
|
+
.protocol-filter-menu input[type="range"] {
|
|
919
|
+
vertical-align: middle;
|
|
920
|
+
width: 160px;
|
|
921
|
+
}
|
|
922
|
+
.protocol-filter-types {
|
|
923
|
+
list-style: none;
|
|
924
|
+
margin: 0;
|
|
925
|
+
padding: 0;
|
|
926
|
+
max-height: 240px;
|
|
927
|
+
overflow-y: auto;
|
|
928
|
+
}
|
|
929
|
+
.protocol-filter-type {
|
|
930
|
+
display: flex;
|
|
931
|
+
align-items: center;
|
|
932
|
+
gap: 6px;
|
|
933
|
+
padding: 2px 4px;
|
|
934
|
+
font-size: 11px;
|
|
935
|
+
cursor: pointer;
|
|
936
|
+
}
|
|
937
|
+
.protocol-filter-type:hover { background: rgba(255, 255, 255, 0.03); }
|
|
938
|
+
.protocol-filter-type input { margin: 0; }
|
|
939
|
+
|
|
940
|
+
/*
|
|
941
|
+
* Narrow viewports (<= 1150px): give up the viewport-bound layout.
|
|
942
|
+
* Stacking four panels in one column squeezes everything below
|
|
943
|
+
* the usable threshold — canvas previews, registry graphs, and
|
|
944
|
+
* protocol rows all need real height. Let the page scroll with
|
|
945
|
+
* per-panel fixed heights so cascading doesn't un-do them.
|
|
946
|
+
*
|
|
947
|
+
* NOTE: This block sits at the very end of the stylesheet on
|
|
948
|
+
* purpose — the cascade goes top-down at equal specificity, so
|
|
949
|
+
* placing panel height overrides above the default `.buffers-panel
|
|
950
|
+
* { flex: 3 }` / `.registry-panel { flex: 2 }` rules means those
|
|
951
|
+
* rules win and the narrow-mode sizes get discarded. Keep it last.
|
|
952
|
+
*/
|
|
953
|
+
/* ── Batches panel ──────────────────────────────────────────────── */
|
|
954
|
+
.batches-panel {
|
|
955
|
+
display: flex;
|
|
956
|
+
flex-direction: column;
|
|
957
|
+
flex: 2;
|
|
958
|
+
min-height: 0;
|
|
959
|
+
overflow: hidden;
|
|
960
|
+
}
|
|
961
|
+
.batches-header {
|
|
962
|
+
display: flex;
|
|
963
|
+
align-items: baseline;
|
|
964
|
+
justify-content: space-between;
|
|
965
|
+
gap: 8px;
|
|
966
|
+
text-transform: none;
|
|
967
|
+
letter-spacing: 0;
|
|
968
|
+
}
|
|
969
|
+
.batches-header > span:first-child {
|
|
970
|
+
font-size: 11px;
|
|
971
|
+
text-transform: uppercase;
|
|
972
|
+
letter-spacing: 0.06em;
|
|
973
|
+
}
|
|
974
|
+
.batches-header-meta {
|
|
975
|
+
font-size: 10px;
|
|
976
|
+
color: var(--fg-dim);
|
|
977
|
+
font-variant-numeric: tabular-nums;
|
|
978
|
+
}
|
|
979
|
+
/* Single scroll container for both sections so the panel scrolls
|
|
980
|
+
* naturally inside `.dashboard-split-col` without nested scrollers. */
|
|
981
|
+
.batches-scroll { flex: 1; overflow-y: auto; min-height: 0; }
|
|
982
|
+
.batches-section { border-top: 1px solid var(--border); }
|
|
983
|
+
.batches-section:first-of-type { border-top: none; }
|
|
984
|
+
.batches-section-title {
|
|
985
|
+
display: flex;
|
|
986
|
+
align-items: center;
|
|
987
|
+
gap: 6px;
|
|
988
|
+
width: 100%;
|
|
989
|
+
padding: 4px 10px;
|
|
990
|
+
font-size: 10px;
|
|
991
|
+
text-transform: uppercase;
|
|
992
|
+
letter-spacing: 0.06em;
|
|
993
|
+
color: var(--fg-dim);
|
|
994
|
+
/* Solid fill — sticky headers mask scrolling content beneath.
|
|
995
|
+
* Layering the subtle highlight over the panel base color keeps
|
|
996
|
+
* the existing shade while blocking the rows behind. */
|
|
997
|
+
background: var(--bg-elev-2);
|
|
998
|
+
background-image: linear-gradient(rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.02));
|
|
999
|
+
position: sticky;
|
|
1000
|
+
top: 0;
|
|
1001
|
+
z-index: 2;
|
|
1002
|
+
border-bottom: 1px solid var(--border);
|
|
1003
|
+
}
|
|
1004
|
+
.batches-section-count {
|
|
1005
|
+
margin-left: auto;
|
|
1006
|
+
color: var(--fg-dim);
|
|
1007
|
+
font-variant-numeric: tabular-nums;
|
|
1008
|
+
text-transform: none;
|
|
1009
|
+
letter-spacing: 0;
|
|
1010
|
+
}
|
|
1011
|
+
/* Per-pass collapse caret in the tree. Styled as a tiny inline
|
|
1012
|
+
* button; `--leaf` variant is an invisible placeholder so leaf
|
|
1013
|
+
* and parent rows share the same label x-offset. */
|
|
1014
|
+
.batches-tree-caret {
|
|
1015
|
+
display: inline-flex;
|
|
1016
|
+
align-items: center;
|
|
1017
|
+
justify-content: center;
|
|
1018
|
+
width: 12px;
|
|
1019
|
+
height: 12px;
|
|
1020
|
+
margin-right: 2px;
|
|
1021
|
+
padding: 0;
|
|
1022
|
+
border: 0;
|
|
1023
|
+
background: transparent;
|
|
1024
|
+
box-shadow: none;
|
|
1025
|
+
-webkit-appearance: none;
|
|
1026
|
+
appearance: none;
|
|
1027
|
+
color: var(--fg-dim);
|
|
1028
|
+
font: inherit;
|
|
1029
|
+
font-size: 9px;
|
|
1030
|
+
line-height: 1;
|
|
1031
|
+
cursor: pointer;
|
|
1032
|
+
flex-shrink: 0;
|
|
1033
|
+
}
|
|
1034
|
+
.batches-tree-caret:hover { color: var(--fg); }
|
|
1035
|
+
.batches-tree-caret:focus { outline: none; }
|
|
1036
|
+
.batches-tree-caret:focus-visible { outline: 1px dotted var(--fg-dim); outline-offset: 1px; }
|
|
1037
|
+
.batches-tree-caret--leaf { cursor: default; visibility: hidden; }
|
|
1038
|
+
.batches-pass-label {
|
|
1039
|
+
display: flex;
|
|
1040
|
+
align-items: center;
|
|
1041
|
+
gap: 2px;
|
|
1042
|
+
min-width: 0;
|
|
1043
|
+
}
|
|
1044
|
+
.batches-pass-label > span:last-child {
|
|
1045
|
+
overflow: hidden;
|
|
1046
|
+
text-overflow: ellipsis;
|
|
1047
|
+
white-space: nowrap;
|
|
1048
|
+
}
|
|
1049
|
+
.batches-pass-row--parent { font-weight: 500; }
|
|
1050
|
+
.batches-table-head,
|
|
1051
|
+
.batches-pass-row,
|
|
1052
|
+
.batches-total-row,
|
|
1053
|
+
.batches-run-head,
|
|
1054
|
+
.batches-batch-row {
|
|
1055
|
+
display: grid;
|
|
1056
|
+
gap: 8px;
|
|
1057
|
+
padding: 3px 10px;
|
|
1058
|
+
font-size: 11px;
|
|
1059
|
+
font-variant-numeric: tabular-nums;
|
|
1060
|
+
align-items: center;
|
|
1061
|
+
}
|
|
1062
|
+
/* Passes section: label | calls | tris | ms (4 columns). */
|
|
1063
|
+
.batches-table-head--passes,
|
|
1064
|
+
.batches-pass-row,
|
|
1065
|
+
.batches-total-row {
|
|
1066
|
+
grid-template-columns: 1fr 60px 70px 60px;
|
|
1067
|
+
}
|
|
1068
|
+
/* Runs section: label | layer | sprites (3 columns). Header + run
|
|
1069
|
+
* rows + per-batch rows all use the same template so labels line up. */
|
|
1070
|
+
.batches-table-head--runs,
|
|
1071
|
+
.batches-run-head,
|
|
1072
|
+
.batches-batch-row {
|
|
1073
|
+
grid-template-columns: 1fr 60px 70px;
|
|
1074
|
+
}
|
|
1075
|
+
.batches-batch-row {
|
|
1076
|
+
padding-left: 22px;
|
|
1077
|
+
}
|
|
1078
|
+
.batches-table-head {
|
|
1079
|
+
color: var(--fg-dim);
|
|
1080
|
+
font-size: 10px;
|
|
1081
|
+
text-transform: uppercase;
|
|
1082
|
+
letter-spacing: 0.04em;
|
|
1083
|
+
border-bottom: 1px solid var(--border);
|
|
1084
|
+
padding-top: 4px;
|
|
1085
|
+
padding-bottom: 4px;
|
|
1086
|
+
}
|
|
1087
|
+
.batches-col-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1088
|
+
.batches-col-num { text-align: right; color: var(--fg); }
|
|
1089
|
+
.batches-table-head .batches-col-num { color: var(--fg-dim); }
|
|
1090
|
+
.batches-pass-list,
|
|
1091
|
+
.batches-run-list { list-style: none; margin: 0; padding: 0; }
|
|
1092
|
+
.batches-pass-row:nth-child(even) { background: rgba(255, 255, 255, 0.02); }
|
|
1093
|
+
.batches-total-row {
|
|
1094
|
+
border-top: 1px solid var(--border);
|
|
1095
|
+
background: rgba(255, 255, 255, 0.03);
|
|
1096
|
+
color: var(--fg);
|
|
1097
|
+
font-weight: 600;
|
|
1098
|
+
}
|
|
1099
|
+
.batches-run { border-bottom: 1px dotted rgba(255, 255, 255, 0.06); }
|
|
1100
|
+
.batches-run-label { display: flex; gap: 6px; align-items: baseline; }
|
|
1101
|
+
.batches-kind {
|
|
1102
|
+
display: inline-block;
|
|
1103
|
+
padding: 0 5px;
|
|
1104
|
+
margin-right: 4px;
|
|
1105
|
+
border-radius: 3px;
|
|
1106
|
+
font-size: 9px;
|
|
1107
|
+
text-transform: uppercase;
|
|
1108
|
+
letter-spacing: 0.04em;
|
|
1109
|
+
line-height: 14px;
|
|
1110
|
+
color: var(--fg-dim);
|
|
1111
|
+
background: rgba(255, 255, 255, 0.06);
|
|
1112
|
+
border: 1px solid var(--border);
|
|
1113
|
+
flex-shrink: 0;
|
|
1114
|
+
}
|
|
1115
|
+
.batches-kind--sprite { color: #47cc6a; border-color: rgba(71, 204, 106, 0.35); }
|
|
1116
|
+
.batches-kind--tilechunk { color: #9d7aff; border-color: rgba(157, 122, 255, 0.35); }
|
|
1117
|
+
.batches-kind--mesh { color: #ffa347; border-color: rgba(255, 163, 71, 0.35); }
|
|
1118
|
+
.batches-mat { color: var(--fg); }
|
|
1119
|
+
.batches-run-sub {
|
|
1120
|
+
font-size: 10px;
|
|
1121
|
+
color: var(--fg-dim);
|
|
1122
|
+
font-variant-numeric: tabular-nums;
|
|
1123
|
+
}
|
|
1124
|
+
.batches-run-children { list-style: none; margin: 0; padding: 0; }
|
|
1125
|
+
.batches-batch-label { color: var(--fg-dim); }
|
|
1126
|
+
.batches-empty { padding: 8px 10px; color: var(--fg-dim); font-size: 11px; }
|
|
1127
|
+
|
|
1128
|
+
@media (max-width: 1150px) {
|
|
1129
|
+
html, body { overflow: auto; }
|
|
1130
|
+
.dashboard-root {
|
|
1131
|
+
height: auto;
|
|
1132
|
+
min-height: 100vh;
|
|
1133
|
+
min-height: 100dvh;
|
|
1134
|
+
}
|
|
1135
|
+
.dashboard-content { overflow: visible; min-height: 0; }
|
|
1136
|
+
.dashboard-split { flex-direction: column; flex: initial; }
|
|
1137
|
+
.dashboard-split-col { flex: initial; }
|
|
1138
|
+
.buffers-panel { flex: initial; height: 340px; }
|
|
1139
|
+
.batches-panel { flex: initial; height: 360px; }
|
|
1140
|
+
.registry-panel { flex: initial; height: 300px; }
|
|
1141
|
+
.protocol-panel { flex: initial; height: 420px; }
|
|
1142
|
+
}
|
|
1143
|
+
</style>
|
|
1144
|
+
</head>
|
|
1145
|
+
<body>
|
|
1146
|
+
<div id="root"></div>
|
|
1147
|
+
<script type="module" src="./index.tsx"></script>
|
|
1148
|
+
</body>
|
|
1149
|
+
</html>
|