@triscope/core 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/compose.d.ts +11 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +152 -0
- package/dist/compose.js.map +1 -0
- package/dist/editor.d.ts +14 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +131 -0
- package/dist/editor.js.map +1 -0
- package/dist/harness.d.ts +199 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +1027 -0
- package/dist/harness.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect.d.ts +94 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +434 -0
- package/dist/inspect.js.map +1 -0
- package/dist/knob-utils.d.ts +22 -0
- package/dist/knob-utils.d.ts.map +1 -0
- package/dist/knob-utils.js +51 -0
- package/dist/knob-utils.js.map +1 -0
- package/dist/lab/css.d.ts +11 -0
- package/dist/lab/css.d.ts.map +1 -0
- package/dist/lab/css.js +57 -0
- package/dist/lab/css.js.map +1 -0
- package/dist/lab/dom.d.ts +13 -0
- package/dist/lab/dom.d.ts.map +1 -0
- package/dist/lab/dom.js +76 -0
- package/dist/lab/dom.js.map +1 -0
- package/dist/motion-probe.d.ts +47 -0
- package/dist/motion-probe.d.ts.map +1 -0
- package/dist/motion-probe.js +122 -0
- package/dist/motion-probe.js.map +1 -0
- package/dist/probe-utils.d.ts +14 -0
- package/dist/probe-utils.d.ts.map +1 -0
- package/dist/probe-utils.js +18 -0
- package/dist/probe-utils.js.map +1 -0
- package/dist/scene-cameras.d.ts +6 -0
- package/dist/scene-cameras.d.ts.map +1 -0
- package/dist/scene-cameras.js +20 -0
- package/dist/scene-cameras.js.map +1 -0
- package/dist/scene-delta.d.ts +21 -0
- package/dist/scene-delta.d.ts.map +1 -0
- package/dist/scene-delta.js +57 -0
- package/dist/scene-delta.js.map +1 -0
- package/dist/scene-introspect.d.ts +78 -0
- package/dist/scene-introspect.d.ts.map +1 -0
- package/dist/scene-introspect.js +164 -0
- package/dist/scene-introspect.js.map +1 -0
- package/dist/scene-registry.d.ts +36 -0
- package/dist/scene-registry.d.ts.map +1 -0
- package/dist/scene-registry.js +64 -0
- package/dist/scene-registry.js.map +1 -0
- package/dist/scene-view.d.ts +52 -0
- package/dist/scene-view.d.ts.map +1 -0
- package/dist/scene-view.js +171 -0
- package/dist/scene-view.js.map +1 -0
- package/dist/source-tag.d.ts +34 -0
- package/dist/source-tag.d.ts.map +1 -0
- package/dist/source-tag.js +120 -0
- package/dist/source-tag.js.map +1 -0
- package/dist/telemetry.d.ts +53 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +302 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +142 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/uniform-access.d.ts +32 -0
- package/dist/uniform-access.d.ts.map +1 -0
- package/dist/uniform-access.js +144 -0
- package/dist/uniform-access.js.map +1 -0
- package/dist/validate.d.ts +2 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +81 -0
- package/dist/validate.js.map +1 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +4 -0
- package/dist/vite.js.map +1 -0
- package/dist/warnings.d.ts +24 -0
- package/dist/warnings.d.ts.map +1 -0
- package/dist/warnings.js +26 -0
- package/dist/warnings.js.map +1 -0
- package/package.json +60 -0
- package/src/compose.ts +164 -0
- package/src/editor.ts +138 -0
- package/src/harness.ts +1263 -0
- package/src/index.ts +58 -0
- package/src/inspect.ts +498 -0
- package/src/knob-utils.ts +60 -0
- package/src/lab/css.ts +56 -0
- package/src/lab/dom.ts +88 -0
- package/src/motion-probe.ts +135 -0
- package/src/probe-utils.ts +17 -0
- package/src/scene-cameras.ts +33 -0
- package/src/scene-delta.ts +69 -0
- package/src/scene-introspect.ts +230 -0
- package/src/scene-registry.ts +103 -0
- package/src/scene-view.ts +204 -0
- package/src/source-tag.ts +139 -0
- package/src/telemetry.ts +337 -0
- package/src/three-webgpu-shim.d.ts +130 -0
- package/src/types.ts +121 -0
- package/src/uniform-access.ts +152 -0
- package/src/validate.ts +82 -0
- package/src/vite.ts +5 -0
- package/src/warnings.ts +41 -0
package/dist/inspect.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspect mode: solo-view + OrbitControls + raycaster picking + hover
|
|
3
|
+
* highlight + click-to-select. Writes selection into
|
|
4
|
+
* window.__TRISCOPE__.lastSelection so MCP read_telemetry .selection
|
|
5
|
+
* surfaces the source frame for the picked mesh.
|
|
6
|
+
*
|
|
7
|
+
* Activation: URL `?inspect=<element>&camera=<name>` (camera optional, falls
|
|
8
|
+
* back to the first declared camera). When inactive the harness behaves
|
|
9
|
+
* exactly as before — the grid view.
|
|
10
|
+
*
|
|
11
|
+
* Highlight is a single shared wireframe Mesh that borrows the hovered
|
|
12
|
+
* object's geometry (no per-frame allocation). Click-selection persists
|
|
13
|
+
* the same overlay with a different color until the next click.
|
|
14
|
+
*/
|
|
15
|
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
16
|
+
import * as THREE from 'three/webgpu';
|
|
17
|
+
export function buildParentChain(obj) {
|
|
18
|
+
const chain = [];
|
|
19
|
+
let cur = obj;
|
|
20
|
+
while (cur) {
|
|
21
|
+
chain.unshift(describeObj(cur));
|
|
22
|
+
cur = cur.parent ?? null;
|
|
23
|
+
}
|
|
24
|
+
return chain;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compact human-readable description of an Object3D used in parentChain
|
|
28
|
+
* and clipboard formats. Prefers the author's `.name` (most specific);
|
|
29
|
+
* falls back to a self-describing shorthand like `Mesh<PlaneGeometry
|
|
30
|
+
* #e6dcc0>` so the user can still grep — even when no names are set on
|
|
31
|
+
* the scene tree.
|
|
32
|
+
*/
|
|
33
|
+
export function describeObj(obj) {
|
|
34
|
+
if (obj.name)
|
|
35
|
+
return obj.name;
|
|
36
|
+
const ctor = obj.constructor?.name ?? '?';
|
|
37
|
+
const mesh = obj;
|
|
38
|
+
const geom = mesh.geometry?.type;
|
|
39
|
+
let color;
|
|
40
|
+
try {
|
|
41
|
+
const mat = mesh.material;
|
|
42
|
+
if (mat?.color?.getHexString)
|
|
43
|
+
color = '#' + mat.color.getHexString();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* color extraction is best-effort */
|
|
47
|
+
}
|
|
48
|
+
if (geom || color) {
|
|
49
|
+
const parts = [geom, color].filter(Boolean).join(' ');
|
|
50
|
+
return `${ctor}<${parts}>`;
|
|
51
|
+
}
|
|
52
|
+
return ctor;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Walk the scene tree and return the first Mesh whose source tag matches
|
|
56
|
+
* the given frame (same file + line). Used by inspect-mode persistence to
|
|
57
|
+
* restore the selection across full-reload — the Mesh object identity
|
|
58
|
+
* changes after reload but the source location is stable.
|
|
59
|
+
*/
|
|
60
|
+
export function findMeshBySource(scene, source) {
|
|
61
|
+
if (!source?.file)
|
|
62
|
+
return null;
|
|
63
|
+
let match = null;
|
|
64
|
+
scene.traverse?.((obj) => {
|
|
65
|
+
if (match)
|
|
66
|
+
return;
|
|
67
|
+
const tag = obj.userData?.__tris;
|
|
68
|
+
if (!tag?.source)
|
|
69
|
+
return;
|
|
70
|
+
if (tag.source.file === source.file && tag.source.line === source.line) {
|
|
71
|
+
match = obj;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return match;
|
|
75
|
+
}
|
|
76
|
+
/** Parse the URL for inspect activation. Returns null when off. */
|
|
77
|
+
export function readInspectFromUrl(elementName) {
|
|
78
|
+
if (typeof window === 'undefined')
|
|
79
|
+
return null;
|
|
80
|
+
const params = new URLSearchParams(window.location.search);
|
|
81
|
+
const inspectParam = params.get('inspect');
|
|
82
|
+
if (inspectParam == null)
|
|
83
|
+
return null;
|
|
84
|
+
// `?inspect`, `?inspect=1`, `?inspect=<el>` all activate.
|
|
85
|
+
// `?inspect=<el>` activates only when the el matches our element.
|
|
86
|
+
if (inspectParam === '' || inspectParam === '1' || inspectParam === elementName) {
|
|
87
|
+
return { camera: params.get('camera') ?? undefined };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
export function createInspectMode(init) {
|
|
92
|
+
const cameraName = init.cameraName ?? Object.keys(init.cameras)[0];
|
|
93
|
+
const camera = init.cameras[cameraName];
|
|
94
|
+
if (!camera) {
|
|
95
|
+
return {
|
|
96
|
+
active: false,
|
|
97
|
+
cameraName: null,
|
|
98
|
+
camera: null,
|
|
99
|
+
render: () => { },
|
|
100
|
+
state: () => ({ hover: null, selection: null, selections: [] }),
|
|
101
|
+
dispose: () => { },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// OrbitControls — right-button rotates per user request, left-button is
|
|
105
|
+
// reserved for picking (we wire click → raycast below). Scroll zooms.
|
|
106
|
+
const controls = new OrbitControls(camera, init.canvas);
|
|
107
|
+
controls.enablePan = true;
|
|
108
|
+
controls.enableDamping = true;
|
|
109
|
+
controls.dampingFactor = 0.08;
|
|
110
|
+
controls.mouseButtons = {
|
|
111
|
+
LEFT: null, // we handle left clicks for picking
|
|
112
|
+
MIDDLE: THREE.MOUSE.PAN,
|
|
113
|
+
RIGHT: THREE.MOUSE.ROTATE,
|
|
114
|
+
};
|
|
115
|
+
// OrbitControls' target defaults to (0,0,0); use the camera's declared target.
|
|
116
|
+
const t = camera.userData?.target;
|
|
117
|
+
if (Array.isArray(t) && t.length === 3)
|
|
118
|
+
controls.target.set(t[0], t[1], t[2]);
|
|
119
|
+
controls.update();
|
|
120
|
+
// Highlight overlay: one wireframe mesh whose geometry is swapped to
|
|
121
|
+
// match the hovered/selected mesh. Bright green for hover, bright cyan
|
|
122
|
+
// for the persistent click-selection. `raycast` is a no-op so the
|
|
123
|
+
// overlay never grabs subsequent picks.
|
|
124
|
+
const hoverMat = new THREE.MeshBasicNodeMaterial({
|
|
125
|
+
color: 0x66ff66,
|
|
126
|
+
wireframe: true,
|
|
127
|
+
transparent: true,
|
|
128
|
+
opacity: 0.9,
|
|
129
|
+
depthTest: false,
|
|
130
|
+
});
|
|
131
|
+
const selectMat = new THREE.MeshBasicNodeMaterial({
|
|
132
|
+
color: 0x00ffff,
|
|
133
|
+
wireframe: true,
|
|
134
|
+
transparent: true,
|
|
135
|
+
opacity: 0.95,
|
|
136
|
+
depthTest: false,
|
|
137
|
+
});
|
|
138
|
+
const overlay = new THREE.Mesh(new THREE.BufferGeometry(), hoverMat);
|
|
139
|
+
overlay.renderOrder = 999;
|
|
140
|
+
overlay.frustumCulled = false;
|
|
141
|
+
overlay.visible = false;
|
|
142
|
+
overlay.raycast = () => { }; // ignore in picking
|
|
143
|
+
init.scene.add(overlay);
|
|
144
|
+
const selectOverlay = new THREE.Mesh(new THREE.BufferGeometry(), selectMat);
|
|
145
|
+
selectOverlay.renderOrder = 1000;
|
|
146
|
+
selectOverlay.frustumCulled = false;
|
|
147
|
+
selectOverlay.visible = false;
|
|
148
|
+
selectOverlay.raycast = () => { };
|
|
149
|
+
init.scene.add(selectOverlay);
|
|
150
|
+
const raycaster = new THREE.Raycaster();
|
|
151
|
+
const ndc = new THREE.Vector2();
|
|
152
|
+
let hoverHit = null;
|
|
153
|
+
let selectionHit = null;
|
|
154
|
+
let hoveredObject = null;
|
|
155
|
+
let selectedObject = null;
|
|
156
|
+
// Pending restore-poller handle (a chain of setTimeouts) + a disposed guard so
|
|
157
|
+
// dispose() can cancel it — otherwise it keeps writing selection state (and an
|
|
158
|
+
// overlay borrowing now-freed geometry) for up to ~3s after teardown.
|
|
159
|
+
let restoreTimer = null;
|
|
160
|
+
let disposed = false;
|
|
161
|
+
// Multi-select state. selectionsByUuid maps uuid → selection. extraOverlays
|
|
162
|
+
// maps uuid → its wireframe overlay Mesh (the primary cyan overlay is
|
|
163
|
+
// reserved for the most-recently-clicked item, kept in sync with
|
|
164
|
+
// selectedObject). Built up via Shift+click; plain click resets.
|
|
165
|
+
const selectionsByUuid = new Map();
|
|
166
|
+
const extraOverlaysByUuid = new Map();
|
|
167
|
+
const extraOverlayMat = new THREE.MeshBasicNodeMaterial({
|
|
168
|
+
color: 0x00ccff,
|
|
169
|
+
wireframe: true,
|
|
170
|
+
transparent: true,
|
|
171
|
+
opacity: 0.75,
|
|
172
|
+
depthTest: false,
|
|
173
|
+
});
|
|
174
|
+
function eventToNdc(ev) {
|
|
175
|
+
const rect = init.canvas.getBoundingClientRect();
|
|
176
|
+
ndc.x = ((ev.clientX - rect.left) / rect.width) * 2 - 1;
|
|
177
|
+
ndc.y = -((ev.clientY - rect.top) / rect.height) * 2 + 1;
|
|
178
|
+
}
|
|
179
|
+
function pick() {
|
|
180
|
+
raycaster.setFromCamera(ndc, camera);
|
|
181
|
+
const hits = raycaster.intersectObjects(init.scene.children, true);
|
|
182
|
+
// Filter out overlays + lights + non-mesh helpers.
|
|
183
|
+
for (const h of hits) {
|
|
184
|
+
if (h.object === overlay || h.object === selectOverlay)
|
|
185
|
+
continue;
|
|
186
|
+
if (!h.object.isMesh)
|
|
187
|
+
continue;
|
|
188
|
+
return { obj: h.object, distance: h.distance, point: h.point };
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
function selectionFrom(hit) {
|
|
193
|
+
const tag = hit.obj.userData?.__tris ?? null;
|
|
194
|
+
return {
|
|
195
|
+
camera: cameraName,
|
|
196
|
+
point: [hit.point.x, hit.point.y, hit.point.z],
|
|
197
|
+
distance: +hit.distance.toFixed(3),
|
|
198
|
+
source: tag?.source ?? null,
|
|
199
|
+
stack: tag?.stack ?? [],
|
|
200
|
+
type: tag?.type ?? hit.obj.constructor.name,
|
|
201
|
+
geometry: tag?.geometry,
|
|
202
|
+
material: tag?.material,
|
|
203
|
+
name: tag?.name ?? hit.obj.name ?? undefined,
|
|
204
|
+
parentChain: buildParentChain(hit.obj),
|
|
205
|
+
uuid: hit.obj.uuid,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function syncOverlayTo(target, which) {
|
|
209
|
+
which.geometry = target.geometry;
|
|
210
|
+
target.updateMatrixWorld(true);
|
|
211
|
+
which.matrix.copy(target.matrixWorld);
|
|
212
|
+
which.matrixAutoUpdate = false;
|
|
213
|
+
which.visible = true;
|
|
214
|
+
}
|
|
215
|
+
// mousemove → hover (RAF-throttled so we don't raycast 1000x/s).
|
|
216
|
+
let pendingHover = false;
|
|
217
|
+
function onMouseMove(ev) {
|
|
218
|
+
eventToNdc(ev);
|
|
219
|
+
if (pendingHover)
|
|
220
|
+
return;
|
|
221
|
+
pendingHover = true;
|
|
222
|
+
requestAnimationFrame(() => {
|
|
223
|
+
pendingHover = false;
|
|
224
|
+
const hit = pick();
|
|
225
|
+
if (!hit) {
|
|
226
|
+
if (hoveredObject) {
|
|
227
|
+
hoveredObject = null;
|
|
228
|
+
hoverHit = null;
|
|
229
|
+
overlay.visible = false;
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (hit.obj === hoveredObject)
|
|
234
|
+
return;
|
|
235
|
+
hoveredObject = hit.obj;
|
|
236
|
+
hoverHit = selectionFrom(hit);
|
|
237
|
+
syncOverlayTo(hit.obj, overlay);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function clearMultiOverlays() {
|
|
241
|
+
for (const ov of extraOverlaysByUuid.values())
|
|
242
|
+
init.scene.remove(ov);
|
|
243
|
+
extraOverlaysByUuid.clear();
|
|
244
|
+
selectionsByUuid.clear();
|
|
245
|
+
}
|
|
246
|
+
function ensureMultiOverlay(uuid, target) {
|
|
247
|
+
if (extraOverlaysByUuid.has(uuid)) {
|
|
248
|
+
// Already overlaid; just re-sync transform in case the mesh moved.
|
|
249
|
+
syncOverlayTo(target, extraOverlaysByUuid.get(uuid));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const ov = new THREE.Mesh(new THREE.BufferGeometry(), extraOverlayMat);
|
|
253
|
+
ov.renderOrder = 1000;
|
|
254
|
+
ov.frustumCulled = false;
|
|
255
|
+
ov.raycast = () => { };
|
|
256
|
+
init.scene.add(ov);
|
|
257
|
+
extraOverlaysByUuid.set(uuid, ov);
|
|
258
|
+
syncOverlayTo(target, ov);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Best-effort clipboard write — silently ignores rejections on browsers
|
|
262
|
+
* that gate navigator.clipboard behind a user-activation check (it's
|
|
263
|
+
* fine here because we're called from a click event handler).
|
|
264
|
+
*
|
|
265
|
+
* Format is rich enough to grep with when source.line drifts (which it
|
|
266
|
+
* does, because Error.stack in browsers reports positions in the
|
|
267
|
+
* vite-served file, not source-mapped originals). Example output:
|
|
268
|
+
* PirateShipMesh.ts:1599 — Mesh<PlaneGeometry #e6dcc0> @ (4.2,8.1,0.3) chain=Scene>pirate.ship>mainmast>Mesh<PlaneGeometry #e6dcc0>
|
|
269
|
+
* The user can paste this into chat or `rg` and find the real call
|
|
270
|
+
* site even if the line number is off by 100 lines.
|
|
271
|
+
*/
|
|
272
|
+
function copySelection(sel) {
|
|
273
|
+
const src = sel.source;
|
|
274
|
+
const fileLine = src ? `${src.file.split('/').slice(-1)[0]}:${src.line}` : `(uuid=${sel.uuid})`;
|
|
275
|
+
const desc = sel.name ?? `${sel.type}<${[sel.geometry, sel.material?.color].filter(Boolean).join(' ')}>`;
|
|
276
|
+
const pos = `(${sel.point.map((n) => n.toFixed(2)).join(',')})`;
|
|
277
|
+
const chain = sel.parentChain?.length ? ` chain=${sel.parentChain.join('>')}` : '';
|
|
278
|
+
const text = `${fileLine} — ${desc} @ ${pos}${chain}`;
|
|
279
|
+
try {
|
|
280
|
+
navigator.clipboard?.writeText(text);
|
|
281
|
+
}
|
|
282
|
+
catch { }
|
|
283
|
+
}
|
|
284
|
+
function onMouseDown(ev) {
|
|
285
|
+
if (ev.button !== 0)
|
|
286
|
+
return; // left only
|
|
287
|
+
eventToNdc(ev);
|
|
288
|
+
const hit = pick();
|
|
289
|
+
if (!hit) {
|
|
290
|
+
// Plain background click clears everything; Shift+background keeps
|
|
291
|
+
// the current multi-set so the user can de-target a stray click.
|
|
292
|
+
if (!ev.shiftKey) {
|
|
293
|
+
selectionHit = null;
|
|
294
|
+
selectedObject = null;
|
|
295
|
+
selectOverlay.visible = false;
|
|
296
|
+
clearMultiOverlays();
|
|
297
|
+
init.onSelectionChange(null, []);
|
|
298
|
+
try {
|
|
299
|
+
window.localStorage.removeItem(STORAGE_KEY);
|
|
300
|
+
}
|
|
301
|
+
catch { }
|
|
302
|
+
}
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const sel = selectionFrom(hit);
|
|
306
|
+
if (!ev.shiftKey) {
|
|
307
|
+
// Plain click: replace set with this one item.
|
|
308
|
+
clearMultiOverlays();
|
|
309
|
+
}
|
|
310
|
+
else if (selectedObject && selectionHit) {
|
|
311
|
+
// Shift+click on a NEW mesh: the previous "primary" is about to lose
|
|
312
|
+
// the primary overlay (which will swap to the new mesh). Give the
|
|
313
|
+
// old primary its own multi-overlay so it stays visible. Without
|
|
314
|
+
// this the user sees only the latest pick, not the accumulated set.
|
|
315
|
+
ensureMultiOverlay(selectionHit.uuid, selectedObject);
|
|
316
|
+
}
|
|
317
|
+
selectedObject = hit.obj;
|
|
318
|
+
selectionHit = sel;
|
|
319
|
+
syncOverlayTo(hit.obj, selectOverlay);
|
|
320
|
+
selectionsByUuid.set(sel.uuid, sel);
|
|
321
|
+
init.onSelectionChange(sel, [...selectionsByUuid.values()]);
|
|
322
|
+
copySelection(sel);
|
|
323
|
+
try {
|
|
324
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(sel));
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
327
|
+
}
|
|
328
|
+
init.canvas.addEventListener('mousemove', onMouseMove);
|
|
329
|
+
init.canvas.addEventListener('mousedown', onMouseDown);
|
|
330
|
+
// Restore last selection across full-reload (vite force-reload on
|
|
331
|
+
// shader edits). Match by source frame (file:line) — the actual Mesh
|
|
332
|
+
// object is new after reload but the source location is stable.
|
|
333
|
+
//
|
|
334
|
+
// Element mounting may happen across many frames (TSL pipeline init,
|
|
335
|
+
// async texture loads, RAF-driven sub-mesh adds). Poll up to ~3 s
|
|
336
|
+
// looking for the stored source in the scene tree, so we don't give
|
|
337
|
+
// up before the element has finished assembling itself.
|
|
338
|
+
const STORAGE_KEY = `triscope:selection:${init.elementName}`;
|
|
339
|
+
try {
|
|
340
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
341
|
+
if (raw) {
|
|
342
|
+
const stored = JSON.parse(raw);
|
|
343
|
+
let attempts = 0;
|
|
344
|
+
const maxAttempts = 30; // 30 × 100ms ≈ 3s
|
|
345
|
+
const tryRestore = () => {
|
|
346
|
+
if (disposed)
|
|
347
|
+
return;
|
|
348
|
+
attempts += 1;
|
|
349
|
+
try {
|
|
350
|
+
const target = findMeshBySource(init.scene, stored.source);
|
|
351
|
+
if (target) {
|
|
352
|
+
selectedObject = target;
|
|
353
|
+
selectionHit = selectionFrom({
|
|
354
|
+
obj: target,
|
|
355
|
+
distance: stored.distance,
|
|
356
|
+
point: new THREE.Vector3(...stored.point),
|
|
357
|
+
});
|
|
358
|
+
syncOverlayTo(target, selectOverlay);
|
|
359
|
+
selectionsByUuid.set(selectionHit.uuid, selectionHit);
|
|
360
|
+
init.onSelectionChange(selectionHit, [...selectionsByUuid.values()]);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
/* corrupt stored selection — drop it */
|
|
366
|
+
try {
|
|
367
|
+
window.localStorage.removeItem(STORAGE_KEY);
|
|
368
|
+
}
|
|
369
|
+
catch { }
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (attempts < maxAttempts)
|
|
373
|
+
restoreTimer = setTimeout(tryRestore, 100);
|
|
374
|
+
};
|
|
375
|
+
restoreTimer = setTimeout(tryRestore, 100);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
/* localStorage unavailable — silent */
|
|
380
|
+
}
|
|
381
|
+
function render() {
|
|
382
|
+
controls.update();
|
|
383
|
+
// Keep overlays glued to their target's current world matrix in case
|
|
384
|
+
// the underlying mesh moved (animation, knob change).
|
|
385
|
+
if (selectedObject && selectionHit) {
|
|
386
|
+
selectedObject.updateMatrixWorld(true);
|
|
387
|
+
selectOverlay.matrix.copy(selectedObject.matrixWorld);
|
|
388
|
+
}
|
|
389
|
+
if (hoveredObject) {
|
|
390
|
+
hoveredObject.updateMatrixWorld(true);
|
|
391
|
+
overlay.matrix.copy(hoveredObject.matrixWorld);
|
|
392
|
+
}
|
|
393
|
+
init.renderer.setScissorTest(false);
|
|
394
|
+
const w = init.renderer.domElement.width / init.renderer.getPixelRatio();
|
|
395
|
+
const h = init.renderer.domElement.height / init.renderer.getPixelRatio();
|
|
396
|
+
init.renderer.setViewport(0, 0, w, h);
|
|
397
|
+
camera.aspect = w / Math.max(h, 1);
|
|
398
|
+
camera.updateProjectionMatrix();
|
|
399
|
+
init.renderer.clear();
|
|
400
|
+
init.renderer.render(init.scene, camera);
|
|
401
|
+
}
|
|
402
|
+
function dispose() {
|
|
403
|
+
disposed = true;
|
|
404
|
+
if (restoreTimer) {
|
|
405
|
+
clearTimeout(restoreTimer);
|
|
406
|
+
restoreTimer = null;
|
|
407
|
+
}
|
|
408
|
+
init.canvas.removeEventListener('mousemove', onMouseMove);
|
|
409
|
+
init.canvas.removeEventListener('mousedown', onMouseDown);
|
|
410
|
+
// Remove every overlay from the shared scene + drop the borrowed geometry
|
|
411
|
+
// references so a disposed element's freed BufferGeometry can be GC'd.
|
|
412
|
+
init.scene.remove(overlay);
|
|
413
|
+
init.scene.remove(selectOverlay);
|
|
414
|
+
overlay.geometry = new THREE.BufferGeometry();
|
|
415
|
+
selectOverlay.geometry = new THREE.BufferGeometry();
|
|
416
|
+
clearMultiOverlays();
|
|
417
|
+
hoveredObject = null;
|
|
418
|
+
selectedObject = null;
|
|
419
|
+
controls.dispose();
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
active: true,
|
|
423
|
+
cameraName,
|
|
424
|
+
camera,
|
|
425
|
+
render,
|
|
426
|
+
state: () => ({
|
|
427
|
+
hover: hoverHit,
|
|
428
|
+
selection: selectionHit,
|
|
429
|
+
selections: [...selectionsByUuid.values()],
|
|
430
|
+
}),
|
|
431
|
+
dispose,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.js","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Knob } from './types.js';
|
|
2
|
+
/** Result of clamping a candidate knob value against its spec. */
|
|
3
|
+
export interface ClampResult {
|
|
4
|
+
/** True when the final value differs from what the caller asked for. */
|
|
5
|
+
clamped: boolean;
|
|
6
|
+
/** The value actually safe to apply. */
|
|
7
|
+
final: number | string | boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Coerce + clamp a candidate knob value to what its spec actually allows, so a
|
|
11
|
+
* stray `set_knob('windPressure', 9999)` can't silently corrupt the scene.
|
|
12
|
+
* Returns both the safe `final` value and whether it had to be changed, so the
|
|
13
|
+
* caller (harness / MCP) can tell the agent its input was out of range.
|
|
14
|
+
*
|
|
15
|
+
* number → coerce to finite, snap to `step` (if any), clamp to [min, max]
|
|
16
|
+
* int → coerce, round, clamp to [min, max]
|
|
17
|
+
* color → keep valid #rgb / #rrggbb, else fall back to the spec default
|
|
18
|
+
* boolean → coerce truthiness
|
|
19
|
+
* trigger → pass the pulse through untouched (no persisted value)
|
|
20
|
+
*/
|
|
21
|
+
export declare function clampKnob(spec: Knob, value: number | string | boolean): ClampResult;
|
|
22
|
+
//# sourceMappingURL=knob-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knob-utils.d.ts","sourceRoot":"","sources":["../src/knob-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAClC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CA+BnF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const HEX_COLOR = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
2
|
+
/**
|
|
3
|
+
* Coerce + clamp a candidate knob value to what its spec actually allows, so a
|
|
4
|
+
* stray `set_knob('windPressure', 9999)` can't silently corrupt the scene.
|
|
5
|
+
* Returns both the safe `final` value and whether it had to be changed, so the
|
|
6
|
+
* caller (harness / MCP) can tell the agent its input was out of range.
|
|
7
|
+
*
|
|
8
|
+
* number → coerce to finite, snap to `step` (if any), clamp to [min, max]
|
|
9
|
+
* int → coerce, round, clamp to [min, max]
|
|
10
|
+
* color → keep valid #rgb / #rrggbb, else fall back to the spec default
|
|
11
|
+
* boolean → coerce truthiness
|
|
12
|
+
* trigger → pass the pulse through untouched (no persisted value)
|
|
13
|
+
*/
|
|
14
|
+
export function clampKnob(spec, value) {
|
|
15
|
+
switch (spec.type) {
|
|
16
|
+
case 'number': {
|
|
17
|
+
const n = Number(value);
|
|
18
|
+
if (!Number.isFinite(n))
|
|
19
|
+
return { clamped: true, final: spec.default };
|
|
20
|
+
let next = n;
|
|
21
|
+
if (spec.step && spec.step > 0) {
|
|
22
|
+
next = spec.min + Math.round((next - spec.min) / spec.step) * spec.step;
|
|
23
|
+
}
|
|
24
|
+
next = Math.min(spec.max, Math.max(spec.min, next));
|
|
25
|
+
return { clamped: !nearlyEqual(next, n), final: next };
|
|
26
|
+
}
|
|
27
|
+
case 'int': {
|
|
28
|
+
const n = Number(value);
|
|
29
|
+
if (!Number.isFinite(n))
|
|
30
|
+
return { clamped: true, final: spec.default };
|
|
31
|
+
const next = Math.min(spec.max, Math.max(spec.min, Math.round(n)));
|
|
32
|
+
return { clamped: next !== n, final: next };
|
|
33
|
+
}
|
|
34
|
+
case 'color': {
|
|
35
|
+
if (typeof value === 'string' && HEX_COLOR.test(value)) {
|
|
36
|
+
return { clamped: false, final: value };
|
|
37
|
+
}
|
|
38
|
+
return { clamped: true, final: spec.default };
|
|
39
|
+
}
|
|
40
|
+
case 'boolean': {
|
|
41
|
+
const next = Boolean(value);
|
|
42
|
+
return { clamped: typeof value !== 'boolean', final: next };
|
|
43
|
+
}
|
|
44
|
+
case 'trigger':
|
|
45
|
+
return { clamped: false, final: value };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function nearlyEqual(a, b) {
|
|
49
|
+
return Math.abs(a - b) < 1e-9;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=knob-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knob-utils.js","sourceRoot":"","sources":["../src/knob-utils.ts"],"names":[],"mappings":"AAUA,MAAM,SAAS,GAAG,sCAAsC,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,IAAU,EAAE,KAAgC;IACpE,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;YACvE,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC/B,IAAI,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;YAC1E,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzD,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;YACvE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnE,OAAO,EAAE,OAAO,EAAE,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC9C,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC1C,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAChD,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC9D,CAAC;QACD,KAAK,SAAS;YACZ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,CAAS,EAAE,CAAS;IACvC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lab UI CSS, exported as a template string so consumers can inject it
|
|
3
|
+
* once via `mountLabDom()` instead of duplicating it in every per-element
|
|
4
|
+
* lab HTML page.
|
|
5
|
+
*
|
|
6
|
+
* Covers: full-bleed canvas, camera-label tags, HUD strip, boot overlay,
|
|
7
|
+
* knob-editor pane. The selectors match the DOM that `mountLabDom()`
|
|
8
|
+
* creates and that `runLab()` / `mountEditor()` populate.
|
|
9
|
+
*/
|
|
10
|
+
export declare const LAB_CSS = "\nhtml, body { margin: 0; padding: 0; overflow: hidden; background: #000; color: #cfd6db; font-family: ui-monospace, monospace; }\n#app { position: fixed; inset: 0; }\ncanvas { display: block; width: 100%; height: 100%; }\n\n.triscope-label, .label {\n position: absolute;\n background: rgba(0,0,0,0.55);\n color: #cfd6db;\n padding: 4px 8px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.05em;\n pointer-events: none;\n border-radius: 3px;\n z-index: 5;\n}\n\n#hud { position: fixed; bottom: 8px; left: 8px; z-index: 10; font-size: 11px; padding: 4px 8px; background: rgba(0,0,0,0.55); border-radius: 3px; }\n#boot { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: #0a1a20; color: #cfd6db; font-size: 14px; z-index: 50; }\n\n#lab-controls {\n position: fixed;\n right: 8px;\n bottom: 8px;\n z-index: 20;\n width: min(320px, calc(100vw - 24px));\n padding: 10px 12px;\n background: rgba(5, 12, 16, 0.78);\n border: 1px solid rgba(210, 230, 240, 0.16);\n border-radius: 6px;\n backdrop-filter: blur(8px);\n box-sizing: border-box;\n}\n.triscope-editor__row {\n display: grid;\n grid-template-columns: 110px 1fr 56px;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: #cfd6db;\n margin: 4px 0;\n}\n.triscope-editor__row label { font-size: 10px; opacity: 0.85; }\n.triscope-editor__row input { width: 100%; accent-color: #8fc7d9; }\n.triscope-editor__row output { text-align: right; color: #f0f5f7; }\n";
|
|
11
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/lab/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,s+CA8CnB,CAAC"}
|
package/dist/lab/css.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lab UI CSS, exported as a template string so consumers can inject it
|
|
3
|
+
* once via `mountLabDom()` instead of duplicating it in every per-element
|
|
4
|
+
* lab HTML page.
|
|
5
|
+
*
|
|
6
|
+
* Covers: full-bleed canvas, camera-label tags, HUD strip, boot overlay,
|
|
7
|
+
* knob-editor pane. The selectors match the DOM that `mountLabDom()`
|
|
8
|
+
* creates and that `runLab()` / `mountEditor()` populate.
|
|
9
|
+
*/
|
|
10
|
+
export const LAB_CSS = `
|
|
11
|
+
html, body { margin: 0; padding: 0; overflow: hidden; background: #000; color: #cfd6db; font-family: ui-monospace, monospace; }
|
|
12
|
+
#app { position: fixed; inset: 0; }
|
|
13
|
+
canvas { display: block; width: 100%; height: 100%; }
|
|
14
|
+
|
|
15
|
+
.triscope-label, .label {
|
|
16
|
+
position: absolute;
|
|
17
|
+
background: rgba(0,0,0,0.55);
|
|
18
|
+
color: #cfd6db;
|
|
19
|
+
padding: 4px 8px;
|
|
20
|
+
font-size: 11px;
|
|
21
|
+
font-weight: 600;
|
|
22
|
+
letter-spacing: 0.05em;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
border-radius: 3px;
|
|
25
|
+
z-index: 5;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#hud { position: fixed; bottom: 8px; left: 8px; z-index: 10; font-size: 11px; padding: 4px 8px; background: rgba(0,0,0,0.55); border-radius: 3px; }
|
|
29
|
+
#boot { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: #0a1a20; color: #cfd6db; font-size: 14px; z-index: 50; }
|
|
30
|
+
|
|
31
|
+
#lab-controls {
|
|
32
|
+
position: fixed;
|
|
33
|
+
right: 8px;
|
|
34
|
+
bottom: 8px;
|
|
35
|
+
z-index: 20;
|
|
36
|
+
width: min(320px, calc(100vw - 24px));
|
|
37
|
+
padding: 10px 12px;
|
|
38
|
+
background: rgba(5, 12, 16, 0.78);
|
|
39
|
+
border: 1px solid rgba(210, 230, 240, 0.16);
|
|
40
|
+
border-radius: 6px;
|
|
41
|
+
backdrop-filter: blur(8px);
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
}
|
|
44
|
+
.triscope-editor__row {
|
|
45
|
+
display: grid;
|
|
46
|
+
grid-template-columns: 110px 1fr 56px;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 6px;
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
color: #cfd6db;
|
|
51
|
+
margin: 4px 0;
|
|
52
|
+
}
|
|
53
|
+
.triscope-editor__row label { font-size: 10px; opacity: 0.85; }
|
|
54
|
+
.triscope-editor__row input { width: 100%; accent-color: #8fc7d9; }
|
|
55
|
+
.triscope-editor__row output { text-align: right; color: #f0f5f7; }
|
|
56
|
+
`;
|
|
57
|
+
//# sourceMappingURL=css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/lab/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CtB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LabDomRefs {
|
|
2
|
+
canvas: HTMLCanvasElement;
|
|
3
|
+
hud: HTMLElement;
|
|
4
|
+
boot: HTMLElement;
|
|
5
|
+
labelContainer: HTMLElement;
|
|
6
|
+
editorContainer: HTMLElement;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build (or reuse) the standard lab DOM and return the refs `runLab()`
|
|
10
|
+
* needs. Idempotent: calling twice does not duplicate nodes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mountLabDom(): LabDomRefs;
|
|
13
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/lab/dom.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,EAAE,WAAW,CAAC;IAC5B,eAAe,EAAE,WAAW,CAAC;CAC9B;AAsBD;;;GAGG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAiCxC"}
|