@viewscript/renderer 0.1.0-202605140639

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 (89) hide show
  1. package/dist/ast/types.d.ts +403 -0
  2. package/dist/ast/types.js +33 -0
  3. package/dist/compiler/chunk-splitter.d.ts +98 -0
  4. package/dist/compiler/chunk-splitter.js +361 -0
  5. package/dist/index.d.ts +55 -0
  6. package/dist/index.js +17 -0
  7. package/dist/rasterizer/__tests__/error-distribution.test.d.ts +7 -0
  8. package/dist/rasterizer/__tests__/error-distribution.test.js +322 -0
  9. package/dist/rasterizer/canvas-mapper.d.ts +280 -0
  10. package/dist/rasterizer/canvas-mapper.js +414 -0
  11. package/dist/rasterizer/error-distribution.d.ts +143 -0
  12. package/dist/rasterizer/error-distribution.js +231 -0
  13. package/dist/rasterizer/gradient-mapper.d.ts +223 -0
  14. package/dist/rasterizer/gradient-mapper.js +352 -0
  15. package/dist/rasterizer/topology-rounding.d.ts +151 -0
  16. package/dist/rasterizer/topology-rounding.js +347 -0
  17. package/dist/runtime/__tests__/event-backpressure.test.d.ts +10 -0
  18. package/dist/runtime/__tests__/event-backpressure.test.js +190 -0
  19. package/dist/runtime/event-backpressure.d.ts +393 -0
  20. package/dist/runtime/event-backpressure.js +458 -0
  21. package/dist/runtime/render-loop.d.ts +277 -0
  22. package/dist/runtime/render-loop.js +435 -0
  23. package/dist/runtime/wasm-resource-manager.d.ts +122 -0
  24. package/dist/runtime/wasm-resource-manager.js +253 -0
  25. package/dist/runtime/wgpu-renderer-adapter.d.ts +168 -0
  26. package/dist/runtime/wgpu-renderer-adapter.js +230 -0
  27. package/dist/semantic/__tests__/semantic-translator.test.d.ts +4 -0
  28. package/dist/semantic/__tests__/semantic-translator.test.js +203 -0
  29. package/dist/semantic/semantic-translator.d.ts +229 -0
  30. package/dist/semantic/semantic-translator.js +398 -0
  31. package/package.json +28 -0
  32. package/playwright-report/data/0bafe4e0863f0e244bba68a838f73241f8f2efaa.md +226 -0
  33. package/playwright-report/data/9281aca8abfb06c6cecb35d5ddd13d61f8c752d8.md +226 -0
  34. package/playwright-report/index.html +90 -0
  35. package/playwright.config.ts +160 -0
  36. package/screenshot-chrome.png +0 -0
  37. package/screenshots/visual-demo-verification.png +0 -0
  38. package/screenshots/visual-demo.png +0 -0
  39. package/src/ast/types.ts +473 -0
  40. package/src/compiler/chunk-splitter.ts +534 -0
  41. package/src/index.ts +62 -0
  42. package/src/rasterizer/__tests__/error-distribution.test.ts +382 -0
  43. package/src/rasterizer/canvas-mapper.ts +677 -0
  44. package/src/rasterizer/error-distribution.ts +344 -0
  45. package/src/rasterizer/gradient-mapper.ts +563 -0
  46. package/src/rasterizer/topology-rounding.ts +499 -0
  47. package/src/runtime/__tests__/event-backpressure.test.ts +254 -0
  48. package/src/runtime/event-backpressure.ts +622 -0
  49. package/src/runtime/render-loop.ts +660 -0
  50. package/src/runtime/wasm-resource-manager.ts +349 -0
  51. package/src/runtime/wgpu-renderer-adapter.ts +318 -0
  52. package/src/semantic/__tests__/semantic-translator.test.ts +263 -0
  53. package/src/semantic/semantic-translator.ts +637 -0
  54. package/test-results/.last-run.json +4 -0
  55. package/tests/e2e/async-race.spec.ts +612 -0
  56. package/tests/e2e/bilayer-sync.spec.ts +405 -0
  57. package/tests/e2e/failures/.gitkeep +0 -0
  58. package/tests/e2e/fullstack.spec.ts +681 -0
  59. package/tests/e2e/g1-continuity.spec.ts +703 -0
  60. package/tests/e2e/golden/.gitkeep +0 -0
  61. package/tests/e2e/golden/conic-color-wheel.raw +0 -0
  62. package/tests/e2e/golden/conic-color-wheel.sha256 +1 -0
  63. package/tests/e2e/golden/conic-rotated.raw +0 -0
  64. package/tests/e2e/golden/conic-rotated.sha256 +1 -0
  65. package/tests/e2e/golden/linear-45deg.raw +0 -0
  66. package/tests/e2e/golden/linear-45deg.sha256 +1 -0
  67. package/tests/e2e/golden/linear-horizontal.raw +0 -0
  68. package/tests/e2e/golden/linear-horizontal.sha256 +1 -0
  69. package/tests/e2e/golden/linear-multi-stop.raw +0 -0
  70. package/tests/e2e/golden/linear-multi-stop.sha256 +1 -0
  71. package/tests/e2e/golden/radial-circle-center.raw +0 -0
  72. package/tests/e2e/golden/radial-circle-center.sha256 +1 -0
  73. package/tests/e2e/golden/radial-offset.raw +0 -0
  74. package/tests/e2e/golden/radial-offset.sha256 +1 -0
  75. package/tests/e2e/golden/tile-mirror.raw +0 -0
  76. package/tests/e2e/golden/tile-mirror.sha256 +1 -0
  77. package/tests/e2e/golden/tile-repeat.raw +0 -0
  78. package/tests/e2e/golden/tile-repeat.sha256 +1 -0
  79. package/tests/e2e/gradient-animation.spec.ts +606 -0
  80. package/tests/e2e/memory-stability.spec.ts +396 -0
  81. package/tests/e2e/path-topology.spec.ts +674 -0
  82. package/tests/e2e/performance-profile.spec.ts +501 -0
  83. package/tests/e2e/screenshot.spec.ts +60 -0
  84. package/tests/e2e/test-harness.html +1005 -0
  85. package/tests/e2e/text-layout.spec.ts +451 -0
  86. package/tests/e2e/visual-demo.html +340 -0
  87. package/tests/e2e/visual-regression.spec.ts +335 -0
  88. package/tsconfig.json +12 -0
  89. package/vitest.config.ts +8 -0
@@ -0,0 +1,160 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ /**
4
+ * Playwright Configuration for ViewScript Renderer E2E Tests
5
+ *
6
+ * ## Determinism Strategy
7
+ *
8
+ * 1. Single browser (Chromium) for consistency
9
+ * 2. Fixed viewport size (800x600)
10
+ * 3. Disable GPU acceleration for visual tests
11
+ * 4. Disable animations for timing predictability
12
+ */
13
+
14
+ export default defineConfig({
15
+ testDir: './tests/e2e',
16
+ fullyParallel: false, // Serial execution for determinism
17
+ forbidOnly: !!process.env.CI,
18
+ retries: process.env.CI ? 2 : 0,
19
+ workers: 1, // Single worker for determinism
20
+ reporter: [
21
+ ['html', { open: 'never' }],
22
+ ['json', { outputFile: 'test-results/results.json' }],
23
+ ],
24
+ use: {
25
+ baseURL: 'http://localhost:3000',
26
+ trace: 'on-first-retry',
27
+ video: 'on-first-retry',
28
+ },
29
+
30
+ projects: [
31
+ {
32
+ name: 'visual-regression',
33
+ testMatch: /visual-regression\.spec\.ts/,
34
+ use: {
35
+ ...devices['Desktop Chrome'],
36
+ viewport: { width: 800, height: 600 },
37
+ deviceScaleFactor: 1, // Fixed DPI
38
+ // Force software rendering
39
+ launchOptions: {
40
+ args: [
41
+ '--disable-gpu',
42
+ '--disable-gpu-compositing',
43
+ '--disable-gpu-rasterization',
44
+ '--disable-software-rasterizer',
45
+ '--use-gl=swiftshader',
46
+ '--disable-accelerated-2d-canvas',
47
+ '--disable-accelerated-video-decode',
48
+ ],
49
+ },
50
+ },
51
+ },
52
+ {
53
+ name: 'bilayer-sync',
54
+ testMatch: /bilayer-sync\.spec\.ts/,
55
+ use: {
56
+ ...devices['Desktop Chrome'],
57
+ viewport: { width: 800, height: 600 },
58
+ deviceScaleFactor: 1, // Fixed DPI for coordinate determinism
59
+ },
60
+ },
61
+ {
62
+ name: 'performance',
63
+ testMatch: /performance-profile\.spec\.ts/,
64
+ use: {
65
+ ...devices['Desktop Chrome'],
66
+ viewport: { width: 800, height: 600 },
67
+ // Enable GPU for realistic perf testing
68
+ launchOptions: {
69
+ args: [
70
+ '--enable-gpu-rasterization',
71
+ '--enable-zero-copy',
72
+ ],
73
+ },
74
+ },
75
+ },
76
+ {
77
+ name: 'memory-stability',
78
+ testMatch: /memory-stability\.spec\.ts/,
79
+ use: {
80
+ ...devices['Desktop Chrome'],
81
+ viewport: { width: 800, height: 600 },
82
+ },
83
+ timeout: 300000, // 5 minutes for stress test
84
+ },
85
+ {
86
+ name: 'async-race',
87
+ testMatch: /async-race\.spec\.ts/,
88
+ use: {
89
+ ...devices['Desktop Chrome'],
90
+ viewport: { width: 800, height: 600 },
91
+ deviceScaleFactor: 1, // Fixed DPI for coordinate determinism
92
+ },
93
+ timeout: 60000, // 1 minute for event storm processing
94
+ },
95
+ {
96
+ name: 'screenshot',
97
+ testMatch: /screenshot\.spec\.ts/,
98
+ use: {
99
+ ...devices['Desktop Chrome'],
100
+ viewport: { width: 800, height: 600 },
101
+ deviceScaleFactor: 1,
102
+ },
103
+ },
104
+ {
105
+ name: 'path-topology',
106
+ testMatch: /path-topology\.spec\.ts/,
107
+ use: {
108
+ ...devices['Desktop Chrome'],
109
+ viewport: { width: 800, height: 600 },
110
+ deviceScaleFactor: 1, // Fixed DPI for pixel-perfect topology verification
111
+ },
112
+ timeout: 30000,
113
+ },
114
+ {
115
+ name: 'g1-continuity',
116
+ testMatch: /g1-continuity\.spec\.ts/,
117
+ use: {
118
+ ...devices['Desktop Chrome'],
119
+ viewport: { width: 800, height: 600 },
120
+ deviceScaleFactor: 1, // Fixed DPI for tangent smoothness verification
121
+ },
122
+ timeout: 30000,
123
+ },
124
+ {
125
+ name: 'fullstack',
126
+ testMatch: /fullstack\.spec\.ts/,
127
+ use: {
128
+ ...devices['Desktop Chrome'],
129
+ viewport: { width: 800, height: 600 },
130
+ deviceScaleFactor: 1, // Fixed DPI for coordinate determinism
131
+ },
132
+ timeout: 60000, // 1 minute for full pipeline tests
133
+ },
134
+ {
135
+ name: 'text-layout',
136
+ testMatch: /text-layout\.spec\.ts/,
137
+ use: {
138
+ ...devices['Desktop Chrome'],
139
+ viewport: { width: 800, height: 600 },
140
+ deviceScaleFactor: 1,
141
+ },
142
+ },
143
+ {
144
+ name: 'gradient-animation',
145
+ testMatch: /gradient-animation\.spec\.ts/,
146
+ use: {
147
+ ...devices['Desktop Chrome'],
148
+ viewport: { width: 800, height: 600 },
149
+ deviceScaleFactor: 1,
150
+ },
151
+ },
152
+ ],
153
+
154
+ webServer: {
155
+ command: 'npx serve tests/e2e -l 3000 --no-clipboard',
156
+ url: 'http://localhost:3000/test-harness.html',
157
+ reuseExistingServer: !process.env.CI,
158
+ timeout: 30000,
159
+ },
160
+ });
Binary file
Binary file
@@ -0,0 +1,473 @@
1
+ /**
2
+ * ViewScript Renderer AST: Bilayer Orthogonal Architecture
3
+ *
4
+ * This module defines the type system for the dual-layer rendering model:
5
+ * - Canvas Layer: Visual representation (wgpu draw commands)
6
+ * - DOM Layer: Interaction regions (accessibility, events, focus)
7
+ *
8
+ * ## Architectural Invariants
9
+ *
10
+ * 1. Every logical entity has exactly one EntityId
11
+ * 2. An entity MAY have a CanvasNode (visual), a DOMNode (interactive), or both
12
+ * 3. Canvas and DOM nodes are synchronized via shared EntityId
13
+ * 4. DOM nodes have NO visual properties; Canvas nodes have NO interaction logic
14
+ *
15
+ * ## Data Flow
16
+ *
17
+ * ```
18
+ * IR (.vs) Renderer AST
19
+ * ────────────────────────────────────────────────────────
20
+ *
21
+ * ┌─────────────┐ ┌─────────────────────────────┐
22
+ * │ Constraint │ │ RenderableEntity │
23
+ * │ Graph │ ──▶ │ ┌─────────┐ ┌───────────┐ │
24
+ * │ (P-dim) │ │ │ Canvas │ │ DOM │ │
25
+ * └─────────────┘ │ │ Node │ │ Node │ │
26
+ * │ └─────────┘ └───────────┘ │
27
+ * │ ▲ ▲ │
28
+ * │ └─────┬─────┘ │
29
+ * │ EntityId │
30
+ * └─────────────────────────────┘
31
+ * ```
32
+ */
33
+
34
+ // =============================================================================
35
+ // Core Identity Types
36
+ // =============================================================================
37
+
38
+ /** Unique identifier for all P-dimension entities. */
39
+ export type EntityId = number;
40
+
41
+ /** Unique identifier for constraints. */
42
+ export type ConstraintId = number;
43
+
44
+ /** Unique identifier for render chunks (for progressive loading). */
45
+ export type ChunkId = string;
46
+
47
+ // =============================================================================
48
+ // Coordinate Types (Pre-Rasterization)
49
+ // =============================================================================
50
+
51
+ /**
52
+ * Rational number representation for exact arithmetic.
53
+ * Used before rasterization to pixel coordinates.
54
+ */
55
+ export interface Rational {
56
+ numerator: bigint;
57
+ denominator: bigint;
58
+ }
59
+
60
+ /**
61
+ * P-dimension vector with exact rational coordinates.
62
+ * T is included for animation/state-dependent positioning.
63
+ */
64
+ export interface PVector {
65
+ x: Rational;
66
+ y: Rational;
67
+ z: Rational; // Layering order (not depth in 3D sense)
68
+ t: Rational; // Time/state parameter
69
+ }
70
+
71
+ /**
72
+ * Rasterized coordinates for actual rendering.
73
+ * Produced by the topology-preserving rounding algorithm.
74
+ */
75
+ export interface RasterCoord {
76
+ /** X in device pixels */
77
+ x: number;
78
+ /** Y in device pixels */
79
+ y: number;
80
+ /** Z-index for layering */
81
+ zIndex: number;
82
+ }
83
+
84
+ // =============================================================================
85
+ // Canvas Layer Types (Visual Representation)
86
+ // =============================================================================
87
+
88
+ /**
89
+ * Base interface for all Canvas layer nodes.
90
+ * These produce wgpu draw commands.
91
+ */
92
+ export interface CanvasNodeBase {
93
+ /** Discriminant for node type */
94
+ readonly kind: string;
95
+
96
+ /** Back-reference to logical entity */
97
+ entityId: EntityId;
98
+
99
+ /** Pre-rasterization coordinates */
100
+ bounds: PVectorBounds;
101
+
102
+ /** Rasterized coordinates (computed by rounding algorithm) */
103
+ rasterBounds: RasterBounds;
104
+
105
+ /** Z-order for painter's algorithm */
106
+ zOrder: number;
107
+
108
+ /** Chunk this node belongs to (for progressive loading) */
109
+ chunkId: ChunkId;
110
+ }
111
+
112
+ export interface PVectorBounds {
113
+ topLeft: PVector;
114
+ bottomRight: PVector;
115
+ }
116
+
117
+ export interface RasterBounds {
118
+ x: number;
119
+ y: number;
120
+ width: number;
121
+ height: number;
122
+ }
123
+
124
+ /**
125
+ * Path-based canvas node (curves, shapes).
126
+ */
127
+ export interface CanvasPathNode extends CanvasNodeBase {
128
+ kind: 'path';
129
+
130
+ /** SVG-like path commands, pre-compiled */
131
+ pathData: PathCommand[];
132
+
133
+ /** Fill style (solid, gradient, pattern) */
134
+ fill: FillStyle | null;
135
+
136
+ /** Stroke style */
137
+ stroke: StrokeStyle | null;
138
+ }
139
+
140
+ export type PathCommand =
141
+ | { type: 'M'; x: Rational; y: Rational }
142
+ | { type: 'L'; x: Rational; y: Rational }
143
+ | { type: 'C'; x1: Rational; y1: Rational; x2: Rational; y2: Rational; x: Rational; y: Rational }
144
+ | { type: 'Q'; x1: Rational; y1: Rational; x: Rational; y: Rational }
145
+ | { type: 'A'; rx: Rational; ry: Rational; rotation: number; largeArc: boolean; sweep: boolean; x: Rational; y: Rational }
146
+ | { type: 'Z' };
147
+
148
+ export interface FillStyle {
149
+ type: 'solid' | 'linear-gradient' | 'radial-gradient' | 'pattern';
150
+ color?: string; // For solid
151
+ stops?: GradientStop[]; // For gradients
152
+ patternRef?: EntityId; // For patterns
153
+ }
154
+
155
+ export interface GradientStop {
156
+ offset: Rational; // 0-1
157
+ color: string;
158
+ }
159
+
160
+ export interface StrokeStyle {
161
+ color: string;
162
+ width: Rational;
163
+ lineCap: 'butt' | 'round' | 'square';
164
+ lineJoin: 'miter' | 'round' | 'bevel';
165
+ dashArray?: Rational[];
166
+ }
167
+
168
+ /**
169
+ * Text canvas node.
170
+ */
171
+ export interface CanvasTextNode extends CanvasNodeBase {
172
+ kind: 'text';
173
+
174
+ /** Text content (may be Q-dimension bound) */
175
+ content: string | QDimensionRef;
176
+
177
+ /** Font specification */
178
+ font: FontSpec;
179
+
180
+ /** Text layout results (computed) */
181
+ glyphs: GlyphRun[];
182
+ }
183
+
184
+ export interface FontSpec {
185
+ family: string;
186
+ size: Rational;
187
+ weight: number;
188
+ style: 'normal' | 'italic' | 'oblique';
189
+ }
190
+
191
+ export interface GlyphRun {
192
+ glyphIds: number[];
193
+ positions: RasterCoord[];
194
+ }
195
+
196
+ /**
197
+ * Image canvas node (Q-dimension source).
198
+ */
199
+ export interface CanvasImageNode extends CanvasNodeBase {
200
+ kind: 'image';
201
+
202
+ /** Reference to Q-dimension image source */
203
+ source: QDimensionRef;
204
+
205
+ /** How to fit the image in bounds */
206
+ fit: 'fill' | 'contain' | 'cover' | 'none';
207
+ }
208
+
209
+ /**
210
+ * Group node for hierarchical transforms.
211
+ */
212
+ export interface CanvasGroupNode extends CanvasNodeBase {
213
+ kind: 'group';
214
+
215
+ /** Child nodes */
216
+ children: CanvasNode[];
217
+
218
+ /** Transform matrix (2D affine) */
219
+ transform: AffineTransform;
220
+
221
+ /** Clip path (optional) */
222
+ clipPath?: PathCommand[];
223
+
224
+ /** Opacity (0-1) */
225
+ opacity: number;
226
+ }
227
+
228
+ export interface AffineTransform {
229
+ a: number; b: number;
230
+ c: number; d: number;
231
+ tx: number; ty: number;
232
+ }
233
+
234
+ export type CanvasNode =
235
+ | CanvasPathNode
236
+ | CanvasTextNode
237
+ | CanvasImageNode
238
+ | CanvasGroupNode;
239
+
240
+ // =============================================================================
241
+ // DOM Layer Types (Interaction Regions)
242
+ // =============================================================================
243
+
244
+ /**
245
+ * Base interface for all DOM layer nodes.
246
+ * These produce invisible DOM elements for interaction.
247
+ */
248
+ export interface DOMNodeBase {
249
+ /** Discriminant for node type */
250
+ readonly kind: string;
251
+
252
+ /** Back-reference to logical entity (MUST match CanvasNode if paired) */
253
+ entityId: EntityId;
254
+
255
+ /** Position synchronized with Canvas layer */
256
+ rasterBounds: RasterBounds;
257
+
258
+ /** ARIA attributes for accessibility */
259
+ aria: ARIAAttributes;
260
+
261
+ /** Tab index for keyboard navigation (-1 = not focusable) */
262
+ tabIndex: number;
263
+ }
264
+
265
+ export interface ARIAAttributes {
266
+ role?: string;
267
+ label?: string;
268
+ describedBy?: EntityId;
269
+ labelledBy?: EntityId;
270
+ hidden?: boolean;
271
+ expanded?: boolean;
272
+ selected?: boolean;
273
+ checked?: boolean | 'mixed';
274
+ disabled?: boolean;
275
+ live?: 'off' | 'polite' | 'assertive';
276
+ }
277
+
278
+ /**
279
+ * Interactive region (clickable, focusable).
280
+ */
281
+ export interface DOMInteractiveNode extends DOMNodeBase {
282
+ kind: 'interactive';
283
+
284
+ /** Event bindings (Q-dimension triggers) */
285
+ events: EventBinding[];
286
+
287
+ /** Cursor style when hovering */
288
+ cursor: string;
289
+ }
290
+
291
+ export interface EventBinding {
292
+ /** DOM event type */
293
+ type: 'click' | 'pointerdown' | 'pointerup' | 'pointermove' | 'focus' | 'blur' | 'keydown' | 'keyup';
294
+
295
+ /** Constraint to update on event (T-vector mutation) */
296
+ targetConstraint: ConstraintId;
297
+
298
+ /** How to compute the new value */
299
+ valueMapping: EventValueMapping;
300
+ }
301
+
302
+ export type EventValueMapping =
303
+ | { type: 'constant'; value: Rational }
304
+ | { type: 'toggle'; values: [Rational, Rational] }
305
+ | { type: 'increment'; delta: Rational }
306
+ | { type: 'pointer-x' }
307
+ | { type: 'pointer-y' }
308
+ | { type: 'pointer-delta-x' }
309
+ | { type: 'pointer-delta-y' };
310
+
311
+ /**
312
+ * Text input region.
313
+ */
314
+ export interface DOMInputNode extends DOMNodeBase {
315
+ kind: 'input';
316
+
317
+ /** Input type */
318
+ inputType: 'text' | 'number' | 'password' | 'email' | 'search';
319
+
320
+ /** Constraint bound to input value */
321
+ valueConstraint: ConstraintId;
322
+
323
+ /** Placeholder text */
324
+ placeholder?: string;
325
+
326
+ /** Max length */
327
+ maxLength?: number;
328
+ }
329
+
330
+ /**
331
+ * Scroll container region.
332
+ */
333
+ export interface DOMScrollNode extends DOMNodeBase {
334
+ kind: 'scroll';
335
+
336
+ /** Scroll direction */
337
+ direction: 'horizontal' | 'vertical' | 'both';
338
+
339
+ /** Constraints bound to scroll position */
340
+ scrollXConstraint?: ConstraintId;
341
+ scrollYConstraint?: ConstraintId;
342
+
343
+ /** Content size (for scrollbar calculation) */
344
+ contentSize: { width: Rational; height: Rational };
345
+ }
346
+
347
+ /**
348
+ * Focus trap region (for modals, dialogs).
349
+ */
350
+ export interface DOMFocusTrapNode extends DOMNodeBase {
351
+ kind: 'focus-trap';
352
+
353
+ /** First and last focusable children */
354
+ focusableChildren: EntityId[];
355
+ }
356
+
357
+ export type DOMNode =
358
+ | DOMInteractiveNode
359
+ | DOMInputNode
360
+ | DOMScrollNode
361
+ | DOMFocusTrapNode;
362
+
363
+ // =============================================================================
364
+ // Q-Dimension References
365
+ // =============================================================================
366
+
367
+ /**
368
+ * Reference to Q-dimension (unpredictable) data.
369
+ */
370
+ export interface QDimensionRef {
371
+ /** Type of Q-dimension source */
372
+ type: 'user-input' | 'fetch' | 'image' | 'video' | 'audio' | 'shader' | 'time';
373
+
374
+ /** Source identifier */
375
+ sourceId: string;
376
+
377
+ /** Current value (runtime state, not in IR) */
378
+ currentValue?: unknown;
379
+ }
380
+
381
+ // =============================================================================
382
+ // Unified Renderable Entity
383
+ // =============================================================================
384
+
385
+ /**
386
+ * A complete renderable entity combining Canvas and DOM representations.
387
+ *
388
+ * ## Pairing Rules
389
+ *
390
+ * | Entity Type | Canvas Node | DOM Node |
391
+ * |-----------------|-------------|-------------|
392
+ * | Static shape | Required | None |
393
+ * | Button | Required | Required |
394
+ * | Text input | Required | Required |
395
+ * | Decorative img | Required | None |
396
+ * | Interactive img | Required | Required |
397
+ * | Scroll area | Optional | Required |
398
+ * | Focus trap | None | Required |
399
+ */
400
+ export interface RenderableEntity {
401
+ /** Unique identifier */
402
+ id: EntityId;
403
+
404
+ /** Human-readable name (for debugging) */
405
+ name?: string;
406
+
407
+ /** Canvas layer representation (visual) */
408
+ canvas: CanvasNode | null;
409
+
410
+ /** DOM layer representation (interactive) */
411
+ dom: DOMNode | null;
412
+
413
+ /** Constraints that affect this entity */
414
+ dependentConstraints: ConstraintId[];
415
+
416
+ /** Entities that this entity references */
417
+ referencedEntities: EntityId[];
418
+
419
+ /** Chunk membership */
420
+ chunkId: ChunkId;
421
+ }
422
+
423
+ // =============================================================================
424
+ // Render Tree (Complete AST)
425
+ // =============================================================================
426
+
427
+ /**
428
+ * The complete render tree produced by the compiler.
429
+ */
430
+ export interface RenderTree {
431
+ /** All renderable entities, indexed by EntityId */
432
+ entities: Map<EntityId, RenderableEntity>;
433
+
434
+ /** Root entity IDs (top-level elements) */
435
+ roots: EntityId[];
436
+
437
+ /** Chunk definitions for progressive loading */
438
+ chunks: Map<ChunkId, Chunk>;
439
+
440
+ /** Viewport configuration */
441
+ viewport: ViewportConfig;
442
+
443
+ /** Device pixel ratio for rasterization */
444
+ devicePixelRatio: number;
445
+ }
446
+
447
+ export interface Chunk {
448
+ id: ChunkId;
449
+
450
+ /** Entities in this chunk */
451
+ entityIds: EntityId[];
452
+
453
+ /** Dependencies on other chunks */
454
+ dependsOn: ChunkId[];
455
+
456
+ /** Is this the initial chunk? */
457
+ isInitial: boolean;
458
+
459
+ /** Trigger conditions for lazy loading */
460
+ loadTriggers: LoadTrigger[];
461
+ }
462
+
463
+ export type LoadTrigger =
464
+ | { type: 'immediate' }
465
+ | { type: 'viewport-intersect'; entityId: EntityId }
466
+ | { type: 'event'; eventType: string; targetEntity: EntityId }
467
+ | { type: 'constraint-change'; constraintId: ConstraintId };
468
+
469
+ export interface ViewportConfig {
470
+ width: Rational;
471
+ height: Rational;
472
+ unitsPerPixel: Rational;
473
+ }