@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
@@ -0,0 +1,913 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-AKLW2MUS.js";
4
+
5
+ // src/scene/index.ts
6
+ var scene_exports = {};
7
+ __export(scene_exports, {
8
+ CURRENT_SCHEMA_VERSION: () => CURRENT_SCHEMA_VERSION,
9
+ ProjectManager: () => ProjectManager,
10
+ SceneDeserializer: () => SceneDeserializer,
11
+ SceneManager: () => SceneManager,
12
+ SceneNode: () => SceneNode,
13
+ SceneQuery: () => SceneQuery,
14
+ SceneSerializer: () => SceneSerializer,
15
+ StateSnapshotCapture: () => StateSnapshotCapture,
16
+ VersionMigration: () => VersionMigration
17
+ });
18
+
19
+ // src/scene/ProjectManager.ts
20
+ var ProjectManager = class _ProjectManager {
21
+ project;
22
+ constructor(name, version = "1.0.0") {
23
+ this.project = {
24
+ name,
25
+ version,
26
+ createdAt: Date.now(),
27
+ modifiedAt: Date.now(),
28
+ scenes: [],
29
+ assets: [],
30
+ buildConfig: {
31
+ target: "development",
32
+ optimizeAssets: false,
33
+ minifyScripts: false,
34
+ bundleAssets: false,
35
+ outputDir: "./dist"
36
+ },
37
+ settings: {}
38
+ };
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Scene Management
42
+ // ---------------------------------------------------------------------------
43
+ addScene(ref) {
44
+ this.project.scenes.push({ ...ref, lastModified: Date.now() });
45
+ this.project.modifiedAt = Date.now();
46
+ }
47
+ removeScene(sceneId) {
48
+ const idx = this.project.scenes.findIndex((s) => s.id === sceneId);
49
+ if (idx < 0) return false;
50
+ this.project.scenes.splice(idx, 1);
51
+ for (const asset of this.project.assets) {
52
+ asset.usedByScenes = asset.usedByScenes.filter((id) => id !== sceneId);
53
+ }
54
+ this.project.modifiedAt = Date.now();
55
+ return true;
56
+ }
57
+ getScene(sceneId) {
58
+ return this.project.scenes.find((s) => s.id === sceneId);
59
+ }
60
+ getScenes() {
61
+ return [...this.project.scenes];
62
+ }
63
+ getStartScene() {
64
+ return this.project.scenes.find((s) => s.isStartScene);
65
+ }
66
+ setStartScene(sceneId) {
67
+ for (const s of this.project.scenes) {
68
+ s.isStartScene = s.id === sceneId;
69
+ }
70
+ return this.project.scenes.some((s) => s.id === sceneId);
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Asset Management
74
+ // ---------------------------------------------------------------------------
75
+ addAsset(ref) {
76
+ this.project.assets.push(ref);
77
+ this.project.modifiedAt = Date.now();
78
+ }
79
+ removeAsset(assetId) {
80
+ const idx = this.project.assets.findIndex((a) => a.id === assetId);
81
+ if (idx < 0) return false;
82
+ this.project.assets.splice(idx, 1);
83
+ this.project.modifiedAt = Date.now();
84
+ return true;
85
+ }
86
+ getAsset(assetId) {
87
+ return this.project.assets.find((a) => a.id === assetId);
88
+ }
89
+ getAssets() {
90
+ return [...this.project.assets];
91
+ }
92
+ /**
93
+ * Find unused assets (not referenced by any scene).
94
+ */
95
+ findUnusedAssets() {
96
+ return this.project.assets.filter((a) => a.usedByScenes.length === 0);
97
+ }
98
+ /**
99
+ * Get total project size in bytes.
100
+ */
101
+ getTotalAssetSize() {
102
+ return this.project.assets.reduce((sum, a) => sum + a.sizeBytes, 0);
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Build Config
106
+ // ---------------------------------------------------------------------------
107
+ setBuildConfig(config) {
108
+ Object.assign(this.project.buildConfig, config);
109
+ this.project.modifiedAt = Date.now();
110
+ }
111
+ getBuildConfig() {
112
+ return { ...this.project.buildConfig };
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Settings
116
+ // ---------------------------------------------------------------------------
117
+ setSetting(key, value) {
118
+ this.project.settings[key] = value;
119
+ this.project.modifiedAt = Date.now();
120
+ }
121
+ getSetting(key) {
122
+ return this.project.settings[key];
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Serialization
126
+ // ---------------------------------------------------------------------------
127
+ serialize() {
128
+ return JSON.stringify(this.project, null, 2);
129
+ }
130
+ static deserialize(json) {
131
+ const data = JSON.parse(json);
132
+ const pm = new _ProjectManager(data.name, data.version);
133
+ pm.project = data;
134
+ return pm;
135
+ }
136
+ getProjectFile() {
137
+ return { ...this.project };
138
+ }
139
+ };
140
+
141
+ // src/scene/SceneDeserializer.ts
142
+ var SceneDeserializer = class {
143
+ /**
144
+ * Deserialize from a SerializedScene object.
145
+ */
146
+ deserialize(scene) {
147
+ return this.rebuildNode(scene.root);
148
+ }
149
+ /**
150
+ * Deserialize from a JSON string.
151
+ */
152
+ fromJSON(json) {
153
+ const scene = JSON.parse(json);
154
+ if (scene.version !== 1) {
155
+ console.warn(`Unknown version ${scene.version}, attempting load`);
156
+ }
157
+ return {
158
+ node: this.rebuildNode(scene.root),
159
+ name: scene.name,
160
+ metadata: scene.metadata
161
+ };
162
+ }
163
+ rebuildNode(serialized) {
164
+ if (serialized.type === "ref") {
165
+ return {
166
+ id: serialized.id,
167
+ type: "entity",
168
+ properties: { _isRef: true },
169
+ traits: /* @__PURE__ */ new Map(),
170
+ children: []
171
+ };
172
+ }
173
+ const traits = /* @__PURE__ */ new Map();
174
+ if (serialized.traits) {
175
+ for (const [key, value] of Object.entries(serialized.traits)) {
176
+ traits.set(key, value);
177
+ }
178
+ }
179
+ const children = (serialized.children || []).map((child) => this.rebuildNode(child));
180
+ return {
181
+ id: serialized.id,
182
+ type: serialized.type,
183
+ properties: { ...serialized.properties },
184
+ traits,
185
+ children
186
+ };
187
+ }
188
+ };
189
+
190
+ // src/scene/SceneSerializer.ts
191
+ var SceneSerializer = class {
192
+ world;
193
+ visitedIds = /* @__PURE__ */ new Set();
194
+ constructor(world) {
195
+ this.world = world;
196
+ }
197
+ /**
198
+ * Serialize a scene.
199
+ * Overload 1: serialize(root: HSPlusNode, name?, metadata?) — from node tree
200
+ * Overload 2: serialize(name?, metadata?) — from World entities
201
+ */
202
+ serialize(rootOrName, sceneNameOrMeta, metadata) {
203
+ if (rootOrName && typeof rootOrName === "object" && "type" in rootOrName) {
204
+ const root = rootOrName;
205
+ const sceneName2 = typeof sceneNameOrMeta === "string" ? sceneNameOrMeta : "untitled";
206
+ const meta2 = typeof sceneNameOrMeta === "string" ? metadata : sceneNameOrMeta;
207
+ return this.serializeFromNode(root, sceneName2, meta2);
208
+ }
209
+ const sceneName = typeof rootOrName === "string" ? rootOrName : "untitled";
210
+ const meta = typeof sceneNameOrMeta === "object" ? sceneNameOrMeta : metadata;
211
+ if (!this.world) {
212
+ return {
213
+ version: 1,
214
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
215
+ name: sceneName,
216
+ root: { id: "scene_root", type: "root", properties: {}, traits: {}, children: [] },
217
+ metadata: meta
218
+ };
219
+ }
220
+ const entities = this.world.getAllEntities();
221
+ const childEntities = /* @__PURE__ */ new Set();
222
+ entities.forEach((e) => {
223
+ const t = this.world.getComponent(e, "Transform");
224
+ if (t && t.parent !== void 0) childEntities.add(e);
225
+ });
226
+ const roots = entities.filter((e) => !childEntities.has(e));
227
+ const validRoots = roots.filter(
228
+ (e) => !this.world.hasTag(e, "NoSelect") && !this.world.hasTag(e, "Gizmo")
229
+ );
230
+ const serializedRoots = validRoots.map((e) => this.serializeEntity(e));
231
+ return {
232
+ version: 1,
233
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
234
+ name: sceneName,
235
+ root: {
236
+ id: "scene_root",
237
+ type: "root",
238
+ properties: {},
239
+ traits: {},
240
+ children: serializedRoots
241
+ },
242
+ metadata: meta
243
+ };
244
+ }
245
+ /**
246
+ * Node-based serialization helper.
247
+ */
248
+ serializeFromNode(root, sceneName, metadata) {
249
+ this.visitedIds.clear();
250
+ return {
251
+ version: 1,
252
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
253
+ name: sceneName,
254
+ root: this.serializeNode(root),
255
+ metadata
256
+ };
257
+ }
258
+ serializeEntity(entity) {
259
+ const id = `e_${entity}`;
260
+ const properties = {};
261
+ const traits = {};
262
+ const compTypes = this.world.getComponentTypes(entity);
263
+ compTypes.forEach((type) => {
264
+ const data = this.world.getComponent(entity, type);
265
+ if (!data) return;
266
+ if (type === "Transform") {
267
+ const pos = data.position || { x: data.x || 0, y: data.y || 0, z: data.z || 0 };
268
+ const rot = data.rotation || {
269
+ x: data.rx || 0,
270
+ y: data.ry || 0,
271
+ z: data.rz || 0,
272
+ w: data.rw || 1
273
+ };
274
+ const scl = data.scale || { x: data.sx || 1, y: data.sy || 1, z: data.sz || 1 };
275
+ properties.position = pos;
276
+ properties.rotation = rot;
277
+ properties.scale = scl;
278
+ } else if (type === "Text") {
279
+ properties.text = data.content;
280
+ } else if (type === "Render") {
281
+ traits["render"] = data;
282
+ } else if (type === "Collider") {
283
+ traits["collider"] = data;
284
+ } else if (type === "Pressable") {
285
+ traits["pressable"] = data;
286
+ } else {
287
+ const traitName = type.charAt(0).toLowerCase() + type.slice(1);
288
+ traits[traitName] = data;
289
+ }
290
+ });
291
+ const children = [];
292
+ const all = this.world.getAllEntities();
293
+ all.forEach((other) => {
294
+ const t = this.world.getComponent(other, "Transform");
295
+ if (t && t.parent === entity) {
296
+ if (!this.world.hasTag(other, "NoSelect")) {
297
+ children.push(this.serializeEntity(other));
298
+ }
299
+ }
300
+ });
301
+ return {
302
+ id,
303
+ type: "entity",
304
+ properties: this.sanitizeProperties(properties),
305
+ traits: this.sanitizeValue(traits),
306
+ children
307
+ };
308
+ }
309
+ /**
310
+ * Legacy/Utility: Serialize a HSPlusNode tree
311
+ */
312
+ serializeNode(node) {
313
+ const id = node.id || "unknown";
314
+ if (this.visitedIds.has(id)) {
315
+ return { id, type: "ref", properties: {}, traits: {}, children: [] };
316
+ }
317
+ this.visitedIds.add(id);
318
+ const traits = {};
319
+ if (node.traits instanceof Map) {
320
+ for (const [key, value] of node.traits) {
321
+ traits[key] = this.sanitizeValue(value);
322
+ }
323
+ } else if (node.traits && typeof node.traits === "object") {
324
+ for (const [key, value] of Object.entries(node.traits)) {
325
+ traits[key] = this.sanitizeValue(value);
326
+ }
327
+ }
328
+ const properties = this.sanitizeProperties(node.properties || {});
329
+ const children = (node.children || []).map((child) => this.serializeNode(child));
330
+ return { id, type: node.type || "entity", properties, traits, children };
331
+ }
332
+ /**
333
+ * Serialize a node tree to a JSON string.
334
+ */
335
+ toJSON(root, sceneName = "untitled") {
336
+ const scene = this.serializeFromNode(root, sceneName);
337
+ return JSON.stringify(scene, null, 2);
338
+ }
339
+ sanitizeProperties(props) {
340
+ const result = {};
341
+ for (const [key, value] of Object.entries(props)) {
342
+ if (key.startsWith("_")) continue;
343
+ if (key === "__holo_id") continue;
344
+ result[key] = this.sanitizeValue(value);
345
+ }
346
+ return result;
347
+ }
348
+ sanitizeValue(value) {
349
+ if (value === null || value === void 0) return value;
350
+ if (typeof value === "function") return void 0;
351
+ if (value instanceof Map) {
352
+ const obj = {};
353
+ for (const [k, v] of value) {
354
+ obj[String(k)] = this.sanitizeValue(v);
355
+ }
356
+ return obj;
357
+ }
358
+ if (value instanceof Set) {
359
+ return Array.from(value).map((v) => this.sanitizeValue(v));
360
+ }
361
+ if (Array.isArray(value)) {
362
+ return value.map((v) => this.sanitizeValue(v));
363
+ }
364
+ if (typeof value === "object") {
365
+ const result = {};
366
+ for (const [k, v] of Object.entries(value)) {
367
+ if (k.startsWith("_")) continue;
368
+ const sanitized = this.sanitizeValue(v);
369
+ if (sanitized !== void 0) {
370
+ result[k] = sanitized;
371
+ }
372
+ }
373
+ return result;
374
+ }
375
+ return value;
376
+ }
377
+ };
378
+
379
+ // src/scene/StateSnapshot.ts
380
+ var StateSnapshotCapture = class {
381
+ /**
382
+ * Create a snapshot of the current runtime state.
383
+ * Subsystem references are passed in to avoid coupling.
384
+ */
385
+ capture(options) {
386
+ const { animationEngine, particleSystems, keyboardSystem, scrollOffsets, custom } = options;
387
+ return {
388
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
389
+ animation: {
390
+ activeClipIds: animationEngine?.getActiveIds() || [],
391
+ springValues: {}
392
+ },
393
+ particles: (particleSystems || []).map((ps) => ({
394
+ emitterId: ps.id,
395
+ isEmitting: ps.isEmitting(),
396
+ activeCount: ps.getActiveCount()
397
+ })),
398
+ ui: {
399
+ focusedInputId: keyboardSystem?.focusedInputId || null,
400
+ cursorIndex: keyboardSystem?.cursorIndex || 0,
401
+ scrollOffsets: scrollOffsets || {}
402
+ },
403
+ custom: custom || {}
404
+ };
405
+ }
406
+ };
407
+
408
+ // src/scene/SceneManager.ts
409
+ var SceneManager = class {
410
+ serializer = new SceneSerializer();
411
+ deserializer = new SceneDeserializer();
412
+ stateCapture = new StateSnapshotCapture();
413
+ storage = /* @__PURE__ */ new Map();
414
+ /**
415
+ * Save a scene to the internal store.
416
+ */
417
+ save(name, root, stateOptions, metadata) {
418
+ const scene = this.serializer.serialize(root, name, metadata);
419
+ const state = stateOptions ? this.stateCapture.capture(stateOptions) : void 0;
420
+ const saved = { scene, state };
421
+ this.storage.set(name, saved);
422
+ return saved;
423
+ }
424
+ /**
425
+ * Load a scene by name.
426
+ */
427
+ load(name) {
428
+ const saved = this.storage.get(name);
429
+ if (!saved) return null;
430
+ const node = this.deserializer.deserialize(saved.scene);
431
+ return { node, state: saved.state };
432
+ }
433
+ /**
434
+ * Check if a scene exists.
435
+ */
436
+ has(name) {
437
+ return this.storage.has(name);
438
+ }
439
+ /**
440
+ * Delete a saved scene.
441
+ */
442
+ delete(name) {
443
+ return this.storage.delete(name);
444
+ }
445
+ /**
446
+ * List all saved scenes.
447
+ */
448
+ list() {
449
+ const entries = [];
450
+ for (const [name, saved] of this.storage) {
451
+ entries.push({
452
+ name,
453
+ timestamp: saved.scene.timestamp,
454
+ nodeCount: this.countNodes(saved.scene.root)
455
+ });
456
+ }
457
+ return entries;
458
+ }
459
+ /**
460
+ * Export a scene to JSON string.
461
+ */
462
+ exportJSON(name) {
463
+ const saved = this.storage.get(name);
464
+ if (!saved) return null;
465
+ return JSON.stringify(saved, null, 2);
466
+ }
467
+ /**
468
+ * Import a scene from JSON string.
469
+ */
470
+ importJSON(json) {
471
+ const saved = JSON.parse(json);
472
+ const name = saved.scene.name;
473
+ this.storage.set(name, saved);
474
+ return name;
475
+ }
476
+ /**
477
+ * Get count of saved scenes.
478
+ */
479
+ get count() {
480
+ return this.storage.size;
481
+ }
482
+ countNodes(node) {
483
+ let count = 1;
484
+ if (node.children) {
485
+ for (const child of node.children) {
486
+ count += this.countNodes(child);
487
+ }
488
+ }
489
+ return count;
490
+ }
491
+ };
492
+
493
+ // src/scene/SceneNode.ts
494
+ var SceneNode = class {
495
+ id;
496
+ name;
497
+ tags = /* @__PURE__ */ new Set();
498
+ layer = 0;
499
+ visible = true;
500
+ local;
501
+ worldMatrix;
502
+ dirty = true;
503
+ parent = null;
504
+ children = [];
505
+ constructor(id, name = "") {
506
+ this.id = id;
507
+ this.name = name || id;
508
+ this.local = {
509
+ position: [0, 0, 0],
510
+ rotation: { x: 0, y: 0, z: 0 },
511
+ scale: { x: 1, y: 1, z: 1 }
512
+ };
513
+ this.worldMatrix = new Float64Array(16);
514
+ this.setIdentity(this.worldMatrix);
515
+ }
516
+ // ---------------------------------------------------------------------------
517
+ // Transform
518
+ // ---------------------------------------------------------------------------
519
+ setPosition(x, y, z) {
520
+ this.local.position = [x, y, z];
521
+ this.markDirty();
522
+ }
523
+ setRotation(x, y, z) {
524
+ this.local.rotation = { x, y, z };
525
+ this.markDirty();
526
+ }
527
+ setScale(x, y, z) {
528
+ this.local.scale = { x, y, z };
529
+ this.markDirty();
530
+ }
531
+ getLocalTransform() {
532
+ return {
533
+ ...this.local,
534
+ position: { ...this.local.position },
535
+ rotation: { ...this.local.rotation },
536
+ scale: { ...this.local.scale }
537
+ };
538
+ }
539
+ getWorldPosition() {
540
+ this.updateWorldMatrix();
541
+ return { x: this.worldMatrix[12], y: this.worldMatrix[13], z: this.worldMatrix[14] };
542
+ }
543
+ getWorldMatrix() {
544
+ this.updateWorldMatrix();
545
+ return new Float64Array(this.worldMatrix);
546
+ }
547
+ // ---------------------------------------------------------------------------
548
+ // Hierarchy
549
+ // ---------------------------------------------------------------------------
550
+ addChild(child) {
551
+ if (child.parent) child.parent.removeChild(child);
552
+ child.parent = this;
553
+ this.children.push(child);
554
+ child.markDirty();
555
+ }
556
+ removeChild(child) {
557
+ const idx = this.children.indexOf(child);
558
+ if (idx >= 0) {
559
+ this.children.splice(idx, 1);
560
+ child.parent = null;
561
+ child.markDirty();
562
+ }
563
+ }
564
+ getParent() {
565
+ return this.parent;
566
+ }
567
+ getChildren() {
568
+ return [...this.children];
569
+ }
570
+ getChildCount() {
571
+ return this.children.length;
572
+ }
573
+ // Depth-first traversal
574
+ traverse(callback, depth = 0) {
575
+ callback(this, depth);
576
+ for (const child of this.children) child.traverse(callback, depth + 1);
577
+ }
578
+ // ---------------------------------------------------------------------------
579
+ // Dirty Propagation & World Matrix
580
+ // ---------------------------------------------------------------------------
581
+ markDirty() {
582
+ this.dirty = true;
583
+ for (const child of this.children) child.markDirty();
584
+ }
585
+ updateWorldMatrix() {
586
+ if (!this.dirty) return;
587
+ const localMat = this.computeLocalMatrix();
588
+ if (this.parent) {
589
+ this.parent.updateWorldMatrix();
590
+ this.multiply(this.parent.worldMatrix, localMat, this.worldMatrix);
591
+ } else {
592
+ this.worldMatrix.set(localMat);
593
+ }
594
+ this.dirty = false;
595
+ }
596
+ computeLocalMatrix() {
597
+ const m = new Float64Array(16);
598
+ const { position: p, rotation: r, scale: s } = this.local;
599
+ const cx = Math.cos(r.x), sx = Math.sin(r.x);
600
+ const cy = Math.cos(r.y), sy = Math.sin(r.y);
601
+ const cz = Math.cos(r.z), sz = Math.sin(r.z);
602
+ m[0] = (cy * cz + sy * sx * sz) * s.x;
603
+ m[1] = cx * sz;
604
+ m[2] = (-sy * cz + cy * sx * sz) * s.x;
605
+ m[3] = 0;
606
+ m[4] = (cy * -sz + sy * sx * cz) * s.y;
607
+ m[5] = cx * cz;
608
+ m[6] = (sy * sz + cy * sx * cz) * s.y;
609
+ m[7] = 0;
610
+ m[8] = sy * cx * s.z;
611
+ m[9] = -sx;
612
+ m[10] = cy * cx * s.z;
613
+ m[11] = 0;
614
+ m[12] = p[0];
615
+ m[13] = p[1];
616
+ m[14] = p[2];
617
+ m[15] = 1;
618
+ return m;
619
+ }
620
+ // ---------------------------------------------------------------------------
621
+ // Matrix Math Helpers
622
+ // ---------------------------------------------------------------------------
623
+ setIdentity(m) {
624
+ m.fill(0);
625
+ m[0] = 1;
626
+ m[5] = 1;
627
+ m[10] = 1;
628
+ m[15] = 1;
629
+ }
630
+ multiply(a, b, out) {
631
+ for (let col = 0; col < 4; col++) {
632
+ for (let row = 0; row < 4; row++) {
633
+ out[col * 4 + row] = a[row] * b[col * 4] + a[4 + row] * b[col * 4 + 1] + a[8 + row] * b[col * 4 + 2] + a[12 + row] * b[col * 4 + 3];
634
+ }
635
+ }
636
+ }
637
+ isDirty() {
638
+ return this.dirty;
639
+ }
640
+ };
641
+
642
+ // src/scene/SceneQuery.ts
643
+ var SceneQuery = class _SceneQuery {
644
+ // ---------------------------------------------------------------------------
645
+ // Tag/Layer Filtering
646
+ // ---------------------------------------------------------------------------
647
+ static findByTag(root, tag) {
648
+ const results = [];
649
+ root.traverse((node) => {
650
+ if (node.tags.has(tag)) results.push(node);
651
+ });
652
+ return results;
653
+ }
654
+ static findByLayer(root, layer) {
655
+ const results = [];
656
+ root.traverse((node) => {
657
+ if (node.layer === layer) results.push(node);
658
+ });
659
+ return results;
660
+ }
661
+ static findByName(root, name) {
662
+ let found = null;
663
+ root.traverse((node) => {
664
+ if (node.name === name) found = node;
665
+ });
666
+ return found;
667
+ }
668
+ // ---------------------------------------------------------------------------
669
+ // Spatial Queries
670
+ // ---------------------------------------------------------------------------
671
+ static findInRadius(root, center, radius) {
672
+ const results = [];
673
+ root.traverse((node) => {
674
+ const wp = node.getWorldPosition();
675
+ const dx = wp.x - center.x, dy = wp.y - center.y, dz = wp.z - center.z;
676
+ if (Math.sqrt(dx * dx + dy * dy + dz * dz) <= radius) results.push(node);
677
+ });
678
+ return results;
679
+ }
680
+ // ---------------------------------------------------------------------------
681
+ // Frustum Culling (simplified cone check)
682
+ // ---------------------------------------------------------------------------
683
+ static frustumCull(root, frustum) {
684
+ const results = [];
685
+ const dir = _SceneQuery.normalize(frustum.direction);
686
+ const halfFovRad = frustum.fov / 2 * Math.PI / 180;
687
+ const cosHalfFov = Math.cos(halfFovRad);
688
+ root.traverse((node) => {
689
+ if (!node.visible) return;
690
+ const wp = node.getWorldPosition();
691
+ const dx = wp[0] - frustum.position[0];
692
+ const dy = wp[1] - frustum.position[1];
693
+ const dz = wp[2] - frustum.position[2];
694
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
695
+ if (dist < frustum.near || dist > frustum.far) return;
696
+ if (dist > 0) {
697
+ const dot = (dx * dir.x + dy * dir.y + dz * dir.z) / dist;
698
+ if (dot >= cosHalfFov) results.push(node);
699
+ }
700
+ });
701
+ return results;
702
+ }
703
+ // ---------------------------------------------------------------------------
704
+ // Visitor Pattern
705
+ // ---------------------------------------------------------------------------
706
+ static visit(root, visitor) {
707
+ const queue = [root];
708
+ while (queue.length > 0) {
709
+ const node = queue.shift();
710
+ const cont = visitor(node);
711
+ if (cont === false) return;
712
+ queue.push(...node.getChildren());
713
+ }
714
+ }
715
+ // ---------------------------------------------------------------------------
716
+ // Helpers
717
+ // ---------------------------------------------------------------------------
718
+ static normalize(v) {
719
+ const len = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) || 1;
720
+ return { x: v.x / len, y: v.y / len, z: v.z / len };
721
+ }
722
+ };
723
+
724
+ // src/scene/VersionMigration.ts
725
+ var CURRENT_SCHEMA_VERSION = 5;
726
+ var VersionMigration = class {
727
+ migrations = [];
728
+ logs = [];
729
+ constructor() {
730
+ this.registerBuiltIns();
731
+ }
732
+ // ---------------------------------------------------------------------------
733
+ // Migration Registration
734
+ // ---------------------------------------------------------------------------
735
+ register(step) {
736
+ this.migrations.push(step);
737
+ this.migrations.sort((a, b) => a.fromVersion - b.fromVersion);
738
+ }
739
+ getMigrations() {
740
+ return [...this.migrations];
741
+ }
742
+ getMigrationCount() {
743
+ return this.migrations.length;
744
+ }
745
+ // ---------------------------------------------------------------------------
746
+ // Migration Execution
747
+ // ---------------------------------------------------------------------------
748
+ /**
749
+ * Migrate data from its current version to the target version.
750
+ */
751
+ migrate(data, targetVersion = CURRENT_SCHEMA_VERSION) {
752
+ const currentVersion = data.version ?? 0;
753
+ const stepsApplied = [];
754
+ const warnings = [];
755
+ if (currentVersion >= targetVersion) {
756
+ return {
757
+ success: true,
758
+ fromVersion: currentVersion,
759
+ toVersion: currentVersion,
760
+ stepsApplied,
761
+ warnings,
762
+ data
763
+ };
764
+ }
765
+ let workingData = { ...data };
766
+ let version = currentVersion;
767
+ while (version < targetVersion) {
768
+ const step = this.migrations.find((m) => m.fromVersion === version);
769
+ if (!step) {
770
+ warnings.push(`No migration step from v${version}, attempting skip to v${version + 1}`);
771
+ version++;
772
+ continue;
773
+ }
774
+ try {
775
+ workingData = step.migrate(workingData);
776
+ stepsApplied.push(step.name);
777
+ version = step.toVersion;
778
+ workingData.version = version;
779
+ } catch (err) {
780
+ return {
781
+ success: false,
782
+ fromVersion: currentVersion,
783
+ toVersion: version,
784
+ stepsApplied,
785
+ warnings: [
786
+ ...warnings,
787
+ `Migration failed at step "${step.name}": ${err.message}`
788
+ ],
789
+ data: workingData
790
+ };
791
+ }
792
+ }
793
+ this.logs.push({
794
+ timestamp: Date.now(),
795
+ fromVersion: currentVersion,
796
+ toVersion: targetVersion,
797
+ stepsApplied,
798
+ dataHash: this.simpleHash(JSON.stringify(workingData))
799
+ });
800
+ return {
801
+ success: true,
802
+ fromVersion: currentVersion,
803
+ toVersion: targetVersion,
804
+ stepsApplied,
805
+ warnings,
806
+ data: workingData
807
+ };
808
+ }
809
+ /**
810
+ * Check if data needs migration.
811
+ */
812
+ needsMigration(data) {
813
+ const version = data.version ?? 0;
814
+ return version < CURRENT_SCHEMA_VERSION;
815
+ }
816
+ /**
817
+ * Get the version of the given data.
818
+ */
819
+ getDataVersion(data) {
820
+ return data.version ?? 0;
821
+ }
822
+ // ---------------------------------------------------------------------------
823
+ // Logs
824
+ // ---------------------------------------------------------------------------
825
+ getLogs() {
826
+ return [...this.logs];
827
+ }
828
+ clearLogs() {
829
+ this.logs = [];
830
+ }
831
+ // ---------------------------------------------------------------------------
832
+ // Built-in migrations
833
+ // ---------------------------------------------------------------------------
834
+ registerBuiltIns() {
835
+ this.register({
836
+ fromVersion: 0,
837
+ toVersion: 1,
838
+ name: "add_metadata",
839
+ migrate: (data) => ({
840
+ ...data,
841
+ metadata: data.metadata ?? {}
842
+ })
843
+ });
844
+ this.register({
845
+ fromVersion: 1,
846
+ toVersion: 2,
847
+ name: "normalize_entity_ids",
848
+ migrate: (data) => {
849
+ const entities = data.entities ?? [];
850
+ for (const e of entities) {
851
+ e.id = String(e.id);
852
+ }
853
+ return { ...data, entities };
854
+ }
855
+ });
856
+ this.register({
857
+ fromVersion: 2,
858
+ toVersion: 3,
859
+ name: "add_entity_active_flag",
860
+ migrate: (data) => {
861
+ const entities = data.entities ?? [];
862
+ for (const e of entities) {
863
+ if (e.active === void 0) e.active = true;
864
+ }
865
+ return { ...data, entities };
866
+ }
867
+ });
868
+ this.register({
869
+ fromVersion: 3,
870
+ toVersion: 4,
871
+ name: "add_globals",
872
+ migrate: (data) => ({
873
+ ...data,
874
+ globals: data.globals ?? {}
875
+ })
876
+ });
877
+ this.register({
878
+ fromVersion: 4,
879
+ toVersion: 5,
880
+ name: "add_entity_tags",
881
+ migrate: (data) => {
882
+ const entities = data.entities ?? [];
883
+ for (const e of entities) {
884
+ if (!Array.isArray(e.tags)) e.tags = [];
885
+ }
886
+ return { ...data, entities };
887
+ }
888
+ });
889
+ }
890
+ // ---------------------------------------------------------------------------
891
+ // Helpers
892
+ // ---------------------------------------------------------------------------
893
+ simpleHash(str) {
894
+ let h = 0;
895
+ for (let i = 0; i < str.length; i++) {
896
+ h = (h << 5) - h + str.charCodeAt(i) | 0;
897
+ }
898
+ return Math.abs(h).toString(16);
899
+ }
900
+ };
901
+
902
+ export {
903
+ ProjectManager,
904
+ SceneDeserializer,
905
+ SceneSerializer,
906
+ StateSnapshotCapture,
907
+ SceneManager,
908
+ SceneNode,
909
+ SceneQuery,
910
+ CURRENT_SCHEMA_VERSION,
911
+ VersionMigration,
912
+ scene_exports
913
+ };