@vizij/render 1.0.0 → 2.0.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/dist/index.js CHANGED
@@ -32,15 +32,16 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  DEFAULT_TONE_MAPPING_MODE: () => DEFAULT_TONE_MAPPING_MODE,
34
34
  EmptyModelError: () => EmptyModelError,
35
- InnerVizij: () => InnerVizij,
36
35
  ShapeMaterial: () => ShapeMaterial,
37
36
  TONE_MAPPING_LABELS: () => TONE_MAPPING_LABELS,
38
37
  TONE_MAPPING_MODES: () => TONE_MAPPING_MODES,
39
38
  Vizij: () => Vizij,
39
+ VizijCanvas: () => VizijCanvas,
40
40
  VizijContext: () => VizijContext,
41
41
  VizijSlice: () => VizijSlice,
42
42
  applyVizijBundle: () => applyVizijBundle,
43
43
  buildSceneGraph: () => buildSceneGraph,
44
+ canvasMounted: () => canvasMounted,
44
45
  createVizijStore: () => createVizijStore,
45
46
  deriveMorphFeatureKeys: () => deriveMorphFeatureKeys,
46
47
  deriveRootBounds: () => deriveRootBounds,
@@ -55,10 +56,9 @@ __export(index_exports, {
55
56
  parseGlbJsonChunk: () => parseGlbJsonChunk,
56
57
  patchGlbJson: () => patchGlbJson,
57
58
  patchVizijBundleMetadata: () => patchVizijBundleMetadata,
58
- resolveToneMapping: () => resolveToneMapping,
59
59
  sanitizeMorphKey: () => sanitizeMorphKey,
60
+ setVizijProgress: () => setVizijProgress,
60
61
  useDefaultVizijStore: () => useDefaultVizijStore,
61
- useFeatures: () => useFeatures,
62
62
  useVizijStore: () => useVizijStore,
63
63
  useVizijStoreGetter: () => useVizijStoreGetter,
64
64
  useVizijStoreSetter: () => useVizijStoreSetter,
@@ -66,1228 +66,548 @@ __export(index_exports, {
66
66
  });
67
67
  module.exports = __toCommonJS(index_exports);
68
68
 
69
- // src/vizij.tsx
70
- var import_react10 = require("react");
71
- var import_react_error_boundary = require("react-error-boundary");
72
- var import_three3 = require("three");
73
- var import_fiber2 = require("@react-three/fiber");
74
- var import_drei3 = require("@react-three/drei");
75
- var import_shallow8 = require("zustand/react/shallow");
76
-
77
- // src/renderables/renderable.tsx
78
- var import_react8 = require("react");
79
- var import_shallow6 = require("zustand/react/shallow");
80
-
81
- // src/hooks/use-vizij-store.ts
82
- var import_react2 = require("react");
83
- var import_zustand = require("zustand");
84
-
85
- // src/context.ts
69
+ // src/page/canvas.tsx
86
70
  var import_react = require("react");
87
- var VizijContext = (0, import_react.createContext)(null);
71
+ var import_runtime = require("@vizij/runtime");
88
72
 
89
- // src/hooks/use-vizij-store.ts
90
- function useVizijStore(selector) {
91
- const store = (0, import_react2.useContext)(VizijContext);
92
- if (!store) throw new Error("Missing VizijProvider in the tree");
93
- return (0, import_zustand.useStore)(store, selector);
73
+ // src/page/progress.ts
74
+ var runtime = {
75
+ stage: "download",
76
+ loaded: 0,
77
+ total: null
78
+ };
79
+ var faces = /* @__PURE__ */ new Map();
80
+ var watchers = /* @__PURE__ */ new Set();
81
+ function setVizijProgress(vizijId, progress) {
82
+ if (progress === null) {
83
+ faces.delete(vizijId);
84
+ } else {
85
+ const face = faces.get(vizijId);
86
+ if (face) face.progress = progress;
87
+ else faces.set(vizijId, { progress, drawn: false });
88
+ }
89
+ notify();
90
+ }
91
+ function setRuntimeStage(stage) {
92
+ runtime = stage;
93
+ notify();
94
+ }
95
+ function markDrawn(vizijId) {
96
+ const face = faces.get(vizijId);
97
+ if (face?.drawn) return;
98
+ if (face) face.drawn = true;
99
+ else faces.set(vizijId, { progress: null, drawn: true });
100
+ notify();
101
+ }
102
+ function loadingStage(vizijId) {
103
+ const face = faces.get(vizijId);
104
+ if (face?.drawn) return null;
105
+ if (runtime?.stage === "failed") return runtime;
106
+ if (face?.progress?.stage === "failed") return face.progress;
107
+ if (runtime !== null) return runtime;
108
+ return face?.progress ?? { stage: "load" };
109
+ }
110
+ function watchLoading(vizijId, listener) {
111
+ const watcher = { vizijId, listener, last: loadingStage(vizijId) };
112
+ watchers.add(watcher);
113
+ listener(watcher.last);
114
+ return () => {
115
+ watchers.delete(watcher);
116
+ };
94
117
  }
95
-
96
- // src/renderables/group.tsx
97
- var import_react4 = require("react");
98
- var THREE = __toESM(require("three"));
99
- var import_shallow2 = require("zustand/react/shallow");
100
- var import_utils2 = require("@vizij/utils");
101
-
102
- // src/hooks/use-features.ts
103
- var import_react3 = require("react");
104
- var import_shallow = require("zustand/shallow");
105
- var import_utils = require("@vizij/utils");
106
- function useFeatures(namespace, features, callbacks, debugInfo) {
107
- const store = (0, import_react3.useContext)(VizijContext);
108
- if (!store) throw new Error("Missing VizijProvider in the tree");
109
- (0, import_react3.useEffect)(() => {
110
- const unsubsribes = [];
111
- Object.keys(callbacks).forEach((key) => {
112
- if (!(key in features)) {
113
- return;
114
- }
115
- const featureInfo = features[key];
116
- if (!featureInfo.animated) {
117
- callbacks[key](featureInfo.value);
118
- } else {
119
- const animatableValueInfo = store.getState().animatables[featureInfo.value];
120
- if (!animatableValueInfo) {
121
- console.error(
122
- `Feature with id ${key} is animated but the animated value with id ${featureInfo.value} is not found`,
123
- debugInfo
124
- );
125
- return;
126
- }
127
- const cb = (value) => {
128
- if (value === void 0) {
129
- callbacks[key](animatableValueInfo.default);
130
- } else {
131
- callbacks[key](value);
132
- }
133
- };
134
- const defaultValue = animatableValueInfo.default;
135
- const lookupKey = (0, import_utils.getLookup)(namespace, animatableValueInfo.id);
136
- const unsubscribe = store.subscribe(
137
- (state) => state.values.get(lookupKey) ?? defaultValue,
138
- cb,
139
- { equalityFn: import_shallow.shallow, fireImmediately: true }
140
- );
141
- unsubsribes.push(unsubscribe);
142
- }
143
- });
144
- return () => {
145
- unsubsribes.forEach((unsubscribe) => unsubscribe());
146
- };
147
- }, [features, callbacks, store, debugInfo, namespace]);
118
+ function notify() {
119
+ for (const watcher of [...watchers]) {
120
+ const next = loadingStage(watcher.vizijId);
121
+ if (sameStage(next, watcher.last)) continue;
122
+ watcher.last = next;
123
+ watcher.listener(next);
124
+ }
125
+ }
126
+ function sameStage(a, b) {
127
+ if (a === null || b === null) return a === b;
128
+ if (a.stage !== b.stage) return false;
129
+ if ("loaded" in a && "loaded" in b) {
130
+ return a.loaded === b.loaded && a.total === b.total;
131
+ }
132
+ if ("error" in a && "error" in b) return a.error === b.error;
133
+ return true;
148
134
  }
149
135
 
150
- // src/functions/create-stored-features.ts
151
- function createStoredFeatures(objectFeatures, animatableValues) {
152
- const stored = Object.entries(objectFeatures).map(
153
- ([key, feat]) => {
154
- if (feat.animated) {
155
- const storedFeat = {
156
- animated: true,
157
- value: animatableValues[feat.value],
158
- ...feat.label ? { label: feat.label } : {}
159
- };
160
- return [key, storedFeat];
161
- }
162
- const staticFeature = feat;
163
- return [key, staticFeature.label ? { ...staticFeature } : staticFeature];
136
+ // src/page/frame.ts
137
+ function frame(slots2, canvas2, view2) {
138
+ const origin = canvas2.getBoundingClientRect();
139
+ const byId = /* @__PURE__ */ new Map();
140
+ for (const slot of slots2) {
141
+ byId.set(slot.vizijId, slot);
142
+ const rect = relative(slot.element.getBoundingClientRect(), origin);
143
+ if (!sameRect(rect, slot.placed)) {
144
+ view2.placeVizij(slot.vizijId, rect);
145
+ slot.placed = rect;
164
146
  }
165
- );
166
- return Object.fromEntries(stored);
147
+ const ready2 = view2.ready(slot.vizijId);
148
+ if (ready2) markDrawn(slot.vizijId);
149
+ if (slot.element.getAttribute("data-vizij-ready") !== String(ready2)) {
150
+ slot.element.setAttribute("data-vizij-ready", String(ready2));
151
+ }
152
+ if (slot.safeArea) {
153
+ outline(slot.safeArea, view2.safeArea(slot.vizijId), origin);
154
+ }
155
+ }
156
+ for (const pick of view2.drainPicks()) {
157
+ byId.get(pick.vizijId)?.onPick?.(pick);
158
+ }
167
159
  }
168
-
169
- // src/functions/create-stored-data.ts
170
- function createStoredRenderable(data, animatableValues) {
171
- const { geometry: _geometry, ...stored } = data;
172
- const storedFeatures = createStoredFeatures(
173
- data.features,
174
- animatableValues
175
- );
176
- return { ...stored, features: storedFeatures };
160
+ function relative(rect, origin) {
161
+ return {
162
+ x: rect.x - origin.x,
163
+ y: rect.y - origin.y,
164
+ width: rect.width,
165
+ height: rect.height
166
+ };
167
+ }
168
+ function sameRect(a, b) {
169
+ return b !== null && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
170
+ }
171
+ function outline({ outline: outline2, frame: frame2 }, area, canvas2) {
172
+ if (!area) {
173
+ outline2.style.display = "none";
174
+ return;
175
+ }
176
+ const at = relative(frame2.getBoundingClientRect(), canvas2);
177
+ outline2.style.display = "block";
178
+ outline2.style.left = `${area.x - at.x}px`;
179
+ outline2.style.top = `${area.y - at.y}px`;
180
+ outline2.style.width = `${area.width}px`;
181
+ outline2.style.height = `${area.height}px`;
177
182
  }
178
183
 
179
- // src/renderables/group.tsx
184
+ // src/page/canvas.tsx
180
185
  var import_jsx_runtime = require("react/jsx-runtime");
181
- THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
182
- function InnerRenderedGroup({
183
- id,
184
- namespace,
185
- chain
186
+ var canvas = null;
187
+ var mounting = null;
188
+ var live = false;
189
+ var settle;
190
+ var mounted = new Promise((resolve, reject) => {
191
+ settle = { resolve, reject };
192
+ });
193
+ var PAINT_WAIT_MS = 100;
194
+ var slots = /* @__PURE__ */ new Set();
195
+ var hosts = 0;
196
+ var loop = null;
197
+ function canvasMounted() {
198
+ return mounted;
199
+ }
200
+ var DEFAULT_STYLE = {
201
+ position: "fixed",
202
+ inset: 0,
203
+ pointerEvents: "none"
204
+ };
205
+ function VizijCanvas({
206
+ options,
207
+ className,
208
+ style
186
209
  }) {
187
- const ref = (0, import_react4.useRef)(null);
188
- const group = useVizijStore((0, import_shallow2.useShallow)((state) => state.world[id]));
189
- const refIsNull = useVizijStore(
190
- (state) => !state.refs[id]?.[namespace]?.current
191
- );
192
- const animatables = useVizijStore((0, import_shallow2.useShallow)((state) => state.animatables));
193
- const setHoveredElement = useVizijStore(
194
- (0, import_shallow2.useShallow)((state) => state.setHoveredElement)
195
- );
196
- const animatableValues = (0, import_react4.useMemo)(() => {
197
- const av = {};
198
- Object.values(group.features).forEach((feat) => {
199
- if (feat.animated) {
200
- const animatable = animatables[feat.value];
201
- av[animatable.id] = animatable;
202
- }
203
- });
204
- return av;
205
- }, [group.features, animatables]);
206
- const userData = {
207
- gltfExtensions: {
208
- RobotData: createStoredRenderable(group, animatableValues)
209
- }
210
- };
211
- useFeatures(namespace, group.features, {
212
- translation: (pos) => {
213
- if (ref.current?.position && (0, import_utils2.instanceOfRawVector3)(pos)) {
214
- ref.current.position.set(
215
- pos.x,
216
- pos.y,
217
- pos.z
218
- );
219
- } else if (ref.current?.position && (0, import_utils2.instanceOfRawVector2)(pos)) {
220
- const currentZ = ref.current.position.z;
221
- ref.current.position.set(pos.x, pos.y, currentZ);
222
- }
223
- },
224
- rotation: (rot) => {
225
- if (ref.current?.rotation && (0, import_utils2.instanceOfRawEuler)(rot)) {
226
- ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
227
- } else if (ref.current?.rotation && (0, import_utils2.instanceOfRawNumber)(rot)) {
228
- ref.current.rotation.set(0, 0, 0);
229
- ref.current.rotateZ(rot);
230
- }
231
- },
232
- scale: (scale) => {
233
- if (ref.current?.scale && (0, import_utils2.instanceOfRawVector3)(scale)) {
234
- if (scale.x === null || scale.y === null || scale.z === null) {
235
- ref.current.scale.set(0.1, 0.1, 0.1);
236
- return;
237
- }
238
- ref.current.scale.set(scale.x, scale.y, scale.z);
239
- } else if (ref.current && (0, import_utils2.instanceOfRawNumber)(scale)) {
240
- ref.current.scale.set(scale, scale, scale);
241
- } else if (ref.current) {
242
- ref.current.scale.set(1, 1, 1);
243
- }
244
- }
245
- });
246
- const setReference = useVizijStore(
247
- (0, import_shallow2.useShallow)((state) => state.setReference)
248
- );
249
- (0, import_react4.useEffect)(() => {
250
- if (ref.current && refIsNull) {
251
- setReference(group.id, namespace, ref);
252
- }
253
- }, [group.id, namespace, ref, setReference, refIsNull]);
254
- (0, import_react4.useEffect)(() => {
255
- if (ref.current) {
256
- ref.current.name = group.name;
210
+ const host = (0, import_react.useRef)(null);
211
+ const firstOptions = (0, import_react.useRef)(options);
212
+ (0, import_react.useEffect)(() => {
213
+ const element = host.current;
214
+ const shown = pageCanvas();
215
+ element.appendChild(shown);
216
+ if (!mounting) {
217
+ mounting = mountOnce(shown, firstOptions.current);
218
+ mounting.then(settle.resolve, settle.reject);
219
+ }
220
+ hosts += 1;
221
+ if (hosts === 1) {
222
+ void mounting.then(start, () => void 0);
257
223
  }
258
- }, [group.name]);
259
- const handlePointerOver = (0, import_react4.useCallback)(
260
- (event) => {
261
- if (event.eventObject !== event.object) {
262
- return;
224
+ return () => {
225
+ hosts -= 1;
226
+ if (hosts === 0) {
227
+ stop();
263
228
  }
264
- setHoveredElement({ id, namespace, type: "group" });
265
- },
266
- [id, namespace, setHoveredElement]
267
- );
268
- const handlePointerOut = (0, import_react4.useCallback)(
269
- (event) => {
270
- if (event.eventObject !== event.object) {
271
- return;
229
+ if (shown.parentElement === element) {
230
+ element.removeChild(shown);
272
231
  }
273
- setHoveredElement(null);
274
- },
275
- [setHoveredElement]
276
- );
232
+ };
233
+ }, []);
277
234
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
278
- "group",
235
+ "div",
279
236
  {
280
- ref,
281
- uuid: `${namespace}.${group.id}`,
282
- userData,
283
- onPointerOver: handlePointerOver,
284
- onPointerOut: handlePointerOut,
285
- children: group.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
286
- Renderable,
287
- {
288
- id: child,
289
- namespace,
290
- chain: [...chain, id]
291
- },
292
- child
293
- ))
237
+ ref: host,
238
+ className,
239
+ style: style ?? (className ? void 0 : DEFAULT_STYLE)
294
240
  }
295
241
  );
296
242
  }
297
- var RenderedGroup = (0, import_react4.memo)(InnerRenderedGroup);
298
-
299
- // src/renderables/ellipse.tsx
300
- var import_react5 = require("react");
301
- var import_shallow3 = require("zustand/react/shallow");
302
- var import_utils3 = require("@vizij/utils");
303
- var import_drei = require("@react-three/drei");
304
- var import_jsx_runtime2 = require("react/jsx-runtime");
305
- function InnerRenderedEllipse({
306
- id,
307
- namespace,
308
- chain
309
- }) {
310
- const ellipseRef = (0, import_react5.useRef)(null);
311
- const materialRef = (0, import_react5.useRef)(
312
- null
313
- );
314
- const lineRef = (0, import_react5.useRef)(null);
315
- const strokeOffsetRef = (0, import_react5.useRef)(0);
316
- const strokeWidthRef = (0, import_react5.useRef)(0);
317
- const onElementClick = useVizijStore(
318
- (0, import_shallow3.useShallow)((state) => state.onElementClick)
319
- );
320
- const setHoveredElement = useVizijStore(
321
- (0, import_shallow3.useShallow)((state) => state.setHoveredElement)
322
- );
323
- const ellipse = useVizijStore(
324
- (0, import_shallow3.useShallow)((state) => state.world[id])
325
- );
326
- const refIsNull = useVizijStore(
327
- (state) => !state.refs[id]?.[namespace]?.current
328
- );
329
- const animatables = useVizijStore((0, import_shallow3.useShallow)((state) => state.animatables));
330
- const animatableValues = (0, import_react5.useMemo)(() => {
331
- const av = {};
332
- Object.values(ellipse.features).forEach((feat) => {
333
- if (feat.animated) {
334
- const animatable = animatables[feat.value];
335
- av[animatable.id] = animatable;
336
- }
337
- });
338
- return av;
339
- }, [ellipse.features, animatables]);
340
- const selectionData = (0, import_react5.useMemo)(
341
- () => ({ id, namespace, type: "ellipse" }),
342
- [id, namespace]
343
- );
344
- const userData = (0, import_react5.useMemo)(
345
- () => ({
346
- gltfExtensions: {
347
- RobotData: createStoredRenderable(ellipse, animatableValues)
348
- },
349
- selection: selectionData
350
- }),
351
- [ellipse, animatableValues, selectionData]
352
- );
353
- useFeatures(namespace, ellipse.features, {
354
- translation: (pos) => {
355
- if (ellipseRef.current?.position && (0, import_utils3.instanceOfRawVector3)(pos)) {
356
- ellipseRef.current.position.set(
357
- pos.x,
358
- pos.y,
359
- pos.z
360
- );
361
- } else if (ellipseRef.current?.position && (0, import_utils3.instanceOfRawVector2)(pos)) {
362
- const currentZ = ellipseRef.current.position.z;
363
- ellipseRef.current.position.set(
364
- pos.x,
365
- pos.y,
366
- currentZ
367
- );
368
- }
369
- if (lineRef.current?.position && (0, import_utils3.instanceOfRawVector3)(pos)) {
370
- lineRef.current.position.set(
371
- pos.x,
372
- pos.y,
373
- pos.z
374
- );
375
- } else if (lineRef.current?.position && (0, import_utils3.instanceOfRawVector2)(pos)) {
376
- const currentZ = lineRef.current.position.z;
377
- lineRef.current.position.set(
378
- pos.x,
379
- pos.y,
380
- currentZ
381
- );
382
- }
383
- },
384
- rotation: (rot) => {
385
- if (ellipseRef.current?.rotation && (0, import_utils3.instanceOfRawEuler)(rot)) {
386
- ellipseRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
387
- } else if (ellipseRef.current?.rotation && (0, import_utils3.instanceOfRawNumber)(rot)) {
388
- ellipseRef.current.rotation.set(0, 0, 0);
389
- ellipseRef.current.rotateZ(rot);
390
- }
391
- if (lineRef.current?.rotation && (0, import_utils3.instanceOfRawEuler)(rot)) {
392
- lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
393
- } else if (lineRef.current?.rotation && (0, import_utils3.instanceOfRawNumber)(rot)) {
394
- lineRef.current.rotation.set(0, 0, 0);
395
- lineRef.current.rotateZ(rot);
396
- }
397
- },
398
- fillOpacity: (op) => {
399
- if (materialRef.current?.opacity !== void 0 && (0, import_utils3.instanceOfRawNumber)(op)) {
400
- materialRef.current.opacity = op;
401
- if (op < 1) {
402
- materialRef.current.transparent = true;
403
- } else {
404
- materialRef.current.transparent = false;
405
- }
406
- materialRef.current.needsUpdate = true;
407
- }
408
- },
409
- fillColor: (color) => {
410
- if (materialRef.current?.color) {
411
- if ((0, import_utils3.instanceOfRawRGB)(color)) {
412
- materialRef.current.color.setRGB(color.r, color.g, color.b);
413
- materialRef.current.needsUpdate = true;
414
- } else if ((0, import_utils3.instanceOfRawVector3)(color)) {
415
- materialRef.current.color.setRGB(color.x, color.y, color.z);
416
- materialRef.current.needsUpdate = true;
417
- } else if ((0, import_utils3.instanceOfRawHSL)(color)) {
418
- materialRef.current.color.setHSL(color.h, color.s, color.l);
419
- materialRef.current.needsUpdate = true;
420
- }
421
- }
422
- },
423
- height: (height) => {
424
- if (ellipseRef.current && (0, import_utils3.instanceOfRawNumber)(height)) {
425
- ellipseRef.current.scale.set(ellipseRef.current.scale.x, height, 1);
426
- }
427
- if (ellipseRef.current && lineRef.current && (0, import_utils3.instanceOfRawNumber)(height)) {
428
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
429
- lineRef.current.scale.set(
430
- ellipseRef.current.scale.x + offset,
431
- height + offset,
432
- 1
433
- );
434
- }
435
- },
436
- width: (width) => {
437
- if (ellipseRef.current && (0, import_utils3.instanceOfRawNumber)(width)) {
438
- ellipseRef.current.scale.set(width, ellipseRef.current.scale.y, 1);
439
- }
440
- if (ellipseRef.current && lineRef.current && (0, import_utils3.instanceOfRawNumber)(width)) {
441
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
442
- lineRef.current.scale.set(
443
- width + offset,
444
- ellipseRef.current.scale.y + offset,
445
- 1
446
- );
447
- }
448
- },
449
- strokeOpacity: (strokeOpacity) => {
450
- if (lineRef.current?.material && (0, import_utils3.instanceOfRawNumber)(strokeOpacity)) {
451
- lineRef.current.material.opacity = strokeOpacity;
452
- if (strokeOpacity < 1) {
453
- lineRef.current.material.transparent = true;
454
- } else {
455
- lineRef.current.material.transparent = false;
456
- }
457
- lineRef.current.material.needsUpdate = true;
458
- }
459
- },
460
- strokeColor: (strokeColor) => {
461
- if (lineRef.current?.material.color) {
462
- if ((0, import_utils3.instanceOfRawRGB)(strokeColor)) {
463
- lineRef.current.material.color.setRGB(
464
- strokeColor.r,
465
- strokeColor.g,
466
- strokeColor.b
467
- );
468
- lineRef.current.material.needsUpdate = true;
469
- } else if ((0, import_utils3.instanceOfRawVector3)(strokeColor)) {
470
- lineRef.current.material.color.setRGB(
471
- strokeColor.x,
472
- strokeColor.y,
473
- strokeColor.z
474
- );
475
- lineRef.current.material.needsUpdate = true;
476
- } else if ((0, import_utils3.instanceOfRawHSL)(strokeColor)) {
477
- lineRef.current.material.color.setHSL(
478
- strokeColor.h,
479
- strokeColor.s,
480
- strokeColor.l
481
- );
482
- lineRef.current.material.needsUpdate = true;
483
- }
484
- }
485
- },
486
- strokeWidth: (strokeWidth) => {
487
- if (lineRef.current?.material && ellipseRef.current) {
488
- if ((0, import_utils3.instanceOfRawNumber)(strokeWidth)) {
489
- strokeWidthRef.current = strokeWidth;
490
- const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
491
- lineRef.current.scale.set(
492
- ellipseRef.current.scale.x + offset,
493
- ellipseRef.current.scale.y + offset,
494
- 1
495
- );
496
- lineRef.current.material.linewidth = strokeWidth;
497
- lineRef.current.material.needsUpdate = true;
498
- }
499
- }
500
- },
501
- strokeOffset: (strokeOffset) => {
502
- if (lineRef.current?.material && ellipseRef.current) {
503
- if ((0, import_utils3.instanceOfRawNumber)(strokeOffset)) {
504
- strokeOffsetRef.current = strokeOffset;
505
- const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
506
- lineRef.current.scale.set(
507
- ellipseRef.current.scale.x + offset,
508
- ellipseRef.current.scale.y + offset,
509
- 1
510
- );
511
- }
512
- }
513
- }
243
+ async function mountOnce(shown, options) {
244
+ const unfollow = (0, import_runtime.onInitProgress)((progress) => {
245
+ setRuntimeStage(progress.stage === "ready" ? { stage: "mount" } : progress);
514
246
  });
515
- const setReference = useVizijStore(
516
- (0, import_shallow3.useShallow)((state) => state.setReference)
517
- );
518
- const points = (0, import_react5.useMemo)(() => {
519
- const n = 600;
520
- const p = [];
521
- const angleStep = 2 * Math.PI / n;
522
- for (let i = 0; i < n; i++) {
523
- const angle = i * angleStep;
524
- const x = Math.cos(angle);
525
- const y = Math.sin(angle);
526
- p.push([x, y, 0]);
527
- }
528
- return p;
529
- }, []);
530
- (0, import_react5.useEffect)(() => {
531
- if (ellipseRef.current && refIsNull)
532
- setReference(ellipse.id, namespace, ellipseRef);
533
- }, [ellipse.id, namespace, ellipseRef, setReference, refIsNull]);
534
- const handlePointerOver = (0, import_react5.useCallback)(
535
- (event) => {
536
- event.stopPropagation();
537
- setHoveredElement({ id, namespace, type: "ellipse" });
538
- },
539
- [id, namespace, setHoveredElement]
540
- );
541
- const handlePointerOut = (0, import_react5.useCallback)(
542
- (event) => {
543
- event.stopPropagation();
544
- setHoveredElement(null);
545
- },
546
- [setHoveredElement]
547
- );
548
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
549
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
550
- import_drei.Circle,
551
- {
552
- ref: ellipseRef,
553
- userData,
554
- args: [1, 100],
555
- onPointerOver: handlePointerOver,
556
- onPointerOut: handlePointerOut,
557
- onClick: (e) => {
558
- onElementClick({ id, type: "ellipse", namespace }, [...chain, id], e);
559
- },
560
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("meshStandardMaterial", { attach: "material", ref: materialRef })
561
- }
562
- ),
563
- showLine(ellipse) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
564
- import_drei.Line,
565
- {
566
- ref: lineRef,
567
- points,
568
- onPointerOver: handlePointerOver,
569
- onPointerOut: handlePointerOut
570
- }
571
- )
572
- ] });
573
- }
574
- var RenderedEllipse = (0, import_react5.memo)(InnerRenderedEllipse);
575
- var showLine = (ellipse) => {
576
- if ("strokeOpacity" in ellipse.features) {
577
- return true;
578
- } else if ("strokeColor" in ellipse.features) {
579
- return true;
580
- } else if ("strokeWidth" in ellipse.features) {
581
- return true;
582
- } else if ("strokeOffset" in ellipse.features) {
583
- return true;
584
- }
585
- return false;
586
- };
587
-
588
- // src/renderables/rectangle.tsx
589
- var import_react6 = require("react");
590
- var import_shallow4 = require("zustand/react/shallow");
591
- var import_utils4 = require("@vizij/utils");
592
- var import_drei2 = require("@react-three/drei");
593
- var import_jsx_runtime3 = require("react/jsx-runtime");
594
- function InnerRenderedRectangle({
595
- id,
596
- namespace,
597
- chain
598
- }) {
599
- const rectangleRef = (0, import_react6.useRef)(null);
600
- const materialRef = (0, import_react6.useRef)(
601
- null
602
- );
603
- const lineRef = (0, import_react6.useRef)(null);
604
- const strokeOffsetRef = (0, import_react6.useRef)(0);
605
- const strokeWidthRef = (0, import_react6.useRef)(0);
606
- const onElementClick = useVizijStore(
607
- (0, import_shallow4.useShallow)((state) => state.onElementClick)
608
- );
609
- const setHoveredElement = useVizijStore(
610
- (0, import_shallow4.useShallow)((state) => state.setHoveredElement)
611
- );
612
- const rectangle = useVizijStore(
613
- (0, import_shallow4.useShallow)((state) => state.world[id])
614
- );
615
- const refIsNull = useVizijStore(
616
- (state) => !state.refs[id]?.[namespace]?.current
617
- );
618
- const animatables = useVizijStore((0, import_shallow4.useShallow)((state) => state.animatables));
619
- const animatableValues = (0, import_react6.useMemo)(() => {
620
- const av = {};
621
- Object.values(rectangle.features).forEach((feat) => {
622
- if (feat.animated) {
623
- const animatable = animatables[feat.value];
624
- av[animatable.id] = animatable;
625
- }
626
- });
627
- return av;
628
- }, [rectangle.features, animatables]);
629
- const selectionData = (0, import_react6.useMemo)(
630
- () => ({ id, namespace, type: "rectangle" }),
631
- [id, namespace]
632
- );
633
- const userData = (0, import_react6.useMemo)(
634
- () => ({
635
- gltfExtensions: {
636
- RobotData: createStoredRenderable(rectangle, animatableValues)
637
- },
638
- selection: selectionData
639
- }),
640
- [rectangle, animatableValues, selectionData]
641
- );
642
- useFeatures(namespace, rectangle.features, {
643
- translation: (pos) => {
644
- if (rectangleRef.current?.position && (0, import_utils4.instanceOfRawVector3)(pos)) {
645
- rectangleRef.current.position.set(
646
- pos.x,
647
- pos.y,
648
- pos.z
649
- );
650
- } else if (rectangleRef.current?.position && (0, import_utils4.instanceOfRawVector2)(pos)) {
651
- const currentZ = rectangleRef.current.position.z;
652
- rectangleRef.current.position.set(
653
- pos.x,
654
- pos.y,
655
- currentZ
656
- );
657
- }
658
- if (lineRef.current?.position && (0, import_utils4.instanceOfRawVector3)(pos)) {
659
- lineRef.current.position.set(
660
- pos.x,
661
- pos.y,
662
- pos.z
663
- );
664
- } else if (lineRef.current?.position && (0, import_utils4.instanceOfRawVector2)(pos)) {
665
- const currentZ = lineRef.current.position.z;
666
- lineRef.current.position.set(
667
- pos.x,
668
- pos.y,
669
- currentZ
670
- );
671
- }
672
- },
673
- rotation: (rot) => {
674
- if (rectangleRef.current?.rotation && (0, import_utils4.instanceOfRawEuler)(rot)) {
675
- rectangleRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
676
- } else if (rectangleRef.current?.rotation && (0, import_utils4.instanceOfRawNumber)(rot)) {
677
- rectangleRef.current.rotation.set(0, 0, 0);
678
- rectangleRef.current.rotateZ(rot);
679
- }
680
- if (lineRef.current?.rotation && (0, import_utils4.instanceOfRawEuler)(rot)) {
681
- lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
682
- } else if (lineRef.current?.rotation && (0, import_utils4.instanceOfRawNumber)(rot)) {
683
- lineRef.current.rotation.set(0, 0, 0);
684
- lineRef.current.rotateZ(rot);
685
- }
686
- },
687
- fillOpacity: (op) => {
688
- if (materialRef.current?.opacity !== void 0 && (0, import_utils4.instanceOfRawNumber)(op)) {
689
- materialRef.current.opacity = op;
690
- if (op < 1) {
691
- materialRef.current.transparent = true;
692
- } else {
693
- materialRef.current.transparent = false;
694
- }
695
- materialRef.current.needsUpdate = true;
696
- }
697
- },
698
- fillColor: (color) => {
699
- if (materialRef.current?.color) {
700
- if ((0, import_utils4.instanceOfRawRGB)(color)) {
701
- materialRef.current.color.setRGB(color.r, color.g, color.b);
702
- materialRef.current.needsUpdate = true;
703
- } else if ((0, import_utils4.instanceOfRawVector3)(color)) {
704
- materialRef.current.color.setRGB(color.x, color.y, color.z);
705
- materialRef.current.needsUpdate = true;
706
- } else if ((0, import_utils4.instanceOfRawHSL)(color)) {
707
- materialRef.current.color.setHSL(color.h, color.s, color.l);
708
- materialRef.current.needsUpdate = true;
709
- }
710
- }
711
- },
712
- height: (height) => {
713
- if (rectangleRef.current && (0, import_utils4.instanceOfRawNumber)(height)) {
714
- rectangleRef.current.scale.set(rectangleRef.current.scale.x, height, 1);
715
- }
716
- if (rectangleRef.current && lineRef.current && (0, import_utils4.instanceOfRawNumber)(height)) {
717
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
718
- lineRef.current.scale.set(
719
- rectangleRef.current.scale.x + offset,
720
- height + offset,
721
- 1
722
- );
723
- }
724
- },
725
- width: (width) => {
726
- if (rectangleRef.current && (0, import_utils4.instanceOfRawNumber)(width)) {
727
- rectangleRef.current.scale.set(width, rectangleRef.current.scale.y, 1);
728
- }
729
- if (rectangleRef.current && lineRef.current && (0, import_utils4.instanceOfRawNumber)(width)) {
730
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
731
- lineRef.current.scale.set(
732
- width + offset,
733
- rectangleRef.current.scale.y + offset,
734
- 1
735
- );
736
- }
737
- },
738
- strokeOpacity: (strokeOpacity) => {
739
- if (lineRef.current?.material && (0, import_utils4.instanceOfRawNumber)(strokeOpacity)) {
740
- lineRef.current.material.opacity = strokeOpacity;
741
- if (strokeOpacity < 1) {
742
- lineRef.current.material.transparent = true;
743
- } else {
744
- lineRef.current.material.transparent = false;
745
- }
746
- lineRef.current.material.needsUpdate = true;
747
- }
748
- },
749
- strokeColor: (strokeColor) => {
750
- if (lineRef.current?.material.color) {
751
- if ((0, import_utils4.instanceOfRawRGB)(strokeColor)) {
752
- lineRef.current.material.color.setRGB(
753
- strokeColor.r,
754
- strokeColor.g,
755
- strokeColor.b
756
- );
757
- lineRef.current.material.needsUpdate = true;
758
- } else if ((0, import_utils4.instanceOfRawVector3)(strokeColor)) {
759
- lineRef.current.material.color.setRGB(
760
- strokeColor.x,
761
- strokeColor.y,
762
- strokeColor.z
763
- );
764
- lineRef.current.material.needsUpdate = true;
765
- } else if ((0, import_utils4.instanceOfRawHSL)(strokeColor)) {
766
- lineRef.current.material.color.setHSL(
767
- strokeColor.h,
768
- strokeColor.s,
769
- strokeColor.l
770
- );
771
- lineRef.current.material.needsUpdate = true;
772
- }
773
- }
774
- },
775
- strokeWidth: (strokeWidth) => {
776
- if (lineRef.current?.material && rectangleRef.current) {
777
- if ((0, import_utils4.instanceOfRawNumber)(strokeWidth)) {
778
- strokeWidthRef.current = strokeWidth;
779
- const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
780
- lineRef.current.scale.set(
781
- rectangleRef.current.scale.x + offset,
782
- rectangleRef.current.scale.y + offset,
783
- 1
784
- );
785
- lineRef.current.material.linewidth = strokeWidth;
786
- lineRef.current.material.needsUpdate = true;
787
- }
788
- }
789
- },
790
- strokeOffset: (strokeOffset) => {
791
- if (lineRef.current?.material && rectangleRef.current) {
792
- if ((0, import_utils4.instanceOfRawNumber)(strokeOffset)) {
793
- strokeOffsetRef.current = strokeOffset;
794
- const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
795
- lineRef.current.scale.set(
796
- rectangleRef.current.scale.x + offset,
797
- rectangleRef.current.scale.y + offset,
798
- 1
799
- );
800
- }
801
- }
802
- }
247
+ try {
248
+ await (0, import_runtime.init)();
249
+ await afterPaint();
250
+ await (0, import_runtime.mount)(shown, options);
251
+ } catch (error) {
252
+ setRuntimeStage({ stage: "failed", error: asError(error) });
253
+ throw error;
254
+ } finally {
255
+ unfollow();
256
+ }
257
+ live = true;
258
+ setRuntimeStage(null);
259
+ await afterPaint();
260
+ return shown;
261
+ }
262
+ function afterPaint() {
263
+ return new Promise((resolve) => {
264
+ const timer = setTimeout(resolve, PAINT_WAIT_MS);
265
+ requestAnimationFrame(
266
+ () => setTimeout(() => {
267
+ clearTimeout(timer);
268
+ resolve();
269
+ }, 0)
270
+ );
803
271
  });
804
- const setReference = useVizijStore(
805
- (0, import_shallow4.useShallow)((state) => state.setReference)
806
- );
807
- const points = (0, import_react6.useMemo)(() => {
808
- return [
809
- [-0.5, 0.5, 0],
810
- [0.5, 0.5, 0],
811
- [0.5, -0.5, 0],
812
- [-0.5, -0.5, 0],
813
- [-0.5, 0.5, 0]
814
- ];
815
- }, []);
816
- (0, import_react6.useEffect)(() => {
817
- if (rectangleRef.current && refIsNull)
818
- setReference(rectangle.id, namespace, rectangleRef);
819
- }, [rectangle.id, namespace, rectangleRef, setReference, refIsNull]);
820
- const handlePointerOver = (0, import_react6.useCallback)(
821
- (event) => {
822
- event.stopPropagation();
823
- setHoveredElement({ id, namespace, type: "rectangle" });
824
- },
825
- [id, namespace, setHoveredElement]
826
- );
827
- const handlePointerOut = (0, import_react6.useCallback)(
828
- (event) => {
829
- event.stopPropagation();
830
- setHoveredElement(null);
831
- },
832
- [setHoveredElement]
833
- );
834
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
835
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
836
- import_drei2.Plane,
837
- {
838
- ref: rectangleRef,
839
- userData,
840
- args: [1, 1],
841
- onPointerOver: handlePointerOver,
842
- onPointerOut: handlePointerOut,
843
- onClick: (e) => {
844
- onElementClick(
845
- { id, type: "rectangle", namespace },
846
- [...chain, id],
847
- e
848
- );
849
- },
850
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("meshStandardMaterial", { attach: "material", ref: materialRef })
851
- }
852
- ),
853
- showLine2(rectangle) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
854
- import_drei2.Line,
855
- {
856
- ref: lineRef,
857
- points,
858
- onPointerOver: handlePointerOver,
859
- onPointerOut: handlePointerOut
860
- }
861
- )
862
- ] });
863
272
  }
864
- var RenderedRectangle = (0, import_react6.memo)(InnerRenderedRectangle);
865
- var showLine2 = (rectangle) => {
866
- if ("strokeOpacity" in rectangle.features) {
867
- return true;
868
- } else if ("strokeColor" in rectangle.features) {
869
- return true;
870
- } else if ("strokeWidth" in rectangle.features) {
871
- return true;
872
- } else if ("strokeOffset" in rectangle.features) {
873
- return true;
874
- }
875
- return false;
876
- };
877
-
878
- // src/renderables/shape.tsx
879
- var import_react7 = require("react");
880
- var THREE2 = __toESM(require("three"));
881
- var import_shallow5 = require("zustand/react/shallow");
882
- var import_utils5 = require("@vizij/utils");
883
-
884
- // src/functions/material-name.ts
885
- var MATERIAL_FEATURE_KEYS = [
886
- "color",
887
- "opacity",
888
- "roughness",
889
- "metalness",
890
- "shininess",
891
- "emissive",
892
- "emissiveIntensity",
893
- "specular"
894
- ];
895
- var MATERIAL_NAME_SUFFIXES = [
896
- " color",
897
- " colours",
898
- " colour",
899
- " opacity",
900
- " roughness",
901
- " metalness",
902
- " shininess",
903
- " emissive intensity",
904
- " emissive",
905
- " specular"
906
- ];
907
- function deriveMaterialName(shape, values) {
908
- for (const key of MATERIAL_FEATURE_KEYS) {
909
- const feature = shape.features[key];
910
- if (feature && feature.animated) {
911
- const animatable = values[feature.value];
912
- const candidate = extractMaterialName(animatable?.name);
913
- if (candidate) {
914
- return candidate;
915
- }
916
- }
917
- }
918
- return void 0;
273
+ function asError(error) {
274
+ return error instanceof Error ? error : new Error(String(error));
919
275
  }
920
- function extractMaterialName(name) {
921
- if (!name) {
922
- return void 0;
276
+ function pageCanvas() {
277
+ if (!canvas) {
278
+ canvas = document.createElement("canvas");
279
+ canvas.style.display = "block";
280
+ canvas.style.width = "100%";
281
+ canvas.style.height = "100%";
923
282
  }
924
- const trimmed = name.trim();
925
- if (!trimmed) {
926
- return void 0;
283
+ return canvas;
284
+ }
285
+ var view = { placeVizij: import_runtime.placeVizij, ready: import_runtime.ready, safeArea: import_runtime.safeArea, drainPicks: import_runtime.drainPicks };
286
+ function start() {
287
+ if (loop !== null || hosts === 0) return;
288
+ const tick = () => {
289
+ frame(slots, canvas, view);
290
+ loop = requestAnimationFrame(tick);
291
+ };
292
+ loop = requestAnimationFrame(tick);
293
+ }
294
+ function stop() {
295
+ if (loop !== null) {
296
+ cancelAnimationFrame(loop);
297
+ loop = null;
927
298
  }
928
- const lowered = trimmed.toLowerCase();
929
- for (const suffix of MATERIAL_NAME_SUFFIXES) {
930
- if (lowered.endsWith(suffix)) {
931
- return trimmed.slice(0, trimmed.length - suffix.length).trim();
299
+ }
300
+ function addSlot(slot) {
301
+ slots.add(slot);
302
+ return () => {
303
+ slots.delete(slot);
304
+ if (live) {
305
+ (0, import_runtime.placeVizij)(slot.vizijId, { x: 0, y: 0, width: 0, height: 0 });
932
306
  }
933
- }
934
- return trimmed;
307
+ };
935
308
  }
936
309
 
937
- // src/renderables/shape.tsx
938
- var import_jsx_runtime4 = require("react/jsx-runtime");
939
- THREE2.Object3D.DEFAULT_UP.set(0, 0, 1);
940
- function InnerRenderedShape({
941
- id,
942
- namespace,
943
- chain
944
- }) {
945
- const refGroup = (0, import_react7.useRef)(null);
946
- const ref = (0, import_react7.useRef)(null);
947
- const shape = useVizijStore((0, import_shallow5.useShallow)((state) => state.world[id]));
948
- const refIsNull = useVizijStore(
949
- (state) => !state.refs[id]?.[namespace]?.current
950
- );
951
- const animatables = useVizijStore((0, import_shallow5.useShallow)((state) => state.animatables));
952
- const animatableValues = (0, import_react7.useMemo)(() => {
953
- const av = {};
954
- Object.values(shape.features).forEach((feat) => {
955
- if (feat.animated) {
956
- const animatable = animatables[feat.value];
957
- av[animatable.id] = animatable;
958
- }
959
- });
960
- return av;
961
- }, [shape.features, animatables]);
962
- const materialName = (0, import_react7.useMemo)(
963
- () => deriveMaterialName(shape, animatableValues),
964
- [shape, animatableValues]
965
- );
966
- const geometry = (0, import_react7.useMemo)(() => {
967
- const cloned = shape.geometry.clone();
968
- if (shape.name) {
969
- cloned.name = shape.name;
970
- }
971
- return cloned;
972
- }, [shape.geometry, shape.name]);
973
- const selectionData = (0, import_react7.useMemo)(
974
- () => ({ id, namespace, type: "shape" }),
975
- [id, namespace]
976
- );
977
- const userData = (0, import_react7.useMemo)(
978
- () => ({
979
- gltfExtensions: {
980
- RobotData: createStoredRenderable(shape, animatableValues)
981
- },
982
- selection: selectionData
983
- }),
984
- [shape, animatableValues, selectionData]
985
- );
986
- const material = (0, import_react7.useRef)(null);
987
- const morphTargetSettings = (0, import_react7.useMemo)(() => {
988
- if (shape.morphTargets) {
989
- const dictionary = shape.morphTargets.reduce(
990
- (acc, target, i) => ({ ...acc, [target]: i }),
991
- {}
992
- );
993
- const initialInfluences = shape.morphTargets.map(() => 0);
994
- const morphFeatureHandlers = {};
995
- shape.morphTargets.forEach((target, i) => {
996
- morphFeatureHandlers[target] = (value) => {
997
- if (ref.current?.morphTargetInfluences && (0, import_utils5.instanceOfRawNumber)(value)) {
998
- ref.current.morphTargetInfluences[i] = value;
999
- }
1000
- };
1001
- });
1002
- return [dictionary, initialInfluences, morphFeatureHandlers];
1003
- } else {
1004
- return [void 0, void 0, {}];
1005
- }
1006
- }, [shape.morphTargets]);
1007
- useFeatures(
1008
- namespace,
1009
- shape.features,
1010
- {
1011
- translation: (pos) => {
1012
- if (ref.current?.position && (0, import_utils5.instanceOfRawVector3)(pos)) {
1013
- ref.current.position.set(
1014
- pos.x,
1015
- pos.y,
1016
- pos.z
1017
- );
1018
- }
1019
- if (refGroup.current?.position && (0, import_utils5.instanceOfRawVector3)(pos)) {
1020
- refGroup.current.position.set(
1021
- pos.x,
1022
- pos.y,
1023
- pos.z
1024
- );
1025
- }
1026
- },
1027
- rotation: (rot) => {
1028
- if (ref.current?.rotation && (0, import_utils5.instanceOfRawEuler)(rot)) {
1029
- ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
1030
- }
1031
- if (refGroup.current?.rotation && (0, import_utils5.instanceOfRawEuler)(rot)) {
1032
- refGroup.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
1033
- }
1034
- },
1035
- scale: (scale) => {
1036
- if (ref.current?.scale && (0, import_utils5.instanceOfRawVector3)(scale)) {
1037
- ref.current.scale.set(scale.x, scale.y, scale.z);
1038
- } else if (ref.current && (0, import_utils5.instanceOfRawNumber)(scale)) {
1039
- ref.current.scale.set(scale, scale, scale);
1040
- } else if (ref.current) {
1041
- ref.current.scale.set(1, 1, 1);
1042
- }
1043
- },
1044
- opacity: (op) => {
1045
- if (material.current?.opacity !== void 0 && (0, import_utils5.instanceOfRawNumber)(op)) {
1046
- material.current.opacity = op;
1047
- if (op < 1) {
1048
- material.current.transparent = true;
1049
- } else {
1050
- material.current.transparent = false;
1051
- }
1052
- material.current.needsUpdate = true;
1053
- }
1054
- },
1055
- color: (color) => {
1056
- if ((material.current || void 0)?.color && (0, import_utils5.instanceOfRawRGB)(color)) {
1057
- material.current.color.setRGB(
1058
- color.r,
1059
- color.g,
1060
- color.b
1061
- );
1062
- material.current.needsUpdate = true;
1063
- } else if ((material.current || void 0)?.color && (0, import_utils5.instanceOfRawVector3)(color)) {
1064
- material.current.color.setRGB(
1065
- color.x,
1066
- color.y,
1067
- color.z
1068
- );
1069
- material.current.needsUpdate = true;
1070
- } else if (material.current && (0, import_utils5.instanceOfRawHSL)(color)) {
1071
- material.current.color.setHSL(
1072
- color.h,
1073
- color.s,
1074
- color.l
1075
- );
1076
- }
1077
- },
1078
- roughness: (value) => {
1079
- const standard = material.current;
1080
- if (standard?.roughness !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1081
- standard.roughness = value;
1082
- standard.needsUpdate = true;
1083
- }
1084
- },
1085
- metalness: (value) => {
1086
- const standard = material.current;
1087
- if (standard?.metalness !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1088
- standard.metalness = value;
1089
- standard.needsUpdate = true;
1090
- }
1091
- },
1092
- shininess: (value) => {
1093
- const phong = material.current;
1094
- if (phong?.shininess !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1095
- phong.shininess = value;
1096
- phong.needsUpdate = true;
1097
- }
1098
- },
1099
- emissive: (value) => {
1100
- const standard = material.current;
1101
- if (standard?.emissive && (0, import_utils5.instanceOfRawRGB)(value)) {
1102
- standard.emissive.setRGB(value.r, value.g, value.b);
1103
- standard.needsUpdate = true;
1104
- }
1105
- },
1106
- emissiveIntensity: (value) => {
1107
- const standard = material.current;
1108
- if (standard?.emissiveIntensity !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1109
- standard.emissiveIntensity = value;
1110
- standard.needsUpdate = true;
310
+ // src/page/vizij.tsx
311
+ var import_react2 = require("react");
312
+ var import_runtime2 = require("@vizij/runtime");
313
+
314
+ // src/page/loading.tsx
315
+ var import_jsx_runtime2 = require("react/jsx-runtime");
316
+ function describeLoading(stage) {
317
+ return {
318
+ stage,
319
+ label: loadingLabel(stage),
320
+ fraction: loadingFraction(stage)
321
+ };
322
+ }
323
+ function loadingLabel(stage) {
324
+ switch (stage.stage) {
325
+ case "download":
326
+ return withBytes("Downloading the runtime", stage.loaded, stage.total);
327
+ case "compile":
328
+ return "Compiling the runtime";
329
+ case "start":
330
+ return "Starting the runtime";
331
+ case "mount":
332
+ return "Starting the view";
333
+ case "fetch":
334
+ return withBytes("Loading the face", stage.loaded, stage.total);
335
+ case "load":
336
+ return "Loading the face";
337
+ case "draw":
338
+ return "Drawing the face";
339
+ case "failed":
340
+ return "Loading failed";
341
+ }
342
+ }
343
+ function loadingFraction(stage) {
344
+ if (stage.stage !== "download" && stage.stage !== "fetch") return null;
345
+ if (stage.total === null || stage.total <= 0) return null;
346
+ return Math.min(1, Math.max(0, stage.loaded / stage.total));
347
+ }
348
+ function withBytes(what, loaded, total) {
349
+ if (total === null) {
350
+ return loaded > 0 ? `${what}: ${megabytes(loaded)} MB` : what;
351
+ }
352
+ return `${what}: ${megabytes(loaded)} of ${megabytes(total)} MB`;
353
+ }
354
+ function megabytes(bytes) {
355
+ return (bytes / 1e6).toFixed(1);
356
+ }
357
+ var INDETERMINATE_CSS = `
358
+ @keyframes vizij-loading-slide {
359
+ from { transform: translateX(-100%); }
360
+ to { transform: translateX(250%); }
361
+ }
362
+ [data-vizij-loading-indeterminate] {
363
+ animation: vizij-loading-slide 1.4s ease-in-out infinite;
364
+ }
365
+ @media (prefers-reduced-motion: reduce) {
366
+ [data-vizij-loading-indeterminate] { animation: none; }
367
+ }
368
+ `;
369
+ function DefaultLoadingImage() {
370
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { viewBox: "0 0 1024 1024", width: "64", height: "64", children: [
371
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "447.6", cy: "486.6", r: "400.6", fill: "#50c4b6" }),
372
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { fillOpacity: "0.894", children: [
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
374
+ "path",
375
+ {
376
+ fill: "#f56b29",
377
+ d: "M471.3 495.5L846.5 488A14.8 14.8 0 0 1 861.5 503.8L835.3 869.7A60.5 60.5 0 0 1 767.6 925.4L469.8 888.9A14.8 14.8 0 0 1 456.8 874.2L456.8 510.3A14.8 14.8 0 0 1 471.3 495.5Z"
1111
378
  }
1112
- },
1113
- specular: (value) => {
1114
- const phong = material.current;
1115
- if (phong?.specular && (0, import_utils5.instanceOfRawRGB)(value)) {
1116
- phong.specular.setRGB(value.r, value.g, value.b);
1117
- phong.needsUpdate = true;
379
+ ),
380
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
381
+ "path",
382
+ {
383
+ fill: "#ff9e00",
384
+ d: "M824.3 339L983.4 619.9A14.8 14.8 0 0 1 970.7 642L790 644.2A14.8 14.8 0 0 1 775 628.3L796.7 345.1A14.8 14.8 0 0 1 824.3 339Z"
1118
385
  }
1119
- },
1120
- ...morphTargetSettings[2]
1121
- },
1122
- shape
1123
- );
1124
- const setReference = useVizijStore((0, import_shallow5.useShallow)((state) => state.setReference));
1125
- const onElementClick = useVizijStore(
1126
- (0, import_shallow5.useShallow)((state) => state.onElementClick)
1127
- );
1128
- const setHoveredElement = useVizijStore(
1129
- (0, import_shallow5.useShallow)((state) => state.setHoveredElement)
1130
- );
1131
- (0, import_react7.useEffect)(() => {
1132
- if (ref.current && refIsNull) setReference(shape.id, namespace, ref);
1133
- }, [shape.id, namespace, ref, setReference, refIsNull]);
1134
- (0, import_react7.useEffect)(() => {
1135
- if (ref.current) {
1136
- ref.current.name = shape.name;
1137
- }
1138
- }, [shape.name]);
1139
- (0, import_react7.useEffect)(() => {
1140
- if (!material.current) {
1141
- return;
1142
- }
1143
- if (materialName) {
1144
- material.current.name = materialName;
1145
- } else if (shape.name) {
1146
- material.current.name = shape.name;
1147
- }
1148
- }, [materialName, shape.name]);
1149
- const handlePointerOver = (0, import_react7.useCallback)(
1150
- (event) => {
1151
- event.stopPropagation();
1152
- setHoveredElement({ id, namespace, type: "shape" });
1153
- },
1154
- [id, namespace, setHoveredElement]
1155
- );
1156
- const handlePointerOut = (0, import_react7.useCallback)(
1157
- (event) => {
1158
- event.stopPropagation();
1159
- setHoveredElement(null);
1160
- },
1161
- [setHoveredElement]
1162
- );
1163
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1164
- "mesh",
386
+ )
387
+ ] })
388
+ ] });
389
+ }
390
+ function DefaultLoading({
391
+ loading,
392
+ image
393
+ }) {
394
+ const { stage, label, fraction } = loading;
395
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
396
+ "div",
1165
397
  {
1166
- ref,
1167
- userData,
1168
- castShadow: true,
1169
- receiveShadow: true,
1170
- up: [0, 0, 1],
1171
- geometry,
1172
- morphTargetDictionary: morphTargetSettings[0],
1173
- morphTargetInfluences: morphTargetSettings[1],
1174
- onPointerOver: handlePointerOver,
1175
- onPointerOut: handlePointerOut,
1176
- onClick: (e) => {
1177
- console.log("Clicked element", shape);
1178
- onElementClick({ id, type: "shape", namespace }, [...chain, id], e);
398
+ style: {
399
+ width: "100%",
400
+ height: "100%",
401
+ display: "flex",
402
+ flexDirection: "column",
403
+ alignItems: "center",
404
+ justifyContent: "center",
405
+ gap: 12,
406
+ font: "13px/1.4 system-ui, sans-serif",
407
+ textAlign: "center"
1179
408
  },
1180
409
  children: [
1181
- shape.material === "basic" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1182
- "meshBasicMaterial",
1183
- {
1184
- attach: "material",
1185
- ref: material,
1186
- side: THREE2.DoubleSide
1187
- }
1188
- ),
1189
- shape.material === "lambert" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1190
- "meshLambertMaterial",
1191
- {
1192
- attach: "material",
1193
- ref: material,
1194
- side: THREE2.DoubleSide
1195
- }
1196
- ),
1197
- shape.material === "phong" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1198
- "meshPhongMaterial",
1199
- {
1200
- attach: "material",
1201
- ref: material,
1202
- side: THREE2.DoubleSide
1203
- }
1204
- ),
1205
- shape.material === "standard" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1206
- "meshStandardMaterial",
1207
- {
1208
- attach: "material",
1209
- ref: material,
1210
- side: THREE2.DoubleSide
1211
- }
1212
- ),
1213
- shape.material === "normal" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1214
- "meshNormalMaterial",
1215
- {
1216
- attach: "material",
1217
- ref: material,
1218
- side: THREE2.DoubleSide
1219
- }
1220
- ),
1221
- shape.children?.map((child) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1222
- Renderable,
1223
- {
1224
- id: child,
1225
- namespace,
1226
- chain: [...chain, id]
1227
- },
1228
- child
1229
- ))
410
+ image !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "aria-hidden": "true", children: image ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultLoadingImage, {}) }),
411
+ stage.stage === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { role: "status", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
413
+ "div",
414
+ {
415
+ role: "progressbar",
416
+ "aria-label": "Loading the face",
417
+ "aria-valuetext": label,
418
+ ...fraction === null ? {} : {
419
+ "aria-valuemin": 0,
420
+ "aria-valuemax": 100,
421
+ "aria-valuenow": Math.round(fraction * 100)
422
+ },
423
+ style: {
424
+ position: "relative",
425
+ width: 160,
426
+ maxWidth: "60%",
427
+ height: 4,
428
+ borderRadius: 2,
429
+ overflow: "hidden"
430
+ },
431
+ children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
433
+ "div",
434
+ {
435
+ style: {
436
+ position: "absolute",
437
+ inset: 0,
438
+ background: "currentColor",
439
+ opacity: 0.2
440
+ }
441
+ }
442
+ ),
443
+ fraction === null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
444
+ "div",
445
+ {
446
+ "data-vizij-loading-indeterminate": "",
447
+ style: {
448
+ position: "absolute",
449
+ top: 0,
450
+ bottom: 0,
451
+ left: 0,
452
+ width: "40%",
453
+ background: "currentColor"
454
+ }
455
+ }
456
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
457
+ "div",
458
+ {
459
+ style: {
460
+ position: "absolute",
461
+ top: 0,
462
+ bottom: 0,
463
+ left: 0,
464
+ width: `${fraction * 100}%`,
465
+ background: "currentColor"
466
+ }
467
+ }
468
+ )
469
+ ]
470
+ }
471
+ ),
472
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "aria-hidden": "true", style: { opacity: 0.8 }, children: label }),
473
+ fraction === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: INDETERMINATE_CSS })
474
+ ] })
1230
475
  ]
1231
476
  }
1232
477
  );
1233
478
  }
1234
- var RenderedShape = (0, import_react7.memo)(InnerRenderedShape);
1235
479
 
1236
- // src/renderables/renderable.tsx
1237
- var import_jsx_runtime5 = require("react/jsx-runtime");
1238
- function InnerRenderable({
1239
- id,
1240
- namespace,
1241
- chain
480
+ // src/page/vizij.tsx
481
+ var import_jsx_runtime3 = require("react/jsx-runtime");
482
+ function Vizij({
483
+ vizijId,
484
+ className,
485
+ style,
486
+ toneMapping,
487
+ fit,
488
+ zoom,
489
+ bounds,
490
+ showSafeArea = false,
491
+ selection,
492
+ onPick,
493
+ loadingImage,
494
+ loading
1242
495
  }) {
1243
- const type = useVizijStore((0, import_shallow6.useShallow)((state) => state.world[id].type));
1244
- const key = `${namespace}.${id}`;
1245
- switch (type) {
1246
- case "group":
1247
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RenderedGroup, { id, namespace, chain }, key);
1248
- case "ellipse":
1249
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1250
- RenderedEllipse,
1251
- {
1252
- id,
1253
- namespace,
1254
- chain
1255
- },
1256
- key
1257
- );
1258
- case "rectangle":
1259
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1260
- RenderedRectangle,
496
+ const element = (0, import_react2.useRef)(null);
497
+ const frame2 = (0, import_react2.useRef)(null);
498
+ const outline2 = (0, import_react2.useRef)(null);
499
+ const slot = (0, import_react2.useRef)(null);
500
+ (0, import_react2.useEffect)(() => {
501
+ const registered = {
502
+ vizijId,
503
+ element: element.current,
504
+ safeArea: null,
505
+ onPick: void 0,
506
+ placed: null
507
+ };
508
+ registered.element.setAttribute("data-vizij-ready", "false");
509
+ slot.current = registered;
510
+ const remove = addSlot(registered);
511
+ return () => {
512
+ slot.current = null;
513
+ remove();
514
+ };
515
+ }, [vizijId]);
516
+ (0, import_react2.useEffect)(() => {
517
+ if (slot.current) slot.current.onPick = onPick;
518
+ }, [onPick, vizijId]);
519
+ (0, import_react2.useEffect)(() => {
520
+ if (!slot.current) return;
521
+ slot.current.safeArea = showSafeArea && outline2.current && frame2.current ? { outline: outline2.current, frame: frame2.current } : null;
522
+ }, [showSafeArea, vizijId]);
523
+ const view2 = JSON.stringify({ toneMapping, fit, zoom, bounds });
524
+ (0, import_react2.useEffect)(
525
+ () => onceMounted(() => (0, import_runtime2.setView)(vizijId, JSON.parse(view2))),
526
+ [vizijId, view2]
527
+ );
528
+ const selected = selection === void 0 ? null : JSON.stringify(selection);
529
+ (0, import_react2.useEffect)(
530
+ () => selected === null ? void 0 : onceMounted(() => (0, import_runtime2.setSelection)(vizijId, JSON.parse(selected))),
531
+ [vizijId, selected]
532
+ );
533
+ const stage = useLoadingStage(vizijId);
534
+ const loadingView = stage === null || loading === null ? null : loading ? loading(describeLoading(stage)) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultLoading, { loading: describeLoading(stage), image: loadingImage });
535
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
536
+ "div",
537
+ {
538
+ ref: element,
539
+ className,
540
+ style,
541
+ "data-vizij-id": vizijId,
542
+ children: (showSafeArea || loadingView !== null) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
543
+ "div",
1261
544
  {
1262
- id,
1263
- namespace,
1264
- chain
1265
- },
1266
- key
1267
- );
1268
- case "shape":
1269
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RenderedShape, { id, namespace, chain }, key);
1270
- default:
1271
- return null;
1272
- }
545
+ ref: frame2,
546
+ style: {
547
+ position: "relative",
548
+ width: "100%",
549
+ height: "100%",
550
+ pointerEvents: "none"
551
+ },
552
+ children: [
553
+ loadingView !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
554
+ "div",
555
+ {
556
+ "data-vizij-loading": "",
557
+ style: { position: "absolute", inset: 0 },
558
+ children: loadingView
559
+ }
560
+ ),
561
+ showSafeArea && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
562
+ "div",
563
+ {
564
+ ref: outline2,
565
+ style: {
566
+ position: "absolute",
567
+ display: "none",
568
+ boxSizing: "border-box",
569
+ border: "2px solid red"
570
+ }
571
+ }
572
+ )
573
+ ]
574
+ }
575
+ )
576
+ }
577
+ );
578
+ }
579
+ function useLoadingStage(vizijId) {
580
+ const [stage, setStage] = (0, import_react2.useState)(() => loadingStage(vizijId));
581
+ (0, import_react2.useEffect)(() => watchLoading(vizijId, setStage), [vizijId]);
582
+ return stage;
583
+ }
584
+ function onceMounted(apply) {
585
+ let current = true;
586
+ canvasMounted().then(
587
+ () => {
588
+ if (current) apply();
589
+ },
590
+ () => void 0
591
+ );
592
+ return () => {
593
+ current = false;
594
+ };
1273
595
  }
1274
- var Renderable = (0, import_react8.memo)(InnerRenderable);
1275
596
 
1276
597
  // src/store.ts
1277
- var import_zustand2 = require("zustand");
598
+ var import_zustand = require("zustand");
1278
599
  var import_middleware = require("zustand/middleware");
1279
600
  var import_immer = require("immer");
1280
- var THREE3 = __toESM(require("three"));
1281
- var import_utils8 = require("@vizij/utils");
601
+ var import_utils3 = require("@vizij/utils");
1282
602
 
1283
603
  // src/functions/create-world-element.ts
1284
- var import_utils6 = require("@vizij/utils");
604
+ var import_utils = require("@vizij/utils");
1285
605
  function createDefaultGroup(partialBody) {
1286
606
  const translation = partialBody.features?.translation || { animated: false, value: { x: 0, y: 0 } };
1287
607
  const rotation = partialBody.features?.rotation || { animated: false, value: { x: 0, y: 0, z: 0 } };
1288
608
  const scale = partialBody.features?.scale || { animated: false, value: { x: 1, y: 1, z: 1 } };
1289
609
  return {
1290
- id: partialBody.id || (0, import_utils6.createBrowserSafeId)(),
610
+ id: partialBody.id || (0, import_utils.createBrowserSafeId)(),
1291
611
  name: partialBody.name || "new-body",
1292
612
  type: partialBody.type || "group",
1293
613
  tags: partialBody.tags || [],
@@ -1341,32 +661,8 @@ function removeFromTree(state, nodesToRemove) {
1341
661
  });
1342
662
  }
1343
663
 
1344
- // src/memoryInvestigation.ts
1345
- function getGlobalMemoryInvestigation() {
1346
- return globalThis;
1347
- }
1348
- function updateRenderState(updater) {
1349
- const globalObj = getGlobalMemoryInvestigation();
1350
- if (!globalObj.__VIZIJ_MEMORY_INVESTIGATION__?.enabled) {
1351
- return;
1352
- }
1353
- if (!globalObj.__vizijMemoryDebugState) {
1354
- globalObj.__vizijMemoryDebugState = {};
1355
- }
1356
- if (!globalObj.__vizijMemoryDebugState.render) {
1357
- globalObj.__vizijMemoryDebugState.render = {};
1358
- }
1359
- updater(globalObj.__vizijMemoryDebugState.render);
1360
- }
1361
- function recordRenderCounter(key, delta = 1) {
1362
- updateRenderState((renderState) => {
1363
- const current = typeof renderState[key] === "number" ? Number(renderState[key]) : 0;
1364
- renderState[key] = current + delta;
1365
- });
1366
- }
1367
-
1368
664
  // src/functions/create-animatable.ts
1369
- var import_utils7 = require("@vizij/utils");
665
+ var import_utils2 = require("@vizij/utils");
1370
666
  function createAnimatable(value) {
1371
667
  if (!value.type) {
1372
668
  return null;
@@ -1376,7 +672,7 @@ function createAnimatable(value) {
1376
672
  }
1377
673
  if (value.type === "euler") {
1378
674
  const newAnimatable = {
1379
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
675
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1380
676
  name: `${value.name} Rotation`,
1381
677
  type: value.type,
1382
678
  default: value.default ?? { x: 0, y: 0, z: 0 },
@@ -1393,7 +689,7 @@ function createAnimatable(value) {
1393
689
  return newAnimatable;
1394
690
  } else if (value.type === "vector3") {
1395
691
  const newAnimatable = {
1396
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
692
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1397
693
  name: `${value.name} Vector3`,
1398
694
  type: value.type,
1399
695
  default: value.default ?? { x: 0, y: 0, z: 0 },
@@ -1410,7 +706,7 @@ function createAnimatable(value) {
1410
706
  return newAnimatable;
1411
707
  } else if (value.type === "string") {
1412
708
  const newAnimatable = {
1413
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
709
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1414
710
  name: value.name,
1415
711
  type: value.type,
1416
712
  default: value.default ?? "Hello World",
@@ -1423,7 +719,7 @@ function createAnimatable(value) {
1423
719
  return newAnimatable;
1424
720
  } else if (value.type === "number") {
1425
721
  const newAnimatable = {
1426
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
722
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1427
723
  name: value.name,
1428
724
  type: value.type,
1429
725
  default: value.default ?? 0,
@@ -1436,7 +732,7 @@ function createAnimatable(value) {
1436
732
  return newAnimatable;
1437
733
  } else if (value.type === "boolean") {
1438
734
  const newAnimatable = {
1439
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
735
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1440
736
  name: value.name,
1441
737
  type: value.type,
1442
738
  default: value.default ?? false,
@@ -1449,7 +745,7 @@ function createAnimatable(value) {
1449
745
  return newAnimatable;
1450
746
  } else if (value.type === "rgb") {
1451
747
  const newAnimatable = {
1452
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
748
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1453
749
  name: value.name,
1454
750
  type: value.type,
1455
751
  default: value.default ?? { r: 0, g: 0, b: 0 },
@@ -1466,7 +762,7 @@ function createAnimatable(value) {
1466
762
  return newAnimatable;
1467
763
  } else if (value.type === "hsl") {
1468
764
  const newAnimatable = {
1469
- id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
765
+ id: value.id ?? (0, import_utils2.createBrowserSafeId)(),
1470
766
  name: value.name,
1471
767
  type: value.type,
1472
768
  default: value.default ?? { h: 0, s: 0, l: 0 },
@@ -1486,51 +782,16 @@ function createAnimatable(value) {
1486
782
  }
1487
783
  createAnimatable({ type: "euler", name: "Rotation" });
1488
784
 
1489
- // src/functions/exportable-bodies.ts
1490
- function asGroupEntries(world) {
1491
- return Object.values(world).filter((entry) => entry.type === "group");
1492
- }
1493
- function selectExportableGroupEntries(world, filterIds) {
1494
- const groupEntries = asGroupEntries(world);
1495
- const filterSet = Array.isArray(filterIds) && filterIds.length > 0 ? new Set(filterIds) : null;
1496
- if (filterSet) {
1497
- return groupEntries.filter((entry) => filterSet.has(entry.id));
1498
- }
1499
- const rootBoundsGroups = groupEntries.filter(
1500
- (entry) => Boolean(entry.rootBounds)
1501
- );
1502
- if (rootBoundsGroups.length > 0) {
1503
- return rootBoundsGroups;
1504
- }
1505
- const explicitRootGroups = groupEntries.filter(
1506
- (entry) => entry.root === true
1507
- );
1508
- if (explicitRootGroups.length > 0) {
1509
- return explicitRootGroups;
1510
- }
1511
- const topLevelGroups = groupEntries.filter((entry) => !entry.parent);
1512
- if (topLevelGroups.length > 0) {
1513
- return topLevelGroups;
1514
- }
1515
- return groupEntries;
1516
- }
1517
-
1518
785
  // src/store.ts
1519
- THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
1520
786
  (0, import_immer.enableMapSet)();
1521
- var VizijSlice = (set, get) => ({
1522
- // worldRef: createRef<THREE.Group>(),
787
+ var VizijSlice = (set, _get) => ({
1523
788
  world: {},
1524
- refs: {},
1525
789
  animatables: {},
1526
790
  values: /* @__PURE__ */ new Map(),
1527
- renderHit: false,
1528
791
  preferences: {
1529
792
  damping: false
1530
793
  },
1531
794
  elementSelection: [],
1532
- hoveredElement: null,
1533
- slotConfig: {},
1534
795
  clearSelection: () => {
1535
796
  set({ elementSelection: [] });
1536
797
  },
@@ -1541,66 +802,6 @@ var VizijSlice = (set, get) => ({
1541
802
  })
1542
803
  );
1543
804
  },
1544
- setHoveredElement: (selection) => {
1545
- set({ hoveredElement: selection });
1546
- },
1547
- onElementClick: (selection, _chain, event) => {
1548
- event.stopPropagation();
1549
- const makeKey = (sel) => `${sel.namespace}__${sel.type}__${sel.id}`;
1550
- const stack = [];
1551
- const seen = /* @__PURE__ */ new Set();
1552
- const push = (sel) => {
1553
- if (!sel) return;
1554
- if (!sel.id || !sel.namespace || !sel.type) return;
1555
- const key = makeKey(sel);
1556
- if (seen.has(key)) return;
1557
- seen.add(key);
1558
- stack.push(sel);
1559
- };
1560
- const intersections = event.intersections ?? [];
1561
- intersections.forEach((hit) => {
1562
- const hitSelection = hit.object?.userData?.selection;
1563
- push(hitSelection);
1564
- });
1565
- if (stack.length === 0) {
1566
- push(selection);
1567
- }
1568
- const stackKeys = new Set(stack.map(makeKey));
1569
- const primary = stack[0];
1570
- const primaryKey = makeKey(primary);
1571
- set(
1572
- (0, import_immer.produce)((state) => {
1573
- if (event.metaKey) {
1574
- const existing = state.elementSelection ?? [];
1575
- const alreadySelected = existing.some(
1576
- (item) => makeKey(item) === primaryKey
1577
- );
1578
- const existingWithoutStack = existing.filter(
1579
- (item) => !stackKeys.has(makeKey(item))
1580
- );
1581
- if (alreadySelected) {
1582
- state.elementSelection = existingWithoutStack;
1583
- } else {
1584
- state.elementSelection = [...stack, ...existingWithoutStack];
1585
- }
1586
- } else {
1587
- state.elementSelection = stack;
1588
- }
1589
- })
1590
- );
1591
- },
1592
- getExportableBodies: (filterIds) => {
1593
- const worldData = get().world;
1594
- const candidateGroups = selectExportableGroupEntries(
1595
- worldData,
1596
- filterIds
1597
- );
1598
- const refs = get().refs;
1599
- return candidateGroups.flatMap((entry) => {
1600
- const mounted = Object.values(refs[entry.id] ?? {}).map((ref) => ref.current).find((object) => Boolean(object));
1601
- return mounted ? [mounted] : [];
1602
- });
1603
- },
1604
805
  setGeometry: (id, geometry) => {
1605
806
  set(
1606
807
  (0, import_immer.produce)((state) => {
@@ -1611,7 +812,7 @@ var VizijSlice = (set, get) => ({
1611
812
  setValue: (id, namespace, value) => {
1612
813
  set(
1613
814
  (0, import_immer.produce)((state) => {
1614
- const lookupId = (0, import_utils8.getLookup)(namespace, id);
815
+ const lookupId = (0, import_utils3.getLookup)(namespace, id);
1615
816
  if (typeof value === "function") {
1616
817
  const current = state.values.get(lookupId);
1617
818
  if (current !== void 0) {
@@ -1640,7 +841,7 @@ var VizijSlice = (set, get) => ({
1640
841
  set(
1641
842
  (0, import_immer.produce)((state) => {
1642
843
  writes.forEach(({ id, namespace, value }) => {
1643
- const lookupId = (0, import_utils8.getLookup)(namespace, id);
844
+ const lookupId = (0, import_utils3.getLookup)(namespace, id);
1644
845
  state.values.set(lookupId, value);
1645
846
  });
1646
847
  })
@@ -1745,59 +946,30 @@ var VizijSlice = (set, get) => ({
1745
946
  state.world[elementId].features[featureName] = {
1746
947
  animated: true,
1747
948
  value: animatable.id
1748
- };
1749
- state.animatables[animatable.id] = animatable;
1750
- })
1751
- );
1752
- },
1753
- createStatic: (elementId, featureName, value) => {
1754
- set(
1755
- (0, import_immer.produce)((state) => {
1756
- state.world[elementId].features[featureName] = {
1757
- animated: false,
1758
- value
1759
- };
1760
- })
1761
- );
1762
- },
1763
- setAnimatableValue: (id, value) => {
1764
- set(
1765
- (0, import_immer.produce)((state) => {
1766
- console.log("Setting animatable value", id, value);
1767
- state.animatables[id] = value;
1768
- })
1769
- );
1770
- },
1771
- setSlot: (parentId, parentNamespace, childId, childNamespace) => {
1772
- set(
1773
- (0, import_immer.produce)((state) => {
1774
- const parentLookupId = (0, import_utils8.getLookup)(parentNamespace, parentId);
1775
- const childLookupId = (0, import_utils8.getLookup)(childNamespace, childId);
1776
- state.slotConfig[parentLookupId] = childLookupId;
949
+ };
950
+ state.animatables[animatable.id] = animatable;
1777
951
  })
1778
952
  );
1779
953
  },
1780
- setSlots: (slots, replace) => {
954
+ createStatic: (elementId, featureName, value) => {
1781
955
  set(
1782
956
  (0, import_immer.produce)((state) => {
1783
- if (replace) {
1784
- state.slotConfig = slots;
1785
- } else {
1786
- state.slotConfig = { ...state.slotConfig, ...slots };
1787
- }
957
+ state.world[elementId].features[featureName] = {
958
+ animated: false,
959
+ value
960
+ };
1788
961
  })
1789
962
  );
1790
963
  },
1791
- clearSlot: (parentId, parentNamespace) => {
964
+ setAnimatableValue: (id, value) => {
1792
965
  set(
1793
966
  (0, import_immer.produce)((state) => {
1794
- const parentLookupId = (0, import_utils8.getLookup)(parentNamespace, parentId);
1795
- delete state.slotConfig[parentLookupId];
967
+ console.log("Setting animatable value", id, value);
968
+ state.animatables[id] = value;
1796
969
  })
1797
970
  );
1798
971
  },
1799
972
  setVizij: (scene, animatables) => {
1800
- recordRenderCounter("rootReplacementCount");
1801
973
  set({
1802
974
  world: scene,
1803
975
  animatables
@@ -1805,7 +977,6 @@ var VizijSlice = (set, get) => ({
1805
977
  },
1806
978
  addWorldElements(world, animatables, replace) {
1807
979
  if (replace) {
1808
- recordRenderCounter("rootReplacementCount");
1809
980
  set({ world, animatables });
1810
981
  } else {
1811
982
  set((state) => ({
@@ -1818,22 +989,14 @@ var VizijSlice = (set, get) => ({
1818
989
  set((state) => ({
1819
990
  preferences: { ...state.preferences, ...preferences }
1820
991
  }));
1821
- },
1822
- setReference: (id, namespace, ref) => {
1823
- set((state) => ({
1824
- refs: {
1825
- ...state.refs,
1826
- [id]: { ...state.refs[id], [namespace]: ref }
1827
- }
1828
- }));
1829
992
  }
1830
993
  });
1831
- var useDefaultVizijStore = (0, import_zustand2.create)()(
994
+ var useDefaultVizijStore = (0, import_zustand.create)()(
1832
995
  (0, import_middleware.subscribeWithSelector)((set, get) => ({
1833
996
  ...VizijSlice(set, get)
1834
997
  }))
1835
998
  );
1836
- var createVizijStore = (initial) => (0, import_zustand2.create)()(
999
+ var createVizijStore = (initial) => (0, import_zustand.create)()(
1837
1000
  (0, import_middleware.subscribeWithSelector)((set, get) => ({
1838
1001
  ...VizijSlice(set, get),
1839
1002
  ...initial ?? {}
@@ -1851,7 +1014,6 @@ var ShapeMaterial = /* @__PURE__ */ ((ShapeMaterial2) => {
1851
1014
  })(ShapeMaterial || {});
1852
1015
 
1853
1016
  // src/types/tone-mapping.ts
1854
- var import_three = require("three");
1855
1017
  var TONE_MAPPING_MODES = [
1856
1018
  "none",
1857
1019
  "agx",
@@ -1865,353 +1027,32 @@ var TONE_MAPPING_LABELS = {
1865
1027
  neutral: "Neutral"
1866
1028
  };
1867
1029
  var DEFAULT_TONE_MAPPING_MODE = "none";
1868
- var TONE_MAPPING_CONSTANTS = {
1869
- none: import_three.NoToneMapping,
1870
- agx: import_three.AgXToneMapping,
1871
- aces: import_three.ACESFilmicToneMapping,
1872
- neutral: import_three.NeutralToneMapping
1873
- };
1874
1030
  function isToneMappingMode(value) {
1875
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(TONE_MAPPING_CONSTANTS, value);
1876
- }
1877
- function resolveToneMapping(mode) {
1878
- if (isToneMappingMode(mode)) {
1879
- return TONE_MAPPING_CONSTANTS[mode];
1880
- }
1881
- return TONE_MAPPING_CONSTANTS[DEFAULT_TONE_MAPPING_MODE];
1031
+ return typeof value === "string" && TONE_MAPPING_MODES.includes(value);
1882
1032
  }
1883
1033
  function normalizeToneMappingMode(value) {
1884
1034
  return isToneMappingMode(value) ? value : DEFAULT_TONE_MAPPING_MODE;
1885
1035
  }
1886
1036
 
1887
- // src/effects/selection-glow-effect.tsx
1888
- var import_react9 = require("react");
1889
- var import_fiber = require("@react-three/fiber");
1890
- var import_shallow7 = require("zustand/react/shallow");
1891
- var import_three2 = require("three");
1892
- var import_jsx_runtime6 = require("react/jsx-runtime");
1893
- function SelectionGlowEffect({
1894
- enabled = false,
1895
- color = "#ff1010",
1896
- opacity = 0.9,
1897
- thresholdAngle = 2
1898
- }) {
1899
- const selections = useVizijStore(
1900
- (0, import_shallow7.useShallow)(
1901
- (state) => enabled ? state.elementSelection ?? [] : []
1902
- )
1903
- );
1904
- if (!enabled || selections.length === 0) {
1905
- return null;
1906
- }
1907
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react9.Fragment, { children: selections.map((selection) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1908
- SelectionOutline,
1909
- {
1910
- selection,
1911
- color: selection.color ?? color,
1912
- opacity,
1913
- thresholdAngle
1914
- },
1915
- `${selection.namespace}:${selection.id}`
1916
- )) });
1917
- }
1918
- function SelectionOutline({
1919
- selection,
1920
- color,
1921
- opacity,
1922
- thresholdAngle
1923
- }) {
1924
- const target = useVizijStore(
1925
- (0, import_shallow7.useShallow)((state) => {
1926
- const entry = state.world[selection.id];
1927
- const ref = state.refs[selection.id]?.[selection.namespace];
1928
- const geometry = entry?.geometry ?? null;
1929
- return { ref, geometry };
1930
- })
1931
- );
1932
- const sourceRef = target.ref;
1933
- const edgesGeometry = (0, import_react9.useMemo)(() => {
1934
- if (!target.geometry) return null;
1935
- const edges = new import_three2.EdgesGeometry(target.geometry, thresholdAngle);
1936
- return edges;
1937
- }, [target.geometry, thresholdAngle]);
1938
- (0, import_react9.useEffect)(() => () => edgesGeometry?.dispose(), [edgesGeometry]);
1939
- const material = (0, import_react9.useMemo)(() => {
1940
- const mat = new import_three2.LineBasicMaterial({
1941
- color: new import_three2.Color(color),
1942
- transparent: true,
1943
- opacity,
1944
- blending: import_three2.AdditiveBlending,
1945
- depthTest: false,
1946
- depthWrite: false,
1947
- toneMapped: false
1948
- });
1949
- return mat;
1950
- }, [color, opacity]);
1951
- (0, import_react9.useEffect)(() => () => material.dispose(), [material]);
1952
- const lineRef = (0, import_react9.useRef)(null);
1953
- (0, import_fiber.useFrame)(() => {
1954
- const source = sourceRef?.current;
1955
- const line = lineRef.current;
1956
- if (!source || !line) return;
1957
- copyWorldTransform(source, line);
1958
- line.visible = source.visible;
1959
- });
1960
- if (!sourceRef || !edgesGeometry) {
1961
- return null;
1962
- }
1963
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1964
- "lineSegments",
1965
- {
1966
- ref: lineRef,
1967
- geometry: edgesGeometry,
1968
- material,
1969
- frustumCulled: false,
1970
- renderOrder: 1e3
1971
- }
1972
- );
1973
- }
1974
- var tempMatrix = new import_three2.Matrix4();
1975
- var tempPosition = new import_three2.Vector3();
1976
- var tempQuaternion = new import_three2.Quaternion();
1977
- var tempScale = new import_three2.Vector3();
1978
- function copyWorldTransform(source, target) {
1979
- source.updateWorldMatrix(true, false);
1980
- tempMatrix.copy(source.matrixWorld);
1981
- tempMatrix.decompose(tempPosition, tempQuaternion, tempScale);
1982
- target.position.copy(tempPosition);
1983
- target.quaternion.copy(tempQuaternion);
1984
- target.scale.copy(tempScale);
1985
- target.updateMatrix();
1986
- }
1037
+ // src/context.ts
1038
+ var import_react3 = require("react");
1039
+ var VizijContext = (0, import_react3.createContext)(null);
1987
1040
 
1988
- // src/vizij.tsx
1989
- var import_jsx_runtime7 = require("react/jsx-runtime");
1990
- import_three3.Object3D.DEFAULT_UP.set(0, 0, 1);
1991
- function Vizij({
1992
- style,
1993
- className,
1994
- rootId,
1995
- namespace = "default",
1996
- showSafeArea = false,
1997
- showSelectionGlow = false,
1998
- toneMapping = "none",
1999
- onPointerMissed
2000
- }) {
2001
- const ctx = (0, import_react10.useContext)(VizijContext);
2002
- (0, import_react10.useEffect)(() => {
2003
- recordRenderCounter("canvasMountCount");
2004
- recordRenderCounter("mountedCanvasCount");
2005
- return () => {
2006
- recordRenderCounter("mountedCanvasCount", -1);
2007
- };
2008
- }, []);
2009
- if (ctx) {
2010
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2011
- import_fiber2.Canvas,
2012
- {
2013
- shadows: false,
2014
- style,
2015
- className,
2016
- onPointerMissed,
2017
- gl: {
2018
- outputColorSpace: import_three3.SRGBColorSpace,
2019
- antialias: true
2020
- },
2021
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2022
- MemoizedInnerVizij,
2023
- {
2024
- rootId,
2025
- namespace,
2026
- showSafeArea,
2027
- showSelectionGlow,
2028
- toneMapping
2029
- }
2030
- )
2031
- }
2032
- );
2033
- } else {
2034
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VizijContext.Provider, { value: useDefaultVizijStore, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2035
- import_fiber2.Canvas,
2036
- {
2037
- style,
2038
- className,
2039
- onPointerMissed,
2040
- gl: {
2041
- outputColorSpace: import_three3.SRGBColorSpace,
2042
- antialias: true
2043
- },
2044
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2045
- MemoizedInnerVizij,
2046
- {
2047
- rootId,
2048
- namespace,
2049
- showSafeArea,
2050
- showSelectionGlow,
2051
- toneMapping
2052
- }
2053
- )
2054
- }
2055
- ) });
2056
- }
2057
- }
2058
- function InnerVizij({
2059
- rootId,
2060
- namespace = "default",
2061
- container,
2062
- showSafeArea,
2063
- showSelectionGlow,
2064
- toneMapping = "none"
2065
- }) {
2066
- const sceneParentSizing = container ? {
2067
- width: container.width * container.resolution,
2068
- height: container.height * container.resolution
2069
- } : void 0;
2070
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2071
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ambientLight", { intensity: Math.PI / 2 }),
2072
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToneMappingController, { mode: toneMapping }),
2073
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2074
- import_drei3.OrthographicCamera,
2075
- {
2076
- makeDefault: true,
2077
- position: [0, 0, 100],
2078
- near: 0.1,
2079
- far: 1e3
2080
- }
2081
- ),
2082
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react10.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2083
- World,
2084
- {
2085
- rootId,
2086
- namespace,
2087
- parentSizing: sceneParentSizing
2088
- }
2089
- ) }),
2090
- showSelectionGlow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectionGlowEffect, { enabled: true }),
2091
- showSafeArea && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SafeAreaRenderer, { rootId })
2092
- ] });
2093
- }
2094
- var MemoizedInnerVizij = (0, import_react10.memo)(InnerVizij);
2095
- function ToneMappingController({ mode }) {
2096
- const gl = (0, import_fiber2.useThree)((state) => state.gl);
2097
- const scene = (0, import_fiber2.useThree)((state) => state.scene);
2098
- (0, import_react10.useEffect)(() => {
2099
- const resolved = resolveToneMapping(mode);
2100
- if (gl.toneMapping === resolved) {
2101
- return;
2102
- }
2103
- gl.toneMapping = resolved;
2104
- scene.traverse((object) => {
2105
- const material = object.material;
2106
- if (!material) {
2107
- return;
2108
- }
2109
- if (Array.isArray(material)) {
2110
- material.forEach((mat) => {
2111
- mat.needsUpdate = true;
2112
- });
2113
- } else {
2114
- material.needsUpdate = true;
2115
- }
2116
- });
2117
- }, [gl, scene, mode]);
2118
- return null;
2119
- }
2120
- function InnerWorld({
2121
- rootId,
2122
- namespace = "default",
2123
- parentSizing
2124
- }) {
2125
- const [present, rootBounds] = useVizijStore(
2126
- (0, import_shallow8.useShallow)((state) => {
2127
- const group = state.world[rootId];
2128
- const bounds = group?.rootBounds ?? defaultRootBounds;
2129
- return [group !== void 0, bounds];
2130
- })
2131
- );
2132
- const { camera, size } = (0, import_fiber2.useThree)((state) => ({
2133
- camera: state.camera,
2134
- size: state.size
2135
- }));
2136
- (0, import_react10.useEffect)(() => {
2137
- const width = rootBounds.size.x;
2138
- const height = rootBounds.size.y;
2139
- if (camera && parentSizing === void 0 && camera.isOrthographicCamera) {
2140
- const zoom = Math.min(size.width / width, size.height / height);
2141
- const center = rootBounds.center;
2142
- if (camera.zoom !== zoom) {
2143
- camera.zoom = zoom;
2144
- camera.updateProjectionMatrix();
2145
- }
2146
- if (camera.position.x !== center.x || camera.position.y !== center.y) {
2147
- camera.position.x = center.x;
2148
- camera.position.y = center.y;
2149
- camera.updateProjectionMatrix();
2150
- }
2151
- } else if (camera && parentSizing !== void 0 && camera.isOrthographicCamera) {
2152
- const zoom = Math.min(
2153
- parentSizing.width / width,
2154
- parentSizing.height / height
2155
- );
2156
- const center = rootBounds.center;
2157
- camera.left = -0.5 * parentSizing.width / zoom + center.x;
2158
- camera.right = 0.5 * parentSizing.width / zoom + center.x;
2159
- camera.top = 0.5 * parentSizing.height / zoom + center.y;
2160
- camera.bottom = -0.5 * parentSizing.height / zoom + center.y;
2161
- camera.updateProjectionMatrix();
2162
- }
2163
- }, [rootBounds, camera, parentSizing, size]);
2164
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_error_boundary.ErrorBoundary, { fallback: null, children: [
2165
- present && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Renderable, { id: rootId, namespace, chain: [] }),
2166
- !present && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2167
- import_drei3.Text,
2168
- {
2169
- position: [0, 0, 0],
2170
- color: "white",
2171
- anchorX: "center",
2172
- anchorY: "middle",
2173
- fontSize: 0.7,
2174
- children: "No Output"
2175
- }
2176
- )
2177
- ] });
2178
- }
2179
- var World = (0, import_react10.memo)(InnerWorld);
2180
- function SafeAreaRenderer({ rootId }) {
2181
- const rootBounds = useVizijStore((state) => {
2182
- const group = state.world[rootId];
2183
- return group?.rootBounds ?? defaultRootBounds;
2184
- });
2185
- const left = rootBounds.center.x - rootBounds.size.x / 2;
2186
- const right = rootBounds.center.x + rootBounds.size.x / 2;
2187
- const top = rootBounds.center.y + rootBounds.size.y / 2;
2188
- const bottom = rootBounds.center.y - rootBounds.size.y / 2;
2189
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2190
- import_drei3.Line,
2191
- {
2192
- points: [
2193
- [left, top, 99],
2194
- [right, top, 99],
2195
- [right, bottom, 99],
2196
- [left, bottom, 99],
2197
- [left, top, 99]
2198
- ],
2199
- color: "red",
2200
- lineWidth: 2
2201
- }
2202
- );
1041
+ // src/hooks/use-vizij-store.ts
1042
+ var import_react4 = require("react");
1043
+ var import_zustand2 = require("zustand");
1044
+ function useVizijStore(selector) {
1045
+ const store = (0, import_react4.useContext)(VizijContext);
1046
+ if (!store) throw new Error("Missing VizijProvider in the tree");
1047
+ return (0, import_zustand2.useStore)(store, selector);
2203
1048
  }
2204
- var defaultRootBounds = {
2205
- center: { x: 0, y: 0 },
2206
- size: { x: 5, y: 4 }
2207
- };
2208
1049
 
2209
1050
  // src/hooks/use-vizij-store-subscription.ts
2210
- var import_react11 = require("react");
1051
+ var import_react5 = require("react");
2211
1052
  function useVizijStoreSubscription(selector, listener) {
2212
- const store = (0, import_react11.useContext)(VizijContext);
1053
+ const store = (0, import_react5.useContext)(VizijContext);
2213
1054
  if (!store) throw new Error("Missing VizijProvider in the tree");
2214
- (0, import_react11.useEffect)(() => {
1055
+ (0, import_react5.useEffect)(() => {
2215
1056
  const initialValue = selector(store.getState());
2216
1057
  listener(initialValue);
2217
1058
  return store.subscribe(selector, listener);
@@ -2219,27 +1060,51 @@ function useVizijStoreSubscription(selector, listener) {
2219
1060
  }
2220
1061
 
2221
1062
  // src/hooks/use-vizij-store-setter.ts
2222
- var import_react12 = require("react");
1063
+ var import_react6 = require("react");
2223
1064
  function useVizijStoreSetter() {
2224
- const store = (0, import_react12.useContext)(VizijContext);
1065
+ const store = (0, import_react6.useContext)(VizijContext);
2225
1066
  if (!store) throw new Error("Missing VizijProvider in the tree");
2226
1067
  return store.setState;
2227
1068
  }
2228
1069
 
2229
1070
  // src/hooks/use-vizij-store-getter.ts
2230
- var import_react13 = require("react");
1071
+ var import_react7 = require("react");
2231
1072
  function useVizijStoreGetter() {
2232
- const store = (0, import_react13.useContext)(VizijContext);
1073
+ const store = (0, import_react7.useContext)(VizijContext);
2233
1074
  if (!store) throw new Error("Missing VizijProvider in the tree");
2234
1075
  return store.getState;
2235
1076
  }
2236
1077
 
2237
1078
  // src/functions/load-gltf.ts
2238
- var THREE5 = __toESM(require("three"));
1079
+ var THREE2 = __toESM(require("three"));
2239
1080
  var import_three_stdlib = require("three-stdlib");
2240
1081
 
1082
+ // src/memoryInvestigation.ts
1083
+ function getGlobalMemoryInvestigation() {
1084
+ return globalThis;
1085
+ }
1086
+ function updateRenderState(updater) {
1087
+ const globalObj = getGlobalMemoryInvestigation();
1088
+ if (!globalObj.__VIZIJ_MEMORY_INVESTIGATION__?.enabled) {
1089
+ return;
1090
+ }
1091
+ if (!globalObj.__vizijMemoryDebugState) {
1092
+ globalObj.__vizijMemoryDebugState = {};
1093
+ }
1094
+ if (!globalObj.__vizijMemoryDebugState.render) {
1095
+ globalObj.__vizijMemoryDebugState.render = {};
1096
+ }
1097
+ updater(globalObj.__vizijMemoryDebugState.render);
1098
+ }
1099
+ function recordRenderCounter(key, delta = 1) {
1100
+ updateRenderState((renderState) => {
1101
+ const current = typeof renderState[key] === "number" ? Number(renderState[key]) : 0;
1102
+ renderState[key] = current + delta;
1103
+ });
1104
+ }
1105
+
2241
1106
  // src/functions/gltf-loading/traverse-three.ts
2242
- var THREE4 = __toESM(require("three"));
1107
+ var THREE = __toESM(require("three"));
2243
1108
 
2244
1109
  // src/functions/gltf-loading/map-features.ts
2245
1110
  function mapFeatures(features) {
@@ -2264,18 +1129,18 @@ function mapFeatures(features) {
2264
1129
  }
2265
1130
 
2266
1131
  // src/functions/gltf-loading/import-scene.ts
2267
- var import_three6 = require("three");
1132
+ var import_three3 = require("three");
2268
1133
 
2269
1134
  // src/functions/gltf-loading/import-group.ts
2270
- var import_three5 = require("three");
2271
- var import_utils11 = require("@vizij/utils");
1135
+ var import_three2 = require("three");
1136
+ var import_utils6 = require("@vizij/utils");
2272
1137
 
2273
1138
  // src/functions/gltf-loading/import-mesh.ts
2274
- var import_three4 = require("three");
2275
- var import_utils10 = require("@vizij/utils");
1139
+ var import_three = require("three");
1140
+ var import_utils5 = require("@vizij/utils");
2276
1141
 
2277
1142
  // src/functions/gltf-loading/import-geometry.ts
2278
- var import_utils9 = require("@vizij/utils");
1143
+ var import_utils4 = require("@vizij/utils");
2279
1144
 
2280
1145
  // src/functions/gltf-loading/morph-keys.ts
2281
1146
  function slugifyMorphName(name, fallbackIndex) {
@@ -2310,7 +1175,7 @@ function importGeometry(geometry, mesh) {
2310
1175
  const usedKeys = /* @__PURE__ */ new Set();
2311
1176
  Object.entries(mesh.morphTargetDictionary ?? {}).forEach(
2312
1177
  ([name, index]) => {
2313
- const morphId = (0, import_utils9.createBrowserSafeId)();
1178
+ const morphId = (0, import_utils4.createBrowserSafeId)();
2314
1179
  const featureKey = sanitizeMorphKey(name, index, usedKeys);
2315
1180
  morphIds.push(featureKey);
2316
1181
  features[featureKey] = {
@@ -2340,13 +1205,13 @@ function importGeometry(geometry, mesh) {
2340
1205
  }
2341
1206
 
2342
1207
  // src/functions/gltf-loading/import-mesh.ts
2343
- import_three4.Object3D.DEFAULT_UP.set(0, 0, 1);
1208
+ import_three.Object3D.DEFAULT_UP.set(0, 0, 1);
2344
1209
  function importMesh(mesh, colorLookup) {
2345
1210
  let world = {};
2346
1211
  let animatables = {};
2347
1212
  let newColorLookup = {};
2348
1213
  const translationAnimatable = {
2349
- id: (0, import_utils10.createBrowserSafeId)(),
1214
+ id: (0, import_utils5.createBrowserSafeId)(),
2350
1215
  name: `${mesh.name ?? "Mesh"} translation`,
2351
1216
  type: "vector3",
2352
1217
  default: { x: mesh.position.x, y: mesh.position.y, z: mesh.position.z },
@@ -2362,7 +1227,7 @@ function importMesh(mesh, colorLookup) {
2362
1227
  [translationAnimatable.id]: translationAnimatable
2363
1228
  };
2364
1229
  const rotationAnimatable = {
2365
- id: (0, import_utils10.createBrowserSafeId)(),
1230
+ id: (0, import_utils5.createBrowserSafeId)(),
2366
1231
  name: `${mesh.name ?? "Mesh"} rotation`,
2367
1232
  type: "euler",
2368
1233
  default: { x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z },
@@ -2375,7 +1240,7 @@ function importMesh(mesh, colorLookup) {
2375
1240
  };
2376
1241
  animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
2377
1242
  const scaleAnimatable = {
2378
- id: (0, import_utils10.createBrowserSafeId)(),
1243
+ id: (0, import_utils5.createBrowserSafeId)(),
2379
1244
  name: `${mesh.name ?? "Mesh"} scale`,
2380
1245
  type: "vector3",
2381
1246
  default: { x: mesh.scale.x, y: mesh.scale.y, z: mesh.scale.z },
@@ -2391,7 +1256,7 @@ function importMesh(mesh, colorLookup) {
2391
1256
  const colorName = material.name;
2392
1257
  const label = (suffix) => colorName ? `${colorName} ${suffix}` : `${mesh.name ?? "Mesh"} ${suffix}`;
2393
1258
  const makeNumberAnimatable = (suffix, value, constraints) => ({
2394
- id: (0, import_utils10.createBrowserSafeId)(),
1259
+ id: (0, import_utils5.createBrowserSafeId)(),
2395
1260
  name: label(suffix),
2396
1261
  type: "number",
2397
1262
  default: value,
@@ -2402,7 +1267,7 @@ function importMesh(mesh, colorLookup) {
2402
1267
  }
2403
1268
  });
2404
1269
  const makeColorAnimatable = (suffix, value) => ({
2405
- id: (0, import_utils10.createBrowserSafeId)(),
1270
+ id: (0, import_utils5.createBrowserSafeId)(),
2406
1271
  name: label(suffix),
2407
1272
  type: "rgb",
2408
1273
  default: { r: value.r, g: value.g, b: value.b },
@@ -2597,14 +1462,14 @@ function shouldImportAsGroupChild(child) {
2597
1462
  }
2598
1463
 
2599
1464
  // src/functions/gltf-loading/import-group.ts
2600
- import_three5.Object3D.DEFAULT_UP.set(0, 0, 1);
1465
+ import_three2.Object3D.DEFAULT_UP.set(0, 0, 1);
2601
1466
  function importGroup(group, colorLookup, rootBounds) {
2602
1467
  let world = {};
2603
1468
  let animatables = {};
2604
1469
  let newColorLookup = {};
2605
1470
  const children = [];
2606
1471
  const translationAnimatable = {
2607
- id: (0, import_utils11.createBrowserSafeId)(),
1472
+ id: (0, import_utils6.createBrowserSafeId)(),
2608
1473
  name: `${group.name ?? "Group"} translation`,
2609
1474
  type: "vector3",
2610
1475
  default: { x: group.position.x, y: group.position.y, z: group.position.z },
@@ -2620,7 +1485,7 @@ function importGroup(group, colorLookup, rootBounds) {
2620
1485
  [translationAnimatable.id]: translationAnimatable
2621
1486
  };
2622
1487
  const rotationAnimatable = {
2623
- id: (0, import_utils11.createBrowserSafeId)(),
1488
+ id: (0, import_utils6.createBrowserSafeId)(),
2624
1489
  name: `${group.name ?? "Group"} rotation`,
2625
1490
  type: "euler",
2626
1491
  default: { x: group.rotation.x, y: group.rotation.y, z: group.rotation.z },
@@ -2633,7 +1498,7 @@ function importGroup(group, colorLookup, rootBounds) {
2633
1498
  };
2634
1499
  animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
2635
1500
  const scaleAnimatable = {
2636
- id: (0, import_utils11.createBrowserSafeId)(),
1501
+ id: (0, import_utils6.createBrowserSafeId)(),
2637
1502
  name: `${group.name ?? "Group"} scale`,
2638
1503
  type: "vector3",
2639
1504
  default: { x: group.scale.x, y: group.scale.y, z: group.scale.z },
@@ -2702,7 +1567,7 @@ function shouldImportAsGroupChild2(child) {
2702
1567
  }
2703
1568
 
2704
1569
  // src/functions/gltf-loading/import-scene.ts
2705
- import_three6.Object3D.DEFAULT_UP.set(0, 0, 1);
1570
+ import_three3.Object3D.DEFAULT_UP.set(0, 0, 1);
2706
1571
  function importScene(scene, rootBounds) {
2707
1572
  let world = {};
2708
1573
  let animatables = {};
@@ -2717,7 +1582,7 @@ function importScene(scene, rootBounds) {
2717
1582
  }
2718
1583
 
2719
1584
  // src/functions/gltf-loading/traverse-three.ts
2720
- THREE4.Object3D.DEFAULT_UP.set(0, 0, 1);
1585
+ THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
2721
1586
  function traverseThree(group, aggressiveImport = false, rootBounds) {
2722
1587
  const worldData = {};
2723
1588
  const animatableData = {};
@@ -2812,7 +1677,7 @@ var MATERIAL_NAME_FEATURE_KEYS = [
2812
1677
  "emissiveIntensity",
2813
1678
  "specular"
2814
1679
  ];
2815
- var MATERIAL_NAME_SUFFIXES2 = [
1680
+ var MATERIAL_NAME_SUFFIXES = [
2816
1681
  " color",
2817
1682
  " colours",
2818
1683
  " colour",
@@ -2886,7 +1751,7 @@ function extractMaterialNameFromFeature(feature) {
2886
1751
  function stripMaterialSuffixes(name) {
2887
1752
  const trimmed = name.trim();
2888
1753
  const lowered = trimmed.toLowerCase();
2889
- for (const suffix of MATERIAL_NAME_SUFFIXES2) {
1754
+ for (const suffix of MATERIAL_NAME_SUFFIXES) {
2890
1755
  if (lowered.endsWith(suffix)) {
2891
1756
  return trimmed.slice(0, trimmed.length - suffix.length).trim();
2892
1757
  }
@@ -2932,8 +1797,34 @@ function isRectangleFeatures(value) {
2932
1797
  );
2933
1798
  }
2934
1799
  }
1800
+ var PRIMITIVE_HALF_EXTENT = {
1801
+ ellipse: 1,
1802
+ rectangle: 0.5
1803
+ };
1804
+ function expandByPrimitives(box, root) {
1805
+ const center = new THREE.Vector3();
1806
+ const half = new THREE.Vector3();
1807
+ root.traverse((object) => {
1808
+ if (object.isMesh) {
1809
+ return;
1810
+ }
1811
+ const type = object.userData?.gltfExtensions?.RobotData?.type;
1812
+ const extent = typeof type === "string" ? PRIMITIVE_HALF_EXTENT[type] : void 0;
1813
+ if (extent === void 0) {
1814
+ return;
1815
+ }
1816
+ const e = object.matrixWorld.elements;
1817
+ const along = type === "ellipse" ? (a, b) => Math.hypot(a, b) : (a, b) => Math.abs(a) + Math.abs(b);
1818
+ half.set(along(e[0], e[4]), along(e[1], e[5]), along(e[2], e[6]));
1819
+ half.multiplyScalar(extent);
1820
+ center.setFromMatrixPosition(object.matrixWorld);
1821
+ box.expandByPoint(center.clone().sub(half));
1822
+ box.expandByPoint(center.clone().add(half));
1823
+ });
1824
+ }
2935
1825
  function deriveRootBounds(group) {
2936
- const boundingBox = new THREE4.Box3().setFromObject(group);
1826
+ const boundingBox = new THREE.Box3().setFromObject(group);
1827
+ expandByPrimitives(boundingBox, group);
2937
1828
  if (boundingBox.isEmpty()) {
2938
1829
  return null;
2939
1830
  }
@@ -2961,10 +1852,10 @@ function deriveRootBounds(group) {
2961
1852
  }
2962
1853
 
2963
1854
  // src/functions/vizij-bundle.ts
2964
- var import_utils12 = require("@vizij/utils");
1855
+ var import_utils7 = require("@vizij/utils");
2965
1856
  var BUNDLE_KEYS = ["VIZIJ_bundle"];
2966
1857
  function cloneBundle(value) {
2967
- return (0, import_utils12.cloneDeepSafe)(value);
1858
+ return (0, import_utils7.cloneDeepSafe)(value);
2968
1859
  }
2969
1860
  function readExtensionValue(extensionContainer) {
2970
1861
  for (const key of BUNDLE_KEYS) {
@@ -3090,7 +1981,7 @@ function applyVizijBundle(object, bundle) {
3090
1981
  }
3091
1982
 
3092
1983
  // src/functions/gltf-loading/extract-animations.ts
3093
- var import_utils13 = require("@vizij/utils");
1984
+ var import_utils8 = require("@vizij/utils");
3094
1985
  var CHANNEL_PATH_TO_TRACK_PROPERTY = {
3095
1986
  translation: "position",
3096
1987
  rotation: "quaternion",
@@ -3106,7 +1997,7 @@ function clonePlainObject(value) {
3106
1997
  if (!value) {
3107
1998
  return void 0;
3108
1999
  }
3109
- return (0, import_utils13.cloneDeepSafe)(value);
2000
+ return (0, import_utils8.cloneDeepSafe)(value);
3110
2001
  }
3111
2002
  function inferValueSize(valueType) {
3112
2003
  switch (valueType) {
@@ -3460,7 +2351,7 @@ function extractVizijAnimations(parserJson, clips) {
3460
2351
  }
3461
2352
 
3462
2353
  // src/functions/load-gltf.ts
3463
- THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
2354
+ THREE2.Object3D.DEFAULT_UP.set(0, 0, 1);
3464
2355
  var GLB_MAGIC = 1179937895;
3465
2356
  var GLB_VERSION = 2;
3466
2357
  var GLB_HEADER_BYTES = 12;
@@ -3476,14 +2367,14 @@ function parseGlbJsonChunk(buffer) {
3476
2367
  if (buffer.byteLength < GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES) {
3477
2368
  return void 0;
3478
2369
  }
3479
- const view = new DataView(buffer);
3480
- const magic = view.getUint32(0, true);
3481
- const version = view.getUint32(4, true);
2370
+ const view2 = new DataView(buffer);
2371
+ const magic = view2.getUint32(0, true);
2372
+ const version = view2.getUint32(4, true);
3482
2373
  if (magic !== GLB_MAGIC || version !== GLB_VERSION) {
3483
2374
  return void 0;
3484
2375
  }
3485
- const chunkLength = view.getUint32(GLB_HEADER_BYTES, true);
3486
- const chunkType = view.getUint32(GLB_HEADER_BYTES + 4, true);
2376
+ const chunkLength = view2.getUint32(GLB_HEADER_BYTES, true);
2377
+ const chunkType = view2.getUint32(GLB_HEADER_BYTES + 4, true);
3487
2378
  if (chunkType !== GLB_JSON_CHUNK_TYPE) {
3488
2379
  return void 0;
3489
2380
  }
@@ -3593,8 +2484,8 @@ async function loadGLTFFromBlobWithBundle(blob, aggressiveImport = false, rootBo
3593
2484
 
3594
2485
  // src/functions/export.ts
3595
2486
  var import_three_stdlib2 = require("three-stdlib");
3596
- var THREE6 = __toESM(require("three"));
3597
- THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
2487
+ var THREE3 = __toESM(require("three"));
2488
+ THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
3598
2489
  function normalizeExportError(error) {
3599
2490
  if (error instanceof Error) {
3600
2491
  return error;
@@ -3690,14 +2581,14 @@ function patchGlbJson(buffer, mutate) {
3690
2581
  return buffer;
3691
2582
  }
3692
2583
  const originalBytes = new Uint8Array(buffer);
3693
- const view = new DataView(buffer);
3694
- const magic = view.getUint32(0, true);
3695
- const version = view.getUint32(4, true);
2584
+ const view2 = new DataView(buffer);
2585
+ const magic = view2.getUint32(0, true);
2586
+ const version = view2.getUint32(4, true);
3696
2587
  if (magic !== GLB_MAGIC2 || version !== GLB_VERSION2) {
3697
2588
  return buffer;
3698
2589
  }
3699
- const jsonChunkLength = view.getUint32(GLB_HEADER_BYTES2, true);
3700
- const jsonChunkType = view.getUint32(GLB_HEADER_BYTES2 + 4, true);
2590
+ const jsonChunkLength = view2.getUint32(GLB_HEADER_BYTES2, true);
2591
+ const jsonChunkType = view2.getUint32(GLB_HEADER_BYTES2 + 4, true);
3701
2592
  if (jsonChunkType !== GLB_JSON_CHUNK_TYPE2) {
3702
2593
  return buffer;
3703
2594
  }
@@ -3781,9 +2672,9 @@ async function exportScene(root, options = {}) {
3781
2672
  }
3782
2673
  }
3783
2674
  }));
3784
- const exportRoot = root instanceof THREE6.Scene ? root : root.clone(true);
3785
- const exportTarget = exportRoot instanceof THREE6.Scene ? exportRoot : (() => {
3786
- const scene = new THREE6.Scene();
2675
+ const exportRoot = root instanceof THREE3.Scene ? root : root.clone(true);
2676
+ const exportTarget = exportRoot instanceof THREE3.Scene ? exportRoot : (() => {
2677
+ const scene = new THREE3.Scene();
3787
2678
  scene.name = root.name?.trim() || "Scene";
3788
2679
  scene.add(exportRoot);
3789
2680
  return scene;
@@ -3812,9 +2703,93 @@ async function exportScene(root, options = {}) {
3812
2703
  }
3813
2704
 
3814
2705
  // src/functions/build-scene-graph.ts
3815
- var THREE7 = __toESM(require("three"));
3816
- var import_utils14 = require("@vizij/utils");
3817
- THREE7.Object3D.DEFAULT_UP.set(0, 0, 1);
2706
+ var THREE4 = __toESM(require("three"));
2707
+ var import_utils9 = require("@vizij/utils");
2708
+
2709
+ // src/functions/create-stored-features.ts
2710
+ function createStoredFeatures(objectFeatures, animatableValues) {
2711
+ const stored = Object.entries(objectFeatures).map(
2712
+ ([key, feat]) => {
2713
+ if (feat.animated) {
2714
+ const storedFeat = {
2715
+ animated: true,
2716
+ value: animatableValues[feat.value],
2717
+ ...feat.label ? { label: feat.label } : {}
2718
+ };
2719
+ return [key, storedFeat];
2720
+ }
2721
+ const staticFeature = feat;
2722
+ return [key, staticFeature.label ? { ...staticFeature } : staticFeature];
2723
+ }
2724
+ );
2725
+ return Object.fromEntries(stored);
2726
+ }
2727
+
2728
+ // src/functions/create-stored-data.ts
2729
+ function createStoredRenderable(data, animatableValues) {
2730
+ const { geometry: _geometry, ...stored } = data;
2731
+ const storedFeatures = createStoredFeatures(
2732
+ data.features,
2733
+ animatableValues
2734
+ );
2735
+ return { ...stored, features: storedFeatures };
2736
+ }
2737
+
2738
+ // src/functions/material-name.ts
2739
+ var MATERIAL_FEATURE_KEYS = [
2740
+ "color",
2741
+ "opacity",
2742
+ "roughness",
2743
+ "metalness",
2744
+ "shininess",
2745
+ "emissive",
2746
+ "emissiveIntensity",
2747
+ "specular"
2748
+ ];
2749
+ var MATERIAL_NAME_SUFFIXES2 = [
2750
+ " color",
2751
+ " colours",
2752
+ " colour",
2753
+ " opacity",
2754
+ " roughness",
2755
+ " metalness",
2756
+ " shininess",
2757
+ " emissive intensity",
2758
+ " emissive",
2759
+ " specular"
2760
+ ];
2761
+ function deriveMaterialName(shape, values) {
2762
+ for (const key of MATERIAL_FEATURE_KEYS) {
2763
+ const feature = shape.features[key];
2764
+ if (feature && feature.animated) {
2765
+ const animatable = values[feature.value];
2766
+ const candidate = extractMaterialName(animatable?.name);
2767
+ if (candidate) {
2768
+ return candidate;
2769
+ }
2770
+ }
2771
+ }
2772
+ return void 0;
2773
+ }
2774
+ function extractMaterialName(name) {
2775
+ if (!name) {
2776
+ return void 0;
2777
+ }
2778
+ const trimmed = name.trim();
2779
+ if (!trimmed) {
2780
+ return void 0;
2781
+ }
2782
+ const lowered = trimmed.toLowerCase();
2783
+ for (const suffix of MATERIAL_NAME_SUFFIXES2) {
2784
+ if (lowered.endsWith(suffix)) {
2785
+ return trimmed.slice(0, trimmed.length - suffix.length).trim();
2786
+ }
2787
+ }
2788
+ return trimmed;
2789
+ }
2790
+
2791
+ // src/functions/build-scene-graph.ts
2792
+ THREE4.Object3D.DEFAULT_UP.set(0, 0, 1);
3818
2793
  function buildSceneGraph(world, animatables, rootId) {
3819
2794
  const entry = world[rootId];
3820
2795
  if (!entry) {
@@ -3829,7 +2804,7 @@ function buildSceneGraph(world, animatables, rootId) {
3829
2804
  const robotData = createStoredRenderable(entry, used);
3830
2805
  switch (entry.type) {
3831
2806
  case "group": {
3832
- const object = new THREE7.Group();
2807
+ const object = new THREE4.Group();
3833
2808
  object.name = entry.name;
3834
2809
  object.userData = { gltfExtensions: { RobotData: robotData } };
3835
2810
  applyFeatures(entry.features, animatables, groupTransform(object));
@@ -3845,7 +2820,7 @@ function buildSceneGraph(world, animatables, rootId) {
3845
2820
  }
3846
2821
  const material = createMaterial(entry.material);
3847
2822
  material.name = deriveMaterialName(entry, used) ?? entry.name ?? "";
3848
- const object = new THREE7.Mesh(geometry, material);
2823
+ const object = new THREE4.Mesh(geometry, material);
3849
2824
  object.name = entry.name;
3850
2825
  object.userData = { gltfExtensions: { RobotData: robotData } };
3851
2826
  applyFeatures(entry.features, animatables, {
@@ -3858,10 +2833,24 @@ function buildSceneGraph(world, animatables, rootId) {
3858
2833
  }
3859
2834
  return object;
3860
2835
  }
3861
- default:
3862
- throw new Error(
3863
- `Element "${rootId}" is a ${entry.type}, which has no exported form.`
3864
- );
2836
+ case "ellipse":
2837
+ case "rectangle": {
2838
+ const object = new THREE4.Group();
2839
+ object.name = entry.name;
2840
+ object.userData = { gltfExtensions: { RobotData: robotData } };
2841
+ const { translation, rotation } = groupTransform(object);
2842
+ applyFeatures(entry.features, animatables, {
2843
+ translation,
2844
+ rotation,
2845
+ width: (value) => {
2846
+ if ((0, import_utils9.instanceOfRawNumber)(value)) object.scale.x = value;
2847
+ },
2848
+ height: (value) => {
2849
+ if ((0, import_utils9.instanceOfRawNumber)(value)) object.scale.y = value;
2850
+ }
2851
+ });
2852
+ return object;
2853
+ }
3865
2854
  }
3866
2855
  }
3867
2856
  function animatableOf(animatables, id) {
@@ -3886,28 +2875,28 @@ function applyFeatures(features, animatables, handlers) {
3886
2875
  function groupTransform(object) {
3887
2876
  return {
3888
2877
  translation: (value) => {
3889
- if ((0, import_utils14.instanceOfRawVector3)(value)) {
2878
+ if ((0, import_utils9.instanceOfRawVector3)(value)) {
3890
2879
  object.position.set(value.x, value.y, value.z);
3891
- } else if ((0, import_utils14.instanceOfRawVector2)(value)) {
2880
+ } else if ((0, import_utils9.instanceOfRawVector2)(value)) {
3892
2881
  object.position.set(value.x, value.y, object.position.z);
3893
2882
  }
3894
2883
  },
3895
2884
  rotation: (value) => {
3896
- if ((0, import_utils14.instanceOfRawEuler)(value)) {
2885
+ if ((0, import_utils9.instanceOfRawEuler)(value)) {
3897
2886
  object.rotation.set(value.x, value.y, value.z, "ZYX");
3898
- } else if ((0, import_utils14.instanceOfRawNumber)(value)) {
2887
+ } else if ((0, import_utils9.instanceOfRawNumber)(value)) {
3899
2888
  object.rotation.set(0, 0, 0);
3900
2889
  object.rotateZ(value);
3901
2890
  }
3902
2891
  },
3903
2892
  scale: (value) => {
3904
- if ((0, import_utils14.instanceOfRawVector3)(value)) {
2893
+ if ((0, import_utils9.instanceOfRawVector3)(value)) {
3905
2894
  if (value.x === null || value.y === null || value.z === null) {
3906
2895
  object.scale.set(0.1, 0.1, 0.1);
3907
2896
  } else {
3908
2897
  object.scale.set(value.x, value.y, value.z);
3909
2898
  }
3910
- } else if ((0, import_utils14.instanceOfRawNumber)(value)) {
2899
+ } else if ((0, import_utils9.instanceOfRawNumber)(value)) {
3911
2900
  object.scale.set(value, value, value);
3912
2901
  } else {
3913
2902
  object.scale.set(1, 1, 1);
@@ -3918,19 +2907,19 @@ function groupTransform(object) {
3918
2907
  function shapeTransform(object) {
3919
2908
  return {
3920
2909
  translation: (value) => {
3921
- if ((0, import_utils14.instanceOfRawVector3)(value)) {
2910
+ if ((0, import_utils9.instanceOfRawVector3)(value)) {
3922
2911
  object.position.set(value.x, value.y, value.z);
3923
2912
  }
3924
2913
  },
3925
2914
  rotation: (value) => {
3926
- if ((0, import_utils14.instanceOfRawEuler)(value)) {
2915
+ if ((0, import_utils9.instanceOfRawEuler)(value)) {
3927
2916
  object.rotation.set(value.x, value.y, value.z, "ZYX");
3928
2917
  }
3929
2918
  },
3930
2919
  scale: (value) => {
3931
- if ((0, import_utils14.instanceOfRawVector3)(value)) {
2920
+ if ((0, import_utils9.instanceOfRawVector3)(value)) {
3932
2921
  object.scale.set(value.x, value.y, value.z);
3933
- } else if ((0, import_utils14.instanceOfRawNumber)(value)) {
2922
+ } else if ((0, import_utils9.instanceOfRawNumber)(value)) {
3934
2923
  object.scale.set(value, value, value);
3935
2924
  } else {
3936
2925
  object.scale.set(1, 1, 1);
@@ -3943,7 +2932,7 @@ function materialFactors(material) {
3943
2932
  const phong = material;
3944
2933
  return {
3945
2934
  opacity: (value) => {
3946
- if ((0, import_utils14.instanceOfRawNumber)(value)) {
2935
+ if ((0, import_utils9.instanceOfRawNumber)(value)) {
3947
2936
  material.opacity = value;
3948
2937
  material.transparent = value < 1;
3949
2938
  }
@@ -3952,41 +2941,41 @@ function materialFactors(material) {
3952
2941
  if (!standard.color) {
3953
2942
  return;
3954
2943
  }
3955
- if ((0, import_utils14.instanceOfRawRGB)(value)) {
2944
+ if ((0, import_utils9.instanceOfRawRGB)(value)) {
3956
2945
  standard.color.setRGB(value.r, value.g, value.b);
3957
- } else if ((0, import_utils14.instanceOfRawVector3)(value)) {
2946
+ } else if ((0, import_utils9.instanceOfRawVector3)(value)) {
3958
2947
  standard.color.setRGB(value.x, value.y, value.z);
3959
- } else if ((0, import_utils14.instanceOfRawHSL)(value)) {
2948
+ } else if ((0, import_utils9.instanceOfRawHSL)(value)) {
3960
2949
  standard.color.setHSL(value.h, value.s, value.l);
3961
2950
  }
3962
2951
  },
3963
2952
  roughness: (value) => {
3964
- if (standard.roughness !== void 0 && (0, import_utils14.instanceOfRawNumber)(value)) {
2953
+ if (standard.roughness !== void 0 && (0, import_utils9.instanceOfRawNumber)(value)) {
3965
2954
  standard.roughness = value;
3966
2955
  }
3967
2956
  },
3968
2957
  metalness: (value) => {
3969
- if (standard.metalness !== void 0 && (0, import_utils14.instanceOfRawNumber)(value)) {
2958
+ if (standard.metalness !== void 0 && (0, import_utils9.instanceOfRawNumber)(value)) {
3970
2959
  standard.metalness = value;
3971
2960
  }
3972
2961
  },
3973
2962
  shininess: (value) => {
3974
- if (phong.shininess !== void 0 && (0, import_utils14.instanceOfRawNumber)(value)) {
2963
+ if (phong.shininess !== void 0 && (0, import_utils9.instanceOfRawNumber)(value)) {
3975
2964
  phong.shininess = value;
3976
2965
  }
3977
2966
  },
3978
2967
  emissive: (value) => {
3979
- if (standard.emissive && (0, import_utils14.instanceOfRawRGB)(value)) {
2968
+ if (standard.emissive && (0, import_utils9.instanceOfRawRGB)(value)) {
3980
2969
  standard.emissive.setRGB(value.r, value.g, value.b);
3981
2970
  }
3982
2971
  },
3983
2972
  emissiveIntensity: (value) => {
3984
- if (standard.emissiveIntensity !== void 0 && (0, import_utils14.instanceOfRawNumber)(value)) {
2973
+ if (standard.emissiveIntensity !== void 0 && (0, import_utils9.instanceOfRawNumber)(value)) {
3985
2974
  standard.emissiveIntensity = value;
3986
2975
  }
3987
2976
  },
3988
2977
  specular: (value) => {
3989
- if (phong.specular && (0, import_utils14.instanceOfRawRGB)(value)) {
2978
+ if (phong.specular && (0, import_utils9.instanceOfRawRGB)(value)) {
3990
2979
  phong.specular.setRGB(value.r, value.g, value.b);
3991
2980
  }
3992
2981
  }
@@ -4006,7 +2995,7 @@ function morphWeights(object, shape) {
4006
2995
  targets.map((target, index) => [
4007
2996
  target,
4008
2997
  (value) => {
4009
- if ((0, import_utils14.instanceOfRawNumber)(value)) {
2998
+ if ((0, import_utils9.instanceOfRawNumber)(value)) {
4010
2999
  influences[index] = value;
4011
3000
  }
4012
3001
  }
@@ -4014,18 +3003,18 @@ function morphWeights(object, shape) {
4014
3003
  );
4015
3004
  }
4016
3005
  function createMaterial(kind) {
4017
- const parameters = { side: THREE7.DoubleSide };
3006
+ const parameters = { side: THREE4.DoubleSide };
4018
3007
  switch (kind) {
4019
3008
  case "basic" /* Basic */:
4020
- return new THREE7.MeshBasicMaterial(parameters);
3009
+ return new THREE4.MeshBasicMaterial(parameters);
4021
3010
  case "lambert" /* Lambert */:
4022
- return new THREE7.MeshLambertMaterial(parameters);
3011
+ return new THREE4.MeshLambertMaterial(parameters);
4023
3012
  case "phong" /* Phong */:
4024
- return new THREE7.MeshPhongMaterial(parameters);
3013
+ return new THREE4.MeshPhongMaterial(parameters);
4025
3014
  case "normal" /* Normal */:
4026
- return new THREE7.MeshNormalMaterial(parameters);
3015
+ return new THREE4.MeshNormalMaterial(parameters);
4027
3016
  case "standard" /* Standard */:
4028
- return new THREE7.MeshStandardMaterial(parameters);
3017
+ return new THREE4.MeshStandardMaterial(parameters);
4029
3018
  }
4030
3019
  }
4031
3020
  var SCALAR_KEYS = ["f32", "f64", "i64", "i32", "u64", "u32", "float"];
@@ -4074,15 +3063,16 @@ function authoredDefault(animatable) {
4074
3063
  0 && (module.exports = {
4075
3064
  DEFAULT_TONE_MAPPING_MODE,
4076
3065
  EmptyModelError,
4077
- InnerVizij,
4078
3066
  ShapeMaterial,
4079
3067
  TONE_MAPPING_LABELS,
4080
3068
  TONE_MAPPING_MODES,
4081
3069
  Vizij,
3070
+ VizijCanvas,
4082
3071
  VizijContext,
4083
3072
  VizijSlice,
4084
3073
  applyVizijBundle,
4085
3074
  buildSceneGraph,
3075
+ canvasMounted,
4086
3076
  createVizijStore,
4087
3077
  deriveMorphFeatureKeys,
4088
3078
  deriveRootBounds,
@@ -4097,10 +3087,9 @@ function authoredDefault(animatable) {
4097
3087
  parseGlbJsonChunk,
4098
3088
  patchGlbJson,
4099
3089
  patchVizijBundleMetadata,
4100
- resolveToneMapping,
4101
3090
  sanitizeMorphKey,
3091
+ setVizijProgress,
4102
3092
  useDefaultVizijStore,
4103
- useFeatures,
4104
3093
  useVizijStore,
4105
3094
  useVizijStoreGetter,
4106
3095
  useVizijStoreSetter,