@vizij/render 1.0.1 → 3.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.mjs CHANGED
@@ -1,1241 +1,547 @@
1
- // src/vizij.tsx
2
- import { Suspense, memo as memo6, useContext as useContext3, useEffect as useEffect7 } from "react";
3
- import { ErrorBoundary } from "react-error-boundary";
4
- import { Object3D as Object3D4, SRGBColorSpace } from "three";
5
- import { Canvas, useThree } from "@react-three/fiber";
6
- import { Line as Line3, OrthographicCamera, Text } from "@react-three/drei";
7
- import { useShallow as useShallow7 } from "zustand/react/shallow";
8
-
9
- // src/renderables/renderable.tsx
10
- import { memo as memo5 } from "react";
11
- import { useShallow as useShallow5 } from "zustand/react/shallow";
12
-
13
- // src/hooks/use-vizij-store.ts
14
- import { useContext } from "react";
15
- import { useStore } from "zustand";
16
-
17
- // src/context.ts
18
- import { createContext } from "react";
19
- var VizijContext = createContext(null);
20
-
21
- // src/hooks/use-vizij-store.ts
22
- function useVizijStore(selector) {
23
- const store = useContext(VizijContext);
24
- if (!store) throw new Error("Missing VizijProvider in the tree");
25
- return useStore(store, selector);
26
- }
27
-
28
- // src/renderables/group.tsx
29
- import { memo, useCallback, useEffect as useEffect2, useRef, useMemo } from "react";
30
- import * as THREE from "three";
31
- import { useShallow } from "zustand/react/shallow";
1
+ // src/page/canvas.tsx
2
+ import { useEffect, useRef } from "react";
32
3
  import {
33
- instanceOfRawEuler,
34
- instanceOfRawNumber,
35
- instanceOfRawVector2,
36
- instanceOfRawVector3
37
- } from "@vizij/utils";
4
+ drainPicks,
5
+ init,
6
+ mount,
7
+ onInitProgress,
8
+ placeVizij,
9
+ ready,
10
+ safeArea
11
+ } from "@vizij/runtime";
38
12
 
39
- // src/hooks/use-features.ts
40
- import { useEffect, useContext as useContext2 } from "react";
41
- import { shallow } from "zustand/shallow";
42
- import { getLookup } from "@vizij/utils";
43
- function useFeatures(namespace, features, callbacks, debugInfo) {
44
- const store = useContext2(VizijContext);
45
- if (!store) throw new Error("Missing VizijProvider in the tree");
46
- useEffect(() => {
47
- const unsubsribes = [];
48
- Object.keys(callbacks).forEach((key) => {
49
- if (!(key in features)) {
50
- return;
51
- }
52
- const featureInfo = features[key];
53
- if (!featureInfo.animated) {
54
- callbacks[key](featureInfo.value);
55
- } else {
56
- const animatableValueInfo = store.getState().animatables[featureInfo.value];
57
- if (!animatableValueInfo) {
58
- console.error(
59
- `Feature with id ${key} is animated but the animated value with id ${featureInfo.value} is not found`,
60
- debugInfo
61
- );
62
- return;
63
- }
64
- const cb = (value) => {
65
- if (value === void 0) {
66
- callbacks[key](animatableValueInfo.default);
67
- } else {
68
- callbacks[key](value);
69
- }
70
- };
71
- const defaultValue = animatableValueInfo.default;
72
- const lookupKey = getLookup(namespace, animatableValueInfo.id);
73
- const unsubscribe = store.subscribe(
74
- (state) => state.values.get(lookupKey) ?? defaultValue,
75
- cb,
76
- { equalityFn: shallow, fireImmediately: true }
77
- );
78
- unsubsribes.push(unsubscribe);
79
- }
80
- });
81
- return () => {
82
- unsubsribes.forEach((unsubscribe) => unsubscribe());
83
- };
84
- }, [features, callbacks, store, debugInfo, namespace]);
13
+ // src/page/progress.ts
14
+ var runtime = {
15
+ stage: "download",
16
+ loaded: 0,
17
+ total: null
18
+ };
19
+ var faces = /* @__PURE__ */ new Map();
20
+ var watchers = /* @__PURE__ */ new Set();
21
+ function setVizijProgress(vizijId, progress) {
22
+ if (progress === null) {
23
+ faces.delete(vizijId);
24
+ } else {
25
+ const face = faces.get(vizijId);
26
+ if (face) face.progress = progress;
27
+ else faces.set(vizijId, { progress, drawn: false });
28
+ }
29
+ notify();
30
+ }
31
+ function setRuntimeStage(stage) {
32
+ runtime = stage;
33
+ notify();
34
+ }
35
+ function markDrawn(vizijId) {
36
+ const face = faces.get(vizijId);
37
+ if (face?.drawn) return;
38
+ if (face) face.drawn = true;
39
+ else faces.set(vizijId, { progress: null, drawn: true });
40
+ notify();
41
+ }
42
+ function loadingStage(vizijId) {
43
+ const face = faces.get(vizijId);
44
+ if (face?.drawn) return null;
45
+ if (runtime?.stage === "failed") return runtime;
46
+ if (face?.progress?.stage === "failed") return face.progress;
47
+ if (runtime !== null) return runtime;
48
+ return face?.progress ?? { stage: "load" };
49
+ }
50
+ function watchLoading(vizijId, listener) {
51
+ const watcher = { vizijId, listener, last: loadingStage(vizijId) };
52
+ watchers.add(watcher);
53
+ listener(watcher.last);
54
+ return () => {
55
+ watchers.delete(watcher);
56
+ };
57
+ }
58
+ function notify() {
59
+ for (const watcher of [...watchers]) {
60
+ const next = loadingStage(watcher.vizijId);
61
+ if (sameStage(next, watcher.last)) continue;
62
+ watcher.last = next;
63
+ watcher.listener(next);
64
+ }
65
+ }
66
+ function sameStage(a, b) {
67
+ if (a === null || b === null) return a === b;
68
+ if (a.stage !== b.stage) return false;
69
+ if ("loaded" in a && "loaded" in b) {
70
+ return a.loaded === b.loaded && a.total === b.total;
71
+ }
72
+ if ("error" in a && "error" in b) return a.error === b.error;
73
+ return true;
85
74
  }
86
75
 
87
- // src/functions/create-stored-features.ts
88
- function createStoredFeatures(objectFeatures, animatableValues) {
89
- const stored = Object.entries(objectFeatures).map(
90
- ([key, feat]) => {
91
- if (feat.animated) {
92
- const storedFeat = {
93
- animated: true,
94
- value: animatableValues[feat.value],
95
- ...feat.label ? { label: feat.label } : {}
96
- };
97
- return [key, storedFeat];
98
- }
99
- const staticFeature = feat;
100
- return [key, staticFeature.label ? { ...staticFeature } : staticFeature];
76
+ // src/page/frame.ts
77
+ function frame(slots2, canvas2, view2) {
78
+ const origin = canvas2.getBoundingClientRect();
79
+ const byId = /* @__PURE__ */ new Map();
80
+ for (const slot of slots2) {
81
+ byId.set(slot.vizijId, slot);
82
+ const rect = relative(slot.element.getBoundingClientRect(), origin);
83
+ if (!sameRect(rect, slot.placed)) {
84
+ view2.placeVizij(slot.vizijId, rect);
85
+ slot.placed = rect;
101
86
  }
102
- );
103
- return Object.fromEntries(stored);
87
+ const ready2 = view2.ready(slot.vizijId);
88
+ if (ready2) markDrawn(slot.vizijId);
89
+ if (slot.element.getAttribute("data-vizij-ready") !== String(ready2)) {
90
+ slot.element.setAttribute("data-vizij-ready", String(ready2));
91
+ }
92
+ if (slot.safeArea) {
93
+ outline(slot.safeArea, view2.safeArea(slot.vizijId), origin);
94
+ }
95
+ }
96
+ for (const pick of view2.drainPicks()) {
97
+ byId.get(pick.vizijId)?.onPick?.(pick);
98
+ }
104
99
  }
105
-
106
- // src/functions/create-stored-data.ts
107
- function createStoredRenderable(data, animatableValues) {
108
- const { geometry: _geometry, ...stored } = data;
109
- const storedFeatures = createStoredFeatures(
110
- data.features,
111
- animatableValues
112
- );
113
- return { ...stored, features: storedFeatures };
100
+ function relative(rect, origin) {
101
+ return {
102
+ x: rect.x - origin.x,
103
+ y: rect.y - origin.y,
104
+ width: rect.width,
105
+ height: rect.height
106
+ };
107
+ }
108
+ function sameRect(a, b) {
109
+ return b !== null && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
110
+ }
111
+ function outline({ outline: outline2, frame: frame2 }, area, canvas2) {
112
+ if (!area) {
113
+ outline2.style.display = "none";
114
+ return;
115
+ }
116
+ const at = relative(frame2.getBoundingClientRect(), canvas2);
117
+ outline2.style.display = "block";
118
+ outline2.style.left = `${area.x - at.x}px`;
119
+ outline2.style.top = `${area.y - at.y}px`;
120
+ outline2.style.width = `${area.width}px`;
121
+ outline2.style.height = `${area.height}px`;
114
122
  }
115
123
 
116
- // src/renderables/group.tsx
124
+ // src/page/canvas.tsx
117
125
  import { jsx } from "react/jsx-runtime";
118
- THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
119
- function InnerRenderedGroup({
120
- id,
121
- namespace,
122
- chain
126
+ var canvas = null;
127
+ var mounting = null;
128
+ var live = false;
129
+ var settle;
130
+ var mounted = new Promise((resolve, reject) => {
131
+ settle = { resolve, reject };
132
+ });
133
+ var PAINT_WAIT_MS = 100;
134
+ var slots = /* @__PURE__ */ new Set();
135
+ var hosts = 0;
136
+ var loop = null;
137
+ function canvasMounted() {
138
+ return mounted;
139
+ }
140
+ var DEFAULT_STYLE = {
141
+ position: "fixed",
142
+ inset: 0,
143
+ pointerEvents: "none"
144
+ };
145
+ function VizijCanvas({
146
+ options,
147
+ className,
148
+ style
123
149
  }) {
124
- const ref = useRef(null);
125
- const group = useVizijStore(useShallow((state) => state.world[id]));
126
- const refIsNull = useVizijStore(
127
- (state) => !state.refs[id]?.[namespace]?.current
128
- );
129
- const animatables = useVizijStore(useShallow((state) => state.animatables));
130
- const setHoveredElement = useVizijStore(
131
- useShallow((state) => state.setHoveredElement)
132
- );
133
- const animatableValues = useMemo(() => {
134
- const av = {};
135
- Object.values(group.features).forEach((feat) => {
136
- if (feat.animated) {
137
- const animatable = animatables[feat.value];
138
- av[animatable.id] = animatable;
139
- }
140
- });
141
- return av;
142
- }, [group.features, animatables]);
143
- const userData = {
144
- gltfExtensions: {
145
- RobotData: createStoredRenderable(group, animatableValues)
146
- }
147
- };
148
- useFeatures(namespace, group.features, {
149
- translation: (pos) => {
150
- if (ref.current?.position && instanceOfRawVector3(pos)) {
151
- ref.current.position.set(
152
- pos.x,
153
- pos.y,
154
- pos.z
155
- );
156
- } else if (ref.current?.position && instanceOfRawVector2(pos)) {
157
- const currentZ = ref.current.position.z;
158
- ref.current.position.set(pos.x, pos.y, currentZ);
159
- }
160
- },
161
- rotation: (rot) => {
162
- if (ref.current?.rotation && instanceOfRawEuler(rot)) {
163
- ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
164
- } else if (ref.current?.rotation && instanceOfRawNumber(rot)) {
165
- ref.current.rotation.set(0, 0, 0);
166
- ref.current.rotateZ(rot);
167
- }
168
- },
169
- scale: (scale) => {
170
- if (ref.current?.scale && instanceOfRawVector3(scale)) {
171
- if (scale.x === null || scale.y === null || scale.z === null) {
172
- ref.current.scale.set(0.1, 0.1, 0.1);
173
- return;
174
- }
175
- ref.current.scale.set(scale.x, scale.y, scale.z);
176
- } else if (ref.current && instanceOfRawNumber(scale)) {
177
- ref.current.scale.set(scale, scale, scale);
178
- } else if (ref.current) {
179
- ref.current.scale.set(1, 1, 1);
180
- }
181
- }
182
- });
183
- const setReference = useVizijStore(
184
- useShallow((state) => state.setReference)
185
- );
186
- useEffect2(() => {
187
- if (ref.current && refIsNull) {
188
- setReference(group.id, namespace, ref);
150
+ const host = useRef(null);
151
+ const firstOptions = useRef(options);
152
+ useEffect(() => {
153
+ const element = host.current;
154
+ const shown = pageCanvas();
155
+ element.appendChild(shown);
156
+ if (!mounting) {
157
+ mounting = mountOnce(shown, firstOptions.current);
158
+ mounting.then(settle.resolve, settle.reject);
189
159
  }
190
- }, [group.id, namespace, ref, setReference, refIsNull]);
191
- useEffect2(() => {
192
- if (ref.current) {
193
- ref.current.name = group.name;
160
+ hosts += 1;
161
+ if (hosts === 1) {
162
+ void mounting.then(start, () => void 0);
194
163
  }
195
- }, [group.name]);
196
- const handlePointerOver = useCallback(
197
- (event) => {
198
- if (event.eventObject !== event.object) {
199
- return;
164
+ return () => {
165
+ hosts -= 1;
166
+ if (hosts === 0) {
167
+ stop();
200
168
  }
201
- setHoveredElement({ id, namespace, type: "group" });
202
- },
203
- [id, namespace, setHoveredElement]
204
- );
205
- const handlePointerOut = useCallback(
206
- (event) => {
207
- if (event.eventObject !== event.object) {
208
- return;
169
+ if (shown.parentElement === element) {
170
+ element.removeChild(shown);
209
171
  }
210
- setHoveredElement(null);
211
- },
212
- [setHoveredElement]
213
- );
172
+ };
173
+ }, []);
214
174
  return /* @__PURE__ */ jsx(
215
- "group",
175
+ "div",
216
176
  {
217
- ref,
218
- uuid: `${namespace}.${group.id}`,
219
- userData,
220
- onPointerOver: handlePointerOver,
221
- onPointerOut: handlePointerOut,
222
- children: group.children.map((child) => /* @__PURE__ */ jsx(
223
- Renderable,
224
- {
225
- id: child,
226
- namespace,
227
- chain: [...chain, id]
228
- },
229
- child
230
- ))
177
+ ref: host,
178
+ className,
179
+ style: style ?? (className ? void 0 : DEFAULT_STYLE)
231
180
  }
232
181
  );
233
182
  }
234
- var RenderedGroup = memo(InnerRenderedGroup);
235
-
236
- // src/renderables/ellipse.tsx
237
- import { memo as memo2, useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useMemo as useMemo2 } from "react";
238
- import { useShallow as useShallow2 } from "zustand/react/shallow";
239
- import {
240
- instanceOfRawNumber as instanceOfRawNumber2,
241
- instanceOfRawVector2 as instanceOfRawVector22,
242
- instanceOfRawVector3 as instanceOfRawVector32,
243
- instanceOfRawEuler as instanceOfRawEuler2,
244
- instanceOfRawRGB,
245
- instanceOfRawHSL
246
- } from "@vizij/utils";
247
- import { Circle, Line } from "@react-three/drei";
248
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
249
- function InnerRenderedEllipse({
250
- id,
251
- namespace,
252
- chain
253
- }) {
254
- const ellipseRef = useRef2(null);
255
- const materialRef = useRef2(
256
- null
257
- );
258
- const lineRef = useRef2(null);
259
- const strokeOffsetRef = useRef2(0);
260
- const strokeWidthRef = useRef2(0);
261
- const onElementClick = useVizijStore(
262
- useShallow2((state) => state.onElementClick)
263
- );
264
- const setHoveredElement = useVizijStore(
265
- useShallow2((state) => state.setHoveredElement)
266
- );
267
- const ellipse = useVizijStore(
268
- useShallow2((state) => state.world[id])
269
- );
270
- const refIsNull = useVizijStore(
271
- (state) => !state.refs[id]?.[namespace]?.current
272
- );
273
- const animatables = useVizijStore(useShallow2((state) => state.animatables));
274
- const animatableValues = useMemo2(() => {
275
- const av = {};
276
- Object.values(ellipse.features).forEach((feat) => {
277
- if (feat.animated) {
278
- const animatable = animatables[feat.value];
279
- av[animatable.id] = animatable;
280
- }
281
- });
282
- return av;
283
- }, [ellipse.features, animatables]);
284
- const selectionData = useMemo2(
285
- () => ({ id, namespace, type: "ellipse" }),
286
- [id, namespace]
287
- );
288
- const userData = useMemo2(
289
- () => ({
290
- gltfExtensions: {
291
- RobotData: createStoredRenderable(ellipse, animatableValues)
292
- },
293
- selection: selectionData
294
- }),
295
- [ellipse, animatableValues, selectionData]
296
- );
297
- useFeatures(namespace, ellipse.features, {
298
- translation: (pos) => {
299
- if (ellipseRef.current?.position && instanceOfRawVector32(pos)) {
300
- ellipseRef.current.position.set(
301
- pos.x,
302
- pos.y,
303
- pos.z
304
- );
305
- } else if (ellipseRef.current?.position && instanceOfRawVector22(pos)) {
306
- const currentZ = ellipseRef.current.position.z;
307
- ellipseRef.current.position.set(
308
- pos.x,
309
- pos.y,
310
- currentZ
311
- );
312
- }
313
- if (lineRef.current?.position && instanceOfRawVector32(pos)) {
314
- lineRef.current.position.set(
315
- pos.x,
316
- pos.y,
317
- pos.z
318
- );
319
- } else if (lineRef.current?.position && instanceOfRawVector22(pos)) {
320
- const currentZ = lineRef.current.position.z;
321
- lineRef.current.position.set(
322
- pos.x,
323
- pos.y,
324
- currentZ
325
- );
326
- }
327
- },
328
- rotation: (rot) => {
329
- if (ellipseRef.current?.rotation && instanceOfRawEuler2(rot)) {
330
- ellipseRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
331
- } else if (ellipseRef.current?.rotation && instanceOfRawNumber2(rot)) {
332
- ellipseRef.current.rotation.set(0, 0, 0);
333
- ellipseRef.current.rotateZ(rot);
334
- }
335
- if (lineRef.current?.rotation && instanceOfRawEuler2(rot)) {
336
- lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
337
- } else if (lineRef.current?.rotation && instanceOfRawNumber2(rot)) {
338
- lineRef.current.rotation.set(0, 0, 0);
339
- lineRef.current.rotateZ(rot);
340
- }
341
- },
342
- fillOpacity: (op) => {
343
- if (materialRef.current?.opacity !== void 0 && instanceOfRawNumber2(op)) {
344
- materialRef.current.opacity = op;
345
- if (op < 1) {
346
- materialRef.current.transparent = true;
347
- } else {
348
- materialRef.current.transparent = false;
349
- }
350
- materialRef.current.needsUpdate = true;
351
- }
352
- },
353
- fillColor: (color) => {
354
- if (materialRef.current?.color) {
355
- if (instanceOfRawRGB(color)) {
356
- materialRef.current.color.setRGB(color.r, color.g, color.b);
357
- materialRef.current.needsUpdate = true;
358
- } else if (instanceOfRawVector32(color)) {
359
- materialRef.current.color.setRGB(color.x, color.y, color.z);
360
- materialRef.current.needsUpdate = true;
361
- } else if (instanceOfRawHSL(color)) {
362
- materialRef.current.color.setHSL(color.h, color.s, color.l);
363
- materialRef.current.needsUpdate = true;
364
- }
365
- }
366
- },
367
- height: (height) => {
368
- if (ellipseRef.current && instanceOfRawNumber2(height)) {
369
- ellipseRef.current.scale.set(ellipseRef.current.scale.x, height, 1);
370
- }
371
- if (ellipseRef.current && lineRef.current && instanceOfRawNumber2(height)) {
372
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
373
- lineRef.current.scale.set(
374
- ellipseRef.current.scale.x + offset,
375
- height + offset,
376
- 1
377
- );
378
- }
379
- },
380
- width: (width) => {
381
- if (ellipseRef.current && instanceOfRawNumber2(width)) {
382
- ellipseRef.current.scale.set(width, ellipseRef.current.scale.y, 1);
383
- }
384
- if (ellipseRef.current && lineRef.current && instanceOfRawNumber2(width)) {
385
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
386
- lineRef.current.scale.set(
387
- width + offset,
388
- ellipseRef.current.scale.y + offset,
389
- 1
390
- );
391
- }
392
- },
393
- strokeOpacity: (strokeOpacity) => {
394
- if (lineRef.current?.material && instanceOfRawNumber2(strokeOpacity)) {
395
- lineRef.current.material.opacity = strokeOpacity;
396
- if (strokeOpacity < 1) {
397
- lineRef.current.material.transparent = true;
398
- } else {
399
- lineRef.current.material.transparent = false;
400
- }
401
- lineRef.current.material.needsUpdate = true;
402
- }
403
- },
404
- strokeColor: (strokeColor) => {
405
- if (lineRef.current?.material.color) {
406
- if (instanceOfRawRGB(strokeColor)) {
407
- lineRef.current.material.color.setRGB(
408
- strokeColor.r,
409
- strokeColor.g,
410
- strokeColor.b
411
- );
412
- lineRef.current.material.needsUpdate = true;
413
- } else if (instanceOfRawVector32(strokeColor)) {
414
- lineRef.current.material.color.setRGB(
415
- strokeColor.x,
416
- strokeColor.y,
417
- strokeColor.z
418
- );
419
- lineRef.current.material.needsUpdate = true;
420
- } else if (instanceOfRawHSL(strokeColor)) {
421
- lineRef.current.material.color.setHSL(
422
- strokeColor.h,
423
- strokeColor.s,
424
- strokeColor.l
425
- );
426
- lineRef.current.material.needsUpdate = true;
427
- }
428
- }
429
- },
430
- strokeWidth: (strokeWidth) => {
431
- if (lineRef.current?.material && ellipseRef.current) {
432
- if (instanceOfRawNumber2(strokeWidth)) {
433
- strokeWidthRef.current = strokeWidth;
434
- const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
435
- lineRef.current.scale.set(
436
- ellipseRef.current.scale.x + offset,
437
- ellipseRef.current.scale.y + offset,
438
- 1
439
- );
440
- lineRef.current.material.linewidth = strokeWidth;
441
- lineRef.current.material.needsUpdate = true;
442
- }
443
- }
444
- },
445
- strokeOffset: (strokeOffset) => {
446
- if (lineRef.current?.material && ellipseRef.current) {
447
- if (instanceOfRawNumber2(strokeOffset)) {
448
- strokeOffsetRef.current = strokeOffset;
449
- const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
450
- lineRef.current.scale.set(
451
- ellipseRef.current.scale.x + offset,
452
- ellipseRef.current.scale.y + offset,
453
- 1
454
- );
455
- }
456
- }
457
- }
183
+ async function mountOnce(shown, options) {
184
+ const unfollow = onInitProgress((progress) => {
185
+ setRuntimeStage(progress.stage === "ready" ? { stage: "mount" } : progress);
458
186
  });
459
- const setReference = useVizijStore(
460
- useShallow2((state) => state.setReference)
461
- );
462
- const points = useMemo2(() => {
463
- const n = 600;
464
- const p = [];
465
- const angleStep = 2 * Math.PI / n;
466
- for (let i = 0; i < n; i++) {
467
- const angle = i * angleStep;
468
- const x = Math.cos(angle);
469
- const y = Math.sin(angle);
470
- p.push([x, y, 0]);
471
- }
472
- return p;
473
- }, []);
474
- useEffect3(() => {
475
- if (ellipseRef.current && refIsNull)
476
- setReference(ellipse.id, namespace, ellipseRef);
477
- }, [ellipse.id, namespace, ellipseRef, setReference, refIsNull]);
478
- const handlePointerOver = useCallback2(
479
- (event) => {
480
- event.stopPropagation();
481
- setHoveredElement({ id, namespace, type: "ellipse" });
482
- },
483
- [id, namespace, setHoveredElement]
484
- );
485
- const handlePointerOut = useCallback2(
486
- (event) => {
487
- event.stopPropagation();
488
- setHoveredElement(null);
489
- },
490
- [setHoveredElement]
491
- );
492
- return /* @__PURE__ */ jsxs(Fragment, { children: [
493
- /* @__PURE__ */ jsx2(
494
- Circle,
495
- {
496
- ref: ellipseRef,
497
- userData,
498
- args: [1, 100],
499
- onPointerOver: handlePointerOver,
500
- onPointerOut: handlePointerOut,
501
- onClick: (e) => {
502
- onElementClick({ id, type: "ellipse", namespace }, [...chain, id], e);
503
- },
504
- children: /* @__PURE__ */ jsx2("meshStandardMaterial", { attach: "material", ref: materialRef })
505
- }
506
- ),
507
- showLine(ellipse) && /* @__PURE__ */ jsx2(
508
- Line,
509
- {
510
- ref: lineRef,
511
- points,
512
- onPointerOver: handlePointerOver,
513
- onPointerOut: handlePointerOut
514
- }
515
- )
516
- ] });
517
- }
518
- var RenderedEllipse = memo2(InnerRenderedEllipse);
519
- var showLine = (ellipse) => {
520
- if ("strokeOpacity" in ellipse.features) {
521
- return true;
522
- } else if ("strokeColor" in ellipse.features) {
523
- return true;
524
- } else if ("strokeWidth" in ellipse.features) {
525
- return true;
526
- } else if ("strokeOffset" in ellipse.features) {
527
- return true;
528
- }
529
- return false;
530
- };
531
-
532
- // src/renderables/rectangle.tsx
533
- import { memo as memo3, useCallback as useCallback3, useEffect as useEffect4, useRef as useRef3, useMemo as useMemo3 } from "react";
534
- import { useShallow as useShallow3 } from "zustand/react/shallow";
535
- import {
536
- instanceOfRawNumber as instanceOfRawNumber3,
537
- instanceOfRawVector2 as instanceOfRawVector23,
538
- instanceOfRawVector3 as instanceOfRawVector33,
539
- instanceOfRawEuler as instanceOfRawEuler3,
540
- instanceOfRawRGB as instanceOfRawRGB2,
541
- instanceOfRawHSL as instanceOfRawHSL2
542
- } from "@vizij/utils";
543
- import { Plane, Line as Line2 } from "@react-three/drei";
544
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
545
- function InnerRenderedRectangle({
546
- id,
547
- namespace,
548
- chain
549
- }) {
550
- const rectangleRef = useRef3(null);
551
- const materialRef = useRef3(
552
- null
553
- );
554
- const lineRef = useRef3(null);
555
- const strokeOffsetRef = useRef3(0);
556
- const strokeWidthRef = useRef3(0);
557
- const onElementClick = useVizijStore(
558
- useShallow3((state) => state.onElementClick)
559
- );
560
- const setHoveredElement = useVizijStore(
561
- useShallow3((state) => state.setHoveredElement)
562
- );
563
- const rectangle = useVizijStore(
564
- useShallow3((state) => state.world[id])
565
- );
566
- const refIsNull = useVizijStore(
567
- (state) => !state.refs[id]?.[namespace]?.current
568
- );
569
- const animatables = useVizijStore(useShallow3((state) => state.animatables));
570
- const animatableValues = useMemo3(() => {
571
- const av = {};
572
- Object.values(rectangle.features).forEach((feat) => {
573
- if (feat.animated) {
574
- const animatable = animatables[feat.value];
575
- av[animatable.id] = animatable;
576
- }
577
- });
578
- return av;
579
- }, [rectangle.features, animatables]);
580
- const selectionData = useMemo3(
581
- () => ({ id, namespace, type: "rectangle" }),
582
- [id, namespace]
583
- );
584
- const userData = useMemo3(
585
- () => ({
586
- gltfExtensions: {
587
- RobotData: createStoredRenderable(rectangle, animatableValues)
588
- },
589
- selection: selectionData
590
- }),
591
- [rectangle, animatableValues, selectionData]
592
- );
593
- useFeatures(namespace, rectangle.features, {
594
- translation: (pos) => {
595
- if (rectangleRef.current?.position && instanceOfRawVector33(pos)) {
596
- rectangleRef.current.position.set(
597
- pos.x,
598
- pos.y,
599
- pos.z
600
- );
601
- } else if (rectangleRef.current?.position && instanceOfRawVector23(pos)) {
602
- const currentZ = rectangleRef.current.position.z;
603
- rectangleRef.current.position.set(
604
- pos.x,
605
- pos.y,
606
- currentZ
607
- );
608
- }
609
- if (lineRef.current?.position && instanceOfRawVector33(pos)) {
610
- lineRef.current.position.set(
611
- pos.x,
612
- pos.y,
613
- pos.z
614
- );
615
- } else if (lineRef.current?.position && instanceOfRawVector23(pos)) {
616
- const currentZ = lineRef.current.position.z;
617
- lineRef.current.position.set(
618
- pos.x,
619
- pos.y,
620
- currentZ
621
- );
622
- }
623
- },
624
- rotation: (rot) => {
625
- if (rectangleRef.current?.rotation && instanceOfRawEuler3(rot)) {
626
- rectangleRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
627
- } else if (rectangleRef.current?.rotation && instanceOfRawNumber3(rot)) {
628
- rectangleRef.current.rotation.set(0, 0, 0);
629
- rectangleRef.current.rotateZ(rot);
630
- }
631
- if (lineRef.current?.rotation && instanceOfRawEuler3(rot)) {
632
- lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
633
- } else if (lineRef.current?.rotation && instanceOfRawNumber3(rot)) {
634
- lineRef.current.rotation.set(0, 0, 0);
635
- lineRef.current.rotateZ(rot);
636
- }
637
- },
638
- fillOpacity: (op) => {
639
- if (materialRef.current?.opacity !== void 0 && instanceOfRawNumber3(op)) {
640
- materialRef.current.opacity = op;
641
- if (op < 1) {
642
- materialRef.current.transparent = true;
643
- } else {
644
- materialRef.current.transparent = false;
645
- }
646
- materialRef.current.needsUpdate = true;
647
- }
648
- },
649
- fillColor: (color) => {
650
- if (materialRef.current?.color) {
651
- if (instanceOfRawRGB2(color)) {
652
- materialRef.current.color.setRGB(color.r, color.g, color.b);
653
- materialRef.current.needsUpdate = true;
654
- } else if (instanceOfRawVector33(color)) {
655
- materialRef.current.color.setRGB(color.x, color.y, color.z);
656
- materialRef.current.needsUpdate = true;
657
- } else if (instanceOfRawHSL2(color)) {
658
- materialRef.current.color.setHSL(color.h, color.s, color.l);
659
- materialRef.current.needsUpdate = true;
660
- }
661
- }
662
- },
663
- height: (height) => {
664
- if (rectangleRef.current && instanceOfRawNumber3(height)) {
665
- rectangleRef.current.scale.set(rectangleRef.current.scale.x, height, 1);
666
- }
667
- if (rectangleRef.current && lineRef.current && instanceOfRawNumber3(height)) {
668
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
669
- lineRef.current.scale.set(
670
- rectangleRef.current.scale.x + offset,
671
- height + offset,
672
- 1
673
- );
674
- }
675
- },
676
- width: (width) => {
677
- if (rectangleRef.current && instanceOfRawNumber3(width)) {
678
- rectangleRef.current.scale.set(width, rectangleRef.current.scale.y, 1);
679
- }
680
- if (rectangleRef.current && lineRef.current && instanceOfRawNumber3(width)) {
681
- const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
682
- lineRef.current.scale.set(
683
- width + offset,
684
- rectangleRef.current.scale.y + offset,
685
- 1
686
- );
687
- }
688
- },
689
- strokeOpacity: (strokeOpacity) => {
690
- if (lineRef.current?.material && instanceOfRawNumber3(strokeOpacity)) {
691
- lineRef.current.material.opacity = strokeOpacity;
692
- if (strokeOpacity < 1) {
693
- lineRef.current.material.transparent = true;
694
- } else {
695
- lineRef.current.material.transparent = false;
696
- }
697
- lineRef.current.material.needsUpdate = true;
698
- }
699
- },
700
- strokeColor: (strokeColor) => {
701
- if (lineRef.current?.material.color) {
702
- if (instanceOfRawRGB2(strokeColor)) {
703
- lineRef.current.material.color.setRGB(
704
- strokeColor.r,
705
- strokeColor.g,
706
- strokeColor.b
707
- );
708
- lineRef.current.material.needsUpdate = true;
709
- } else if (instanceOfRawVector33(strokeColor)) {
710
- lineRef.current.material.color.setRGB(
711
- strokeColor.x,
712
- strokeColor.y,
713
- strokeColor.z
714
- );
715
- lineRef.current.material.needsUpdate = true;
716
- } else if (instanceOfRawHSL2(strokeColor)) {
717
- lineRef.current.material.color.setHSL(
718
- strokeColor.h,
719
- strokeColor.s,
720
- strokeColor.l
721
- );
722
- lineRef.current.material.needsUpdate = true;
723
- }
724
- }
725
- },
726
- strokeWidth: (strokeWidth) => {
727
- if (lineRef.current?.material && rectangleRef.current) {
728
- if (instanceOfRawNumber3(strokeWidth)) {
729
- strokeWidthRef.current = strokeWidth;
730
- const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
731
- lineRef.current.scale.set(
732
- rectangleRef.current.scale.x + offset,
733
- rectangleRef.current.scale.y + offset,
734
- 1
735
- );
736
- lineRef.current.material.linewidth = strokeWidth;
737
- lineRef.current.material.needsUpdate = true;
738
- }
739
- }
740
- },
741
- strokeOffset: (strokeOffset) => {
742
- if (lineRef.current?.material && rectangleRef.current) {
743
- if (instanceOfRawNumber3(strokeOffset)) {
744
- strokeOffsetRef.current = strokeOffset;
745
- const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
746
- lineRef.current.scale.set(
747
- rectangleRef.current.scale.x + offset,
748
- rectangleRef.current.scale.y + offset,
749
- 1
750
- );
751
- }
752
- }
753
- }
187
+ try {
188
+ await init();
189
+ await afterPaint();
190
+ await mount(shown, options);
191
+ } catch (error) {
192
+ setRuntimeStage({ stage: "failed", error: asError(error) });
193
+ throw error;
194
+ } finally {
195
+ unfollow();
196
+ }
197
+ live = true;
198
+ setRuntimeStage(null);
199
+ await afterPaint();
200
+ return shown;
201
+ }
202
+ function afterPaint() {
203
+ return new Promise((resolve) => {
204
+ const timer = setTimeout(resolve, PAINT_WAIT_MS);
205
+ requestAnimationFrame(
206
+ () => setTimeout(() => {
207
+ clearTimeout(timer);
208
+ resolve();
209
+ }, 0)
210
+ );
754
211
  });
755
- const setReference = useVizijStore(
756
- useShallow3((state) => state.setReference)
757
- );
758
- const points = useMemo3(() => {
759
- return [
760
- [-0.5, 0.5, 0],
761
- [0.5, 0.5, 0],
762
- [0.5, -0.5, 0],
763
- [-0.5, -0.5, 0],
764
- [-0.5, 0.5, 0]
765
- ];
766
- }, []);
767
- useEffect4(() => {
768
- if (rectangleRef.current && refIsNull)
769
- setReference(rectangle.id, namespace, rectangleRef);
770
- }, [rectangle.id, namespace, rectangleRef, setReference, refIsNull]);
771
- const handlePointerOver = useCallback3(
772
- (event) => {
773
- event.stopPropagation();
774
- setHoveredElement({ id, namespace, type: "rectangle" });
775
- },
776
- [id, namespace, setHoveredElement]
777
- );
778
- const handlePointerOut = useCallback3(
779
- (event) => {
780
- event.stopPropagation();
781
- setHoveredElement(null);
782
- },
783
- [setHoveredElement]
784
- );
785
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
786
- /* @__PURE__ */ jsx3(
787
- Plane,
788
- {
789
- ref: rectangleRef,
790
- userData,
791
- args: [1, 1],
792
- onPointerOver: handlePointerOver,
793
- onPointerOut: handlePointerOut,
794
- onClick: (e) => {
795
- onElementClick(
796
- { id, type: "rectangle", namespace },
797
- [...chain, id],
798
- e
799
- );
800
- },
801
- children: /* @__PURE__ */ jsx3("meshStandardMaterial", { attach: "material", ref: materialRef })
802
- }
803
- ),
804
- showLine2(rectangle) && /* @__PURE__ */ jsx3(
805
- Line2,
806
- {
807
- ref: lineRef,
808
- points,
809
- onPointerOver: handlePointerOver,
810
- onPointerOut: handlePointerOut
811
- }
812
- )
813
- ] });
814
212
  }
815
- var RenderedRectangle = memo3(InnerRenderedRectangle);
816
- var showLine2 = (rectangle) => {
817
- if ("strokeOpacity" in rectangle.features) {
818
- return true;
819
- } else if ("strokeColor" in rectangle.features) {
820
- return true;
821
- } else if ("strokeWidth" in rectangle.features) {
822
- return true;
823
- } else if ("strokeOffset" in rectangle.features) {
824
- return true;
825
- }
826
- return false;
827
- };
828
-
829
- // src/renderables/shape.tsx
830
- import { memo as memo4, useCallback as useCallback4, useRef as useRef4, useMemo as useMemo4, useEffect as useEffect5 } from "react";
831
- import * as THREE2 from "three";
832
- import { useShallow as useShallow4 } from "zustand/react/shallow";
833
- import {
834
- instanceOfRawEuler as instanceOfRawEuler4,
835
- instanceOfRawHSL as instanceOfRawHSL3,
836
- instanceOfRawNumber as instanceOfRawNumber4,
837
- instanceOfRawRGB as instanceOfRawRGB3,
838
- instanceOfRawVector3 as instanceOfRawVector34
839
- } from "@vizij/utils";
840
-
841
- // src/functions/material-name.ts
842
- var MATERIAL_FEATURE_KEYS = [
843
- "color",
844
- "opacity",
845
- "roughness",
846
- "metalness",
847
- "shininess",
848
- "emissive",
849
- "emissiveIntensity",
850
- "specular"
851
- ];
852
- var MATERIAL_NAME_SUFFIXES = [
853
- " color",
854
- " colours",
855
- " colour",
856
- " opacity",
857
- " roughness",
858
- " metalness",
859
- " shininess",
860
- " emissive intensity",
861
- " emissive",
862
- " specular"
863
- ];
864
- function deriveMaterialName(shape, values) {
865
- for (const key of MATERIAL_FEATURE_KEYS) {
866
- const feature = shape.features[key];
867
- if (feature && feature.animated) {
868
- const animatable = values[feature.value];
869
- const candidate = extractMaterialName(animatable?.name);
870
- if (candidate) {
871
- return candidate;
872
- }
873
- }
874
- }
875
- return void 0;
213
+ function asError(error) {
214
+ return error instanceof Error ? error : new Error(String(error));
876
215
  }
877
- function extractMaterialName(name) {
878
- if (!name) {
879
- return void 0;
216
+ function pageCanvas() {
217
+ if (!canvas) {
218
+ canvas = document.createElement("canvas");
219
+ canvas.style.display = "block";
220
+ canvas.style.width = "100%";
221
+ canvas.style.height = "100%";
880
222
  }
881
- const trimmed = name.trim();
882
- if (!trimmed) {
883
- return void 0;
223
+ return canvas;
224
+ }
225
+ var view = { placeVizij, ready, safeArea, drainPicks };
226
+ function start() {
227
+ if (loop !== null || hosts === 0) return;
228
+ const tick = () => {
229
+ frame(slots, canvas, view);
230
+ loop = requestAnimationFrame(tick);
231
+ };
232
+ loop = requestAnimationFrame(tick);
233
+ }
234
+ function stop() {
235
+ if (loop !== null) {
236
+ cancelAnimationFrame(loop);
237
+ loop = null;
884
238
  }
885
- const lowered = trimmed.toLowerCase();
886
- for (const suffix of MATERIAL_NAME_SUFFIXES) {
887
- if (lowered.endsWith(suffix)) {
888
- return trimmed.slice(0, trimmed.length - suffix.length).trim();
239
+ }
240
+ function addSlot(slot) {
241
+ slots.add(slot);
242
+ return () => {
243
+ slots.delete(slot);
244
+ if (live) {
245
+ placeVizij(slot.vizijId, { x: 0, y: 0, width: 0, height: 0 });
889
246
  }
890
- }
891
- return trimmed;
247
+ };
892
248
  }
893
249
 
894
- // src/renderables/shape.tsx
895
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
896
- THREE2.Object3D.DEFAULT_UP.set(0, 0, 1);
897
- function InnerRenderedShape({
898
- id,
899
- namespace,
900
- chain
901
- }) {
902
- const refGroup = useRef4(null);
903
- const ref = useRef4(null);
904
- const shape = useVizijStore(useShallow4((state) => state.world[id]));
905
- const refIsNull = useVizijStore(
906
- (state) => !state.refs[id]?.[namespace]?.current
907
- );
908
- const animatables = useVizijStore(useShallow4((state) => state.animatables));
909
- const animatableValues = useMemo4(() => {
910
- const av = {};
911
- Object.values(shape.features).forEach((feat) => {
912
- if (feat.animated) {
913
- const animatable = animatables[feat.value];
914
- av[animatable.id] = animatable;
915
- }
916
- });
917
- return av;
918
- }, [shape.features, animatables]);
919
- const materialName = useMemo4(
920
- () => deriveMaterialName(shape, animatableValues),
921
- [shape, animatableValues]
922
- );
923
- const geometry = useMemo4(() => {
924
- const cloned = shape.geometry.clone();
925
- if (shape.name) {
926
- cloned.name = shape.name;
927
- }
928
- return cloned;
929
- }, [shape.geometry, shape.name]);
930
- const selectionData = useMemo4(
931
- () => ({ id, namespace, type: "shape" }),
932
- [id, namespace]
933
- );
934
- const userData = useMemo4(
935
- () => ({
936
- gltfExtensions: {
937
- RobotData: createStoredRenderable(shape, animatableValues)
938
- },
939
- selection: selectionData
940
- }),
941
- [shape, animatableValues, selectionData]
942
- );
943
- const material = useRef4(null);
944
- const morphTargetSettings = useMemo4(() => {
945
- if (shape.morphTargets) {
946
- const dictionary = shape.morphTargets.reduce(
947
- (acc, target, i) => ({ ...acc, [target]: i }),
948
- {}
949
- );
950
- const initialInfluences = shape.morphTargets.map(() => 0);
951
- const morphFeatureHandlers = {};
952
- shape.morphTargets.forEach((target, i) => {
953
- morphFeatureHandlers[target] = (value) => {
954
- if (ref.current?.morphTargetInfluences && instanceOfRawNumber4(value)) {
955
- ref.current.morphTargetInfluences[i] = value;
956
- }
957
- };
958
- });
959
- return [dictionary, initialInfluences, morphFeatureHandlers];
960
- } else {
961
- return [void 0, void 0, {}];
962
- }
963
- }, [shape.morphTargets]);
964
- useFeatures(
965
- namespace,
966
- shape.features,
967
- {
968
- translation: (pos) => {
969
- if (ref.current?.position && instanceOfRawVector34(pos)) {
970
- ref.current.position.set(
971
- pos.x,
972
- pos.y,
973
- pos.z
974
- );
975
- }
976
- if (refGroup.current?.position && instanceOfRawVector34(pos)) {
977
- refGroup.current.position.set(
978
- pos.x,
979
- pos.y,
980
- pos.z
981
- );
982
- }
983
- },
984
- rotation: (rot) => {
985
- if (ref.current?.rotation && instanceOfRawEuler4(rot)) {
986
- ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
987
- }
988
- if (refGroup.current?.rotation && instanceOfRawEuler4(rot)) {
989
- refGroup.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
990
- }
991
- },
992
- scale: (scale) => {
993
- if (ref.current?.scale && instanceOfRawVector34(scale)) {
994
- ref.current.scale.set(scale.x, scale.y, scale.z);
995
- } else if (ref.current && instanceOfRawNumber4(scale)) {
996
- ref.current.scale.set(scale, scale, scale);
997
- } else if (ref.current) {
998
- ref.current.scale.set(1, 1, 1);
999
- }
1000
- },
1001
- opacity: (op) => {
1002
- if (material.current?.opacity !== void 0 && instanceOfRawNumber4(op)) {
1003
- material.current.opacity = op;
1004
- if (op < 1) {
1005
- material.current.transparent = true;
1006
- } else {
1007
- material.current.transparent = false;
1008
- }
1009
- material.current.needsUpdate = true;
1010
- }
1011
- },
1012
- color: (color) => {
1013
- if ((material.current || void 0)?.color && instanceOfRawRGB3(color)) {
1014
- material.current.color.setRGB(
1015
- color.r,
1016
- color.g,
1017
- color.b
1018
- );
1019
- material.current.needsUpdate = true;
1020
- } else if ((material.current || void 0)?.color && instanceOfRawVector34(color)) {
1021
- material.current.color.setRGB(
1022
- color.x,
1023
- color.y,
1024
- color.z
1025
- );
1026
- material.current.needsUpdate = true;
1027
- } else if (material.current && instanceOfRawHSL3(color)) {
1028
- material.current.color.setHSL(
1029
- color.h,
1030
- color.s,
1031
- color.l
1032
- );
1033
- }
1034
- },
1035
- roughness: (value) => {
1036
- const standard = material.current;
1037
- if (standard?.roughness !== void 0 && instanceOfRawNumber4(value)) {
1038
- standard.roughness = value;
1039
- standard.needsUpdate = true;
1040
- }
1041
- },
1042
- metalness: (value) => {
1043
- const standard = material.current;
1044
- if (standard?.metalness !== void 0 && instanceOfRawNumber4(value)) {
1045
- standard.metalness = value;
1046
- standard.needsUpdate = true;
1047
- }
1048
- },
1049
- shininess: (value) => {
1050
- const phong = material.current;
1051
- if (phong?.shininess !== void 0 && instanceOfRawNumber4(value)) {
1052
- phong.shininess = value;
1053
- phong.needsUpdate = true;
1054
- }
1055
- },
1056
- emissive: (value) => {
1057
- const standard = material.current;
1058
- if (standard?.emissive && instanceOfRawRGB3(value)) {
1059
- standard.emissive.setRGB(value.r, value.g, value.b);
1060
- standard.needsUpdate = true;
1061
- }
1062
- },
1063
- emissiveIntensity: (value) => {
1064
- const standard = material.current;
1065
- if (standard?.emissiveIntensity !== void 0 && instanceOfRawNumber4(value)) {
1066
- standard.emissiveIntensity = value;
1067
- standard.needsUpdate = true;
250
+ // src/page/vizij.tsx
251
+ import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
252
+ import {
253
+ setSelection,
254
+ setView
255
+ } from "@vizij/runtime";
256
+
257
+ // src/page/loading.tsx
258
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
259
+ function describeLoading(stage) {
260
+ return {
261
+ stage,
262
+ label: loadingLabel(stage),
263
+ fraction: loadingFraction(stage)
264
+ };
265
+ }
266
+ function loadingLabel(stage) {
267
+ switch (stage.stage) {
268
+ case "download":
269
+ return withBytes("Downloading the runtime", stage.loaded, stage.total);
270
+ case "compile":
271
+ return "Compiling the runtime";
272
+ case "start":
273
+ return "Starting the runtime";
274
+ case "mount":
275
+ return "Starting the view";
276
+ case "fetch":
277
+ return withBytes("Loading the face", stage.loaded, stage.total);
278
+ case "load":
279
+ return "Loading the face";
280
+ case "draw":
281
+ return "Drawing the face";
282
+ case "failed":
283
+ return "Loading failed";
284
+ }
285
+ }
286
+ function loadingFraction(stage) {
287
+ if (stage.stage !== "download" && stage.stage !== "fetch") return null;
288
+ if (stage.total === null || stage.total <= 0) return null;
289
+ return Math.min(1, Math.max(0, stage.loaded / stage.total));
290
+ }
291
+ function withBytes(what, loaded, total) {
292
+ if (total === null) {
293
+ return loaded > 0 ? `${what}: ${megabytes(loaded)} MB` : what;
294
+ }
295
+ return `${what}: ${megabytes(loaded)} of ${megabytes(total)} MB`;
296
+ }
297
+ function megabytes(bytes) {
298
+ return (bytes / 1e6).toFixed(1);
299
+ }
300
+ var INDETERMINATE_CSS = `
301
+ @keyframes vizij-loading-slide {
302
+ from { transform: translateX(-100%); }
303
+ to { transform: translateX(250%); }
304
+ }
305
+ [data-vizij-loading-indeterminate] {
306
+ animation: vizij-loading-slide 1.4s ease-in-out infinite;
307
+ }
308
+ @media (prefers-reduced-motion: reduce) {
309
+ [data-vizij-loading-indeterminate] { animation: none; }
310
+ }
311
+ `;
312
+ function DefaultLoadingImage() {
313
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 1024 1024", width: "64", height: "64", children: [
314
+ /* @__PURE__ */ jsx2("circle", { cx: "447.6", cy: "486.6", r: "400.6", fill: "#50c4b6" }),
315
+ /* @__PURE__ */ jsxs("g", { fillOpacity: "0.894", children: [
316
+ /* @__PURE__ */ jsx2(
317
+ "path",
318
+ {
319
+ fill: "#f56b29",
320
+ 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"
1068
321
  }
1069
- },
1070
- specular: (value) => {
1071
- const phong = material.current;
1072
- if (phong?.specular && instanceOfRawRGB3(value)) {
1073
- phong.specular.setRGB(value.r, value.g, value.b);
1074
- phong.needsUpdate = true;
322
+ ),
323
+ /* @__PURE__ */ jsx2(
324
+ "path",
325
+ {
326
+ fill: "#ff9e00",
327
+ 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"
1075
328
  }
1076
- },
1077
- ...morphTargetSettings[2]
1078
- },
1079
- shape
1080
- );
1081
- const setReference = useVizijStore(useShallow4((state) => state.setReference));
1082
- const onElementClick = useVizijStore(
1083
- useShallow4((state) => state.onElementClick)
1084
- );
1085
- const setHoveredElement = useVizijStore(
1086
- useShallow4((state) => state.setHoveredElement)
1087
- );
1088
- useEffect5(() => {
1089
- if (ref.current && refIsNull) setReference(shape.id, namespace, ref);
1090
- }, [shape.id, namespace, ref, setReference, refIsNull]);
1091
- useEffect5(() => {
1092
- if (ref.current) {
1093
- ref.current.name = shape.name;
1094
- }
1095
- }, [shape.name]);
1096
- useEffect5(() => {
1097
- if (!material.current) {
1098
- return;
1099
- }
1100
- if (materialName) {
1101
- material.current.name = materialName;
1102
- } else if (shape.name) {
1103
- material.current.name = shape.name;
1104
- }
1105
- }, [materialName, shape.name]);
1106
- const handlePointerOver = useCallback4(
1107
- (event) => {
1108
- event.stopPropagation();
1109
- setHoveredElement({ id, namespace, type: "shape" });
1110
- },
1111
- [id, namespace, setHoveredElement]
1112
- );
1113
- const handlePointerOut = useCallback4(
1114
- (event) => {
1115
- event.stopPropagation();
1116
- setHoveredElement(null);
1117
- },
1118
- [setHoveredElement]
1119
- );
1120
- return /* @__PURE__ */ jsxs3(
1121
- "mesh",
329
+ )
330
+ ] })
331
+ ] });
332
+ }
333
+ function DefaultLoading({
334
+ loading,
335
+ image
336
+ }) {
337
+ const { stage, label, fraction } = loading;
338
+ return /* @__PURE__ */ jsxs(
339
+ "div",
1122
340
  {
1123
- ref,
1124
- userData,
1125
- castShadow: true,
1126
- receiveShadow: true,
1127
- up: [0, 0, 1],
1128
- geometry,
1129
- morphTargetDictionary: morphTargetSettings[0],
1130
- morphTargetInfluences: morphTargetSettings[1],
1131
- onPointerOver: handlePointerOver,
1132
- onPointerOut: handlePointerOut,
1133
- onClick: (e) => {
1134
- console.log("Clicked element", shape);
1135
- onElementClick({ id, type: "shape", namespace }, [...chain, id], e);
341
+ style: {
342
+ width: "100%",
343
+ height: "100%",
344
+ display: "flex",
345
+ flexDirection: "column",
346
+ alignItems: "center",
347
+ justifyContent: "center",
348
+ gap: 12,
349
+ font: "13px/1.4 system-ui, sans-serif",
350
+ textAlign: "center"
1136
351
  },
1137
352
  children: [
1138
- shape.material === "basic" && /* @__PURE__ */ jsx4(
1139
- "meshBasicMaterial",
1140
- {
1141
- attach: "material",
1142
- ref: material,
1143
- side: THREE2.DoubleSide
1144
- }
1145
- ),
1146
- shape.material === "lambert" && /* @__PURE__ */ jsx4(
1147
- "meshLambertMaterial",
1148
- {
1149
- attach: "material",
1150
- ref: material,
1151
- side: THREE2.DoubleSide
1152
- }
1153
- ),
1154
- shape.material === "phong" && /* @__PURE__ */ jsx4(
1155
- "meshPhongMaterial",
1156
- {
1157
- attach: "material",
1158
- ref: material,
1159
- side: THREE2.DoubleSide
1160
- }
1161
- ),
1162
- shape.material === "standard" && /* @__PURE__ */ jsx4(
1163
- "meshStandardMaterial",
1164
- {
1165
- attach: "material",
1166
- ref: material,
1167
- side: THREE2.DoubleSide
1168
- }
1169
- ),
1170
- shape.material === "normal" && /* @__PURE__ */ jsx4(
1171
- "meshNormalMaterial",
1172
- {
1173
- attach: "material",
1174
- ref: material,
1175
- side: THREE2.DoubleSide
1176
- }
1177
- ),
1178
- shape.children?.map((child) => /* @__PURE__ */ jsx4(
1179
- Renderable,
1180
- {
1181
- id: child,
1182
- namespace,
1183
- chain: [...chain, id]
1184
- },
1185
- child
1186
- ))
353
+ image !== null && /* @__PURE__ */ jsx2("div", { "aria-hidden": "true", children: image ?? /* @__PURE__ */ jsx2(DefaultLoadingImage, {}) }),
354
+ stage.stage === "failed" ? /* @__PURE__ */ jsx2("div", { role: "status", children: label }) : /* @__PURE__ */ jsxs(Fragment, { children: [
355
+ /* @__PURE__ */ jsxs(
356
+ "div",
357
+ {
358
+ role: "progressbar",
359
+ "aria-label": "Loading the face",
360
+ "aria-valuetext": label,
361
+ ...fraction === null ? {} : {
362
+ "aria-valuemin": 0,
363
+ "aria-valuemax": 100,
364
+ "aria-valuenow": Math.round(fraction * 100)
365
+ },
366
+ style: {
367
+ position: "relative",
368
+ width: 160,
369
+ maxWidth: "60%",
370
+ height: 4,
371
+ borderRadius: 2,
372
+ overflow: "hidden"
373
+ },
374
+ children: [
375
+ /* @__PURE__ */ jsx2(
376
+ "div",
377
+ {
378
+ style: {
379
+ position: "absolute",
380
+ inset: 0,
381
+ background: "currentColor",
382
+ opacity: 0.2
383
+ }
384
+ }
385
+ ),
386
+ fraction === null ? /* @__PURE__ */ jsx2(
387
+ "div",
388
+ {
389
+ "data-vizij-loading-indeterminate": "",
390
+ style: {
391
+ position: "absolute",
392
+ top: 0,
393
+ bottom: 0,
394
+ left: 0,
395
+ width: "40%",
396
+ background: "currentColor"
397
+ }
398
+ }
399
+ ) : /* @__PURE__ */ jsx2(
400
+ "div",
401
+ {
402
+ style: {
403
+ position: "absolute",
404
+ top: 0,
405
+ bottom: 0,
406
+ left: 0,
407
+ width: `${fraction * 100}%`,
408
+ background: "currentColor"
409
+ }
410
+ }
411
+ )
412
+ ]
413
+ }
414
+ ),
415
+ /* @__PURE__ */ jsx2("div", { "aria-hidden": "true", style: { opacity: 0.8 }, children: label }),
416
+ fraction === null && /* @__PURE__ */ jsx2("style", { children: INDETERMINATE_CSS })
417
+ ] })
1187
418
  ]
1188
419
  }
1189
420
  );
1190
421
  }
1191
- var RenderedShape = memo4(InnerRenderedShape);
1192
422
 
1193
- // src/renderables/renderable.tsx
1194
- import { jsx as jsx5 } from "react/jsx-runtime";
1195
- function InnerRenderable({
1196
- id,
1197
- namespace,
1198
- chain
423
+ // src/page/vizij.tsx
424
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
425
+ function Vizij({
426
+ vizijId,
427
+ className,
428
+ style,
429
+ toneMapping,
430
+ fit,
431
+ zoom,
432
+ bounds,
433
+ showSafeArea = false,
434
+ selection,
435
+ onPick,
436
+ loadingImage,
437
+ loading
1199
438
  }) {
1200
- const type = useVizijStore(useShallow5((state) => state.world[id].type));
1201
- const key = `${namespace}.${id}`;
1202
- switch (type) {
1203
- case "group":
1204
- return /* @__PURE__ */ jsx5(RenderedGroup, { id, namespace, chain }, key);
1205
- case "ellipse":
1206
- return /* @__PURE__ */ jsx5(
1207
- RenderedEllipse,
1208
- {
1209
- id,
1210
- namespace,
1211
- chain
1212
- },
1213
- key
1214
- );
1215
- case "rectangle":
1216
- return /* @__PURE__ */ jsx5(
1217
- RenderedRectangle,
439
+ const element = useRef2(null);
440
+ const frame2 = useRef2(null);
441
+ const outline2 = useRef2(null);
442
+ const slot = useRef2(null);
443
+ useEffect2(() => {
444
+ const registered = {
445
+ vizijId,
446
+ element: element.current,
447
+ safeArea: null,
448
+ onPick: void 0,
449
+ placed: null
450
+ };
451
+ registered.element.setAttribute("data-vizij-ready", "false");
452
+ slot.current = registered;
453
+ const remove = addSlot(registered);
454
+ return () => {
455
+ slot.current = null;
456
+ remove();
457
+ };
458
+ }, [vizijId]);
459
+ useEffect2(() => {
460
+ if (slot.current) slot.current.onPick = onPick;
461
+ }, [onPick, vizijId]);
462
+ useEffect2(() => {
463
+ if (!slot.current) return;
464
+ slot.current.safeArea = showSafeArea && outline2.current && frame2.current ? { outline: outline2.current, frame: frame2.current } : null;
465
+ }, [showSafeArea, vizijId]);
466
+ const view2 = JSON.stringify({ toneMapping, fit, zoom, bounds });
467
+ useEffect2(
468
+ () => onceMounted(() => setView(vizijId, JSON.parse(view2))),
469
+ [vizijId, view2]
470
+ );
471
+ const selected = selection === void 0 ? null : JSON.stringify(selection);
472
+ useEffect2(
473
+ () => selected === null ? void 0 : onceMounted(() => setSelection(vizijId, JSON.parse(selected))),
474
+ [vizijId, selected]
475
+ );
476
+ const stage = useLoadingStage(vizijId);
477
+ const loadingView = stage === null || loading === null ? null : loading ? loading(describeLoading(stage)) : /* @__PURE__ */ jsx3(DefaultLoading, { loading: describeLoading(stage), image: loadingImage });
478
+ return /* @__PURE__ */ jsx3(
479
+ "div",
480
+ {
481
+ ref: element,
482
+ className,
483
+ style,
484
+ "data-vizij-id": vizijId,
485
+ children: (showSafeArea || loadingView !== null) && /* @__PURE__ */ jsxs2(
486
+ "div",
1218
487
  {
1219
- id,
1220
- namespace,
1221
- chain
1222
- },
1223
- key
1224
- );
1225
- case "shape":
1226
- return /* @__PURE__ */ jsx5(RenderedShape, { id, namespace, chain }, key);
1227
- default:
1228
- return null;
1229
- }
488
+ ref: frame2,
489
+ style: {
490
+ position: "relative",
491
+ width: "100%",
492
+ height: "100%",
493
+ pointerEvents: "none"
494
+ },
495
+ children: [
496
+ loadingView !== null && /* @__PURE__ */ jsx3(
497
+ "div",
498
+ {
499
+ "data-vizij-loading": "",
500
+ style: { position: "absolute", inset: 0 },
501
+ children: loadingView
502
+ }
503
+ ),
504
+ showSafeArea && /* @__PURE__ */ jsx3(
505
+ "div",
506
+ {
507
+ ref: outline2,
508
+ style: {
509
+ position: "absolute",
510
+ display: "none",
511
+ boxSizing: "border-box",
512
+ border: "2px solid red"
513
+ }
514
+ }
515
+ )
516
+ ]
517
+ }
518
+ )
519
+ }
520
+ );
521
+ }
522
+ function useLoadingStage(vizijId) {
523
+ const [stage, setStage] = useState(() => loadingStage(vizijId));
524
+ useEffect2(() => watchLoading(vizijId, setStage), [vizijId]);
525
+ return stage;
526
+ }
527
+ function onceMounted(apply) {
528
+ let current = true;
529
+ canvasMounted().then(
530
+ () => {
531
+ if (current) apply();
532
+ },
533
+ () => void 0
534
+ );
535
+ return () => {
536
+ current = false;
537
+ };
1230
538
  }
1231
- var Renderable = memo5(InnerRenderable);
1232
539
 
1233
540
  // src/store.ts
1234
541
  import { create } from "zustand";
1235
542
  import { subscribeWithSelector } from "zustand/middleware";
1236
543
  import { produce, enableMapSet } from "immer";
1237
- import * as THREE3 from "three";
1238
- import { getLookup as getLookup2 } from "@vizij/utils";
544
+ import { getLookup } from "@vizij/utils";
1239
545
 
1240
546
  // src/functions/create-world-element.ts
1241
547
  import { createBrowserSafeId } from "@vizij/utils";
@@ -1298,30 +604,6 @@ function removeFromTree(state, nodesToRemove) {
1298
604
  });
1299
605
  }
1300
606
 
1301
- // src/memoryInvestigation.ts
1302
- function getGlobalMemoryInvestigation() {
1303
- return globalThis;
1304
- }
1305
- function updateRenderState(updater) {
1306
- const globalObj = getGlobalMemoryInvestigation();
1307
- if (!globalObj.__VIZIJ_MEMORY_INVESTIGATION__?.enabled) {
1308
- return;
1309
- }
1310
- if (!globalObj.__vizijMemoryDebugState) {
1311
- globalObj.__vizijMemoryDebugState = {};
1312
- }
1313
- if (!globalObj.__vizijMemoryDebugState.render) {
1314
- globalObj.__vizijMemoryDebugState.render = {};
1315
- }
1316
- updater(globalObj.__vizijMemoryDebugState.render);
1317
- }
1318
- function recordRenderCounter(key, delta = 1) {
1319
- updateRenderState((renderState) => {
1320
- const current = typeof renderState[key] === "number" ? Number(renderState[key]) : 0;
1321
- renderState[key] = current + delta;
1322
- });
1323
- }
1324
-
1325
607
  // src/functions/create-animatable.ts
1326
608
  import { createBrowserSafeId as createBrowserSafeId2 } from "@vizij/utils";
1327
609
  function createAnimatable(value) {
@@ -1443,51 +725,16 @@ function createAnimatable(value) {
1443
725
  }
1444
726
  createAnimatable({ type: "euler", name: "Rotation" });
1445
727
 
1446
- // src/functions/exportable-bodies.ts
1447
- function asGroupEntries(world) {
1448
- return Object.values(world).filter((entry) => entry.type === "group");
1449
- }
1450
- function selectExportableGroupEntries(world, filterIds) {
1451
- const groupEntries = asGroupEntries(world);
1452
- const filterSet = Array.isArray(filterIds) && filterIds.length > 0 ? new Set(filterIds) : null;
1453
- if (filterSet) {
1454
- return groupEntries.filter((entry) => filterSet.has(entry.id));
1455
- }
1456
- const rootBoundsGroups = groupEntries.filter(
1457
- (entry) => Boolean(entry.rootBounds)
1458
- );
1459
- if (rootBoundsGroups.length > 0) {
1460
- return rootBoundsGroups;
1461
- }
1462
- const explicitRootGroups = groupEntries.filter(
1463
- (entry) => entry.root === true
1464
- );
1465
- if (explicitRootGroups.length > 0) {
1466
- return explicitRootGroups;
1467
- }
1468
- const topLevelGroups = groupEntries.filter((entry) => !entry.parent);
1469
- if (topLevelGroups.length > 0) {
1470
- return topLevelGroups;
1471
- }
1472
- return groupEntries;
1473
- }
1474
-
1475
728
  // src/store.ts
1476
- THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
1477
729
  enableMapSet();
1478
- var VizijSlice = (set, get) => ({
1479
- // worldRef: createRef<THREE.Group>(),
730
+ var VizijSlice = (set, _get) => ({
1480
731
  world: {},
1481
- refs: {},
1482
732
  animatables: {},
1483
733
  values: /* @__PURE__ */ new Map(),
1484
- renderHit: false,
1485
734
  preferences: {
1486
735
  damping: false
1487
736
  },
1488
737
  elementSelection: [],
1489
- hoveredElement: null,
1490
- slotConfig: {},
1491
738
  clearSelection: () => {
1492
739
  set({ elementSelection: [] });
1493
740
  },
@@ -1498,66 +745,6 @@ var VizijSlice = (set, get) => ({
1498
745
  })
1499
746
  );
1500
747
  },
1501
- setHoveredElement: (selection) => {
1502
- set({ hoveredElement: selection });
1503
- },
1504
- onElementClick: (selection, _chain, event) => {
1505
- event.stopPropagation();
1506
- const makeKey = (sel) => `${sel.namespace}__${sel.type}__${sel.id}`;
1507
- const stack = [];
1508
- const seen = /* @__PURE__ */ new Set();
1509
- const push = (sel) => {
1510
- if (!sel) return;
1511
- if (!sel.id || !sel.namespace || !sel.type) return;
1512
- const key = makeKey(sel);
1513
- if (seen.has(key)) return;
1514
- seen.add(key);
1515
- stack.push(sel);
1516
- };
1517
- const intersections = event.intersections ?? [];
1518
- intersections.forEach((hit) => {
1519
- const hitSelection = hit.object?.userData?.selection;
1520
- push(hitSelection);
1521
- });
1522
- if (stack.length === 0) {
1523
- push(selection);
1524
- }
1525
- const stackKeys = new Set(stack.map(makeKey));
1526
- const primary = stack[0];
1527
- const primaryKey = makeKey(primary);
1528
- set(
1529
- produce((state) => {
1530
- if (event.metaKey) {
1531
- const existing = state.elementSelection ?? [];
1532
- const alreadySelected = existing.some(
1533
- (item) => makeKey(item) === primaryKey
1534
- );
1535
- const existingWithoutStack = existing.filter(
1536
- (item) => !stackKeys.has(makeKey(item))
1537
- );
1538
- if (alreadySelected) {
1539
- state.elementSelection = existingWithoutStack;
1540
- } else {
1541
- state.elementSelection = [...stack, ...existingWithoutStack];
1542
- }
1543
- } else {
1544
- state.elementSelection = stack;
1545
- }
1546
- })
1547
- );
1548
- },
1549
- getExportableBodies: (filterIds) => {
1550
- const worldData = get().world;
1551
- const candidateGroups = selectExportableGroupEntries(
1552
- worldData,
1553
- filterIds
1554
- );
1555
- const refs = get().refs;
1556
- return candidateGroups.flatMap((entry) => {
1557
- const mounted = Object.values(refs[entry.id] ?? {}).map((ref) => ref.current).find((object) => Boolean(object));
1558
- return mounted ? [mounted] : [];
1559
- });
1560
- },
1561
748
  setGeometry: (id, geometry) => {
1562
749
  set(
1563
750
  produce((state) => {
@@ -1568,21 +755,12 @@ var VizijSlice = (set, get) => ({
1568
755
  setValue: (id, namespace, value) => {
1569
756
  set(
1570
757
  produce((state) => {
1571
- const lookupId = getLookup2(namespace, id);
758
+ const lookupId = getLookup(namespace, id);
1572
759
  if (typeof value === "function") {
1573
- const current = state.values.get(lookupId);
1574
- if (current !== void 0) {
1575
- if (value(current !== void 0)) {
1576
- state.values.set(lookupId, value(current));
1577
- }
1578
- } else {
1579
- const animatableLookup = state.animatables[id];
1580
- const updatedValue = value(
1581
- animatableLookup !== void 0 ? animatableLookup.default : void 0
1582
- );
1583
- if (updatedValue !== void 0) {
1584
- state.values.set(lookupId, updatedValue);
1585
- }
760
+ const current = state.values.get(lookupId) ?? state.animatables[id]?.default;
761
+ const updatedValue = value(current);
762
+ if (updatedValue !== void 0) {
763
+ state.values.set(lookupId, updatedValue);
1586
764
  }
1587
765
  } else {
1588
766
  state.values.set(lookupId, value);
@@ -1597,7 +775,7 @@ var VizijSlice = (set, get) => ({
1597
775
  set(
1598
776
  produce((state) => {
1599
777
  writes.forEach(({ id, namespace, value }) => {
1600
- const lookupId = getLookup2(namespace, id);
778
+ const lookupId = getLookup(namespace, id);
1601
779
  state.values.set(lookupId, value);
1602
780
  });
1603
781
  })
@@ -1725,36 +903,7 @@ var VizijSlice = (set, get) => ({
1725
903
  })
1726
904
  );
1727
905
  },
1728
- setSlot: (parentId, parentNamespace, childId, childNamespace) => {
1729
- set(
1730
- produce((state) => {
1731
- const parentLookupId = getLookup2(parentNamespace, parentId);
1732
- const childLookupId = getLookup2(childNamespace, childId);
1733
- state.slotConfig[parentLookupId] = childLookupId;
1734
- })
1735
- );
1736
- },
1737
- setSlots: (slots, replace) => {
1738
- set(
1739
- produce((state) => {
1740
- if (replace) {
1741
- state.slotConfig = slots;
1742
- } else {
1743
- state.slotConfig = { ...state.slotConfig, ...slots };
1744
- }
1745
- })
1746
- );
1747
- },
1748
- clearSlot: (parentId, parentNamespace) => {
1749
- set(
1750
- produce((state) => {
1751
- const parentLookupId = getLookup2(parentNamespace, parentId);
1752
- delete state.slotConfig[parentLookupId];
1753
- })
1754
- );
1755
- },
1756
906
  setVizij: (scene, animatables) => {
1757
- recordRenderCounter("rootReplacementCount");
1758
907
  set({
1759
908
  world: scene,
1760
909
  animatables
@@ -1762,7 +911,6 @@ var VizijSlice = (set, get) => ({
1762
911
  },
1763
912
  addWorldElements(world, animatables, replace) {
1764
913
  if (replace) {
1765
- recordRenderCounter("rootReplacementCount");
1766
914
  set({ world, animatables });
1767
915
  } else {
1768
916
  set((state) => ({
@@ -1775,14 +923,6 @@ var VizijSlice = (set, get) => ({
1775
923
  set((state) => ({
1776
924
  preferences: { ...state.preferences, ...preferences }
1777
925
  }));
1778
- },
1779
- setReference: (id, namespace, ref) => {
1780
- set((state) => ({
1781
- refs: {
1782
- ...state.refs,
1783
- [id]: { ...state.refs[id], [namespace]: ref }
1784
- }
1785
- }));
1786
926
  }
1787
927
  });
1788
928
  var useDefaultVizijStore = create()(
@@ -1808,12 +948,6 @@ var ShapeMaterial = /* @__PURE__ */ ((ShapeMaterial2) => {
1808
948
  })(ShapeMaterial || {});
1809
949
 
1810
950
  // src/types/tone-mapping.ts
1811
- import {
1812
- ACESFilmicToneMapping,
1813
- AgXToneMapping,
1814
- NeutralToneMapping,
1815
- NoToneMapping
1816
- } from "three";
1817
951
  var TONE_MAPPING_MODES = [
1818
952
  "none",
1819
953
  "agx",
@@ -1825,363 +959,34 @@ var TONE_MAPPING_LABELS = {
1825
959
  agx: "AgX (Blender)",
1826
960
  aces: "ACES Filmic",
1827
961
  neutral: "Neutral"
1828
- };
1829
- var DEFAULT_TONE_MAPPING_MODE = "none";
1830
- var TONE_MAPPING_CONSTANTS = {
1831
- none: NoToneMapping,
1832
- agx: AgXToneMapping,
1833
- aces: ACESFilmicToneMapping,
1834
- neutral: NeutralToneMapping
1835
- };
1836
- function isToneMappingMode(value) {
1837
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(TONE_MAPPING_CONSTANTS, value);
1838
- }
1839
- function resolveToneMapping(mode) {
1840
- if (isToneMappingMode(mode)) {
1841
- return TONE_MAPPING_CONSTANTS[mode];
1842
- }
1843
- return TONE_MAPPING_CONSTANTS[DEFAULT_TONE_MAPPING_MODE];
1844
- }
1845
- function normalizeToneMappingMode(value) {
1846
- return isToneMappingMode(value) ? value : DEFAULT_TONE_MAPPING_MODE;
1847
- }
1848
-
1849
- // src/effects/selection-glow-effect.tsx
1850
- import { Fragment as Fragment3, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef5 } from "react";
1851
- import { useFrame } from "@react-three/fiber";
1852
- import { useShallow as useShallow6 } from "zustand/react/shallow";
1853
- import {
1854
- AdditiveBlending,
1855
- Color,
1856
- EdgesGeometry,
1857
- LineBasicMaterial,
1858
- Matrix4,
1859
- Quaternion,
1860
- Vector3
1861
- } from "three";
1862
- import { jsx as jsx6 } from "react/jsx-runtime";
1863
- function SelectionGlowEffect({
1864
- enabled = false,
1865
- color = "#ff1010",
1866
- opacity = 0.9,
1867
- thresholdAngle = 2
1868
- }) {
1869
- const selections = useVizijStore(
1870
- useShallow6(
1871
- (state) => enabled ? state.elementSelection ?? [] : []
1872
- )
1873
- );
1874
- if (!enabled || selections.length === 0) {
1875
- return null;
1876
- }
1877
- return /* @__PURE__ */ jsx6(Fragment3, { children: selections.map((selection) => /* @__PURE__ */ jsx6(
1878
- SelectionOutline,
1879
- {
1880
- selection,
1881
- color: selection.color ?? color,
1882
- opacity,
1883
- thresholdAngle
1884
- },
1885
- `${selection.namespace}:${selection.id}`
1886
- )) });
1887
- }
1888
- function SelectionOutline({
1889
- selection,
1890
- color,
1891
- opacity,
1892
- thresholdAngle
1893
- }) {
1894
- const target = useVizijStore(
1895
- useShallow6((state) => {
1896
- const entry = state.world[selection.id];
1897
- const ref = state.refs[selection.id]?.[selection.namespace];
1898
- const geometry = entry?.geometry ?? null;
1899
- return { ref, geometry };
1900
- })
1901
- );
1902
- const sourceRef = target.ref;
1903
- const edgesGeometry = useMemo5(() => {
1904
- if (!target.geometry) return null;
1905
- const edges = new EdgesGeometry(target.geometry, thresholdAngle);
1906
- return edges;
1907
- }, [target.geometry, thresholdAngle]);
1908
- useEffect6(() => () => edgesGeometry?.dispose(), [edgesGeometry]);
1909
- const material = useMemo5(() => {
1910
- const mat = new LineBasicMaterial({
1911
- color: new Color(color),
1912
- transparent: true,
1913
- opacity,
1914
- blending: AdditiveBlending,
1915
- depthTest: false,
1916
- depthWrite: false,
1917
- toneMapped: false
1918
- });
1919
- return mat;
1920
- }, [color, opacity]);
1921
- useEffect6(() => () => material.dispose(), [material]);
1922
- const lineRef = useRef5(null);
1923
- useFrame(() => {
1924
- const source = sourceRef?.current;
1925
- const line = lineRef.current;
1926
- if (!source || !line) return;
1927
- copyWorldTransform(source, line);
1928
- line.visible = source.visible;
1929
- });
1930
- if (!sourceRef || !edgesGeometry) {
1931
- return null;
1932
- }
1933
- return /* @__PURE__ */ jsx6(
1934
- "lineSegments",
1935
- {
1936
- ref: lineRef,
1937
- geometry: edgesGeometry,
1938
- material,
1939
- frustumCulled: false,
1940
- renderOrder: 1e3
1941
- }
1942
- );
1943
- }
1944
- var tempMatrix = new Matrix4();
1945
- var tempPosition = new Vector3();
1946
- var tempQuaternion = new Quaternion();
1947
- var tempScale = new Vector3();
1948
- function copyWorldTransform(source, target) {
1949
- source.updateWorldMatrix(true, false);
1950
- tempMatrix.copy(source.matrixWorld);
1951
- tempMatrix.decompose(tempPosition, tempQuaternion, tempScale);
1952
- target.position.copy(tempPosition);
1953
- target.quaternion.copy(tempQuaternion);
1954
- target.scale.copy(tempScale);
1955
- target.updateMatrix();
1956
- }
1957
-
1958
- // src/vizij.tsx
1959
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1960
- Object3D4.DEFAULT_UP.set(0, 0, 1);
1961
- function Vizij({
1962
- style,
1963
- className,
1964
- rootId,
1965
- namespace = "default",
1966
- showSafeArea = false,
1967
- showSelectionGlow = false,
1968
- toneMapping = "none",
1969
- onPointerMissed
1970
- }) {
1971
- const ctx = useContext3(VizijContext);
1972
- useEffect7(() => {
1973
- recordRenderCounter("canvasMountCount");
1974
- recordRenderCounter("mountedCanvasCount");
1975
- return () => {
1976
- recordRenderCounter("mountedCanvasCount", -1);
1977
- };
1978
- }, []);
1979
- if (ctx) {
1980
- return /* @__PURE__ */ jsx7(
1981
- Canvas,
1982
- {
1983
- shadows: false,
1984
- style,
1985
- className,
1986
- onPointerMissed,
1987
- gl: {
1988
- outputColorSpace: SRGBColorSpace,
1989
- antialias: true
1990
- },
1991
- children: /* @__PURE__ */ jsx7(
1992
- MemoizedInnerVizij,
1993
- {
1994
- rootId,
1995
- namespace,
1996
- showSafeArea,
1997
- showSelectionGlow,
1998
- toneMapping
1999
- }
2000
- )
2001
- }
2002
- );
2003
- } else {
2004
- return /* @__PURE__ */ jsx7(VizijContext.Provider, { value: useDefaultVizijStore, children: /* @__PURE__ */ jsx7(
2005
- Canvas,
2006
- {
2007
- style,
2008
- className,
2009
- onPointerMissed,
2010
- gl: {
2011
- outputColorSpace: SRGBColorSpace,
2012
- antialias: true
2013
- },
2014
- children: /* @__PURE__ */ jsx7(
2015
- MemoizedInnerVizij,
2016
- {
2017
- rootId,
2018
- namespace,
2019
- showSafeArea,
2020
- showSelectionGlow,
2021
- toneMapping
2022
- }
2023
- )
2024
- }
2025
- ) });
2026
- }
2027
- }
2028
- function InnerVizij({
2029
- rootId,
2030
- namespace = "default",
2031
- container,
2032
- showSafeArea,
2033
- showSelectionGlow,
2034
- toneMapping = "none"
2035
- }) {
2036
- const sceneParentSizing = container ? {
2037
- width: container.width * container.resolution,
2038
- height: container.height * container.resolution
2039
- } : void 0;
2040
- return /* @__PURE__ */ jsxs4(Fragment4, { children: [
2041
- /* @__PURE__ */ jsx7("ambientLight", { intensity: Math.PI / 2 }),
2042
- /* @__PURE__ */ jsx7(ToneMappingController, { mode: toneMapping }),
2043
- /* @__PURE__ */ jsx7(
2044
- OrthographicCamera,
2045
- {
2046
- makeDefault: true,
2047
- position: [0, 0, 100],
2048
- near: 0.1,
2049
- far: 1e3
2050
- }
2051
- ),
2052
- /* @__PURE__ */ jsx7(Suspense, { fallback: null, children: /* @__PURE__ */ jsx7(
2053
- World,
2054
- {
2055
- rootId,
2056
- namespace,
2057
- parentSizing: sceneParentSizing
2058
- }
2059
- ) }),
2060
- showSelectionGlow && /* @__PURE__ */ jsx7(SelectionGlowEffect, { enabled: true }),
2061
- showSafeArea && /* @__PURE__ */ jsx7(SafeAreaRenderer, { rootId })
2062
- ] });
2063
- }
2064
- var MemoizedInnerVizij = memo6(InnerVizij);
2065
- function ToneMappingController({ mode }) {
2066
- const gl = useThree((state) => state.gl);
2067
- const scene = useThree((state) => state.scene);
2068
- useEffect7(() => {
2069
- const resolved = resolveToneMapping(mode);
2070
- if (gl.toneMapping === resolved) {
2071
- return;
2072
- }
2073
- gl.toneMapping = resolved;
2074
- scene.traverse((object) => {
2075
- const material = object.material;
2076
- if (!material) {
2077
- return;
2078
- }
2079
- if (Array.isArray(material)) {
2080
- material.forEach((mat) => {
2081
- mat.needsUpdate = true;
2082
- });
2083
- } else {
2084
- material.needsUpdate = true;
2085
- }
2086
- });
2087
- }, [gl, scene, mode]);
2088
- return null;
2089
- }
2090
- function InnerWorld({
2091
- rootId,
2092
- namespace = "default",
2093
- parentSizing
2094
- }) {
2095
- const [present, rootBounds] = useVizijStore(
2096
- useShallow7((state) => {
2097
- const group = state.world[rootId];
2098
- const bounds = group?.rootBounds ?? defaultRootBounds;
2099
- return [group !== void 0, bounds];
2100
- })
2101
- );
2102
- const { camera, size } = useThree((state) => ({
2103
- camera: state.camera,
2104
- size: state.size
2105
- }));
2106
- useEffect7(() => {
2107
- const width = rootBounds.size.x;
2108
- const height = rootBounds.size.y;
2109
- if (camera && parentSizing === void 0 && camera.isOrthographicCamera) {
2110
- const zoom = Math.min(size.width / width, size.height / height);
2111
- const center = rootBounds.center;
2112
- if (camera.zoom !== zoom) {
2113
- camera.zoom = zoom;
2114
- camera.updateProjectionMatrix();
2115
- }
2116
- if (camera.position.x !== center.x || camera.position.y !== center.y) {
2117
- camera.position.x = center.x;
2118
- camera.position.y = center.y;
2119
- camera.updateProjectionMatrix();
2120
- }
2121
- } else if (camera && parentSizing !== void 0 && camera.isOrthographicCamera) {
2122
- const zoom = Math.min(
2123
- parentSizing.width / width,
2124
- parentSizing.height / height
2125
- );
2126
- const center = rootBounds.center;
2127
- camera.left = -0.5 * parentSizing.width / zoom + center.x;
2128
- camera.right = 0.5 * parentSizing.width / zoom + center.x;
2129
- camera.top = 0.5 * parentSizing.height / zoom + center.y;
2130
- camera.bottom = -0.5 * parentSizing.height / zoom + center.y;
2131
- camera.updateProjectionMatrix();
2132
- }
2133
- }, [rootBounds, camera, parentSizing, size]);
2134
- return /* @__PURE__ */ jsxs4(ErrorBoundary, { fallback: null, children: [
2135
- present && /* @__PURE__ */ jsx7(Renderable, { id: rootId, namespace, chain: [] }),
2136
- !present && /* @__PURE__ */ jsx7(
2137
- Text,
2138
- {
2139
- position: [0, 0, 0],
2140
- color: "white",
2141
- anchorX: "center",
2142
- anchorY: "middle",
2143
- fontSize: 0.7,
2144
- children: "No Output"
2145
- }
2146
- )
2147
- ] });
962
+ };
963
+ var DEFAULT_TONE_MAPPING_MODE = "none";
964
+ function isToneMappingMode(value) {
965
+ return typeof value === "string" && TONE_MAPPING_MODES.includes(value);
2148
966
  }
2149
- var World = memo6(InnerWorld);
2150
- function SafeAreaRenderer({ rootId }) {
2151
- const rootBounds = useVizijStore((state) => {
2152
- const group = state.world[rootId];
2153
- return group?.rootBounds ?? defaultRootBounds;
2154
- });
2155
- const left = rootBounds.center.x - rootBounds.size.x / 2;
2156
- const right = rootBounds.center.x + rootBounds.size.x / 2;
2157
- const top = rootBounds.center.y + rootBounds.size.y / 2;
2158
- const bottom = rootBounds.center.y - rootBounds.size.y / 2;
2159
- return /* @__PURE__ */ jsx7(
2160
- Line3,
2161
- {
2162
- points: [
2163
- [left, top, 99],
2164
- [right, top, 99],
2165
- [right, bottom, 99],
2166
- [left, bottom, 99],
2167
- [left, top, 99]
2168
- ],
2169
- color: "red",
2170
- lineWidth: 2
2171
- }
2172
- );
967
+ function normalizeToneMappingMode(value) {
968
+ return isToneMappingMode(value) ? value : DEFAULT_TONE_MAPPING_MODE;
969
+ }
970
+
971
+ // src/context.ts
972
+ import { createContext } from "react";
973
+ var VizijContext = createContext(null);
974
+
975
+ // src/hooks/use-vizij-store.ts
976
+ import { useContext } from "react";
977
+ import { useStore } from "zustand";
978
+ function useVizijStore(selector) {
979
+ const store = useContext(VizijContext);
980
+ if (!store) throw new Error("Missing VizijProvider in the tree");
981
+ return useStore(store, selector);
2173
982
  }
2174
- var defaultRootBounds = {
2175
- center: { x: 0, y: 0 },
2176
- size: { x: 5, y: 4 }
2177
- };
2178
983
 
2179
984
  // src/hooks/use-vizij-store-subscription.ts
2180
- import { useContext as useContext4, useEffect as useEffect8 } from "react";
985
+ import { useContext as useContext2, useEffect as useEffect3 } from "react";
2181
986
  function useVizijStoreSubscription(selector, listener) {
2182
- const store = useContext4(VizijContext);
987
+ const store = useContext2(VizijContext);
2183
988
  if (!store) throw new Error("Missing VizijProvider in the tree");
2184
- useEffect8(() => {
989
+ useEffect3(() => {
2185
990
  const initialValue = selector(store.getState());
2186
991
  listener(initialValue);
2187
992
  return store.subscribe(selector, listener);
@@ -2189,27 +994,59 @@ function useVizijStoreSubscription(selector, listener) {
2189
994
  }
2190
995
 
2191
996
  // src/hooks/use-vizij-store-setter.ts
2192
- import { useContext as useContext5 } from "react";
997
+ import { useContext as useContext3 } from "react";
2193
998
  function useVizijStoreSetter() {
2194
- const store = useContext5(VizijContext);
999
+ const store = useContext3(VizijContext);
2195
1000
  if (!store) throw new Error("Missing VizijProvider in the tree");
2196
1001
  return store.setState;
2197
1002
  }
2198
1003
 
2199
1004
  // src/hooks/use-vizij-store-getter.ts
2200
- import { useContext as useContext6 } from "react";
1005
+ import { useContext as useContext4 } from "react";
2201
1006
  function useVizijStoreGetter() {
2202
- const store = useContext6(VizijContext);
1007
+ const store = useContext4(VizijContext);
2203
1008
  if (!store) throw new Error("Missing VizijProvider in the tree");
2204
1009
  return store.getState;
2205
1010
  }
2206
1011
 
2207
1012
  // src/functions/load-gltf.ts
2208
- import * as THREE5 from "three";
1013
+ import * as THREE2 from "three";
2209
1014
  import { GLTFLoader, DRACOLoader } from "three-stdlib";
2210
1015
 
1016
+ // src/memoryInvestigation.ts
1017
+ function getGlobalMemoryInvestigation() {
1018
+ return globalThis;
1019
+ }
1020
+ function updateRenderState(updater) {
1021
+ const globalObj = getGlobalMemoryInvestigation();
1022
+ if (!globalObj.__VIZIJ_MEMORY_INVESTIGATION__?.enabled) {
1023
+ return;
1024
+ }
1025
+ if (!globalObj.__vizijMemoryDebugState) {
1026
+ globalObj.__vizijMemoryDebugState = {};
1027
+ }
1028
+ if (!globalObj.__vizijMemoryDebugState.render) {
1029
+ globalObj.__vizijMemoryDebugState.render = {};
1030
+ }
1031
+ updater(globalObj.__vizijMemoryDebugState.render);
1032
+ }
1033
+ function recordRenderCounter(key, delta = 1) {
1034
+ updateRenderState((renderState) => {
1035
+ const current = typeof renderState[key] === "number" ? Number(renderState[key]) : 0;
1036
+ renderState[key] = current + delta;
1037
+ });
1038
+ }
1039
+
2211
1040
  // src/functions/gltf-loading/traverse-three.ts
2212
- import * as THREE4 from "three";
1041
+ import * as THREE from "three";
1042
+
1043
+ // src/functions/empty-model-error.ts
1044
+ var EmptyModelError = class extends Error {
1045
+ constructor(message) {
1046
+ super(message);
1047
+ this.name = "EmptyModelError";
1048
+ }
1049
+ };
2213
1050
 
2214
1051
  // src/functions/gltf-loading/map-features.ts
2215
1052
  function mapFeatures(features) {
@@ -2234,14 +1071,14 @@ function mapFeatures(features) {
2234
1071
  }
2235
1072
 
2236
1073
  // src/functions/gltf-loading/import-scene.ts
2237
- import { Object3D as Object3D7 } from "three";
1074
+ import { Object3D as Object3D3 } from "three";
2238
1075
 
2239
1076
  // src/functions/gltf-loading/import-group.ts
2240
- import { Object3D as Object3D6 } from "three";
1077
+ import { Object3D as Object3D2 } from "three";
2241
1078
  import { createBrowserSafeId as createBrowserSafeId5 } from "@vizij/utils";
2242
1079
 
2243
1080
  // src/functions/gltf-loading/import-mesh.ts
2244
- import { Object3D as Object3D5 } from "three";
1081
+ import { Object3D } from "three";
2245
1082
  import { createBrowserSafeId as createBrowserSafeId4 } from "@vizij/utils";
2246
1083
 
2247
1084
  // src/functions/gltf-loading/import-geometry.ts
@@ -2312,7 +1149,7 @@ function importGeometry(geometry, mesh) {
2312
1149
  }
2313
1150
 
2314
1151
  // src/functions/gltf-loading/import-mesh.ts
2315
- Object3D5.DEFAULT_UP.set(0, 0, 1);
1152
+ Object3D.DEFAULT_UP.set(0, 0, 1);
2316
1153
  function importMesh(mesh, colorLookup) {
2317
1154
  let world = {};
2318
1155
  let animatables = {};
@@ -2569,7 +1406,7 @@ function shouldImportAsGroupChild(child) {
2569
1406
  }
2570
1407
 
2571
1408
  // src/functions/gltf-loading/import-group.ts
2572
- Object3D6.DEFAULT_UP.set(0, 0, 1);
1409
+ Object3D2.DEFAULT_UP.set(0, 0, 1);
2573
1410
  function importGroup(group, colorLookup, rootBounds) {
2574
1411
  let world = {};
2575
1412
  let animatables = {};
@@ -2674,7 +1511,7 @@ function shouldImportAsGroupChild2(child) {
2674
1511
  }
2675
1512
 
2676
1513
  // src/functions/gltf-loading/import-scene.ts
2677
- Object3D7.DEFAULT_UP.set(0, 0, 1);
1514
+ Object3D3.DEFAULT_UP.set(0, 0, 1);
2678
1515
  function importScene(scene, rootBounds) {
2679
1516
  let world = {};
2680
1517
  let animatables = {};
@@ -2689,7 +1526,7 @@ function importScene(scene, rootBounds) {
2689
1526
  }
2690
1527
 
2691
1528
  // src/functions/gltf-loading/traverse-three.ts
2692
- THREE4.Object3D.DEFAULT_UP.set(0, 0, 1);
1529
+ THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
2693
1530
  function traverseThree(group, aggressiveImport = false, rootBounds) {
2694
1531
  const worldData = {};
2695
1532
  const animatableData = {};
@@ -2763,7 +1600,9 @@ function traverseThree(group, aggressiveImport = false, rootBounds) {
2763
1600
  } else {
2764
1601
  const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
2765
1602
  if (!derivedRootBounds) {
2766
- throw new Error("Root bounds are expected if using an aggressive import");
1603
+ throw new EmptyModelError(
1604
+ "The GLB has no RobotData and no mesh, ellipse or rectangle to import"
1605
+ );
2767
1606
  }
2768
1607
  const [newWorldData, newAnimatableData] = importScene(
2769
1608
  group,
@@ -2784,7 +1623,7 @@ var MATERIAL_NAME_FEATURE_KEYS = [
2784
1623
  "emissiveIntensity",
2785
1624
  "specular"
2786
1625
  ];
2787
- var MATERIAL_NAME_SUFFIXES2 = [
1626
+ var MATERIAL_NAME_SUFFIXES = [
2788
1627
  " color",
2789
1628
  " colours",
2790
1629
  " colour",
@@ -2858,7 +1697,7 @@ function extractMaterialNameFromFeature(feature) {
2858
1697
  function stripMaterialSuffixes(name) {
2859
1698
  const trimmed = name.trim();
2860
1699
  const lowered = trimmed.toLowerCase();
2861
- for (const suffix of MATERIAL_NAME_SUFFIXES2) {
1700
+ for (const suffix of MATERIAL_NAME_SUFFIXES) {
2862
1701
  if (lowered.endsWith(suffix)) {
2863
1702
  return trimmed.slice(0, trimmed.length - suffix.length).trim();
2864
1703
  }
@@ -2868,44 +1707,69 @@ function stripMaterialSuffixes(name) {
2868
1707
  function isStoredAnimatedFeature(feature) {
2869
1708
  return Boolean(feature) && typeof feature === "object" && feature.animated === true && "value" in feature;
2870
1709
  }
2871
- function isGroupFeatures(value) {
1710
+ var TRANSFORM = [
1711
+ ["translation", ["x", "y", "z"]],
1712
+ ["rotation", ["r", "p", "y"]]
1713
+ ];
1714
+ var REQUIRED_FEATURES = {
1715
+ group: TRANSFORM,
1716
+ shape: TRANSFORM,
1717
+ ellipse: [...TRANSFORM, ["width", []], ["height", []]],
1718
+ rectangle: [...TRANSFORM, ["width", []], ["height", []]]
1719
+ };
1720
+ function assertRequiredFeatures(kind, value) {
2872
1721
  if (!value || typeof value !== "object") {
2873
1722
  throw new Error("Expected object");
2874
1723
  }
2875
- if (!["translation", "rotation"].every((key) => key in value)) {
2876
- throw new Error("Expected translation and rotation keys in features");
1724
+ const missing = REQUIRED_FEATURES[kind].filter(
1725
+ ([name, axes]) => !(name in value) && !axes.some((axis) => `${name}.${axis}` in value)
1726
+ ).map(([name]) => name);
1727
+ if (missing.length > 0) {
1728
+ throw new Error(
1729
+ `An element of type ${kind} lacks ${missing.join(", ")} in its features`
1730
+ );
2877
1731
  }
2878
1732
  }
1733
+ function isGroupFeatures(value) {
1734
+ assertRequiredFeatures("group", value);
1735
+ }
2879
1736
  function isShapeFeatures(value) {
2880
- if (!value || typeof value !== "object") {
2881
- throw new Error("Expected object");
2882
- }
2883
- if (!["translation", "rotation"].every((key) => key in value)) {
2884
- throw new Error("Expected translation and rotation keys in features");
2885
- }
1737
+ assertRequiredFeatures("shape", value);
2886
1738
  }
2887
1739
  function isEllipseFeatures(value) {
2888
- if (!value || typeof value !== "object") {
2889
- throw new Error("Expected object");
2890
- }
2891
- if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
2892
- throw new Error(
2893
- "Expected translation, rotation, width, and height keys in features"
2894
- );
2895
- }
1740
+ assertRequiredFeatures("ellipse", value);
2896
1741
  }
2897
1742
  function isRectangleFeatures(value) {
2898
- if (!value || typeof value !== "object") {
2899
- throw new Error("Expected object");
2900
- }
2901
- if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
2902
- throw new Error(
2903
- "Expected translation, rotation, width, and height keys in features"
2904
- );
2905
- }
1743
+ assertRequiredFeatures("rectangle", value);
1744
+ }
1745
+ var PRIMITIVE_HALF_EXTENT = {
1746
+ ellipse: 1,
1747
+ rectangle: 0.5
1748
+ };
1749
+ function expandByPrimitives(box, root) {
1750
+ const center = new THREE.Vector3();
1751
+ const half = new THREE.Vector3();
1752
+ root.traverse((object) => {
1753
+ if (object.isMesh) {
1754
+ return;
1755
+ }
1756
+ const type = object.userData?.gltfExtensions?.RobotData?.type;
1757
+ const extent = typeof type === "string" ? PRIMITIVE_HALF_EXTENT[type] : void 0;
1758
+ if (extent === void 0) {
1759
+ return;
1760
+ }
1761
+ const e = object.matrixWorld.elements;
1762
+ const along = type === "ellipse" ? (a, b) => Math.hypot(a, b) : (a, b) => Math.abs(a) + Math.abs(b);
1763
+ half.set(along(e[0], e[4]), along(e[1], e[5]), along(e[2], e[6]));
1764
+ half.multiplyScalar(extent);
1765
+ center.setFromMatrixPosition(object.matrixWorld);
1766
+ box.expandByPoint(center.clone().sub(half));
1767
+ box.expandByPoint(center.clone().add(half));
1768
+ });
2906
1769
  }
2907
1770
  function deriveRootBounds(group) {
2908
- const boundingBox = new THREE4.Box3().setFromObject(group);
1771
+ const boundingBox = new THREE.Box3().setFromObject(group);
1772
+ expandByPrimitives(boundingBox, group);
2909
1773
  if (boundingBox.isEmpty()) {
2910
1774
  return null;
2911
1775
  }
@@ -3432,30 +2296,24 @@ function extractVizijAnimations(parserJson, clips) {
3432
2296
  }
3433
2297
 
3434
2298
  // src/functions/load-gltf.ts
3435
- THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
2299
+ THREE2.Object3D.DEFAULT_UP.set(0, 0, 1);
3436
2300
  var GLB_MAGIC = 1179937895;
3437
2301
  var GLB_VERSION = 2;
3438
2302
  var GLB_HEADER_BYTES = 12;
3439
2303
  var GLB_CHUNK_HEADER_BYTES = 8;
3440
2304
  var GLB_JSON_CHUNK_TYPE = 1313821514;
3441
- var EmptyModelError = class extends Error {
3442
- constructor(message) {
3443
- super(message);
3444
- this.name = "EmptyModelError";
3445
- }
3446
- };
3447
2305
  function parseGlbJsonChunk(buffer) {
3448
2306
  if (buffer.byteLength < GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES) {
3449
2307
  return void 0;
3450
2308
  }
3451
- const view = new DataView(buffer);
3452
- const magic = view.getUint32(0, true);
3453
- const version = view.getUint32(4, true);
2309
+ const view2 = new DataView(buffer);
2310
+ const magic = view2.getUint32(0, true);
2311
+ const version = view2.getUint32(4, true);
3454
2312
  if (magic !== GLB_MAGIC || version !== GLB_VERSION) {
3455
2313
  return void 0;
3456
2314
  }
3457
- const chunkLength = view.getUint32(GLB_HEADER_BYTES, true);
3458
- const chunkType = view.getUint32(GLB_HEADER_BYTES + 4, true);
2315
+ const chunkLength = view2.getUint32(GLB_HEADER_BYTES, true);
2316
+ const chunkType = view2.getUint32(GLB_HEADER_BYTES + 4, true);
3459
2317
  if (chunkType !== GLB_JSON_CHUNK_TYPE) {
3460
2318
  return void 0;
3461
2319
  }
@@ -3565,8 +2423,8 @@ async function loadGLTFFromBlobWithBundle(blob, aggressiveImport = false, rootBo
3565
2423
 
3566
2424
  // src/functions/export.ts
3567
2425
  import { GLTFExporter } from "three-stdlib";
3568
- import * as THREE6 from "three";
3569
- THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
2426
+ import * as THREE3 from "three";
2427
+ THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
3570
2428
  function normalizeExportError(error) {
3571
2429
  if (error instanceof Error) {
3572
2430
  return error;
@@ -3662,14 +2520,14 @@ function patchGlbJson(buffer, mutate) {
3662
2520
  return buffer;
3663
2521
  }
3664
2522
  const originalBytes = new Uint8Array(buffer);
3665
- const view = new DataView(buffer);
3666
- const magic = view.getUint32(0, true);
3667
- const version = view.getUint32(4, true);
2523
+ const view2 = new DataView(buffer);
2524
+ const magic = view2.getUint32(0, true);
2525
+ const version = view2.getUint32(4, true);
3668
2526
  if (magic !== GLB_MAGIC2 || version !== GLB_VERSION2) {
3669
2527
  return buffer;
3670
2528
  }
3671
- const jsonChunkLength = view.getUint32(GLB_HEADER_BYTES2, true);
3672
- const jsonChunkType = view.getUint32(GLB_HEADER_BYTES2 + 4, true);
2529
+ const jsonChunkLength = view2.getUint32(GLB_HEADER_BYTES2, true);
2530
+ const jsonChunkType = view2.getUint32(GLB_HEADER_BYTES2 + 4, true);
3673
2531
  if (jsonChunkType !== GLB_JSON_CHUNK_TYPE2) {
3674
2532
  return buffer;
3675
2533
  }
@@ -3753,9 +2611,9 @@ async function exportScene(root, options = {}) {
3753
2611
  }
3754
2612
  }
3755
2613
  }));
3756
- const exportRoot = root instanceof THREE6.Scene ? root : root.clone(true);
3757
- const exportTarget = exportRoot instanceof THREE6.Scene ? exportRoot : (() => {
3758
- const scene = new THREE6.Scene();
2614
+ const exportRoot = root instanceof THREE3.Scene ? root : root.clone(true);
2615
+ const exportTarget = exportRoot instanceof THREE3.Scene ? exportRoot : (() => {
2616
+ const scene = new THREE3.Scene();
3759
2617
  scene.name = root.name?.trim() || "Scene";
3760
2618
  scene.add(exportRoot);
3761
2619
  return scene;
@@ -3783,17 +2641,171 @@ async function exportScene(root, options = {}) {
3783
2641
  return sanitizeExportedGlb(gltf, root.name?.trim() || void 0);
3784
2642
  }
3785
2643
 
2644
+ // src/functions/export-animation.ts
2645
+ import {
2646
+ AnimationClip,
2647
+ NumberKeyframeTrack,
2648
+ QuaternionKeyframeTrack,
2649
+ VectorKeyframeTrack
2650
+ } from "three";
2651
+ function findByName(root, name) {
2652
+ let found = null;
2653
+ root.traverse((child) => {
2654
+ if (!found && child.name === name) {
2655
+ found = child;
2656
+ }
2657
+ });
2658
+ return found;
2659
+ }
2660
+ function parseMorphTargetName(trackName) {
2661
+ const match = /\.morphTargetInfluences\[(.+)\]$/.exec(trackName);
2662
+ return match ? match[1] : null;
2663
+ }
2664
+ function createExportAnimation(options) {
2665
+ const { name, duration, tracks, root } = options;
2666
+ const issues = [];
2667
+ const built = [];
2668
+ for (const spec of tracks) {
2669
+ const node = findByName(root, spec.elementName);
2670
+ if (!node) {
2671
+ issues.push({
2672
+ trackName: spec.name,
2673
+ reason: "node-not-found",
2674
+ detail: spec.elementName
2675
+ });
2676
+ continue;
2677
+ }
2678
+ if (spec.values.length !== spec.times.length * spec.stride) {
2679
+ issues.push({
2680
+ trackName: spec.name,
2681
+ reason: "bad-stride",
2682
+ detail: `${spec.values.length} values for ${spec.times.length} times at stride ${spec.stride}`
2683
+ });
2684
+ continue;
2685
+ }
2686
+ if (spec.property === "morphTargetInfluences") {
2687
+ const morphName = parseMorphTargetName(spec.name);
2688
+ const dictionary = node.morphTargetDictionary;
2689
+ if (!morphName || !dictionary || !(morphName in dictionary)) {
2690
+ issues.push({
2691
+ trackName: spec.name,
2692
+ reason: "morph-target-not-found",
2693
+ detail: morphName ?? void 0
2694
+ });
2695
+ continue;
2696
+ }
2697
+ built.push(new NumberKeyframeTrack(spec.name, spec.times, spec.values));
2698
+ continue;
2699
+ }
2700
+ if (spec.property === "quaternion") {
2701
+ built.push(
2702
+ new QuaternionKeyframeTrack(spec.name, spec.times, spec.values)
2703
+ );
2704
+ continue;
2705
+ }
2706
+ built.push(new VectorKeyframeTrack(spec.name, spec.times, spec.values));
2707
+ }
2708
+ if (built.length === 0) {
2709
+ return { animation: null, issues };
2710
+ }
2711
+ return { animation: new AnimationClip(name, duration, built), issues };
2712
+ }
2713
+
3786
2714
  // src/functions/build-scene-graph.ts
3787
- import * as THREE7 from "three";
2715
+ import * as THREE4 from "three";
3788
2716
  import {
3789
- instanceOfRawEuler as instanceOfRawEuler5,
3790
- instanceOfRawHSL as instanceOfRawHSL4,
3791
- instanceOfRawNumber as instanceOfRawNumber5,
3792
- instanceOfRawRGB as instanceOfRawRGB4,
3793
- instanceOfRawVector2 as instanceOfRawVector24,
3794
- instanceOfRawVector3 as instanceOfRawVector35
2717
+ instanceOfRawEuler,
2718
+ instanceOfRawHSL,
2719
+ instanceOfRawNumber,
2720
+ instanceOfRawRGB,
2721
+ instanceOfRawVector2,
2722
+ instanceOfRawVector3
3795
2723
  } from "@vizij/utils";
3796
- THREE7.Object3D.DEFAULT_UP.set(0, 0, 1);
2724
+
2725
+ // src/functions/create-stored-features.ts
2726
+ function createStoredFeatures(objectFeatures, animatableValues) {
2727
+ const stored = Object.entries(objectFeatures).map(
2728
+ ([key, feat]) => {
2729
+ if (feat.animated) {
2730
+ const storedFeat = {
2731
+ animated: true,
2732
+ value: animatableValues[feat.value],
2733
+ ...feat.label ? { label: feat.label } : {}
2734
+ };
2735
+ return [key, storedFeat];
2736
+ }
2737
+ const staticFeature = feat;
2738
+ return [key, staticFeature.label ? { ...staticFeature } : staticFeature];
2739
+ }
2740
+ );
2741
+ return Object.fromEntries(stored);
2742
+ }
2743
+
2744
+ // src/functions/create-stored-data.ts
2745
+ function createStoredRenderable(data, animatableValues) {
2746
+ const { geometry: _geometry, ...stored } = data;
2747
+ const storedFeatures = createStoredFeatures(
2748
+ data.features,
2749
+ animatableValues
2750
+ );
2751
+ return { ...stored, features: storedFeatures };
2752
+ }
2753
+
2754
+ // src/functions/material-name.ts
2755
+ var MATERIAL_FEATURE_KEYS = [
2756
+ "color",
2757
+ "opacity",
2758
+ "roughness",
2759
+ "metalness",
2760
+ "shininess",
2761
+ "emissive",
2762
+ "emissiveIntensity",
2763
+ "specular"
2764
+ ];
2765
+ var MATERIAL_NAME_SUFFIXES2 = [
2766
+ " color",
2767
+ " colours",
2768
+ " colour",
2769
+ " opacity",
2770
+ " roughness",
2771
+ " metalness",
2772
+ " shininess",
2773
+ " emissive intensity",
2774
+ " emissive",
2775
+ " specular"
2776
+ ];
2777
+ function deriveMaterialName(shape, values) {
2778
+ for (const key of MATERIAL_FEATURE_KEYS) {
2779
+ const feature = shape.features[key];
2780
+ if (feature && feature.animated) {
2781
+ const animatable = values[feature.value];
2782
+ const candidate = extractMaterialName(animatable?.name);
2783
+ if (candidate) {
2784
+ return candidate;
2785
+ }
2786
+ }
2787
+ }
2788
+ return void 0;
2789
+ }
2790
+ function extractMaterialName(name) {
2791
+ if (!name) {
2792
+ return void 0;
2793
+ }
2794
+ const trimmed = name.trim();
2795
+ if (!trimmed) {
2796
+ return void 0;
2797
+ }
2798
+ const lowered = trimmed.toLowerCase();
2799
+ for (const suffix of MATERIAL_NAME_SUFFIXES2) {
2800
+ if (lowered.endsWith(suffix)) {
2801
+ return trimmed.slice(0, trimmed.length - suffix.length).trim();
2802
+ }
2803
+ }
2804
+ return trimmed;
2805
+ }
2806
+
2807
+ // src/functions/build-scene-graph.ts
2808
+ THREE4.Object3D.DEFAULT_UP.set(0, 0, 1);
3797
2809
  function buildSceneGraph(world, animatables, rootId) {
3798
2810
  const entry = world[rootId];
3799
2811
  if (!entry) {
@@ -3808,10 +2820,13 @@ function buildSceneGraph(world, animatables, rootId) {
3808
2820
  const robotData = createStoredRenderable(entry, used);
3809
2821
  switch (entry.type) {
3810
2822
  case "group": {
3811
- const object = new THREE7.Group();
2823
+ const object = new THREE4.Group();
3812
2824
  object.name = entry.name;
3813
2825
  object.userData = { gltfExtensions: { RobotData: robotData } };
3814
- applyFeatures(entry.features, animatables, groupTransform(object));
2826
+ applyFeatures(entry.features, animatables, {
2827
+ ...groupTransform(object),
2828
+ ...perAxisTransform(object, { scale: true })
2829
+ });
3815
2830
  for (const child of entry.children) {
3816
2831
  object.add(buildSceneGraph(world, animatables, child));
3817
2832
  }
@@ -3824,12 +2839,14 @@ function buildSceneGraph(world, animatables, rootId) {
3824
2839
  }
3825
2840
  const material = createMaterial(entry.material);
3826
2841
  material.name = deriveMaterialName(entry, used) ?? entry.name ?? "";
3827
- const object = new THREE7.Mesh(geometry, material);
2842
+ const object = new THREE4.Mesh(geometry, material);
3828
2843
  object.name = entry.name;
3829
2844
  object.userData = { gltfExtensions: { RobotData: robotData } };
3830
2845
  applyFeatures(entry.features, animatables, {
3831
2846
  ...shapeTransform(object),
2847
+ ...perAxisTransform(object, { scale: true }),
3832
2848
  ...materialFactors(material),
2849
+ ...perAxisMaterialFactors(material),
3833
2850
  ...morphWeights(object, entry)
3834
2851
  });
3835
2852
  for (const child of entry.children ?? []) {
@@ -3837,10 +2854,25 @@ function buildSceneGraph(world, animatables, rootId) {
3837
2854
  }
3838
2855
  return object;
3839
2856
  }
3840
- default:
3841
- throw new Error(
3842
- `Element "${rootId}" is a ${entry.type}, which has no exported form.`
3843
- );
2857
+ case "ellipse":
2858
+ case "rectangle": {
2859
+ const object = new THREE4.Group();
2860
+ object.name = entry.name;
2861
+ object.userData = { gltfExtensions: { RobotData: robotData } };
2862
+ const { translation, rotation } = groupTransform(object);
2863
+ applyFeatures(entry.features, animatables, {
2864
+ translation,
2865
+ rotation,
2866
+ ...perAxisTransform(object, { scale: false }),
2867
+ width: (value) => {
2868
+ if (instanceOfRawNumber(value)) object.scale.x = value;
2869
+ },
2870
+ height: (value) => {
2871
+ if (instanceOfRawNumber(value)) object.scale.y = value;
2872
+ }
2873
+ });
2874
+ return object;
2875
+ }
3844
2876
  }
3845
2877
  }
3846
2878
  function animatableOf(animatables, id) {
@@ -3865,28 +2897,29 @@ function applyFeatures(features, animatables, handlers) {
3865
2897
  function groupTransform(object) {
3866
2898
  return {
3867
2899
  translation: (value) => {
3868
- if (instanceOfRawVector35(value)) {
2900
+ if (instanceOfRawVector3(value)) {
3869
2901
  object.position.set(value.x, value.y, value.z);
3870
- } else if (instanceOfRawVector24(value)) {
2902
+ } else if (instanceOfRawVector2(value)) {
3871
2903
  object.position.set(value.x, value.y, object.position.z);
3872
2904
  }
3873
2905
  },
3874
2906
  rotation: (value) => {
3875
- if (instanceOfRawEuler5(value)) {
3876
- object.rotation.set(value.x, value.y, value.z, "ZYX");
3877
- } else if (instanceOfRawNumber5(value)) {
2907
+ const euler = eulerOf(value);
2908
+ if (euler) {
2909
+ object.rotation.set(...euler, "ZYX");
2910
+ } else if (instanceOfRawNumber(value)) {
3878
2911
  object.rotation.set(0, 0, 0);
3879
2912
  object.rotateZ(value);
3880
2913
  }
3881
2914
  },
3882
2915
  scale: (value) => {
3883
- if (instanceOfRawVector35(value)) {
2916
+ if (instanceOfRawVector3(value)) {
3884
2917
  if (value.x === null || value.y === null || value.z === null) {
3885
2918
  object.scale.set(0.1, 0.1, 0.1);
3886
2919
  } else {
3887
2920
  object.scale.set(value.x, value.y, value.z);
3888
2921
  }
3889
- } else if (instanceOfRawNumber5(value)) {
2922
+ } else if (instanceOfRawNumber(value)) {
3890
2923
  object.scale.set(value, value, value);
3891
2924
  } else {
3892
2925
  object.scale.set(1, 1, 1);
@@ -3897,19 +2930,20 @@ function groupTransform(object) {
3897
2930
  function shapeTransform(object) {
3898
2931
  return {
3899
2932
  translation: (value) => {
3900
- if (instanceOfRawVector35(value)) {
2933
+ if (instanceOfRawVector3(value)) {
3901
2934
  object.position.set(value.x, value.y, value.z);
3902
2935
  }
3903
2936
  },
3904
2937
  rotation: (value) => {
3905
- if (instanceOfRawEuler5(value)) {
3906
- object.rotation.set(value.x, value.y, value.z, "ZYX");
2938
+ const euler = eulerOf(value);
2939
+ if (euler) {
2940
+ object.rotation.set(...euler, "ZYX");
3907
2941
  }
3908
2942
  },
3909
2943
  scale: (value) => {
3910
- if (instanceOfRawVector35(value)) {
2944
+ if (instanceOfRawVector3(value)) {
3911
2945
  object.scale.set(value.x, value.y, value.z);
3912
- } else if (instanceOfRawNumber5(value)) {
2946
+ } else if (instanceOfRawNumber(value)) {
3913
2947
  object.scale.set(value, value, value);
3914
2948
  } else {
3915
2949
  object.scale.set(1, 1, 1);
@@ -3917,12 +2951,64 @@ function shapeTransform(object) {
3917
2951
  }
3918
2952
  };
3919
2953
  }
2954
+ function eulerOf(value) {
2955
+ if (instanceOfRawEuler(value)) {
2956
+ return [value.x, value.y, value.z];
2957
+ }
2958
+ const rpy = value;
2959
+ if (rpy !== null && typeof rpy === "object" && typeof rpy.r === "number" && typeof rpy.p === "number" && typeof rpy.y === "number") {
2960
+ return [rpy.r, rpy.p, rpy.y];
2961
+ }
2962
+ return void 0;
2963
+ }
2964
+ function perAxis(name, axes, set) {
2965
+ return Object.fromEntries(
2966
+ axes.map((axis, index) => [
2967
+ `${name}.${axis}`,
2968
+ (value) => {
2969
+ if (instanceOfRawNumber(value)) {
2970
+ set(index, value);
2971
+ }
2972
+ }
2973
+ ])
2974
+ );
2975
+ }
2976
+ function perAxisTransform(object, { scale }) {
2977
+ return {
2978
+ ...perAxis("translation", ["x", "y", "z"], (index, value) => {
2979
+ object.position.setComponent(index, value);
2980
+ }),
2981
+ ...perAxis("rotation", ["r", "p", "y"], (index, value) => {
2982
+ const euler = [object.rotation.x, object.rotation.y, object.rotation.z];
2983
+ euler[index] = value;
2984
+ object.rotation.set(euler[0], euler[1], euler[2], "ZYX");
2985
+ }),
2986
+ ...scale ? perAxis("scale", ["x", "y", "z"], (index, value) => {
2987
+ object.scale.setComponent(index, value);
2988
+ }) : {}
2989
+ };
2990
+ }
2991
+ function perAxisMaterialFactors(material) {
2992
+ const standard = material;
2993
+ const phong = material;
2994
+ const channels = ["r", "g", "b"];
2995
+ const component = (color) => (index, value) => {
2996
+ if (color) {
2997
+ color[channels[index]] = value;
2998
+ }
2999
+ };
3000
+ return {
3001
+ ...perAxis("color", channels, component(standard.color)),
3002
+ ...perAxis("emissive", channels, component(standard.emissive)),
3003
+ ...perAxis("specular", channels, component(phong.specular))
3004
+ };
3005
+ }
3920
3006
  function materialFactors(material) {
3921
3007
  const standard = material;
3922
3008
  const phong = material;
3923
3009
  return {
3924
3010
  opacity: (value) => {
3925
- if (instanceOfRawNumber5(value)) {
3011
+ if (instanceOfRawNumber(value)) {
3926
3012
  material.opacity = value;
3927
3013
  material.transparent = value < 1;
3928
3014
  }
@@ -3931,41 +3017,41 @@ function materialFactors(material) {
3931
3017
  if (!standard.color) {
3932
3018
  return;
3933
3019
  }
3934
- if (instanceOfRawRGB4(value)) {
3020
+ if (instanceOfRawRGB(value)) {
3935
3021
  standard.color.setRGB(value.r, value.g, value.b);
3936
- } else if (instanceOfRawVector35(value)) {
3022
+ } else if (instanceOfRawVector3(value)) {
3937
3023
  standard.color.setRGB(value.x, value.y, value.z);
3938
- } else if (instanceOfRawHSL4(value)) {
3024
+ } else if (instanceOfRawHSL(value)) {
3939
3025
  standard.color.setHSL(value.h, value.s, value.l);
3940
3026
  }
3941
3027
  },
3942
3028
  roughness: (value) => {
3943
- if (standard.roughness !== void 0 && instanceOfRawNumber5(value)) {
3029
+ if (standard.roughness !== void 0 && instanceOfRawNumber(value)) {
3944
3030
  standard.roughness = value;
3945
3031
  }
3946
3032
  },
3947
3033
  metalness: (value) => {
3948
- if (standard.metalness !== void 0 && instanceOfRawNumber5(value)) {
3034
+ if (standard.metalness !== void 0 && instanceOfRawNumber(value)) {
3949
3035
  standard.metalness = value;
3950
3036
  }
3951
3037
  },
3952
3038
  shininess: (value) => {
3953
- if (phong.shininess !== void 0 && instanceOfRawNumber5(value)) {
3039
+ if (phong.shininess !== void 0 && instanceOfRawNumber(value)) {
3954
3040
  phong.shininess = value;
3955
3041
  }
3956
3042
  },
3957
3043
  emissive: (value) => {
3958
- if (standard.emissive && instanceOfRawRGB4(value)) {
3044
+ if (standard.emissive && instanceOfRawRGB(value)) {
3959
3045
  standard.emissive.setRGB(value.r, value.g, value.b);
3960
3046
  }
3961
3047
  },
3962
3048
  emissiveIntensity: (value) => {
3963
- if (standard.emissiveIntensity !== void 0 && instanceOfRawNumber5(value)) {
3049
+ if (standard.emissiveIntensity !== void 0 && instanceOfRawNumber(value)) {
3964
3050
  standard.emissiveIntensity = value;
3965
3051
  }
3966
3052
  },
3967
3053
  specular: (value) => {
3968
- if (phong.specular && instanceOfRawRGB4(value)) {
3054
+ if (phong.specular && instanceOfRawRGB(value)) {
3969
3055
  phong.specular.setRGB(value.r, value.g, value.b);
3970
3056
  }
3971
3057
  }
@@ -3985,7 +3071,7 @@ function morphWeights(object, shape) {
3985
3071
  targets.map((target, index) => [
3986
3072
  target,
3987
3073
  (value) => {
3988
- if (instanceOfRawNumber5(value)) {
3074
+ if (instanceOfRawNumber(value)) {
3989
3075
  influences[index] = value;
3990
3076
  }
3991
3077
  }
@@ -3993,18 +3079,18 @@ function morphWeights(object, shape) {
3993
3079
  );
3994
3080
  }
3995
3081
  function createMaterial(kind) {
3996
- const parameters = { side: THREE7.DoubleSide };
3082
+ const parameters = { side: THREE4.DoubleSide };
3997
3083
  switch (kind) {
3998
3084
  case "basic" /* Basic */:
3999
- return new THREE7.MeshBasicMaterial(parameters);
3085
+ return new THREE4.MeshBasicMaterial(parameters);
4000
3086
  case "lambert" /* Lambert */:
4001
- return new THREE7.MeshLambertMaterial(parameters);
3087
+ return new THREE4.MeshLambertMaterial(parameters);
4002
3088
  case "phong" /* Phong */:
4003
- return new THREE7.MeshPhongMaterial(parameters);
3089
+ return new THREE4.MeshPhongMaterial(parameters);
4004
3090
  case "normal" /* Normal */:
4005
- return new THREE7.MeshNormalMaterial(parameters);
3091
+ return new THREE4.MeshNormalMaterial(parameters);
4006
3092
  case "standard" /* Standard */:
4007
- return new THREE7.MeshStandardMaterial(parameters);
3093
+ return new THREE4.MeshStandardMaterial(parameters);
4008
3094
  }
4009
3095
  }
4010
3096
  var SCALAR_KEYS = ["f32", "f64", "i64", "i32", "u64", "u32", "float"];
@@ -4052,15 +3138,17 @@ function authoredDefault(animatable) {
4052
3138
  export {
4053
3139
  DEFAULT_TONE_MAPPING_MODE,
4054
3140
  EmptyModelError,
4055
- InnerVizij,
4056
3141
  ShapeMaterial,
4057
3142
  TONE_MAPPING_LABELS,
4058
3143
  TONE_MAPPING_MODES,
4059
3144
  Vizij,
3145
+ VizijCanvas,
4060
3146
  VizijContext,
4061
3147
  VizijSlice,
4062
3148
  applyVizijBundle,
4063
3149
  buildSceneGraph,
3150
+ canvasMounted,
3151
+ createExportAnimation,
4064
3152
  createVizijStore,
4065
3153
  deriveMorphFeatureKeys,
4066
3154
  deriveRootBounds,
@@ -4075,10 +3163,9 @@ export {
4075
3163
  parseGlbJsonChunk,
4076
3164
  patchGlbJson,
4077
3165
  patchVizijBundleMetadata,
4078
- resolveToneMapping,
4079
3166
  sanitizeMorphKey,
3167
+ setVizijProgress,
4080
3168
  useDefaultVizijStore,
4081
- useFeatures,
4082
3169
  useVizijStore,
4083
3170
  useVizijStoreGetter,
4084
3171
  useVizijStoreSetter,