summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-05 22:17:05 +0100
committerskal <pascal.massimino@gmail.com>2026-02-05 22:17:05 +0100
commitdfb0529a0e017d0bedd79d949e827e51c622056b (patch)
treed32d92bb7fabdf4fa32f98aa6b75827118bce652 /tools
parent4cbea130aca50bd1a4f114ed6a5170cd0dd00d17 (diff)
feat(timeline-editor): Final UI polish - auto-apply, stack-order priority
Implemented final UI improvements: - Removed sequence info text (Start:, Priority) - Removed 'Properties updated' message - Auto-apply properties with oninput handlers - Stack-order-based priority with Up/Down buttons - Added 'Same as Above' toggle for = priority modifier New functions: autoApplyProperties(), moveEffectUp(), moveEffectDown(), toggleSamePriority() Task #57 Phase 1 complete: Interactive editor with drag-drop, resize handles, snap-to-beat, and stack-based priority. handoff(Claude): Timeline editor Phase 1 feature-complete. All dragging bugs fixed, dynamic bounds working, stack-order priority operational. Ready for Phase 2 planning.
Diffstat (limited to 'tools')
-rw-r--r--tools/timeline_editor/index.html98
1 files changed, 61 insertions, 37 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html
index c6c54bd..5ab8ed0 100644
--- a/tools/timeline_editor/index.html
+++ b/tools/timeline_editor/index.html
@@ -663,28 +663,12 @@
seq._yPosition = cumulativeY;
cumulativeY += seqHeight + sequenceGap;
- // Format time display based on mode (show visual start, not seq.startTime)
- let seqTimeDisplay;
- if (showBeats) {
- const beatDuration = 60.0 / bpm;
- const startBeat = (seqVisualStart / beatDuration).toFixed(1);
- seqTimeDisplay = `Start: ${startBeat}b`;
- } else {
- seqTimeDisplay = `Start: ${seqVisualStart.toFixed(2)}s`;
- }
-
// Create sequence name overlay (large, centered, fades on hover)
const seqNameDiv = document.createElement('div');
seqNameDiv.className = 'sequence-name';
seqNameDiv.textContent = seq.name || `Sequence ${seqIndex + 1}`;
- // Create small info text in corner
- const seqInfoDiv = document.createElement('div');
- seqInfoDiv.className = 'sequence-info';
- seqInfoDiv.innerHTML = `${seqTimeDisplay} | Priority: ${seq.priority}`;
-
seqDiv.appendChild(seqNameDiv);
- seqDiv.appendChild(seqInfoDiv);
if (selectedItem && selectedItem.type === 'sequence' && selectedItem.index === seqIndex) {
seqDiv.classList.add('selected');
@@ -912,70 +896,110 @@
propertiesContent.innerHTML = `
<div class="property-group">
<label>Name</label>
- <input type="text" id="propName" value="${seq.name || ''}" placeholder="Sequence name">
+ <input type="text" id="propName" value="${seq.name || ''}" placeholder="Sequence name" oninput="autoApplyProperties()">
</div>
<div class="property-group">
<label>Start Time (seconds)</label>
- <input type="number" id="propStartTime" value="${seq.startTime}" step="0.1" min="0">
- </div>
- <div class="property-group">
- <label>Priority</label>
- <input type="number" id="propPriority" value="${seq.priority}" step="1">
+ <input type="number" id="propStartTime" value="${seq.startTime}" step="0.1" min="0" oninput="autoApplyProperties()">
</div>
- <button onclick="applyProperties()">Apply</button>
`;
} else if (selectedItem.type === 'effect') {
const effect = sequences[selectedItem.seqIndex].effects[selectedItem.effectIndex];
+ const effects = sequences[selectedItem.seqIndex].effects;
+ const canMoveUp = selectedItem.effectIndex < effects.length - 1;
+ const canMoveDown = selectedItem.effectIndex > 0;
+ const samePriority = effect.priorityModifier === '=';
+
propertiesContent.innerHTML = `
<div class="property-group">
<label>Effect Class</label>
- <input type="text" id="propClassName" value="${effect.className}">
+ <input type="text" id="propClassName" value="${effect.className}" oninput="autoApplyProperties()">
</div>
<div class="property-group">
<label>Start Time (relative to sequence)</label>
- <input type="number" id="propStartTime" value="${effect.startTime}" step="0.1" min="0">
+ <input type="number" id="propStartTime" value="${effect.startTime}" step="0.1" oninput="autoApplyProperties()">
</div>
<div class="property-group">
<label>End Time (relative to sequence)</label>
- <input type="number" id="propEndTime" value="${effect.endTime}" step="0.1" min="0">
+ <input type="number" id="propEndTime" value="${effect.endTime}" step="0.1" oninput="autoApplyProperties()">
</div>
<div class="property-group">
- <label>Priority</label>
- <input type="number" id="propPriority" value="${effect.priority}" step="1">
+ <label>Constructor Arguments</label>
+ <input type="text" id="propArgs" value="${effect.args || ''}" oninput="autoApplyProperties()">
</div>
<div class="property-group">
- <label>Constructor Arguments</label>
- <input type="text" id="propArgs" value="${effect.args}">
+ <label>Stack Position (determines priority)</label>
+ <div style="display: flex; gap: 5px; margin-bottom: 10px;">
+ <button onclick="moveEffectUp()" ${!canMoveUp ? 'disabled' : ''} style="flex: 1;">↑ Up</button>
+ <button onclick="moveEffectDown()" ${!canMoveDown ? 'disabled' : ''} style="flex: 1;">↓ Down</button>
+ </div>
+ <button onclick="toggleSamePriority()" style="width: 100%;">
+ ${samePriority ? '✓ Same as Above (=)' : 'Increment (+)'}
+ </button>
</div>
- <button onclick="applyProperties()">Apply</button>
`;
}
}
- function applyProperties() {
+ // Auto-apply properties on input change (no Apply button needed)
+ function autoApplyProperties() {
if (!selectedItem) return;
if (selectedItem.type === 'sequence') {
const seq = sequences[selectedItem.index];
seq.name = document.getElementById('propName').value;
seq.startTime = parseFloat(document.getElementById('propStartTime').value);
- seq.priority = parseInt(document.getElementById('propPriority').value);
} else if (selectedItem.type === 'effect') {
const effect = sequences[selectedItem.seqIndex].effects[selectedItem.effectIndex];
effect.className = document.getElementById('propClassName').value;
effect.startTime = parseFloat(document.getElementById('propStartTime').value);
effect.endTime = parseFloat(document.getElementById('propEndTime').value);
- effect.priority = parseInt(document.getElementById('propPriority').value);
effect.args = document.getElementById('propArgs').value;
}
// Re-render timeline (recalculates sequence bounds)
renderTimeline();
+ }
- // Update properties panel to reflect any calculated changes
- updateProperties();
+ // Move effect up in stack (higher priority)
+ function moveEffectUp() {
+ if (!selectedItem || selectedItem.type !== 'effect') return;
- showMessage('Properties updated', 'success');
+ const effects = sequences[selectedItem.seqIndex].effects;
+ const index = selectedItem.effectIndex;
+
+ if (index < effects.length - 1) {
+ // Swap with effect above
+ [effects[index], effects[index + 1]] = [effects[index + 1], effects[index]];
+ selectedItem.effectIndex = index + 1;
+ renderTimeline();
+ updateProperties();
+ }
+ }
+
+ // Move effect down in stack (lower priority)
+ function moveEffectDown() {
+ if (!selectedItem || selectedItem.type !== 'effect') return;
+
+ const effects = sequences[selectedItem.seqIndex].effects;
+ const index = selectedItem.effectIndex;
+
+ if (index > 0) {
+ // Swap with effect below
+ [effects[index], effects[index - 1]] = [effects[index - 1], effects[index]];
+ selectedItem.effectIndex = index - 1;
+ renderTimeline();
+ updateProperties();
+ }
+ }
+
+ // Toggle same priority as previous effect (= modifier)
+ function toggleSamePriority() {
+ if (!selectedItem || selectedItem.type !== 'effect') return;
+
+ const effect = sequences[selectedItem.seqIndex].effects[selectedItem.effectIndex];
+ effect.priorityModifier = effect.priorityModifier === '=' ? '+' : '=';
+ updateProperties();
}
// File operations