@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,263 @@
1
+ /**
2
+ * Tests for SemanticTranslator (Phase 7: Task 20)
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ SemanticTranslator,
8
+ createEntityRegistry,
9
+ createEmptyRegistry,
10
+ type EntityMetadata,
11
+ type RawSolution,
12
+ } from '../semantic-translator';
13
+
14
+ describe('SemanticTranslator', () => {
15
+ describe('parseVarId', () => {
16
+ it('should parse valid VarId keys', () => {
17
+ const translator = new SemanticTranslator(createEmptyRegistry());
18
+
19
+ expect(translator.parseVarId('1:x')).toEqual({ entityId: 1, component: 'x' });
20
+ expect(translator.parseVarId('42:y')).toEqual({ entityId: 42, component: 'y' });
21
+ expect(translator.parseVarId('100:value')).toEqual({ entityId: 100, component: 'value' });
22
+ expect(translator.parseVarId('5:r')).toEqual({ entityId: 5, component: 'r' });
23
+ expect(translator.parseVarId('5:alpha')).toEqual({ entityId: 5, component: 'alpha' });
24
+ });
25
+
26
+ it('should return null for invalid keys', () => {
27
+ const translator = new SemanticTranslator(createEmptyRegistry());
28
+
29
+ expect(translator.parseVarId('')).toBeNull();
30
+ expect(translator.parseVarId('invalid')).toBeNull();
31
+ expect(translator.parseVarId(':x')).toBeNull();
32
+ expect(translator.parseVarId('1:')).toBeNull();
33
+ expect(translator.parseVarId('1:unknown_component')).toBeNull();
34
+ expect(translator.parseVarId('abc:x')).toBeNull();
35
+ });
36
+ });
37
+
38
+ describe('parseRationalToFloat', () => {
39
+ it('should parse rational strings', () => {
40
+ const translator = new SemanticTranslator(createEmptyRegistry());
41
+
42
+ expect(translator.parseRationalToFloat('100/1')).toBe(100);
43
+ expect(translator.parseRationalToFloat('1/2')).toBe(0.5);
44
+ expect(translator.parseRationalToFloat('100')).toBe(100);
45
+ expect(translator.parseRationalToFloat('-50/1')).toBe(-50);
46
+ expect(translator.parseRationalToFloat('1/3')).toBeCloseTo(0.333, 2);
47
+ });
48
+
49
+ it('should handle zero denominator', () => {
50
+ const translator = new SemanticTranslator(createEmptyRegistry());
51
+ expect(translator.parseRationalToFloat('1/0')).toBeNaN();
52
+ });
53
+ });
54
+
55
+ describe('translateSolution', () => {
56
+ it('should translate a simple point solution', () => {
57
+ const registry = createEntityRegistry([
58
+ { entityId: 1, type: 'point', name: 'P1' },
59
+ ]);
60
+ const translator = new SemanticTranslator(registry);
61
+
62
+ const rawSolution: RawSolution = new Map([
63
+ ['1:x', '100/1'],
64
+ ['1:y', '200/1'],
65
+ ]);
66
+
67
+ const result = translator.translateSolution(rawSolution, 0);
68
+
69
+ expect(result.solutionIndex).toBe(0);
70
+ expect(result.entities).toHaveLength(1);
71
+ expect(result.entities[0].entityId).toBe(1);
72
+ expect(result.entities[0].type).toBe('point');
73
+ expect(result.entities[0].coordinates?.x).toBe(100);
74
+ expect(result.entities[0].coordinates?.y).toBe(200);
75
+ expect(result.entities[0].description).toContain('P1');
76
+ expect(result.entities[0].description).toContain('100');
77
+ expect(result.entities[0].description).toContain('200');
78
+ });
79
+
80
+ it('should translate multiple entities', () => {
81
+ const registry = createEntityRegistry([
82
+ { entityId: 1, type: 'point', name: 'Start' },
83
+ { entityId: 2, type: 'point', name: 'End' },
84
+ { entityId: 3, type: 'control_point', name: 'CP1' },
85
+ ]);
86
+ const translator = new SemanticTranslator(registry);
87
+
88
+ const rawSolution: RawSolution = new Map([
89
+ ['1:x', '0/1'],
90
+ ['1:y', '0/1'],
91
+ ['2:x', '100/1'],
92
+ ['2:y', '0/1'],
93
+ ['3:x', '50/1'],
94
+ ['3:y', '50/1'],
95
+ ]);
96
+
97
+ const result = translator.translateSolution(rawSolution, 0);
98
+
99
+ expect(result.entities).toHaveLength(3);
100
+ expect(result.summary).toContain('3 entities');
101
+ });
102
+
103
+ it('should translate color stop entities', () => {
104
+ const registry = createEntityRegistry([
105
+ { entityId: 10, type: 'color_stop', name: 'Stop1' },
106
+ ]);
107
+ const translator = new SemanticTranslator(registry);
108
+
109
+ const rawSolution: RawSolution = new Map([
110
+ ['10:r', '255/1'],
111
+ ['10:g', '128/1'],
112
+ ['10:b', '0/1'],
113
+ ['10:alpha', '1/1'],
114
+ ['10:position', '1/2'],
115
+ ]);
116
+
117
+ const result = translator.translateSolution(rawSolution, 0);
118
+
119
+ expect(result.entities).toHaveLength(1);
120
+ expect(result.entities[0].type).toBe('color_stop');
121
+ expect(result.entities[0].color?.r).toBe(255);
122
+ expect(result.entities[0].color?.g).toBe(128);
123
+ expect(result.entities[0].color?.b).toBe(0);
124
+ expect(result.entities[0].color?.position).toBe(0.5);
125
+ expect(result.entities[0].description).toContain('50%');
126
+ expect(result.entities[0].description).toContain('rgb(255, 128, 0)');
127
+ });
128
+
129
+ it('should handle unknown entity types', () => {
130
+ const translator = new SemanticTranslator(createEmptyRegistry());
131
+
132
+ const rawSolution: RawSolution = new Map([
133
+ ['99:x', '50/1'],
134
+ ['99:y', '75/1'],
135
+ ]);
136
+
137
+ const result = translator.translateSolution(rawSolution, 0);
138
+
139
+ expect(result.entities).toHaveLength(1);
140
+ expect(result.entities[0].type).toBe('unknown');
141
+ expect(result.entities[0].coordinates?.x).toBe(50);
142
+ });
143
+
144
+ it('should detect horizontal alignment relationships', () => {
145
+ const registry = createEntityRegistry([
146
+ { entityId: 1, type: 'point' },
147
+ { entityId: 2, type: 'point' },
148
+ { entityId: 3, type: 'point' },
149
+ ]);
150
+ const translator = new SemanticTranslator(registry);
151
+
152
+ // Three points on the same horizontal line (y=100)
153
+ const rawSolution: RawSolution = new Map([
154
+ ['1:x', '0/1'],
155
+ ['1:y', '100/1'],
156
+ ['2:x', '50/1'],
157
+ ['2:y', '100/1'],
158
+ ['3:x', '100/1'],
159
+ ['3:y', '100/1'],
160
+ ]);
161
+
162
+ const result = translator.translateSolution(rawSolution, 0);
163
+
164
+ expect(result.relationships.length).toBeGreaterThan(0);
165
+ const alignment = result.relationships.find(r => r.type === 'alignment');
166
+ expect(alignment).toBeDefined();
167
+ expect(alignment?.description).toContain('horizontally aligned');
168
+ });
169
+ });
170
+
171
+ describe('compareSolutions', () => {
172
+ it('should detect identical solutions', () => {
173
+ const registry = createEntityRegistry([
174
+ { entityId: 1, type: 'point', name: 'P1' },
175
+ ]);
176
+ const translator = new SemanticTranslator(registry);
177
+
178
+ const rawSolution: RawSolution = new Map([
179
+ ['1:x', '100/1'],
180
+ ['1:y', '200/1'],
181
+ ]);
182
+
183
+ const solution1 = translator.translateSolution(rawSolution, 0);
184
+ const solution2 = translator.translateSolution(rawSolution, 1);
185
+
186
+ const diff = translator.compareSolutions(solution1, solution2);
187
+
188
+ expect(diff.differingEntities).toHaveLength(0);
189
+ expect(diff.summary).toContain('identical');
190
+ });
191
+
192
+ it('should detect differing solutions', () => {
193
+ const registry = createEntityRegistry([
194
+ { entityId: 1, type: 'point', name: 'P1' },
195
+ ]);
196
+ const translator = new SemanticTranslator(registry);
197
+
198
+ const solution1 = translator.translateSolution(
199
+ new Map([['1:x', '100/1'], ['1:y', '200/1']]),
200
+ 0,
201
+ );
202
+ const solution2 = translator.translateSolution(
203
+ new Map([['1:x', '150/1'], ['1:y', '250/1']]),
204
+ 1,
205
+ );
206
+
207
+ const diff = translator.compareSolutions(solution1, solution2);
208
+
209
+ expect(diff.differingEntities).toHaveLength(1);
210
+ expect(diff.differingEntities[0].entityId).toBe(1);
211
+ expect(diff.summary).toContain('1 entities differ');
212
+ });
213
+ });
214
+
215
+ describe('translateMultipleSolutions', () => {
216
+ it('should translate and compare multiple solutions', () => {
217
+ const registry = createEntityRegistry([
218
+ { entityId: 1, type: 'point', name: 'P1' },
219
+ ]);
220
+ const translator = new SemanticTranslator(registry);
221
+
222
+ const rawSolutions: RawSolution[] = [
223
+ new Map([['1:x', '100/1'], ['1:y', '0/1']]),
224
+ new Map([['1:x', '-100/1'], ['1:y', '0/1']]),
225
+ ];
226
+
227
+ const result = translator.translateMultipleSolutions(rawSolutions);
228
+
229
+ expect(result.solutions).toHaveLength(2);
230
+ expect(result.comparison).toContain('2 solutions found');
231
+ expect(result.comparison).toContain('differ');
232
+ });
233
+
234
+ it('should handle single solution', () => {
235
+ const translator = new SemanticTranslator(createEmptyRegistry());
236
+
237
+ const rawSolutions: RawSolution[] = [
238
+ new Map([['1:x', '100/1']]),
239
+ ];
240
+
241
+ const result = translator.translateMultipleSolutions(rawSolutions);
242
+
243
+ expect(result.solutions).toHaveLength(1);
244
+ expect(result.comparison).toContain('Only one solution');
245
+ });
246
+ });
247
+ });
248
+
249
+ describe('createEntityRegistry', () => {
250
+ it('should create a working registry', () => {
251
+ const entities: EntityMetadata[] = [
252
+ { entityId: 1, type: 'point', name: 'P1' },
253
+ { entityId: 2, type: 'rect', name: 'R1' },
254
+ ];
255
+
256
+ const registry = createEntityRegistry(entities);
257
+
258
+ expect(registry.get(1)?.type).toBe('point');
259
+ expect(registry.get(2)?.name).toBe('R1');
260
+ expect(registry.get(999)).toBeUndefined();
261
+ expect(registry.getAll()).toHaveLength(2);
262
+ });
263
+ });