oomi-ai 0.2.39 → 0.2.40

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.
Files changed (45) hide show
  1. package/README.md +5 -3
  2. package/lib/personaRuntimeManager.js +21 -17
  3. package/lib/personaRuntimeProcess.js +392 -49
  4. package/lib/scaffold.js +14 -14
  5. package/openclaw.plugin.json +1 -1
  6. package/package.json +10 -8
  7. package/templates/persona-app/package.json +6 -4
  8. package/templates/persona-app/src/App.css +564 -79
  9. package/templates/persona-app/src/App.tsx +2 -2
  10. package/templates/persona-app/src/main.tsx +13 -0
  11. package/templates/persona-app/src/pages/HomePage.tsx +120 -39
  12. package/templates/persona-app/src/pages/ScenePage.tsx +2 -15
  13. package/templates/persona-app/src/persona/notes.ts +3 -1
  14. package/templates/persona-app/src/spatial.ts +82 -0
  15. package/templates/persona-app/template.json +1 -1
  16. package/templates/persona-app/vendor/webspatial/FORK.md +6 -0
  17. package/templates/persona-app/vendor/webspatial/core-sdk/LICENSE +21 -0
  18. package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.d.ts +906 -0
  19. package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.global.js +75 -0
  20. package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.global.js.map +1 -0
  21. package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.d.ts +906 -0
  22. package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.js +3131 -0
  23. package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.js.map +1 -0
  24. package/templates/persona-app/vendor/webspatial/core-sdk/package.json +45 -0
  25. package/templates/persona-app/vendor/webspatial/react-sdk/LICENSE +21 -0
  26. package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.d.ts +365 -0
  27. package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js +4296 -0
  28. package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js.map +1 -0
  29. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.d.ts +82 -0
  30. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.js +66 -0
  31. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.js.map +1 -0
  32. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.d.ts +2 -0
  33. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.js +18 -0
  34. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.js.map +1 -0
  35. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.d.ts +5 -0
  36. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.js +66 -0
  37. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.js.map +1 -0
  38. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.d.ts +1 -0
  39. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.js +18 -0
  40. package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.js.map +1 -0
  41. package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.d.ts +365 -0
  42. package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js +4336 -0
  43. package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js.map +1 -0
  44. package/templates/persona-app/vendor/webspatial/react-sdk/package.json +94 -0
  45. package/templates/persona-app/vite.config.ts +13 -0
@@ -0,0 +1,4336 @@
1
+
2
+ (function(){
3
+ if(typeof window === 'undefined') return;
4
+ if(!window.__webspatialsdk__) window.__webspatialsdk__ = {}
5
+ window.__webspatialsdk__['react-sdk-version'] = "1.2.1"
6
+ window.__webspatialsdk__['XR_ENV'] = "web"
7
+ })()
8
+
9
+
10
+ // src/spatialized-container/hooks/useDomProxy.ts
11
+ import { useCallback, useEffect, useRef } from "react";
12
+
13
+ // src/spatialized-container/types.ts
14
+ var SpatialCustomStyleVars = {
15
+ back: "--xr-back",
16
+ depth: "--xr-depth",
17
+ backgroundMaterial: "--xr-background-material",
18
+ xrZIndex: "--xr-z-index"
19
+ };
20
+
21
+ // src/spatialized-container/utils.ts
22
+ function getInheritedStyleProps(computedStyle) {
23
+ var propNames = [
24
+ "azimuth",
25
+ "borderCollapse",
26
+ "borderSpacing",
27
+ "captionSide",
28
+ "color",
29
+ "cursor",
30
+ "direction",
31
+ // 'elevation',
32
+ "emptyCells",
33
+ "fontFamily",
34
+ "fontSize",
35
+ "fontStyle",
36
+ "fontVariant",
37
+ "fontWeight",
38
+ "font",
39
+ "letterSpacing",
40
+ "lineHeight",
41
+ "listStyleImage",
42
+ "listStylePosition",
43
+ "listStyleType",
44
+ "listStyle",
45
+ "orphans",
46
+ // 'pitchRange',
47
+ // 'pitch',
48
+ "quotes",
49
+ // 'richness',
50
+ // 'speakHeader',
51
+ // 'speakNumeral',
52
+ // 'speakPunctuation',
53
+ // 'speak',
54
+ // 'speechRate',
55
+ // 'stress',
56
+ "textAlign",
57
+ "textIndent",
58
+ "textTransform",
59
+ "visibility",
60
+ // 'voiceFamily',
61
+ // 'volume',
62
+ "whiteSpace",
63
+ "widows",
64
+ "wordSpacing",
65
+ // background also need to be synced
66
+ "background",
67
+ // position also need to be synced
68
+ "position",
69
+ "width",
70
+ "height",
71
+ "display",
72
+ // content-visibility also need to be synced
73
+ "contentVisibility"
74
+ ];
75
+ var props = {};
76
+ for (var cssName of propNames) {
77
+ if (computedStyle[cssName]) {
78
+ props[cssName] = computedStyle[cssName];
79
+ }
80
+ }
81
+ return props;
82
+ }
83
+ function parseTransformOrigin(computedStyle) {
84
+ const transformOriginProperty = computedStyle.getPropertyValue("transform-origin");
85
+ const [x, y] = transformOriginProperty.split(" ").map(parseFloat);
86
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
87
+ const height = parseFloat(computedStyle.getPropertyValue("height"));
88
+ return {
89
+ x: width > 0 ? x / width : 0.5,
90
+ y: height > 0 ? y / height : 0.5,
91
+ z: 0.5
92
+ };
93
+ }
94
+ function parseBorderRadius(borderProperty, width) {
95
+ if (borderProperty === "") {
96
+ return 0;
97
+ }
98
+ if (borderProperty.endsWith("%")) {
99
+ return width * parseFloat(borderProperty) / 100;
100
+ }
101
+ return parseFloat(borderProperty);
102
+ }
103
+ function parseCornerRadius(computedStyle) {
104
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
105
+ const topLeftPropertyValue = computedStyle.getPropertyValue(
106
+ "border-top-left-radius"
107
+ );
108
+ const topRightPropertyValue = computedStyle.getPropertyValue(
109
+ "border-top-right-radius"
110
+ );
111
+ const bottomLeftPropertyValue = computedStyle.getPropertyValue(
112
+ "border-bottom-left-radius"
113
+ );
114
+ const bottomRightPropertyValue = computedStyle.getPropertyValue(
115
+ "border-bottom-right-radius"
116
+ );
117
+ const cornerRadius = {
118
+ topLeading: parseBorderRadius(topLeftPropertyValue, width),
119
+ bottomLeading: parseBorderRadius(bottomLeftPropertyValue, width),
120
+ topTrailing: parseBorderRadius(topRightPropertyValue, width),
121
+ bottomTrailing: parseBorderRadius(bottomRightPropertyValue, width)
122
+ };
123
+ return cornerRadius;
124
+ }
125
+ function extractAndRemoveCustomProperties(cssText, properties) {
126
+ if (!cssText) {
127
+ return { extractedValues: {}, filteredCssText: "" };
128
+ }
129
+ const extractedValues = {};
130
+ const rules = cssText.split(";");
131
+ const filteredRules = rules.filter((rule) => {
132
+ const [key, value] = rule.split(":").map((part) => part.trim());
133
+ if (properties.includes(key)) {
134
+ extractedValues[key] = value;
135
+ return false;
136
+ }
137
+ return true;
138
+ });
139
+ const filteredCssText = filteredRules.join(";").trim();
140
+ return { extractedValues, filteredCssText };
141
+ }
142
+ function joinToCSSText(cssKV) {
143
+ const rules = Object.entries(cssKV).map(([key, value]) => `${key}: ${value}`);
144
+ return rules.join(";");
145
+ }
146
+
147
+ // src/spatialized-container/hooks/useDomProxy.ts
148
+ function makeOriginalKey(key) {
149
+ return `__original_${key}`;
150
+ }
151
+ var SpatialContainerRefProxy = class {
152
+ transformVisibilityTaskContainerDom = null;
153
+ ref;
154
+ domProxy;
155
+ styleProxy;
156
+ // extre ref props, used to add extra props to ref
157
+ extraRefProps;
158
+ constructor(ref, extraRefProps) {
159
+ this.ref = ref;
160
+ this.extraRefProps = extraRefProps;
161
+ }
162
+ updateStandardSpatializedContainerDom(dom) {
163
+ const self = this;
164
+ if (dom) {
165
+ let cacheExtraRefProps;
166
+ const domProxy = new Proxy(
167
+ dom,
168
+ {
169
+ get(target, prop) {
170
+ if (prop === "__raw") {
171
+ return target;
172
+ }
173
+ if (prop === "clientDepth") {
174
+ return target.style.getPropertyValue(SpatialCustomStyleVars.depth);
175
+ }
176
+ if (prop === "offsetBack") {
177
+ return target.style.getPropertyValue(SpatialCustomStyleVars.back);
178
+ }
179
+ if (prop === "getBoundingClientRect") {
180
+ return dom.__getBoundingClientRect;
181
+ }
182
+ if (prop === "getBoundingClientCube") {
183
+ return dom.__getBoundingClientCube;
184
+ }
185
+ if (prop === "style") {
186
+ if (!self.styleProxy) {
187
+ self.styleProxy = new Proxy(target.style, {
188
+ get(target2, prop2) {
189
+ if (prop2 === "visibility" || prop2 === "transform") {
190
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
191
+ prop2
192
+ );
193
+ }
194
+ const value2 = Reflect.get(target2, prop2);
195
+ if (typeof value2 === "function") {
196
+ if (prop2 === "setProperty" || prop2 === "removeProperty" || prop2 === "getPropertyValue") {
197
+ return function(...args) {
198
+ const validProperties = ["visibility", "transform"];
199
+ const [property] = args;
200
+ if (validProperties.includes(property)) {
201
+ if (prop2 === "setProperty") {
202
+ const [, kValue] = args;
203
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
204
+ property,
205
+ kValue
206
+ );
207
+ } else if (prop2 === "removeProperty") {
208
+ self.transformVisibilityTaskContainerDom?.style.removeProperty(
209
+ property
210
+ );
211
+ } else if (prop2 === "getPropertyValue") {
212
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
213
+ property
214
+ );
215
+ }
216
+ } else {
217
+ return value2.apply(this, args);
218
+ }
219
+ }.bind(target2);
220
+ } else {
221
+ return value2.bind(target2);
222
+ }
223
+ } else {
224
+ return value2;
225
+ }
226
+ },
227
+ set(target2, prop2, value2) {
228
+ if (prop2 === "visibility") {
229
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
230
+ "visibility",
231
+ value2
232
+ );
233
+ return true;
234
+ }
235
+ if (prop2 === "transform") {
236
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
237
+ "transform",
238
+ value2
239
+ );
240
+ return true;
241
+ }
242
+ if (prop2 === SpatialCustomStyleVars.backgroundMaterial) {
243
+ target2.setProperty(
244
+ SpatialCustomStyleVars.backgroundMaterial,
245
+ value2
246
+ );
247
+ } else if (prop2 === SpatialCustomStyleVars.back) {
248
+ target2.setProperty(
249
+ SpatialCustomStyleVars.back,
250
+ value2
251
+ );
252
+ } else if (prop2 === SpatialCustomStyleVars.xrZIndex) {
253
+ target2.setProperty(
254
+ SpatialCustomStyleVars.xrZIndex,
255
+ value2
256
+ );
257
+ } else if (prop2 === SpatialCustomStyleVars.depth) {
258
+ target2.setProperty(
259
+ SpatialCustomStyleVars.depth,
260
+ value2
261
+ );
262
+ } else if (prop2 === "cssText") {
263
+ const toFilteredCSSProperties = [
264
+ "transform",
265
+ "visibility"
266
+ ];
267
+ const { extractedValues, filteredCssText } = extractAndRemoveCustomProperties(
268
+ value2,
269
+ toFilteredCSSProperties
270
+ );
271
+ toFilteredCSSProperties.forEach((key) => {
272
+ if (extractedValues[key]) {
273
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
274
+ key,
275
+ extractedValues[key]
276
+ );
277
+ } else {
278
+ target2.removeProperty(key);
279
+ }
280
+ });
281
+ const appendedCSSText = joinToCSSText({
282
+ transform: "none",
283
+ visibility: "hidden"
284
+ });
285
+ return Reflect.set(
286
+ target2,
287
+ prop2,
288
+ [appendedCSSText, filteredCssText].join(";")
289
+ );
290
+ }
291
+ return Reflect.set(target2, prop2, value2);
292
+ }
293
+ });
294
+ }
295
+ return self.styleProxy;
296
+ }
297
+ if (typeof prop === "string" && self.extraRefProps) {
298
+ if (!cacheExtraRefProps) {
299
+ cacheExtraRefProps = self.extraRefProps(domProxy);
300
+ }
301
+ const extraProps = cacheExtraRefProps;
302
+ if (extraProps.hasOwnProperty(prop)) {
303
+ return extraProps[prop];
304
+ }
305
+ }
306
+ const value = Reflect.get(target, prop);
307
+ if (typeof value === "function") {
308
+ if ("removeAttribute" === prop) {
309
+ return function(...args) {
310
+ const [property] = args;
311
+ if (property === "style") {
312
+ dom.style.cssText = "visibility: hidden; transition: none; transform: none;";
313
+ if (self.transformVisibilityTaskContainerDom) {
314
+ self.transformVisibilityTaskContainerDom.style.visibility = "";
315
+ self.transformVisibilityTaskContainerDom.style.transform = "";
316
+ }
317
+ return true;
318
+ }
319
+ if (property === "class") {
320
+ domProxy.className = "xr-spatial-default";
321
+ return true;
322
+ }
323
+ };
324
+ }
325
+ return value.bind(target);
326
+ }
327
+ return value;
328
+ },
329
+ set(target, prop, value) {
330
+ if (prop === "className") {
331
+ if (value && value.indexOf("xr-spatial-default") === -1) {
332
+ value = value + " xr-spatial-default";
333
+ }
334
+ if (self.transformVisibilityTaskContainerDom) {
335
+ self.transformVisibilityTaskContainerDom.className = value;
336
+ }
337
+ }
338
+ if (typeof prop === "string" && self.extraRefProps) {
339
+ if (!cacheExtraRefProps) {
340
+ cacheExtraRefProps = self.extraRefProps(domProxy);
341
+ }
342
+ cacheExtraRefProps[prop] = value;
343
+ }
344
+ return Reflect.set(target, prop, value);
345
+ }
346
+ }
347
+ );
348
+ this.domProxy = domProxy;
349
+ const domClassList = dom.classList;
350
+ const domClassMethodKeys = ["add", "remove", "toggle", "replace"];
351
+ domClassMethodKeys.forEach((key) => {
352
+ const hiddenKey = makeOriginalKey(key);
353
+ const hiddenKeyExist = domClassList[hiddenKey] !== void 0;
354
+ const originalMethod = hiddenKeyExist ? domClassList[hiddenKey] : domClassList[key].bind(domClassList);
355
+ domClassList[hiddenKey] = originalMethod;
356
+ domClassList[key] = function(...args) {
357
+ const result = originalMethod(...args);
358
+ if (self.transformVisibilityTaskContainerDom) {
359
+ self.transformVisibilityTaskContainerDom.className = dom.className;
360
+ }
361
+ return result;
362
+ };
363
+ });
364
+ this.styleProxy = void 0;
365
+ this.updateDomProxyToRef();
366
+ Object.assign(dom, {
367
+ __targetProxy: domProxy
368
+ });
369
+ }
370
+ }
371
+ updateTransformVisibilityTaskContainerDom(dom) {
372
+ this.transformVisibilityTaskContainerDom = dom;
373
+ this.updateDomProxyToRef();
374
+ }
375
+ updateDomProxyToRef() {
376
+ const ref = this.ref;
377
+ if (!ref) {
378
+ return;
379
+ }
380
+ if (this.domProxy && this.transformVisibilityTaskContainerDom) {
381
+ if (typeof ref === "function") {
382
+ ref(this.domProxy);
383
+ } else {
384
+ ref.current = this.domProxy;
385
+ }
386
+ } else {
387
+ if (typeof ref === "function") {
388
+ ref(null);
389
+ } else {
390
+ ref.current = null;
391
+ }
392
+ }
393
+ }
394
+ updateRef(ref) {
395
+ this.ref = ref;
396
+ }
397
+ };
398
+ function hijackGetComputedStyle() {
399
+ const rawFn = window.getComputedStyle.bind(window);
400
+ window.getComputedStyle = (element, pseudoElt) => {
401
+ const dom = element.__raw;
402
+ if (dom) {
403
+ return rawFn(dom, pseudoElt);
404
+ }
405
+ return rawFn(element, pseudoElt);
406
+ };
407
+ }
408
+ function useDomProxy(ref, extraRefProps) {
409
+ const spatialContainerRefProxy = useRef(
410
+ new SpatialContainerRefProxy(ref, extraRefProps)
411
+ );
412
+ useEffect(() => {
413
+ spatialContainerRefProxy.current.updateRef(ref);
414
+ }, [ref]);
415
+ const transformVisibilityTaskContainerCallback = useCallback(
416
+ (el) => {
417
+ spatialContainerRefProxy.current.updateTransformVisibilityTaskContainerDom(
418
+ el
419
+ );
420
+ },
421
+ []
422
+ );
423
+ const standardSpatializedContainerCallback = useCallback(
424
+ (el) => {
425
+ spatialContainerRefProxy.current.updateStandardSpatializedContainerDom(el);
426
+ },
427
+ []
428
+ );
429
+ return {
430
+ transformVisibilityTaskContainerCallback,
431
+ standardSpatializedContainerCallback,
432
+ spatialContainerRefProxy
433
+ };
434
+ }
435
+
436
+ // src/spatialized-container/hooks/use2DFrameDetector.ts
437
+ import {
438
+ useContext,
439
+ useLayoutEffect,
440
+ useEffect as useEffect2,
441
+ useCallback as useCallback2
442
+ } from "react";
443
+
444
+ // src/spatialized-container/context/SpatializedContainerContext.ts
445
+ import { createContext } from "react";
446
+
447
+ // src/spatialized-container/SpatialID.ts
448
+ var SpatialID = "data-spatial-id";
449
+
450
+ // src/spatialized-container/context/SpatializedContainerContext.ts
451
+ var SpatializedContainerObject = class {
452
+ dom = null;
453
+ domSpatialId = null;
454
+ fns = {};
455
+ // cache dom for each spatialId
456
+ spatialId2dom = {};
457
+ spatialId2parentSpatialDom = {};
458
+ // layer : [standardInstance sequence, portalInstance sequence]
459
+ layerSequences = {};
460
+ notify2DFramePlaceHolderChange(dom) {
461
+ this.dom = dom;
462
+ this.domSpatialId = dom.getAttribute(SpatialID);
463
+ Object.values(this.fns).forEach((fn) => fn());
464
+ }
465
+ spatialId2transformVisibility = {};
466
+ updateSpatialTransformVisibility(spatialId, spatialTransformVisibility) {
467
+ this.spatialId2transformVisibility[spatialId] = spatialTransformVisibility;
468
+ this.fnsForSpatialTransformVisibility[spatialId]?.forEach(
469
+ (fn) => fn(spatialTransformVisibility)
470
+ );
471
+ }
472
+ // this is used by onSpatialEvent.currentTarget property
473
+ spatialId2ContainerRefProxy = {};
474
+ // this is called in sub standardInstance env
475
+ updateSpatialContainerRefProxyInfo(spatialId, spatialContainerRefProxy) {
476
+ this.spatialId2ContainerRefProxy[spatialId] = spatialContainerRefProxy;
477
+ }
478
+ getSpatialContainerRefProxyBySpatialId(spatialId) {
479
+ return this.spatialId2ContainerRefProxy[spatialId];
480
+ }
481
+ // notify when TransformVisibilityTaskContainer data change
482
+ fnsForSpatialTransformVisibility = {};
483
+ // used by StandardSpatializedContainer and PortalSpatializedContainer
484
+ onSpatialTransformVisibilityChange(spatialId, fn) {
485
+ if (!this.fnsForSpatialTransformVisibility[spatialId]) {
486
+ this.fnsForSpatialTransformVisibility[spatialId] = [];
487
+ }
488
+ this.fnsForSpatialTransformVisibility[spatialId].push(fn);
489
+ if (this.spatialId2transformVisibility[spatialId]) {
490
+ fn(this.spatialId2transformVisibility[spatialId]);
491
+ }
492
+ }
493
+ offSpatialTransformVisibilityChange(spatialId, fn) {
494
+ const fns = this.fnsForSpatialTransformVisibility[spatialId];
495
+ if (fns) {
496
+ this.fnsForSpatialTransformVisibility[spatialId] = fns.filter(
497
+ (f) => f !== fn
498
+ );
499
+ }
500
+ }
501
+ on2DFrameChange(spatialId, fn) {
502
+ this.fns[spatialId] = fn;
503
+ if (this.dom) {
504
+ fn();
505
+ }
506
+ }
507
+ off2DFrameChange(spatialId) {
508
+ delete this.fns[spatialId];
509
+ delete this.spatialId2dom[spatialId];
510
+ delete this.spatialId2parentSpatialDom[spatialId];
511
+ }
512
+ querySpatialDomBySpatialId(spatialId) {
513
+ if (this.domSpatialId === spatialId) {
514
+ return this.dom;
515
+ }
516
+ if (!this.dom) {
517
+ return null;
518
+ }
519
+ if (!this.spatialId2dom[spatialId]) {
520
+ const spatialDom = this.dom.querySelector(`[${SpatialID}="${spatialId}"]`);
521
+ if (spatialDom) {
522
+ this.spatialId2dom[spatialId] = spatialDom;
523
+ }
524
+ }
525
+ return this.spatialId2dom[spatialId];
526
+ }
527
+ queryParentSpatialDomBySpatialId(spatialId) {
528
+ if (this.domSpatialId === spatialId) {
529
+ return null;
530
+ }
531
+ if (this.spatialId2parentSpatialDom[spatialId]) {
532
+ return this.spatialId2parentSpatialDom[spatialId];
533
+ }
534
+ let spatialDom = this.querySpatialDomBySpatialId(spatialId);
535
+ if (spatialDom) {
536
+ if (spatialDom === this.dom) return null;
537
+ let parentSpatialDom = spatialDom.parentElement;
538
+ while (parentSpatialDom && spatialDom !== this.dom) {
539
+ if (parentSpatialDom.hasAttribute(SpatialID)) {
540
+ break;
541
+ } else {
542
+ parentSpatialDom = parentSpatialDom.parentElement;
543
+ }
544
+ }
545
+ this.spatialId2parentSpatialDom[spatialId] = parentSpatialDom;
546
+ return parentSpatialDom;
547
+ }
548
+ return null;
549
+ }
550
+ getSpatialId(layer, isInStandardInstance, name = "") {
551
+ if (this.layerSequences[layer] === void 0) {
552
+ this.layerSequences[layer] = [0, 0];
553
+ }
554
+ const idx = isInStandardInstance ? 0 : 1;
555
+ const sequenceId = this.layerSequences[layer][idx];
556
+ this.layerSequences[layer][idx] = sequenceId + 1;
557
+ const spatialId = `${name}_${layer}_${sequenceId}`;
558
+ return spatialId;
559
+ }
560
+ };
561
+ var SpatializedContainerContext = createContext(null);
562
+
563
+ // src/spatialized-container/hooks/use2DFrameDetector.ts
564
+ function use2DFrameDetector(ref) {
565
+ const spatializedContainerObject = useContext(
566
+ SpatializedContainerContext
567
+ );
568
+ const notify2DFrameChange = useCallback2(() => {
569
+ ref.current && spatializedContainerObject.notify2DFramePlaceHolderChange(ref.current);
570
+ }, [ref.current, spatializedContainerObject]);
571
+ useLayoutEffect(notify2DFrameChange, [notify2DFrameChange]);
572
+ useEffect2(() => {
573
+ if (!ref.current || !spatializedContainerObject) {
574
+ console.warn(
575
+ "Ref is not attached to the DOM or spatializedContainerObject is not available"
576
+ );
577
+ return;
578
+ }
579
+ window.addEventListener("resize", notify2DFrameChange);
580
+ return () => {
581
+ window.removeEventListener("resize", notify2DFrameChange);
582
+ };
583
+ }, []);
584
+ useEffect2(() => {
585
+ if (!ref.current) {
586
+ console.warn("Ref is not attached to the DOM");
587
+ return;
588
+ }
589
+ const ro = new ResizeObserver(notify2DFrameChange);
590
+ ro.observe(ref.current);
591
+ return () => {
592
+ ro.disconnect();
593
+ };
594
+ }, []);
595
+ useEffect2(() => {
596
+ if (!ref.current) {
597
+ console.warn("Ref is not attached to the DOM");
598
+ return;
599
+ }
600
+ const ro = new MutationObserver(notify2DFrameChange);
601
+ ro.observe(ref.current, {
602
+ attributeFilter: ["class", "style"],
603
+ subtree: true
604
+ });
605
+ return () => {
606
+ ro.disconnect();
607
+ };
608
+ }, []);
609
+ }
610
+
611
+ // src/spatialized-container/StandardSpatializedContainer.tsx
612
+ import {
613
+ forwardRef,
614
+ useCallback as useCallback3,
615
+ useContext as useContext2,
616
+ useEffect as useEffect3,
617
+ useRef as useRef2,
618
+ useState
619
+ } from "react";
620
+ import { jsx } from "react/jsx-runtime";
621
+ function useSpatialTransformVisibilityWatcher(spatialId) {
622
+ const [transformExist, setTransformExist] = useState(false);
623
+ const spatializedContainerObject = useContext2(SpatializedContainerContext);
624
+ useEffect3(() => {
625
+ const fn = (spatialTransform) => {
626
+ setTransformExist(spatialTransform.transform !== "none");
627
+ };
628
+ spatializedContainerObject.onSpatialTransformVisibilityChange(spatialId, fn);
629
+ return () => {
630
+ spatializedContainerObject.offSpatialTransformVisibilityChange(
631
+ spatialId,
632
+ fn
633
+ );
634
+ };
635
+ }, [spatialId, spatializedContainerObject]);
636
+ return transformExist;
637
+ }
638
+ function useInternalRef(ref) {
639
+ const refInternal = useRef2(null);
640
+ const refInternalCallback = useCallback3(
641
+ (node) => {
642
+ refInternal.current = node;
643
+ if (typeof ref === "function") {
644
+ ref(node);
645
+ } else if (ref) {
646
+ ref.current = node;
647
+ }
648
+ },
649
+ [ref]
650
+ );
651
+ return { refInternal, refInternalCallback };
652
+ }
653
+ function StandardSpatializedContainerBase(props, ref) {
654
+ const {
655
+ component: Component,
656
+ style: inStyle = {},
657
+ className,
658
+ inStandardSpatializedContainer = false,
659
+ ...restProps
660
+ } = props;
661
+ const { refInternal, refInternalCallback } = useInternalRef(ref);
662
+ if (!inStandardSpatializedContainer) {
663
+ use2DFrameDetector(refInternal);
664
+ }
665
+ const transformExist = useSpatialTransformVisibilityWatcher(props[SpatialID]);
666
+ const extraStyle = {
667
+ visibility: "hidden",
668
+ transition: "none",
669
+ transform: transformExist ? "translateZ(0)" : "none"
670
+ };
671
+ const style = { ...inStyle, ...extraStyle };
672
+ const classNames = className ? `${className} xr-spatial-default` : "xr-spatial-default";
673
+ return /* @__PURE__ */ jsx(
674
+ Component,
675
+ {
676
+ ref: refInternalCallback,
677
+ style,
678
+ className: classNames,
679
+ ...restProps
680
+ }
681
+ );
682
+ }
683
+ var StandardSpatializedContainer = forwardRef(
684
+ StandardSpatializedContainerBase
685
+ );
686
+ function injectSpatialDefaultStyle() {
687
+ const styleElement = document.createElement("style");
688
+ styleElement.type = "text/css";
689
+ styleElement.innerHTML = " :where(.xr-spatial-default) { --xr-back: 0; --xr-depth: 0; --xr-z-index: 0; --xr-background-material: none; } ";
690
+ document.head.appendChild(styleElement);
691
+ }
692
+
693
+ // src/spatialized-container/TransformVisibilityTaskContainer.tsx
694
+ import {
695
+ forwardRef as forwardRef2,
696
+ useCallback as useCallback5,
697
+ useRef as useRef3
698
+ } from "react";
699
+ import { createPortal } from "react-dom";
700
+
701
+ // src/spatialized-container/hooks/useSpatialTransformVisibility.ts
702
+ import { useCallback as useCallback4, useContext as useContext3, useEffect as useEffect4 } from "react";
703
+
704
+ // src/notifyUpdateStandInstanceLayout.ts
705
+ function notifyUpdateStandInstanceLayout() {
706
+ document.dispatchEvent(
707
+ new CustomEvent("standInstanceLayout" /* standInstanceLayout */, {
708
+ detail: {}
709
+ })
710
+ );
711
+ }
712
+ function notifyDOMUpdate(mutationsList) {
713
+ document.dispatchEvent(
714
+ new CustomEvent("domUpdated" /* domUpdated */, {
715
+ detail: mutationsList
716
+ })
717
+ );
718
+ }
719
+
720
+ // src/spatialized-container/hooks/useSpatialTransformVisibility.ts
721
+ function parseTransformAndVisibilityProperties(node) {
722
+ const computedStyle = getComputedStyle(node);
723
+ const transform = computedStyle.getPropertyValue("transform");
724
+ const visibility = computedStyle.getPropertyValue("visibility");
725
+ return {
726
+ visibility,
727
+ transform
728
+ };
729
+ }
730
+ function useSpatialTransformVisibility(spatialId, ref) {
731
+ const spatializedContainerObject = useContext3(SpatializedContainerContext);
732
+ const checkSpatialStyleUpdate = useCallback4(() => {
733
+ if (!ref.current) {
734
+ return;
735
+ }
736
+ const spatialTransformVisibility = parseTransformAndVisibilityProperties(
737
+ ref.current
738
+ );
739
+ spatializedContainerObject.updateSpatialTransformVisibility(
740
+ spatialId,
741
+ spatialTransformVisibility
742
+ );
743
+ }, []);
744
+ useEffect4(() => {
745
+ checkSpatialStyleUpdate();
746
+ }, [checkSpatialStyleUpdate]);
747
+ useEffect4(() => {
748
+ const observer = new MutationObserver((mutationsList) => {
749
+ checkSpatialStyleUpdate();
750
+ });
751
+ const config = {
752
+ childList: false,
753
+ subtree: false,
754
+ attributes: true,
755
+ // attributeOldValue: true,
756
+ attributeFilter: ["style", "class"]
757
+ };
758
+ observer.observe(ref.current, config);
759
+ return () => {
760
+ observer.disconnect();
761
+ };
762
+ }, []);
763
+ useEffect4(() => {
764
+ const headObserver = new MutationObserver((mutations) => {
765
+ checkSpatialStyleUpdate();
766
+ });
767
+ headObserver.observe(document.head, { childList: true, subtree: true });
768
+ return () => {
769
+ headObserver.disconnect();
770
+ };
771
+ }, []);
772
+ useEffect4(() => {
773
+ const onDomUpdated = (event) => {
774
+ checkSpatialStyleUpdate();
775
+ };
776
+ document.addEventListener(
777
+ "domUpdated" /* domUpdated */,
778
+ onDomUpdated
779
+ );
780
+ return () => {
781
+ document.removeEventListener(
782
+ "domUpdated" /* domUpdated */,
783
+ onDomUpdated
784
+ );
785
+ };
786
+ }, []);
787
+ }
788
+
789
+ // src/spatialized-container/TransformVisibilityTaskContainer.tsx
790
+ import { jsx as jsx2 } from "react/jsx-runtime";
791
+ var cssParserDivContainer = null;
792
+ function initCSSParserDivContainer() {
793
+ cssParserDivContainer = document?.createElement("div");
794
+ if (cssParserDivContainer) {
795
+ cssParserDivContainer.style.position = "absolute";
796
+ cssParserDivContainer.setAttribute("data-id", "css-parser-div-container");
797
+ }
798
+ }
799
+ function createOrGetCSSParserDivContainer() {
800
+ if (cssParserDivContainer && !cssParserDivContainer.parentElement) {
801
+ document?.body.appendChild(cssParserDivContainer);
802
+ }
803
+ return cssParserDivContainer;
804
+ }
805
+ function useInternalRef2(ref) {
806
+ const refInternal = useRef3(null);
807
+ const refInternalCallback = useCallback5(
808
+ (node) => {
809
+ refInternal.current = node;
810
+ if (typeof ref === "function") {
811
+ ref(node);
812
+ } else if (ref) {
813
+ ref.current = node;
814
+ }
815
+ },
816
+ [ref]
817
+ );
818
+ return { refInternal, refInternalCallback };
819
+ }
820
+ function TransformVisibilityTaskContainerBase(props, ref) {
821
+ const { style: inStyle, ...restProps } = props;
822
+ const extraStyle = {
823
+ // when width/height equal to zero, transform: translateX(-50%) won't work
824
+ // to make sure the element is not visible, we set left/top to a very large negative value
825
+ left: -1e4,
826
+ top: -1e4,
827
+ pointerEvents: "none",
828
+ opacity: 0,
829
+ // width: 0,
830
+ // height: 0,
831
+ padding: 0,
832
+ transition: "none",
833
+ position: "absolute"
834
+ };
835
+ const { refInternal, refInternalCallback } = useInternalRef2(ref);
836
+ const style = { ...inStyle, ...extraStyle };
837
+ useSpatialTransformVisibility(props[SpatialID], refInternal);
838
+ const cssParserDivContainer2 = createOrGetCSSParserDivContainer();
839
+ if (!cssParserDivContainer2) {
840
+ return null;
841
+ }
842
+ return createPortal(
843
+ /* @__PURE__ */ jsx2("div", { ref: refInternalCallback, style, ...restProps }),
844
+ cssParserDivContainer2
845
+ );
846
+ }
847
+ var TransformVisibilityTaskContainer = forwardRef2(
848
+ TransformVisibilityTaskContainerBase
849
+ );
850
+
851
+ // src/spatialized-container/SpatializedContainer.tsx
852
+ import { forwardRef as forwardRef4, useContext as useContext7, useEffect as useEffect10, useMemo as useMemo2 } from "react";
853
+
854
+ // src/noRuntime.ts
855
+ var Spatial = class {
856
+ /**
857
+ * Requests a session object from the browser
858
+ * @returns The session or null if not availible in the current browser
859
+ * [TODO] discuss implications of this not being async
860
+ */
861
+ requestSession() {
862
+ return null;
863
+ }
864
+ /**
865
+ * Checks if the current page is running in a spatial web environment.
866
+ * This method detects if the application is running in a WebSpatial-compatible browser.
867
+ * @returns True if running in a spatial web environment, false otherwise
868
+ */
869
+ runInSpatialWeb() {
870
+ return false;
871
+ }
872
+ /**
873
+ * @returns true if web spatial is supported by this webpage
874
+ */
875
+ isSupported() {
876
+ return false;
877
+ }
878
+ /**
879
+ * Gets the native version, format is "x.x.x"
880
+ * @returns native version string
881
+ */
882
+ getNativeVersion() {
883
+ return null;
884
+ }
885
+ /**
886
+ * Gets the client version, format is "x.x.x"
887
+ * @returns client version string
888
+ */
889
+ getClientVersion() {
890
+ return null;
891
+ }
892
+ };
893
+ function isSSREnv() {
894
+ return false;
895
+ }
896
+
897
+ // src/utils/getSession.ts
898
+ var spatial = null;
899
+ var _currentSession = null;
900
+ function getSession() {
901
+ if (isSSREnv()) return null;
902
+ if (!spatial) {
903
+ spatial = new Spatial();
904
+ }
905
+ if (!spatial.isSupported()) {
906
+ return null;
907
+ }
908
+ if (_currentSession) {
909
+ return _currentSession;
910
+ }
911
+ _currentSession = spatial.requestSession();
912
+ return _currentSession;
913
+ }
914
+
915
+ // src/spatialized-container/context/SpatialLayerContext.ts
916
+ import { createContext as createContext2 } from "react";
917
+ var SpatialLayerContext = createContext2(0);
918
+
919
+ // src/spatialized-container/PortalSpatializedContainer.tsx
920
+ import { useMemo, useContext as useContext4, useEffect as useEffect7 } from "react";
921
+
922
+ // src/spatialized-container/context/PortalInstanceContext.ts
923
+ import { createContext as createContext3 } from "react";
924
+
925
+ // src/utils/debugTool.ts
926
+ async function inspectCurrentSpatialScene() {
927
+ const spatialScene = getSession().getSpatialScene();
928
+ return spatialScene.inspect();
929
+ }
930
+ function getSpatialized2DElement(spatialized2DElement) {
931
+ return spatialized2DElement.__innerSpatializedElement?.();
932
+ }
933
+ function enableDebugTool() {
934
+ if (isSSREnv()) return;
935
+ Object.assign(window, {
936
+ inspectCurrentSpatialScene,
937
+ getSpatialized2DElement
938
+ });
939
+ }
940
+
941
+ // src/utils/androidBitmapCapture.ts
942
+ var snapdomModule = null;
943
+ var snapdomChecked = false;
944
+ var snapdomAvailable = false;
945
+ var html2canvasModule = null;
946
+ var html2canvasChecked = false;
947
+ var html2canvasAvailable = false;
948
+ async function loadSnapdom() {
949
+ if (snapdomModule) return snapdomModule;
950
+ if (snapdomChecked && !snapdomAvailable) return null;
951
+ console.log("[WebSpatial] Checking for snapdom...", {
952
+ windowExists: typeof window !== "undefined",
953
+ snapdomOnWindow: typeof window?.snapdom,
954
+ html2canvasOnWindow: typeof window?.html2canvas
955
+ });
956
+ for (let attempt = 0; attempt < 3; attempt++) {
957
+ if (typeof window !== "undefined" && window.snapdom) {
958
+ snapdomModule = window.snapdom;
959
+ snapdomChecked = true;
960
+ snapdomAvailable = true;
961
+ console.log("[WebSpatial] Using globally provided snapdom (fast mode)");
962
+ return snapdomModule;
963
+ }
964
+ if (attempt < 2) {
965
+ console.log(`[WebSpatial] snapdom not on window, retry ${attempt + 1}/3...`);
966
+ await new Promise((resolve) => setTimeout(resolve, 100));
967
+ }
968
+ }
969
+ try {
970
+ console.log("[WebSpatial] Trying dynamic import of @zumer/snapdom...");
971
+ const moduleName = "@zumer/snapdom";
972
+ const dynamicImport = new Function("moduleName", "return import(moduleName)");
973
+ const module = await dynamicImport(moduleName);
974
+ snapdomModule = module.snapdom || module.default || module;
975
+ snapdomChecked = true;
976
+ snapdomAvailable = true;
977
+ console.log("[WebSpatial] Loaded snapdom via dynamic import (fast mode)");
978
+ return snapdomModule;
979
+ } catch (error) {
980
+ snapdomChecked = true;
981
+ snapdomAvailable = false;
982
+ console.log("[WebSpatial] snapdom not available:", error.message);
983
+ console.log("[WebSpatial] Falling back to html2canvas");
984
+ return null;
985
+ }
986
+ }
987
+ async function loadHtml2Canvas() {
988
+ if (html2canvasModule) return html2canvasModule;
989
+ if (html2canvasChecked && !html2canvasAvailable) return null;
990
+ if (typeof window !== "undefined" && window.html2canvas) {
991
+ html2canvasModule = window.html2canvas;
992
+ html2canvasChecked = true;
993
+ html2canvasAvailable = true;
994
+ console.log("[WebSpatial] Using globally provided html2canvas (fallback mode)");
995
+ return html2canvasModule;
996
+ }
997
+ try {
998
+ const moduleName = "html2canvas";
999
+ const dynamicImport = new Function("moduleName", "return import(moduleName)");
1000
+ const module = await dynamicImport(moduleName);
1001
+ html2canvasModule = module.default || module;
1002
+ html2canvasChecked = true;
1003
+ html2canvasAvailable = true;
1004
+ console.log("[WebSpatial] Loaded html2canvas via dynamic import (fallback mode)");
1005
+ return html2canvasModule;
1006
+ } catch (error) {
1007
+ html2canvasChecked = true;
1008
+ html2canvasAvailable = false;
1009
+ console.warn(
1010
+ "[WebSpatial] Neither snapdom nor html2canvas available. Bitmap capture for Android XR is disabled. Install @zumer/snapdom (recommended) or html2canvas."
1011
+ );
1012
+ return null;
1013
+ }
1014
+ }
1015
+ function isAndroidPlatform() {
1016
+ if (typeof window === "undefined") return false;
1017
+ const ua = window.navigator.userAgent;
1018
+ const hasWebSpatialBridge = typeof window.webspatialBridge !== "undefined";
1019
+ return hasWebSpatialBridge && (ua.includes("Android") || ua.includes("Linux"));
1020
+ }
1021
+ function getAndroidRenderMode() {
1022
+ if (typeof window === "undefined") {
1023
+ return null;
1024
+ }
1025
+ const bridgeMode = window.webspatialBridge?.getRenderMode?.();
1026
+ if (bridgeMode === "live-window" || bridgeMode === "bitmap-capture") {
1027
+ return bridgeMode;
1028
+ }
1029
+ const configuredMode = window.__WebSpatialAndroidConfig?.renderMode;
1030
+ if (configuredMode === "live-window" || configuredMode === "bitmap-capture") {
1031
+ return configuredMode;
1032
+ }
1033
+ if (!isAndroidPlatform()) {
1034
+ return null;
1035
+ }
1036
+ return "bitmap-capture";
1037
+ }
1038
+ function supportsAndroidLiveWindowProxy() {
1039
+ return getAndroidRenderMode() === "live-window";
1040
+ }
1041
+ function usesAndroidBitmapCapture() {
1042
+ return isAndroidPlatform() && !supportsAndroidLiveWindowProxy();
1043
+ }
1044
+ var DEFAULT_CAPTURE_BACKGROUND = "#1a1a2e";
1045
+ function hasTransparentBackground(element) {
1046
+ const style = window.getComputedStyle(element);
1047
+ const bg = style.backgroundColor;
1048
+ const bgImage = style.backgroundImage;
1049
+ if (bg === "transparent" || bg === "rgba(0, 0, 0, 0)" || bg === "" || bg === "initial") {
1050
+ if (bgImage === "none" || bgImage === "" || bgImage === "initial") {
1051
+ return true;
1052
+ }
1053
+ }
1054
+ return false;
1055
+ }
1056
+ function injectCaptureBackground(element, backgroundColor = DEFAULT_CAPTURE_BACKGROUND) {
1057
+ const restoreFunctions = [];
1058
+ const originalBg = element.style.backgroundColor;
1059
+ const wasTransparent = hasTransparentBackground(element);
1060
+ element.style.backgroundColor = backgroundColor;
1061
+ restoreFunctions.push(() => {
1062
+ element.style.backgroundColor = originalBg;
1063
+ });
1064
+ const allDescendants = element.querySelectorAll("*");
1065
+ let injectedCount = 0;
1066
+ allDescendants.forEach((el) => {
1067
+ const htmlEl = el;
1068
+ if (hasTransparentBackground(htmlEl)) {
1069
+ const childOriginalBg = htmlEl.style.backgroundColor;
1070
+ htmlEl.style.backgroundColor = backgroundColor;
1071
+ injectedCount++;
1072
+ restoreFunctions.push(() => {
1073
+ htmlEl.style.backgroundColor = childOriginalBg;
1074
+ });
1075
+ }
1076
+ });
1077
+ console.log(
1078
+ `[WebSpatial] Injected background ${backgroundColor} for capture (parent transparent: ${wasTransparent}, ${injectedCount} children)`
1079
+ );
1080
+ return () => {
1081
+ restoreFunctions.forEach((restore) => restore());
1082
+ };
1083
+ }
1084
+ var initialRenderDelayApplied = false;
1085
+ async function waitForContent(element, imageTimeoutMs = 2e3) {
1086
+ if (!initialRenderDelayApplied) {
1087
+ initialRenderDelayApplied = true;
1088
+ console.log("[WebSpatial] Applying initial render delay (1500ms) for first capture");
1089
+ await new Promise((resolve) => setTimeout(resolve, 1500));
1090
+ }
1091
+ try {
1092
+ await Promise.race([
1093
+ document.fonts.ready,
1094
+ new Promise((resolve) => setTimeout(resolve, 500))
1095
+ ]);
1096
+ } catch {
1097
+ }
1098
+ const images = element.querySelectorAll("img");
1099
+ console.log(`[WebSpatial] Found ${images.length} images in element`);
1100
+ if (images.length > 0) {
1101
+ Array.from(images).forEach((img, i) => {
1102
+ const src = img.src?.substring(0, 80) || "no-src";
1103
+ console.log(
1104
+ `[WebSpatial] Image ${i}: complete=${img.complete}, naturalWidth=${img.naturalWidth}, src=${src}...`
1105
+ );
1106
+ });
1107
+ const incompleteImages = Array.from(images).filter((img) => !img.complete);
1108
+ if (incompleteImages.length > 0) {
1109
+ console.log(`[WebSpatial] Waiting for ${incompleteImages.length} images to load (timeout: ${imageTimeoutMs}ms)`);
1110
+ const imagePromises = incompleteImages.map((img) => {
1111
+ return new Promise((resolve) => {
1112
+ const handler = () => resolve();
1113
+ img.addEventListener("load", handler, { once: true });
1114
+ img.addEventListener("error", handler, { once: true });
1115
+ });
1116
+ });
1117
+ await Promise.race([
1118
+ Promise.all(imagePromises),
1119
+ new Promise((resolve) => setTimeout(resolve, imageTimeoutMs))
1120
+ ]);
1121
+ const stillIncomplete = incompleteImages.filter((img) => !img.complete).length;
1122
+ console.log(`[WebSpatial] Image wait complete. ${stillIncomplete} images still loading.`);
1123
+ } else {
1124
+ console.log(`[WebSpatial] All ${images.length} images already complete`);
1125
+ }
1126
+ }
1127
+ const textContent = element.innerText?.trim() || "";
1128
+ if (textContent.length < 100) {
1129
+ console.log(`[WebSpatial] Element has minimal content (${textContent.length} chars), waiting 500ms more`);
1130
+ await new Promise((resolve) => setTimeout(resolve, 500));
1131
+ }
1132
+ }
1133
+ var MAX_BITMAP_DIMENSION = 2048;
1134
+ function resizeCanvasIfNeeded(canvas) {
1135
+ const { width, height } = canvas;
1136
+ if (width <= MAX_BITMAP_DIMENSION && height <= MAX_BITMAP_DIMENSION) {
1137
+ return canvas;
1138
+ }
1139
+ const scaleFactor = Math.min(
1140
+ MAX_BITMAP_DIMENSION / width,
1141
+ MAX_BITMAP_DIMENSION / height
1142
+ );
1143
+ const newWidth = Math.round(width * scaleFactor);
1144
+ const newHeight = Math.round(height * scaleFactor);
1145
+ console.log(
1146
+ `[WebSpatial] Resizing bitmap from ${width}x${height} to ${newWidth}x${newHeight}`
1147
+ );
1148
+ const resizedCanvas = document.createElement("canvas");
1149
+ resizedCanvas.width = newWidth;
1150
+ resizedCanvas.height = newHeight;
1151
+ const ctx = resizedCanvas.getContext("2d");
1152
+ if (ctx) {
1153
+ ctx.imageSmoothingEnabled = true;
1154
+ ctx.imageSmoothingQuality = "high";
1155
+ ctx.drawImage(canvas, 0, 0, newWidth, newHeight);
1156
+ }
1157
+ return resizedCanvas;
1158
+ }
1159
+ async function captureWithSnapdom(snapdom, element, scale) {
1160
+ try {
1161
+ const cappedScale = Math.min(scale, 1.5);
1162
+ const rect = element.getBoundingClientRect();
1163
+ console.log(`[WebSpatial] snapdom capturing: rect=(${rect.x.toFixed(0)},${rect.y.toFixed(0)},${rect.width.toFixed(0)},${rect.height.toFixed(0)}), scale=${cappedScale}`);
1164
+ const result = await snapdom(element, {
1165
+ scale: cappedScale,
1166
+ embedFonts: false
1167
+ });
1168
+ let canvas = await result.toCanvas();
1169
+ canvas = resizeCanvasIfNeeded(canvas);
1170
+ const dataUrl = canvas.toDataURL("image/webp", 0.85);
1171
+ return dataUrl;
1172
+ } catch (error) {
1173
+ console.error("[WebSpatial] snapdom capture failed:", error);
1174
+ return null;
1175
+ }
1176
+ }
1177
+ function findOffsetElements(element) {
1178
+ const offsetElements = [];
1179
+ const style = window.getComputedStyle(element);
1180
+ if (style.position === "relative") {
1181
+ const top = parseFloat(style.top) || 0;
1182
+ const left = parseFloat(style.left) || 0;
1183
+ if (top !== 0 || left !== 0) {
1184
+ offsetElements.push({
1185
+ element,
1186
+ originalTop: element.style.top,
1187
+ originalLeft: element.style.left,
1188
+ topValue: top,
1189
+ leftValue: left
1190
+ });
1191
+ }
1192
+ }
1193
+ let parent = element.parentElement;
1194
+ let depth = 0;
1195
+ const maxDepth = 5;
1196
+ while (parent && depth < maxDepth) {
1197
+ const parentStyle = window.getComputedStyle(parent);
1198
+ if (parentStyle.position === "relative") {
1199
+ const parentTop = parseFloat(parentStyle.top) || 0;
1200
+ const parentLeft = parseFloat(parentStyle.left) || 0;
1201
+ if (parentTop !== 0 || parentLeft !== 0) {
1202
+ console.log(
1203
+ `[WebSpatial] Found offset element: ${parent.tagName}.${parent.className?.split(" ")[0] || ""} top=${parentTop}px, left=${parentLeft}px`
1204
+ );
1205
+ offsetElements.push({
1206
+ element: parent,
1207
+ originalTop: parent.style.top,
1208
+ originalLeft: parent.style.left,
1209
+ topValue: parentTop,
1210
+ leftValue: parentLeft
1211
+ });
1212
+ }
1213
+ }
1214
+ parent = parent.parentElement;
1215
+ depth++;
1216
+ }
1217
+ return offsetElements;
1218
+ }
1219
+ function getContentOffset(element) {
1220
+ const offsetElements = findOffsetElements(element);
1221
+ return {
1222
+ top: offsetElements.reduce((sum, el) => sum + el.topValue, 0),
1223
+ left: offsetElements.reduce((sum, el) => sum + el.leftValue, 0)
1224
+ };
1225
+ }
1226
+ function makeElementVisible(element) {
1227
+ const restoreFunctions = [];
1228
+ const originalVisibility = element.style.visibility;
1229
+ const computedVisibility = window.getComputedStyle(element).visibility;
1230
+ if (computedVisibility === "hidden") {
1231
+ element.style.visibility = "visible";
1232
+ console.log(`[WebSpatial] Made element visible for capture (was: ${computedVisibility})`);
1233
+ restoreFunctions.push(() => {
1234
+ element.style.visibility = originalVisibility;
1235
+ });
1236
+ }
1237
+ const allDescendants = element.querySelectorAll("*");
1238
+ allDescendants.forEach((el) => {
1239
+ const htmlEl = el;
1240
+ const childComputedVisibility = window.getComputedStyle(htmlEl).visibility;
1241
+ if (childComputedVisibility === "hidden") {
1242
+ const childOriginalVisibility = htmlEl.style.visibility;
1243
+ htmlEl.style.visibility = "visible";
1244
+ restoreFunctions.push(() => {
1245
+ htmlEl.style.visibility = childOriginalVisibility;
1246
+ });
1247
+ }
1248
+ });
1249
+ if (restoreFunctions.length > 1) {
1250
+ console.log(`[WebSpatial] Made ${restoreFunctions.length} elements visible for capture`);
1251
+ }
1252
+ return () => {
1253
+ restoreFunctions.forEach((restore) => restore());
1254
+ };
1255
+ }
1256
+ async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundColor) {
1257
+ try {
1258
+ const rect = element.getBoundingClientRect();
1259
+ const contentOffset = getContentOffset(element);
1260
+ console.log(`[WebSpatial] html2canvas capturing: rect=(${rect.x.toFixed(0)},${rect.y.toFixed(0)},${rect.width.toFixed(0)},${rect.height.toFixed(0)}), contentOffset=(${contentOffset.top},${contentOffset.left})`);
1261
+ const scrollX = window.scrollX || window.pageXOffset || 0;
1262
+ const scrollY = window.scrollY || window.pageYOffset || 0;
1263
+ const absoluteX = rect.x + scrollX;
1264
+ const absoluteY = rect.y + scrollY;
1265
+ const offsetElements = findOffsetElements(element);
1266
+ if (offsetElements.length > 0) {
1267
+ console.log(
1268
+ `[WebSpatial] Temporarily resetting ${offsetElements.length} offset elements for capture`
1269
+ );
1270
+ offsetElements.forEach(({ element: el, topValue, leftValue }) => {
1271
+ el.style.top = "0px";
1272
+ el.style.left = "0px";
1273
+ console.log(
1274
+ `[WebSpatial] Reset: ${el.tagName}.${el.className?.split(" ")[0] || ""} from top=${topValue}px, left=${leftValue}px to 0`
1275
+ );
1276
+ });
1277
+ void element.offsetHeight;
1278
+ await new Promise((resolve) => setTimeout(resolve, 50));
1279
+ console.log("[WebSpatial] Reflow complete after offset reset");
1280
+ }
1281
+ const restorePositions = () => {
1282
+ offsetElements.forEach(({ element: el, originalTop, originalLeft }) => {
1283
+ el.style.top = originalTop;
1284
+ el.style.left = originalLeft;
1285
+ });
1286
+ };
1287
+ if (element.scrollTop !== 0) {
1288
+ console.log(`[WebSpatial] Resetting element scroll from ${element.scrollTop} to 0`);
1289
+ element.scrollTop = 0;
1290
+ }
1291
+ const restoreVisibility = makeElementVisible(element);
1292
+ const viewportTop = scrollY;
1293
+ const viewportBottom = scrollY + window.innerHeight;
1294
+ const elementTop = rect.y + scrollY;
1295
+ const elementBottom = elementTop + rect.height;
1296
+ console.log(
1297
+ `[WebSpatial] Capture context: viewport=(${viewportTop}-${viewportBottom}), element=(${elementTop}-${elementBottom}), innerHeight=${window.innerHeight}`
1298
+ );
1299
+ let canvas;
1300
+ try {
1301
+ const currentRect = element.getBoundingClientRect();
1302
+ const captureX = currentRect.x + scrollX;
1303
+ const captureY = currentRect.y + scrollY;
1304
+ console.log(
1305
+ `[WebSpatial] Capture bounds: original=(${absoluteX},${absoluteY}), afterReset=(${captureX},${captureY}), size=(${currentRect.width}x${currentRect.height})`
1306
+ );
1307
+ canvas = await html2canvas(element, {
1308
+ backgroundColor,
1309
+ logging: true,
1310
+ // Enable logging to debug
1311
+ scale: Math.min(scale, 1.5),
1312
+ useCORS: true,
1313
+ allowTaint: true,
1314
+ imageTimeout: 5e3,
1315
+ removeContainer: true,
1316
+ foreignObjectRendering: false,
1317
+ // Don't set scroll offset - let html2canvas use default (current scroll)
1318
+ // scrollX and scrollY would affect rendering position but we want document coordinates
1319
+ // Crop to element's document position
1320
+ // x/y define the top-left corner of the crop region in document coordinates
1321
+ x: captureX,
1322
+ y: captureY,
1323
+ width: currentRect.width,
1324
+ height: currentRect.height,
1325
+ // Use onclone to MOVE the element to (0,0) in the cloned document
1326
+ // This is the key fix for elements far down the page (like footer at Y=1142)
1327
+ // By moving to absolute position (0,0), html2canvas will render at the top-left
1328
+ onclone: (clonedDoc, clonedElement) => {
1329
+ const originalRect = clonedElement.getBoundingClientRect();
1330
+ console.log(`[WebSpatial] CLONE: Element position in clone (${originalRect.x.toFixed(0)},${originalRect.y.toFixed(0)})`);
1331
+ clonedElement.style.visibility = "visible";
1332
+ const hiddenElements = clonedElement.querySelectorAll("*");
1333
+ let visibilityFixCount = 0;
1334
+ hiddenElements.forEach((el) => {
1335
+ const htmlEl = el;
1336
+ const style = clonedDoc.defaultView?.getComputedStyle(htmlEl);
1337
+ if (style && style.visibility === "hidden") {
1338
+ htmlEl.style.visibility = "visible";
1339
+ visibilityFixCount++;
1340
+ }
1341
+ });
1342
+ if (visibilityFixCount > 0) {
1343
+ console.log(`[WebSpatial] CLONE: Made ${visibilityFixCount + 1} elements visible`);
1344
+ }
1345
+ let parent = clonedElement.parentElement;
1346
+ let depth = 0;
1347
+ const maxDepth = 10;
1348
+ while (parent && depth < maxDepth) {
1349
+ const parentStyle = clonedDoc.defaultView?.getComputedStyle(parent);
1350
+ if (parentStyle && parentStyle.position === "relative") {
1351
+ const parentTop = parseFloat(parentStyle.top) || 0;
1352
+ const parentLeft = parseFloat(parentStyle.left) || 0;
1353
+ if (parentTop !== 0 || parentLeft !== 0) {
1354
+ console.log(
1355
+ `[WebSpatial] CLONE: Reset ${parent.tagName}.${parent.className?.split(" ")[0] || ""} from top=${parentTop}px, left=${parentLeft}px to 0`
1356
+ );
1357
+ parent.style.top = "0px";
1358
+ parent.style.left = "0px";
1359
+ }
1360
+ }
1361
+ parent = parent.parentElement;
1362
+ depth++;
1363
+ }
1364
+ void clonedElement.offsetHeight;
1365
+ const clonedRect = clonedElement.getBoundingClientRect();
1366
+ const clonedStyle = clonedDoc.defaultView?.getComputedStyle(clonedElement);
1367
+ console.log(
1368
+ `[WebSpatial] Cloned element (after visibility fix): rect=(${clonedRect.x.toFixed(0)},${clonedRect.y.toFixed(0)},${clonedRect.width.toFixed(0)},${clonedRect.height.toFixed(0)}), visibility=${clonedStyle?.visibility}`
1369
+ );
1370
+ const numChildren = Math.min(5, clonedElement.children.length);
1371
+ for (let i = 0; i < numChildren; i++) {
1372
+ const child = clonedElement.children[i];
1373
+ const childRect = child.getBoundingClientRect();
1374
+ const childStyle = clonedDoc.defaultView?.getComputedStyle(child);
1375
+ console.log(
1376
+ `[WebSpatial] Child ${i}: ${child.tagName}.${child.className?.split(" ")[0] || ""}, rect=(${childRect.x.toFixed(0)},${childRect.y.toFixed(0)},${childRect.width.toFixed(0)},${childRect.height.toFixed(0)}), vis=${childStyle?.visibility}, display=${childStyle?.display}`
1377
+ );
1378
+ }
1379
+ const productGrid = clonedElement.querySelector(".auto-fill-grid");
1380
+ if (productGrid) {
1381
+ const gridRect = productGrid.getBoundingClientRect();
1382
+ const gridStyle = clonedDoc.defaultView?.getComputedStyle(productGrid);
1383
+ console.log(
1384
+ `[WebSpatial] Product grid: rect=(${gridRect.x.toFixed(0)},${gridRect.y.toFixed(0)},${gridRect.width.toFixed(0)},${gridRect.height.toFixed(0)}), columns=${gridStyle?.gridTemplateColumns?.substring(0, 100)}`
1385
+ );
1386
+ const firstCard = productGrid.children[0];
1387
+ if (firstCard) {
1388
+ const cardRect = firstCard.getBoundingClientRect();
1389
+ console.log(`[WebSpatial] First product card: rect=(${cardRect.x.toFixed(0)},${cardRect.y.toFixed(0)},${cardRect.width.toFixed(0)},${cardRect.height.toFixed(0)})`);
1390
+ }
1391
+ }
1392
+ }
1393
+ });
1394
+ } finally {
1395
+ restoreVisibility();
1396
+ restorePositions();
1397
+ }
1398
+ const ctx = canvas.getContext("2d");
1399
+ if (ctx) {
1400
+ const xPositions = [50, Math.floor(canvas.width / 4), Math.floor(canvas.width / 2), Math.floor(canvas.width * 3 / 4)];
1401
+ const yPositions = [50, 100, 200, 400, 600, 800, 1e3, 1200, 1400];
1402
+ const samples = [];
1403
+ yPositions.forEach((y) => {
1404
+ if (y < canvas.height) {
1405
+ xPositions.forEach((x) => {
1406
+ if (x < canvas.width) {
1407
+ samples.push({ name: `(${x},${y})`, x, y });
1408
+ }
1409
+ });
1410
+ }
1411
+ });
1412
+ console.log(`[WebSpatial] Canvas size: ${canvas.width}x${canvas.height}, scale=${scale}`);
1413
+ let bgCount = 0;
1414
+ let contentCount = 0;
1415
+ let contentPixels = [];
1416
+ samples.forEach((s) => {
1417
+ const pixel = ctx.getImageData(s.x, s.y, 1, 1).data;
1418
+ const isBackground = pixel[0] === 26 && pixel[1] === 26 && pixel[2] === 46;
1419
+ if (isBackground) {
1420
+ bgCount++;
1421
+ } else {
1422
+ contentCount++;
1423
+ contentPixels.push(`${s.name}=rgba(${pixel[0]},${pixel[1]},${pixel[2]})`);
1424
+ }
1425
+ });
1426
+ console.log(`[WebSpatial] Grid sample: ${bgCount} BG, ${contentCount} CONTENT`);
1427
+ if (contentPixels.length > 0) {
1428
+ console.log(`[WebSpatial] Content pixels: ${contentPixels.slice(0, 10).join(", ")}`);
1429
+ }
1430
+ if (contentCount === 0) {
1431
+ console.log(`[WebSpatial] No content in grid sample - scanning center column...`);
1432
+ for (let y = 0; y < canvas.height; y += 30) {
1433
+ const pixel = ctx.getImageData(Math.floor(canvas.width / 2), y, 1, 1).data;
1434
+ const isBackground = pixel[0] === 26 && pixel[1] === 26 && pixel[2] === 46;
1435
+ if (!isBackground) {
1436
+ console.log(`[WebSpatial] First content at Y=${y}: rgba(${pixel[0]},${pixel[1]},${pixel[2]})`);
1437
+ break;
1438
+ }
1439
+ }
1440
+ }
1441
+ }
1442
+ canvas = resizeCanvasIfNeeded(canvas);
1443
+ const dataUrl = canvas.toDataURL("image/webp", 0.85);
1444
+ return dataUrl;
1445
+ } catch (error) {
1446
+ console.error("[WebSpatial] html2canvas capture failed:", error);
1447
+ return null;
1448
+ }
1449
+ }
1450
+ async function captureElementBitmap(element, options) {
1451
+ if (!usesAndroidBitmapCapture()) {
1452
+ return null;
1453
+ }
1454
+ const scale = options?.scale ?? (window.devicePixelRatio || 1);
1455
+ const startTime = performance.now();
1456
+ if (options?.waitForImages !== false) {
1457
+ await waitForContent(element, 500);
1458
+ }
1459
+ const restoreBackground = injectCaptureBackground(
1460
+ element,
1461
+ options?.backgroundColor || DEFAULT_CAPTURE_BACKGROUND
1462
+ );
1463
+ let result = null;
1464
+ try {
1465
+ const html2canvas = await loadHtml2Canvas();
1466
+ if (html2canvas) {
1467
+ console.log("[WebSpatial] Using html2canvas (primary)");
1468
+ result = await captureWithHtml2Canvas(
1469
+ html2canvas,
1470
+ element,
1471
+ scale,
1472
+ options?.backgroundColor ?? DEFAULT_CAPTURE_BACKGROUND
1473
+ );
1474
+ if (result) {
1475
+ const elapsed = Math.round(performance.now() - startTime);
1476
+ console.log(`[WebSpatial] Capture complete (html2canvas, ${elapsed}ms)`);
1477
+ return result;
1478
+ }
1479
+ }
1480
+ const snapdom = await loadSnapdom();
1481
+ if (snapdom) {
1482
+ console.log("[WebSpatial] Falling back to snapdom");
1483
+ result = await captureWithSnapdom(snapdom, element, scale);
1484
+ if (result) {
1485
+ const elapsed = Math.round(performance.now() - startTime);
1486
+ console.log(`[WebSpatial] Capture complete (snapdom, ${elapsed}ms)`);
1487
+ return result;
1488
+ }
1489
+ }
1490
+ console.error("[WebSpatial] No capture library available");
1491
+ return null;
1492
+ } finally {
1493
+ restoreBackground();
1494
+ }
1495
+ }
1496
+ function observeContentChanges(element, onContentChange) {
1497
+ const observer = new MutationObserver((_mutations) => {
1498
+ onContentChange();
1499
+ });
1500
+ observer.observe(element, {
1501
+ childList: true,
1502
+ subtree: true,
1503
+ characterData: true,
1504
+ attributes: true,
1505
+ attributeFilter: ["class", "style"]
1506
+ });
1507
+ let resizeObserver = null;
1508
+ if (typeof ResizeObserver !== "undefined") {
1509
+ resizeObserver = new ResizeObserver((_entries) => {
1510
+ onContentChange();
1511
+ });
1512
+ resizeObserver.observe(element);
1513
+ }
1514
+ return () => {
1515
+ observer.disconnect();
1516
+ resizeObserver?.disconnect();
1517
+ };
1518
+ }
1519
+
1520
+ // src/utils/BitmapCaptureCoordinator.ts
1521
+ var BitmapCaptureCoordinatorClass = class {
1522
+ // Track completed captures by element UUID
1523
+ capturedElements = /* @__PURE__ */ new Set();
1524
+ // Track in-flight capture requests
1525
+ pendingCaptures = /* @__PURE__ */ new Map();
1526
+ // Minimum time between recaptures of the same element (ms)
1527
+ recaptureThrottleMs = 750;
1528
+ // Capture queue for serialized processing (prevents thread contention)
1529
+ captureQueue = [];
1530
+ isProcessingQueue = false;
1531
+ /**
1532
+ * Request a bitmap capture for an element.
1533
+ * Returns null immediately if the element has already been captured.
1534
+ * Deduplicates concurrent requests for the same element.
1535
+ * Captures are serialized to prevent thread contention.
1536
+ *
1537
+ * @param elementId Unique element ID (UUID, not spatialId)
1538
+ * @param dom The DOM element to capture
1539
+ * @returns Promise resolving to bitmap data URL, or null if already captured
1540
+ */
1541
+ async requestCapture(elementId, dom) {
1542
+ if (this.capturedElements.has(elementId)) {
1543
+ console.log(`[WebSpatial] Skipping capture for ${elementId} (already captured)`);
1544
+ return null;
1545
+ }
1546
+ const pending = this.pendingCaptures.get(elementId);
1547
+ if (pending) {
1548
+ console.log(`[WebSpatial] Joining existing capture for ${elementId}`);
1549
+ return pending.promise;
1550
+ }
1551
+ this.capturedElements.add(elementId);
1552
+ const promise = new Promise((resolve, reject) => {
1553
+ this.captureQueue.push({ elementId, dom, resolve, reject });
1554
+ console.log(`[WebSpatial] Queued capture for ${elementId} (queue size: ${this.captureQueue.length})`);
1555
+ });
1556
+ this.pendingCaptures.set(elementId, {
1557
+ promise,
1558
+ timestamp: Date.now()
1559
+ });
1560
+ promise.finally(() => {
1561
+ this.pendingCaptures.delete(elementId);
1562
+ });
1563
+ this.processQueue();
1564
+ return promise;
1565
+ }
1566
+ /**
1567
+ * Process the capture queue one at a time.
1568
+ * This prevents thread contention and ensures consistent capture performance.
1569
+ */
1570
+ async processQueue() {
1571
+ if (this.isProcessingQueue) return;
1572
+ this.isProcessingQueue = true;
1573
+ while (this.captureQueue.length > 0) {
1574
+ const item = this.captureQueue.shift();
1575
+ const { elementId, dom, resolve, reject } = item;
1576
+ try {
1577
+ console.log(`[WebSpatial] Processing capture for ${elementId} (${this.captureQueue.length} remaining)`);
1578
+ const bitmap = await this.doCapture(elementId, dom);
1579
+ resolve(bitmap);
1580
+ } catch (error) {
1581
+ reject(error);
1582
+ }
1583
+ await new Promise((r) => setTimeout(r, 50));
1584
+ }
1585
+ this.isProcessingQueue = false;
1586
+ }
1587
+ /**
1588
+ * Force a recapture of an element (e.g., after content change).
1589
+ * Respects throttling to prevent excessive recaptures.
1590
+ */
1591
+ async requestRecapture(elementId, dom) {
1592
+ const pending = this.pendingCaptures.get(elementId);
1593
+ if (pending && Date.now() - pending.timestamp < this.recaptureThrottleMs) {
1594
+ console.log(`[WebSpatial] Throttling recapture for ${elementId}`);
1595
+ return pending.promise;
1596
+ }
1597
+ this.capturedElements.delete(elementId);
1598
+ return this.requestCapture(elementId, dom);
1599
+ }
1600
+ /**
1601
+ * Perform the actual capture.
1602
+ */
1603
+ async doCapture(elementId, dom) {
1604
+ console.log(`[WebSpatial] Starting capture for ${elementId}`);
1605
+ this.logElementDiagnostics(elementId, dom);
1606
+ try {
1607
+ const bitmap = await captureElementBitmap(dom);
1608
+ if (bitmap) {
1609
+ const sizeKB = Math.round(bitmap.length / 1024);
1610
+ console.log(`[WebSpatial] Capture complete for ${elementId} (${sizeKB}KB)`);
1611
+ } else {
1612
+ console.log(`[WebSpatial] Capture returned null for ${elementId}`);
1613
+ }
1614
+ return bitmap;
1615
+ } catch (error) {
1616
+ console.error(`[WebSpatial] Capture failed for ${elementId}:`, error);
1617
+ this.capturedElements.delete(elementId);
1618
+ return null;
1619
+ }
1620
+ }
1621
+ /**
1622
+ * Clear the capture state for an element (e.g., when destroyed).
1623
+ */
1624
+ clearElement(elementId) {
1625
+ this.capturedElements.delete(elementId);
1626
+ this.pendingCaptures.delete(elementId);
1627
+ }
1628
+ /**
1629
+ * Clear all capture state (e.g., on page navigation).
1630
+ */
1631
+ clearAll() {
1632
+ this.capturedElements.clear();
1633
+ this.pendingCaptures.clear();
1634
+ }
1635
+ /**
1636
+ * Check if an element has been captured.
1637
+ */
1638
+ hasCaptured(elementId) {
1639
+ return this.capturedElements.has(elementId);
1640
+ }
1641
+ /**
1642
+ * Log detailed diagnostics about an element before capture.
1643
+ * This helps debug issues like content being shifted or hidden.
1644
+ */
1645
+ logElementDiagnostics(elementId, dom) {
1646
+ const rect = dom.getBoundingClientRect();
1647
+ const style = window.getComputedStyle(dom);
1648
+ console.log(`[WebSpatial] === CAPTURE DIAGNOSTICS: ${elementId} ===`);
1649
+ console.log(`[WebSpatial] Element: ${dom.tagName}.${dom.className}`);
1650
+ console.log(`[WebSpatial] BoundingRect: (${rect.x.toFixed(0)}, ${rect.y.toFixed(0)}, ${rect.width.toFixed(0)}, ${rect.height.toFixed(0)})`);
1651
+ console.log(`[WebSpatial] Position: ${style.position}, Top: ${style.top}, Left: ${style.left}`);
1652
+ console.log(`[WebSpatial] Padding: ${style.paddingTop} / ${style.paddingRight} / ${style.paddingBottom} / ${style.paddingLeft}`);
1653
+ console.log(`[WebSpatial] Margin: ${style.marginTop} / ${style.marginRight} / ${style.marginBottom} / ${style.marginLeft}`);
1654
+ console.log(`[WebSpatial] Visibility: ${style.visibility}, Display: ${style.display}`);
1655
+ console.log(`[WebSpatial] Overflow: ${style.overflow}, OverflowY: ${style.overflowY}`);
1656
+ const parent = dom.parentElement;
1657
+ if (parent) {
1658
+ const parentStyle = window.getComputedStyle(parent);
1659
+ const parentRect = parent.getBoundingClientRect();
1660
+ console.log(`[WebSpatial] Parent: ${parent.tagName}.${parent.className}`);
1661
+ console.log(`[WebSpatial] Parent BoundingRect: (${parentRect.x.toFixed(0)}, ${parentRect.y.toFixed(0)}, ${parentRect.width.toFixed(0)}, ${parentRect.height.toFixed(0)})`);
1662
+ console.log(`[WebSpatial] Parent Position: ${parentStyle.position}, Top: ${parentStyle.top}`);
1663
+ }
1664
+ const children = dom.children;
1665
+ console.log(`[WebSpatial] Children count: ${children.length}`);
1666
+ for (let i = 0; i < Math.min(5, children.length); i++) {
1667
+ const child = children[i];
1668
+ const childRect = child.getBoundingClientRect();
1669
+ const childStyle = window.getComputedStyle(child);
1670
+ console.log(`[WebSpatial] Child ${i}: ${child.tagName}.${child.className?.substring(0, 30)}...`);
1671
+ console.log(`[WebSpatial] Rect: (${childRect.x.toFixed(0)}, ${childRect.y.toFixed(0)}, ${childRect.width.toFixed(0)}, ${childRect.height.toFixed(0)})`);
1672
+ console.log(`[WebSpatial] Position: ${childStyle.position}, Visibility: ${childStyle.visibility}`);
1673
+ }
1674
+ console.log(`[WebSpatial] === END DIAGNOSTICS ===`);
1675
+ }
1676
+ };
1677
+ var BitmapCaptureCoordinator = new BitmapCaptureCoordinatorClass();
1678
+
1679
+ // src/spatialized-container/transform-utils.ts
1680
+ function toSceneSpatial(point, spatializedElement) {
1681
+ return spatializedElement.__toSceneSpace(point);
1682
+ }
1683
+ function toLocalSpace(point, spatializedElement) {
1684
+ return spatializedElement.__toLocalSpace(point);
1685
+ }
1686
+ function convertDOMRectToSceneSpace(originalRect, matrix) {
1687
+ const topLeft = new DOMPoint(originalRect.left, originalRect.top);
1688
+ const topRight = new DOMPoint(originalRect.right, originalRect.top);
1689
+ const bottomRight = new DOMPoint(originalRect.right, originalRect.bottom);
1690
+ const bottomLeft = new DOMPoint(originalRect.left, originalRect.bottom);
1691
+ const transformedTopLeft = matrix.transformPoint(topLeft);
1692
+ const transformedTopRight = matrix.transformPoint(topRight);
1693
+ const transformedBottomRight = matrix.transformPoint(bottomRight);
1694
+ const transformedBottomLeft = matrix.transformPoint(bottomLeft);
1695
+ const allPoints = [
1696
+ transformedTopLeft,
1697
+ transformedTopRight,
1698
+ transformedBottomRight,
1699
+ transformedBottomLeft
1700
+ ];
1701
+ const xCoords = allPoints.map((point) => point.x);
1702
+ const yCoords = allPoints.map((point) => point.y);
1703
+ const newMinX = Math.min(...xCoords);
1704
+ const newMaxX = Math.max(...xCoords);
1705
+ const newMinY = Math.min(...yCoords);
1706
+ const newMaxY = Math.max(...yCoords);
1707
+ return new DOMRect(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY);
1708
+ }
1709
+
1710
+ // src/spatialized-container/context/PortalInstanceContext.ts
1711
+ var PortalInstanceObject = class {
1712
+ spatialId;
1713
+ spatializedContainerObject;
1714
+ parentPortalInstanceObject;
1715
+ spatializedElement;
1716
+ // cachedDomInfo used for cache dom info
1717
+ // when dom is updated, this property should be updated as well
1718
+ cachedDomInfo;
1719
+ get dom() {
1720
+ return this.cachedDomInfo?.dom;
1721
+ }
1722
+ get computedStyle() {
1723
+ return this.cachedDomInfo?.computedStyle;
1724
+ }
1725
+ get isFixedPosition() {
1726
+ return this.cachedDomInfo?.isFixedPosition;
1727
+ }
1728
+ // cachedDomRect used for cache dom rect
1729
+ cachedDomRect;
1730
+ get domRect() {
1731
+ return this.cachedDomRect;
1732
+ }
1733
+ // cachedTransformVisibilityInfo used for cache transform visibility info
1734
+ cachedTransformVisibilityInfo;
1735
+ get transformMatrix() {
1736
+ return this.cachedTransformVisibilityInfo?.transformMatrix;
1737
+ }
1738
+ get visibility() {
1739
+ return this.cachedTransformVisibilityInfo?.visibility;
1740
+ }
1741
+ // spatializedElementPromise used for get spatialized element
1742
+ // SpatializedElement is when attachSpatializedElement is called
1743
+ spatializedElementPromise;
1744
+ spatializedElementResolver;
1745
+ // used for get extra spatialized element properties
1746
+ getExtraSpatializedElementProperties;
1747
+ // Bitmap capture state for Android
1748
+ pendingBitmapCapture = null;
1749
+ // Initial delay before first capture (0ms = start immediately, content detection handles fonts/images)
1750
+ bitmapCaptureInitialDelayMs = 0;
1751
+ // Track if capture has been requested via coordinator
1752
+ captureRequested = false;
1753
+ observedContentDom = null;
1754
+ stopObservingContentChanges = null;
1755
+ constructor(spatialId, spatializedContainerObject, parentPortalInstanceObject, getExtraSpatializedElementProperties2) {
1756
+ this.spatialId = spatialId;
1757
+ this.spatializedContainerObject = spatializedContainerObject;
1758
+ this.parentPortalInstanceObject = parentPortalInstanceObject;
1759
+ this.getExtraSpatializedElementProperties = getExtraSpatializedElementProperties2;
1760
+ this.spatializedElementPromise = new Promise(
1761
+ (resolve) => {
1762
+ this.spatializedElementResolver = resolve;
1763
+ }
1764
+ );
1765
+ }
1766
+ // called when PortalSpatializedContainer is mounted
1767
+ init() {
1768
+ this.spatializedContainerObject.onSpatialTransformVisibilityChange(
1769
+ this.spatialId,
1770
+ this.onSpatialTransformVisibilityChange
1771
+ );
1772
+ }
1773
+ // called when PortalSpatializedContainer is unmounted
1774
+ destroy() {
1775
+ this.spatializedContainerObject.offSpatialTransformVisibilityChange(
1776
+ this.spatialId,
1777
+ this.onSpatialTransformVisibilityChange
1778
+ );
1779
+ if (this.pendingBitmapCapture) {
1780
+ clearTimeout(this.pendingBitmapCapture);
1781
+ this.pendingBitmapCapture = null;
1782
+ }
1783
+ if (this.spatializedElement) {
1784
+ BitmapCaptureCoordinator.clearElement(this.spatializedElement.id);
1785
+ }
1786
+ this.stopObservingContentChanges?.();
1787
+ this.observedContentDom = null;
1788
+ this.stopObservingContentChanges = null;
1789
+ }
1790
+ onSpatialTransformVisibilityChange = (spatialTransform) => {
1791
+ this.cachedTransformVisibilityInfo = {
1792
+ transformMatrix: new DOMMatrix(spatialTransform.transform),
1793
+ visibility: spatialTransform.visibility
1794
+ };
1795
+ this.updateSpatializedElementProperties();
1796
+ };
1797
+ // called when 2D frame change
1798
+ notify2DFrameChange() {
1799
+ const dom = this.spatializedContainerObject.querySpatialDomBySpatialId(
1800
+ this.spatialId
1801
+ );
1802
+ if (!dom) {
1803
+ return;
1804
+ }
1805
+ const computedStyle = getComputedStyle(dom);
1806
+ this.cachedDomInfo = {
1807
+ dom,
1808
+ computedStyle,
1809
+ isFixedPosition: computedStyle.getPropertyValue("position") === "fixed"
1810
+ };
1811
+ if (usesAndroidBitmapCapture()) {
1812
+ this.ensureContentObserver(dom);
1813
+ }
1814
+ this.updateSpatializedElementProperties();
1815
+ const __getBoundingClientCube = () => {
1816
+ return this.spatializedElement?.cubeInfo;
1817
+ };
1818
+ const __getBoundingClientRect = () => {
1819
+ if (!this.spatializedElement?.transform) {
1820
+ return null;
1821
+ }
1822
+ const domRect = new DOMRect(
1823
+ 0,
1824
+ 0,
1825
+ this.domRect?.width,
1826
+ this.domRect?.height
1827
+ );
1828
+ return convertDOMRectToSceneSpace(
1829
+ domRect,
1830
+ this.spatializedElement?.transform
1831
+ );
1832
+ };
1833
+ const __toSceneSpace = (point) => {
1834
+ return new DOMPoint(point.x, point.y, point.z).matrixTransform(
1835
+ this.spatializedElement?.transform
1836
+ );
1837
+ };
1838
+ const __toLocalSpace = (point) => {
1839
+ return new DOMPoint(point.x, point.y, point.z).matrixTransform(
1840
+ this.spatializedElement?.transformInv
1841
+ );
1842
+ };
1843
+ const __innerSpatializedElement = () => this.spatializedElement;
1844
+ Object.assign(dom, {
1845
+ __getBoundingClientCube,
1846
+ __getBoundingClientRect,
1847
+ __toSceneSpace,
1848
+ __toLocalSpace,
1849
+ __innerSpatializedElement
1850
+ });
1851
+ }
1852
+ ensureContentObserver(dom) {
1853
+ if (!usesAndroidBitmapCapture()) {
1854
+ return;
1855
+ }
1856
+ if (this.observedContentDom === dom && this.stopObservingContentChanges) {
1857
+ return;
1858
+ }
1859
+ this.stopObservingContentChanges?.();
1860
+ this.observedContentDom = dom;
1861
+ this.stopObservingContentChanges = observeContentChanges(dom, () => {
1862
+ this.scheduleBitmapCapture(true);
1863
+ });
1864
+ }
1865
+ async getSpatializedElement() {
1866
+ return this.spatializedElementPromise;
1867
+ }
1868
+ // called when SpatializedElement is created
1869
+ attachSpatializedElement(spatializedElement) {
1870
+ this.spatializedElement = spatializedElement;
1871
+ this.addToParent(spatializedElement);
1872
+ this.spatializedElementResolver?.(spatializedElement);
1873
+ this.updateSpatializedElementProperties();
1874
+ }
1875
+ inAddingToParent = false;
1876
+ async addToParent(spatializedElement) {
1877
+ if (this.inAddingToParent) {
1878
+ return;
1879
+ }
1880
+ this.inAddingToParent = true;
1881
+ if (this.isFixedPosition || !this.parentPortalInstanceObject) {
1882
+ var spatialScene = await getSession().getSpatialScene();
1883
+ await spatialScene.addSpatializedElement(spatializedElement);
1884
+ } else {
1885
+ const parentSpatialized2DElement = await this.parentPortalInstanceObject.getSpatializedElement();
1886
+ parentSpatialized2DElement.addSpatializedElement(spatializedElement);
1887
+ }
1888
+ this.inAddingToParent = false;
1889
+ }
1890
+ /**
1891
+ * Captures the DOM element as a bitmap for Android XR rendering.
1892
+ * Uses BitmapCaptureCoordinator to prevent duplicate captures across instances.
1893
+ * The initial capture is delayed to allow images to load.
1894
+ */
1895
+ scheduleBitmapCapture(forceRecapture = false) {
1896
+ if (!usesAndroidBitmapCapture()) return;
1897
+ if (!this.dom || !this.spatializedElement) return;
1898
+ const elementId = this.spatializedElement.id;
1899
+ if (this.captureRequested) {
1900
+ return;
1901
+ }
1902
+ this.captureRequested = true;
1903
+ if (this.pendingBitmapCapture) {
1904
+ clearTimeout(this.pendingBitmapCapture);
1905
+ }
1906
+ console.log(
1907
+ `[WebSpatial] Scheduling capture for: ${elementId} (in ${this.bitmapCaptureInitialDelayMs}ms)`
1908
+ );
1909
+ this.pendingBitmapCapture = setTimeout(async () => {
1910
+ this.pendingBitmapCapture = null;
1911
+ if (!this.dom || !this.spatializedElement) {
1912
+ console.log(`[WebSpatial] Capture cancelled - element gone: ${elementId}`);
1913
+ return;
1914
+ }
1915
+ try {
1916
+ const captureStyleId = "__webspatial_capture_style__";
1917
+ let captureStyle = document.getElementById(captureStyleId);
1918
+ if (!captureStyle) {
1919
+ captureStyle = document.createElement("style");
1920
+ captureStyle.id = captureStyleId;
1921
+ document.head.appendChild(captureStyle);
1922
+ }
1923
+ captureStyle.textContent = `
1924
+ .xr-spatial-default,
1925
+ [enable-xr],
1926
+ .xr-spatial-default * {
1927
+ visibility: visible !important;
1928
+ }
1929
+ `;
1930
+ const originalVisibility = this.dom.style.visibility;
1931
+ const originalCssText = this.dom.style.cssText;
1932
+ this.dom.style.setProperty("visibility", "visible", "important");
1933
+ const nestedSpatialElements = this.dom.querySelectorAll(".xr-spatial-default");
1934
+ const nestedOriginalVisibilities = [];
1935
+ nestedSpatialElements.forEach((el) => {
1936
+ const htmlEl = el;
1937
+ nestedOriginalVisibilities.push({
1938
+ element: htmlEl,
1939
+ visibility: htmlEl.style.visibility,
1940
+ cssText: htmlEl.style.cssText
1941
+ });
1942
+ htmlEl.style.setProperty("visibility", "visible", "important");
1943
+ });
1944
+ const fixedElements = [];
1945
+ this.dom.querySelectorAll("*").forEach((el) => {
1946
+ const htmlEl = el;
1947
+ const style = window.getComputedStyle(htmlEl);
1948
+ if (style.position === "fixed") {
1949
+ fixedElements.push({
1950
+ element: htmlEl,
1951
+ display: htmlEl.style.display
1952
+ });
1953
+ htmlEl.style.display = "none";
1954
+ }
1955
+ });
1956
+ console.log(
1957
+ `[WebSpatial] Capturing ${elementId} with ${nestedSpatialElements.length} nested spatial elements made visible, ${fixedElements.length} fixed elements hidden`
1958
+ );
1959
+ const bitmap = forceRecapture || BitmapCaptureCoordinator.hasCaptured(elementId) ? await BitmapCaptureCoordinator.requestRecapture(
1960
+ elementId,
1961
+ this.dom
1962
+ ) : await BitmapCaptureCoordinator.requestCapture(
1963
+ elementId,
1964
+ this.dom
1965
+ );
1966
+ const captureStyleToRemove = document.getElementById("__webspatial_capture_style__");
1967
+ if (captureStyleToRemove) {
1968
+ captureStyleToRemove.textContent = "";
1969
+ }
1970
+ this.dom.style.cssText = originalCssText;
1971
+ if (originalVisibility) {
1972
+ this.dom.style.visibility = originalVisibility;
1973
+ }
1974
+ nestedOriginalVisibilities.forEach(({ element, visibility, cssText }) => {
1975
+ element.style.cssText = cssText;
1976
+ if (visibility) {
1977
+ element.style.visibility = visibility;
1978
+ }
1979
+ });
1980
+ fixedElements.forEach(({ element, display }) => {
1981
+ element.style.display = display;
1982
+ });
1983
+ if (bitmap) {
1984
+ this.spatializedElement.updateProperties({ bitmap });
1985
+ }
1986
+ } catch (error) {
1987
+ console.error(`[WebSpatial] Capture failed: ${elementId}`, error);
1988
+ } finally {
1989
+ this.captureRequested = false;
1990
+ }
1991
+ }, this.bitmapCaptureInitialDelayMs);
1992
+ }
1993
+ updateSpatializedElementProperties() {
1994
+ const dom = this.dom;
1995
+ const spatializedElement = this.spatializedElement;
1996
+ const visibility = this.visibility;
1997
+ if (!dom || !spatializedElement || !visibility || !this.transformMatrix) {
1998
+ return;
1999
+ }
2000
+ const computedStyle = this.computedStyle;
2001
+ const isFixedPosition = this.isFixedPosition;
2002
+ let domRect = dom.getBoundingClientRect();
2003
+ let { x, y } = domRect;
2004
+ if (!isFixedPosition) {
2005
+ const parentDom = this.spatializedContainerObject.queryParentSpatialDomBySpatialId(
2006
+ this.spatialId
2007
+ );
2008
+ if (parentDom) {
2009
+ const parentDomRect = parentDom.getBoundingClientRect();
2010
+ x -= parentDomRect.x;
2011
+ y -= parentDomRect.y;
2012
+ } else {
2013
+ x += window.scrollX;
2014
+ y += window.scrollY;
2015
+ }
2016
+ }
2017
+ this.cachedDomRect = {
2018
+ x: domRect.x,
2019
+ y: domRect.y,
2020
+ width: domRect.width,
2021
+ height: domRect.height
2022
+ };
2023
+ const width = domRect.width;
2024
+ const height = domRect.height;
2025
+ const opacity = parseFloat(computedStyle.getPropertyValue("opacity"));
2026
+ const scrollWithParent = !isFixedPosition;
2027
+ const display = computedStyle.getPropertyValue("display");
2028
+ const visible = visibility === "visible" && display !== "none";
2029
+ const zIndex = parseFloat(
2030
+ computedStyle.getPropertyValue(SpatialCustomStyleVars.xrZIndex)
2031
+ ) || 0;
2032
+ const backOffset = parseFloat(computedStyle.getPropertyValue(SpatialCustomStyleVars.back)) || 0;
2033
+ const depth = parseFloat(
2034
+ computedStyle.getPropertyValue(SpatialCustomStyleVars.depth)
2035
+ ) || 0;
2036
+ const rotationAnchor = parseTransformOrigin(computedStyle);
2037
+ const extraProperties = this.getExtraSpatializedElementProperties?.(computedStyle) || {};
2038
+ spatializedElement.updateProperties({
2039
+ clientX: x,
2040
+ clientY: y,
2041
+ width,
2042
+ height,
2043
+ depth,
2044
+ opacity,
2045
+ scrollWithParent,
2046
+ zIndex,
2047
+ visible,
2048
+ backOffset,
2049
+ rotationAnchor,
2050
+ ...extraProperties
2051
+ });
2052
+ spatializedElement.updateTransform(this.transformMatrix);
2053
+ Object.assign(this.dom, {
2054
+ __spatializedElement: spatializedElement
2055
+ });
2056
+ if (usesAndroidBitmapCapture()) {
2057
+ this.scheduleBitmapCapture();
2058
+ }
2059
+ }
2060
+ };
2061
+ var PortalInstanceContext = createContext3(
2062
+ null
2063
+ );
2064
+
2065
+ // src/spatialized-container/hooks/useSync2DFrame.ts
2066
+ import { useEffect as useEffect5, useState as useState2 } from "react";
2067
+ function useForceUpdate() {
2068
+ const [, setToggle] = useState2(false);
2069
+ return () => setToggle((toggle) => !toggle);
2070
+ }
2071
+ function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject) {
2072
+ const forceUpdate = useForceUpdate();
2073
+ useEffect5(() => {
2074
+ spatializedContainerObject.on2DFrameChange(spatialId, () => {
2075
+ portalInstanceObject.notify2DFrameChange();
2076
+ forceUpdate();
2077
+ });
2078
+ return () => {
2079
+ spatializedContainerObject.off2DFrameChange(spatialId);
2080
+ };
2081
+ }, []);
2082
+ }
2083
+
2084
+ // src/spatialized-container/hooks/useSpatializedElement.ts
2085
+ import { useEffect as useEffect6, useState as useState3 } from "react";
2086
+ function useSpatializedElement(createSpatializedElement2, portalInstanceObject) {
2087
+ const [spatializedElement, setSpatializedElement] = useState3();
2088
+ useEffect6(() => {
2089
+ let isDestroyed = false;
2090
+ let spatializedElement2;
2091
+ createSpatializedElement2().then(
2092
+ (inSpatializedElement) => {
2093
+ if (!isDestroyed) {
2094
+ spatializedElement2 = inSpatializedElement;
2095
+ portalInstanceObject.attachSpatializedElement(spatializedElement2);
2096
+ setSpatializedElement(spatializedElement2);
2097
+ } else {
2098
+ inSpatializedElement?.destroy();
2099
+ }
2100
+ }
2101
+ );
2102
+ return () => {
2103
+ isDestroyed = true;
2104
+ if (spatializedElement2) {
2105
+ spatializedElement2.destroy();
2106
+ spatializedElement2 = void 0;
2107
+ }
2108
+ };
2109
+ }, [createSpatializedElement2, portalInstanceObject]);
2110
+ return spatializedElement;
2111
+ }
2112
+
2113
+ // src/spatialized-container/PortalSpatializedContainer.tsx
2114
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
2115
+ function renderPlaceholderInSubPortal(portalInstanceObject, El) {
2116
+ const spatialId = portalInstanceObject.spatialId;
2117
+ const inPortalInstanceEnv = !!portalInstanceObject.parentPortalInstanceObject;
2118
+ const position = portalInstanceObject.computedStyle?.getPropertyValue("position");
2119
+ const shouldRenderPlaceHolder = inPortalInstanceEnv && portalInstanceObject && portalInstanceObject.domRect && position !== "absolute" && position !== "fixed";
2120
+ if (!shouldRenderPlaceHolder) {
2121
+ return /* @__PURE__ */ jsx3(Fragment, {});
2122
+ }
2123
+ const { width, height } = portalInstanceObject.domRect;
2124
+ const display = portalInstanceObject.computedStyle.getPropertyPriority("display");
2125
+ const spatialIdProps = { [SpatialID]: spatialId };
2126
+ return /* @__PURE__ */ jsx3(
2127
+ El,
2128
+ {
2129
+ ...spatialIdProps,
2130
+ style: {
2131
+ position: "relative",
2132
+ width: `${width}px`,
2133
+ height: `${height}px`,
2134
+ visibility: "hidden",
2135
+ display
2136
+ }
2137
+ }
2138
+ );
2139
+ }
2140
+ function PortalSpatializedContainer(props) {
2141
+ const {
2142
+ spatializedContent: Content,
2143
+ createSpatializedElement: createSpatializedElement2,
2144
+ getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
2145
+ onSpatialTap,
2146
+ onSpatialDragStart,
2147
+ onSpatialDrag,
2148
+ onSpatialDragEnd,
2149
+ onSpatialRotate,
2150
+ onSpatialRotateEnd,
2151
+ onSpatialMagnify,
2152
+ onSpatialMagnifyEnd,
2153
+ [SpatialID]: spatialId,
2154
+ ...restProps
2155
+ } = props;
2156
+ const spatializedContainerObject = useContext4(
2157
+ SpatializedContainerContext
2158
+ );
2159
+ const parentPortalInstanceObject = useContext4(PortalInstanceContext);
2160
+ const portalInstanceObject = useMemo(
2161
+ () => new PortalInstanceObject(
2162
+ spatialId,
2163
+ spatializedContainerObject,
2164
+ parentPortalInstanceObject,
2165
+ getExtraSpatializedElementProperties2
2166
+ ),
2167
+ []
2168
+ );
2169
+ useEffect7(() => {
2170
+ portalInstanceObject.init();
2171
+ return () => {
2172
+ portalInstanceObject.destroy();
2173
+ };
2174
+ }, []);
2175
+ useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject);
2176
+ const spatializedElement = useSpatializedElement(
2177
+ createSpatializedElement2,
2178
+ portalInstanceObject
2179
+ );
2180
+ const PlaceholderEl = renderPlaceholderInSubPortal(
2181
+ portalInstanceObject,
2182
+ props.component
2183
+ );
2184
+ useEffect7(() => {
2185
+ if (spatializedElement) {
2186
+ spatializedElement.onSpatialTap = onSpatialTap;
2187
+ }
2188
+ }, [spatializedElement, onSpatialTap]);
2189
+ useEffect7(() => {
2190
+ if (spatializedElement) {
2191
+ spatializedElement.onSpatialDrag = onSpatialDrag;
2192
+ }
2193
+ }, [spatializedElement, onSpatialDrag]);
2194
+ useEffect7(() => {
2195
+ if (spatializedElement) {
2196
+ spatializedElement.onSpatialDragEnd = onSpatialDragEnd;
2197
+ }
2198
+ }, [spatializedElement, onSpatialDragEnd]);
2199
+ useEffect7(() => {
2200
+ if (spatializedElement) {
2201
+ spatializedElement.onSpatialRotate = onSpatialRotate;
2202
+ }
2203
+ }, [spatializedElement, onSpatialRotate]);
2204
+ useEffect7(() => {
2205
+ if (spatializedElement) {
2206
+ spatializedElement.onSpatialRotateEnd = onSpatialRotateEnd;
2207
+ }
2208
+ }, [spatializedElement, onSpatialRotateEnd]);
2209
+ useEffect7(() => {
2210
+ if (spatializedElement) {
2211
+ spatializedElement.onSpatialMagnify = onSpatialMagnify;
2212
+ }
2213
+ }, [spatializedElement, onSpatialMagnify]);
2214
+ useEffect7(() => {
2215
+ if (spatializedElement) {
2216
+ spatializedElement.onSpatialMagnifyEnd = onSpatialMagnifyEnd;
2217
+ }
2218
+ }, [spatializedElement, onSpatialMagnifyEnd]);
2219
+ useEffect7(() => {
2220
+ if (spatializedElement) {
2221
+ spatializedElement.onSpatialDragStart = onSpatialDragStart;
2222
+ }
2223
+ }, [spatializedElement, onSpatialDragStart]);
2224
+ return /* @__PURE__ */ jsxs(PortalInstanceContext.Provider, { value: portalInstanceObject, children: [
2225
+ spatializedElement && portalInstanceObject.dom && /* @__PURE__ */ jsx3(Content, { spatializedElement, ...restProps }),
2226
+ PlaceholderEl
2227
+ ] });
2228
+ }
2229
+
2230
+ // src/reality/context/InsideAttachmentContext.tsx
2231
+ import { createContext as createContext4, useContext as useContext5 } from "react";
2232
+ var InsideAttachmentContext = createContext4(false);
2233
+ var useInsideAttachment = () => useContext5(InsideAttachmentContext);
2234
+
2235
+ // src/spatialized-container/hooks/useSpatialEvents.ts
2236
+ function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
2237
+ return new Proxy(event, {
2238
+ get(target, prop) {
2239
+ if (prop === "currentTarget") {
2240
+ return currentTargetGetter();
2241
+ }
2242
+ if (prop === "isTrusted") {
2243
+ return true;
2244
+ }
2245
+ if (prop === "bubbles") {
2246
+ return false;
2247
+ }
2248
+ if (prop === "offsetX" && offsetXGetter) {
2249
+ return offsetXGetter(target) ?? 0;
2250
+ }
2251
+ if (prop === "offsetY" && offsetYGetter) {
2252
+ return offsetYGetter(target) ?? 0;
2253
+ }
2254
+ if (prop === "offsetZ" && offsetZGetter) {
2255
+ return offsetZGetter(target) ?? 0;
2256
+ }
2257
+ if (prop === "clientX" && clientXGetter) {
2258
+ return clientXGetter(target) ?? 0;
2259
+ }
2260
+ if (prop === "clientY" && clientYGetter) {
2261
+ return clientYGetter(target) ?? 0;
2262
+ }
2263
+ if (prop === "clientZ" && clientZGetter) {
2264
+ return clientZGetter(target) ?? 0;
2265
+ }
2266
+ if (prop === "translationX" && translationXGetter) {
2267
+ return translationXGetter(target) ?? 0;
2268
+ }
2269
+ if (prop === "translationY" && translationYGetter) {
2270
+ return translationYGetter(target) ?? 0;
2271
+ }
2272
+ if (prop === "translationZ" && translationZGetter) {
2273
+ return translationZGetter(target) ?? 0;
2274
+ }
2275
+ if (prop === "quaternion" && quaternionGetter) {
2276
+ return quaternionGetter(target) ?? { x: 0, y: 0, z: 0, w: 1 };
2277
+ }
2278
+ if (prop === "magnification" && magnificationGetter) {
2279
+ return magnificationGetter(target) ?? 1;
2280
+ }
2281
+ return Reflect.get(target, prop);
2282
+ }
2283
+ });
2284
+ }
2285
+ function createEventHandler(handler, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
2286
+ return handler ? (event) => {
2287
+ const proxyEvent = createEventProxy(
2288
+ event,
2289
+ currentTargetGetter,
2290
+ offsetXGetter,
2291
+ offsetYGetter,
2292
+ offsetZGetter,
2293
+ clientXGetter,
2294
+ clientYGetter,
2295
+ clientZGetter,
2296
+ translationXGetter,
2297
+ translationYGetter,
2298
+ translationZGetter,
2299
+ quaternionGetter,
2300
+ magnificationGetter
2301
+ );
2302
+ handler(proxyEvent);
2303
+ } : void 0;
2304
+ }
2305
+ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
2306
+ const onSpatialTap = createEventHandler(
2307
+ spatialEvents.onSpatialTap,
2308
+ currentTargetGetter,
2309
+ // offsetX/Y/Z come from local coordinates
2310
+ (ev) => ev.detail?.location3D?.x,
2311
+ (ev) => ev.detail?.location3D?.y,
2312
+ (ev) => ev.detail?.location3D?.z,
2313
+ // clientX/Y/Z come from global scene coordinates
2314
+ (ev) => ev.detail?.globalLocation3D?.x,
2315
+ (ev) => ev.detail?.globalLocation3D?.y,
2316
+ (ev) => ev.detail?.globalLocation3D?.z
2317
+ );
2318
+ const onSpatialDrag = createEventHandler(
2319
+ spatialEvents.onSpatialDrag,
2320
+ currentTargetGetter,
2321
+ void 0,
2322
+ void 0,
2323
+ void 0,
2324
+ void 0,
2325
+ void 0,
2326
+ void 0,
2327
+ (ev) => ev.detail?.translation3D?.x,
2328
+ (ev) => ev.detail?.translation3D?.y,
2329
+ (ev) => ev.detail?.translation3D?.z
2330
+ );
2331
+ const onSpatialDragEnd = createEventHandler(
2332
+ spatialEvents.onSpatialDragEnd,
2333
+ currentTargetGetter
2334
+ );
2335
+ const onSpatialRotate = createEventHandler(
2336
+ spatialEvents.onSpatialRotate,
2337
+ currentTargetGetter,
2338
+ void 0,
2339
+ void 0,
2340
+ void 0,
2341
+ void 0,
2342
+ void 0,
2343
+ void 0,
2344
+ void 0,
2345
+ void 0,
2346
+ void 0,
2347
+ (ev) => ev.detail?.quaternion
2348
+ );
2349
+ const onSpatialRotateEnd = createEventHandler(
2350
+ spatialEvents.onSpatialRotateEnd,
2351
+ currentTargetGetter
2352
+ );
2353
+ const onSpatialMagnify = createEventHandler(
2354
+ spatialEvents.onSpatialMagnify,
2355
+ currentTargetGetter,
2356
+ void 0,
2357
+ void 0,
2358
+ void 0,
2359
+ void 0,
2360
+ void 0,
2361
+ void 0,
2362
+ void 0,
2363
+ void 0,
2364
+ void 0,
2365
+ void 0,
2366
+ (ev) => ev.detail?.magnification
2367
+ );
2368
+ const onSpatialMagnifyEnd = createEventHandler(
2369
+ spatialEvents.onSpatialMagnifyEnd,
2370
+ currentTargetGetter
2371
+ );
2372
+ const onSpatialDragStart = createEventHandler(
2373
+ spatialEvents.onSpatialDragStart,
2374
+ currentTargetGetter,
2375
+ (ev) => ev.detail?.startLocation3D?.x,
2376
+ (ev) => ev.detail?.startLocation3D?.y,
2377
+ (ev) => ev.detail?.startLocation3D?.z,
2378
+ (ev) => ev.detail?.globalLocation3D?.x,
2379
+ (ev) => ev.detail?.globalLocation3D?.y,
2380
+ (ev) => ev.detail?.globalLocation3D?.z
2381
+ );
2382
+ return {
2383
+ onSpatialTap,
2384
+ onSpatialDragStart,
2385
+ onSpatialDrag,
2386
+ onSpatialDragEnd,
2387
+ onSpatialRotate,
2388
+ onSpatialRotateEnd,
2389
+ onSpatialMagnify,
2390
+ onSpatialMagnifyEnd
2391
+ };
2392
+ }
2393
+ function useSpatialEvents(spatialEvents, spatialContainerRefProxy) {
2394
+ return useSpatialEventsBase(
2395
+ spatialEvents,
2396
+ () => spatialContainerRefProxy.current?.domProxy
2397
+ );
2398
+ }
2399
+ function useSpatialEventsWhenSpatializedContainerExist(spatialEvents, spatialId, spatializedContainerObject) {
2400
+ return useSpatialEventsBase(spatialEvents, () => {
2401
+ const spatialContainerRefProxy = spatializedContainerObject.getSpatialContainerRefProxyBySpatialId(
2402
+ spatialId
2403
+ );
2404
+ return spatialContainerRefProxy?.domProxy;
2405
+ });
2406
+ }
2407
+
2408
+ // src/ssr/SSRContext.tsx
2409
+ import { createContext as createContext5, useState as useState4, useEffect as useEffect8 } from "react";
2410
+ import { jsx as jsx4 } from "react/jsx-runtime";
2411
+ var SSRContext = createContext5(false);
2412
+ var SSRProvider = ({
2413
+ isSSR: initialIsSSR = true,
2414
+ children
2415
+ }) => {
2416
+ const [isSSR, setIsSSR] = useState4(initialIsSSR);
2417
+ useEffect8(() => {
2418
+ if (isSSR) {
2419
+ setIsSSR(false);
2420
+ }
2421
+ }, []);
2422
+ return /* @__PURE__ */ jsx4(SSRContext.Provider, { value: isSSR, children });
2423
+ };
2424
+
2425
+ // src/ssr/withSSRSupported.tsx
2426
+ import { forwardRef as forwardRef3 } from "react";
2427
+
2428
+ // src/ssr/useSSRPhase.tsx
2429
+ import { useContext as useContext6, useState as useState5, useEffect as useEffect9 } from "react";
2430
+ function useSSRPhase() {
2431
+ const isSSRContext = useContext6(SSRContext);
2432
+ const isServer = typeof window === "undefined";
2433
+ const [hydrated, setHydrated] = useState5(false);
2434
+ useEffect9(() => setHydrated(true), []);
2435
+ if (isServer) {
2436
+ return "ssr";
2437
+ }
2438
+ if (isSSRContext) {
2439
+ return hydrated ? "after-hydrate" : "hydrate";
2440
+ } else {
2441
+ return "after-hydrate";
2442
+ }
2443
+ }
2444
+
2445
+ // src/ssr/withSSRSupported.tsx
2446
+ import { jsx as jsx5 } from "react/jsx-runtime";
2447
+ function withSSRSupported(Component) {
2448
+ const ClientOnlyComponent = (props, ref) => {
2449
+ const phase = useSSRPhase();
2450
+ let renderType = "real";
2451
+ if (phase === "ssr" || phase === "hydrate") {
2452
+ renderType = "fake";
2453
+ }
2454
+ if (renderType === "fake") {
2455
+ const { style, className } = props;
2456
+ return /* @__PURE__ */ jsx5("div", { style, className, ref });
2457
+ } else {
2458
+ return /* @__PURE__ */ jsx5(Component, { ...props, ref });
2459
+ }
2460
+ };
2461
+ ClientOnlyComponent.displayName = `withClientOnly(${Component.displayName || Component.name || "Component"})`;
2462
+ return forwardRef3(ClientOnlyComponent);
2463
+ }
2464
+
2465
+ // src/spatialized-container/SpatializedContainer.tsx
2466
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
2467
+ function DegradedContainer({
2468
+ innerRef,
2469
+ ...inprops
2470
+ }) {
2471
+ const {
2472
+ component: Component,
2473
+ children,
2474
+ ["enable-xr"]: _enableXR,
2475
+ onSpatialTap: _onSpatialTap,
2476
+ onSpatialDragStart: _onSpatialDragStart,
2477
+ onSpatialDrag: _onSpatialDrag,
2478
+ onSpatialDragEnd: _onSpatialDragEnd,
2479
+ onSpatialRotate: _onSpatialRotate,
2480
+ onSpatialRotateEnd: _onSpatialRotateEnd,
2481
+ onSpatialMagnify: _onSpatialMagnify,
2482
+ onSpatialMagnifyEnd: _onSpatialMagnifyEnd,
2483
+ spatializedContent: _content,
2484
+ createSpatializedElement: _create,
2485
+ getExtraSpatializedElementProperties: _getExtra,
2486
+ extraRefProps: _extraRef,
2487
+ sizingMode: _sizingMode,
2488
+ ...restProps
2489
+ } = inprops;
2490
+ return /* @__PURE__ */ jsx6(Component, { ref: innerRef, ...restProps, children });
2491
+ }
2492
+ function SpatializedContainerBase(inprops, ref) {
2493
+ const isWebSpatialEnv = getSession() !== null;
2494
+ const insideAttachment = useInsideAttachment();
2495
+ if (!isWebSpatialEnv || insideAttachment) {
2496
+ if (insideAttachment) {
2497
+ console.warn(
2498
+ `[WebSpatial] ${inprops.component || "Spatial element"} cannot be used inside AttachmentAsset. Rendering as plain HTML.`
2499
+ );
2500
+ }
2501
+ return /* @__PURE__ */ jsx6(DegradedContainer, { ...inprops, innerRef: ref });
2502
+ }
2503
+ const layer = useContext7(SpatialLayerContext) + 1;
2504
+ const rootSpatializedContainerObject = useContext7(
2505
+ SpatializedContainerContext
2506
+ );
2507
+ const inSpatializedContainer = !!rootSpatializedContainerObject;
2508
+ const portalInstanceObject = useContext7(PortalInstanceContext);
2509
+ const inPortalInstanceEnv = !!portalInstanceObject;
2510
+ const isInStandardInstance = !inPortalInstanceEnv;
2511
+ const spatialId = useMemo2(() => {
2512
+ return !inSpatializedContainer ? `root_container` : rootSpatializedContainerObject.getSpatialId(layer, isInStandardInstance);
2513
+ }, []);
2514
+ const spatialIdProps = {
2515
+ [SpatialID]: spatialId
2516
+ };
2517
+ const {
2518
+ onSpatialTap,
2519
+ onSpatialDragStart,
2520
+ onSpatialDrag,
2521
+ onSpatialDragEnd,
2522
+ onSpatialRotate,
2523
+ onSpatialRotateEnd,
2524
+ onSpatialMagnify,
2525
+ onSpatialMagnifyEnd,
2526
+ extraRefProps,
2527
+ ...props
2528
+ } = inprops;
2529
+ if (inSpatializedContainer) {
2530
+ if (inPortalInstanceEnv) {
2531
+ const spatialEvents = useSpatialEventsWhenSpatializedContainerExist(
2532
+ {
2533
+ onSpatialTap,
2534
+ onSpatialDragStart,
2535
+ onSpatialDrag,
2536
+ onSpatialDragEnd,
2537
+ onSpatialRotate,
2538
+ onSpatialRotateEnd,
2539
+ onSpatialMagnify,
2540
+ onSpatialMagnifyEnd
2541
+ },
2542
+ spatialId,
2543
+ rootSpatializedContainerObject
2544
+ );
2545
+ return /* @__PURE__ */ jsx6(SpatialLayerContext.Provider, { value: layer, children: /* @__PURE__ */ jsx6(
2546
+ PortalSpatializedContainer,
2547
+ {
2548
+ ...spatialIdProps,
2549
+ ...props,
2550
+ ...spatialEvents
2551
+ }
2552
+ ) });
2553
+ } else {
2554
+ const {
2555
+ transformVisibilityTaskContainerCallback,
2556
+ standardSpatializedContainerCallback,
2557
+ spatialContainerRefProxy
2558
+ } = useDomProxy(ref, extraRefProps);
2559
+ useEffect10(() => {
2560
+ rootSpatializedContainerObject.updateSpatialContainerRefProxyInfo(
2561
+ spatialId,
2562
+ spatialContainerRefProxy.current
2563
+ );
2564
+ }, [spatialContainerRefProxy.current]);
2565
+ const {
2566
+ spatializedContent,
2567
+ createSpatializedElement: createSpatializedElement2,
2568
+ getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
2569
+ ...restProps
2570
+ } = props;
2571
+ return /* @__PURE__ */ jsxs2(SpatialLayerContext.Provider, { value: layer, children: [
2572
+ /* @__PURE__ */ jsx6(
2573
+ StandardSpatializedContainer,
2574
+ {
2575
+ ref: standardSpatializedContainerCallback,
2576
+ ...spatialIdProps,
2577
+ ...restProps,
2578
+ inStandardSpatializedContainer: true
2579
+ }
2580
+ ),
2581
+ /* @__PURE__ */ jsx6(
2582
+ TransformVisibilityTaskContainer,
2583
+ {
2584
+ ref: transformVisibilityTaskContainerCallback,
2585
+ ...spatialIdProps,
2586
+ className: props.className,
2587
+ style: props.style
2588
+ }
2589
+ )
2590
+ ] });
2591
+ }
2592
+ } else {
2593
+ const {
2594
+ transformVisibilityTaskContainerCallback,
2595
+ standardSpatializedContainerCallback,
2596
+ spatialContainerRefProxy
2597
+ } = useDomProxy(ref, extraRefProps);
2598
+ const spatialEvents = useSpatialEvents(
2599
+ {
2600
+ onSpatialTap,
2601
+ onSpatialDragStart,
2602
+ onSpatialDrag,
2603
+ onSpatialDragEnd,
2604
+ onSpatialRotate,
2605
+ onSpatialRotateEnd,
2606
+ onSpatialMagnify,
2607
+ onSpatialMagnifyEnd
2608
+ },
2609
+ spatialContainerRefProxy
2610
+ );
2611
+ const spatializedContainerObject = useMemo2(
2612
+ () => new SpatializedContainerObject(),
2613
+ []
2614
+ );
2615
+ const {
2616
+ spatializedContent,
2617
+ createSpatializedElement: createSpatializedElement2,
2618
+ getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
2619
+ ...restProps
2620
+ } = props;
2621
+ return /* @__PURE__ */ jsx6(SpatialLayerContext.Provider, { value: layer, children: /* @__PURE__ */ jsxs2(
2622
+ SpatializedContainerContext.Provider,
2623
+ {
2624
+ value: spatializedContainerObject,
2625
+ children: [
2626
+ /* @__PURE__ */ jsx6(
2627
+ StandardSpatializedContainer,
2628
+ {
2629
+ ref: standardSpatializedContainerCallback,
2630
+ ...spatialIdProps,
2631
+ ...restProps,
2632
+ inStandardSpatializedContainer: false
2633
+ }
2634
+ ),
2635
+ /* @__PURE__ */ jsx6(
2636
+ PortalSpatializedContainer,
2637
+ {
2638
+ ...spatialIdProps,
2639
+ ...props,
2640
+ ...spatialEvents
2641
+ }
2642
+ ),
2643
+ /* @__PURE__ */ jsx6(
2644
+ TransformVisibilityTaskContainer,
2645
+ {
2646
+ ref: transformVisibilityTaskContainerCallback,
2647
+ ...spatialIdProps,
2648
+ className: props.className,
2649
+ style: props.style
2650
+ }
2651
+ )
2652
+ ]
2653
+ }
2654
+ ) });
2655
+ }
2656
+ }
2657
+ var SpatializedContainer = withSSRSupported(
2658
+ forwardRef4(SpatializedContainerBase)
2659
+ );
2660
+
2661
+ // src/spatialized-container/Spatialized2DElementContainer.tsx
2662
+ import { createPortal as createPortal2 } from "react-dom";
2663
+ import {
2664
+ forwardRef as forwardRef5,
2665
+ useContext as useContext8,
2666
+ useEffect as useEffect12
2667
+ } from "react";
2668
+
2669
+ // src/utils/windowStyleSync.ts
2670
+ function ensureWindowDocumentStructure(openedWindow) {
2671
+ try {
2672
+ const { document: document2 } = openedWindow;
2673
+ let documentElement = document2.documentElement;
2674
+ if (!documentElement) {
2675
+ documentElement = document2.createElement("html");
2676
+ document2.appendChild(documentElement);
2677
+ }
2678
+ let head = document2.head;
2679
+ if (!head) {
2680
+ head = document2.createElement("head");
2681
+ if (documentElement.firstChild) {
2682
+ documentElement.insertBefore(head, documentElement.firstChild);
2683
+ } else {
2684
+ documentElement.appendChild(head);
2685
+ }
2686
+ }
2687
+ let body = document2.body;
2688
+ if (!body) {
2689
+ body = document2.createElement("body");
2690
+ documentElement.appendChild(body);
2691
+ }
2692
+ return {
2693
+ document: document2,
2694
+ documentElement,
2695
+ head,
2696
+ body
2697
+ };
2698
+ } catch (error) {
2699
+ console.warn(
2700
+ "[WebSpatial] Failed to ensure child window document structure",
2701
+ error
2702
+ );
2703
+ return null;
2704
+ }
2705
+ }
2706
+ function asyncLoadStyleToChildWindow(childWindow, link, isCurrent) {
2707
+ return new Promise((resolve) => {
2708
+ const { href } = link;
2709
+ const sep = href.includes("?") ? "&" : "?";
2710
+ link.href = `${href}${sep}uniqueURL=${Math.random()}`;
2711
+ let finished = false;
2712
+ const finish = (ok) => {
2713
+ if (finished) return;
2714
+ finished = true;
2715
+ resolve(ok);
2716
+ };
2717
+ link.onerror = () => {
2718
+ finish(false);
2719
+ };
2720
+ link.onload = () => {
2721
+ if (!isCurrent()) {
2722
+ link.parentNode?.removeChild(link);
2723
+ finish(false);
2724
+ return;
2725
+ }
2726
+ finish(true);
2727
+ };
2728
+ setTimeout(() => {
2729
+ if (!isCurrent()) {
2730
+ finish(false);
2731
+ return;
2732
+ }
2733
+ const childDocument = ensureWindowDocumentStructure(childWindow);
2734
+ if (!childDocument) {
2735
+ finish(false);
2736
+ return;
2737
+ }
2738
+ childDocument.head.appendChild(link);
2739
+ }, 50);
2740
+ });
2741
+ }
2742
+ var WEBSPATIAL_SYNC_ATTR = "data-webspatial-sync";
2743
+ var WEBSPATIAL_SYNC_KEY_ATTR = "data-webspatial-sync-key";
2744
+ function setOpenWindowStyle(openedWindow) {
2745
+ const childDocument = ensureWindowDocumentStructure(openedWindow);
2746
+ if (!childDocument) return;
2747
+ childDocument.documentElement.style.cssText += document.documentElement.style.cssText;
2748
+ childDocument.documentElement.style.backgroundColor = "transparent";
2749
+ childDocument.body.style.margin = "0px";
2750
+ childDocument.body.style.display = "inline-block";
2751
+ childDocument.body.style.minWidth = "auto";
2752
+ childDocument.body.style.minHeight = "auto";
2753
+ childDocument.body.style.maxWidth = "fit-content";
2754
+ childDocument.body.style.minWidth = "fit-content";
2755
+ childDocument.body.style.background = "transparent";
2756
+ }
2757
+ var controllers = /* @__PURE__ */ new WeakMap();
2758
+ function getController(childWindow) {
2759
+ const prev = controllers.get(childWindow);
2760
+ if (prev) return prev;
2761
+ const next = { version: 0 };
2762
+ controllers.set(childWindow, next);
2763
+ return next;
2764
+ }
2765
+ async function syncParentHeadToChild(childWindow) {
2766
+ const controller = getController(childWindow);
2767
+ const version2 = ++controller.version;
2768
+ const styleLoadedPromises = [];
2769
+ const childDocument = ensureWindowDocumentStructure(childWindow);
2770
+ if (!childDocument) {
2771
+ return [];
2772
+ }
2773
+ const { head } = childDocument;
2774
+ const isCurrent = () => controller.version === version2;
2775
+ const parentStyles = Array.from(document.head.querySelectorAll("style"));
2776
+ const parentStylesheets = Array.from(
2777
+ document.head.querySelectorAll('link[rel="stylesheet"][href]')
2778
+ );
2779
+ const desiredStylesheetKeys = /* @__PURE__ */ new Set();
2780
+ for (const link of parentStylesheets) {
2781
+ if (link.href) desiredStylesheetKeys.add(link.href);
2782
+ }
2783
+ const existingSyncedLinks = Array.from(
2784
+ head.querySelectorAll(
2785
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
2786
+ )
2787
+ );
2788
+ for (const link of existingSyncedLinks) {
2789
+ const key = link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href;
2790
+ if (!desiredStylesheetKeys.has(key)) link.parentNode?.removeChild(link);
2791
+ }
2792
+ const prevSyncedStyles = head.querySelectorAll(
2793
+ `style[${WEBSPATIAL_SYNC_ATTR}="1"]`
2794
+ );
2795
+ prevSyncedStyles.forEach((n) => n.parentNode?.removeChild(n));
2796
+ for (const styleEl of parentStyles) {
2797
+ const node = styleEl.cloneNode(true);
2798
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
2799
+ head.appendChild(node);
2800
+ }
2801
+ const currentKeys = /* @__PURE__ */ new Set();
2802
+ const currentSyncedLinks = Array.from(
2803
+ head.querySelectorAll(
2804
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
2805
+ )
2806
+ );
2807
+ for (const link of currentSyncedLinks) {
2808
+ currentKeys.add(link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href);
2809
+ }
2810
+ for (const link of parentStylesheets) {
2811
+ const key = link.href;
2812
+ if (!key || currentKeys.has(key)) continue;
2813
+ const node = link.cloneNode(true);
2814
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
2815
+ node.setAttribute(WEBSPATIAL_SYNC_KEY_ATTR, key);
2816
+ styleLoadedPromises.push(
2817
+ asyncLoadStyleToChildWindow(childWindow, node, isCurrent)
2818
+ );
2819
+ }
2820
+ childDocument.documentElement.className = document.documentElement.className;
2821
+ return Promise.all(styleLoadedPromises);
2822
+ }
2823
+
2824
+ // src/utils/useSyncHeadStyles.ts
2825
+ import { useEffect as useEffect11 } from "react";
2826
+ function defaultShouldSync(mutations) {
2827
+ if (!Array.isArray(mutations) || mutations.length === 0) return false;
2828
+ for (const mutation of mutations) {
2829
+ const nodes = [
2830
+ ...Array.from(mutation.addedNodes),
2831
+ ...Array.from(mutation.removedNodes)
2832
+ ];
2833
+ for (const node of nodes) {
2834
+ if (!(node instanceof Element)) continue;
2835
+ const tag = node.tagName;
2836
+ if (tag === "STYLE") return true;
2837
+ if (tag === "LINK") {
2838
+ const { rel } = node;
2839
+ if (rel && rel.toLowerCase() === "stylesheet") return true;
2840
+ }
2841
+ }
2842
+ }
2843
+ return false;
2844
+ }
2845
+ function useSyncHeadStyles(childWindow, options) {
2846
+ const delayMs = 100;
2847
+ const subtree = options?.subtree ?? false;
2848
+ const immediate = options?.immediate ?? true;
2849
+ useEffect11(() => {
2850
+ if (!childWindow) return;
2851
+ let timer;
2852
+ const scheduleSync = () => {
2853
+ if (timer) window.clearTimeout(timer);
2854
+ timer = window.setTimeout(() => {
2855
+ syncParentHeadToChild(childWindow);
2856
+ }, delayMs);
2857
+ };
2858
+ if (immediate) scheduleSync();
2859
+ const observer = new MutationObserver((mutations) => {
2860
+ if (!defaultShouldSync(mutations)) return;
2861
+ scheduleSync();
2862
+ });
2863
+ observer.observe(document.head, { childList: true, subtree });
2864
+ return () => {
2865
+ if (timer) window.clearTimeout(timer);
2866
+ observer.disconnect();
2867
+ };
2868
+ }, [childWindow, delayMs, subtree, immediate]);
2869
+ }
2870
+
2871
+ // src/spatialized-container/Spatialized2DElementContainer.tsx
2872
+ import { jsx as jsx7 } from "react/jsx-runtime";
2873
+ function getJSXPortalInstance(inProps, portalInstanceObject) {
2874
+ const { component: El, style: inStyle = {}, ...props } = inProps;
2875
+ const extraStyle = {
2876
+ visibility: "visible",
2877
+ position: "relative",
2878
+ top: "0px",
2879
+ left: "0px",
2880
+ margin: "0px",
2881
+ marginLeft: "0px",
2882
+ marginRight: "0px",
2883
+ marginTop: "0px",
2884
+ marginBottom: "0px",
2885
+ borderRadius: "0px",
2886
+ // overflow: '',
2887
+ transform: "none"
2888
+ };
2889
+ const computedStyle = portalInstanceObject.computedStyle;
2890
+ const inheritedPortalStyle = getInheritedStyleProps(computedStyle);
2891
+ const style = {
2892
+ ...inStyle,
2893
+ ...inheritedPortalStyle,
2894
+ ...extraStyle
2895
+ };
2896
+ return /* @__PURE__ */ jsx7(El, { style, ...props });
2897
+ }
2898
+ function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
2899
+ useEffect12(() => {
2900
+ const childDocument = ensureWindowDocumentStructure(windowProxy);
2901
+ if (!childDocument) return;
2902
+ childDocument.document.title = name;
2903
+ spatializedElement.updateProperties({
2904
+ name
2905
+ });
2906
+ }, [name]);
2907
+ }
2908
+ function SpatializedContent(props) {
2909
+ const { spatializedElement, ...restProps } = props;
2910
+ const spatialized2DElement = spatializedElement;
2911
+ const { windowProxy } = spatialized2DElement;
2912
+ const isAndroidBitmapMode = usesAndroidBitmapCapture();
2913
+ useSyncHeadStyles(isAndroidBitmapMode ? null : windowProxy, {
2914
+ subtree: false
2915
+ });
2916
+ const name = restProps["data-name"] || "";
2917
+ useSyncDocumentTitle(windowProxy, spatialized2DElement, name);
2918
+ const portalInstanceObject = useContext8(
2919
+ PortalInstanceContext
2920
+ );
2921
+ if (isAndroidBitmapMode) {
2922
+ return null;
2923
+ }
2924
+ const childDocument = ensureWindowDocumentStructure(windowProxy);
2925
+ if (!childDocument?.body) {
2926
+ return null;
2927
+ }
2928
+ const JSXPortalInstance = getJSXPortalInstance(
2929
+ restProps,
2930
+ portalInstanceObject
2931
+ );
2932
+ return createPortal2(JSXPortalInstance, childDocument.body);
2933
+ }
2934
+ function getExtraSpatializedElementProperties(computedStyle) {
2935
+ const overflow = computedStyle.getPropertyValue("overflow");
2936
+ const scrollPageEnabled = ["visible", "hidden", "clip"].indexOf(overflow) >= 0;
2937
+ const material = computedStyle.getPropertyValue(
2938
+ SpatialCustomStyleVars.backgroundMaterial
2939
+ );
2940
+ const properties = {};
2941
+ properties.scrollPageEnabled = scrollPageEnabled;
2942
+ properties.cornerRadius = parseCornerRadius(computedStyle);
2943
+ if (material) {
2944
+ properties.material = material;
2945
+ }
2946
+ return properties;
2947
+ }
2948
+ async function createSpatializedElement() {
2949
+ const spatializedElement = await getSession().createSpatialized2DElement();
2950
+ const windowProxy = spatializedElement.windowProxy;
2951
+ if (usesAndroidBitmapCapture()) {
2952
+ console.log(
2953
+ "[WebSpatial] Android: Skipping WindowProxy setup, using bitmap capture"
2954
+ );
2955
+ return spatializedElement;
2956
+ }
2957
+ setOpenWindowStyle(windowProxy);
2958
+ await syncParentHeadToChild(windowProxy);
2959
+ const childDocument = ensureWindowDocumentStructure(windowProxy);
2960
+ if (!childDocument) {
2961
+ return spatializedElement;
2962
+ }
2963
+ const viewport = childDocument.document.querySelector('meta[name="viewport"]');
2964
+ if (viewport) {
2965
+ viewport?.setAttribute(
2966
+ "content",
2967
+ " initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
2968
+ );
2969
+ } else {
2970
+ const meta = childDocument.document.createElement("meta");
2971
+ meta.name = "viewport";
2972
+ meta.content = "initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
2973
+ childDocument.head.appendChild(meta);
2974
+ }
2975
+ return spatializedElement;
2976
+ }
2977
+ function Spatialized2DElementContainerBase(props, ref) {
2978
+ return /* @__PURE__ */ jsx7(
2979
+ SpatializedContainer,
2980
+ {
2981
+ ref,
2982
+ createSpatializedElement,
2983
+ getExtraSpatializedElementProperties,
2984
+ spatializedContent: SpatializedContent,
2985
+ ...props
2986
+ }
2987
+ );
2988
+ }
2989
+ var Spatialized2DElementContainer = forwardRef5(
2990
+ Spatialized2DElementContainerBase
2991
+ );
2992
+
2993
+ // src/spatialized-container/SpatializedStatic3DElementContainer.tsx
2994
+ import {
2995
+ forwardRef as forwardRef6,
2996
+ useCallback as useCallback6,
2997
+ useContext as useContext9,
2998
+ useEffect as useEffect13,
2999
+ useMemo as useMemo3,
3000
+ useRef as useRef4
3001
+ } from "react";
3002
+ import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
3003
+ function getAbsoluteURL(url) {
3004
+ if (!url) {
3005
+ return "";
3006
+ }
3007
+ try {
3008
+ return new URL(url, document.baseURI).toString();
3009
+ } catch {
3010
+ return url;
3011
+ }
3012
+ }
3013
+ function createLoadEvent(type, targetGetter) {
3014
+ const event = new CustomEvent(type, {
3015
+ bubbles: false,
3016
+ cancelable: false
3017
+ });
3018
+ const proxyEvent = new Proxy(event, {
3019
+ get(target, prop) {
3020
+ if (prop === "target") {
3021
+ return targetGetter();
3022
+ }
3023
+ return Reflect.get(target, prop);
3024
+ }
3025
+ });
3026
+ return proxyEvent;
3027
+ }
3028
+ function createLoadFailureEvent(targetGetter) {
3029
+ return createLoadEvent("modelloadfailed", targetGetter);
3030
+ }
3031
+ function createLoadSuccessEvent(targetGetter) {
3032
+ return createLoadEvent("modelloaded", targetGetter);
3033
+ }
3034
+ function SpatializedContent2(props) {
3035
+ const { src, spatializedElement, onLoad, onError } = props;
3036
+ const spatializedStatic3DElement = spatializedElement;
3037
+ const portalInstanceObject = useContext9(
3038
+ PortalInstanceContext
3039
+ );
3040
+ const currentSrc = useMemo3(() => getAbsoluteURL(src), [src]);
3041
+ useEffect13(() => {
3042
+ if (src) {
3043
+ spatializedStatic3DElement.updateProperties({ modelURL: currentSrc });
3044
+ }
3045
+ }, [currentSrc]);
3046
+ useEffect13(() => {
3047
+ if (onLoad) {
3048
+ spatializedStatic3DElement.onLoadCallback = () => {
3049
+ onLoad(
3050
+ createLoadSuccessEvent(
3051
+ () => portalInstanceObject.dom.__targetProxy
3052
+ )
3053
+ );
3054
+ };
3055
+ } else {
3056
+ spatializedStatic3DElement.onLoadCallback = void 0;
3057
+ }
3058
+ }, [onLoad]);
3059
+ useEffect13(() => {
3060
+ if (onError) {
3061
+ spatializedStatic3DElement.onLoadFailureCallback = () => {
3062
+ onError(
3063
+ createLoadFailureEvent(
3064
+ () => portalInstanceObject.dom.__targetProxy
3065
+ )
3066
+ );
3067
+ };
3068
+ } else {
3069
+ spatializedStatic3DElement.onLoadFailureCallback = void 0;
3070
+ }
3071
+ }, [onError]);
3072
+ return /* @__PURE__ */ jsx8(Fragment2, {});
3073
+ }
3074
+ function SpatializedStatic3DElementContainerBase(props, ref) {
3075
+ const promiseRef = useRef4(null);
3076
+ const createSpatializedElement2 = useCallback6(() => {
3077
+ const url = getAbsoluteURL(props.src);
3078
+ promiseRef.current = getSession().createSpatializedStatic3DElement(url);
3079
+ return promiseRef.current;
3080
+ }, []);
3081
+ const extraRefProps = useCallback6(
3082
+ (domProxy) => {
3083
+ let modelTransform = new DOMMatrixReadOnly();
3084
+ return {
3085
+ get currentSrc() {
3086
+ return getAbsoluteURL(props.src);
3087
+ },
3088
+ get ready() {
3089
+ return promiseRef.current.then((spatializedElement) => spatializedElement.ready).then((success) => {
3090
+ if (success) return createLoadSuccessEvent(() => domProxy);
3091
+ throw createLoadFailureEvent(() => domProxy);
3092
+ });
3093
+ },
3094
+ get entityTransform() {
3095
+ return modelTransform;
3096
+ },
3097
+ set entityTransform(value) {
3098
+ modelTransform = value;
3099
+ const spatializedElement = domProxy.__spatializedElement;
3100
+ spatializedElement?.updateModelTransform(modelTransform);
3101
+ }
3102
+ };
3103
+ },
3104
+ []
3105
+ );
3106
+ return /* @__PURE__ */ jsx8(
3107
+ SpatializedContainer,
3108
+ {
3109
+ ref,
3110
+ component: "div",
3111
+ createSpatializedElement: createSpatializedElement2,
3112
+ spatializedContent: SpatializedContent2,
3113
+ extraRefProps,
3114
+ ...props
3115
+ }
3116
+ );
3117
+ }
3118
+ var SpatializedStatic3DElementContainer = forwardRef6(
3119
+ SpatializedStatic3DElementContainerBase
3120
+ );
3121
+
3122
+ // src/spatialized-container/Spatialized2DElementContainerFactory.tsx
3123
+ import { forwardRef as forwardRef7 } from "react";
3124
+ import { jsx as jsx9 } from "react/jsx-runtime";
3125
+ var CachedSpatialized2DElementContainerType = /* @__PURE__ */ new Map();
3126
+ function withSpatialized2DElementContainer(Component) {
3127
+ if (CachedSpatialized2DElementContainerType.has(Component)) {
3128
+ return CachedSpatialized2DElementContainerType.get(Component);
3129
+ } else {
3130
+ const TypedSpatialized2DElementContainer = forwardRef7(
3131
+ (givenProps, ref) => {
3132
+ const { component: ignoreComponent, ...props } = givenProps;
3133
+ return /* @__PURE__ */ jsx9(
3134
+ Spatialized2DElementContainer,
3135
+ {
3136
+ component: Component,
3137
+ ...props,
3138
+ ref
3139
+ }
3140
+ );
3141
+ }
3142
+ );
3143
+ CachedSpatialized2DElementContainerType.set(
3144
+ Component,
3145
+ TypedSpatialized2DElementContainer
3146
+ );
3147
+ CachedSpatialized2DElementContainerType.set(
3148
+ TypedSpatialized2DElementContainer,
3149
+ TypedSpatialized2DElementContainer
3150
+ );
3151
+ return TypedSpatialized2DElementContainer;
3152
+ }
3153
+ }
3154
+
3155
+ // src/spatialized-container/index.ts
3156
+ function initPolyfill() {
3157
+ hijackGetComputedStyle();
3158
+ injectSpatialDefaultStyle();
3159
+ initCSSParserDivContainer();
3160
+ }
3161
+
3162
+ // src/initScene.web.ts
3163
+ function initScene(name, callback) {
3164
+ return;
3165
+ }
3166
+
3167
+ // src/spatialized-container-monitor/withSpatialMonitor.tsx
3168
+ import { forwardRef as forwardRef9 } from "react";
3169
+
3170
+ // src/spatialized-container-monitor/useMonitorDomChange.tsx
3171
+ import { useRef as useRef5, useEffect as useEffect14, useMemo as useMemo4 } from "react";
3172
+ function useMonitorDomChange(inRef) {
3173
+ const ref = useRef5(null);
3174
+ useEffect14(() => {
3175
+ const observer = new MutationObserver((mutationsList) => {
3176
+ notifyDOMUpdate(mutationsList);
3177
+ });
3178
+ const config = {
3179
+ childList: true,
3180
+ subtree: true,
3181
+ attributes: true,
3182
+ attributeFilter: ["style", "class"]
3183
+ };
3184
+ ref.current && observer.observe(ref.current, config);
3185
+ return () => {
3186
+ observer.disconnect();
3187
+ };
3188
+ }, []);
3189
+ const proxyRef = useMemo4(
3190
+ () => new Proxy(ref, {
3191
+ set: function(target, key, value) {
3192
+ if (key === "current") {
3193
+ if (inRef) {
3194
+ if (typeof inRef === "function") {
3195
+ inRef(value);
3196
+ } else if (inRef) {
3197
+ inRef.current = value;
3198
+ }
3199
+ }
3200
+ }
3201
+ return Reflect.set(target, key, value);
3202
+ }
3203
+ }),
3204
+ []
3205
+ );
3206
+ return proxyRef;
3207
+ }
3208
+
3209
+ // src/spatialized-container-monitor/useMonitorDocumentHeaderChange.tsx
3210
+ import { useEffect as useEffect15 } from "react";
3211
+ function useMonitorDocumentHeaderChange() {
3212
+ useEffect15(() => {
3213
+ const observer = new MutationObserver((mutationsList) => {
3214
+ notifyUpdateStandInstanceLayout();
3215
+ });
3216
+ const config = {
3217
+ childList: true,
3218
+ subtree: true,
3219
+ attributes: true
3220
+ };
3221
+ observer.observe(document.head, config);
3222
+ return () => {
3223
+ observer.disconnect();
3224
+ };
3225
+ }, []);
3226
+ }
3227
+
3228
+ // src/spatialized-container-monitor/SpatialMonitor.tsx
3229
+ import { forwardRef as forwardRef8 } from "react";
3230
+ import { jsx as jsx10 } from "react/jsx-runtime";
3231
+ function SpatialMonitorBase(inProps, inRef) {
3232
+ const { El = "div", ...props } = inProps;
3233
+ const ref = useMonitorDomChange(inRef);
3234
+ useMonitorDocumentHeaderChange();
3235
+ return /* @__PURE__ */ jsx10(El, { ...props, ref });
3236
+ }
3237
+ var SpatialMonitor = forwardRef8(SpatialMonitorBase);
3238
+
3239
+ // src/spatialized-container-monitor/withSpatialMonitor.tsx
3240
+ import { jsx as jsx11 } from "react/jsx-runtime";
3241
+ var cachedWithSpatialMonitorType = /* @__PURE__ */ new Map();
3242
+ function withSpatialMonitor(El) {
3243
+ if (cachedWithSpatialMonitorType.has(El)) {
3244
+ return cachedWithSpatialMonitorType.get(El);
3245
+ } else {
3246
+ const WithSpatialMonitorComponent = forwardRef9(
3247
+ (givenProps, givenRef) => {
3248
+ const {
3249
+ El: _,
3250
+ ...props
3251
+ } = givenProps;
3252
+ return /* @__PURE__ */ jsx11(SpatialMonitor, { El, ...props, ref: givenRef });
3253
+ }
3254
+ );
3255
+ WithSpatialMonitorComponent.displayName = `WithSpatialMonitor(${typeof El === "string" ? El : El.displayName || El.name})`;
3256
+ cachedWithSpatialMonitorType.set(El, WithSpatialMonitorComponent);
3257
+ cachedWithSpatialMonitorType.set(
3258
+ cachedWithSpatialMonitorType,
3259
+ cachedWithSpatialMonitorType
3260
+ );
3261
+ return WithSpatialMonitorComponent;
3262
+ }
3263
+ }
3264
+
3265
+ // src/reality/components/Entity.tsx
3266
+ import { forwardRef as forwardRef11 } from "react";
3267
+
3268
+ // src/reality/components/BaseEntity.tsx
3269
+ import { forwardRef as forwardRef10 } from "react";
3270
+
3271
+ // src/reality/context/RealityContext.tsx
3272
+ import { createContext as createContext6, useContext as useContext10 } from "react";
3273
+ var RealityContext = createContext6(null);
3274
+ var useRealityContext = () => useContext10(RealityContext);
3275
+
3276
+ // src/reality/context/ParentContext.tsx
3277
+ import { createContext as createContext7, useContext as useContext11 } from "react";
3278
+ var ParentContext = createContext7(null);
3279
+ var useParentContext = () => useContext11(ParentContext);
3280
+
3281
+ // src/reality/context/AttachmentContext.tsx
3282
+ import { createContext as createContext8, useContext as useContext12 } from "react";
3283
+ var AttachmentRegistry = class {
3284
+ // name → (instanceId → container)
3285
+ containers = /* @__PURE__ */ new Map();
3286
+ listeners = /* @__PURE__ */ new Map();
3287
+ addContainer(name, instanceId, container) {
3288
+ if (!this.containers.has(name)) {
3289
+ this.containers.set(name, /* @__PURE__ */ new Map());
3290
+ }
3291
+ this.containers.get(name).set(instanceId, container);
3292
+ this.notifyListeners(name);
3293
+ }
3294
+ removeContainer(name, instanceId) {
3295
+ this.containers.get(name)?.delete(instanceId);
3296
+ if (this.containers.get(name)?.size === 0) {
3297
+ this.containers.delete(name);
3298
+ }
3299
+ this.notifyListeners(name);
3300
+ }
3301
+ getContainers(name) {
3302
+ const map = this.containers.get(name);
3303
+ return map ? Array.from(map.values()) : [];
3304
+ }
3305
+ onContainersChange(name, cb) {
3306
+ const current = this.getContainers(name);
3307
+ if (current.length > 0) {
3308
+ cb(current);
3309
+ }
3310
+ const prev = this.listeners.get(name);
3311
+ if (prev) prev([]);
3312
+ this.listeners.set(name, cb);
3313
+ return () => {
3314
+ if (this.listeners.get(name) === cb) {
3315
+ this.listeners.delete(name);
3316
+ }
3317
+ };
3318
+ }
3319
+ notifyListeners(name) {
3320
+ const cs = this.getContainers(name);
3321
+ this.listeners.get(name)?.(cs);
3322
+ }
3323
+ destroy() {
3324
+ this.containers.clear();
3325
+ this.listeners.clear();
3326
+ }
3327
+ };
3328
+ var AttachmentContext = createContext8(null);
3329
+
3330
+ // src/reality/hooks/useEntityTransform.tsx
3331
+ import { useEffect as useEffect16, useRef as useRef6 } from "react";
3332
+
3333
+ // src/reality/utils/ResourceRegistry.ts
3334
+ var ResourceRegistry = class {
3335
+ resources = /* @__PURE__ */ new Map();
3336
+ add(id, resource) {
3337
+ this.resources.set(id, resource);
3338
+ }
3339
+ remove(id) {
3340
+ this.resources.delete(id);
3341
+ }
3342
+ // Remove the resource by id and destroy it once resolved
3343
+ // This does not cancel in-flight creation; it schedules destruction after resolution
3344
+ removeAndDestroy(id) {
3345
+ const p = this.resources.get(id);
3346
+ if (p) {
3347
+ p.then((spatialObj) => spatialObj.destroy()).catch(() => {
3348
+ });
3349
+ }
3350
+ this.resources.delete(id);
3351
+ }
3352
+ get(id) {
3353
+ return this.resources.get(id);
3354
+ }
3355
+ destroy() {
3356
+ const pending = Array.from(this.resources.values());
3357
+ this.resources.clear();
3358
+ pending.forEach(
3359
+ (promise) => promise.then((spatialObj) => spatialObj.destroy()).catch(() => {
3360
+ })
3361
+ );
3362
+ }
3363
+ };
3364
+
3365
+ // src/reality/utils/equal.ts
3366
+ function shallowEqualVec3(a, b) {
3367
+ if (a === b) return true;
3368
+ if (!a || !b) return false;
3369
+ return a.x === b.x && a.y === b.y && a.z === b.z;
3370
+ }
3371
+ function shallowEqualRotation(a, b) {
3372
+ if (a === b) return true;
3373
+ if (!a || !b) return false;
3374
+ return a.x === b.x && a.y === b.y && a.z === b.z && ("w" in a ? a.w === b.w : true);
3375
+ }
3376
+
3377
+ // src/reality/utils/AbortResourceManager.ts
3378
+ var AbortResourceManager = class {
3379
+ constructor(signal) {
3380
+ this.signal = signal;
3381
+ signal.addEventListener("abort", () => {
3382
+ this.aborted = true;
3383
+ void this.dispose();
3384
+ });
3385
+ }
3386
+ resources = [];
3387
+ aborted = false;
3388
+ async addResource(factory) {
3389
+ if (this.aborted) throw new DOMException("Aborted", "AbortError");
3390
+ const resource = await factory();
3391
+ if (this.aborted) {
3392
+ await resource.destroy();
3393
+ throw new DOMException("Aborted", "AbortError");
3394
+ }
3395
+ this.resources.push(resource);
3396
+ return resource;
3397
+ }
3398
+ async dispose() {
3399
+ const resources = this.resources.splice(0);
3400
+ for (const r of resources) {
3401
+ try {
3402
+ await r.destroy();
3403
+ } catch (e) {
3404
+ console.error("AbortResourceManager dispose error:", e, r);
3405
+ }
3406
+ }
3407
+ }
3408
+ };
3409
+
3410
+ // src/reality/hooks/useEntityTransform.tsx
3411
+ function useEntityTransform(entity, { position, rotation, scale }) {
3412
+ const last = useRef6({});
3413
+ useEffect16(() => {
3414
+ if (!entity) return;
3415
+ const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
3416
+ if (!shouldUpdate) return;
3417
+ last.current = { position, rotation, scale };
3418
+ const updateTransform = async () => {
3419
+ try {
3420
+ await entity.updateTransform({ position, rotation, scale });
3421
+ } catch (err) {
3422
+ console.error("[useEntityTransform] Failed to update transform:", err);
3423
+ }
3424
+ };
3425
+ updateTransform();
3426
+ return () => {
3427
+ };
3428
+ }, [entity, position, rotation, scale]);
3429
+ }
3430
+
3431
+ // src/reality/hooks/useEntityEvent.tsx
3432
+ import { useEffect as useEffect18, useRef as useRef8 } from "react";
3433
+
3434
+ // src/reality/type.ts
3435
+ var eventMap = {
3436
+ // tap
3437
+ onSpatialTap: "spatialtap",
3438
+ // drag
3439
+ onSpatialDragStart: "spatialdragstart",
3440
+ onSpatialDrag: "spatialdrag",
3441
+ onSpatialDragEnd: "spatialdragend",
3442
+ // rotate
3443
+ onSpatialRotateStart: "spatialrotatestart",
3444
+ onSpatialRotate: "spatialrotate",
3445
+ onSpatialRotateEnd: "spatialrotateend",
3446
+ // magnify
3447
+ onSpatialMagnifyStart: "spatialmagnifystart",
3448
+ onSpatialMagnify: "spatialmagnify",
3449
+ onSpatialMagnifyEnd: "spatialmagnifyend"
3450
+ };
3451
+
3452
+ // src/reality/hooks/useEntityRef.tsx
3453
+ import { useImperativeHandle } from "react";
3454
+ var useEntityRef = (ref, instance) => {
3455
+ useImperativeHandle(ref, () => instance);
3456
+ };
3457
+ var EntityRef = class {
3458
+ _entity;
3459
+ _ctx;
3460
+ constructor(entity = null, ctx = null) {
3461
+ this._entity = entity;
3462
+ this._ctx = ctx;
3463
+ }
3464
+ updateEntity(entity) {
3465
+ if (entity) this._entity = entity;
3466
+ }
3467
+ updateCtx(ctx) {
3468
+ if (ctx) this._ctx = ctx;
3469
+ }
3470
+ destroy() {
3471
+ this._entity?.destroy();
3472
+ }
3473
+ get entity() {
3474
+ return this._entity;
3475
+ }
3476
+ get id() {
3477
+ return this._entity?.userData?.id;
3478
+ }
3479
+ get name() {
3480
+ return this._entity?.userData?.name;
3481
+ }
3482
+ async convertFromEntityToEntity(fromEntityId, toEntityId, position) {
3483
+ if (!this._entity) return position;
3484
+ try {
3485
+ const fromEnt = await this._ctx?.resourceRegistry.get(fromEntityId);
3486
+ const toEnt = await this._ctx?.resourceRegistry.get(toEntityId);
3487
+ if (!fromEnt || !toEnt) return position;
3488
+ const ret = await this._entity.convertFromEntityToEntity(
3489
+ fromEnt.id,
3490
+ toEnt.id,
3491
+ position
3492
+ );
3493
+ return ret?.data ?? position;
3494
+ } catch {
3495
+ return position;
3496
+ }
3497
+ }
3498
+ async convertFromEntityToReality(entityId, position) {
3499
+ if (!this._entity) return position;
3500
+ try {
3501
+ const ent = await this._ctx?.resourceRegistry.get(entityId);
3502
+ if (!ent) return position;
3503
+ const ret = await this._entity.convertFromEntityToScene(ent.id, position);
3504
+ return ret?.data ?? position;
3505
+ } catch {
3506
+ return position;
3507
+ }
3508
+ }
3509
+ async convertFromRealityToEntity(entityId, position) {
3510
+ if (!this._entity) return position;
3511
+ try {
3512
+ const ent = await this._ctx?.resourceRegistry.get(entityId);
3513
+ if (!ent) return position;
3514
+ const ret = await this._entity.convertFromSceneToEntity(ent.id, position);
3515
+ return ret?.data ?? position;
3516
+ } catch {
3517
+ return position;
3518
+ }
3519
+ }
3520
+ };
3521
+
3522
+ // src/reality/hooks/useEntityEvent.tsx
3523
+ function createEventProxy2(ev, instance) {
3524
+ return new Proxy(ev, {
3525
+ get(target, prop) {
3526
+ if (prop === "currentTarget") {
3527
+ return instance;
3528
+ }
3529
+ if (prop === "target") {
3530
+ const origin = target.__origin;
3531
+ if (origin) {
3532
+ return new EntityRef(origin, null);
3533
+ }
3534
+ return instance;
3535
+ }
3536
+ if (prop === "bubbles") {
3537
+ return true;
3538
+ }
3539
+ if (prop === "offsetX") {
3540
+ const type = target.type;
3541
+ if (type === "spatialtap") {
3542
+ return target.detail?.location3D?.x ?? 0;
3543
+ }
3544
+ if (type === "spatialdragstart") {
3545
+ return target.detail?.startLocation3D?.x ?? 0;
3546
+ }
3547
+ return void 0;
3548
+ }
3549
+ if (prop === "offsetY") {
3550
+ const type = target.type;
3551
+ if (type === "spatialtap") {
3552
+ return target.detail?.location3D?.y ?? 0;
3553
+ }
3554
+ if (type === "spatialdragstart") {
3555
+ return target.detail?.startLocation3D?.y ?? 0;
3556
+ }
3557
+ return void 0;
3558
+ }
3559
+ if (prop === "offsetZ") {
3560
+ const type = target.type;
3561
+ if (type === "spatialtap") {
3562
+ return target.detail?.location3D?.z ?? 0;
3563
+ }
3564
+ if (type === "spatialdragstart") {
3565
+ return target.detail?.startLocation3D?.z ?? 0;
3566
+ }
3567
+ return void 0;
3568
+ }
3569
+ if (prop === "translationX") {
3570
+ const type = target.type;
3571
+ if (type === "spatialdrag") {
3572
+ return target.detail?.translation3D?.x ?? 0;
3573
+ }
3574
+ return void 0;
3575
+ }
3576
+ if (prop === "translationY") {
3577
+ const type = target.type;
3578
+ if (type === "spatialdrag") {
3579
+ return target.detail?.translation3D?.y ?? 0;
3580
+ }
3581
+ return void 0;
3582
+ }
3583
+ if (prop === "translationZ") {
3584
+ const type = target.type;
3585
+ if (type === "spatialdrag") {
3586
+ return target.detail?.translation3D?.z ?? 0;
3587
+ }
3588
+ return void 0;
3589
+ }
3590
+ if (prop === "quaternion") {
3591
+ const type = target.type;
3592
+ if (type === "spatialrotate") {
3593
+ return target.detail?.quaternion ?? {
3594
+ x: 0,
3595
+ y: 0,
3596
+ z: 0,
3597
+ w: 1
3598
+ };
3599
+ }
3600
+ return void 0;
3601
+ }
3602
+ if (prop === "magnification") {
3603
+ const type = target.type;
3604
+ if (type === "spatialmagnify") {
3605
+ return target.detail?.magnification ?? 1;
3606
+ }
3607
+ return void 0;
3608
+ }
3609
+ if (prop === "clientX") {
3610
+ const type = target.type;
3611
+ if (type === "spatialtap" || type === "spatialdragstart") {
3612
+ return target.detail?.globalLocation3D?.x ?? 0;
3613
+ }
3614
+ return void 0;
3615
+ }
3616
+ if (prop === "clientY") {
3617
+ const type = target.type;
3618
+ if (type === "spatialtap" || type === "spatialdragstart") {
3619
+ return target.detail?.globalLocation3D?.y ?? 0;
3620
+ }
3621
+ return void 0;
3622
+ }
3623
+ if (prop === "clientZ") {
3624
+ const type = target.type;
3625
+ if (type === "spatialtap" || type === "spatialdragstart") {
3626
+ return target.detail?.globalLocation3D?.z ?? 0;
3627
+ }
3628
+ return void 0;
3629
+ }
3630
+ const val = target[prop];
3631
+ return typeof val === "function" ? val.bind(target) : val;
3632
+ }
3633
+ });
3634
+ }
3635
+ var useEntityEvent = ({ instance, ...handlers }) => {
3636
+ const eventsSetRef = useRef8(/* @__PURE__ */ new Set());
3637
+ useEffect18(() => {
3638
+ const entity = instance.entity;
3639
+ if (!entity) return;
3640
+ Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
3641
+ const handlerFn = handlers[reactKey];
3642
+ if (!handlerFn) return;
3643
+ const wrapped = (ev) => handlerFn(createEventProxy2(ev, instance));
3644
+ entity.addEvent(spatialEvent, wrapped);
3645
+ eventsSetRef.current.add(reactKey);
3646
+ });
3647
+ return () => {
3648
+ };
3649
+ }, [instance.entity, ...Object.values(handlers)]);
3650
+ useEffect18(() => {
3651
+ const entity = instance.entity;
3652
+ if (!entity) return;
3653
+ return () => {
3654
+ for (let x of eventsSetRef.current) {
3655
+ entity.removeEvent(x);
3656
+ }
3657
+ eventsSetRef.current.clear();
3658
+ };
3659
+ }, [instance.entity]);
3660
+ return null;
3661
+ };
3662
+
3663
+ // src/reality/hooks/useEntityId.tsx
3664
+ import { useEffect as useEffect19 } from "react";
3665
+ var useEntityId = ({ id, entity }) => {
3666
+ const ctx = useRealityContext();
3667
+ useEffect19(() => {
3668
+ if (!id || !entity || !ctx) return;
3669
+ ctx.resourceRegistry.add(id, Promise.resolve(entity));
3670
+ return () => {
3671
+ ctx.resourceRegistry.remove(id);
3672
+ };
3673
+ }, [id, entity, ctx]);
3674
+ return null;
3675
+ };
3676
+
3677
+ // src/reality/hooks/useEntity.tsx
3678
+ import { useEffect as useEffect20, useRef as useRef9 } from "react";
3679
+ var useEntity = ({
3680
+ ref,
3681
+ id,
3682
+ position,
3683
+ rotation,
3684
+ scale,
3685
+ onSpatialTap,
3686
+ onSpatialDragStart,
3687
+ onSpatialDrag,
3688
+ onSpatialDragEnd,
3689
+ // onSpatialRotateStart,
3690
+ onSpatialRotate,
3691
+ onSpatialRotateEnd,
3692
+ // onSpatialMagnifyStart,
3693
+ onSpatialMagnify,
3694
+ onSpatialMagnifyEnd,
3695
+ // TODO: add other event handlers
3696
+ createEntity
3697
+ }) => {
3698
+ const ctx = useRealityContext();
3699
+ const parent = useParentContext();
3700
+ const instanceRef = useRef9(new EntityRef(null, ctx));
3701
+ const forceUpdate = useForceUpdate2();
3702
+ useEffect20(() => {
3703
+ if (!ctx) return;
3704
+ const controller = new AbortController();
3705
+ const init = async () => {
3706
+ try {
3707
+ const ent = await createEntity(controller.signal);
3708
+ if (!ent) return;
3709
+ if (controller.signal.aborted) {
3710
+ ent.destroy();
3711
+ return;
3712
+ }
3713
+ if (parent) {
3714
+ const result = await parent.addEntity(ent);
3715
+ if (!result.success) throw new Error("parent.addEntity failed");
3716
+ } else {
3717
+ const result = await ctx.reality.addEntity(ent);
3718
+ if (!result.success) throw new Error("ctx.reality.addEntity failed");
3719
+ }
3720
+ instanceRef.current?.updateEntity(ent);
3721
+ forceUpdate();
3722
+ } catch (error) {
3723
+ console.error("useEntity init ~ error:", error);
3724
+ }
3725
+ };
3726
+ init();
3727
+ return () => {
3728
+ controller.abort();
3729
+ instanceRef.current?.destroy();
3730
+ };
3731
+ }, [ctx, parent]);
3732
+ useEntityId({ id, entity: instanceRef.current.entity });
3733
+ useEntityTransform(instanceRef.current.entity, { position, rotation, scale });
3734
+ useEntityRef(ref, instanceRef.current);
3735
+ useEntityEvent({
3736
+ instance: instanceRef.current,
3737
+ onSpatialTap,
3738
+ onSpatialDragStart,
3739
+ onSpatialDrag,
3740
+ onSpatialDragEnd,
3741
+ // onSpatialRotateStart,
3742
+ onSpatialRotate,
3743
+ onSpatialRotateEnd,
3744
+ // onSpatialMagnifyStart,
3745
+ onSpatialMagnify,
3746
+ onSpatialMagnifyEnd
3747
+ });
3748
+ return instanceRef.current.entity;
3749
+ };
3750
+
3751
+ // src/reality/hooks/useForceUpdate.tsx
3752
+ import { useCallback as useCallback7, useState as useState7 } from "react";
3753
+ var useForceUpdate2 = () => {
3754
+ const [, setTick] = useState7(0);
3755
+ return useCallback7(() => setTick((tick) => tick + 1), []);
3756
+ };
3757
+
3758
+ // src/reality/components/BaseEntity.tsx
3759
+ import { jsx as jsx12 } from "react/jsx-runtime";
3760
+ var BaseEntity = forwardRef10(
3761
+ ({ children, createEntity, ...rest }, ref) => {
3762
+ const ctx = useRealityContext();
3763
+ const entity = useEntity({
3764
+ ...rest,
3765
+ ref,
3766
+ createEntity: (signal) => createEntity(ctx, signal)
3767
+ });
3768
+ if (!entity) return null;
3769
+ return /* @__PURE__ */ jsx12(ParentContext.Provider, { value: entity, children });
3770
+ }
3771
+ );
3772
+
3773
+ // src/reality/components/Entity.tsx
3774
+ import { jsx as jsx13 } from "react/jsx-runtime";
3775
+ var Entity = forwardRef11((props, ref) => {
3776
+ const { id, name, children, ...rest } = props;
3777
+ return /* @__PURE__ */ jsx13(
3778
+ BaseEntity,
3779
+ {
3780
+ ...rest,
3781
+ id,
3782
+ ref,
3783
+ createEntity: async (ctxVal) => ctxVal.session.createEntity({ id, name }),
3784
+ children
3785
+ }
3786
+ );
3787
+ });
3788
+
3789
+ // src/reality/components/BoxEntity.tsx
3790
+ import { forwardRef as forwardRef13 } from "react";
3791
+
3792
+ // src/reality/components/GeometryEntity.tsx
3793
+ import { forwardRef as forwardRef12 } from "react";
3794
+ import { jsx as jsx14 } from "react/jsx-runtime";
3795
+ var GeometryEntity = forwardRef12(
3796
+ ({ id, children, name, materials, geometryOptions, createGeometry, ...rest }, ref) => {
3797
+ return /* @__PURE__ */ jsx14(
3798
+ BaseEntity,
3799
+ {
3800
+ ...rest,
3801
+ id,
3802
+ ref,
3803
+ createEntity: async (ctx, signal) => {
3804
+ const manager = new AbortResourceManager(signal);
3805
+ try {
3806
+ const ent = await manager.addResource(
3807
+ () => ctx.session.createEntity({ id, name })
3808
+ );
3809
+ const geometry = await manager.addResource(
3810
+ () => createGeometry(geometryOptions)
3811
+ );
3812
+ const materialList = await Promise.all(
3813
+ materials?.map((id2) => ctx.resourceRegistry.get(id2)).filter(Boolean) ?? []
3814
+ );
3815
+ const modelComponent = await manager.addResource(
3816
+ () => ctx.session.createModelComponent({
3817
+ mesh: geometry,
3818
+ materials: materialList
3819
+ })
3820
+ );
3821
+ await ent.addComponent(modelComponent);
3822
+ return ent;
3823
+ } catch (error) {
3824
+ await manager.dispose();
3825
+ return null;
3826
+ }
3827
+ },
3828
+ children
3829
+ }
3830
+ );
3831
+ }
3832
+ );
3833
+
3834
+ // src/reality/components/BoxEntity.tsx
3835
+ import { jsx as jsx15 } from "react/jsx-runtime";
3836
+ var BoxEntity = forwardRef13(
3837
+ ({ children, ...props }, ref) => {
3838
+ const ctx = useRealityContext();
3839
+ return /* @__PURE__ */ jsx15(
3840
+ GeometryEntity,
3841
+ {
3842
+ ...props,
3843
+ ref,
3844
+ createGeometry: (options) => ctx.session.createBoxGeometry(options),
3845
+ geometryOptions: {
3846
+ width: props.width,
3847
+ height: props.height,
3848
+ depth: props.depth,
3849
+ cornerRadius: props.cornerRadius,
3850
+ splitFaces: props.splitFaces
3851
+ },
3852
+ children
3853
+ }
3854
+ );
3855
+ }
3856
+ );
3857
+
3858
+ // src/reality/components/UnlitMaterial.tsx
3859
+ import { useEffect as useEffect21, useRef as useRef10 } from "react";
3860
+ var UnlitMaterial = ({ children, ...options }) => {
3861
+ const ctx = useRealityContext();
3862
+ const materialRef = useRef10();
3863
+ useEffect21(() => {
3864
+ if (!ctx) return;
3865
+ const { session, reality, resourceRegistry } = ctx;
3866
+ const init = async () => {
3867
+ const materialPromise = session.createUnlitMaterial(options);
3868
+ resourceRegistry.add(options.id, materialPromise);
3869
+ try {
3870
+ const mat = await materialPromise;
3871
+ materialRef.current = mat;
3872
+ } catch (error) {
3873
+ console.error(" ~ UnlitMaterial ~ error:", error);
3874
+ }
3875
+ };
3876
+ init();
3877
+ return () => {
3878
+ resourceRegistry.removeAndDestroy(options.id);
3879
+ };
3880
+ }, [ctx]);
3881
+ return null;
3882
+ };
3883
+
3884
+ // src/reality/components/SphereEntity.tsx
3885
+ import { forwardRef as forwardRef14 } from "react";
3886
+ import { jsx as jsx16 } from "react/jsx-runtime";
3887
+ var SphereEntity = forwardRef14(
3888
+ ({ children, ...props }, ref) => {
3889
+ const ctx = useRealityContext();
3890
+ return /* @__PURE__ */ jsx16(
3891
+ GeometryEntity,
3892
+ {
3893
+ ...props,
3894
+ ref,
3895
+ createGeometry: (options) => ctx.session.createSphereGeometry(options),
3896
+ geometryOptions: {
3897
+ radius: props.radius
3898
+ },
3899
+ children
3900
+ }
3901
+ );
3902
+ }
3903
+ );
3904
+
3905
+ // src/reality/components/ConeEntity.tsx
3906
+ import { forwardRef as forwardRef15 } from "react";
3907
+ import { jsx as jsx17 } from "react/jsx-runtime";
3908
+ var ConeEntity = forwardRef15(
3909
+ ({ children, ...props }, ref) => {
3910
+ const ctx = useRealityContext();
3911
+ return /* @__PURE__ */ jsx17(
3912
+ GeometryEntity,
3913
+ {
3914
+ ...props,
3915
+ ref,
3916
+ createGeometry: (options) => ctx.session.createConeGeometry(options),
3917
+ geometryOptions: {
3918
+ radius: props.radius,
3919
+ height: props.height
3920
+ },
3921
+ children
3922
+ }
3923
+ );
3924
+ }
3925
+ );
3926
+
3927
+ // src/reality/components/CylinderEntity.tsx
3928
+ import { forwardRef as forwardRef16 } from "react";
3929
+ import { jsx as jsx18 } from "react/jsx-runtime";
3930
+ var CylinderEntity = forwardRef16(
3931
+ ({ children, ...props }, ref) => {
3932
+ const ctx = useRealityContext();
3933
+ return /* @__PURE__ */ jsx18(
3934
+ GeometryEntity,
3935
+ {
3936
+ ...props,
3937
+ ref,
3938
+ createGeometry: (options) => ctx.session.createCylinderGeometry(options),
3939
+ geometryOptions: {
3940
+ radius: props.radius,
3941
+ height: props.height
3942
+ },
3943
+ children
3944
+ }
3945
+ );
3946
+ }
3947
+ );
3948
+
3949
+ // src/reality/components/PlaneEntity.tsx
3950
+ import { forwardRef as forwardRef17 } from "react";
3951
+ import { jsx as jsx19 } from "react/jsx-runtime";
3952
+ var PlaneEntity = forwardRef17(
3953
+ ({ children, ...props }, ref) => {
3954
+ const ctx = useRealityContext();
3955
+ return /* @__PURE__ */ jsx19(
3956
+ GeometryEntity,
3957
+ {
3958
+ ...props,
3959
+ ref,
3960
+ createGeometry: (options) => ctx.session.createPlaneGeometry(options),
3961
+ geometryOptions: {
3962
+ width: props.width,
3963
+ height: props.height,
3964
+ cornerRadius: props.cornerRadius
3965
+ },
3966
+ children
3967
+ }
3968
+ );
3969
+ }
3970
+ );
3971
+
3972
+ // src/reality/components/SceneGraph.tsx
3973
+ import { jsx as jsx20 } from "react/jsx-runtime";
3974
+ var SceneGraph = ({ children }) => {
3975
+ return /* @__PURE__ */ jsx20(ParentContext.Provider, { value: null, children });
3976
+ };
3977
+
3978
+ // src/reality/components/ModelAsset.tsx
3979
+ import { useEffect as useEffect22, useRef as useRef11 } from "react";
3980
+ var resolveAssetUrl = (url) => {
3981
+ if (url.startsWith("http://") || url.startsWith("https://")) {
3982
+ return url;
3983
+ }
3984
+ return new URL(url, window.location.href).href;
3985
+ };
3986
+ var ModelAsset = ({ children, ...options }) => {
3987
+ const ctx = useRealityContext();
3988
+ const materialRef = useRef11();
3989
+ useEffect22(() => {
3990
+ const controller = new AbortController();
3991
+ if (!ctx) return;
3992
+ const { session, reality, resourceRegistry } = ctx;
3993
+ const init = async () => {
3994
+ try {
3995
+ const resolvedUrl = resolveAssetUrl(options.src);
3996
+ const modelAssetPromise = session.createModelAsset({ url: resolvedUrl });
3997
+ resourceRegistry.add(options.id, modelAssetPromise);
3998
+ const mat = await modelAssetPromise;
3999
+ if (controller.signal.aborted) {
4000
+ mat.destroy();
4001
+ return;
4002
+ }
4003
+ materialRef.current = mat;
4004
+ options.onLoad?.();
4005
+ } catch (error) {
4006
+ options.onError?.(error);
4007
+ }
4008
+ };
4009
+ init();
4010
+ return () => {
4011
+ controller.abort();
4012
+ materialRef.current?.destroy();
4013
+ };
4014
+ }, [ctx]);
4015
+ return null;
4016
+ };
4017
+
4018
+ // src/reality/components/ModelEntity.tsx
4019
+ import { forwardRef as forwardRef18 } from "react";
4020
+ import { jsx as jsx21 } from "react/jsx-runtime";
4021
+ var ModelEntity = forwardRef18(
4022
+ ({ id, model, children, name, ...rest }, ref) => {
4023
+ return /* @__PURE__ */ jsx21(
4024
+ BaseEntity,
4025
+ {
4026
+ ...rest,
4027
+ id,
4028
+ ref,
4029
+ createEntity: async (ctx, signal) => {
4030
+ try {
4031
+ const modelAsset = await ctx.resourceRegistry.get(model);
4032
+ if (!modelAsset)
4033
+ throw new Error(`ModelEntity: model not found ${model}`);
4034
+ if (signal.aborted) return null;
4035
+ return ctx.session.createSpatialModelEntity(
4036
+ {
4037
+ modelAssetId: modelAsset.id,
4038
+ name
4039
+ },
4040
+ { id, name }
4041
+ );
4042
+ } catch (error) {
4043
+ return null;
4044
+ }
4045
+ },
4046
+ children
4047
+ }
4048
+ );
4049
+ }
4050
+ );
4051
+
4052
+ // src/reality/components/Reality.tsx
4053
+ import {
4054
+ forwardRef as forwardRef19,
4055
+ useCallback as useCallback8,
4056
+ useEffect as useEffect23,
4057
+ useRef as useRef12,
4058
+ useState as useState8
4059
+ } from "react";
4060
+ import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
4061
+ var Reality = forwardRef19(
4062
+ function RealityBase({ children, ...inProps }, ref) {
4063
+ const insideAttachment = useInsideAttachment();
4064
+ if (insideAttachment) {
4065
+ console.warn(
4066
+ "[WebSpatial] Reality cannot be used inside AttachmentAsset."
4067
+ );
4068
+ return null;
4069
+ }
4070
+ const {
4071
+ onSpatialTap,
4072
+ onSpatialDragStart,
4073
+ onSpatialDrag,
4074
+ onSpatialDragEnd,
4075
+ onSpatialRotate,
4076
+ onSpatialRotateEnd,
4077
+ onSpatialMagnify,
4078
+ onSpatialMagnifyEnd,
4079
+ ...props
4080
+ } = inProps;
4081
+ const ctxRef = useRef12(null);
4082
+ const creationId = useRef12(0);
4083
+ const [isReady, setIsReady] = useState8(false);
4084
+ const cleanupReality = useCallback8(() => {
4085
+ ctxRef.current?.attachmentRegistry.destroy();
4086
+ ctxRef.current?.resourceRegistry.destroy();
4087
+ ctxRef.current?.reality.destroy();
4088
+ ctxRef.current = null;
4089
+ setIsReady(false);
4090
+ }, []);
4091
+ useEffect23(() => {
4092
+ return () => {
4093
+ creationId.current++;
4094
+ cleanupReality();
4095
+ };
4096
+ }, [cleanupReality]);
4097
+ const createReality = useCallback8(async () => {
4098
+ const id = ++creationId.current;
4099
+ const resourceRegistry = new ResourceRegistry();
4100
+ const attachmentRegistry = new AttachmentRegistry();
4101
+ const session = await getSession();
4102
+ if (!session) {
4103
+ resourceRegistry.destroy();
4104
+ attachmentRegistry.destroy();
4105
+ return null;
4106
+ }
4107
+ const reality = await session.createSpatializedDynamic3DElement();
4108
+ const isCancelled = () => id !== creationId.current;
4109
+ if (isCancelled()) {
4110
+ resourceRegistry.destroy();
4111
+ attachmentRegistry.destroy();
4112
+ reality.destroy();
4113
+ return null;
4114
+ }
4115
+ try {
4116
+ const result = await session.getSpatialScene().addSpatializedElement(reality);
4117
+ if (!result.success || isCancelled()) {
4118
+ resourceRegistry.destroy();
4119
+ attachmentRegistry.destroy();
4120
+ reality.destroy();
4121
+ return null;
4122
+ }
4123
+ cleanupReality();
4124
+ ctxRef.current = {
4125
+ session,
4126
+ reality,
4127
+ resourceRegistry,
4128
+ attachmentRegistry
4129
+ };
4130
+ setIsReady(true);
4131
+ return reality;
4132
+ } catch (err) {
4133
+ console.error("[createReality] failed", err);
4134
+ resourceRegistry.destroy();
4135
+ attachmentRegistry.destroy();
4136
+ reality.destroy();
4137
+ return null;
4138
+ }
4139
+ }, [cleanupReality]);
4140
+ const content = useCallback8(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
4141
+ return /* @__PURE__ */ jsxs3(RealityContext.Provider, { value: ctxRef.current, children: [
4142
+ /* @__PURE__ */ jsx22(
4143
+ SpatializedContainer,
4144
+ {
4145
+ component: "div",
4146
+ ref,
4147
+ createSpatializedElement: createReality,
4148
+ spatializedContent: content,
4149
+ ...props
4150
+ }
4151
+ ),
4152
+ isReady && children
4153
+ ] });
4154
+ }
4155
+ );
4156
+
4157
+ // src/reality/components/AttachmentAsset.tsx
4158
+ import { useEffect as useEffect24, useState as useState9 } from "react";
4159
+ import { createPortal as createPortal3 } from "react-dom";
4160
+ import { jsx as jsx23 } from "react/jsx-runtime";
4161
+ var AttachmentAsset = ({
4162
+ name,
4163
+ children
4164
+ }) => {
4165
+ const ctx = useRealityContext();
4166
+ const [containers, setContainers] = useState9([]);
4167
+ useEffect24(() => {
4168
+ if (!ctx) return;
4169
+ return ctx.attachmentRegistry.onContainersChange(name, setContainers);
4170
+ }, [ctx, name]);
4171
+ if (!containers.length) return null;
4172
+ return /* @__PURE__ */ jsx23(InsideAttachmentContext.Provider, { value: true, children: containers.map((c, idx) => createPortal3(children, c, `${name}-${idx}`)) });
4173
+ };
4174
+
4175
+ // src/reality/components/AttachmentEntity.tsx
4176
+ import { useEffect as useEffect25, useRef as useRef13, useState as useState10 } from "react";
4177
+ var instanceCounter = 0;
4178
+ var AttachmentEntity = ({
4179
+ attachment: attachmentName,
4180
+ position,
4181
+ size
4182
+ }) => {
4183
+ const ctx = useRealityContext();
4184
+ const parent = useParentContext();
4185
+ const attachmentRef = useRef13(null);
4186
+ const parentIdRef = useRef13(null);
4187
+ const instanceIdRef = useRef13(`att_${++instanceCounter}`);
4188
+ const attachmentNameRef = useRef13(attachmentName);
4189
+ const [childWindow, setChildWindow] = useState10(null);
4190
+ useEffect25(() => {
4191
+ if (!ctx || !parent) return;
4192
+ const parentId = parent.id;
4193
+ parentIdRef.current = parentId;
4194
+ let cancelled = false;
4195
+ const init = async () => {
4196
+ try {
4197
+ const att = await ctx.session.createAttachmentEntity({
4198
+ parentEntityId: parentId,
4199
+ position: position ?? [0, 0, 0],
4200
+ size
4201
+ });
4202
+ if (cancelled) {
4203
+ att.destroy();
4204
+ return;
4205
+ }
4206
+ const windowProxy = att.getWindowProxy();
4207
+ setOpenWindowStyle(windowProxy);
4208
+ windowProxy.document.body.style.display = "block";
4209
+ windowProxy.document.body.style.minWidth = "100%";
4210
+ windowProxy.document.body.style.maxWidth = "100%";
4211
+ windowProxy.document.body.style.minHeight = "100%";
4212
+ await syncParentHeadToChild(windowProxy);
4213
+ const viewport = windowProxy.document.querySelector(
4214
+ 'meta[name="viewport"]'
4215
+ );
4216
+ if (!viewport) {
4217
+ const meta = windowProxy.document.createElement("meta");
4218
+ meta.name = "viewport";
4219
+ meta.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
4220
+ windowProxy.document.head.appendChild(meta);
4221
+ }
4222
+ const base = windowProxy.document.createElement("base");
4223
+ base.href = document.baseURI;
4224
+ windowProxy.document.head.appendChild(base);
4225
+ attachmentRef.current = att;
4226
+ setChildWindow(windowProxy);
4227
+ ctx.attachmentRegistry.addContainer(
4228
+ attachmentNameRef.current,
4229
+ instanceIdRef.current,
4230
+ att.getContainer()
4231
+ );
4232
+ } catch (error) {
4233
+ console.error("[AttachmentEntity] init error:", error);
4234
+ }
4235
+ };
4236
+ init();
4237
+ return () => {
4238
+ cancelled = true;
4239
+ const att = attachmentRef.current;
4240
+ if (att) {
4241
+ ctx.attachmentRegistry.removeContainer(
4242
+ attachmentNameRef.current,
4243
+ instanceIdRef.current
4244
+ );
4245
+ att.destroy();
4246
+ attachmentRef.current = null;
4247
+ setChildWindow(null);
4248
+ }
4249
+ };
4250
+ }, [ctx, parent]);
4251
+ useEffect25(() => {
4252
+ if (!ctx) return;
4253
+ const att = attachmentRef.current;
4254
+ const prevName = attachmentNameRef.current;
4255
+ if (att && prevName !== attachmentName) {
4256
+ ctx.attachmentRegistry.removeContainer(prevName, instanceIdRef.current);
4257
+ ctx.attachmentRegistry.addContainer(
4258
+ attachmentName,
4259
+ instanceIdRef.current,
4260
+ att.getContainer()
4261
+ );
4262
+ attachmentNameRef.current = attachmentName;
4263
+ } else {
4264
+ attachmentNameRef.current = attachmentName;
4265
+ }
4266
+ }, [ctx, attachmentName]);
4267
+ useSyncHeadStyles(childWindow, { subtree: false });
4268
+ useEffect25(() => {
4269
+ if (!attachmentRef.current) return;
4270
+ attachmentRef.current.update({ position, size });
4271
+ }, [position?.[0], position?.[1], position?.[2], size?.width, size?.height]);
4272
+ return null;
4273
+ };
4274
+
4275
+ // src/Model.tsx
4276
+ import { forwardRef as forwardRef20 } from "react";
4277
+ import { jsx as jsx24 } from "react/jsx-runtime";
4278
+ var spatial2 = new Spatial();
4279
+ function ModelBase(props, ref) {
4280
+ const insideAttachment = useInsideAttachment();
4281
+ const { "enable-xr": enableXR, ...restProps } = props;
4282
+ if (!enableXR || !spatial2.runInSpatialWeb() || insideAttachment) {
4283
+ const {
4284
+ onSpatialTap,
4285
+ onSpatialDragStart,
4286
+ onSpatialDrag,
4287
+ onSpatialDragEnd,
4288
+ onSpatialRotate,
4289
+ onSpatialRotateEnd,
4290
+ onSpatialMagnify,
4291
+ onSpatialMagnifyEnd,
4292
+ ...modelProps
4293
+ } = restProps;
4294
+ return /* @__PURE__ */ jsx24("model", { ref, ...modelProps });
4295
+ }
4296
+ return /* @__PURE__ */ jsx24(SpatializedStatic3DElementContainer, { ref, ...restProps });
4297
+ }
4298
+ var Model = withSSRSupported(forwardRef20(ModelBase));
4299
+ Model.displayName = "Model";
4300
+
4301
+ // src/index.ts
4302
+ var version = "1.2.1";
4303
+ if (typeof window !== "undefined") {
4304
+ initPolyfill();
4305
+ }
4306
+ export {
4307
+ AttachmentAsset,
4308
+ AttachmentEntity,
4309
+ BoxEntity,
4310
+ ConeEntity,
4311
+ CylinderEntity,
4312
+ Entity,
4313
+ Model,
4314
+ ModelAsset,
4315
+ ModelEntity,
4316
+ PlaneEntity,
4317
+ Reality,
4318
+ SSRProvider,
4319
+ SceneGraph,
4320
+ SpatialMonitor,
4321
+ Spatialized2DElementContainer,
4322
+ SpatializedContainer,
4323
+ SpatializedStatic3DElementContainer,
4324
+ SphereEntity,
4325
+ UnlitMaterial,
4326
+ enableDebugTool,
4327
+ eventMap,
4328
+ initPolyfill,
4329
+ initScene,
4330
+ toLocalSpace,
4331
+ toSceneSpatial,
4332
+ version,
4333
+ withSpatialMonitor,
4334
+ withSpatialized2DElementContainer
4335
+ };
4336
+ //# sourceMappingURL=index.js.map