@vizij/render 1.0.0 → 2.0.0

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