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