@holoscript/engine 6.0.3 → 6.0.4

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 (192) hide show
  1. package/dist/AutoMesher-CK47F6AV.js +17 -0
  2. package/dist/GPUBuffers-2LHBCD7X.js +9 -0
  3. package/dist/WebGPUContext-TNEUYU2Y.js +11 -0
  4. package/dist/animation/index.cjs +38 -38
  5. package/dist/animation/index.d.cts +1 -1
  6. package/dist/animation/index.d.ts +1 -1
  7. package/dist/animation/index.js +1 -1
  8. package/dist/audio/index.cjs +16 -6
  9. package/dist/audio/index.d.cts +1 -1
  10. package/dist/audio/index.d.ts +1 -1
  11. package/dist/audio/index.js +1 -1
  12. package/dist/camera/index.cjs +23 -23
  13. package/dist/camera/index.d.cts +1 -1
  14. package/dist/camera/index.d.ts +1 -1
  15. package/dist/camera/index.js +1 -1
  16. package/dist/character/index.cjs +6 -4
  17. package/dist/character/index.js +1 -1
  18. package/dist/choreography/index.cjs +1194 -0
  19. package/dist/choreography/index.d.cts +687 -0
  20. package/dist/choreography/index.d.ts +687 -0
  21. package/dist/choreography/index.js +1156 -0
  22. package/dist/chunk-2CSNRI2N.js +217 -0
  23. package/dist/chunk-33T2WINR.js +266 -0
  24. package/dist/chunk-35R73OFM.js +1257 -0
  25. package/dist/chunk-4MMDSUNP.js +1256 -0
  26. package/dist/chunk-5V6HOU72.js +319 -0
  27. package/dist/chunk-6QOP6PYF.js +1038 -0
  28. package/dist/chunk-7KMJVHIL.js +8944 -0
  29. package/dist/chunk-7VPUC62U.js +1106 -0
  30. package/dist/chunk-A2Y6RCAT.js +1878 -0
  31. package/dist/chunk-AHM42MK6.js +8944 -0
  32. package/dist/chunk-BL7IDTHE.js +218 -0
  33. package/dist/chunk-CITOMSWL.js +10462 -0
  34. package/dist/chunk-CXDPKW2K.js +8944 -0
  35. package/dist/chunk-CXZPLD4S.js +223 -0
  36. package/dist/chunk-CZYJE7IH.js +5169 -0
  37. package/dist/chunk-D2OP7YC7.js +6325 -0
  38. package/dist/chunk-EDRVQHUU.js +1544 -0
  39. package/dist/chunk-EJSLOOW2.js +3589 -0
  40. package/dist/chunk-F53SFGW5.js +1878 -0
  41. package/dist/chunk-HCFPELPY.js +919 -0
  42. package/dist/chunk-HNEE36PY.js +93 -0
  43. package/dist/chunk-HYXNV36F.js +1256 -0
  44. package/dist/chunk-IB7KHVFY.js +821 -0
  45. package/dist/chunk-IBBO7YYG.js +690 -0
  46. package/dist/chunk-ILIBGINU.js +5470 -0
  47. package/dist/chunk-IS4MHLKN.js +5479 -0
  48. package/dist/chunk-JT2PFKWD.js +5479 -0
  49. package/dist/chunk-K4CUB4NY.js +1038 -0
  50. package/dist/chunk-KATDQXRJ.js +10462 -0
  51. package/dist/chunk-KBQE6ZFJ.js +8944 -0
  52. package/dist/chunk-KBVD5K7E.js +560 -0
  53. package/dist/chunk-KCDPVQRY.js +4088 -0
  54. package/dist/chunk-KN4QJPKN.js +8944 -0
  55. package/dist/chunk-KWJ3ROSI.js +8944 -0
  56. package/dist/chunk-L45VF6DD.js +919 -0
  57. package/dist/chunk-LY4T37YK.js +307 -0
  58. package/dist/chunk-MDN5WZXA.js +1544 -0
  59. package/dist/chunk-MGCDP6VU.js +928 -0
  60. package/dist/chunk-NCX7X6G2.js +8681 -0
  61. package/dist/chunk-OF54BPVD.js +913 -0
  62. package/dist/chunk-OWSN2Q3Q.js +690 -0
  63. package/dist/chunk-PRRB5TTA.js +406 -0
  64. package/dist/chunk-PXWVQF76.js +4086 -0
  65. package/dist/chunk-PYCOIDT2.js +812 -0
  66. package/dist/chunk-PZCSADOV.js +928 -0
  67. package/dist/chunk-Q2XBVS2K.js +1038 -0
  68. package/dist/chunk-QDZRXWN5.js +1776 -0
  69. package/dist/chunk-RNWOZ6WQ.js +913 -0
  70. package/dist/chunk-ROLFT4CJ.js +1693 -0
  71. package/dist/chunk-SLTJRZ2N.js +266 -0
  72. package/dist/chunk-SRUS5XSU.js +4088 -0
  73. package/dist/chunk-TKCA3WZ5.js +5409 -0
  74. package/dist/chunk-TNRMXYI2.js +1650 -0
  75. package/dist/chunk-TQB3GJGM.js +9763 -0
  76. package/dist/chunk-TUFGXG6K.js +510 -0
  77. package/dist/chunk-U6KMTGQJ.js +632 -0
  78. package/dist/chunk-VMGJQST6.js +8681 -0
  79. package/dist/chunk-X4F4TCG4.js +5470 -0
  80. package/dist/chunk-ZIFROE75.js +1544 -0
  81. package/dist/chunk-ZIJQYHSQ.js +1204 -0
  82. package/dist/combat/index.cjs +4 -4
  83. package/dist/combat/index.d.cts +1 -1
  84. package/dist/combat/index.d.ts +1 -1
  85. package/dist/combat/index.js +1 -1
  86. package/dist/ecs/index.cjs +1 -1
  87. package/dist/ecs/index.js +1 -1
  88. package/dist/environment/index.cjs +14 -14
  89. package/dist/environment/index.d.cts +1 -1
  90. package/dist/environment/index.d.ts +1 -1
  91. package/dist/environment/index.js +1 -1
  92. package/dist/gpu/index.cjs +4810 -0
  93. package/dist/gpu/index.js +3714 -0
  94. package/dist/hologram/index.cjs +27 -1
  95. package/dist/hologram/index.js +1 -1
  96. package/dist/index-B2PIsAmR.d.cts +2180 -0
  97. package/dist/index-B2PIsAmR.d.ts +2180 -0
  98. package/dist/index-BHySEPX7.d.cts +2921 -0
  99. package/dist/index-BJV21zuy.d.cts +341 -0
  100. package/dist/index-BJV21zuy.d.ts +341 -0
  101. package/dist/index-BQutTphC.d.cts +790 -0
  102. package/dist/index-ByIq2XrS.d.cts +3910 -0
  103. package/dist/index-BysHjDSO.d.cts +224 -0
  104. package/dist/index-BysHjDSO.d.ts +224 -0
  105. package/dist/index-CKwAJGck.d.ts +455 -0
  106. package/dist/index-CUl3QstQ.d.cts +3006 -0
  107. package/dist/index-CUl3QstQ.d.ts +3006 -0
  108. package/dist/index-CmYtNiI-.d.cts +953 -0
  109. package/dist/index-CmYtNiI-.d.ts +953 -0
  110. package/dist/index-CnRzWxi_.d.cts +522 -0
  111. package/dist/index-CnRzWxi_.d.ts +522 -0
  112. package/dist/index-CwRWbSC7.d.ts +2921 -0
  113. package/dist/index-CxKIBstO.d.ts +790 -0
  114. package/dist/index-DJ6-R8vh.d.cts +455 -0
  115. package/dist/index-DQKisbcI.d.cts +4968 -0
  116. package/dist/index-DQKisbcI.d.ts +4968 -0
  117. package/dist/index-DRT2zJez.d.ts +3910 -0
  118. package/dist/index-DfNLiAka.d.cts +192 -0
  119. package/dist/index-DfNLiAka.d.ts +192 -0
  120. package/dist/index-nMvkoRm8.d.cts +405 -0
  121. package/dist/index-nMvkoRm8.d.ts +405 -0
  122. package/dist/index-s9yOFU37.d.cts +604 -0
  123. package/dist/index-s9yOFU37.d.ts +604 -0
  124. package/dist/index.cjs +22966 -6960
  125. package/dist/index.d.cts +864 -20
  126. package/dist/index.d.ts +864 -20
  127. package/dist/index.js +3062 -48
  128. package/dist/input/index.cjs +1 -1
  129. package/dist/input/index.js +1 -1
  130. package/dist/orbital/index.cjs +3 -3
  131. package/dist/orbital/index.d.cts +1 -1
  132. package/dist/orbital/index.d.ts +1 -1
  133. package/dist/orbital/index.js +1 -1
  134. package/dist/particles/index.cjs +16 -16
  135. package/dist/particles/index.d.cts +1 -1
  136. package/dist/particles/index.d.ts +1 -1
  137. package/dist/particles/index.js +1 -1
  138. package/dist/physics/index.cjs +2377 -21
  139. package/dist/physics/index.d.cts +1 -1
  140. package/dist/physics/index.d.ts +1 -1
  141. package/dist/physics/index.js +35 -1
  142. package/dist/postfx/index.cjs +3491 -0
  143. package/dist/postfx/index.js +93 -0
  144. package/dist/procedural/index.cjs +1 -1
  145. package/dist/procedural/index.js +1 -1
  146. package/dist/puppeteer-5VF6KDVO.js +52197 -0
  147. package/dist/puppeteer-IZVZ3SG4.js +52197 -0
  148. package/dist/rendering/index.cjs +33 -32
  149. package/dist/rendering/index.d.cts +1 -1
  150. package/dist/rendering/index.d.ts +1 -1
  151. package/dist/rendering/index.js +8 -6
  152. package/dist/runtime/index.cjs +23 -13
  153. package/dist/runtime/index.d.cts +1 -1
  154. package/dist/runtime/index.d.ts +1 -1
  155. package/dist/runtime/index.js +8 -6
  156. package/dist/runtime/protocols/index.cjs +349 -0
  157. package/dist/runtime/protocols/index.js +15 -0
  158. package/dist/scene/index.cjs +8 -8
  159. package/dist/scene/index.d.cts +1 -1
  160. package/dist/scene/index.d.ts +1 -1
  161. package/dist/scene/index.js +1 -1
  162. package/dist/shader/index.cjs +3087 -0
  163. package/dist/shader/index.js +3044 -0
  164. package/dist/simulation/index.cjs +10680 -0
  165. package/dist/simulation/index.d.cts +3 -0
  166. package/dist/simulation/index.d.ts +3 -0
  167. package/dist/simulation/index.js +307 -0
  168. package/dist/spatial/index.cjs +2443 -0
  169. package/dist/spatial/index.d.cts +1545 -0
  170. package/dist/spatial/index.d.ts +1545 -0
  171. package/dist/spatial/index.js +2400 -0
  172. package/dist/terrain/index.cjs +1 -1
  173. package/dist/terrain/index.d.cts +1 -1
  174. package/dist/terrain/index.d.ts +1 -1
  175. package/dist/terrain/index.js +1 -1
  176. package/dist/transformers.node-4NKAPD5U.js +45620 -0
  177. package/dist/vm/index.cjs +7 -8
  178. package/dist/vm/index.d.cts +1 -1
  179. package/dist/vm/index.d.ts +1 -1
  180. package/dist/vm/index.js +1 -1
  181. package/dist/vm-bridge/index.cjs +2 -2
  182. package/dist/vm-bridge/index.d.cts +2 -2
  183. package/dist/vm-bridge/index.d.ts +2 -2
  184. package/dist/vm-bridge/index.js +1 -1
  185. package/dist/vr/index.cjs +6 -6
  186. package/dist/vr/index.js +1 -1
  187. package/dist/world/index.cjs +3 -3
  188. package/dist/world/index.d.cts +1 -1
  189. package/dist/world/index.d.ts +1 -1
  190. package/dist/world/index.js +1 -1
  191. package/package.json +53 -21
  192. package/LICENSE +0 -21
package/dist/index.js CHANGED
@@ -1,14 +1,43 @@
1
+ import {
2
+ simulation_exports
3
+ } from "./chunk-TQB3GJGM.js";
4
+ import "./chunk-BL7IDTHE.js";
5
+ import {
6
+ terrain_exports
7
+ } from "./chunk-PYCOIDT2.js";
8
+ import {
9
+ tilemap_exports
10
+ } from "./chunk-3JORF6V4.js";
11
+ import {
12
+ vm_exports
13
+ } from "./chunk-HYXNV36F.js";
14
+ import {
15
+ vm_bridge_exports
16
+ } from "./chunk-SLTJRZ2N.js";
1
17
  import {
2
18
  vr_exports
3
- } from "./chunk-FTINXFRJ.js";
19
+ } from "./chunk-KBVD5K7E.js";
4
20
  import {
5
21
  world_exports
6
- } from "./chunk-HW57JP55.js";
22
+ } from "./chunk-PRRB5TTA.js";
23
+ import {
24
+ PhysicsWorldImpl,
25
+ VRPhysicsBridge,
26
+ physics_exports
27
+ } from "./chunk-VMGJQST6.js";
28
+ import {
29
+ particles_exports
30
+ } from "./chunk-Q2XBVS2K.js";
31
+ import {
32
+ procedural_exports
33
+ } from "./chunk-HCFPELPY.js";
34
+ import {
35
+ scene_exports
36
+ } from "./chunk-RNWOZ6WQ.js";
7
37
  import {
8
38
  AdvancedLightingManager,
9
39
  AdvancedPBRMaterial,
10
40
  BVH,
11
- BloomEffect,
12
41
  CloudRenderer,
13
42
  ColorGrading,
14
43
  DecalBatcher,
@@ -20,10 +49,7 @@ import {
20
49
  MATERIAL_PRESETS2,
21
50
  MaterialLibrary,
22
51
  MaterialSystem,
23
- PP_PRESETS,
24
52
  PhysicsDebugDrawer,
25
- PostProcessStack,
26
- PostProcessingStack,
27
53
  ProjectorLight,
28
54
  PuppeteerRenderer,
29
55
  RayTracer,
@@ -93,7 +119,6 @@ import {
93
119
  packRect,
94
120
  parseIESProfile,
95
121
  pathTrace,
96
- postprocess_exports,
97
122
  prerenderHTML,
98
123
  rectSolidAngle,
99
124
  renderPDF,
@@ -110,73 +135,65 @@ import {
110
135
  sheenVisibility,
111
136
  thinSlabTransmission,
112
137
  triplanarWeights
113
- } from "./chunk-5M7G5AFS.js";
138
+ } from "./chunk-TKCA3WZ5.js";
114
139
  import {
115
- procedural_exports
116
- } from "./chunk-VSFEPRSB.js";
140
+ BloomEffect,
141
+ PP_PRESETS,
142
+ PostProcessStack,
143
+ PostProcessingStack,
144
+ postprocess_exports
145
+ } from "./chunk-EJSLOOW2.js";
117
146
  import {
147
+ eventBus,
118
148
  runtime_exports
119
- } from "./chunk-HWUUFZKL.js";
120
- import {
121
- scene_exports
122
- } from "./chunk-HWEANAXF.js";
123
- import {
124
- terrain_exports
125
- } from "./chunk-HLZJ37AZ.js";
126
- import {
127
- tilemap_exports
128
- } from "./chunk-3JORF6V4.js";
129
- import {
130
- vm_bridge_exports
131
- } from "./chunk-RN5CTZ6Q.js";
132
- import {
133
- vm_exports
134
- } from "./chunk-PXUX4KT5.js";
149
+ } from "./chunk-CZYJE7IH.js";
150
+ import "./chunk-5V6HOU72.js";
135
151
  import {
136
152
  ecs_exports
137
- } from "./chunk-D2SAKYOU.js";
153
+ } from "./chunk-IB7KHVFY.js";
154
+ import {
155
+ environment_exports
156
+ } from "./chunk-ZIFROE75.js";
138
157
  import {
139
158
  gameplay_exports
140
159
  } from "./chunk-RCQHJUSC.js";
160
+ import {
161
+ GaussianSplatExtractor
162
+ } from "./chunk-HNEE36PY.js";
163
+ import "./chunk-TUFGXG6K.js";
164
+ import "./chunk-2CSNRI2N.js";
141
165
  import {
142
166
  hologram_exports
143
- } from "./chunk-DBDXVKCH.js";
167
+ } from "./chunk-7VPUC62U.js";
144
168
  import {
145
169
  input_exports
146
- } from "./chunk-6PT5NOF7.js";
170
+ } from "./chunk-U6KMTGQJ.js";
147
171
  import {
148
172
  navigation_exports
149
173
  } from "./chunk-4YSUBXFR.js";
150
174
  import {
151
175
  orbital_exports
152
- } from "./chunk-UUSS3ONQ.js";
153
- import {
154
- particles_exports
155
- } from "./chunk-DWP6F6V4.js";
156
- import {
157
- physics_exports
158
- } from "./chunk-JQIUSARK.js";
176
+ } from "./chunk-LY4T37YK.js";
159
177
  import {
178
+ AnimationEngine,
179
+ TransitionSystem,
160
180
  animation_exports
161
- } from "./chunk-QJR7PURY.js";
181
+ } from "./chunk-F53SFGW5.js";
162
182
  import {
163
183
  audio_exports
164
- } from "./chunk-UK53LWFY.js";
184
+ } from "./chunk-SRUS5XSU.js";
165
185
  import {
166
186
  camera_exports
167
- } from "./chunk-OK3BGLJQ.js";
187
+ } from "./chunk-MGCDP6VU.js";
168
188
  import {
169
189
  character_exports
170
- } from "./chunk-COLFS4LL.js";
190
+ } from "./chunk-ZIJQYHSQ.js";
171
191
  import {
172
192
  combat_exports
173
- } from "./chunk-GJE5WTO6.js";
193
+ } from "./chunk-OWSN2Q3Q.js";
174
194
  import {
175
195
  dialogue_exports
176
196
  } from "./chunk-2MUNCP4Y.js";
177
- import {
178
- environment_exports
179
- } from "./chunk-CRVDNMZI.js";
180
197
  import {
181
198
  __export
182
199
  } from "./chunk-AKLW2MUS.js";
@@ -1465,7 +1482,7 @@ var LODManager = class {
1465
1482
  /**
1466
1483
  * Process transition queue (max transitions per frame to prevent stuttering)
1467
1484
  */
1468
- processTransitionQueue(deltaTime) {
1485
+ processTransitionQueue(_deltaTime) {
1469
1486
  this.transitionQueue.sort((a, b) => b.priority - a.priority);
1470
1487
  const transitionsToProcess = this.transitionQueue.slice(0, this.maxTransitionsPerFrame);
1471
1488
  for (const { objectId, level } of transitionsToProcess) {
@@ -2644,7 +2661,7 @@ var TransitionScheduler = class {
2644
2661
  /**
2645
2662
  * Record actual GPU cost for a transition (for budget tuning)
2646
2663
  */
2647
- recordCost(entityId, actualCostMs) {
2664
+ recordCost(entityId, _actualCostMs) {
2648
2665
  if (this.activatedThisFrame.has(entityId)) {
2649
2666
  }
2650
2667
  }
@@ -2809,7 +2826,7 @@ var LODMemoryPool = class {
2809
2826
  }
2810
2827
  this.stats.defragmentationCount++;
2811
2828
  this.updateStatistics();
2812
- const duration = performance.now() - startTime;
2829
+ const _duration = performance.now() - startTime;
2813
2830
  }
2814
2831
  /**
2815
2832
  * Check memory pressure and trigger callbacks
@@ -3163,7 +3180,7 @@ var LODPerformanceMetrics = class {
3163
3180
  /**
3164
3181
  * Start profiling a code section
3165
3182
  */
3166
- startProfile(name) {
3183
+ startProfile(_name) {
3167
3184
  if (!this.enabled) return 0;
3168
3185
  return performance.now();
3169
3186
  }
@@ -3690,6 +3707,2993 @@ function createMeshletGenerator(options) {
3690
3707
  function generateMeshlets(mesh, options) {
3691
3708
  return new MeshletGenerator(options).generate(mesh);
3692
3709
  }
3710
+
3711
+ // src/simulation/TetGenWasmMesher.ts
3712
+ var TetGenWasmMesher = class {
3713
+ wasmModule = null;
3714
+ constructor() {
3715
+ }
3716
+ async initialize() {
3717
+ if (this.wasmModule) return;
3718
+ console.log("TetGenWasmMesher: Initializing WASM binary...");
3719
+ }
3720
+ async tetrahedralize(surface, options) {
3721
+ await this.initialize();
3722
+ console.log(`TetGenWasmMesher: Tetrahedralizing surface with ${surface.triangles.length / 3} faces...`);
3723
+ const flags = options?.quality ? `-pq${options.quality}` : "-pq";
3724
+ console.log(`TetGenWasmMesher: Using flags ${flags}`);
3725
+ const { meshBox } = await import("./AutoMesher-CK47F6AV.js");
3726
+ return meshBox({
3727
+ size: [1, 1, 1],
3728
+ // Placeholder
3729
+ divisions: [10, 10, 10]
3730
+ });
3731
+ }
3732
+ };
3733
+
3734
+ // src/runtime/HoloScriptPlusRuntime.ts
3735
+ import { createState, ExpressionEvaluator } from "@holoscript/core";
3736
+ import { vrTraitRegistry } from "@holoscript/core";
3737
+
3738
+ // src/runtime/loader.ts
3739
+ var ChunkLoader = class {
3740
+ runtime;
3741
+ manifest = null;
3742
+ loadedChunks = /* @__PURE__ */ new Set();
3743
+ loadingChunks = /* @__PURE__ */ new Set();
3744
+ options;
3745
+ constructor(runtime, options) {
3746
+ this.runtime = runtime;
3747
+ this.options = options;
3748
+ }
3749
+ /**
3750
+ * Initialize the loader by fetching the manifest
3751
+ */
3752
+ async init() {
3753
+ try {
3754
+ const response = await fetch(this.options.manifestUrl);
3755
+ this.manifest = await response.json();
3756
+ } catch (e) {
3757
+ console.error("[ChunkLoader] Failed to load manifest", e);
3758
+ }
3759
+ }
3760
+ /**
3761
+ * Update the loader (check for spatial triggers)
3762
+ */
3763
+ update() {
3764
+ if (!this.manifest) return;
3765
+ const playerPos = this.runtime.vrContext.headset.position;
3766
+ if (!playerPos) return;
3767
+ for (const [chunkId, info] of Object.entries(this.manifest.chunks)) {
3768
+ if (this.loadedChunks.has(chunkId) || this.loadingChunks.has(chunkId)) continue;
3769
+ if (info.bounds && this.isPointInBounds(playerPos, info.bounds)) {
3770
+ this.loadChunk(chunkId);
3771
+ }
3772
+ }
3773
+ }
3774
+ /**
3775
+ * Manually load a chunk
3776
+ */
3777
+ async loadChunk(chunkId) {
3778
+ if (!this.manifest || !this.manifest.chunks[chunkId]) return;
3779
+ if (this.loadedChunks.has(chunkId) || this.loadingChunks.has(chunkId)) return;
3780
+ this.loadingChunks.add(chunkId);
3781
+ try {
3782
+ const info = this.manifest.chunks[chunkId];
3783
+ const url = this.options.baseUrl ? `${this.options.baseUrl}/${info.file}` : info.file;
3784
+ const response = await fetch(url);
3785
+ const chunkData = await response.json();
3786
+ await this.integrateChunk(chunkData);
3787
+ this.loadedChunks.add(chunkId);
3788
+ } catch (e) {
3789
+ console.error(`[ChunkLoader] Failed to load chunk ${chunkId}`, e);
3790
+ } finally {
3791
+ this.loadingChunks.delete(chunkId);
3792
+ }
3793
+ }
3794
+ async integrateChunk(chunk) {
3795
+ const objects = chunk.objects;
3796
+ if (!objects) return;
3797
+ const extRuntime = this.runtime;
3798
+ for (const obj of objects) {
3799
+ if (extRuntime.mountObject) {
3800
+ extRuntime.mountObject(obj);
3801
+ } else if (extRuntime.instantiateNode) {
3802
+ extRuntime.instantiateNode(obj, extRuntime.rootInstance);
3803
+ }
3804
+ }
3805
+ }
3806
+ isPointInBounds(point, bounds) {
3807
+ if (bounds.length < 2) return false;
3808
+ const [min, max] = bounds;
3809
+ return point[0] >= min[0] && point[0] <= max[0] && point[1] >= min[1] && point[1] <= max[1] && point[2] >= min[2] && point[2] <= max[2];
3810
+ }
3811
+ };
3812
+
3813
+ // src/runtime/HotReloader.ts
3814
+ import {
3815
+ diffState,
3816
+ buildMigrationChain,
3817
+ snapshotState,
3818
+ applyAutoMigration
3819
+ } from "@holoscript/core";
3820
+ var HotReloader = class {
3821
+ templates = /* @__PURE__ */ new Map();
3822
+ instances = /* @__PURE__ */ new Map();
3823
+ devMode;
3824
+ onWarning;
3825
+ onReload;
3826
+ onRollback;
3827
+ migrationExecutor = null;
3828
+ constructor(options = {}) {
3829
+ this.devMode = options.devMode ?? false;
3830
+ this.onWarning = options.onWarning ?? (() => {
3831
+ });
3832
+ this.onReload = options.onReload ?? (() => {
3833
+ });
3834
+ this.onRollback = options.onRollback ?? (() => {
3835
+ });
3836
+ }
3837
+ /**
3838
+ * Set the migration executor — the function that runs migration statement bodies.
3839
+ * This decouples the HotReloader from the runtime's statement interpreter.
3840
+ */
3841
+ setMigrationExecutor(executor) {
3842
+ this.migrationExecutor = executor;
3843
+ }
3844
+ /**
3845
+ * Register a template. Called on initial load.
3846
+ */
3847
+ registerTemplate(template) {
3848
+ this.templates.set(template.name, template);
3849
+ if (!this.instances.has(template.name)) {
3850
+ this.instances.set(template.name, []);
3851
+ }
3852
+ }
3853
+ /**
3854
+ * Register a live instance of a template.
3855
+ */
3856
+ registerInstance(instance) {
3857
+ const list = this.instances.get(instance.templateName);
3858
+ if (list) {
3859
+ list.push(instance);
3860
+ } else {
3861
+ this.instances.set(instance.templateName, [instance]);
3862
+ }
3863
+ }
3864
+ /**
3865
+ * Unregister an instance (e.g., on destroy).
3866
+ */
3867
+ unregisterInstance(holoId) {
3868
+ for (const [, list] of this.instances) {
3869
+ const idx = list.findIndex((i) => i.__holo_id === holoId);
3870
+ if (idx !== -1) {
3871
+ list.splice(idx, 1);
3872
+ return;
3873
+ }
3874
+ }
3875
+ }
3876
+ /**
3877
+ * Hot-reload a template. This is the main entry point.
3878
+ *
3879
+ * Steps:
3880
+ * 1. Snapshot all instances of the template
3881
+ * 2. Diff old vs new state schema
3882
+ * 3. Build migration chain if needed
3883
+ * 4. Apply migrations to all instances
3884
+ * 5. Validate and swap
3885
+ * 6. Rollback on failure
3886
+ */
3887
+ async reload(newTemplate) {
3888
+ const warnings = [];
3889
+ const templateName = newTemplate.name;
3890
+ const oldTemplate = this.templates.get(templateName);
3891
+ const oldVersion = oldTemplate?.version ?? 0;
3892
+ const newVersion = newTemplate.version ?? 0;
3893
+ const instanceList = this.instances.get(templateName) ?? [];
3894
+ const snapshots = /* @__PURE__ */ new Map();
3895
+ for (const instance of instanceList) {
3896
+ snapshots.set(instance.__holo_id, snapshotState(instance.state));
3897
+ }
3898
+ const diff = diffState(oldTemplate?.state, newTemplate.state);
3899
+ if (!diff.hasChanges && oldVersion === newVersion) {
3900
+ this.templates.set(templateName, newTemplate);
3901
+ const result = {
3902
+ success: true,
3903
+ templateName,
3904
+ oldVersion,
3905
+ newVersion,
3906
+ diff,
3907
+ migrationChain: null,
3908
+ instancesMigrated: 0,
3909
+ rollback: false,
3910
+ warnings
3911
+ };
3912
+ this.onReload(result);
3913
+ return result;
3914
+ }
3915
+ let migrationChain = null;
3916
+ if (newVersion > oldVersion || diff.requiresMigration) {
3917
+ migrationChain = buildMigrationChain(newTemplate, oldVersion, newVersion);
3918
+ if (!migrationChain) {
3919
+ if (diff.typeChanged.length > 0) {
3920
+ const error = `Missing migration chain from v${oldVersion} to v${newVersion} for template "${templateName}". ${diff.typeChanged.length} field(s) changed type: ${diff.typeChanged.map((c) => c.key).join(", ")}`;
3921
+ const result = {
3922
+ success: false,
3923
+ templateName,
3924
+ oldVersion,
3925
+ newVersion,
3926
+ diff,
3927
+ migrationChain: null,
3928
+ instancesMigrated: 0,
3929
+ rollback: true,
3930
+ error,
3931
+ warnings
3932
+ };
3933
+ this.onRollback(result);
3934
+ return result;
3935
+ }
3936
+ }
3937
+ }
3938
+ const oldDefaults = /* @__PURE__ */ new Map();
3939
+ if (oldTemplate?.state?.properties) {
3940
+ for (const prop of oldTemplate.state.properties) {
3941
+ oldDefaults.set(prop.key, prop.value);
3942
+ }
3943
+ }
3944
+ let migrated = 0;
3945
+ try {
3946
+ for (const instance of instanceList) {
3947
+ applyAutoMigration(instance.state, diff, oldDefaults);
3948
+ if (migrationChain && this.migrationExecutor) {
3949
+ for (const step of migrationChain.steps) {
3950
+ await this.migrationExecutor(instance, step.body);
3951
+ }
3952
+ }
3953
+ instance.version = newVersion;
3954
+ migrated++;
3955
+ }
3956
+ if (this.devMode) {
3957
+ for (const change of diff.removed) {
3958
+ warnings.push(`Field "${change.key}" removed from template "${templateName}"`);
3959
+ }
3960
+ for (const change of diff.reactiveChanged) {
3961
+ warnings.push(
3962
+ `Reactivity changed for field "${change.key}" in template "${templateName}"`
3963
+ );
3964
+ }
3965
+ }
3966
+ if (newTemplate.state?.properties) {
3967
+ for (const instance of instanceList) {
3968
+ for (const prop of newTemplate.state.properties) {
3969
+ if (!instance.state.has(prop.key)) {
3970
+ warnings.push(
3971
+ `Instance ${instance.__holo_id}: missing field "${prop.key}" after migration`
3972
+ );
3973
+ }
3974
+ }
3975
+ }
3976
+ }
3977
+ this.templates.set(templateName, newTemplate);
3978
+ const result = {
3979
+ success: true,
3980
+ templateName,
3981
+ oldVersion,
3982
+ newVersion,
3983
+ diff,
3984
+ migrationChain,
3985
+ instancesMigrated: migrated,
3986
+ rollback: false,
3987
+ warnings
3988
+ };
3989
+ this.onReload(result);
3990
+ return result;
3991
+ } catch (err) {
3992
+ for (const instance of instanceList) {
3993
+ const snapshot = snapshots.get(instance.__holo_id);
3994
+ if (snapshot) {
3995
+ instance.state = snapshot;
3996
+ instance.version = oldVersion;
3997
+ }
3998
+ }
3999
+ const result = {
4000
+ success: false,
4001
+ templateName,
4002
+ oldVersion,
4003
+ newVersion,
4004
+ diff,
4005
+ migrationChain,
4006
+ instancesMigrated: 0,
4007
+ rollback: true,
4008
+ error: err instanceof Error ? err.message : String(err),
4009
+ warnings
4010
+ };
4011
+ this.onRollback(result);
4012
+ return result;
4013
+ }
4014
+ }
4015
+ /**
4016
+ * Get all registered templates.
4017
+ */
4018
+ getTemplates() {
4019
+ return this.templates;
4020
+ }
4021
+ /**
4022
+ * Get all instances of a template.
4023
+ */
4024
+ getInstances(templateName) {
4025
+ return this.instances.get(templateName) ?? [];
4026
+ }
4027
+ };
4028
+
4029
+ // src/runtime/NetworkPredictor.ts
4030
+ var NetworkPredictor = class {
4031
+ inputBuffer = [];
4032
+ stateBuffer = [];
4033
+ lastConfirmedSequence = -1;
4034
+ predictedState;
4035
+ confirmedState;
4036
+ rtt = 0;
4037
+ jitter = 0;
4038
+ lastPingTime = 0;
4039
+ constructor(initialState) {
4040
+ this.predictedState = { ...initialState };
4041
+ this.confirmedState = { ...initialState };
4042
+ this.stateBuffer.push({ sequenceNumber: 0, state: { ...initialState } });
4043
+ }
4044
+ /**
4045
+ * Add a local input and predict the resulting state immediately.
4046
+ */
4047
+ predict(input, applyFn) {
4048
+ const seq = ++this.lastConfirmedSequence;
4049
+ const networkInput = {
4050
+ sequenceNumber: seq,
4051
+ timestamp: performance.now(),
4052
+ data: input
4053
+ };
4054
+ this.inputBuffer.push(networkInput);
4055
+ applyFn(this.predictedState, input);
4056
+ this.stateBuffer.push({
4057
+ sequenceNumber: seq,
4058
+ state: JSON.parse(JSON.stringify(this.predictedState))
4059
+ });
4060
+ if (this.stateBuffer.length > 120) {
4061
+ this.stateBuffer.shift();
4062
+ }
4063
+ return this.predictedState;
4064
+ }
4065
+ /**
4066
+ * Reconcile local state with authoritative server state.
4067
+ */
4068
+ reconcile(serverState, applyFn) {
4069
+ this.confirmedState = JSON.parse(JSON.stringify(serverState.state));
4070
+ this.inputBuffer = this.inputBuffer.filter(
4071
+ (i) => i.sequenceNumber > serverState.sequenceNumber
4072
+ );
4073
+ const newState = JSON.parse(JSON.stringify(this.confirmedState));
4074
+ for (const input of this.inputBuffer) {
4075
+ applyFn(newState, input.data);
4076
+ }
4077
+ this.predictedState = newState;
4078
+ return this.predictedState;
4079
+ }
4080
+ /**
4081
+ * Update network metrics for adaptive prediction.
4082
+ */
4083
+ updateMetrics(serverTimestamp) {
4084
+ const now = performance.now();
4085
+ const currentRtt = now - serverTimestamp;
4086
+ this.jitter = Math.abs(currentRtt - this.rtt) * 0.1 + this.jitter * 0.9;
4087
+ this.rtt = currentRtt * 0.1 + this.rtt * 0.9;
4088
+ }
4089
+ getPredictionHorizon() {
4090
+ return this.rtt / 2 + this.jitter * 2;
4091
+ }
4092
+ getPredictedState() {
4093
+ return this.predictedState;
4094
+ }
4095
+ };
4096
+
4097
+ // src/runtime/MovementPredictor.ts
4098
+ var MovementPredictor = class {
4099
+ lastPosition = { x: 0, y: 0, z: 0 };
4100
+ velocity = { x: 0, y: 0, z: 0 };
4101
+ history = [];
4102
+ maxHistory = 60;
4103
+ // 1 second at 60fps
4104
+ intent = null;
4105
+ /**
4106
+ * Convert Vector3 to tuple for consistent handling
4107
+ */
4108
+ toTuple(v) {
4109
+ return Array.isArray(v) ? [v[0], v[1], v[2]] : [v.x, v.y, v.z];
4110
+ }
4111
+ /**
4112
+ * Update internal state with current player transform
4113
+ */
4114
+ update(position, dt) {
4115
+ const currentPos = this.toTuple(position);
4116
+ if (dt > 0) {
4117
+ const lastPos = this.toTuple(this.lastPosition);
4118
+ this.velocity = {
4119
+ x: (currentPos[0] - lastPos[0]) / dt,
4120
+ y: (currentPos[1] - lastPos[1]) / dt,
4121
+ z: (currentPos[2] - lastPos[2]) / dt
4122
+ };
4123
+ }
4124
+ this.lastPosition = { x: currentPos[0], y: currentPos[1], z: currentPos[2] };
4125
+ this.history.push({ x: currentPos[0], y: currentPos[1], z: currentPos[2] });
4126
+ if (this.history.length > this.maxHistory) {
4127
+ this.history.shift();
4128
+ }
4129
+ }
4130
+ /**
4131
+ * Set the player's current intent signal (Tier 3)
4132
+ */
4133
+ setIntent(intent) {
4134
+ this.intent = intent;
4135
+ }
4136
+ /**
4137
+ * Tier 1: Linear Extrapolation
4138
+ */
4139
+ predictLinear(lookahead) {
4140
+ const lastPosTuple = this.toTuple(this.lastPosition);
4141
+ const velTuple = this.toTuple(this.velocity);
4142
+ return [
4143
+ lastPosTuple[0] + velTuple[0] * lookahead,
4144
+ lastPosTuple[1] + velTuple[1] * lookahead,
4145
+ lastPosTuple[2] + velTuple[2] * lookahead
4146
+ ];
4147
+ }
4148
+ /**
4149
+ * Tier 2: Non-linear pathing (Bezier-based extrapolation)
4150
+ * Analyzes path curvature to predict smooth turns.
4151
+ */
4152
+ predictRecurrent(lookahead) {
4153
+ if (this.history.length < 5) return this.predictLinear(lookahead);
4154
+ const p2 = this.toTuple(this.history[this.history.length - 1]);
4155
+ const p1 = this.toTuple(this.history[this.history.length - 3]);
4156
+ const p0 = this.toTuple(this.history[this.history.length - 5]);
4157
+ const v1 = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
4158
+ const v0 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
4159
+ const acc = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
4160
+ return [
4161
+ p2[0] + v1[0] * lookahead + 0.5 * acc[0] * lookahead ** 2,
4162
+ p2[1] + v1[1] * lookahead + 0.5 * acc[1] * lookahead ** 2,
4163
+ p2[2] + v1[2] * lookahead + 0.5 * acc[2] * lookahead ** 2
4164
+ ];
4165
+ }
4166
+ /**
4167
+ * Tier 3: Intent-based biasing
4168
+ */
4169
+ predictIntent(lookahead) {
4170
+ const recurrent = this.predictRecurrent(lookahead);
4171
+ if (!this.intent) return recurrent;
4172
+ const targetPos = this.intent.target;
4173
+ const weight = this.intent.weight;
4174
+ const targetPosTuple = this.toTuple(targetPos);
4175
+ return [
4176
+ recurrent[0] * (1 - weight) + targetPosTuple[0] * weight,
4177
+ recurrent[1] * (1 - weight) + targetPosTuple[1] * weight,
4178
+ recurrent[2] * (1 - weight) + targetPosTuple[2] * weight
4179
+ ];
4180
+ }
4181
+ /**
4182
+ * Get predictive windows for asset pre-fetching.
4183
+ * Combines all tiers into a prioritized set of windows.
4184
+ */
4185
+ getPredictiveWindows(lookaheadSeconds) {
4186
+ const windows = [];
4187
+ windows.push({
4188
+ center: this.toTuple(this.lastPosition),
4189
+ radius: 10,
4190
+ likelihood: 1
4191
+ });
4192
+ const speed = Math.sqrt(
4193
+ (this.velocity.x ?? 0) ** 2 + (this.velocity.y ?? 0) ** 2 + (this.velocity.z ?? 0) ** 2
4194
+ );
4195
+ if (speed > 0.5) {
4196
+ windows.push({
4197
+ center: this.predictLinear(lookaheadSeconds),
4198
+ radius: speed * lookaheadSeconds * 0.3 + 5,
4199
+ likelihood: 0.9
4200
+ });
4201
+ const ensembleCenter = this.predictIntent(lookaheadSeconds);
4202
+ windows.push({
4203
+ center: ensembleCenter,
4204
+ radius: speed * lookaheadSeconds * 0.6 + 8,
4205
+ likelihood: 0.7
4206
+ });
4207
+ }
4208
+ return windows;
4209
+ }
4210
+ };
4211
+
4212
+ // src/runtime/WebXRManager.ts
4213
+ var WebXRManager = class {
4214
+ session = null;
4215
+ referenceSpace = null;
4216
+ glBinding = null;
4217
+ projectionLayer = null;
4218
+ context;
4219
+ onSessionStart = null;
4220
+ onSessionEnd = null;
4221
+ onInputSourcesChange = null;
4222
+ constructor(context) {
4223
+ this.context = context;
4224
+ }
4225
+ /**
4226
+ * Check if WebXR is supported
4227
+ */
4228
+ static async isSupported() {
4229
+ if (typeof navigator !== "undefined" && "xr" in navigator) {
4230
+ return await navigator.xr.isSessionSupported("immersive-vr");
4231
+ }
4232
+ return false;
4233
+ }
4234
+ /**
4235
+ * Instance method to check if a specific session mode is supported.
4236
+ */
4237
+ async isSessionSupported(mode = "immersive-vr") {
4238
+ if (typeof navigator !== "undefined" && "xr" in navigator) {
4239
+ return await navigator.xr.isSessionSupported(mode);
4240
+ }
4241
+ return false;
4242
+ }
4243
+ /**
4244
+ * Trigger haptic pulse on a controller
4245
+ */
4246
+ triggerHaptic(hand, intensity, duration) {
4247
+ if (!this.session) return;
4248
+ for (const source of this.session.inputSources) {
4249
+ if (source.handedness === hand && source.gamepad) {
4250
+ const actuators = source.gamepad.hapticActuators;
4251
+ if (actuators && actuators.length > 0) {
4252
+ actuators[0].pulse(intensity, duration);
4253
+ }
4254
+ }
4255
+ }
4256
+ }
4257
+ /**
4258
+ * Request an immersive VR session
4259
+ */
4260
+ async requestSession(config = {}) {
4261
+ if (this.session) {
4262
+ console.warn("WebXR session already active");
4263
+ return this.session;
4264
+ }
4265
+ const sessionInit = {
4266
+ requiredFeatures: ["local-floor"],
4267
+ optionalFeatures: ["hand-tracking", "layers", ...config.features || []]
4268
+ };
4269
+ try {
4270
+ this.session = await navigator.xr.requestSession(
4271
+ "immersive-vr",
4272
+ sessionInit
4273
+ );
4274
+ this.session.addEventListener("end", this.handleSessionEnd);
4275
+ this.session.addEventListener(
4276
+ "inputsourceschange",
4277
+ this.handleInputSourcesChange
4278
+ );
4279
+ const g = globalThis;
4280
+ if (typeof g.XRWebGPUBinding !== "undefined") {
4281
+ this.glBinding = new g.XRWebGPUBinding(this.session, this.context.device);
4282
+ } else {
4283
+ console.warn("XRWebGPUBinding not found. Rendering may fail.");
4284
+ }
4285
+ this.referenceSpace = await this.session.requestReferenceSpace("local-floor");
4286
+ if (this.glBinding) {
4287
+ this.projectionLayer = this.glBinding.createProjectionLayer({
4288
+ colorFormat: this.context.format,
4289
+ depthStencilFormat: "depth24plus"
4290
+ });
4291
+ this.session.updateRenderState({ layers: [this.projectionLayer] });
4292
+ }
4293
+ this.onSessionStart?.(this.session);
4294
+ return this.session;
4295
+ } catch (error) {
4296
+ console.error("Failed to start WebXR session:", error);
4297
+ throw error;
4298
+ }
4299
+ }
4300
+ /**
4301
+ * Set a callback to be invoked each XR frame.
4302
+ */
4303
+ setAnimationLoop(callback) {
4304
+ this.animationLoopCallback = callback;
4305
+ if (this.session && callback) {
4306
+ const loop = (time, frame) => {
4307
+ if (this.animationLoopCallback) {
4308
+ this.animationLoopCallback(time, frame);
4309
+ this.session?.requestAnimationFrame?.(loop);
4310
+ }
4311
+ };
4312
+ this.session.requestAnimationFrame?.(loop);
4313
+ }
4314
+ }
4315
+ animationLoopCallback = null;
4316
+ /**
4317
+ * End the current session
4318
+ */
4319
+ async endSession() {
4320
+ if (this.session) {
4321
+ this.animationLoopCallback = null;
4322
+ await this.session.end();
4323
+ }
4324
+ }
4325
+ /**
4326
+ * Get the current XRSession
4327
+ */
4328
+ getSession() {
4329
+ return this.session;
4330
+ }
4331
+ /**
4332
+ * Get the current Reference Space
4333
+ */
4334
+ getReferenceSpace() {
4335
+ return this.referenceSpace;
4336
+ }
4337
+ /**
4338
+ * Get the WebGPU Binding
4339
+ */
4340
+ getBinding() {
4341
+ return this.glBinding;
4342
+ }
4343
+ /**
4344
+ * Get the active Projection Layer
4345
+ */
4346
+ getProjectionLayer() {
4347
+ return this.projectionLayer;
4348
+ }
4349
+ // ==========================================================================
4350
+ // HANDLERS
4351
+ // ==========================================================================
4352
+ handleSessionEnd = () => {
4353
+ this.session = null;
4354
+ this.referenceSpace = null;
4355
+ this.glBinding = null;
4356
+ this.projectionLayer = null;
4357
+ this.onSessionEnd?.();
4358
+ };
4359
+ handleInputSourcesChange = (event) => {
4360
+ this.onInputSourcesChange?.(event.added, event.removed);
4361
+ };
4362
+ };
4363
+
4364
+ // src/runtime/KeyboardSystem.ts
4365
+ var KeyboardSystem = class _KeyboardSystem {
4366
+ runtime;
4367
+ focusedInputId = null;
4368
+ cursorIndex = 0;
4369
+ static CHAR_WIDTH = 0.048;
4370
+ // Monospace approximation for fontSize 0.08
4371
+ static START_X = -0.2;
4372
+ constructor(runtime) {
4373
+ this.runtime = runtime;
4374
+ this.setupListeners();
4375
+ }
4376
+ setupListeners() {
4377
+ }
4378
+ handleEvent(event, payload) {
4379
+ if (event === "ui_press_end") {
4380
+ const nodeId = payload.nodeId;
4381
+ if (nodeId.includes("_key_")) {
4382
+ const key = nodeId.split("_key_")[1];
4383
+ this.handleKeyPress(key);
4384
+ } else if (nodeId.includes("input")) {
4385
+ if (this.focusedInputId && this.focusedInputId !== nodeId) {
4386
+ this.setCursorVisible(this.focusedInputId, false);
4387
+ }
4388
+ this.focusedInputId = nodeId;
4389
+ const text = this.getText(nodeId);
4390
+ this.cursorIndex = text.length;
4391
+ this.setCursorVisible(nodeId, true);
4392
+ this.updateCursorVisuals(nodeId);
4393
+ }
4394
+ }
4395
+ }
4396
+ handleKeyPress(key) {
4397
+ if (!this.focusedInputId) return;
4398
+ let currentText = this.getText(this.focusedInputId);
4399
+ switch (key) {
4400
+ case "LEFT":
4401
+ this.cursorIndex = Math.max(0, this.cursorIndex - 1);
4402
+ break;
4403
+ case "RIGHT":
4404
+ this.cursorIndex = Math.min(currentText.length, this.cursorIndex + 1);
4405
+ break;
4406
+ case "BACKSPACE":
4407
+ if (this.cursorIndex > 0) {
4408
+ const before = currentText.slice(0, this.cursorIndex - 1);
4409
+ const after = currentText.slice(this.cursorIndex);
4410
+ currentText = before + after;
4411
+ this.cursorIndex--;
4412
+ }
4413
+ break;
4414
+ case "SPACE":
4415
+ currentText = this.insertAtCursor(currentText, " ");
4416
+ this.cursorIndex++;
4417
+ break;
4418
+ default:
4419
+ if (key.length === 1) {
4420
+ currentText = this.insertAtCursor(currentText, key);
4421
+ this.cursorIndex++;
4422
+ }
4423
+ break;
4424
+ }
4425
+ this.updateInputState(this.focusedInputId, currentText);
4426
+ }
4427
+ insertAtCursor(text, char) {
4428
+ const before = text.slice(0, this.cursorIndex);
4429
+ const after = text.slice(this.cursorIndex);
4430
+ return before + char + after;
4431
+ }
4432
+ getText(nodeId) {
4433
+ const instance = this.runtime.findInstanceById(nodeId);
4434
+ return String(instance && instance.node.properties.text || "");
4435
+ }
4436
+ updateInputState(nodeId, text) {
4437
+ this.runtime.updateNodeProperty(nodeId, "text", text);
4438
+ const instance = this.runtime.findInstanceById(nodeId);
4439
+ if (instance && instance.children) {
4440
+ const textChild = instance.children.find(
4441
+ (c) => c.node.type === "text"
4442
+ );
4443
+ if (textChild) {
4444
+ this.runtime.updateNodeProperty(textChild.node.id, "text", text);
4445
+ }
4446
+ }
4447
+ this.updateCursorVisuals(nodeId);
4448
+ }
4449
+ setCursorVisible(nodeId, visible) {
4450
+ const instance = this.runtime.findInstanceById(nodeId);
4451
+ if (instance && instance.children) {
4452
+ const cursorChild = instance.children.find(
4453
+ (c) => c.node.properties.tag === "cursor"
4454
+ );
4455
+ if (cursorChild) {
4456
+ this.runtime.updateNodeProperty(cursorChild.node.id, "visible", visible);
4457
+ }
4458
+ }
4459
+ }
4460
+ updateCursorVisuals(nodeId) {
4461
+ const instance = this.runtime.findInstanceById(nodeId);
4462
+ if (!instance || !instance.children) return;
4463
+ const cursorChild = instance.children.find(
4464
+ (c) => c.node.properties.tag === "cursor"
4465
+ );
4466
+ if (cursorChild) {
4467
+ const newX = _KeyboardSystem.START_X + this.cursorIndex * _KeyboardSystem.CHAR_WIDTH;
4468
+ const currentPos = cursorChild.node.properties.position || { x: 0, y: 0, z: 0 };
4469
+ const newPos = { ...currentPos, x: newX };
4470
+ this.runtime.updateNodeProperty(cursorChild.node.id, "position", newPos);
4471
+ }
4472
+ }
4473
+ };
4474
+
4475
+ // src/runtime/HandMenuSystem.ts
4476
+ var createUIButton = (id, props) => ({ id, type: "button", properties: props });
4477
+ var createUIPanel = (id, props, children) => ({ id, type: "panel", properties: props, children });
4478
+ var HandMenuSystem = class {
4479
+ constructor(runtime, engine) {
4480
+ this.runtime = runtime;
4481
+ this.transitions = new TransitionSystem(engine || new AnimationEngine());
4482
+ }
4483
+ runtime;
4484
+ menuNodeId = null;
4485
+ menuNode = null;
4486
+ isMenuVisible = false;
4487
+ isTransitioning = false;
4488
+ lastToggleTime = 0;
4489
+ transitions;
4490
+ update(delta) {
4491
+ this.transitions.update(delta);
4492
+ const leftHand = this.runtime.vrContext?.hands?.left;
4493
+ if (!leftHand) return;
4494
+ if (this.checkPalmFacingUser(leftHand)) {
4495
+ if (!this.isMenuVisible && !this.isTransitioning && Date.now() - this.lastToggleTime > 1e3) {
4496
+ this.showMenu(leftHand);
4497
+ }
4498
+ } else {
4499
+ if (this.isMenuVisible && !this.isTransitioning) {
4500
+ this.hideMenu();
4501
+ }
4502
+ }
4503
+ }
4504
+ // Check if palm normal points to headset
4505
+ checkPalmFacingUser(_hand) {
4506
+ return false;
4507
+ }
4508
+ showMenu(hand) {
4509
+ if (this.menuNodeId) return;
4510
+ const menuId = "hand_menu_" + Date.now();
4511
+ const menu = createUIPanel(
4512
+ menuId,
4513
+ {
4514
+ width: 0.2,
4515
+ height: 0.15,
4516
+ color: "#1a1a1a"
4517
+ },
4518
+ [
4519
+ createUIButton(`${menuId}_btn1`, {
4520
+ text: "Home",
4521
+ position: [0, 0.03, 0.01],
4522
+ width: 0.18,
4523
+ height: 0.04,
4524
+ data: { action: "home" }
4525
+ }),
4526
+ createUIButton(`${menuId}_btn2`, {
4527
+ text: "Settings",
4528
+ position: [0, -0.03, 0.01],
4529
+ width: 0.18,
4530
+ height: 0.04,
4531
+ data: { action: "settings" }
4532
+ })
4533
+ ]
4534
+ );
4535
+ menu.properties.position = {
4536
+ x: hand.position.x,
4537
+ y: hand.position.y + 0.1,
4538
+ z: hand.position.z
4539
+ };
4540
+ menu.properties.opacity = 0;
4541
+ menu.properties.scale = 0;
4542
+ this.runtime.mountObject(menu);
4543
+ this.menuNodeId = menuId;
4544
+ this.menuNode = menu;
4545
+ this.isMenuVisible = true;
4546
+ this.isTransitioning = true;
4547
+ this.lastToggleTime = Date.now();
4548
+ this.transitions.popIn(
4549
+ menuId,
4550
+ (s) => {
4551
+ if (this.menuNode?.properties) this.menuNode.properties.scale = s;
4552
+ },
4553
+ (o) => {
4554
+ if (this.menuNode?.properties) this.menuNode.properties.opacity = o;
4555
+ },
4556
+ {
4557
+ duration: 0.35,
4558
+ onComplete: () => {
4559
+ this.isTransitioning = false;
4560
+ }
4561
+ }
4562
+ );
4563
+ }
4564
+ hideMenu() {
4565
+ if (!this.menuNodeId || !this.menuNode) return;
4566
+ this.isTransitioning = true;
4567
+ const nodeIdToRemove = this.menuNodeId;
4568
+ this.transitions.popOut(
4569
+ nodeIdToRemove,
4570
+ (s) => {
4571
+ if (this.menuNode?.properties) this.menuNode.properties.scale = s;
4572
+ },
4573
+ (o) => {
4574
+ if (this.menuNode?.properties) this.menuNode.properties.opacity = o;
4575
+ },
4576
+ {
4577
+ duration: 0.25,
4578
+ onComplete: () => {
4579
+ this.runtime.unmountObject(nodeIdToRemove);
4580
+ this.isTransitioning = false;
4581
+ }
4582
+ }
4583
+ );
4584
+ this.menuNodeId = null;
4585
+ this.menuNode = null;
4586
+ this.isMenuVisible = false;
4587
+ this.lastToggleTime = Date.now();
4588
+ }
4589
+ };
4590
+
4591
+ // src/runtime/HoloScriptPlusRuntime.ts
4592
+ var StateSync = class {
4593
+ constructor(_options) {
4594
+ }
4595
+ getInterpolatedState(_id) {
4596
+ return null;
4597
+ }
4598
+ };
4599
+ var HoloScriptPlusRuntimeImpl = class {
4600
+ ast;
4601
+ options;
4602
+ state;
4603
+ evaluator;
4604
+ builtins;
4605
+ traitRegistry;
4606
+ rootInstance = null;
4607
+ eventHandlers = /* @__PURE__ */ new Map();
4608
+ templates = /* @__PURE__ */ new Map();
4609
+ updateLoopId = null;
4610
+ lastUpdateTime = 0;
4611
+ companions;
4612
+ networkSync;
4613
+ mounted = false;
4614
+ scaleMultiplier = 1;
4615
+ chunkLoader = null;
4616
+ hotReloader;
4617
+ networkPredictor = null;
4618
+ movementPredictor;
4619
+ // Optimization: Flat list for iteration
4620
+ _flatEntities = [];
4621
+ // AI Copilot integration
4622
+ _copilot = null;
4623
+ /**
4624
+ * Set the AI Copilot for generate directive processing.
4625
+ */
4626
+ setCopilot(copilot) {
4627
+ this._copilot = copilot;
4628
+ }
4629
+ // VR context
4630
+ vrContext = {
4631
+ hands: {
4632
+ left: null,
4633
+ right: null
4634
+ },
4635
+ headset: {
4636
+ position: { x: 0, y: 1.6, z: 0 },
4637
+ rotation: { x: 0, y: 0, z: 0 }
4638
+ },
4639
+ controllers: {
4640
+ left: null,
4641
+ right: null
4642
+ }
4643
+ };
4644
+ // WebXR Manager
4645
+ webXrManager = null;
4646
+ isXRSessionActive = false;
4647
+ // Physics World
4648
+ physicsWorld;
4649
+ vrPhysicsBridge;
4650
+ debugDrawer = null;
4651
+ keyboardSystem;
4652
+ handMenuSystem;
4653
+ constructor(ast, options = {}) {
4654
+ this.ast = ast;
4655
+ this.options = options;
4656
+ console.log("[[TYPE OF PW]]", typeof PhysicsWorldImpl, PhysicsWorldImpl);
4657
+ this.physicsWorld = new PhysicsWorldImpl({
4658
+ gravity: { x: 0, y: -9.81, z: 0 },
4659
+ maxSubsteps: 2
4660
+ });
4661
+ this.vrPhysicsBridge = new VRPhysicsBridge(this.physicsWorld, (hand, intensity, duration) => {
4662
+ if (this.webXrManager) {
4663
+ this.webXrManager.triggerHaptic(hand, intensity, duration);
4664
+ }
4665
+ });
4666
+ if (options.renderer instanceof WebGPURenderer) {
4667
+ this.debugDrawer = new PhysicsDebugDrawer(this.physicsWorld, options.renderer);
4668
+ }
4669
+ this.keyboardSystem = new KeyboardSystem(this);
4670
+ this.handMenuSystem = new HandMenuSystem(this);
4671
+ this.on("physics_grab", (payload) => {
4672
+ const { nodeId, hand } = payload;
4673
+ const handBodyId = this.vrPhysicsBridge.getHandBodyId(hand);
4674
+ const objectBody = this.physicsWorld.getBody(nodeId);
4675
+ if (handBodyId && objectBody) {
4676
+ this.physicsWorld.createConstraint({
4677
+ type: "fixed",
4678
+ id: `grab_${handBodyId}_${nodeId}`,
4679
+ bodyA: handBodyId,
4680
+ bodyB: nodeId,
4681
+ pivotA: { x: 0, y: 0, z: 0 },
4682
+ // Center of hand
4683
+ pivotB: { x: 0, y: 0, z: 0 }
4684
+ // Should be relative offset
4685
+ // For simplicity, we snap center-to-center or need to calculate offset based on current positions
4686
+ });
4687
+ }
4688
+ });
4689
+ this.on("physics_release", (payload) => {
4690
+ const { nodeId, velocity } = payload;
4691
+ this.physicsWorld.removeConstraint(`grab_${nodeId}`);
4692
+ if (velocity) {
4693
+ const body = this.physicsWorld.getBody(nodeId);
4694
+ if (body) {
4695
+ body.velocity = { x: velocity[0], y: velocity[1], z: velocity[2] };
4696
+ }
4697
+ }
4698
+ });
4699
+ this.on("ui_press_end", (payload) => {
4700
+ this.keyboardSystem.handleEvent("ui_press_end", payload);
4701
+ });
4702
+ this.on("physics_add_constraint", (payload) => {
4703
+ const { type, nodeId, axis, min, max, spring } = payload;
4704
+ this.physicsWorld.createConstraint({
4705
+ type: "slider",
4706
+ // Mapped to slider for prismatic capability
4707
+ id: `constraint_slider_${nodeId}`,
4708
+ bodyA: "WORLD_ANCHOR",
4709
+ // Placeholder for "Static Anchor at start pos"
4710
+ bodyB: nodeId,
4711
+ pivotA: { x: 0, y: 0, z: 0 },
4712
+ axisA: axis || { x: 0, y: 1, z: 0 },
4713
+ limits: { low: min || 0, high: max || 0 }
4714
+ });
4715
+ });
4716
+ if (options.renderer && options.renderer.context && options.vrEnabled) {
4717
+ }
4718
+ const isNetworked = ast.root.traits?.has("networked") || ast.root.directives?.some(
4719
+ (d) => d.type === "sync" || d.type === "networked"
4720
+ );
4721
+ const _syncId = isNetworked ? ast.root.id || "global_session" : void 0;
4722
+ this.state = createState({});
4723
+ this.traitRegistry = vrTraitRegistry;
4724
+ this.companions = options.companions || {};
4725
+ this.builtins = createBuiltins(this);
4726
+ this.networkSync = new StateSync({ interpolation: true });
4727
+ this.evaluator = new ExpressionEvaluator(
4728
+ // @ts-expect-error - TS2554 structural type mismatch
4729
+ this.state.getSnapshot(),
4730
+ this.builtins
4731
+ );
4732
+ this.initializeState();
4733
+ this.loadImports();
4734
+ this.movementPredictor = new MovementPredictor();
4735
+ if (isNetworked) {
4736
+ this.networkPredictor = new NetworkPredictor(this.state.getSnapshot());
4737
+ }
4738
+ this.hotReloader = new HotReloader({ devMode: true });
4739
+ this.hotReloader.setMigrationExecutor(async (instance, body) => {
4740
+ let nodeInstance = this.findInstanceById(instance.__holo_id);
4741
+ if (!nodeInstance && instance.templateName === "@program" && this.rootInstance) {
4742
+ nodeInstance = this.rootInstance;
4743
+ }
4744
+ if (nodeInstance) {
4745
+ this.executeMigrationCode(nodeInstance, body);
4746
+ }
4747
+ });
4748
+ const self = this;
4749
+ if (this.ast.version !== void 0) {
4750
+ this.hotReloader.registerTemplate({
4751
+ type: "template",
4752
+ name: "@program",
4753
+ version: this.ast.version || 0,
4754
+ migrations: this.ast.migrations || [],
4755
+ state: { properties: [] }
4756
+ });
4757
+ const stateBridge = this.createStateMapProxy();
4758
+ this.hotReloader.registerInstance({
4759
+ __holo_id: "root",
4760
+ templateName: "@program",
4761
+ get version() {
4762
+ return self.ast.version || 0;
4763
+ },
4764
+ set version(v) {
4765
+ self.ast.version = v;
4766
+ },
4767
+ state: stateBridge
4768
+ });
4769
+ }
4770
+ const initialTemplates = this.findAllTemplates(this.ast.root);
4771
+ for (const [name, node] of initialTemplates) {
4772
+ this.templates.set(name, node);
4773
+ this.hotReloader.registerTemplate(node);
4774
+ }
4775
+ if (this.options.manifestUrl) {
4776
+ this.chunkLoader = new ChunkLoader(this, {
4777
+ manifestUrl: this.options.manifestUrl,
4778
+ baseUrl: this.options.baseUrl
4779
+ });
4780
+ this.chunkLoader.init();
4781
+ }
4782
+ }
4783
+ // ==========================================================================
4784
+ // WEBXR INTEGRATION
4785
+ // ==========================================================================
4786
+ async enterVR() {
4787
+ if (!this.options.vrEnabled) {
4788
+ console.warn("VR is not enabled in runtime options");
4789
+ return;
4790
+ }
4791
+ const renderer = this.options.renderer;
4792
+ if (!renderer) {
4793
+ console.error("Cannot enter VR: No renderer found");
4794
+ return;
4795
+ }
4796
+ if (!this.webXrManager) {
4797
+ const context = renderer.getContext ? renderer.getContext() : renderer.context;
4798
+ if (!context) {
4799
+ console.error("WebGPU context not found on renderer");
4800
+ return;
4801
+ }
4802
+ const ManagerClass = this.options.webXrManagerClass || WebXRManager;
4803
+ this.webXrManager = new ManagerClass(context);
4804
+ this.webXrManager.onSessionStart = (session) => {
4805
+ this.isXRSessionActive = true;
4806
+ this.stopUpdateLoop();
4807
+ if (renderer.setXRSession) {
4808
+ renderer.setXRSession(
4809
+ session,
4810
+ this.webXrManager.getBinding(),
4811
+ this.webXrManager.getProjectionLayer()
4812
+ );
4813
+ }
4814
+ this.webXrManager.setAnimationLoop(this.xrLoop.bind(this));
4815
+ };
4816
+ this.webXrManager.onSessionEnd = () => {
4817
+ this.isXRSessionActive = false;
4818
+ if (renderer.setXRSession) {
4819
+ renderer.setXRSession(null, null, null);
4820
+ }
4821
+ this.startUpdateLoop();
4822
+ };
4823
+ }
4824
+ if (await this.webXrManager.isSessionSupported("immersive-vr")) {
4825
+ await this.webXrManager.requestSession();
4826
+ } else {
4827
+ console.warn("WebXR not supported in this environment");
4828
+ }
4829
+ }
4830
+ /**
4831
+ * Main XR Loop - called by WebXRManager
4832
+ */
4833
+ xrLoop(time, frame) {
4834
+ const now = performance.now();
4835
+ const delta = (now - this.lastUpdateTime) / 1e3;
4836
+ this.lastUpdateTime = now;
4837
+ this.updateVRInput(frame);
4838
+ this.update(delta);
4839
+ if (this.handMenuSystem) this.handMenuSystem.update(delta);
4840
+ const renderer = this.options.renderer;
4841
+ if (renderer?.renderXR) {
4842
+ renderer.renderXR(frame);
4843
+ }
4844
+ }
4845
+ async exitVR() {
4846
+ if (this.webXrManager) {
4847
+ await this.webXrManager.endSession();
4848
+ }
4849
+ }
4850
+ /**
4851
+ * Convert Quaternion to Euler Angles (YXZ sequence)
4852
+ */
4853
+ quaternionToEuler(q) {
4854
+ const x = q[0], y = q[1], z = q[2], w = q[3];
4855
+ const t0 = 2 * (w * y - z * x);
4856
+ const ry = Math.asin(Math.max(-1, Math.min(1, t0)));
4857
+ const t1 = 2 * (w * x + y * z);
4858
+ const t2 = 1 - 2 * (x * x + y * y);
4859
+ const rx = Math.atan2(t1, t2);
4860
+ const t3 = 2 * (w * z + x * y);
4861
+ const t4 = 1 - 2 * (y * y + z * z);
4862
+ const rz = Math.atan2(t3, t4);
4863
+ return { x: rx, y: ry, z: rz };
4864
+ }
4865
+ updateVRInput(frame) {
4866
+ if (!this.isXRSessionActive || !this.webXrManager) return;
4867
+ const session = frame ? frame.session : this.webXrManager.getSession();
4868
+ const refSpace = this.webXrManager.getReferenceSpace();
4869
+ if (!session || !refSpace) return;
4870
+ if (frame) {
4871
+ const viewerPose = frame.getViewerPose(refSpace);
4872
+ if (viewerPose) {
4873
+ const { position, orientation } = viewerPose.transform;
4874
+ this.vrContext.headset.position = { x: position.x, y: position.y, z: position.z };
4875
+ const eulerH = this.quaternionToEuler([
4876
+ orientation.x,
4877
+ orientation.y,
4878
+ orientation.z,
4879
+ orientation.w
4880
+ ]);
4881
+ this.vrContext.headset.rotation = { x: eulerH[0], y: eulerH[1], z: eulerH[2] };
4882
+ }
4883
+ }
4884
+ for (const source of session.inputSources) {
4885
+ if (!source.gripSpace) continue;
4886
+ let pose;
4887
+ if (frame) {
4888
+ pose = frame.getPose(source.gripSpace, refSpace) ?? void 0;
4889
+ }
4890
+ if (pose) {
4891
+ const { position, orientation } = pose.transform;
4892
+ const handSide = source.handedness;
4893
+ const handData = {
4894
+ id: `${handSide}_hand`,
4895
+ grip: 0,
4896
+ trigger: 0,
4897
+ position: [position.x, position.y, position.z],
4898
+ rotation: this.quaternionToEuler([
4899
+ orientation.x,
4900
+ orientation.y,
4901
+ orientation.z,
4902
+ orientation.w
4903
+ ]),
4904
+ velocity: { x: 0, y: 0, z: 0 },
4905
+ // Not provided by WebXR directly without previous frame diff
4906
+ pinchStrength: 0,
4907
+ gripStrength: 0
4908
+ };
4909
+ if (source.gamepad) {
4910
+ if (source.gamepad.buttons.length > 0) {
4911
+ handData.pinchStrength = source.gamepad.buttons[0].value;
4912
+ }
4913
+ if (source.gamepad.buttons.length > 1) {
4914
+ handData.gripStrength = source.gamepad.buttons[1].value;
4915
+ }
4916
+ }
4917
+ const prevHand = handSide === "left" ? this.vrContext.hands.left : this.vrContext.hands.right;
4918
+ if (prevHand) {
4919
+ }
4920
+ if (handSide === "left") {
4921
+ this.vrContext.hands.left = handData;
4922
+ } else if (handSide === "right") {
4923
+ this.vrContext.hands.right = handData;
4924
+ }
4925
+ }
4926
+ }
4927
+ }
4928
+ // ==========================================================================
4929
+ // INITIALIZATION
4930
+ // ==========================================================================
4931
+ initializeState() {
4932
+ const stateDirective = (this.ast.root.directives || []).find(
4933
+ (d) => d.type === "state"
4934
+ );
4935
+ if (stateDirective && stateDirective.type === "state") {
4936
+ this.state.update(stateDirective.body);
4937
+ }
4938
+ }
4939
+ loadImports() {
4940
+ for (const imp of this.ast.imports || []) {
4941
+ const alias = imp.alias || imp.source || imp.path;
4942
+ if (this.companions[alias]) {
4943
+ continue;
4944
+ }
4945
+ console.warn(
4946
+ `Import ${imp.path || imp.source} not found. Provide via companions option.`
4947
+ );
4948
+ }
4949
+ }
4950
+ // ==========================================================================
4951
+ // MOUNTING
4952
+ // ==========================================================================
4953
+ mount(container) {
4954
+ if (this.mounted) {
4955
+ console.warn("Runtime already mounted");
4956
+ return;
4957
+ }
4958
+ this.mounted = true;
4959
+ this.rootInstance = this.instantiateNode(this.ast.root, null);
4960
+ if (this.options.renderer && this.rootInstance) {
4961
+ this.options.renderer.appendChild(container, this.rootInstance.renderedNode);
4962
+ }
4963
+ this.callLifecycle(this.rootInstance, "on_mount");
4964
+ this.startUpdateLoop();
4965
+ }
4966
+ unmount() {
4967
+ if (!this.mounted) return;
4968
+ this.stopUpdateLoop();
4969
+ if (this.rootInstance) {
4970
+ this.callLifecycle(this.rootInstance, "on_unmount");
4971
+ this.destroyInstance(this.rootInstance);
4972
+ }
4973
+ this.rootInstance = null;
4974
+ this.mounted = false;
4975
+ }
4976
+ /**
4977
+ * Scans the AST for all template definitions, including nested ones.
4978
+ */
4979
+ findAllTemplates(node, templates = /* @__PURE__ */ new Map()) {
4980
+ if (node.type === "template" && node.name) {
4981
+ templates.set(node.name, node);
4982
+ }
4983
+ if (node.children) {
4984
+ for (const child of node.children) {
4985
+ this.findAllTemplates(child, templates);
4986
+ }
4987
+ }
4988
+ if (node.type === "composition") {
4989
+ const comp = node;
4990
+ if (comp.children) {
4991
+ for (const child of comp.children) {
4992
+ this.findAllTemplates(child, templates);
4993
+ }
4994
+ }
4995
+ }
4996
+ return templates;
4997
+ }
4998
+ /**
4999
+ * Dynamically mount a new object into the scene (e.g. from a lazy-loaded chunk)
5000
+ */
5001
+ mountObject(node, parent = null) {
5002
+ const targetParent = parent || this.rootInstance;
5003
+ const instance = this.instantiateNode(node, targetParent);
5004
+ if (targetParent) {
5005
+ targetParent.children.push(instance);
5006
+ if (this.options.renderer && targetParent.renderedNode && instance.renderedNode) {
5007
+ this.options.renderer.appendChild(targetParent.renderedNode, instance.renderedNode);
5008
+ }
5009
+ }
5010
+ this.callLifecycle(instance, "on_mount");
5011
+ return instance;
5012
+ }
5013
+ unmountObject(idOrInstance) {
5014
+ let instance;
5015
+ if (typeof idOrInstance === "string") {
5016
+ instance = this._flatEntities.find(
5017
+ (n) => n.node.id === idOrInstance || n.__holo_id === idOrInstance
5018
+ );
5019
+ } else {
5020
+ instance = idOrInstance;
5021
+ }
5022
+ if (!instance) return;
5023
+ if (instance.parent) {
5024
+ const idx = instance.parent.children.indexOf(instance);
5025
+ if (idx > -1) instance.parent.children.splice(idx, 1);
5026
+ }
5027
+ const flatIdx = this._flatEntities.indexOf(instance);
5028
+ if (flatIdx > -1) this._flatEntities.splice(flatIdx, 1);
5029
+ if (this.options.renderer && instance.renderedNode) {
5030
+ this.options.renderer.destroy(instance.renderedNode);
5031
+ }
5032
+ this.callLifecycle(instance, "on_unmount");
5033
+ }
5034
+ // ==========================================================================
5035
+ // NODE INSTANTIATION
5036
+ // ==========================================================================
5037
+ _holoIdCounter = 0;
5038
+ generateHoloId(node) {
5039
+ const name = node.name || node.type || "obj";
5040
+ return `${name}_${++this._holoIdCounter}_${Date.now().toString(36)}`;
5041
+ }
5042
+ instantiateNode(node, parent) {
5043
+ const instance = {
5044
+ __holo_id: this.generateHoloId(node),
5045
+ node,
5046
+ get properties() {
5047
+ return this.node.properties || {};
5048
+ },
5049
+ renderedNode: null,
5050
+ lifecycleHandlers: /* @__PURE__ */ new Map(),
5051
+ children: [],
5052
+ parent,
5053
+ destroyed: false,
5054
+ dirty: true,
5055
+ hasTraits: !!(node.traits && node.traits.size > 0)
5056
+ };
5057
+ this._flatEntities.push(instance);
5058
+ let templateName = node.template || node.properties && node.properties.__templateRef;
5059
+ if (!templateName && this.templates.has(node.type)) {
5060
+ templateName = node.type;
5061
+ }
5062
+ if (templateName) {
5063
+ instance.templateName = templateName;
5064
+ const templateNode = this.templates.get(templateName);
5065
+ instance.templateVersion = templateNode?.version || node.version || 0;
5066
+ const stateBridge = this.createStateMapProxy();
5067
+ const _self = this;
5068
+ this.hotReloader.registerInstance({
5069
+ __holo_id: instance.__holo_id,
5070
+ templateName: instance.templateName,
5071
+ get version() {
5072
+ return instance.templateVersion || 0;
5073
+ },
5074
+ set version(v) {
5075
+ instance.templateVersion = v;
5076
+ },
5077
+ state: stateBridge
5078
+ });
5079
+ }
5080
+ const templateNodeForDirectives = templateName ? this.templates.get(templateName) : null;
5081
+ this.processDirectives(instance, templateNodeForDirectives?.directives);
5082
+ if (this.options.renderer) {
5083
+ const properties = node.properties ? this.evaluateProperties(node.properties) : {};
5084
+ instance.renderedNode = this.options.renderer.createElement(node.type, properties);
5085
+ }
5086
+ if (node.traits) {
5087
+ for (const [traitName, config] of node.traits) {
5088
+ this.traitRegistry.attachTrait(node, traitName, config, this.createTraitContext(instance));
5089
+ }
5090
+ }
5091
+ const childrenNodes = node.children || node.body || [];
5092
+ const children = this.processControlFlow(childrenNodes, node.directives || []);
5093
+ for (const childNode of children) {
5094
+ const childInstance = this.instantiateNode(childNode, instance);
5095
+ instance.children.push(childInstance);
5096
+ if (this.options.renderer && instance.renderedNode) {
5097
+ this.options.renderer.appendChild(instance.renderedNode, childInstance.renderedNode);
5098
+ }
5099
+ }
5100
+ return instance;
5101
+ }
5102
+ getNode(id) {
5103
+ const instance = this._flatEntities.find((n) => n.node.id === id || n.__holo_id === id);
5104
+ return instance ? instance.node : void 0;
5105
+ }
5106
+ processDirectives(instance, extraDirectives) {
5107
+ const directives = [...instance.node.directives || [], ...extraDirectives || []];
5108
+ for (const directive of directives) {
5109
+ if (directive.type === "lifecycle") {
5110
+ this.registerLifecycleHandler(instance, directive);
5111
+ } else if (directive.type === "state") {
5112
+ const stateBody = directive.body || {};
5113
+ for (const [key, value] of Object.entries(stateBody)) {
5114
+ if (!this.state.has(key)) {
5115
+ this.state.set(key, value);
5116
+ }
5117
+ }
5118
+ }
5119
+ }
5120
+ }
5121
+ registerLifecycleHandler(instance, directive) {
5122
+ const { hook, params, body } = directive;
5123
+ const handler = (...args) => {
5124
+ const paramContext = {};
5125
+ if (params) {
5126
+ params.forEach((param, i) => {
5127
+ paramContext[param] = args[i];
5128
+ });
5129
+ }
5130
+ this.evaluator.updateContext({
5131
+ ...this.state.getSnapshot(),
5132
+ ...paramContext,
5133
+ node: instance.node,
5134
+ self: instance.node
5135
+ });
5136
+ try {
5137
+ if (body.includes(";") || body.includes("{")) {
5138
+ new Function(
5139
+ ...Object.keys(this.builtins),
5140
+ ...Object.keys(paramContext),
5141
+ "state",
5142
+ "node",
5143
+ body
5144
+ )(
5145
+ ...Object.values(this.builtins),
5146
+ ...Object.values(paramContext),
5147
+ this.state,
5148
+ instance.node
5149
+ );
5150
+ } else {
5151
+ this.evaluator.evaluate(body);
5152
+ }
5153
+ } catch (error) {
5154
+ console.error(`Error in lifecycle handler ${hook}:`, error);
5155
+ }
5156
+ };
5157
+ if (!instance.lifecycleHandlers.has(hook)) {
5158
+ instance.lifecycleHandlers.set(hook, []);
5159
+ }
5160
+ instance.lifecycleHandlers.get(hook).push(handler);
5161
+ }
5162
+ // ==========================================================================
5163
+ // CONTROL FLOW
5164
+ // ==========================================================================
5165
+ processControlFlow(children, directives) {
5166
+ const result = [];
5167
+ for (const directive of directives) {
5168
+ if (directive.type === "for") {
5169
+ const items = this.evaluateExpression(directive.iterable);
5170
+ if (Array.isArray(items)) {
5171
+ items.forEach((item, index) => {
5172
+ const iterContext = {
5173
+ [directive.variable]: item,
5174
+ index,
5175
+ first: index === 0,
5176
+ last: index === items.length - 1,
5177
+ even: index % 2 === 0,
5178
+ odd: index % 2 !== 0
5179
+ };
5180
+ for (const bodyNode of directive.body) {
5181
+ const cloned = this.cloneNodeWithContext(bodyNode, iterContext);
5182
+ result.push(cloned);
5183
+ }
5184
+ });
5185
+ }
5186
+ } else if (directive.type === "forEach") {
5187
+ const items = this.evaluateExpression(
5188
+ directive.collection
5189
+ );
5190
+ if (Array.isArray(items)) {
5191
+ items.forEach((item, index) => {
5192
+ const iterContext = {
5193
+ [directive.variable]: item,
5194
+ index,
5195
+ first: index === 0,
5196
+ last: index === items.length - 1,
5197
+ even: index % 2 === 0,
5198
+ odd: index % 2 !== 0
5199
+ };
5200
+ for (const bodyNode of directive.body) {
5201
+ const cloned = this.cloneNodeWithContext(bodyNode, iterContext);
5202
+ result.push(cloned);
5203
+ }
5204
+ });
5205
+ }
5206
+ } else if (directive.type === "while") {
5207
+ const MAX_ITERATIONS = 1e3;
5208
+ let iterations = 0;
5209
+ while (iterations < MAX_ITERATIONS) {
5210
+ const condition = this.evaluateExpression(
5211
+ directive.condition
5212
+ );
5213
+ if (!condition) break;
5214
+ const iterContext = {
5215
+ iteration: iterations,
5216
+ index: iterations
5217
+ };
5218
+ for (const bodyNode of directive.body) {
5219
+ const cloned = this.cloneNodeWithContext(bodyNode, iterContext);
5220
+ result.push(cloned);
5221
+ }
5222
+ iterations++;
5223
+ }
5224
+ if (iterations >= MAX_ITERATIONS) {
5225
+ console.warn("@while loop hit maximum iteration limit (1000)");
5226
+ }
5227
+ } else if (directive.type === "if") {
5228
+ const condition = this.evaluateExpression(
5229
+ directive.condition
5230
+ );
5231
+ if (condition) {
5232
+ result.push(...directive.body);
5233
+ } else if (directive.else) {
5234
+ result.push(...directive.else);
5235
+ }
5236
+ }
5237
+ }
5238
+ result.push(...children);
5239
+ return result;
5240
+ }
5241
+ cloneNodeWithContext(node, context) {
5242
+ const cloned = {
5243
+ type: node.type,
5244
+ id: node.id ? this.interpolateString(node.id, context) : void 0,
5245
+ properties: node.properties ? this.interpolateProperties(node.properties, context) : {},
5246
+ directives: node.directives ? [...node.directives] : [],
5247
+ children: (node.children || []).map(
5248
+ (child) => this.cloneNodeWithContext(child, context)
5249
+ ),
5250
+ traits: node.traits ? new Map(node.traits) : /* @__PURE__ */ new Map(),
5251
+ loc: node.loc
5252
+ };
5253
+ return cloned;
5254
+ }
5255
+ interpolateString(str, context) {
5256
+ return str.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
5257
+ this.evaluator.updateContext(context);
5258
+ const value = this.evaluator.evaluate(expr);
5259
+ return String(value ?? "");
5260
+ });
5261
+ }
5262
+ interpolateProperties(properties, context) {
5263
+ const result = {};
5264
+ for (const [key, value] of Object.entries(properties)) {
5265
+ if (typeof value === "string") {
5266
+ result[key] = this.interpolateString(value, context);
5267
+ } else if (value && typeof value === "object" && "__expr" in value) {
5268
+ this.evaluator.updateContext(context);
5269
+ result[key] = this.evaluator.evaluate(value.__raw);
5270
+ } else {
5271
+ result[key] = value;
5272
+ }
5273
+ }
5274
+ return result;
5275
+ }
5276
+ // ==========================================================================
5277
+ // EXPRESSION EVALUATION
5278
+ // ==========================================================================
5279
+ evaluateExpression(expr) {
5280
+ this.evaluator.updateContext(this.state.getSnapshot());
5281
+ return this.evaluator.evaluate(expr);
5282
+ }
5283
+ evaluateProperties(properties) {
5284
+ const expandedProperties = this.expandPropertySpreads(properties);
5285
+ const result = {};
5286
+ for (const [key, value] of Object.entries(expandedProperties)) {
5287
+ if (value && typeof value === "object" && "__expr" in value) {
5288
+ result[key] = this.evaluateExpression(value.__raw);
5289
+ } else if (value && typeof value === "object" && "__ref" in value) {
5290
+ const ref = value.__ref;
5291
+ result[key] = this.state.get(ref) ?? ref;
5292
+ } else if (typeof value === "string" && value.includes("${")) {
5293
+ result[key] = this.interpolateString(value, this.state.getSnapshot());
5294
+ } else {
5295
+ result[key] = value;
5296
+ }
5297
+ }
5298
+ return result;
5299
+ }
5300
+ /**
5301
+ * Expands spread expressions in a properties object.
5302
+ * Spread keys are formatted as __spread_N with value { type: 'spread', argument: ... }
5303
+ */
5304
+ expandPropertySpreads(properties) {
5305
+ const result = {};
5306
+ const spreadKeys = [];
5307
+ for (const [key, value] of Object.entries(properties)) {
5308
+ if (key.startsWith("__spread_")) {
5309
+ spreadKeys.push(key);
5310
+ } else if (value && typeof value === "object" && "type" in value && value.type === "spread") {
5311
+ spreadKeys.push(key);
5312
+ } else {
5313
+ if (value && typeof value === "object" && !Array.isArray(value) && !("__ref" in value) && !("__expr" in value)) {
5314
+ result[key] = this.expandPropertySpreads(value);
5315
+ } else if (Array.isArray(value)) {
5316
+ result[key] = this.expandArraySpreads(value);
5317
+ } else {
5318
+ result[key] = value;
5319
+ }
5320
+ }
5321
+ }
5322
+ for (const spreadKey of spreadKeys) {
5323
+ const spreadValue = properties[spreadKey];
5324
+ if (spreadValue && typeof spreadValue === "object" && "type" in spreadValue && spreadValue.type === "spread") {
5325
+ const resolved = this.resolveSpreadArgument(
5326
+ spreadValue.argument
5327
+ );
5328
+ if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
5329
+ Object.assign(result, this.expandPropertySpreads(resolved));
5330
+ }
5331
+ }
5332
+ }
5333
+ for (const [key, value] of Object.entries(properties)) {
5334
+ if (!key.startsWith("__spread_") && !(value && typeof value === "object" && "type" in value && value.type === "spread")) {
5335
+ if (value && typeof value === "object" && !Array.isArray(value) && !("__ref" in value) && !("__expr" in value)) {
5336
+ result[key] = this.expandPropertySpreads(value);
5337
+ } else if (Array.isArray(value)) {
5338
+ result[key] = this.expandArraySpreads(value);
5339
+ } else {
5340
+ result[key] = value;
5341
+ }
5342
+ }
5343
+ }
5344
+ return result;
5345
+ }
5346
+ /**
5347
+ * Expands spread expressions within an array.
5348
+ */
5349
+ expandArraySpreads(arr) {
5350
+ const result = [];
5351
+ for (const item of arr) {
5352
+ if (item && typeof item === "object" && "type" in item && item.type === "spread") {
5353
+ const resolved = this.resolveSpreadArgument(item.argument);
5354
+ if (Array.isArray(resolved)) {
5355
+ result.push(...resolved);
5356
+ } else if (resolved !== void 0 && resolved !== null) {
5357
+ result.push(resolved);
5358
+ }
5359
+ } else if (item && typeof item === "object" && !Array.isArray(item)) {
5360
+ result.push(this.expandPropertySpreads(item));
5361
+ } else {
5362
+ result.push(item);
5363
+ }
5364
+ }
5365
+ return result;
5366
+ }
5367
+ /**
5368
+ * Resolves a spread argument to its runtime value.
5369
+ */
5370
+ resolveSpreadArgument(argument) {
5371
+ if (argument === null || argument === void 0) {
5372
+ return void 0;
5373
+ }
5374
+ if (typeof argument === "object" && !("__ref" in argument)) {
5375
+ return argument;
5376
+ }
5377
+ if (typeof argument === "object" && "__ref" in argument) {
5378
+ const ref = argument.__ref;
5379
+ const stateValue = this.state.get(ref);
5380
+ if (stateValue !== void 0) {
5381
+ return stateValue;
5382
+ }
5383
+ if (ref.includes(".")) {
5384
+ const snapshot = this.state.getSnapshot();
5385
+ const parts = ref.split(".");
5386
+ let value = snapshot;
5387
+ for (const part of parts) {
5388
+ if (value && typeof value === "object" && part in value) {
5389
+ value = value[part];
5390
+ } else {
5391
+ return void 0;
5392
+ }
5393
+ }
5394
+ return value;
5395
+ }
5396
+ return void 0;
5397
+ }
5398
+ if (typeof argument === "string") {
5399
+ return this.state.get(argument);
5400
+ }
5401
+ return argument;
5402
+ }
5403
+ // ==========================================================================
5404
+ // LIFECYCLE
5405
+ // ==========================================================================
5406
+ callLifecycle(instance, hook, ...args) {
5407
+ if (!instance || instance.destroyed) return;
5408
+ const handlers = instance.lifecycleHandlers.get(hook);
5409
+ if (handlers) {
5410
+ handlers.forEach((handler) => {
5411
+ try {
5412
+ handler(...args);
5413
+ } catch (error) {
5414
+ console.error(`Error in lifecycle ${hook}:`, error);
5415
+ }
5416
+ });
5417
+ }
5418
+ for (const child of instance.children) {
5419
+ this.callLifecycle(child, hook, ...args);
5420
+ }
5421
+ }
5422
+ // ==========================================================================
5423
+ // UPDATE LOOP
5424
+ // ==========================================================================
5425
+ startUpdateLoop() {
5426
+ const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(() => cb(performance.now()), 16);
5427
+ this.lastUpdateTime = performance.now();
5428
+ const update = () => {
5429
+ const now = performance.now();
5430
+ const delta = (now - this.lastUpdateTime) / 1e3;
5431
+ this.lastUpdateTime = now;
5432
+ this.updateVRInput();
5433
+ this.update(delta);
5434
+ this.updateLoopId = raf(update);
5435
+ };
5436
+ this.updateLoopId = raf(update);
5437
+ }
5438
+ stopUpdateLoop() {
5439
+ if (this.updateLoopId !== null) {
5440
+ if (typeof cancelAnimationFrame !== "undefined") {
5441
+ cancelAnimationFrame(this.updateLoopId);
5442
+ } else {
5443
+ clearTimeout(this.updateLoopId);
5444
+ }
5445
+ this.updateLoopId = null;
5446
+ }
5447
+ }
5448
+ update(delta) {
5449
+ if (!this.rootInstance) return;
5450
+ try {
5451
+ this.vrPhysicsBridge.update(this.vrContext, delta);
5452
+ } catch (e) {
5453
+ console.error("[Runtime] Error in vrPhysicsBridge.update:", e);
5454
+ }
5455
+ this.physicsWorld.step(delta);
5456
+ if (this.debugDrawer) {
5457
+ this.debugDrawer.update();
5458
+ }
5459
+ const count = this._flatEntities.length;
5460
+ for (let i = 0; i < count; i++) {
5461
+ this.updateInstance(this._flatEntities[i], delta);
5462
+ }
5463
+ this.callLifecycle(this.rootInstance, "on_update", delta);
5464
+ if (this.chunkLoader) {
5465
+ if (this.chunkLoader) this.chunkLoader.update();
5466
+ }
5467
+ }
5468
+ /**
5469
+ * Authoritative server sync for networked state.
5470
+ */
5471
+ onNetworkStateUpdate(serverState) {
5472
+ if (this.networkPredictor) {
5473
+ const reconciled = this.networkPredictor.reconcile(serverState, (state, input) => {
5474
+ if (input.type === "state_update") {
5475
+ Object.assign(state, input.payload);
5476
+ }
5477
+ });
5478
+ this.state.update(reconciled);
5479
+ this.networkPredictor.updateMetrics(performance.now() - 100);
5480
+ }
5481
+ }
5482
+ updateInstance(instance, delta) {
5483
+ if (instance.destroyed) return;
5484
+ if (instance.hasTraits) {
5485
+ const traitContext = this.createTraitContext(instance);
5486
+ this.traitRegistry.updateAllTraits(instance.node, traitContext, delta);
5487
+ }
5488
+ if (instance.node.type === "avatar") {
5489
+ this.syncAvatarParts(instance);
5490
+ }
5491
+ if (instance.node.traits?.has("networked")) {
5492
+ const interpolated = this.networkSync.getInterpolatedState(
5493
+ instance.node.id || ""
5494
+ );
5495
+ const body = this.physicsWorld.getBody(instance.node.id || "");
5496
+ if (interpolated && instance.node.properties) {
5497
+ instance.dirty = true;
5498
+ if (interpolated.position) {
5499
+ instance.node.properties.position = [
5500
+ interpolated.position.x,
5501
+ interpolated.position.y,
5502
+ interpolated.position.z
5503
+ ];
5504
+ }
5505
+ if (interpolated.rotation) {
5506
+ instance.node.properties.rotation = [
5507
+ interpolated.rotation.x,
5508
+ interpolated.rotation.y,
5509
+ interpolated.rotation.z
5510
+ ];
5511
+ }
5512
+ if (body) {
5513
+ if (body.type !== "kinematic") {
5514
+ if (!instance.node.__originalPhysicsType) {
5515
+ instance.node.__originalPhysicsType = body.type;
5516
+ }
5517
+ body.type = "kinematic";
5518
+ }
5519
+ if (interpolated.position) {
5520
+ const pos = interpolated.position;
5521
+ body.position = {
5522
+ x: pos.x,
5523
+ y: pos.y,
5524
+ z: pos.z
5525
+ };
5526
+ body.velocity = { x: 0, y: 0, z: 0 };
5527
+ }
5528
+ if (interpolated.rotation) {
5529
+ const rot = interpolated.rotation;
5530
+ body.rotation = {
5531
+ x: rot.x,
5532
+ y: rot.y,
5533
+ z: rot.z,
5534
+ w: rot.w || 1
5535
+ };
5536
+ body.angularVelocity = { x: 0, y: 0, z: 0 };
5537
+ }
5538
+ }
5539
+ } else if (!interpolated && body && instance.node.__originalPhysicsType) {
5540
+ if (body.type === "kinematic" && instance.node.__originalPhysicsType !== "kinematic") {
5541
+ body.type = instance.node.__originalPhysicsType;
5542
+ }
5543
+ }
5544
+ }
5545
+ if (instance.dirty && this.options.renderer && instance.renderedNode) {
5546
+ const properties = this.evaluateProperties(instance.node.properties || {});
5547
+ this.options.renderer.updateElement(instance.renderedNode, properties);
5548
+ instance.dirty = false;
5549
+ }
5550
+ this.updateExternalApis(instance, delta);
5551
+ this.processGenerateDirectives(instance);
5552
+ }
5553
+ syncAvatarParts(instance) {
5554
+ const vrHands = this.vrContext.hands;
5555
+ const vrHead = this.vrContext.headset;
5556
+ if (instance.node.id === "local_player" && instance.node.properties) {
5557
+ instance.node.properties.position = vrHead.position;
5558
+ instance.node.properties.rotation = vrHead.rotation;
5559
+ instance.children.forEach((child) => {
5560
+ if (child.node.id === "left_hand" && vrHands.left && child.node.properties) {
5561
+ child.node.properties.position = vrHands.left.position;
5562
+ child.node.properties.rotation = vrHands.left.rotation;
5563
+ } else if (child.node.id === "right_hand" && vrHands.right && child.node.properties) {
5564
+ child.node.properties.position = vrHands.right.position;
5565
+ child.node.properties.rotation = vrHands.right.rotation;
5566
+ }
5567
+ });
5568
+ if (instance.node.traits?.has("networked")) {
5569
+ this.emit("network_snapshot", {
5570
+ objectId: instance.node.id,
5571
+ position: [
5572
+ instance.node.properties.position[0],
5573
+ instance.node.properties.position[1],
5574
+ instance.node.properties.position[2]
5575
+ ],
5576
+ rotation: [
5577
+ instance.node.properties.rotation[0],
5578
+ instance.node.properties.rotation[1],
5579
+ instance.node.properties.rotation[2]
5580
+ ]
5581
+ });
5582
+ }
5583
+ }
5584
+ }
5585
+ generatedNodes = /* @__PURE__ */ new Set();
5586
+ processGenerateDirectives(instance) {
5587
+ if (!instance.node.directives) return;
5588
+ const generateDirectives = instance.node.directives.filter((d) => d.type === "generate");
5589
+ for (const d of generateDirectives) {
5590
+ const directive = d;
5591
+ const genId = `${instance.node.id || "node"}_${directive.prompt.substring(0, 10)}`;
5592
+ if (this.generatedNodes.has(genId)) continue;
5593
+ if (this._copilot && this._copilot.isReady()) {
5594
+ this._copilot.generateFromPrompt(directive.prompt, {
5595
+ context: directive.context
5596
+ }).then((response) => {
5597
+ this.emit("generate_complete", {
5598
+ id: genId,
5599
+ nodeId: instance.node.id,
5600
+ result: response
5601
+ });
5602
+ }).catch(() => {
5603
+ });
5604
+ } else {
5605
+ this.emit("generate_request", {
5606
+ id: genId,
5607
+ nodeId: instance.node.id,
5608
+ prompt: directive.prompt,
5609
+ context: directive.context,
5610
+ target: directive.target || "children"
5611
+ });
5612
+ }
5613
+ this.generatedNodes.add(genId);
5614
+ }
5615
+ }
5616
+ apiPollingTimers = /* @__PURE__ */ new Map();
5617
+ updateExternalApis(instance, _delta) {
5618
+ if (!instance.node.directives) return;
5619
+ const apiDirectives = instance.node.directives.filter((d) => d.type === "external_api");
5620
+ for (const d of apiDirectives) {
5621
+ const directive = d;
5622
+ if (directive.type !== "external_api") continue;
5623
+ const intervalStr = directive.interval || "0s";
5624
+ const intervalMs = this.parseDurationToMs(String(intervalStr));
5625
+ if (intervalMs <= 0) continue;
5626
+ const lastTime = this.apiPollingTimers.get(instance) || 0;
5627
+ const now = performance.now();
5628
+ if (now - lastTime >= intervalMs) {
5629
+ this.apiPollingTimers.set(instance, now);
5630
+ this.executeExternalApi(instance, directive);
5631
+ }
5632
+ }
5633
+ }
5634
+ async executeExternalApi(instance, directive) {
5635
+ try {
5636
+ const apiCall = this.builtins["api_call"];
5637
+ const data = apiCall ? await apiCall(String(directive.url), String(directive.method || "GET")) : void 0;
5638
+ this.state.set("api_data", data);
5639
+ this.updateData(data);
5640
+ } catch (error) {
5641
+ console.error(`External API error for ${directive.url}:`, error);
5642
+ }
5643
+ }
5644
+ parseDurationToMs(duration) {
5645
+ const match = duration.match(/^(\d+)(ms|s|m)$/);
5646
+ if (!match) return 0;
5647
+ const value = parseInt(match[1], 10);
5648
+ const unit = match[2];
5649
+ switch (unit) {
5650
+ case "ms":
5651
+ return value;
5652
+ case "s":
5653
+ return value * 1e3;
5654
+ case "m":
5655
+ return value * 6e4;
5656
+ default:
5657
+ return 0;
5658
+ }
5659
+ }
5660
+ // ==========================================================================
5661
+ // TRAIT CONTEXT
5662
+ // ==========================================================================
5663
+ createTraitContext(_instance) {
5664
+ return {
5665
+ vr: {
5666
+ hands: this.vrContext.hands,
5667
+ headset: this.vrContext.headset,
5668
+ getPointerRay: (hand) => {
5669
+ const vrHand = hand === "left" ? this.vrContext.hands.left : this.vrContext.hands.right;
5670
+ if (!vrHand) return null;
5671
+ return {
5672
+ origin: vrHand.position,
5673
+ direction: { x: 0, y: 0, z: -1 }
5674
+ // Forward direction - should be calculated from rotation
5675
+ };
5676
+ },
5677
+ getDominantHand: () => this.vrContext.hands.right || this.vrContext.hands.left
5678
+ },
5679
+ physics: {
5680
+ applyVelocity: (node, velocity) => {
5681
+ const body = this.physicsWorld.getBody(node.id || "");
5682
+ if (body) {
5683
+ body.velocity = {
5684
+ x: velocity[0],
5685
+ y: velocity[1],
5686
+ z: velocity[2]
5687
+ };
5688
+ }
5689
+ },
5690
+ applyAngularVelocity: (node, angularVelocity) => {
5691
+ const body = this.physicsWorld.getBody(node.id || "");
5692
+ if (body) {
5693
+ body.angularVelocity = {
5694
+ x: angularVelocity[0],
5695
+ y: angularVelocity[1],
5696
+ z: angularVelocity[2]
5697
+ };
5698
+ }
5699
+ },
5700
+ setKinematic: (node, kinematic) => {
5701
+ const body = this.physicsWorld.getBody(node.id || "");
5702
+ if (body) {
5703
+ body.type = kinematic ? "kinematic" : "dynamic";
5704
+ }
5705
+ },
5706
+ raycast: (origin, direction, maxDistance) => {
5707
+ const hit = this.physicsWorld.raycastClosest({
5708
+ origin: { x: origin[0], y: origin[1], z: origin[2] },
5709
+ direction: {
5710
+ x: direction[0],
5711
+ y: direction[1],
5712
+ z: direction[2]
5713
+ },
5714
+ maxDistance
5715
+ });
5716
+ if (hit) {
5717
+ const h = hit;
5718
+ return {
5719
+ point: { x: h.point.x || 0, y: h.point.y || 0, z: h.point.z || 0 },
5720
+ normal: { x: h.normal.x || 0, y: h.normal.y || 0, z: h.normal.z || 0 },
5721
+ distance: h.distance,
5722
+ bodyId: h.bodyId,
5723
+ nodeId: h.bodyId,
5724
+ node: h.bodyId
5725
+ };
5726
+ }
5727
+ return null;
5728
+ },
5729
+ getBodyPosition: (nodeId) => {
5730
+ const body = this.physicsWorld.getBody(nodeId);
5731
+ if (body && body.position) return { x: body.position.x || 0, y: body.position.y || 0, z: body.position.z || 0 };
5732
+ return null;
5733
+ },
5734
+ getBodyVelocity: (nodeId) => {
5735
+ const body = this.physicsWorld.getBody(nodeId);
5736
+ if (body && body.velocity) return { x: body.velocity.x || 0, y: body.velocity.y || 0, z: body.velocity.z || 0 };
5737
+ return null;
5738
+ }
5739
+ },
5740
+ audio: {
5741
+ playSound: (source, options) => {
5742
+ this.emit("play_sound", { source, ...options });
5743
+ }
5744
+ },
5745
+ haptics: {
5746
+ pulse: (hand, intensity, duration) => {
5747
+ this.emit("haptic", { hand, intensity, duration, type: "pulse" });
5748
+ },
5749
+ rumble: (hand, intensity) => {
5750
+ this.emit("haptic", { hand, intensity, type: "rumble" });
5751
+ }
5752
+ },
5753
+ emit: this.emit.bind(this),
5754
+ getState: () => this.state.getSnapshot(),
5755
+ setState: (updates) => this.state.update(updates),
5756
+ getScaleMultiplier: () => this.scaleMultiplier,
5757
+ setScaleContext: (magnitude) => {
5758
+ const multipliers = {
5759
+ galactic: 1e6,
5760
+ macro: 1e3,
5761
+ standard: 1,
5762
+ micro: 1e-3,
5763
+ atomic: 1e-6
5764
+ };
5765
+ const newMultiplier = multipliers[magnitude] || 1;
5766
+ if (this.scaleMultiplier !== newMultiplier) {
5767
+ this.scaleMultiplier = newMultiplier;
5768
+ this.emit("scale_change", { magnitude, multiplier: newMultiplier });
5769
+ }
5770
+ }
5771
+ };
5772
+ }
5773
+ // ==========================================================================
5774
+ // NODE DESTRUCTION
5775
+ // ==========================================================================
5776
+ destroyInstance(instance) {
5777
+ if (instance.destroyed) return;
5778
+ instance.destroyed = true;
5779
+ for (const child of instance.children) {
5780
+ this.destroyInstance(child);
5781
+ }
5782
+ const traitContext = this.createTraitContext(instance);
5783
+ if (instance.node.traits) {
5784
+ for (const traitName of instance.node.traits.keys()) {
5785
+ this.traitRegistry.detachTrait(instance.node, traitName, traitContext);
5786
+ }
5787
+ }
5788
+ if (this.options.renderer && instance.renderedNode) {
5789
+ this.options.renderer.destroy(instance.renderedNode);
5790
+ }
5791
+ instance.lifecycleHandlers.clear();
5792
+ instance.children = [];
5793
+ const index = this._flatEntities.indexOf(instance);
5794
+ if (index !== -1) {
5795
+ this._flatEntities.splice(index, 1);
5796
+ }
5797
+ }
5798
+ // ==========================================================================
5799
+ // PUBLIC API
5800
+ // ==========================================================================
5801
+ togglePhysicsDebug(enabled) {
5802
+ if (this.debugDrawer) {
5803
+ this.debugDrawer.setEnabled(enabled);
5804
+ }
5805
+ }
5806
+ updateData(data) {
5807
+ this.state.set("data", data);
5808
+ this.callLifecycle(this.rootInstance, "on_data_update", data);
5809
+ }
5810
+ updateNodeProperty(nodeId, property, value) {
5811
+ const instance = this.findInstanceById(nodeId);
5812
+ if (instance && instance.node.properties) {
5813
+ instance.node.properties[property] = value;
5814
+ if (this.options.renderer && instance.renderedNode) {
5815
+ instance.dirty = true;
5816
+ }
5817
+ this.emit("property_update", { nodeId, property, value });
5818
+ }
5819
+ }
5820
+ getState() {
5821
+ return this.state.getSnapshot();
5822
+ }
5823
+ // ==========================================================================
5824
+ // COMPATIBILITY METHODS
5825
+ // ==========================================================================
5826
+ getVariable(name) {
5827
+ return this.state.get(name);
5828
+ }
5829
+ setVariable(name, value) {
5830
+ this.state.set(name, value);
5831
+ }
5832
+ getContext() {
5833
+ const spatialMemory = /* @__PURE__ */ new Map();
5834
+ const hologramState = /* @__PURE__ */ new Map();
5835
+ const traverse = (instance) => {
5836
+ if (instance.node.id && instance.node.properties) {
5837
+ spatialMemory.set(
5838
+ instance.node.id,
5839
+ instance.node.properties.position || { x: 0, y: 0, z: 0 }
5840
+ );
5841
+ hologramState.set(instance.node.id, {
5842
+ shape: instance.node.properties.shape || instance.node.type,
5843
+ color: instance.node.properties.color,
5844
+ size: instance.node.properties.size,
5845
+ glow: instance.node.properties.glow,
5846
+ interactive: instance.node.properties.interactive
5847
+ });
5848
+ }
5849
+ instance.children.forEach(traverse);
5850
+ };
5851
+ if (this.rootInstance) traverse(this.rootInstance);
5852
+ return {
5853
+ spatialMemory,
5854
+ hologramState,
5855
+ state: this.state,
5856
+ builtins: this.builtins,
5857
+ vr: this.vrContext
5858
+ };
5859
+ }
5860
+ reset() {
5861
+ this.unmount();
5862
+ this.state = createState({});
5863
+ this.mounted = false;
5864
+ }
5865
+ updateAnimations() {
5866
+ this.update(1 / 60);
5867
+ }
5868
+ updateParticles(delta) {
5869
+ this.update(delta);
5870
+ }
5871
+ getHologramStates() {
5872
+ return this.getContext().hologramState;
5873
+ }
5874
+ setState(updates) {
5875
+ this.state.update(updates);
5876
+ }
5877
+ emit(event, payload) {
5878
+ const handlers = this.eventHandlers.get(event);
5879
+ if (handlers) {
5880
+ handlers.forEach((handler) => {
5881
+ try {
5882
+ handler(payload);
5883
+ } catch (error) {
5884
+ console.error(`Error in event handler for ${event}:`, error);
5885
+ }
5886
+ });
5887
+ }
5888
+ eventBus.emit(event, payload);
5889
+ }
5890
+ updateEntity(id, properties) {
5891
+ if (!this.rootInstance) return false;
5892
+ let found = false;
5893
+ const traverse = (instance) => {
5894
+ if (instance.node.id === id) {
5895
+ instance.node.properties = { ...instance.node.properties, ...properties };
5896
+ if (this.options.renderer && instance.renderedNode) {
5897
+ this.options.renderer.updateElement(instance.renderedNode, properties);
5898
+ }
5899
+ found = true;
5900
+ }
5901
+ instance.children.forEach(traverse);
5902
+ };
5903
+ traverse(this.rootInstance);
5904
+ return found;
5905
+ }
5906
+ on(event, handler) {
5907
+ if (!this.eventHandlers.has(event)) {
5908
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
5909
+ }
5910
+ this.eventHandlers.get(event).add(handler);
5911
+ return () => {
5912
+ this.eventHandlers.get(event)?.delete(handler);
5913
+ };
5914
+ }
5915
+ // ==========================================================================
5916
+ // HOT-RELOAD & MIGRATION
5917
+ // ==========================================================================
5918
+ async hotReload(newAst) {
5919
+ const newTemplates = this.findAllTemplates(newAst.root);
5920
+ for (const [name, newNode] of newTemplates) {
5921
+ const oldNode = this.templates.get(name);
5922
+ if (oldNode && newNode.version !== oldNode.version) {
5923
+ const result = await this.hotReloader.reload(newNode);
5924
+ if (result.success) {
5925
+ this.templates.set(name, newNode);
5926
+ } else {
5927
+ console.error(`[Hot-Reload] Failed for template "${name}":`, result.error);
5928
+ }
5929
+ } else {
5930
+ this.templates.set(name, newNode);
5931
+ this.hotReloader.registerTemplate(newNode);
5932
+ }
5933
+ }
5934
+ if (newAst.version !== void 0 && newAst.version !== this.ast.version) {
5935
+ const result = await this.hotReloader.reload({
5936
+ type: "Template",
5937
+ name: "@program",
5938
+ version: newAst.version,
5939
+ migrations: newAst.migrations,
5940
+ state: { type: "State", properties: [] },
5941
+ properties: [],
5942
+ actions: [],
5943
+ traits: []
5944
+ });
5945
+ if (!result.success) {
5946
+ console.error(`[Hot-Reload] Global program migration failed:`, result.error);
5947
+ } else {
5948
+ }
5949
+ }
5950
+ this.ast = newAst;
5951
+ }
5952
+ /**
5953
+ * Creates a Map proxy that reflects the reactive state.
5954
+ * This allowed the HotReloader (designed for Map-based state) to work with
5955
+ * the runtime's Record-based reactive state.
5956
+ */
5957
+ createStateMapProxy() {
5958
+ const runtime = this;
5959
+ return {
5960
+ get(key) {
5961
+ return runtime.state.get(key);
5962
+ },
5963
+ set(key, value) {
5964
+ runtime.state.set(key, value);
5965
+ return this;
5966
+ },
5967
+ has(key) {
5968
+ return runtime.state.get(key) !== void 0;
5969
+ },
5970
+ delete(key) {
5971
+ runtime.state.set(key, void 0);
5972
+ return true;
5973
+ },
5974
+ clear() {
5975
+ },
5976
+ get size() {
5977
+ return Object.keys(runtime.state.getSnapshot()).length;
5978
+ },
5979
+ forEach(cb) {
5980
+ const snap = runtime.state.getSnapshot();
5981
+ Object.entries(snap).forEach(([k, v]) => cb(v, k, this));
5982
+ },
5983
+ [Symbol.iterator]() {
5984
+ const snap = runtime.state.getSnapshot();
5985
+ return Object.entries(snap)[Symbol.iterator]();
5986
+ },
5987
+ entries() {
5988
+ const snap = runtime.state.getSnapshot();
5989
+ return Object.entries(snap)[Symbol.iterator]();
5990
+ },
5991
+ keys() {
5992
+ const snap = runtime.state.getSnapshot();
5993
+ return Object.keys(snap)[Symbol.iterator]();
5994
+ },
5995
+ values() {
5996
+ const snap = runtime.state.getSnapshot();
5997
+ return Object.values(snap)[Symbol.iterator]();
5998
+ }
5999
+ };
6000
+ }
6001
+ findInstanceById(id, root = this.rootInstance) {
6002
+ if (!root) return null;
6003
+ if (root.__holo_id === id || root.node.id === id) return root;
6004
+ for (const child of root.children) {
6005
+ const found = this.findInstanceById(id, child);
6006
+ if (found) return found;
6007
+ }
6008
+ return null;
6009
+ }
6010
+ /**
6011
+ * Executes a block of HoloScript+ statements
6012
+ */
6013
+ async executeStatementBlock(instance, body) {
6014
+ for (const stmt of body) {
6015
+ await this.executeStatement(instance, stmt);
6016
+ }
6017
+ }
6018
+ /**
6019
+ * Executes a single HoloScript+ statement
6020
+ */
6021
+ async executeStatement(instance, stmt) {
6022
+ const context = {
6023
+ ...this.state.getSnapshot(),
6024
+ node: instance.node,
6025
+ self: instance.node,
6026
+ props: instance.node.properties || {}
6027
+ };
6028
+ this.evaluator.updateContext(context);
6029
+ try {
6030
+ switch (stmt.type) {
6031
+ case "Assignment": {
6032
+ const value = this.evaluator.evaluate(String(stmt.value));
6033
+ const target = stmt.target;
6034
+ if (target.startsWith("props.")) {
6035
+ const propName = target.split(".")[1];
6036
+ if (instance.node.properties) {
6037
+ instance.node.properties[propName] = value;
6038
+ }
6039
+ } else if (target.startsWith("state.")) {
6040
+ const stateKey = target.split(".")[1];
6041
+ this.state.set(stateKey, value);
6042
+ } else {
6043
+ context[target] = value;
6044
+ }
6045
+ break;
6046
+ }
6047
+ case "MethodCall": {
6048
+ const args = (stmt.arguments || []).map((arg) => this.evaluator.evaluate(String(arg)));
6049
+ const method = this.builtins[stmt.method];
6050
+ if (typeof method === "function") {
6051
+ await method(...args);
6052
+ }
6053
+ break;
6054
+ }
6055
+ case "IfStatement": {
6056
+ const condition = this.evaluator.evaluate(String(stmt.condition));
6057
+ if (condition) {
6058
+ await this.executeStatementBlock(instance, stmt.consequent);
6059
+ } else if (stmt.alternate) {
6060
+ await this.executeStatementBlock(instance, stmt.alternate);
6061
+ }
6062
+ break;
6063
+ }
6064
+ case "EmitStatement": {
6065
+ const data = stmt.data ? this.evaluator.evaluate(String(stmt.data)) : void 0;
6066
+ this.emit(stmt.event, data);
6067
+ break;
6068
+ }
6069
+ // Add more statement types as needed
6070
+ default:
6071
+ console.warn(`[Runtime] Unsupported statement type: ${stmt.type}`);
6072
+ }
6073
+ } catch (error) {
6074
+ console.error(`[Runtime] Execution error in statement ${stmt.type}:`, error);
6075
+ }
6076
+ }
6077
+ migrateInstancesOfTemplate(name, oldVersion, newTemplate) {
6078
+ const instances = this.findAllInstancesOfTemplate(name);
6079
+ for (const instance of instances) {
6080
+ this.migrateInstance(instance, oldVersion, newTemplate);
6081
+ }
6082
+ }
6083
+ findAllInstancesOfTemplate(name, root = this.rootInstance) {
6084
+ if (!root) return [];
6085
+ const results = [];
6086
+ if (root.templateName === name) {
6087
+ results.push(root);
6088
+ }
6089
+ for (const child of root.children) {
6090
+ results.push(...this.findAllInstancesOfTemplate(name, child));
6091
+ }
6092
+ return results;
6093
+ }
6094
+ migrateInstance(instance, oldVersion, newTemplate) {
6095
+ const _context = this.createTraitContext(instance);
6096
+ const currentProperties = { ...instance.node.properties || {} };
6097
+ const newNode = this.cloneNodeWithContext(newTemplate, {
6098
+ position: currentProperties.position
6099
+ });
6100
+ const oldNode = instance.node;
6101
+ Object.keys(oldNode).forEach(
6102
+ (key) => delete oldNode[key]
6103
+ );
6104
+ Object.assign(oldNode, newNode);
6105
+ oldNode.properties = { ...oldNode.properties || {}, ...currentProperties };
6106
+ instance.templateVersion = newTemplate.version;
6107
+ const migrations = newTemplate.migrations || [];
6108
+ const migration = migrations.find((m) => m.fromVersion === oldVersion);
6109
+ if (migration && migration.body) {
6110
+ this.executeMigrationCode(instance, migration.body);
6111
+ }
6112
+ if (this.options.renderer && instance.renderedNode) {
6113
+ const properties = this.evaluateProperties(instance.node.properties || {});
6114
+ this.options.renderer.updateElement(instance.renderedNode, properties);
6115
+ }
6116
+ }
6117
+ executeMigrationCode(instance, code) {
6118
+ const stateProxy = new Proxy(this.state, {
6119
+ get: (target, prop) => {
6120
+ if (typeof prop === "string" && prop in target && typeof target[prop] === "function") {
6121
+ return target[prop].bind(target);
6122
+ }
6123
+ return target.get(String(prop));
6124
+ },
6125
+ set: (target, prop, value) => {
6126
+ target.set(String(prop), value);
6127
+ return true;
6128
+ }
6129
+ });
6130
+ const sandbox = {
6131
+ ...this.builtins,
6132
+ state: stateProxy,
6133
+ node: instance.node,
6134
+ self: instance.node,
6135
+ props: instance.node.properties || {},
6136
+ renameProperty: (oldName, newName) => {
6137
+ if (instance.node.properties && instance.node.properties[oldName] !== void 0) {
6138
+ instance.node.properties[newName] = instance.node.properties[oldName];
6139
+ delete instance.node.properties[oldName];
6140
+ }
6141
+ }
6142
+ };
6143
+ try {
6144
+ const fn = new Function(...Object.keys(sandbox), code);
6145
+ fn(...Object.values(sandbox));
6146
+ } catch (error) {
6147
+ console.error(`[Runtime] Migration execution failed in "${instance.templateName}":`, error);
6148
+ }
6149
+ }
6150
+ // ==========================================================================
6151
+ // VR INTEGRATION
6152
+ // ==========================================================================
6153
+ updateVRContext(context) {
6154
+ this.vrContext = context;
6155
+ }
6156
+ handleVREvent(event, node) {
6157
+ const instance = this.findInstance(node);
6158
+ if (!instance) return;
6159
+ const traitContext = this.createTraitContext(instance);
6160
+ this.traitRegistry.handleEventForAllTraits(node, traitContext, event);
6161
+ const hookMapping = {
6162
+ grab_start: "on_grab",
6163
+ grab_end: "on_release",
6164
+ hover_enter: "on_hover_enter",
6165
+ hover_exit: "on_hover_exit",
6166
+ point_enter: "on_point_enter",
6167
+ point_exit: "on_point_exit",
6168
+ collision: "on_collision",
6169
+ trigger_enter: "on_trigger_enter",
6170
+ trigger_exit: "on_trigger_exit",
6171
+ click: "on_click"
6172
+ };
6173
+ const hook = hookMapping[event.type];
6174
+ if (hook) {
6175
+ this.callLifecycle(instance, hook, event);
6176
+ }
6177
+ }
6178
+ findInstance(node, root = this.rootInstance) {
6179
+ if (!root) return null;
6180
+ if (root.node === node) return root;
6181
+ for (const child of root.children) {
6182
+ const found = this.findInstance(node, child);
6183
+ if (found) return found;
6184
+ }
6185
+ return null;
6186
+ }
6187
+ // ==========================================================================
6188
+ // TEMPLATES & SPAWNING
6189
+ // ==========================================================================
6190
+ registerTemplate(name, node) {
6191
+ this.templates.set(name, node);
6192
+ }
6193
+ spawnTemplate(name, position) {
6194
+ const template = this.templates.get(name);
6195
+ if (!template) {
6196
+ throw new Error(`Template "${name}" not found`);
6197
+ }
6198
+ const cloned = this.cloneNodeWithContext(template, { position });
6199
+ if (!cloned.properties) cloned.properties = {};
6200
+ cloned.properties.position = position;
6201
+ if (this.rootInstance) {
6202
+ const instance = this.instantiateNode(cloned, this.rootInstance);
6203
+ instance.templateName = name;
6204
+ instance.templateVersion = typeof template.version === "number" ? template.version : parseInt(template.version || "0");
6205
+ this.rootInstance.children.push(instance);
6206
+ if (this.options.renderer && this.rootInstance.renderedNode) {
6207
+ this.options.renderer.appendChild(this.rootInstance.renderedNode, instance.renderedNode);
6208
+ }
6209
+ this.callLifecycle(instance, "on_mount");
6210
+ }
6211
+ return cloned;
6212
+ }
6213
+ destroyNode(node) {
6214
+ const instance = this.findInstance(node);
6215
+ if (!instance) return;
6216
+ this.callLifecycle(instance, "on_unmount");
6217
+ if (instance.parent) {
6218
+ const index = instance.parent.children.indexOf(instance);
6219
+ if (index > -1) {
6220
+ instance.parent.children.splice(index, 1);
6221
+ }
6222
+ if (this.options.renderer && instance.parent.renderedNode && instance.renderedNode) {
6223
+ this.options.renderer.removeChild(instance.parent.renderedNode, instance.renderedNode);
6224
+ }
6225
+ }
6226
+ this.destroyInstance(instance);
6227
+ }
6228
+ };
6229
+ function createBuiltins(runtime) {
6230
+ return {
6231
+ log: (...args) => console.log("[HoloScript]", ...args),
6232
+ warn: (...args) => console.warn("[HoloScript]", ...args),
6233
+ error: (...args) => console.error("[HoloScript]", ...args),
6234
+ Math,
6235
+ range: (start, end, step = 1) => {
6236
+ const result = [];
6237
+ if (step > 0) {
6238
+ for (let i = start; i < end; i += step) {
6239
+ result.push(i);
6240
+ }
6241
+ } else if (step < 0) {
6242
+ for (let i = start; i > end; i += step) {
6243
+ result.push(i);
6244
+ }
6245
+ }
6246
+ return result;
6247
+ },
6248
+ interpolate_color: (t, from, to) => {
6249
+ const parseHex = (hex) => {
6250
+ const clean = hex.replace("#", "");
6251
+ return [
6252
+ parseInt(clean.substring(0, 2), 16),
6253
+ parseInt(clean.substring(2, 4), 16),
6254
+ parseInt(clean.substring(4, 6), 16)
6255
+ ];
6256
+ };
6257
+ const toHex = (r, g, b) => {
6258
+ const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
6259
+ return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
6260
+ };
6261
+ const [r1, g1, b1] = typeof from === "string" ? parseHex(from) : [from.r || 0, from.g || 0, from.b || 0];
6262
+ const [r2, g2, b2] = typeof to === "string" ? parseHex(to) : [to.r || 0, to.g || 0, to.b || 0];
6263
+ return toHex(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
6264
+ },
6265
+ distance_to: (point) => {
6266
+ const viewer = runtime.vrContext.headset.position;
6267
+ return Math.sqrt(
6268
+ Math.pow(point.x - viewer.x, 2) + Math.pow(point.y - viewer.y, 2) + Math.pow(point.z - viewer.z, 2)
6269
+ );
6270
+ },
6271
+ distance_to_viewer: () => {
6272
+ return 0;
6273
+ },
6274
+ hand_position: (handId) => {
6275
+ const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
6276
+ return hand?.position || { x: 0, y: 0, z: 0 };
6277
+ },
6278
+ hand_velocity: (handId) => {
6279
+ const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
6280
+ return hand?.velocity || { x: 0, y: 0, z: 0 };
6281
+ },
6282
+ dominant_hand: () => {
6283
+ return runtime.vrContext.hands.right || runtime.vrContext.hands.left || {
6284
+ id: "right",
6285
+ position: [0, 0, 0],
6286
+ rotation: { x: 0, y: 0, z: 0 },
6287
+ velocity: { x: 0, y: 0, z: 0 },
6288
+ gripStrength: 0,
6289
+ pinchStrength: 0
6290
+ };
6291
+ },
6292
+ play_sound: (source, options) => {
6293
+ runtime.emit("play_sound", { source, ...options });
6294
+ },
6295
+ haptic_feedback: (hand, intensity) => {
6296
+ const handId = typeof hand === "string" ? hand : hand.id;
6297
+ runtime.emit("haptic", { hand: handId, intensity });
6298
+ },
6299
+ haptic_pulse: (intensity) => {
6300
+ runtime.emit("haptic", { hand: "both", intensity });
6301
+ },
6302
+ apply_velocity: (node, velocity) => {
6303
+ runtime.emit("apply_velocity", { node, velocity });
6304
+ },
6305
+ spawn: (template, position) => {
6306
+ return runtime.spawnTemplate(template, position);
6307
+ },
6308
+ assistant_generate: (prompt, context) => {
6309
+ runtime.emit("assistant_generate", { prompt, context });
6310
+ },
6311
+ destroy: (node) => {
6312
+ runtime.destroyNode(node);
6313
+ },
6314
+ api_call: async (url, method, body) => {
6315
+ const response = await fetch(url, {
6316
+ method,
6317
+ headers: body ? { "Content-Type": "application/json" } : void 0,
6318
+ body: body ? JSON.stringify(body) : void 0
6319
+ });
6320
+ return response.json();
6321
+ },
6322
+ open_modal: (modalId) => {
6323
+ runtime.emit("open_modal", { id: modalId });
6324
+ },
6325
+ close_modal: (modalId) => {
6326
+ runtime.emit("close_modal", { id: modalId });
6327
+ },
6328
+ setTimeout: (callback, delay) => {
6329
+ return window.setTimeout(callback, delay);
6330
+ },
6331
+ clearTimeout: (id) => {
6332
+ window.clearTimeout(id);
6333
+ },
6334
+ animate: (node, properties, options = {}) => {
6335
+ if (options.sync) {
6336
+ runtime.emit("network_animation", {
6337
+ objectId: node.id,
6338
+ properties,
6339
+ options,
6340
+ timestamp: Date.now()
6341
+ });
6342
+ }
6343
+ if (node.properties) {
6344
+ Object.assign(node.properties, properties);
6345
+ }
6346
+ runtime.emit("animate", { node, properties, options });
6347
+ },
6348
+ transition: (targetScene, options = {}) => {
6349
+ if (options.audio) {
6350
+ runtime.emit("play_sound", { source: options.audio });
6351
+ }
6352
+ runtime.emit("scene_transition", { target: targetScene, options });
6353
+ }
6354
+ };
6355
+ }
6356
+ function createRuntime(ast, options = {}) {
6357
+ return new HoloScriptPlusRuntimeImpl(ast, options);
6358
+ }
6359
+
6360
+ // src/runtime/InstancedRenderer.ts
6361
+ function getThree() {
6362
+ return globalThis.THREE;
6363
+ }
6364
+ var InstancedRenderer = class {
6365
+ batches = /* @__PURE__ */ new Map();
6366
+ objects = /* @__PURE__ */ new Map();
6367
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6368
+ scene;
6369
+ // THREE.Scene
6370
+ maxInstancesPerBatch = 1e3;
6371
+ // Configurable
6372
+ enabled = true;
6373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6374
+ constructor(scene, maxInstancesPerBatch = 1e3) {
6375
+ this.scene = scene;
6376
+ this.maxInstancesPerBatch = maxInstancesPerBatch;
6377
+ }
6378
+ /**
6379
+ * Add object to instanced rendering
6380
+ */
6381
+ addInstance(id, geometryType, materialType, position, rotation = [0, 0, 0], scale = [1, 1, 1], color) {
6382
+ if (!this.enabled) return false;
6383
+ const batchKey = `${geometryType}_${materialType}`;
6384
+ let batch = this.batches.get(batchKey);
6385
+ if (!batch) {
6386
+ const newBatch = this.createBatch(batchKey, geometryType, materialType);
6387
+ if (!newBatch) return false;
6388
+ batch = newBatch;
6389
+ }
6390
+ if (batch.count >= batch.maxInstances && batch.freeIndices.length === 0) {
6391
+ console.warn(
6392
+ `[InstancedRenderer] Batch ${batchKey} is full (${batch.maxInstances} instances)`
6393
+ );
6394
+ return false;
6395
+ }
6396
+ const instanceIndex = batch.freeIndices.length > 0 ? batch.freeIndices.pop() : batch.count++;
6397
+ const matrix = this.createTransformMatrix(position, rotation, scale);
6398
+ const instance = {
6399
+ id,
6400
+ batchKey,
6401
+ instanceIndex,
6402
+ matrix,
6403
+ color,
6404
+ visible: true
6405
+ };
6406
+ this.objects.set(id, instance);
6407
+ batch.instanceMap.set(id, instanceIndex);
6408
+ batch.instancedMesh.setMatrixAt(instanceIndex, this.arrayToMatrix4(matrix));
6409
+ if (color && batch.instancedMesh.instanceColor) {
6410
+ const THREE = getThree();
6411
+ batch.instancedMesh.setColorAt(instanceIndex, new THREE.Color(color[0], color[1], color[2]));
6412
+ }
6413
+ batch.needsUpdate = true;
6414
+ return true;
6415
+ }
6416
+ /**
6417
+ * Remove instance from rendering
6418
+ */
6419
+ removeInstance(id) {
6420
+ const instance = this.objects.get(id);
6421
+ if (!instance) return false;
6422
+ const batch = this.batches.get(instance.batchKey);
6423
+ if (!batch) return false;
6424
+ const matrix = this.createTransformMatrix([999999, 999999, 999999], [0, 0, 0], [0, 0, 0]);
6425
+ batch.instancedMesh.setMatrixAt(instance.instanceIndex, this.arrayToMatrix4(matrix));
6426
+ batch.needsUpdate = true;
6427
+ batch.freeIndices.push(instance.instanceIndex);
6428
+ batch.instanceMap.delete(id);
6429
+ this.objects.delete(id);
6430
+ return true;
6431
+ }
6432
+ /**
6433
+ * Update instance transform
6434
+ */
6435
+ updateInstance(id, position, rotation, scale) {
6436
+ const instance = this.objects.get(id);
6437
+ if (!instance) return false;
6438
+ const batch = this.batches.get(instance.batchKey);
6439
+ if (!batch) return false;
6440
+ const current = this.matrixToTransform(instance.matrix);
6441
+ const newPosition = position || current.position;
6442
+ const newRotation = rotation || current.rotation;
6443
+ const newScale = scale || current.scale;
6444
+ const matrix = this.createTransformMatrix(newPosition, newRotation, newScale);
6445
+ instance.matrix = matrix;
6446
+ batch.instancedMesh.setMatrixAt(instance.instanceIndex, this.arrayToMatrix4(matrix));
6447
+ batch.needsUpdate = true;
6448
+ return true;
6449
+ }
6450
+ /**
6451
+ * Update instance color
6452
+ */
6453
+ updateInstanceColor(id, color) {
6454
+ const instance = this.objects.get(id);
6455
+ if (!instance) return false;
6456
+ const batch = this.batches.get(instance.batchKey);
6457
+ if (!batch || !batch.instancedMesh.instanceColor) return false;
6458
+ const THREE = getThree();
6459
+ batch.instancedMesh.setColorAt(
6460
+ instance.instanceIndex,
6461
+ new THREE.Color(color[0], color[1], color[2])
6462
+ );
6463
+ instance.color = color;
6464
+ batch.needsUpdate = true;
6465
+ return true;
6466
+ }
6467
+ /**
6468
+ * Update all batches (call once per frame)
6469
+ */
6470
+ update() {
6471
+ for (const batch of this.batches.values()) {
6472
+ if (batch.needsUpdate) {
6473
+ batch.instancedMesh.instanceMatrix.needsUpdate = true;
6474
+ if (batch.instancedMesh.instanceColor) {
6475
+ batch.instancedMesh.instanceColor.needsUpdate = true;
6476
+ }
6477
+ batch.needsUpdate = false;
6478
+ }
6479
+ }
6480
+ }
6481
+ /**
6482
+ * Create a new batch
6483
+ */
6484
+ createBatch(key, geometryType, materialType) {
6485
+ if (!getThree()) return null;
6486
+ const THREE = getThree();
6487
+ const geometry = this.createGeometry(geometryType);
6488
+ if (!geometry) return null;
6489
+ const material = this.createMaterial(materialType);
6490
+ if (!material) return null;
6491
+ const instancedMesh = new THREE.InstancedMesh(geometry, material, this.maxInstancesPerBatch);
6492
+ instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
6493
+ instancedMesh.castShadow = true;
6494
+ instancedMesh.receiveShadow = true;
6495
+ instancedMesh.instanceColor = new THREE.InstancedBufferAttribute(
6496
+ new Float32Array(this.maxInstancesPerBatch * 3),
6497
+ 3
6498
+ );
6499
+ this.scene.add(instancedMesh);
6500
+ const batch = {
6501
+ key,
6502
+ geometryType,
6503
+ materialType,
6504
+ instancedMesh,
6505
+ maxInstances: this.maxInstancesPerBatch,
6506
+ count: 0,
6507
+ instanceMap: /* @__PURE__ */ new Map(),
6508
+ freeIndices: [],
6509
+ needsUpdate: false
6510
+ };
6511
+ this.batches.set(key, batch);
6512
+ return batch;
6513
+ }
6514
+ /**
6515
+ * Create geometry by type
6516
+ */
6517
+ createGeometry(type) {
6518
+ const THREE = getThree();
6519
+ const geometryMap = {
6520
+ // ===== CORE PRIMITIVES =====
6521
+ box: () => new THREE.BoxGeometry(1, 1, 1),
6522
+ sphere: () => new THREE.SphereGeometry(0.5, 16, 16),
6523
+ cylinder: () => new THREE.CylinderGeometry(0.5, 0.5, 1, 16),
6524
+ cone: () => new THREE.ConeGeometry(0.5, 1, 16),
6525
+ plane: () => new THREE.PlaneGeometry(1, 1),
6526
+ torus: () => new THREE.TorusGeometry(0.5, 0.2, 16, 50),
6527
+ ring: () => new THREE.RingGeometry(0.25, 0.5, 16),
6528
+ circle: () => new THREE.CircleGeometry(0.5, 16),
6529
+ // ===== ADVANCED GEOMETRIES =====
6530
+ capsule: () => new THREE.CapsuleGeometry(0.25, 0.5, 4, 8),
6531
+ torusknot: () => new THREE.TorusKnotGeometry(0.5, 0.15, 64, 8, 2, 3),
6532
+ // ===== POLYHEDRONS =====
6533
+ dodecahedron: () => new THREE.DodecahedronGeometry(0.5, 0),
6534
+ icosahedron: () => new THREE.IcosahedronGeometry(0.5, 0),
6535
+ octahedron: () => new THREE.OctahedronGeometry(0.5, 0),
6536
+ tetrahedron: () => new THREE.TetrahedronGeometry(0.5, 0)
6537
+ };
6538
+ return geometryMap[type]?.() || new THREE.BoxGeometry(1, 1, 1);
6539
+ }
6540
+ /**
6541
+ * Create material by type
6542
+ */
6543
+ createMaterial(_type) {
6544
+ const THREE = getThree();
6545
+ return new THREE.MeshStandardMaterial({
6546
+ color: 16777215,
6547
+ roughness: 0.7,
6548
+ metalness: 0.3,
6549
+ vertexColors: true
6550
+ // Enable per-instance colors
6551
+ });
6552
+ }
6553
+ /**
6554
+ * Create transform matrix from position, rotation, scale
6555
+ */
6556
+ createTransformMatrix(position, rotation, scale) {
6557
+ const matrix = new Float32Array(16);
6558
+ const cx = Math.cos(rotation[0]);
6559
+ const sx = Math.sin(rotation[0]);
6560
+ const cy = Math.cos(rotation[1]);
6561
+ const sy = Math.sin(rotation[1]);
6562
+ const cz = Math.cos(rotation[2]);
6563
+ const sz = Math.sin(rotation[2]);
6564
+ matrix[0] = cy * cz * scale[0];
6565
+ matrix[1] = cy * sz * scale[0];
6566
+ matrix[2] = -sy * scale[0];
6567
+ matrix[3] = 0;
6568
+ matrix[4] = (sx * sy * cz - cx * sz) * scale[1];
6569
+ matrix[5] = (sx * sy * sz + cx * cz) * scale[1];
6570
+ matrix[6] = sx * cy * scale[1];
6571
+ matrix[7] = 0;
6572
+ matrix[8] = (cx * sy * cz + sx * sz) * scale[2];
6573
+ matrix[9] = (cx * sy * sz - sx * cz) * scale[2];
6574
+ matrix[10] = cx * cy * scale[2];
6575
+ matrix[11] = 0;
6576
+ matrix[12] = position[0];
6577
+ matrix[13] = position[1];
6578
+ matrix[14] = position[2];
6579
+ matrix[15] = 1;
6580
+ return matrix;
6581
+ }
6582
+ /**
6583
+ * Convert Float32Array to THREE.Matrix4
6584
+ */
6585
+ arrayToMatrix4(array) {
6586
+ const THREE = getThree();
6587
+ const matrix = new THREE.Matrix4();
6588
+ matrix.fromArray(array);
6589
+ return matrix;
6590
+ }
6591
+ /**
6592
+ * Extract transform from matrix
6593
+ */
6594
+ matrixToTransform(matrix) {
6595
+ const THREE = getThree();
6596
+ if (THREE) {
6597
+ const m = new THREE.Matrix4().fromArray(matrix);
6598
+ const pos = new THREE.Vector3();
6599
+ const quat = new THREE.Quaternion();
6600
+ const scl = new THREE.Vector3();
6601
+ m.decompose(pos, quat, scl);
6602
+ const euler = new THREE.Euler().setFromQuaternion(quat, "XYZ");
6603
+ return {
6604
+ position: [pos.x, pos.y, pos.z],
6605
+ rotation: [euler.x, euler.y, euler.z],
6606
+ scale: [scl.x, scl.y, scl.z]
6607
+ };
6608
+ }
6609
+ const position = [matrix[12], matrix[13], matrix[14]];
6610
+ const sx = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1] + matrix[2] * matrix[2]);
6611
+ const sy = Math.sqrt(matrix[4] * matrix[4] + matrix[5] * matrix[5] + matrix[6] * matrix[6]);
6612
+ const sz = Math.sqrt(matrix[8] * matrix[8] + matrix[9] * matrix[9] + matrix[10] * matrix[10]);
6613
+ const scale = [sx, sy, sz];
6614
+ let rx = 0, ry = 0, rz = 0;
6615
+ if (sx !== 0 && sy !== 0 && sz !== 0) {
6616
+ const m13 = matrix[8] / sz;
6617
+ ry = Math.asin(Math.max(-1, Math.min(1, m13)));
6618
+ if (Math.abs(m13) < 0.999999) {
6619
+ rx = Math.atan2(-matrix[9] / sz, matrix[10] / sz);
6620
+ rz = Math.atan2(-matrix[4] / sy, matrix[0] / sx);
6621
+ } else {
6622
+ rx = Math.atan2(matrix[6] / sy, matrix[5] / sy);
6623
+ rz = 0;
6624
+ }
6625
+ }
6626
+ const rotation = [rx, ry, rz];
6627
+ return { position, rotation, scale };
6628
+ }
6629
+ /**
6630
+ * Get statistics
6631
+ */
6632
+ getStatistics() {
6633
+ let totalInstances = 0;
6634
+ let memoryUsage = 0;
6635
+ for (const batch of this.batches.values()) {
6636
+ totalInstances += batch.count - batch.freeIndices.length;
6637
+ memoryUsage += batch.count * 76 / (1024 * 1024);
6638
+ }
6639
+ const drawCalls = this.batches.size;
6640
+ const nonInstancedDrawCalls = totalInstances;
6641
+ const improvement = nonInstancedDrawCalls > 0 ? `${((1 - drawCalls / nonInstancedDrawCalls) * 100).toFixed(1)}% fewer draw calls` : "N/A";
6642
+ return {
6643
+ batchCount: this.batches.size,
6644
+ totalInstances,
6645
+ drawCalls,
6646
+ memoryUsage: parseFloat(memoryUsage.toFixed(2)),
6647
+ improvement
6648
+ };
6649
+ }
6650
+ /**
6651
+ * Enable/disable instanced rendering
6652
+ */
6653
+ setEnabled(enabled) {
6654
+ this.enabled = enabled;
6655
+ }
6656
+ /**
6657
+ * Clear all batches
6658
+ */
6659
+ clear() {
6660
+ for (const batch of this.batches.values()) {
6661
+ this.scene.remove(batch.instancedMesh);
6662
+ batch.instancedMesh.geometry.dispose();
6663
+ batch.instancedMesh.material.dispose();
6664
+ }
6665
+ this.batches.clear();
6666
+ this.objects.clear();
6667
+ }
6668
+ /**
6669
+ * Get batch for inspection
6670
+ */
6671
+ getBatch(key) {
6672
+ return this.batches.get(key);
6673
+ }
6674
+ /**
6675
+ * Get all batch keys
6676
+ */
6677
+ getBatchKeys() {
6678
+ return Array.from(this.batches.keys());
6679
+ }
6680
+ };
6681
+
6682
+ // src/runtime/AssetStreamer.ts
6683
+ var StreamPriority = /* @__PURE__ */ ((StreamPriority2) => {
6684
+ StreamPriority2[StreamPriority2["IMMEDIATE"] = 0] = "IMMEDIATE";
6685
+ StreamPriority2[StreamPriority2["PREDICTIVE_HIGH"] = 1] = "PREDICTIVE_HIGH";
6686
+ StreamPriority2[StreamPriority2["PREDICTIVE_LOW"] = 2] = "PREDICTIVE_LOW";
6687
+ StreamPriority2[StreamPriority2["BACKGROUND"] = 3] = "BACKGROUND";
6688
+ return StreamPriority2;
6689
+ })(StreamPriority || {});
6690
+ var assetStreamerRegistry = /* @__PURE__ */ new Map();
6691
+ function registerAssetStreamer(name, streamer) {
6692
+ assetStreamerRegistry.set(name, streamer);
6693
+ }
6694
+ function getAssetStreamer(name) {
6695
+ return assetStreamerRegistry.get(name);
6696
+ }
3693
6697
  export {
3694
6698
  MATERIAL_PRESETS as ADVANCED_PBR_PRESETS,
3695
6699
  AdvancedLightingManager,
@@ -3716,8 +6720,11 @@ export {
3716
6720
  FogSystem,
3717
6721
  GIProbeGrid,
3718
6722
  gameplay_exports as Gameplay,
6723
+ GaussianSplatExtractor,
6724
+ HoloScriptPlusRuntimeImpl,
3719
6725
  hologram_exports as Hologram,
3720
6726
  input_exports as Input,
6727
+ InstancedRenderer,
3721
6728
  lod_exports as LOD,
3722
6729
  LODBridge,
3723
6730
  LODGenerator,
@@ -3757,8 +6764,11 @@ export {
3757
6764
  STANDARD_VERTEX_SHADER,
3758
6765
  scene_exports as Scene,
3759
6766
  ShaderGraph,
6767
+ simulation_exports as Simulation,
3760
6768
  SpatialEngine,
6769
+ StreamPriority,
3761
6770
  terrain_exports as Terrain,
6771
+ TetGenWasmMesher,
3762
6772
  tilemap_exports as Tilemap,
3763
6773
  TransitionScheduler,
3764
6774
  UNLIT_FRAGMENT_SHADER,
@@ -3779,6 +6789,7 @@ export {
3779
6789
  applyFilmGrain,
3780
6790
  applyMotionBlur,
3781
6791
  applyVignette,
6792
+ assetStreamerRegistry,
3782
6793
  blendDetailNormal,
3783
6794
  blendTAA,
3784
6795
  buildCircleCookie,
@@ -3812,6 +6823,7 @@ export {
3812
6823
  createMeshletGenerator,
3813
6824
  createMobileLODManager,
3814
6825
  createMobileMemoryPool,
6826
+ createRuntime,
3815
6827
  createSH9,
3816
6828
  createSolidTexture,
3817
6829
  createStandardLODConfig,
@@ -3832,6 +6844,7 @@ export {
3832
6844
  generateLODs,
3833
6845
  generateMeshlets,
3834
6846
  geometrySmith,
6847
+ getAssetStreamer,
3835
6848
  getAtlasEfficiency,
3836
6849
  getRecommendedLODCount,
3837
6850
  getRectUV,
@@ -3848,6 +6861,7 @@ export {
3848
6861
  pathTrace,
3849
6862
  prerenderHTML,
3850
6863
  rectSolidAngle,
6864
+ registerAssetStreamer,
3851
6865
  renderPDF,
3852
6866
  renderScreenshot,
3853
6867
  rgbaToHex,