diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-17 07:52:48 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-17 07:52:48 +0100 |
| commit | cd53ff0be8971b592d8d01836a6572c4123e5495 (patch) | |
| tree | aa0f9e48a906e352b6c3ab198580ff48978cc2da /tools/timeline_editor/test_format.html | |
| parent | cdd14146df16de0493acfd6dfbf24c154edbfce3 (diff) | |
feat: add Sequence V2 format support to timeline editor
Implements full support for the sequence v2 DAG format with explicit
node routing and arrow syntax.
New features:
- timeline-format.js module for parsing/serializing v2 format
- NODE declarations with typed buffers (u8x4_norm, f32x4, etc.)
- Arrow syntax for effect routing: input1 input2 -> output1 output2
- Buffer chain visualization in properties panel and tooltips
- Node editor modal for adding/deleting node declarations
- Validation for undeclared node references (when NODEs explicit)
- Backward compatible with auto-inferred nodes
Files added:
- tools/timeline_editor/timeline-format.js (214 lines)
- tools/timeline_editor/test_format.html (automated tests)
- workspaces/test/timeline_v2_test.seq (test file with NODE declarations)
Files modified:
- tools/timeline_editor/index.html (~40 changes for v2 support)
All success criteria met. Round-trip tested with existing timelines.
handoff(Claude): Timeline editor now fully supports v2 format with
explicit node routing, NODE declarations, and buffer chain visualization.
Parser handles both explicit NODE declarations and auto-inferred nodes.
Validation only runs when explicit NODEs exist. Ready for production use.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/timeline_editor/test_format.html')
| -rw-r--r-- | tools/timeline_editor/test_format.html | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/tools/timeline_editor/test_format.html b/tools/timeline_editor/test_format.html new file mode 100644 index 0000000..12b788f --- /dev/null +++ b/tools/timeline_editor/test_format.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Timeline Format Test</title> + <style> + body { font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; } + .test { margin: 20px 0; padding: 10px; border: 1px solid #444; } + .pass { border-color: #4ec9b0; } + .fail { border-color: #f48771; } + pre { background: #2d2d30; padding: 10px; overflow-x: auto; } + </style> +</head> +<body> + <h1>Timeline Format V2 Test</h1> + <div id="results"></div> + + <script type="module"> + import { TimelineFormat } from './timeline-format.js'; + + const timelineFormat = new TimelineFormat(); + const results = document.getElementById('results'); + + function addTest(name, passed, details) { + const div = document.createElement('div'); + div.className = `test ${passed ? 'pass' : 'fail'}`; + div.innerHTML = `<h3>${passed ? '✓' : '✗'} ${name}</h3>${details}`; + results.appendChild(div); + } + + // Test 1: Parse existing timeline (no NODE declarations) + const testTimeline1 = `# WORKSPACE: test +# BPM 120 + +SEQUENCE 0.0 0 "MainLoop" +EFFECT + Hybrid3D source -> temp1 0.00 4.00 +EFFECT + GaussianBlur temp1 -> sink 0.00 4.00`; + + const parsed1 = timelineFormat.parse(testTimeline1, 120); + const test1Pass = parsed1.sequences.length === 1 && + parsed1.sequences[0].effects.length === 2 && + parsed1.sequences[0].effects[0].inputs[0] === 'source' && + parsed1.sequences[0].effects[0].outputs[0] === 'temp1'; + addTest('Parse timeline without NODE declarations', test1Pass, + `<pre>Sequences: ${parsed1.sequences.length} +Effects: ${parsed1.sequences[0].effects.length} +First effect inputs: ${parsed1.sequences[0].effects[0].inputs.join(', ')} +First effect outputs: ${parsed1.sequences[0].effects[0].outputs.join(', ')}</pre>`); + + // Test 2: Parse timeline with NODE declarations + const testTimeline2 = `# BPM 120 +SEQUENCE 0.0 0 "v2_test" + NODE temp1 u8x4_norm + NODE temp2 f32x4 + EFFECT + Hybrid3D source -> temp1 0.0 4.0 + EFFECT + GaussianBlur temp1 -> temp2 0.0 4.0`; + + const parsed2 = timelineFormat.parse(testTimeline2, 120); + const test2Pass = parsed2.sequences[0].nodes.length === 2 && + parsed2.sequences[0].nodes[0].name === 'temp1' && + parsed2.sequences[0].nodes[0].type === 'u8x4_norm'; + addTest('Parse timeline with NODE declarations', test2Pass, + `<pre>Nodes: ${parsed2.sequences[0].nodes.length} +Node 1: ${parsed2.sequences[0].nodes[0].name} (${parsed2.sequences[0].nodes[0].type}) +Node 2: ${parsed2.sequences[0].nodes[1].name} (${parsed2.sequences[0].nodes[1].type})</pre>`); + + // Test 3: Serialize without nodes + const serialized1 = timelineFormat.serialize(parsed1.sequences, 120); + const test3Pass = serialized1.includes('EFFECT + Hybrid3D source -> temp1') && + !serialized1.includes('NODE'); + addTest('Serialize timeline without NODE declarations', test3Pass, + `<pre>${serialized1}</pre>`); + + // Test 4: Serialize with nodes + const serialized2 = timelineFormat.serialize(parsed2.sequences, 120); + const test4Pass = serialized2.includes('NODE temp1 u8x4_norm') && + serialized2.includes('NODE temp2 f32x4'); + addTest('Serialize timeline with NODE declarations', test4Pass, + `<pre>${serialized2}</pre>`); + + // Test 5: Validation (no nodes - should pass) + const errors1 = timelineFormat.validateSequence(parsed1.sequences[0]); + const test5Pass = errors1.length === 0; + addTest('Validation without NODE declarations', test5Pass, + `<pre>Errors: ${errors1.length}</pre>`); + + // Test 6: Validation (with nodes - should pass) + const errors2 = timelineFormat.validateSequence(parsed2.sequences[0]); + const test6Pass = errors2.length === 0; + addTest('Validation with NODE declarations', test6Pass, + `<pre>Errors: ${errors2.length}</pre>`); + + // Test 7: Validation failure (undeclared node) + const invalidSeq = { + nodes: [{ name: 'temp1', type: 'u8x4_norm' }], + effects: [{ + className: 'Test', + inputs: ['source'], + outputs: ['temp2'], // temp2 not declared! + startTime: 0, + endTime: 4 + }] + }; + const errors3 = timelineFormat.validateSequence(invalidSeq); + const test7Pass = errors3.length > 0 && errors3[0].includes('temp2'); + addTest('Validation detects undeclared nodes', test7Pass, + `<pre>Errors: ${errors3.join('\n')}</pre>`); + + // Test 8: Round-trip (parse → serialize → parse) + const reparsed = timelineFormat.parse(serialized2, 120); + const test8Pass = JSON.stringify(parsed2.sequences[0].nodes) === + JSON.stringify(reparsed.sequences[0].nodes); + addTest('Round-trip consistency', test8Pass, + `<pre>Original nodes: ${JSON.stringify(parsed2.sequences[0].nodes, null, 2)} +Reparsed nodes: ${JSON.stringify(reparsed.sequences[0].nodes, null, 2)}</pre>`); + + // Summary + const allTests = document.querySelectorAll('.test'); + const passed = document.querySelectorAll('.test.pass').length; + const total = allTests.length; + const summary = document.createElement('div'); + summary.style.cssText = 'margin-top: 30px; padding: 20px; background: #2d2d30; font-size: 18px;'; + summary.innerHTML = `<strong>Results: ${passed}/${total} tests passed</strong>`; + results.appendChild(summary); + </script> +</body> +</html> |
