@three-flatland/devtools 0.1.0-alpha.3 → 1.0.0-alpha.6
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 +1 -1
- package/dist/buffers-modal.d.ts +16 -52
- package/dist/buffers-modal.d.ts.map +1 -0
- package/dist/buffers-modal.js +459 -464
- package/dist/buffers-modal.js.map +1 -1
- package/dist/buffers-view.d.ts +22 -34
- package/dist/buffers-view.d.ts.map +1 -0
- package/dist/buffers-view.js +252 -240
- package/dist/buffers-view.js.map +1 -1
- package/dist/create-pane.d.ts +38 -37
- package/dist/create-pane.d.ts.map +1 -0
- package/dist/create-pane.js +197 -166
- package/dist/create-pane.js.map +1 -1
- package/dist/dashboard/app.tsx +2 -0
- package/dist/dashboard/export.ts +1 -3
- package/dist/dashboard/frame-cursor.ts +81 -0
- package/dist/dashboard/hooks.ts +10 -2
- package/dist/dashboard/index.html +61 -0
- package/dist/dashboard/panels/batches.tsx +5 -18
- package/dist/dashboard/panels/buffers.tsx +52 -26
- package/dist/dashboard/panels/env.tsx +5 -6
- package/dist/dashboard/panels/protocol-log.tsx +146 -58
- package/dist/dashboard/panels/registry.tsx +55 -69
- package/dist/dashboard/panels/scrubber.tsx +100 -0
- package/dist/dashboard/panels/stats.tsx +60 -32
- package/dist/dashboard/protocol-store.ts +25 -8
- package/dist/dashboard/vendor/hooks.module.d.ts +146 -0
- package/dist/dashboard/vendor/jsx-runtime.d.ts +55 -0
- package/dist/dashboard/vendor/jsx.d.ts +3123 -0
- package/dist/dashboard/vendor/preact.module.d.ts +397 -0
- package/dist/devtools-client.d.ts +312 -300
- package/dist/devtools-client.d.ts.map +1 -0
- package/dist/devtools-client.js +866 -838
- package/dist/devtools-client.js.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.js +5 -12
- package/dist/perf-trace.d.ts +6 -4
- package/dist/perf-trace.d.ts.map +1 -0
- package/dist/perf-trace.js +80 -66
- package/dist/perf-trace.js.map +1 -1
- package/dist/plugins.d.ts +6 -6
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +5 -5
- package/dist/plugins.js.map +1 -1
- package/dist/provider-switcher.d.ts +8 -15
- package/dist/provider-switcher.d.ts.map +1 -0
- package/dist/provider-switcher.js +68 -68
- package/dist/provider-switcher.js.map +1 -1
- package/dist/react/devtools-provider.d.ts +11 -10
- package/dist/react/devtools-provider.d.ts.map +1 -0
- package/dist/react/devtools-provider.js +60 -32
- package/dist/react/devtools-provider.js.map +1 -1
- package/dist/react/use-fps-graph.d.ts +7 -7
- package/dist/react/use-fps-graph.d.ts.map +1 -0
- package/dist/react/use-fps-graph.js +27 -21
- package/dist/react/use-fps-graph.js.map +1 -1
- package/dist/react/use-pane-button.d.ts +4 -4
- package/dist/react/use-pane-button.d.ts.map +1 -0
- package/dist/react/use-pane-button.js +35 -30
- package/dist/react/use-pane-button.js.map +1 -1
- package/dist/react/use-pane-folder.d.ts +5 -4
- package/dist/react/use-pane-folder.d.ts.map +1 -0
- package/dist/react/use-pane-folder.js +33 -20
- package/dist/react/use-pane-folder.js.map +1 -1
- package/dist/react/use-pane-input.d.ts +31 -30
- package/dist/react/use-pane-input.d.ts.map +1 -0
- package/dist/react/use-pane-input.js +46 -42
- package/dist/react/use-pane-input.js.map +1 -1
- package/dist/react/use-pane-radio-grid.d.ts +20 -20
- package/dist/react/use-pane-radio-grid.d.ts.map +1 -0
- package/dist/react/use-pane-radio-grid.js +72 -50
- package/dist/react/use-pane-radio-grid.js.map +1 -1
- package/dist/react/use-pane.d.ts +4 -4
- package/dist/react/use-pane.d.ts.map +1 -0
- package/dist/react/use-pane.js +52 -17
- package/dist/react/use-pane.js.map +1 -1
- package/dist/react.d.ts +10 -12
- package/dist/react.js +2 -12
- package/dist/registry-view.d.ts +8 -21
- package/dist/registry-view.d.ts.map +1 -0
- package/dist/registry-view.js +357 -338
- package/dist/registry-view.js.map +1 -1
- package/dist/relay.d.ts +16 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +189 -0
- package/dist/relay.js.map +1 -0
- package/dist/remote.d.ts +10 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +28 -0
- package/dist/remote.js.map +1 -0
- package/dist/stats-graph.d.ts +23 -31
- package/dist/stats-graph.d.ts.map +1 -0
- package/dist/stats-graph.js +323 -264
- package/dist/stats-graph.js.map +1 -1
- package/dist/stats-row.d.ts +8 -21
- package/dist/stats-row.d.ts.map +1 -0
- package/dist/stats-row.js +99 -101
- package/dist/stats-row.js.map +1 -1
- package/dist/theme.d.ts +3 -1
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +43 -54
- package/dist/theme.js.map +1 -1
- package/dist/vite-plugin.d.ts +7 -6
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +87 -78
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +33 -29
- package/dist/buffers-modal.cjs +0 -491
- package/dist/buffers-modal.cjs.map +0 -1
- package/dist/buffers-modal.d.cts +0 -62
- package/dist/buffers-view.cjs +0 -269
- package/dist/buffers-view.cjs.map +0 -1
- package/dist/buffers-view.d.cts +0 -39
- package/dist/create-pane.cjs +0 -203
- package/dist/create-pane.cjs.map +0 -1
- package/dist/create-pane.d.cts +0 -66
- package/dist/devtools-client.cjs +0 -870
- package/dist/devtools-client.cjs.map +0 -1
- package/dist/devtools-client.d.cts +0 -362
- package/dist/index.cjs +0 -42
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -8
- package/dist/index.js.map +0 -1
- package/dist/perf-trace.cjs +0 -103
- package/dist/perf-trace.cjs.map +0 -1
- package/dist/perf-trace.d.cts +0 -27
- package/dist/plugins.cjs +0 -44
- package/dist/plugins.cjs.map +0 -1
- package/dist/plugins.d.cts +0 -7
- package/dist/provider-switcher.cjs +0 -95
- package/dist/provider-switcher.cjs.map +0 -1
- package/dist/provider-switcher.d.cts +0 -18
- package/dist/react/devtools-provider.cjs +0 -59
- package/dist/react/devtools-provider.cjs.map +0 -1
- package/dist/react/devtools-provider.d.cts +0 -39
- package/dist/react/use-fps-graph.cjs +0 -49
- package/dist/react/use-fps-graph.cjs.map +0 -1
- package/dist/react/use-fps-graph.d.cts +0 -15
- package/dist/react/use-pane-button.cjs +0 -58
- package/dist/react/use-pane-button.cjs.map +0 -1
- package/dist/react/use-pane-button.d.cts +0 -10
- package/dist/react/use-pane-folder.cjs +0 -48
- package/dist/react/use-pane-folder.cjs.map +0 -1
- package/dist/react/use-pane-folder.d.cts +0 -15
- package/dist/react/use-pane-input.cjs +0 -70
- package/dist/react/use-pane-input.cjs.map +0 -1
- package/dist/react/use-pane-input.d.cts +0 -44
- package/dist/react/use-pane-radio-grid.cjs +0 -78
- package/dist/react/use-pane-radio-grid.cjs.map +0 -1
- package/dist/react/use-pane-radio-grid.d.cts +0 -35
- package/dist/react/use-pane.cjs +0 -45
- package/dist/react/use-pane.cjs.map +0 -1
- package/dist/react/use-pane.d.cts +0 -40
- package/dist/react.cjs +0 -50
- package/dist/react.cjs.map +0 -1
- package/dist/react.d.cts +0 -12
- package/dist/react.js.map +0 -1
- package/dist/registry-view.cjs +0 -371
- package/dist/registry-view.cjs.map +0 -1
- package/dist/registry-view.d.cts +0 -24
- package/dist/stats-graph.cjs +0 -297
- package/dist/stats-graph.cjs.map +0 -1
- package/dist/stats-graph.d.cts +0 -40
- package/dist/stats-row.cjs +0 -153
- package/dist/stats-row.cjs.map +0 -1
- package/dist/stats-row.d.cts +0 -24
- package/dist/theme.cjs +0 -260
- package/dist/theme.cjs.map +0 -1
- package/dist/theme.d.cts +0 -10
- package/dist/vite-plugin.cjs +0 -113
- package/dist/vite-plugin.cjs.map +0 -1
- package/dist/vite-plugin.d.cts +0 -9
package/dist/buffers-modal.cjs
DELETED
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var buffers_modal_exports = {};
|
|
20
|
-
__export(buffers_modal_exports, {
|
|
21
|
-
createBuffersModal: () => createBuffersModal
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(buffers_modal_exports);
|
|
24
|
-
function createBuffersModal(client, options = {}) {
|
|
25
|
-
const root = document.createElement("div");
|
|
26
|
-
root.style.cssText = [
|
|
27
|
-
"position:fixed",
|
|
28
|
-
"inset:0",
|
|
29
|
-
"z-index:99999",
|
|
30
|
-
"background:rgba(0,2,28,0.92)",
|
|
31
|
-
"color:var(--tp-label-foreground-color, #f0edd8)",
|
|
32
|
-
"font-family:var(--tp-base-font-family, ui-monospace, monospace)",
|
|
33
|
-
"font-size:12px",
|
|
34
|
-
"display:none",
|
|
35
|
-
"flex-direction:column"
|
|
36
|
-
].join(";");
|
|
37
|
-
const header = document.createElement("div");
|
|
38
|
-
header.style.cssText = [
|
|
39
|
-
"display:flex",
|
|
40
|
-
"align-items:center",
|
|
41
|
-
"gap:12px",
|
|
42
|
-
"padding:8px 12px",
|
|
43
|
-
"border-bottom:1px solid rgba(240,237,216,0.08)",
|
|
44
|
-
"flex:0 0 auto"
|
|
45
|
-
].join(";");
|
|
46
|
-
const sidebarToggle = document.createElement("span");
|
|
47
|
-
sidebarToggle.textContent = "\u25C0";
|
|
48
|
-
sidebarToggle.style.cssText = "cursor:pointer;padding:2px 6px;opacity:0.8;user-select:none";
|
|
49
|
-
sidebarToggle.title = "Toggle sidebar";
|
|
50
|
-
const titleEl = document.createElement("span");
|
|
51
|
-
titleEl.style.cssText = "flex:1;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
52
|
-
const dimsEl = document.createElement("span");
|
|
53
|
-
dimsEl.style.cssText = "opacity:0.7;font-variant-numeric:tabular-nums";
|
|
54
|
-
const closeBtn = document.createElement("span");
|
|
55
|
-
closeBtn.textContent = "\xD7";
|
|
56
|
-
closeBtn.style.cssText = "cursor:pointer;padding:2px 10px;font-size:18px;line-height:1;opacity:0.8;user-select:none";
|
|
57
|
-
closeBtn.title = "Close";
|
|
58
|
-
header.appendChild(sidebarToggle);
|
|
59
|
-
header.appendChild(titleEl);
|
|
60
|
-
header.appendChild(dimsEl);
|
|
61
|
-
header.appendChild(closeBtn);
|
|
62
|
-
const body = document.createElement("div");
|
|
63
|
-
body.style.cssText = "display:flex;flex:1;min-height:0";
|
|
64
|
-
const sidebar = document.createElement("div");
|
|
65
|
-
sidebar.style.cssText = [
|
|
66
|
-
"display:flex",
|
|
67
|
-
"flex-direction:column",
|
|
68
|
-
"width:240px",
|
|
69
|
-
"border-right:1px solid rgba(240,237,216,0.08)",
|
|
70
|
-
"overflow-y:auto",
|
|
71
|
-
"padding:8px 0",
|
|
72
|
-
"flex:0 0 auto"
|
|
73
|
-
].join(";");
|
|
74
|
-
const main = document.createElement("div");
|
|
75
|
-
main.style.cssText = "flex:1;display:flex;align-items:center;justify-content:center;padding:16px;min-width:0;min-height:0;overflow:hidden;position:relative";
|
|
76
|
-
const canvas = document.createElement("canvas");
|
|
77
|
-
canvas.style.cssText = "display:block;background:rgba(0,2,28,0.6);image-rendering:pixelated;max-width:100%;max-height:100%";
|
|
78
|
-
const ctx = canvas.getContext("2d");
|
|
79
|
-
const offscreen = document.createElement("canvas");
|
|
80
|
-
offscreen.width = 1;
|
|
81
|
-
offscreen.height = 1;
|
|
82
|
-
const zoomBar = document.createElement("div");
|
|
83
|
-
zoomBar.style.cssText = [
|
|
84
|
-
"position:absolute",
|
|
85
|
-
"top:8px",
|
|
86
|
-
"left:8px",
|
|
87
|
-
"display:flex",
|
|
88
|
-
"gap:6px",
|
|
89
|
-
"align-items:center",
|
|
90
|
-
"z-index:1"
|
|
91
|
-
].join(";");
|
|
92
|
-
const zoomInfo = document.createElement("span");
|
|
93
|
-
zoomInfo.style.cssText = [
|
|
94
|
-
"padding:3px 8px",
|
|
95
|
-
"border-radius:4px",
|
|
96
|
-
"background:rgba(0,0,0,0.6)",
|
|
97
|
-
"color:#aaa",
|
|
98
|
-
"font:11px/1.4 monospace",
|
|
99
|
-
"pointer-events:none",
|
|
100
|
-
"user-select:none"
|
|
101
|
-
].join(";");
|
|
102
|
-
zoomInfo.textContent = "1.0\xD7";
|
|
103
|
-
const resetBtn = document.createElement("span");
|
|
104
|
-
resetBtn.style.cssText = [
|
|
105
|
-
"padding:3px 8px",
|
|
106
|
-
"border-radius:4px",
|
|
107
|
-
"background:rgba(0,0,0,0.6)",
|
|
108
|
-
"color:#aaa",
|
|
109
|
-
"font:11px/1.4 monospace",
|
|
110
|
-
"cursor:pointer",
|
|
111
|
-
"user-select:none"
|
|
112
|
-
].join(";");
|
|
113
|
-
resetBtn.textContent = "\u27F2 Reset";
|
|
114
|
-
resetBtn.title = "Reset zoom & pan (double-click also resets)";
|
|
115
|
-
zoomBar.appendChild(zoomInfo);
|
|
116
|
-
zoomBar.appendChild(resetBtn);
|
|
117
|
-
main.appendChild(canvas);
|
|
118
|
-
main.appendChild(zoomBar);
|
|
119
|
-
body.appendChild(sidebar);
|
|
120
|
-
body.appendChild(main);
|
|
121
|
-
root.appendChild(header);
|
|
122
|
-
root.appendChild(body);
|
|
123
|
-
document.body.appendChild(root);
|
|
124
|
-
let isOpen = false;
|
|
125
|
-
let activeName = null;
|
|
126
|
-
let lastRenderedVersion = -1;
|
|
127
|
-
let sidebarCollapsed = false;
|
|
128
|
-
const MIN_ZOOM = 0.25;
|
|
129
|
-
const MAX_ZOOM = 64;
|
|
130
|
-
let zoom = 1;
|
|
131
|
-
let panX = 0;
|
|
132
|
-
let panY = 0;
|
|
133
|
-
let isDragging = false;
|
|
134
|
-
let dragStartX = 0;
|
|
135
|
-
let dragStartY = 0;
|
|
136
|
-
let panStartX = 0;
|
|
137
|
-
let panStartY = 0;
|
|
138
|
-
function updateZoomInfo() {
|
|
139
|
-
const z = zoom < 10 ? zoom.toFixed(1) : Math.round(zoom).toString();
|
|
140
|
-
zoomInfo.textContent = zoom === 1 ? "1.0\xD7" : `${z}\xD7 \xB7 (${Math.round(panX)}, ${Math.round(panY)})`;
|
|
141
|
-
const atIdentity = zoom === 1 && panX === 0 && panY === 0;
|
|
142
|
-
resetBtn.style.display = atIdentity ? "none" : "inline";
|
|
143
|
-
}
|
|
144
|
-
function applyTransform() {
|
|
145
|
-
canvas.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`;
|
|
146
|
-
canvas.style.transformOrigin = "center center";
|
|
147
|
-
updateZoomInfo();
|
|
148
|
-
}
|
|
149
|
-
function resetTransform() {
|
|
150
|
-
zoom = 1;
|
|
151
|
-
panX = 0;
|
|
152
|
-
panY = 0;
|
|
153
|
-
applyTransform();
|
|
154
|
-
}
|
|
155
|
-
main.addEventListener("wheel", (e) => {
|
|
156
|
-
e.preventDefault();
|
|
157
|
-
e.stopPropagation();
|
|
158
|
-
const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15;
|
|
159
|
-
const newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom * factor));
|
|
160
|
-
if (newZoom === zoom) return;
|
|
161
|
-
const rect = main.getBoundingClientRect();
|
|
162
|
-
const mx = e.clientX - rect.left - rect.width / 2;
|
|
163
|
-
const my = e.clientY - rect.top - rect.height / 2;
|
|
164
|
-
const ratio = 1 - newZoom / zoom;
|
|
165
|
-
panX += (mx - panX) * ratio;
|
|
166
|
-
panY += (my - panY) * ratio;
|
|
167
|
-
zoom = newZoom;
|
|
168
|
-
applyTransform();
|
|
169
|
-
}, { passive: false });
|
|
170
|
-
main.addEventListener("mousedown", (e) => {
|
|
171
|
-
if (e.button !== 0) return;
|
|
172
|
-
isDragging = true;
|
|
173
|
-
dragStartX = e.clientX;
|
|
174
|
-
dragStartY = e.clientY;
|
|
175
|
-
panStartX = panX;
|
|
176
|
-
panStartY = panY;
|
|
177
|
-
main.style.cursor = "grabbing";
|
|
178
|
-
e.preventDefault();
|
|
179
|
-
});
|
|
180
|
-
const onMouseMove = (e) => {
|
|
181
|
-
if (!isDragging) return;
|
|
182
|
-
panX = panStartX + (e.clientX - dragStartX);
|
|
183
|
-
panY = panStartY + (e.clientY - dragStartY);
|
|
184
|
-
applyTransform();
|
|
185
|
-
};
|
|
186
|
-
const onMouseUp = () => {
|
|
187
|
-
if (!isDragging) return;
|
|
188
|
-
isDragging = false;
|
|
189
|
-
main.style.cursor = "grab";
|
|
190
|
-
};
|
|
191
|
-
window.addEventListener("mousemove", onMouseMove);
|
|
192
|
-
window.addEventListener("mouseup", onMouseUp);
|
|
193
|
-
main.addEventListener("dblclick", () => resetTransform());
|
|
194
|
-
resetBtn.addEventListener("click", () => resetTransform());
|
|
195
|
-
main.style.cursor = "grab";
|
|
196
|
-
const rowEls = /* @__PURE__ */ new Map();
|
|
197
|
-
const groupContainers = /* @__PURE__ */ new Map();
|
|
198
|
-
function ensureGroup(group) {
|
|
199
|
-
let el = groupContainers.get(group);
|
|
200
|
-
if (el !== void 0) return el;
|
|
201
|
-
el = document.createElement("div");
|
|
202
|
-
el.style.cssText = "margin:6px 0";
|
|
203
|
-
const head = document.createElement("div");
|
|
204
|
-
head.textContent = group;
|
|
205
|
-
head.style.cssText = "padding:4px 12px;opacity:0.6;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;user-select:none";
|
|
206
|
-
el.appendChild(head);
|
|
207
|
-
sidebar.appendChild(el);
|
|
208
|
-
groupContainers.set(group, el);
|
|
209
|
-
return el;
|
|
210
|
-
}
|
|
211
|
-
function ensureRow(group, name, shortName) {
|
|
212
|
-
let row = rowEls.get(name);
|
|
213
|
-
if (row !== void 0) return row;
|
|
214
|
-
const groupEl = ensureGroup(group);
|
|
215
|
-
row = document.createElement("div");
|
|
216
|
-
row.textContent = shortName;
|
|
217
|
-
row.dataset["name"] = name;
|
|
218
|
-
row.style.cssText = "padding:4px 12px 4px 22px;cursor:pointer;user-select:none;border-left:2px solid transparent";
|
|
219
|
-
row.addEventListener("click", () => setActive(name));
|
|
220
|
-
groupEl.appendChild(row);
|
|
221
|
-
rowEls.set(name, row);
|
|
222
|
-
return row;
|
|
223
|
-
}
|
|
224
|
-
function destroyRow(name) {
|
|
225
|
-
const row = rowEls.get(name);
|
|
226
|
-
if (row === void 0) return;
|
|
227
|
-
row.remove();
|
|
228
|
-
rowEls.delete(name);
|
|
229
|
-
}
|
|
230
|
-
function highlightActive() {
|
|
231
|
-
for (const [name, row] of rowEls) {
|
|
232
|
-
const on = name === activeName;
|
|
233
|
-
row.style.background = on ? "rgba(71,204,169,0.15)" : "transparent";
|
|
234
|
-
row.style.borderLeftColor = on ? "var(--tp-button-foreground-color-active, #47cca9)" : "transparent";
|
|
235
|
-
row.style.color = on ? "var(--tp-label-foreground-color, #f0edd8)" : "rgba(240,237,216,0.7)";
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function paint(snap) {
|
|
239
|
-
if (ctx === null) return;
|
|
240
|
-
const { width, height, pixels } = snap;
|
|
241
|
-
if (width === 0 || height === 0 || pixels === null) {
|
|
242
|
-
canvas.width = 1;
|
|
243
|
-
canvas.height = 1;
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
if (offscreen.width !== width || offscreen.height !== height) {
|
|
247
|
-
offscreen.width = width;
|
|
248
|
-
offscreen.height = height;
|
|
249
|
-
}
|
|
250
|
-
const offCtx = offscreen.getContext("2d");
|
|
251
|
-
if (offCtx === null) return;
|
|
252
|
-
const imgData = offCtx.createImageData(width, height);
|
|
253
|
-
const out = imgData.data;
|
|
254
|
-
if (pixels instanceof Uint8Array) {
|
|
255
|
-
out.set(pixels.subarray(0, out.length));
|
|
256
|
-
} else {
|
|
257
|
-
const count = width * height;
|
|
258
|
-
for (let i = 0; i < count; i++) {
|
|
259
|
-
const o = i * 4;
|
|
260
|
-
out[o] = Math.round(Math.max(0, Math.min(1, pixels[i * 4] ?? 0)) * 255);
|
|
261
|
-
out[o + 1] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 1] ?? 0)) * 255);
|
|
262
|
-
out[o + 2] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 2] ?? 0)) * 255);
|
|
263
|
-
out[o + 3] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 3] ?? 1)) * 255);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
offCtx.putImageData(imgData, 0, 0);
|
|
267
|
-
if (canvas.width !== width || canvas.height !== height) {
|
|
268
|
-
canvas.width = width;
|
|
269
|
-
canvas.height = height;
|
|
270
|
-
}
|
|
271
|
-
const fitW = snap.srcWidth > 0 ? snap.srcWidth : width;
|
|
272
|
-
const fitH = snap.srcHeight > 0 ? snap.srcHeight : height;
|
|
273
|
-
canvasSrcW = fitW;
|
|
274
|
-
canvasSrcH = fitH;
|
|
275
|
-
fitCanvas(fitW, fitH);
|
|
276
|
-
ctx.imageSmoothingEnabled = false;
|
|
277
|
-
ctx.drawImage(offscreen, 0, 0);
|
|
278
|
-
}
|
|
279
|
-
function refresh() {
|
|
280
|
-
if (!isOpen) return;
|
|
281
|
-
const state = client.state;
|
|
282
|
-
const present = /* @__PURE__ */ new Set();
|
|
283
|
-
for (const [name] of state.buffers) {
|
|
284
|
-
present.add(name);
|
|
285
|
-
const dot = name.indexOf(".");
|
|
286
|
-
const group = dot === -1 ? "ungrouped" : name.slice(0, dot);
|
|
287
|
-
const short = dot === -1 ? name : name.slice(dot + 1);
|
|
288
|
-
ensureRow(group, name, short);
|
|
289
|
-
}
|
|
290
|
-
for (const name of rowEls.keys()) {
|
|
291
|
-
if (!present.has(name)) destroyRow(name);
|
|
292
|
-
}
|
|
293
|
-
if (activeName !== null && !state.buffers.has(activeName)) {
|
|
294
|
-
const first = state.buffers.keys().next().value;
|
|
295
|
-
activeName = first ?? null;
|
|
296
|
-
if (activeName !== null) client.setBuffers({ [activeName]: { mode: "stream" } });
|
|
297
|
-
else client.setBuffers({});
|
|
298
|
-
}
|
|
299
|
-
highlightActive();
|
|
300
|
-
if (activeName === null) {
|
|
301
|
-
titleEl.textContent = "\u2014";
|
|
302
|
-
dimsEl.textContent = "";
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
const snap = state.buffers.get(activeName);
|
|
306
|
-
if (snap === void 0) return;
|
|
307
|
-
titleEl.textContent = snap.label !== void 0 ? `${activeName} \u2014 ${snap.label}` : activeName;
|
|
308
|
-
dimsEl.textContent = `${snap.width}\xD7${snap.height} \xB7 ${snap.pixelType} \xB7 ${snap.display}`;
|
|
309
|
-
if (decoder === null && snap.version !== lastRenderedVersion) {
|
|
310
|
-
lastRenderedVersion = snap.version;
|
|
311
|
-
paint(snap);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function setActive(name) {
|
|
315
|
-
if (activeName === name) return;
|
|
316
|
-
activeName = name;
|
|
317
|
-
lastRenderedVersion = -1;
|
|
318
|
-
resetTransform();
|
|
319
|
-
stopDecoder();
|
|
320
|
-
decoderWidth = 0;
|
|
321
|
-
decoderHeight = 0;
|
|
322
|
-
client.setBuffers({ [name]: { mode: "stream" } });
|
|
323
|
-
options.onActiveChange?.(name);
|
|
324
|
-
highlightActive();
|
|
325
|
-
refresh();
|
|
326
|
-
}
|
|
327
|
-
const unsubscribe = client.addListener(() => refresh());
|
|
328
|
-
closeBtn.addEventListener("click", () => close());
|
|
329
|
-
sidebarToggle.addEventListener("click", () => {
|
|
330
|
-
sidebarCollapsed = !sidebarCollapsed;
|
|
331
|
-
sidebar.style.display = sidebarCollapsed ? "none" : "flex";
|
|
332
|
-
sidebarToggle.textContent = sidebarCollapsed ? "\u25B6" : "\u25C0";
|
|
333
|
-
refresh();
|
|
334
|
-
});
|
|
335
|
-
const onKeydown = (e) => {
|
|
336
|
-
if (!isOpen) return;
|
|
337
|
-
if (e.key === "Escape") close();
|
|
338
|
-
};
|
|
339
|
-
document.addEventListener("keydown", onKeydown);
|
|
340
|
-
function fitCanvas(srcW, srcH) {
|
|
341
|
-
if (srcW <= 0 || srcH <= 0) return;
|
|
342
|
-
const mainRect = main.getBoundingClientRect();
|
|
343
|
-
const maxW = Math.max(1, mainRect.width - 32);
|
|
344
|
-
const maxH = Math.max(1, mainRect.height - 32);
|
|
345
|
-
const srcAspect = srcW / srcH;
|
|
346
|
-
const boxAspect = maxW / maxH;
|
|
347
|
-
let cssW;
|
|
348
|
-
let cssH;
|
|
349
|
-
if (srcAspect > boxAspect) {
|
|
350
|
-
cssW = maxW;
|
|
351
|
-
cssH = Math.max(1, Math.round(maxW / srcAspect));
|
|
352
|
-
} else {
|
|
353
|
-
cssH = maxH;
|
|
354
|
-
cssW = Math.max(1, Math.round(maxH * srcAspect));
|
|
355
|
-
}
|
|
356
|
-
canvas.style.width = `${cssW}px`;
|
|
357
|
-
canvas.style.height = `${cssH}px`;
|
|
358
|
-
}
|
|
359
|
-
let canvasSrcW = 0;
|
|
360
|
-
let canvasSrcH = 0;
|
|
361
|
-
const onResize = () => {
|
|
362
|
-
if (!isOpen || activeName === null) return;
|
|
363
|
-
const snap = client.state.buffers.get(activeName);
|
|
364
|
-
if (snap !== void 0) paint(snap);
|
|
365
|
-
};
|
|
366
|
-
window.addEventListener("resize", onResize);
|
|
367
|
-
const mainResizeObserver = new ResizeObserver(() => {
|
|
368
|
-
if (!isOpen) return;
|
|
369
|
-
if (canvasSrcW > 0 && canvasSrcH > 0) fitCanvas(canvasSrcW, canvasSrcH);
|
|
370
|
-
});
|
|
371
|
-
mainResizeObserver.observe(main);
|
|
372
|
-
const codecAvailable = typeof VideoDecoder !== "undefined";
|
|
373
|
-
let decoder = null;
|
|
374
|
-
let unsubChunks = null;
|
|
375
|
-
let waitingForKeyFrame = true;
|
|
376
|
-
function startDecoder(width, height) {
|
|
377
|
-
stopDecoder();
|
|
378
|
-
if (!codecAvailable) return;
|
|
379
|
-
waitingForKeyFrame = true;
|
|
380
|
-
decoder = new VideoDecoder({
|
|
381
|
-
output: (frame) => {
|
|
382
|
-
if (ctx === null) {
|
|
383
|
-
frame.close();
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
if (canvas.width !== frame.codedWidth || canvas.height !== frame.codedHeight) {
|
|
387
|
-
canvas.width = frame.codedWidth;
|
|
388
|
-
canvas.height = frame.codedHeight;
|
|
389
|
-
}
|
|
390
|
-
canvasSrcW = frame.codedWidth;
|
|
391
|
-
canvasSrcH = frame.codedHeight;
|
|
392
|
-
fitCanvas(frame.codedWidth, frame.codedHeight);
|
|
393
|
-
ctx.drawImage(frame, 0, 0);
|
|
394
|
-
frame.close();
|
|
395
|
-
},
|
|
396
|
-
error: () => {
|
|
397
|
-
stopDecoder();
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
decoder.configure({
|
|
401
|
-
codec: "vp09.00.10.08",
|
|
402
|
-
codedWidth: width,
|
|
403
|
-
codedHeight: height
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
function stopDecoder() {
|
|
407
|
-
if (decoder !== null && decoder.state !== "closed") {
|
|
408
|
-
try {
|
|
409
|
-
decoder.close();
|
|
410
|
-
} catch {
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
decoder = null;
|
|
414
|
-
waitingForKeyFrame = true;
|
|
415
|
-
}
|
|
416
|
-
let decoderWidth = 0;
|
|
417
|
-
let decoderHeight = 0;
|
|
418
|
-
function onChunk(chunk) {
|
|
419
|
-
if (!isOpen) return;
|
|
420
|
-
if (chunk.name !== activeName) return;
|
|
421
|
-
if (decoder === null || chunk.width !== decoderWidth || chunk.height !== decoderHeight) {
|
|
422
|
-
decoderWidth = chunk.width;
|
|
423
|
-
decoderHeight = chunk.height;
|
|
424
|
-
startDecoder(chunk.width, chunk.height);
|
|
425
|
-
}
|
|
426
|
-
if (decoder === null) return;
|
|
427
|
-
if (waitingForKeyFrame && !chunk.keyFrame) return;
|
|
428
|
-
if (chunk.keyFrame) waitingForKeyFrame = false;
|
|
429
|
-
try {
|
|
430
|
-
const encoded = new EncodedVideoChunk({
|
|
431
|
-
type: chunk.keyFrame ? "key" : "delta",
|
|
432
|
-
timestamp: chunk.capturedAt * 1e3,
|
|
433
|
-
data: chunk.data
|
|
434
|
-
});
|
|
435
|
-
decoder.decode(encoded);
|
|
436
|
-
} catch {
|
|
437
|
-
stopDecoder();
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
function open(name) {
|
|
441
|
-
if (isOpen && activeName === name) return;
|
|
442
|
-
isOpen = true;
|
|
443
|
-
root.style.display = "flex";
|
|
444
|
-
options.onOpen?.();
|
|
445
|
-
setActive(name);
|
|
446
|
-
client.setBuffers({ [name]: { mode: "stream" } });
|
|
447
|
-
if (codecAvailable && unsubChunks === null) {
|
|
448
|
-
unsubChunks = client.addChunkListener(onChunk);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
function close() {
|
|
452
|
-
if (!isOpen) return;
|
|
453
|
-
isOpen = false;
|
|
454
|
-
root.style.display = "none";
|
|
455
|
-
stopDecoder();
|
|
456
|
-
if (unsubChunks !== null) {
|
|
457
|
-
unsubChunks();
|
|
458
|
-
unsubChunks = null;
|
|
459
|
-
}
|
|
460
|
-
client.setBuffers({});
|
|
461
|
-
options.onClose?.();
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
get isOpen() {
|
|
465
|
-
return isOpen;
|
|
466
|
-
},
|
|
467
|
-
open,
|
|
468
|
-
close,
|
|
469
|
-
dispose() {
|
|
470
|
-
stopDecoder();
|
|
471
|
-
if (unsubChunks !== null) {
|
|
472
|
-
unsubChunks();
|
|
473
|
-
unsubChunks = null;
|
|
474
|
-
}
|
|
475
|
-
unsubscribe();
|
|
476
|
-
mainResizeObserver.disconnect();
|
|
477
|
-
document.removeEventListener("keydown", onKeydown);
|
|
478
|
-
window.removeEventListener("resize", onResize);
|
|
479
|
-
window.removeEventListener("mousemove", onMouseMove);
|
|
480
|
-
window.removeEventListener("mouseup", onMouseUp);
|
|
481
|
-
root.remove();
|
|
482
|
-
rowEls.clear();
|
|
483
|
-
groupContainers.clear();
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
488
|
-
0 && (module.exports = {
|
|
489
|
-
createBuffersModal
|
|
490
|
-
});
|
|
491
|
-
//# sourceMappingURL=buffers-modal.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffers-modal.ts"],"sourcesContent":["/**\n * Fullscreen buffer viewer.\n *\n * Lives outside the Tweakpane DOM tree — mounts a top-level\n * `<div>` to `document.body` with a high `z-index`, fully blocks\n * interaction with the host page until closed. Layout:\n *\n * ┌───────────────────────────────────────────┐\n * │ ◀ tree │ ⤡ × │ ← header bar\n * │────────│ │\n * │ group1 │ canvas │\n * │ • a │ (aspect-correct) │\n * │ • b │ │\n * │ group2 │ │\n * │ • c │ │\n * │ │ │\n * └───────────────────────────────────────────┘\n *\n * Sidebar (defaults expanded, collapsible via the ◀ at the top-left):\n * - Group tree: every registered buffer, grouped by `name` prefix.\n * - Click a buffer to make it active. Active row highlighted.\n *\n * Main area:\n * - Canvas sized to fill remaining space, source bitmap rendered\n * aspect-correct (`object-fit: contain` semantics) with the same\n * decoder pipeline as the in-pane thumbnail.\n * - Header chips: dimensions, pixel type, display mode.\n * - `×` button (top-right) closes; `Esc` also closes; outer-click\n * does not close (modal is intentionally sticky to avoid\n * accidental dismiss while inspecting).\n *\n * Selection drives `client.setBuffers([activeName])` so only the\n * one buffer the user is looking at gets streamed. Closing the\n * modal restores whatever the in-pane thumbnail's selection was.\n */\n\nimport type { BufferChunkPayload, BufferSnapshot, DevtoolsClient } from './devtools-client.js'\n\nexport interface BuffersModalHandle {\n /** Show the modal with the given buffer pre-selected. */\n open(name: string): void\n /** Close it (no-op if already closed). */\n close(): void\n /** True when visible. */\n readonly isOpen: boolean\n /** Tear down DOM + listener. */\n dispose(): void\n}\n\nexport interface BuffersModalOptions {\n onActiveChange?: (name: string) => void\n onOpen?: () => void\n onClose?: () => void\n}\n\n/**\n * Build a fullscreen modal anchored to `document.body`. Returns an\n * imperative handle — the in-pane thumbnail's expand button calls\n * `open(activeName)` to surface it.\n */\nexport function createBuffersModal(\n client: DevtoolsClient,\n options: BuffersModalOptions = {},\n): BuffersModalHandle {\n // ── Root + overlay ────────────────────────────────────────────────────\n\n const root = document.createElement('div')\n root.style.cssText = [\n 'position:fixed',\n 'inset:0',\n 'z-index:99999',\n 'background:rgba(0,2,28,0.92)',\n 'color:var(--tp-label-foreground-color, #f0edd8)',\n 'font-family:var(--tp-base-font-family, ui-monospace, monospace)',\n 'font-size:12px',\n 'display:none',\n 'flex-direction:column',\n ].join(';')\n\n // ── Header bar ────────────────────────────────────────────────────────\n\n const header = document.createElement('div')\n header.style.cssText = [\n 'display:flex',\n 'align-items:center',\n 'gap:12px',\n 'padding:8px 12px',\n 'border-bottom:1px solid rgba(240,237,216,0.08)',\n 'flex:0 0 auto',\n ].join(';')\n\n const sidebarToggle = document.createElement('span')\n sidebarToggle.textContent = '◀'\n sidebarToggle.style.cssText = 'cursor:pointer;padding:2px 6px;opacity:0.8;user-select:none'\n sidebarToggle.title = 'Toggle sidebar'\n\n const titleEl = document.createElement('span')\n titleEl.style.cssText = 'flex:1;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'\n\n const dimsEl = document.createElement('span')\n dimsEl.style.cssText = 'opacity:0.7;font-variant-numeric:tabular-nums'\n\n const closeBtn = document.createElement('span')\n closeBtn.textContent = '×'\n closeBtn.style.cssText = 'cursor:pointer;padding:2px 10px;font-size:18px;line-height:1;opacity:0.8;user-select:none'\n closeBtn.title = 'Close'\n\n header.appendChild(sidebarToggle)\n header.appendChild(titleEl)\n header.appendChild(dimsEl)\n header.appendChild(closeBtn)\n\n // ── Body: sidebar + main ──────────────────────────────────────────────\n\n const body = document.createElement('div')\n body.style.cssText = 'display:flex;flex:1;min-height:0'\n\n const sidebar = document.createElement('div')\n sidebar.style.cssText = [\n 'display:flex',\n 'flex-direction:column',\n 'width:240px',\n 'border-right:1px solid rgba(240,237,216,0.08)',\n 'overflow-y:auto',\n 'padding:8px 0',\n 'flex:0 0 auto',\n ].join(';')\n\n const main = document.createElement('div')\n main.style.cssText = 'flex:1;display:flex;align-items:center;justify-content:center;padding:16px;min-width:0;min-height:0;overflow:hidden;position:relative'\n\n const canvas = document.createElement('canvas')\n canvas.style.cssText = 'display:block;background:rgba(0,2,28,0.6);image-rendering:pixelated;max-width:100%;max-height:100%'\n const ctx = canvas.getContext('2d')\n\n const offscreen = document.createElement('canvas')\n offscreen.width = 1\n offscreen.height = 1\n\n // Zoom info + reset — top-left to avoid conflict with docs page controls\n const zoomBar = document.createElement('div')\n zoomBar.style.cssText = [\n 'position:absolute', 'top:8px', 'left:8px',\n 'display:flex', 'gap:6px', 'align-items:center',\n 'z-index:1',\n ].join(';')\n\n const zoomInfo = document.createElement('span')\n zoomInfo.style.cssText = [\n 'padding:3px 8px', 'border-radius:4px',\n 'background:rgba(0,0,0,0.6)', 'color:#aaa',\n 'font:11px/1.4 monospace', 'pointer-events:none',\n 'user-select:none',\n ].join(';')\n zoomInfo.textContent = '1.0×'\n\n const resetBtn = document.createElement('span')\n resetBtn.style.cssText = [\n 'padding:3px 8px', 'border-radius:4px',\n 'background:rgba(0,0,0,0.6)', 'color:#aaa',\n 'font:11px/1.4 monospace', 'cursor:pointer',\n 'user-select:none',\n ].join(';')\n resetBtn.textContent = '⟲ Reset'\n resetBtn.title = 'Reset zoom & pan (double-click also resets)'\n\n zoomBar.appendChild(zoomInfo)\n zoomBar.appendChild(resetBtn)\n\n main.appendChild(canvas)\n main.appendChild(zoomBar)\n body.appendChild(sidebar)\n body.appendChild(main)\n\n root.appendChild(header)\n root.appendChild(body)\n document.body.appendChild(root)\n\n // ── State ─────────────────────────────────────────────────────────────\n\n let isOpen = false\n let activeName: string | null = null\n let lastRenderedVersion = -1\n let sidebarCollapsed = false\n\n // ── Pan + zoom ─────────────────────────────────────────────────────────\n\n const MIN_ZOOM = 0.25\n const MAX_ZOOM = 64\n\n let zoom = 1\n let panX = 0\n let panY = 0\n let isDragging = false\n let dragStartX = 0\n let dragStartY = 0\n let panStartX = 0\n let panStartY = 0\n\n function updateZoomInfo(): void {\n const z = zoom < 10 ? zoom.toFixed(1) : Math.round(zoom).toString()\n zoomInfo.textContent = zoom === 1 ? '1.0×' : `${z}× · (${Math.round(panX)}, ${Math.round(panY)})`\n const atIdentity = zoom === 1 && panX === 0 && panY === 0\n resetBtn.style.display = atIdentity ? 'none' : 'inline'\n }\n\n function applyTransform(): void {\n canvas.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`\n canvas.style.transformOrigin = 'center center'\n updateZoomInfo()\n }\n\n function resetTransform(): void {\n zoom = 1\n panX = 0\n panY = 0\n applyTransform()\n }\n\n // Prevent page scroll when pointer is over the main area\n main.addEventListener('wheel', (e) => {\n e.preventDefault()\n e.stopPropagation()\n\n const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15\n const newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom * factor))\n if (newZoom === zoom) return\n\n // Zoom toward cursor position\n const rect = main.getBoundingClientRect()\n const mx = e.clientX - rect.left - rect.width / 2\n const my = e.clientY - rect.top - rect.height / 2\n const ratio = 1 - newZoom / zoom\n panX += (mx - panX) * ratio\n panY += (my - panY) * ratio\n zoom = newZoom\n applyTransform()\n }, { passive: false })\n\n main.addEventListener('mousedown', (e) => {\n if (e.button !== 0) return\n isDragging = true\n dragStartX = e.clientX\n dragStartY = e.clientY\n panStartX = panX\n panStartY = panY\n main.style.cursor = 'grabbing'\n e.preventDefault()\n })\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return\n panX = panStartX + (e.clientX - dragStartX)\n panY = panStartY + (e.clientY - dragStartY)\n applyTransform()\n }\n\n const onMouseUp = () => {\n if (!isDragging) return\n isDragging = false\n main.style.cursor = 'grab'\n }\n\n window.addEventListener('mousemove', onMouseMove)\n window.addEventListener('mouseup', onMouseUp)\n\n main.addEventListener('dblclick', () => resetTransform())\n resetBtn.addEventListener('click', () => resetTransform())\n\n main.style.cursor = 'grab'\n\n // Track the per-row DOM nodes so we can update highlight without\n // re-rendering the whole sidebar each batch.\n const rowEls = new Map<string, HTMLDivElement>()\n\n // ── Sidebar render ────────────────────────────────────────────────────\n\n const groupContainers = new Map<string, HTMLDivElement>()\n\n function ensureGroup(group: string): HTMLDivElement {\n let el = groupContainers.get(group)\n if (el !== undefined) return el\n el = document.createElement('div')\n el.style.cssText = 'margin:6px 0'\n const head = document.createElement('div')\n head.textContent = group\n head.style.cssText = 'padding:4px 12px;opacity:0.6;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;user-select:none'\n el.appendChild(head)\n sidebar.appendChild(el)\n groupContainers.set(group, el)\n return el\n }\n\n function ensureRow(group: string, name: string, shortName: string): HTMLDivElement {\n let row = rowEls.get(name)\n if (row !== undefined) return row\n const groupEl = ensureGroup(group)\n row = document.createElement('div')\n row.textContent = shortName\n row.dataset['name'] = name\n row.style.cssText = 'padding:4px 12px 4px 22px;cursor:pointer;user-select:none;border-left:2px solid transparent'\n row.addEventListener('click', () => setActive(name))\n groupEl.appendChild(row)\n rowEls.set(name, row)\n return row\n }\n\n function destroyRow(name: string): void {\n const row = rowEls.get(name)\n if (row === undefined) return\n row.remove()\n rowEls.delete(name)\n }\n\n function highlightActive(): void {\n for (const [name, row] of rowEls) {\n const on = name === activeName\n row.style.background = on ? 'rgba(71,204,169,0.15)' : 'transparent'\n row.style.borderLeftColor = on ? 'var(--tp-button-foreground-color-active, #47cca9)' : 'transparent'\n row.style.color = on ? 'var(--tp-label-foreground-color, #f0edd8)' : 'rgba(240,237,216,0.7)'\n }\n }\n\n // ── Render the active buffer into the canvas ──────────────────────────\n\n function paint(snap: BufferSnapshot): void {\n if (ctx === null) return\n const { width, height, pixels } = snap\n if (width === 0 || height === 0 || pixels === null) {\n // Size canvas to a placeholder so it still occupies space.\n canvas.width = 1; canvas.height = 1\n return\n }\n\n if (offscreen.width !== width || offscreen.height !== height) {\n offscreen.width = width\n offscreen.height = height\n }\n const offCtx = offscreen.getContext('2d')\n if (offCtx === null) return\n\n const imgData = offCtx.createImageData(width, height)\n const out = imgData.data\n\n // Pixels arrive as display-ready RGBA8 from the worker — copy\n // directly into the ImageData without any decoder logic.\n if (pixels instanceof Uint8Array) {\n out.set(pixels.subarray(0, out.length))\n } else {\n // Float32Array fallback (shouldn't happen post-pipeline, but\n // guard against legacy callers).\n const count = width * height\n for (let i = 0; i < count; i++) {\n const o = i * 4\n out[o] = Math.round(Math.max(0, Math.min(1, pixels[i * 4] ?? 0)) * 255)\n out[o + 1] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 1] ?? 0)) * 255)\n out[o + 2] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 2] ?? 0)) * 255)\n out[o + 3] = Math.round(Math.max(0, Math.min(1, pixels[i * 4 + 3] ?? 1)) * 255)\n }\n }\n offCtx.putImageData(imgData, 0, 0)\n\n // Backing matches displayed pixels at 1:1 (no DPR mul — modal is\n // about inspection, not pretty rendering, and 1:1 keeps the\n // pixel grid clearly visible).\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width\n canvas.height = height\n }\n // Fit the canvas CSS box to the SOURCE aspect — thumbnails are\n // downsampled, but the modal should still display at native aspect\n // so the user doesn't see a wildly-stretched preview while the\n // stream mode warms up.\n const fitW = snap.srcWidth > 0 ? snap.srcWidth : width\n const fitH = snap.srcHeight > 0 ? snap.srcHeight : height\n canvasSrcW = fitW\n canvasSrcH = fitH\n fitCanvas(fitW, fitH)\n ctx.imageSmoothingEnabled = false\n ctx.drawImage(offscreen, 0, 0)\n }\n\n function refresh(): void {\n if (!isOpen) return\n const state = client.state\n\n // Sync the sidebar tree.\n const present = new Set<string>()\n for (const [name] of state.buffers) {\n present.add(name)\n const dot = name.indexOf('.')\n const group = dot === -1 ? 'ungrouped' : name.slice(0, dot)\n const short = dot === -1 ? name : name.slice(dot + 1)\n ensureRow(group, name, short)\n }\n for (const name of rowEls.keys()) {\n if (!present.has(name)) destroyRow(name)\n }\n\n // If active was removed, fall back to first available.\n if (activeName !== null && !state.buffers.has(activeName)) {\n const first = state.buffers.keys().next().value\n activeName = first ?? null\n // setActive resyncs server filter, but we may be in mid-refresh\n // so call the underlying setter directly.\n if (activeName !== null) client.setBuffers({ [activeName]: { mode: 'stream' } })\n else client.setBuffers({})\n }\n highlightActive()\n\n if (activeName === null) {\n titleEl.textContent = '—'\n dimsEl.textContent = ''\n return\n }\n const snap = state.buffers.get(activeName)\n if (snap === undefined) return\n titleEl.textContent = snap.label !== undefined ? `${activeName} — ${snap.label}` : activeName\n dimsEl.textContent = `${snap.width}×${snap.height} · ${snap.pixelType} · ${snap.display}`\n // In stream mode the VideoDecoder draws directly to the canvas —\n // don't let paint() overwrite it (pixels are stripped from the\n // batch so snap.pixels is null, which would clear the canvas).\n if (decoder === null && snap.version !== lastRenderedVersion) {\n lastRenderedVersion = snap.version\n paint(snap)\n }\n }\n\n function setActive(name: string): void {\n if (activeName === name) return\n activeName = name\n lastRenderedVersion = -1\n resetTransform()\n stopDecoder()\n decoderWidth = 0\n decoderHeight = 0\n // Modal always wants the live stream — codec fallback is handled\n // provider-side (falls back to raw broadcasts when WebCodecs is\n // unavailable).\n client.setBuffers({ [name]: { mode: 'stream' } })\n options.onActiveChange?.(name)\n highlightActive()\n refresh()\n }\n\n // ── Event wiring ──────────────────────────────────────────────────────\n\n const unsubscribe = client.addListener(() => refresh())\n\n closeBtn.addEventListener('click', () => close())\n sidebarToggle.addEventListener('click', () => {\n sidebarCollapsed = !sidebarCollapsed\n sidebar.style.display = sidebarCollapsed ? 'none' : 'flex'\n sidebarToggle.textContent = sidebarCollapsed ? '▶' : '◀'\n refresh()\n })\n const onKeydown = (e: KeyboardEvent) => {\n if (!isOpen) return\n if (e.key === 'Escape') close()\n }\n document.addEventListener('keydown', onKeydown)\n\n // Aspect-fit the canvas box inside `main`, given source dimensions.\n // Shared by `paint()`, the decoder output callback, and the\n // ResizeObserver below so the CSS box always tracks the container\n // regardless of which path last touched the canvas.\n function fitCanvas(srcW: number, srcH: number): void {\n if (srcW <= 0 || srcH <= 0) return\n const mainRect = main.getBoundingClientRect()\n const maxW = Math.max(1, mainRect.width - 32)\n const maxH = Math.max(1, mainRect.height - 32)\n const srcAspect = srcW / srcH\n const boxAspect = maxW / maxH\n let cssW: number\n let cssH: number\n if (srcAspect > boxAspect) {\n cssW = maxW\n cssH = Math.max(1, Math.round(maxW / srcAspect))\n } else {\n cssH = maxH\n cssW = Math.max(1, Math.round(maxH * srcAspect))\n }\n canvas.style.width = `${cssW}px`\n canvas.style.height = `${cssH}px`\n }\n\n // Track current source dimensions so the ResizeObserver can re-fit\n // without waiting for a new frame. Set by both paint() (thumbnail\n // mode) and the decoder output callback (stream mode).\n let canvasSrcW = 0\n let canvasSrcH = 0\n\n // Re-paint on viewport resize so the canvas re-fits its container.\n const onResize = () => {\n if (!isOpen || activeName === null) return\n const snap = client.state.buffers.get(activeName)\n if (snap !== undefined) paint(snap)\n }\n window.addEventListener('resize', onResize)\n\n // Observe the container itself: sidebar collapse, viewport resize,\n // or ancestor layout changes can all change `main`'s box without\n // firing a window resize. Without this, a zoomed-in canvas keeps\n // its stale CSS size and clips against the shrunken main container.\n const mainResizeObserver = new ResizeObserver(() => {\n if (!isOpen) return\n if (canvasSrcW > 0 && canvasSrcH > 0) fitCanvas(canvasSrcW, canvasSrcH)\n })\n mainResizeObserver.observe(main)\n\n // ── Open / close ──────────────────────────────────────────────────────\n\n // ── WebCodecs decoder ─────────────────────────────────────────────────\n\n const codecAvailable = typeof VideoDecoder !== 'undefined'\n let decoder: VideoDecoder | null = null\n let unsubChunks: (() => void) | null = null\n let waitingForKeyFrame = true\n\n function startDecoder(width: number, height: number): void {\n stopDecoder()\n if (!codecAvailable) return\n waitingForKeyFrame = true\n decoder = new VideoDecoder({\n output: (frame) => {\n if (ctx === null) { frame.close(); return }\n if (canvas.width !== frame.codedWidth || canvas.height !== frame.codedHeight) {\n canvas.width = frame.codedWidth\n canvas.height = frame.codedHeight\n }\n canvasSrcW = frame.codedWidth\n canvasSrcH = frame.codedHeight\n fitCanvas(frame.codedWidth, frame.codedHeight)\n ctx.drawImage(frame as unknown as CanvasImageSource, 0, 0)\n frame.close()\n },\n error: () => {\n stopDecoder()\n },\n })\n decoder.configure({\n codec: 'vp09.00.10.08',\n codedWidth: width,\n codedHeight: height,\n })\n }\n\n function stopDecoder(): void {\n if (decoder !== null && decoder.state !== 'closed') {\n try { decoder.close() } catch { /* may already be errored */ }\n }\n decoder = null\n waitingForKeyFrame = true\n }\n\n let decoderWidth = 0\n let decoderHeight = 0\n\n function onChunk(chunk: BufferChunkPayload): void {\n if (!isOpen) return\n if (chunk.name !== activeName) return\n\n // Start or reconfigure decoder on dimension change\n if (decoder === null || chunk.width !== decoderWidth || chunk.height !== decoderHeight) {\n decoderWidth = chunk.width\n decoderHeight = chunk.height\n startDecoder(chunk.width, chunk.height)\n }\n if (decoder === null) return\n\n if (waitingForKeyFrame && !chunk.keyFrame) return\n if (chunk.keyFrame) waitingForKeyFrame = false\n\n try {\n const encoded = new EncodedVideoChunk({\n type: chunk.keyFrame ? 'key' : 'delta',\n timestamp: chunk.capturedAt * 1000,\n data: chunk.data,\n })\n decoder.decode(encoded)\n } catch {\n stopDecoder()\n }\n }\n\n // ── Open / close ──────────────────────────────────────────────────────\n\n function open(name: string): void {\n if (isOpen && activeName === name) return\n isOpen = true\n root.style.display = 'flex'\n options.onOpen?.()\n setActive(name)\n // setActive short-circuits when re-opening the same target, but\n // close() already wiped the subscription (and buffers-view may have\n // switched it to thumbnail). Force stream mode here — no-op when\n // setActive just set it.\n client.setBuffers({ [name]: { mode: 'stream' } })\n if (codecAvailable && unsubChunks === null) {\n unsubChunks = client.addChunkListener(onChunk)\n }\n }\n\n function close(): void {\n if (!isOpen) return\n isOpen = false\n root.style.display = 'none'\n stopDecoder()\n if (unsubChunks !== null) {\n unsubChunks()\n unsubChunks = null\n }\n // Revert to idle — thumbnail view (if any) will re-subscribe\n // in thumbnail mode itself when it becomes the active consumer.\n client.setBuffers({})\n options.onClose?.()\n }\n\n return {\n get isOpen(): boolean { return isOpen },\n open,\n close,\n dispose() {\n stopDecoder()\n if (unsubChunks !== null) {\n unsubChunks()\n unsubChunks = null\n }\n unsubscribe()\n mainResizeObserver.disconnect()\n document.removeEventListener('keydown', onKeydown)\n window.removeEventListener('resize', onResize)\n window.removeEventListener('mousemove', onMouseMove)\n window.removeEventListener('mouseup', onMouseUp)\n root.remove()\n rowEls.clear()\n groupContainers.clear()\n },\n }\n}\n\n"],"mappings":"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","names":[]}
|
package/dist/buffers-modal.d.cts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { DevtoolsClient } from './devtools-client.cjs';
|
|
2
|
-
import 'three-flatland/debug-protocol';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Fullscreen buffer viewer.
|
|
6
|
-
*
|
|
7
|
-
* Lives outside the Tweakpane DOM tree — mounts a top-level
|
|
8
|
-
* `<div>` to `document.body` with a high `z-index`, fully blocks
|
|
9
|
-
* interaction with the host page until closed. Layout:
|
|
10
|
-
*
|
|
11
|
-
* ┌───────────────────────────────────────────┐
|
|
12
|
-
* │ ◀ tree │ ⤡ × │ ← header bar
|
|
13
|
-
* │────────│ │
|
|
14
|
-
* │ group1 │ canvas │
|
|
15
|
-
* │ • a │ (aspect-correct) │
|
|
16
|
-
* │ • b │ │
|
|
17
|
-
* │ group2 │ │
|
|
18
|
-
* │ • c │ │
|
|
19
|
-
* │ │ │
|
|
20
|
-
* └───────────────────────────────────────────┘
|
|
21
|
-
*
|
|
22
|
-
* Sidebar (defaults expanded, collapsible via the ◀ at the top-left):
|
|
23
|
-
* - Group tree: every registered buffer, grouped by `name` prefix.
|
|
24
|
-
* - Click a buffer to make it active. Active row highlighted.
|
|
25
|
-
*
|
|
26
|
-
* Main area:
|
|
27
|
-
* - Canvas sized to fill remaining space, source bitmap rendered
|
|
28
|
-
* aspect-correct (`object-fit: contain` semantics) with the same
|
|
29
|
-
* decoder pipeline as the in-pane thumbnail.
|
|
30
|
-
* - Header chips: dimensions, pixel type, display mode.
|
|
31
|
-
* - `×` button (top-right) closes; `Esc` also closes; outer-click
|
|
32
|
-
* does not close (modal is intentionally sticky to avoid
|
|
33
|
-
* accidental dismiss while inspecting).
|
|
34
|
-
*
|
|
35
|
-
* Selection drives `client.setBuffers([activeName])` so only the
|
|
36
|
-
* one buffer the user is looking at gets streamed. Closing the
|
|
37
|
-
* modal restores whatever the in-pane thumbnail's selection was.
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
interface BuffersModalHandle {
|
|
41
|
-
/** Show the modal with the given buffer pre-selected. */
|
|
42
|
-
open(name: string): void;
|
|
43
|
-
/** Close it (no-op if already closed). */
|
|
44
|
-
close(): void;
|
|
45
|
-
/** True when visible. */
|
|
46
|
-
readonly isOpen: boolean;
|
|
47
|
-
/** Tear down DOM + listener. */
|
|
48
|
-
dispose(): void;
|
|
49
|
-
}
|
|
50
|
-
interface BuffersModalOptions {
|
|
51
|
-
onActiveChange?: (name: string) => void;
|
|
52
|
-
onOpen?: () => void;
|
|
53
|
-
onClose?: () => void;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Build a fullscreen modal anchored to `document.body`. Returns an
|
|
57
|
-
* imperative handle — the in-pane thumbnail's expand button calls
|
|
58
|
-
* `open(activeName)` to surface it.
|
|
59
|
-
*/
|
|
60
|
-
declare function createBuffersModal(client: DevtoolsClient, options?: BuffersModalOptions): BuffersModalHandle;
|
|
61
|
-
|
|
62
|
-
export { type BuffersModalHandle, type BuffersModalOptions, createBuffersModal };
|