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