summaryrefslogtreecommitdiff
path: root/tools/timeline_editor
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-05 21:13:26 +0100
committerskal <pascal.massimino@gmail.com>2026-02-05 21:13:26 +0100
commita794102f59e8baf654b8807cf74fdc014830fb6f (patch)
tree56ab1b660589460df621d0120c112c918e806e21 /tools/timeline_editor
parenta38359aeb4ab74814ee523fff02dcc8a3eee7efd (diff)
fix(timeline-editor): Fix parser to handle actual demo.seq format
Fixed critical parsing bugs that prevented loading real demo.seq files. Issues fixed: 1. Beat notation support: Parse '0b', '4b' as beats and convert to seconds - Added BPM parsing from '# BPM 120' comments - Helper function: parseTime() converts beats to seconds using BPM 2. Priority modifiers: Handle '+', '=', '-' modifiers correctly - '+' increments priority - '=' keeps same priority - '-' decrements priority (for background effects) - Store both absolute priority and modifier for round-trip 3. Inline comments: Strip comments from end of lines with '#' - Effect lines like 'EFFECT + FlashEffect 0.0 1.0 # comment' now parse 4. Sequence names: Parse optional sequence names in quotes - Format: SEQUENCE <time> <priority> "name" [end] 5. Updated serializer: Output correct format with modifiers - Generate 'EFFECT + ClassName ...' instead of 'EFFECT ClassName ... priority' - Preserve BPM comment in output - Preserve sequence names Parser now correctly handles: - SEQUENCE 0b 0 → Start at beat 0, priority 0 - SEQUENCE 4b 0 → Start at beat 4 (2 seconds @ 120 BPM) - EFFECT - FlashCubeEffect .2 3 → Background effect (priority -1) - EFFECT + FlashEffect 0.0 1. → Normal effect (priority 0) - EFFECT = GaussianBlur 0 8 → Same priority as previous Testing: Load assets/demo.seq should now work correctly.
Diffstat (limited to 'tools/timeline_editor')
-rw-r--r--tools/timeline_editor/index.html83
1 files changed, 68 insertions, 15 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html
index 0d10e4a..293a03f 100644
--- a/tools/timeline_editor/index.html
+++ b/tools/timeline_editor/index.html
@@ -285,35 +285,85 @@
const sequences = [];
const lines = content.split('\n');
let currentSequence = null;
+ let bpm = 120; // Default BPM
+ let currentPriority = 0; // Track priority for + = - modifiers
+
+ // Helper: Convert time notation to seconds
+ function parseTime(timeStr) {
+ if (timeStr.endsWith('b')) {
+ // Beat notation: "4b" = 4 beats
+ const beats = parseFloat(timeStr.slice(0, -1));
+ return beats * (60.0 / bpm);
+ }
+ return parseFloat(timeStr);
+ }
+
+ // Helper: Strip inline comments
+ function stripComment(line) {
+ const commentIdx = line.indexOf('#');
+ if (commentIdx >= 0) {
+ return line.slice(0, commentIdx).trim();
+ }
+ return line;
+ }
for (let line of lines) {
line = line.trim();
- // Skip empty lines and comments
- if (!line || line.startsWith('#')) continue;
+ // Skip empty lines
+ if (!line) continue;
- // Parse SEQUENCE line
- const seqMatch = line.match(/^SEQUENCE\s+([\d.]+)\s+(\d+)$/);
+ // Parse BPM comment
+ if (line.startsWith('# BPM ')) {
+ const bpmMatch = line.match(/# BPM (\d+)/);
+ if (bpmMatch) {
+ bpm = parseInt(bpmMatch[1]);
+ }
+ continue;
+ }
+
+ // Skip other comments
+ if (line.startsWith('#')) continue;
+
+ // Strip inline comments
+ line = stripComment(line);
+ if (!line) continue;
+
+ // Parse SEQUENCE line: SEQUENCE <time> <priority> [name] [end]
+ const seqMatch = line.match(/^SEQUENCE\s+(\S+)\s+(\d+)(?:\s+"([^"]+)")?(?:\s+(\S+))?$/);
if (seqMatch) {
currentSequence = {
type: 'sequence',
- startTime: parseFloat(seqMatch[1]),
+ startTime: parseTime(seqMatch[1]),
priority: parseInt(seqMatch[2]),
- effects: []
+ effects: [],
+ name: seqMatch[3] || ''
};
sequences.push(currentSequence);
+ currentPriority = -1; // Reset effect priority for new sequence
continue;
}
- // Parse EFFECT line
- const effectMatch = line.match(/^EFFECT\s+(\w+)\s+([\d.]+)\s+([\d.]+)\s+(-?\d+)(?:\s+(.*))?$/);
+ // Parse EFFECT line: EFFECT <modifier> <ClassName> <start> <end> [args]
+ const effectMatch = line.match(/^EFFECT\s+([+=-])\s+(\w+)\s+(\S+)\s+(\S+)(?:\s+(.*))?$/);
if (effectMatch && currentSequence) {
+ const modifier = effectMatch[1];
+
+ // Update priority based on modifier
+ if (modifier === '+') {
+ currentPriority++;
+ } else if (modifier === '-') {
+ currentPriority--;
+ }
+ // '=' keeps current priority
+
const effect = {
type: 'effect',
- className: effectMatch[1],
- startTime: parseFloat(effectMatch[2]),
- endTime: parseFloat(effectMatch[3]),
- priority: parseInt(effectMatch[4]),
+ className: effectMatch[2],
+ startTime: parseTime(effectMatch[3]),
+ endTime: parseTime(effectMatch[4]),
+ priority: currentPriority,
+ priorityModifier: modifier,
args: effectMatch[5] || ''
};
currentSequence.effects.push(effect);
@@ -326,13 +376,16 @@
// Serializer: JavaScript objects → demo.seq
function serializeSeqFile(sequences) {
let output = '# Demo Timeline\n';
- output += '# Generated by Timeline Editor\n\n';
+ output += '# Generated by Timeline Editor\n';
+ output += '# BPM 120\n\n';
for (const seq of sequences) {
- output += `SEQUENCE ${seq.startTime.toFixed(2)} ${seq.priority}\n`;
+ const seqLine = `SEQUENCE ${seq.startTime.toFixed(2)} ${seq.priority}`;
+ output += seq.name ? `${seqLine} "${seq.name}"\n` : `${seqLine}\n`;
for (const effect of seq.effects) {
- output += ` EFFECT ${effect.className} ${effect.startTime.toFixed(2)} ${effect.endTime.toFixed(2)} ${effect.priority}`;
+ const modifier = effect.priorityModifier || '+';
+ output += ` EFFECT ${modifier} ${effect.className} ${effect.startTime.toFixed(2)} ${effect.endTime.toFixed(2)}`;
if (effect.args) {
output += ` ${effect.args}`;
}