@holoscript/core 2.0.0 → 2.0.2

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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-3N67RLQP.cjs +1298 -0
  3. package/dist/chunk-3N67RLQP.cjs.map +1 -0
  4. package/dist/chunk-3X2EGU7Z.cjs +52 -0
  5. package/dist/chunk-3X2EGU7Z.cjs.map +1 -0
  6. package/dist/chunk-4CV4JOE5.js +24 -0
  7. package/dist/chunk-4CV4JOE5.js.map +1 -0
  8. package/dist/chunk-4OHVW4XR.cjs +1027 -0
  9. package/dist/chunk-4OHVW4XR.cjs.map +1 -0
  10. package/dist/chunk-CZLDE2OZ.cjs +28 -0
  11. package/dist/chunk-CZLDE2OZ.cjs.map +1 -0
  12. package/{src/HoloScriptRuntime.ts → dist/chunk-EU6CZMGJ.js} +437 -794
  13. package/dist/chunk-EU6CZMGJ.js.map +1 -0
  14. package/dist/chunk-KWYIVRIH.js +344 -0
  15. package/dist/chunk-KWYIVRIH.js.map +1 -0
  16. package/dist/chunk-MCP6D4LT.js +1025 -0
  17. package/dist/chunk-MCP6D4LT.js.map +1 -0
  18. package/dist/chunk-SATNCODL.js +45 -0
  19. package/dist/chunk-SATNCODL.js.map +1 -0
  20. package/dist/chunk-VMZN4EVR.cjs +347 -0
  21. package/dist/chunk-VMZN4EVR.cjs.map +1 -0
  22. package/{src/HoloScriptDebugger.ts → dist/chunk-VYIDLUCV.js} +118 -257
  23. package/dist/chunk-VYIDLUCV.js.map +1 -0
  24. package/dist/chunk-WFI4T3XB.cjs +424 -0
  25. package/dist/chunk-WFI4T3XB.cjs.map +1 -0
  26. package/dist/debugger.cjs +20 -0
  27. package/dist/debugger.cjs.map +1 -0
  28. package/dist/debugger.d.cts +171 -0
  29. package/dist/debugger.d.ts +171 -0
  30. package/dist/debugger.js +7 -0
  31. package/dist/debugger.js.map +1 -0
  32. package/dist/index.cjs +6006 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.cts +2482 -0
  35. package/dist/index.d.ts +2482 -0
  36. package/dist/index.js +5926 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/parser.cjs +14 -0
  39. package/dist/parser.cjs.map +1 -0
  40. package/dist/parser.d.cts +139 -0
  41. package/dist/parser.d.ts +139 -0
  42. package/dist/parser.js +5 -0
  43. package/dist/parser.js.map +1 -0
  44. package/dist/runtime.cjs +14 -0
  45. package/dist/runtime.cjs.map +1 -0
  46. package/dist/runtime.d.cts +180 -0
  47. package/dist/runtime.d.ts +180 -0
  48. package/dist/runtime.js +5 -0
  49. package/dist/runtime.js.map +1 -0
  50. package/dist/type-checker.cjs +17 -0
  51. package/dist/type-checker.cjs.map +1 -0
  52. package/dist/type-checker.d.cts +105 -0
  53. package/dist/type-checker.d.ts +105 -0
  54. package/dist/type-checker.js +4 -0
  55. package/dist/type-checker.js.map +1 -0
  56. package/dist/types-D6g4ACjP.d.cts +262 -0
  57. package/dist/types-D6g4ACjP.d.ts +262 -0
  58. package/package.json +11 -8
  59. package/src/HoloScript2DParser.js +0 -227
  60. package/src/HoloScript2DParser.ts +0 -261
  61. package/src/HoloScriptCodeParser.js +0 -1102
  62. package/src/HoloScriptCodeParser.ts +0 -1188
  63. package/src/HoloScriptDebugger.js +0 -458
  64. package/src/HoloScriptParser.js +0 -338
  65. package/src/HoloScriptParser.ts +0 -397
  66. package/src/HoloScriptPlusParser.js +0 -371
  67. package/src/HoloScriptPlusParser.ts +0 -543
  68. package/src/HoloScriptRuntime.js +0 -1399
  69. package/src/HoloScriptRuntime.test.js +0 -351
  70. package/src/HoloScriptRuntime.test.ts +0 -436
  71. package/src/HoloScriptTypeChecker.js +0 -356
  72. package/src/HoloScriptTypeChecker.ts +0 -475
  73. package/src/__tests__/GraphicsServices.test.js +0 -357
  74. package/src/__tests__/GraphicsServices.test.ts +0 -427
  75. package/src/__tests__/HoloScriptPlusParser.test.js +0 -317
  76. package/src/__tests__/HoloScriptPlusParser.test.ts +0 -392
  77. package/src/__tests__/integration.test.js +0 -336
  78. package/src/__tests__/integration.test.ts +0 -416
  79. package/src/__tests__/performance.bench.js +0 -218
  80. package/src/__tests__/performance.bench.ts +0 -262
  81. package/src/__tests__/type-checker.test.js +0 -60
  82. package/src/__tests__/type-checker.test.ts +0 -73
  83. package/src/index.js +0 -217
  84. package/src/index.ts +0 -426
  85. package/src/interop/Interoperability.js +0 -413
  86. package/src/interop/Interoperability.ts +0 -494
  87. package/src/logger.js +0 -42
  88. package/src/logger.ts +0 -57
  89. package/src/parser/EnhancedParser.js +0 -205
  90. package/src/parser/EnhancedParser.ts +0 -251
  91. package/src/parser/HoloScriptPlusParser.js +0 -928
  92. package/src/parser/HoloScriptPlusParser.ts +0 -1089
  93. package/src/runtime/HoloScriptPlusRuntime.js +0 -674
  94. package/src/runtime/HoloScriptPlusRuntime.ts +0 -861
  95. package/src/runtime/PerformanceTelemetry.js +0 -323
  96. package/src/runtime/PerformanceTelemetry.ts +0 -467
  97. package/src/runtime/RuntimeOptimization.js +0 -361
  98. package/src/runtime/RuntimeOptimization.ts +0 -416
  99. package/src/services/HololandGraphicsPipelineService.js +0 -506
  100. package/src/services/HololandGraphicsPipelineService.ts +0 -662
  101. package/src/services/PlatformPerformanceOptimizer.js +0 -356
  102. package/src/services/PlatformPerformanceOptimizer.ts +0 -503
  103. package/src/state/ReactiveState.js +0 -427
  104. package/src/state/ReactiveState.ts +0 -572
  105. package/src/tools/DeveloperExperience.js +0 -376
  106. package/src/tools/DeveloperExperience.ts +0 -438
  107. package/src/traits/AIDriverTrait.js +0 -322
  108. package/src/traits/AIDriverTrait.test.js +0 -329
  109. package/src/traits/AIDriverTrait.test.ts +0 -357
  110. package/src/traits/AIDriverTrait.ts +0 -474
  111. package/src/traits/LightingTrait.js +0 -313
  112. package/src/traits/LightingTrait.test.js +0 -410
  113. package/src/traits/LightingTrait.test.ts +0 -462
  114. package/src/traits/LightingTrait.ts +0 -505
  115. package/src/traits/MaterialTrait.js +0 -194
  116. package/src/traits/MaterialTrait.test.js +0 -286
  117. package/src/traits/MaterialTrait.test.ts +0 -329
  118. package/src/traits/MaterialTrait.ts +0 -324
  119. package/src/traits/RenderingTrait.js +0 -356
  120. package/src/traits/RenderingTrait.test.js +0 -363
  121. package/src/traits/RenderingTrait.test.ts +0 -427
  122. package/src/traits/RenderingTrait.ts +0 -555
  123. package/src/traits/VRTraitSystem.js +0 -740
  124. package/src/traits/VRTraitSystem.ts +0 -1040
  125. package/src/traits/VoiceInputTrait.js +0 -284
  126. package/src/traits/VoiceInputTrait.test.js +0 -226
  127. package/src/traits/VoiceInputTrait.test.ts +0 -252
  128. package/src/traits/VoiceInputTrait.ts +0 -401
  129. package/src/types/AdvancedTypeSystem.js +0 -226
  130. package/src/types/AdvancedTypeSystem.ts +0 -494
  131. package/src/types/HoloScriptPlus.d.ts +0 -853
  132. package/src/types.js +0 -6
  133. package/src/types.ts +0 -369
  134. package/tsconfig.json +0 -23
  135. package/tsup.config.d.ts +0 -2
  136. package/tsup.config.js +0 -18
  137. package/tsup.config.ts +0 -19
@@ -1,674 +0,0 @@
1
- /**
2
- * HoloScript+ Runtime Engine
3
- *
4
- * Executes parsed HoloScript+ AST with:
5
- * - Control flow (@for, @if) evaluation
6
- * - Lifecycle hook management
7
- * - VR trait integration
8
- * - Reactive state binding
9
- * - TypeScript companion integration
10
- *
11
- * @version 1.0.0
12
- */
13
- import { createState, ExpressionEvaluator } from '../state/ReactiveState';
14
- import { vrTraitRegistry } from '../traits/VRTraitSystem';
15
- // =============================================================================
16
- // BUILT-IN FUNCTIONS
17
- // =============================================================================
18
- function createBuiltins(runtime) {
19
- return {
20
- Math,
21
- range: (start, end, step = 1) => {
22
- const result = [];
23
- if (step > 0) {
24
- for (let i = start; i < end; i += step) {
25
- result.push(i);
26
- }
27
- }
28
- else if (step < 0) {
29
- for (let i = start; i > end; i += step) {
30
- result.push(i);
31
- }
32
- }
33
- return result;
34
- },
35
- interpolate_color: (t, from, to) => {
36
- // Parse hex colors
37
- const parseHex = (hex) => {
38
- const clean = hex.replace('#', '');
39
- return [
40
- parseInt(clean.substring(0, 2), 16),
41
- parseInt(clean.substring(2, 4), 16),
42
- parseInt(clean.substring(4, 6), 16),
43
- ];
44
- };
45
- const toHex = (r, g, b) => {
46
- const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
47
- return `#${clamp(r).toString(16).padStart(2, '0')}${clamp(g).toString(16).padStart(2, '0')}${clamp(b).toString(16).padStart(2, '0')}`;
48
- };
49
- const [r1, g1, b1] = parseHex(from);
50
- const [r2, g2, b2] = parseHex(to);
51
- return toHex(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
52
- },
53
- distance_to: (point) => {
54
- const viewer = runtime.vrContext.headset.position;
55
- return Math.sqrt(Math.pow(point[0] - viewer[0], 2) +
56
- Math.pow(point[1] - viewer[1], 2) +
57
- Math.pow(point[2] - viewer[2], 2));
58
- },
59
- distance_to_viewer: () => {
60
- return 0; // Override in node context
61
- },
62
- hand_position: (handId) => {
63
- const hand = handId === 'left' ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
64
- return hand?.position || [0, 0, 0];
65
- },
66
- hand_velocity: (handId) => {
67
- const hand = handId === 'left' ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
68
- return hand?.velocity || [0, 0, 0];
69
- },
70
- dominant_hand: () => {
71
- // Default to right hand
72
- return runtime.vrContext.hands.right || runtime.vrContext.hands.left || {
73
- id: 'right',
74
- position: [0, 0, 0],
75
- rotation: [0, 0, 0],
76
- velocity: [0, 0, 0],
77
- grip: 0,
78
- trigger: 0,
79
- };
80
- },
81
- play_sound: (source, options) => {
82
- runtime.emit('play_sound', { source, ...options });
83
- },
84
- haptic_feedback: (hand, intensity) => {
85
- const handId = typeof hand === 'string' ? hand : hand.id;
86
- runtime.emit('haptic', { hand: handId, intensity });
87
- },
88
- haptic_pulse: (intensity) => {
89
- runtime.emit('haptic', { hand: 'both', intensity });
90
- },
91
- apply_velocity: (node, velocity) => {
92
- runtime.emit('apply_velocity', { node, velocity });
93
- },
94
- spawn: (template, position) => {
95
- return runtime.spawnTemplate(template, position);
96
- },
97
- destroy: (node) => {
98
- runtime.destroyNode(node);
99
- },
100
- api_call: async (url, method, body) => {
101
- const response = await fetch(url, {
102
- method,
103
- headers: body ? { 'Content-Type': 'application/json' } : undefined,
104
- body: body ? JSON.stringify(body) : undefined,
105
- });
106
- return response.json();
107
- },
108
- open_modal: (modalId) => {
109
- runtime.emit('open_modal', { id: modalId });
110
- },
111
- close_modal: (modalId) => {
112
- runtime.emit('close_modal', { id: modalId });
113
- },
114
- setTimeout: (callback, delay) => {
115
- return window.setTimeout(callback, delay);
116
- },
117
- clearTimeout: (id) => {
118
- window.clearTimeout(id);
119
- },
120
- };
121
- }
122
- // =============================================================================
123
- // RUNTIME IMPLEMENTATION
124
- // =============================================================================
125
- class HoloScriptPlusRuntimeImpl {
126
- constructor(ast, options = {}) {
127
- this.rootInstance = null;
128
- this.eventHandlers = new Map();
129
- this.templates = new Map();
130
- this.updateLoopId = null;
131
- this.lastUpdateTime = 0;
132
- this.mounted = false;
133
- // VR context
134
- this.vrContext = {
135
- hands: {
136
- left: null,
137
- right: null,
138
- },
139
- headset: {
140
- position: [0, 1.6, 0],
141
- rotation: [0, 0, 0],
142
- },
143
- controllers: {
144
- left: null,
145
- right: null,
146
- },
147
- };
148
- this.ast = ast;
149
- this.options = options;
150
- this.state = createState({});
151
- this.traitRegistry = vrTraitRegistry;
152
- this.companions = options.companions || {};
153
- this.builtins = createBuiltins(this);
154
- // Create expression evaluator with context
155
- this.evaluator = new ExpressionEvaluator(this.state.getSnapshot(), this.builtins);
156
- // Initialize state from AST
157
- this.initializeState();
158
- // Load imports
159
- this.loadImports();
160
- }
161
- // ==========================================================================
162
- // INITIALIZATION
163
- // ==========================================================================
164
- initializeState() {
165
- const stateDirective = this.ast.root.directives.find((d) => d.type === 'state');
166
- if (stateDirective && stateDirective.type === 'state') {
167
- this.state.update(stateDirective.body);
168
- }
169
- }
170
- loadImports() {
171
- for (const imp of this.ast.imports) {
172
- // Companions should be provided via options
173
- if (this.companions[imp.alias]) {
174
- // Already loaded
175
- continue;
176
- }
177
- console.warn(`Import ${imp.path} not found. Provide via companions option.`);
178
- }
179
- }
180
- // ==========================================================================
181
- // MOUNTING
182
- // ==========================================================================
183
- mount(container) {
184
- if (this.mounted) {
185
- console.warn('Runtime already mounted');
186
- return;
187
- }
188
- this.mounted = true;
189
- // Build node tree
190
- this.rootInstance = this.instantiateNode(this.ast.root, null);
191
- // Mount to container
192
- if (this.options.renderer && this.rootInstance) {
193
- this.options.renderer.appendChild(container, this.rootInstance.renderedNode);
194
- }
195
- // Call mount lifecycle
196
- this.callLifecycle(this.rootInstance, 'on_mount');
197
- // Start update loop
198
- this.startUpdateLoop();
199
- }
200
- unmount() {
201
- if (!this.mounted)
202
- return;
203
- // Stop update loop
204
- this.stopUpdateLoop();
205
- // Call unmount lifecycle
206
- if (this.rootInstance) {
207
- this.callLifecycle(this.rootInstance, 'on_unmount');
208
- this.destroyInstance(this.rootInstance);
209
- }
210
- this.rootInstance = null;
211
- this.mounted = false;
212
- }
213
- // ==========================================================================
214
- // NODE INSTANTIATION
215
- // ==========================================================================
216
- instantiateNode(node, parent) {
217
- const instance = {
218
- node,
219
- renderedNode: null,
220
- lifecycleHandlers: new Map(),
221
- children: [],
222
- parent,
223
- destroyed: false,
224
- };
225
- // Process directives
226
- this.processDirectives(instance);
227
- // Create rendered element
228
- if (this.options.renderer) {
229
- const properties = this.evaluateProperties(node.properties);
230
- instance.renderedNode = this.options.renderer.createElement(node.type, properties);
231
- }
232
- // Attach VR traits
233
- for (const [traitName, config] of node.traits) {
234
- this.traitRegistry.attachTrait(node, traitName, config, this.createTraitContext(instance));
235
- }
236
- // Process children with control flow
237
- const children = this.processControlFlow(node.children, node.directives);
238
- for (const childNode of children) {
239
- const childInstance = this.instantiateNode(childNode, instance);
240
- instance.children.push(childInstance);
241
- if (this.options.renderer && instance.renderedNode) {
242
- this.options.renderer.appendChild(instance.renderedNode, childInstance.renderedNode);
243
- }
244
- }
245
- return instance;
246
- }
247
- processDirectives(instance) {
248
- for (const directive of instance.node.directives) {
249
- if (directive.type === 'lifecycle') {
250
- this.registerLifecycleHandler(instance, directive);
251
- }
252
- }
253
- }
254
- registerLifecycleHandler(instance, directive) {
255
- const { hook, params, body } = directive;
256
- // Create handler function
257
- const handler = (...args) => {
258
- // Build parameter context
259
- const paramContext = {};
260
- if (params) {
261
- params.forEach((param, i) => {
262
- paramContext[param] = args[i];
263
- });
264
- }
265
- // Evaluate body
266
- this.evaluator.updateContext({
267
- ...this.state.getSnapshot(),
268
- ...paramContext,
269
- node: instance.node,
270
- self: instance.node,
271
- });
272
- try {
273
- // Check if body looks like code or expression
274
- if (body.includes(';') || body.includes('{')) {
275
- // Execute as code block
276
- new Function(...Object.keys(this.builtins), ...Object.keys(paramContext), 'state', 'node', body)(...Object.values(this.builtins), ...Object.values(paramContext), this.state, instance.node);
277
- }
278
- else {
279
- // Evaluate as expression
280
- this.evaluator.evaluate(body);
281
- }
282
- }
283
- catch (error) {
284
- console.error(`Error in lifecycle handler ${hook}:`, error);
285
- }
286
- };
287
- // Register handler
288
- if (!instance.lifecycleHandlers.has(hook)) {
289
- instance.lifecycleHandlers.set(hook, []);
290
- }
291
- instance.lifecycleHandlers.get(hook).push(handler);
292
- }
293
- // ==========================================================================
294
- // CONTROL FLOW
295
- // ==========================================================================
296
- processControlFlow(children, directives) {
297
- const result = [];
298
- // Process @for directives
299
- for (const directive of directives) {
300
- if (directive.type === 'for') {
301
- const items = this.evaluateExpression(directive.iterable);
302
- if (Array.isArray(items)) {
303
- items.forEach((item, index) => {
304
- // Create context for each iteration
305
- const iterContext = {
306
- [directive.variable]: item,
307
- index,
308
- first: index === 0,
309
- last: index === items.length - 1,
310
- even: index % 2 === 0,
311
- odd: index % 2 !== 0,
312
- };
313
- // Clone and process body nodes
314
- for (const bodyNode of directive.body) {
315
- const cloned = this.cloneNodeWithContext(bodyNode, iterContext);
316
- result.push(cloned);
317
- }
318
- });
319
- }
320
- }
321
- else if (directive.type === 'if') {
322
- const condition = this.evaluateExpression(directive.condition);
323
- if (condition) {
324
- result.push(...directive.body);
325
- }
326
- else if (directive.else) {
327
- result.push(...directive.else);
328
- }
329
- }
330
- }
331
- // Add regular children
332
- result.push(...children);
333
- return result;
334
- }
335
- cloneNodeWithContext(node, context) {
336
- // Deep clone the node
337
- const cloned = {
338
- type: node.type,
339
- id: node.id ? this.interpolateString(node.id, context) : undefined,
340
- properties: this.interpolateProperties(node.properties, context),
341
- directives: [...node.directives],
342
- children: node.children.map((child) => this.cloneNodeWithContext(child, context)),
343
- traits: new Map(node.traits),
344
- loc: node.loc,
345
- };
346
- return cloned;
347
- }
348
- interpolateString(str, context) {
349
- return str.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
350
- this.evaluator.updateContext(context);
351
- const value = this.evaluator.evaluate(expr);
352
- return String(value ?? '');
353
- });
354
- }
355
- interpolateProperties(properties, context) {
356
- const result = {};
357
- for (const [key, value] of Object.entries(properties)) {
358
- if (typeof value === 'string') {
359
- result[key] = this.interpolateString(value, context);
360
- }
361
- else if (value && typeof value === 'object' && '__expr' in value) {
362
- this.evaluator.updateContext(context);
363
- result[key] = this.evaluator.evaluate(value.__raw);
364
- }
365
- else {
366
- result[key] = value;
367
- }
368
- }
369
- return result;
370
- }
371
- // ==========================================================================
372
- // EXPRESSION EVALUATION
373
- // ==========================================================================
374
- evaluateExpression(expr) {
375
- this.evaluator.updateContext(this.state.getSnapshot());
376
- return this.evaluator.evaluate(expr);
377
- }
378
- evaluateProperties(properties) {
379
- const result = {};
380
- for (const [key, value] of Object.entries(properties)) {
381
- if (value && typeof value === 'object' && '__expr' in value) {
382
- result[key] = this.evaluateExpression(value.__raw);
383
- }
384
- else if (value && typeof value === 'object' && '__ref' in value) {
385
- // Reference to state or companion
386
- const ref = value.__ref;
387
- result[key] = this.state.get(ref) ?? ref;
388
- }
389
- else if (typeof value === 'string' && value.includes('${')) {
390
- // String interpolation
391
- result[key] = this.interpolateString(value, this.state.getSnapshot());
392
- }
393
- else {
394
- result[key] = value;
395
- }
396
- }
397
- return result;
398
- }
399
- // ==========================================================================
400
- // LIFECYCLE
401
- // ==========================================================================
402
- callLifecycle(instance, hook, ...args) {
403
- if (!instance || instance.destroyed)
404
- return;
405
- const handlers = instance.lifecycleHandlers.get(hook);
406
- if (handlers) {
407
- handlers.forEach((handler) => {
408
- try {
409
- handler(...args);
410
- }
411
- catch (error) {
412
- console.error(`Error in lifecycle ${hook}:`, error);
413
- }
414
- });
415
- }
416
- // Recurse to children
417
- for (const child of instance.children) {
418
- this.callLifecycle(child, hook, ...args);
419
- }
420
- }
421
- // ==========================================================================
422
- // UPDATE LOOP
423
- // ==========================================================================
424
- startUpdateLoop() {
425
- this.lastUpdateTime = performance.now();
426
- const update = () => {
427
- const now = performance.now();
428
- const delta = (now - this.lastUpdateTime) / 1000; // Convert to seconds
429
- this.lastUpdateTime = now;
430
- this.update(delta);
431
- this.updateLoopId = requestAnimationFrame(update);
432
- };
433
- this.updateLoopId = requestAnimationFrame(update);
434
- }
435
- stopUpdateLoop() {
436
- if (this.updateLoopId !== null) {
437
- cancelAnimationFrame(this.updateLoopId);
438
- this.updateLoopId = null;
439
- }
440
- }
441
- update(delta) {
442
- if (!this.rootInstance)
443
- return;
444
- // Update all instances
445
- this.updateInstance(this.rootInstance, delta);
446
- // Call update lifecycle
447
- this.callLifecycle(this.rootInstance, 'on_update', delta);
448
- }
449
- updateInstance(instance, delta) {
450
- if (instance.destroyed)
451
- return;
452
- // Update VR traits
453
- const traitContext = this.createTraitContext(instance);
454
- this.traitRegistry.updateAllTraits(instance.node, traitContext, delta);
455
- // Update rendered element if properties changed
456
- if (this.options.renderer && instance.renderedNode) {
457
- const properties = this.evaluateProperties(instance.node.properties);
458
- this.options.renderer.updateElement(instance.renderedNode, properties);
459
- }
460
- // Update children
461
- for (const child of instance.children) {
462
- this.updateInstance(child, delta);
463
- }
464
- }
465
- // ==========================================================================
466
- // TRAIT CONTEXT
467
- // ==========================================================================
468
- createTraitContext(_instance) {
469
- return {
470
- vr: {
471
- hands: this.vrContext.hands,
472
- headset: this.vrContext.headset,
473
- getPointerRay: (hand) => {
474
- const vrHand = hand === 'left' ? this.vrContext.hands.left : this.vrContext.hands.right;
475
- if (!vrHand)
476
- return null;
477
- return {
478
- origin: vrHand.position,
479
- direction: [0, 0, -1], // Forward direction - should be calculated from rotation
480
- };
481
- },
482
- getDominantHand: () => this.vrContext.hands.right || this.vrContext.hands.left,
483
- },
484
- physics: {
485
- applyVelocity: (node, velocity) => {
486
- this.emit('apply_velocity', { node, velocity });
487
- },
488
- applyAngularVelocity: (node, angularVelocity) => {
489
- this.emit('apply_angular_velocity', { node, angularVelocity });
490
- },
491
- setKinematic: (node, kinematic) => {
492
- this.emit('set_kinematic', { node, kinematic });
493
- },
494
- raycast: (_origin, _direction, _maxDistance) => {
495
- // Would need physics engine integration
496
- return null;
497
- },
498
- },
499
- audio: {
500
- playSound: (source, options) => {
501
- this.emit('play_sound', { source, ...options });
502
- },
503
- },
504
- haptics: {
505
- pulse: (hand, intensity, duration) => {
506
- this.emit('haptic', { hand, intensity, duration, type: 'pulse' });
507
- },
508
- rumble: (hand, intensity) => {
509
- this.emit('haptic', { hand, intensity, type: 'rumble' });
510
- },
511
- },
512
- emit: this.emit.bind(this),
513
- getState: () => this.state.getSnapshot(),
514
- setState: (updates) => this.state.update(updates),
515
- };
516
- }
517
- // ==========================================================================
518
- // NODE DESTRUCTION
519
- // ==========================================================================
520
- destroyInstance(instance) {
521
- if (instance.destroyed)
522
- return;
523
- instance.destroyed = true;
524
- // Destroy children first
525
- for (const child of instance.children) {
526
- this.destroyInstance(child);
527
- }
528
- // Detach traits
529
- const traitContext = this.createTraitContext(instance);
530
- for (const traitName of instance.node.traits.keys()) {
531
- this.traitRegistry.detachTrait(instance.node, traitName, traitContext);
532
- }
533
- // Destroy rendered element
534
- if (this.options.renderer && instance.renderedNode) {
535
- this.options.renderer.destroy(instance.renderedNode);
536
- }
537
- // Clear handlers
538
- instance.lifecycleHandlers.clear();
539
- instance.children = [];
540
- }
541
- // ==========================================================================
542
- // PUBLIC API
543
- // ==========================================================================
544
- updateData(data) {
545
- this.state.set('data', data);
546
- this.callLifecycle(this.rootInstance, 'on_data_update', data);
547
- }
548
- getState() {
549
- return this.state.getSnapshot();
550
- }
551
- setState(updates) {
552
- this.state.update(updates);
553
- }
554
- emit(event, payload) {
555
- const handlers = this.eventHandlers.get(event);
556
- if (handlers) {
557
- handlers.forEach((handler) => {
558
- try {
559
- handler(payload);
560
- }
561
- catch (error) {
562
- console.error(`Error in event handler for ${event}:`, error);
563
- }
564
- });
565
- }
566
- }
567
- on(event, handler) {
568
- if (!this.eventHandlers.has(event)) {
569
- this.eventHandlers.set(event, new Set());
570
- }
571
- this.eventHandlers.get(event).add(handler);
572
- return () => {
573
- this.eventHandlers.get(event)?.delete(handler);
574
- };
575
- }
576
- // ==========================================================================
577
- // VR INTEGRATION
578
- // ==========================================================================
579
- updateVRContext(context) {
580
- this.vrContext = context;
581
- }
582
- handleVREvent(event, node) {
583
- // Find instance for node
584
- const instance = this.findInstance(node);
585
- if (!instance)
586
- return;
587
- // Dispatch to traits
588
- const traitContext = this.createTraitContext(instance);
589
- this.traitRegistry.handleEventForAllTraits(node, traitContext, event);
590
- // Call lifecycle hooks based on event type
591
- const hookMapping = {
592
- grab_start: 'on_grab',
593
- grab_end: 'on_release',
594
- hover_enter: 'on_hover_enter',
595
- hover_exit: 'on_hover_exit',
596
- point_enter: 'on_point_enter',
597
- point_exit: 'on_point_exit',
598
- collision: 'on_collision',
599
- trigger_enter: 'on_trigger_enter',
600
- trigger_exit: 'on_trigger_exit',
601
- click: 'on_click',
602
- };
603
- const hook = hookMapping[event.type];
604
- if (hook) {
605
- this.callLifecycle(instance, hook, event);
606
- }
607
- }
608
- findInstance(node, root = this.rootInstance) {
609
- if (!root)
610
- return null;
611
- if (root.node === node)
612
- return root;
613
- for (const child of root.children) {
614
- const found = this.findInstance(node, child);
615
- if (found)
616
- return found;
617
- }
618
- return null;
619
- }
620
- // ==========================================================================
621
- // TEMPLATES & SPAWNING
622
- // ==========================================================================
623
- registerTemplate(name, node) {
624
- this.templates.set(name, node);
625
- }
626
- spawnTemplate(name, position) {
627
- const template = this.templates.get(name);
628
- if (!template) {
629
- throw new Error(`Template "${name}" not found`);
630
- }
631
- // Clone template
632
- const cloned = this.cloneNodeWithContext(template, { position });
633
- cloned.properties.position = position;
634
- // Instantiate
635
- if (this.rootInstance) {
636
- const instance = this.instantiateNode(cloned, this.rootInstance);
637
- this.rootInstance.children.push(instance);
638
- if (this.options.renderer && this.rootInstance.renderedNode) {
639
- this.options.renderer.appendChild(this.rootInstance.renderedNode, instance.renderedNode);
640
- }
641
- this.callLifecycle(instance, 'on_mount');
642
- }
643
- return cloned;
644
- }
645
- destroyNode(node) {
646
- const instance = this.findInstance(node);
647
- if (!instance)
648
- return;
649
- // Call unmount
650
- this.callLifecycle(instance, 'on_unmount');
651
- // Remove from parent
652
- if (instance.parent) {
653
- const index = instance.parent.children.indexOf(instance);
654
- if (index > -1) {
655
- instance.parent.children.splice(index, 1);
656
- }
657
- if (this.options.renderer && instance.parent.renderedNode && instance.renderedNode) {
658
- this.options.renderer.removeChild(instance.parent.renderedNode, instance.renderedNode);
659
- }
660
- }
661
- // Destroy
662
- this.destroyInstance(instance);
663
- }
664
- }
665
- // =============================================================================
666
- // FACTORY FUNCTION
667
- // =============================================================================
668
- export function createRuntime(ast, options) {
669
- return new HoloScriptPlusRuntimeImpl(ast, options);
670
- }
671
- // =============================================================================
672
- // EXPORTS
673
- // =============================================================================
674
- export { HoloScriptPlusRuntimeImpl };