summaryrefslogtreecommitdiff
path: root/tools/timeline_editor
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-05 21:37:14 +0100
committerskal <pascal.massimino@gmail.com>2026-02-05 21:37:14 +0100
commitafded2b335a469ac550c345d1676a910a650a988 (patch)
tree99d4e0c1ad607841f716832dc0769234cfc9f0e8 /tools/timeline_editor
parent73b3e19f46a138c89562357d23082c85fd2c0bf3 (diff)
fix(timeline-editor): Dynamic sequence bounds, mouse wheel scroll, responsive layout
Bug Fix #1: Dynamic Sequence Start Time - Sequences now dynamically adjust start position based on earliest effect - Sequence box shrinks from left when effects move later in time - Calculate visual bounds: min(effect.startTime) to max(effect.endTime) - Sequence position: seq.startTime + minEffectStart - Sequence width: maxEffectEnd - minEffectStart - Time display shows actual visual start time, not fixed seq.startTime - Eliminates empty space at sequence start when effects are delayed Bug Fix #2: Mouse Wheel Horizontal Scroll - Mouse wheel now scrolls timeline horizontally (natural navigation) - Prevents default vertical scroll behavior on timeline container - More intuitive than using horizontal scrollbar - Smooth scrolling with deltaY mapped to scrollLeft Bug Fix #3: Responsive Layout - Container now uses 100% width (was: fixed 1400px max) - Added window resize event listener to re-render timeline - Body uses box-sizing: border-box for proper padding - Timeline recalculates on browser window resize - Works correctly when going fullscreen Technical details: - seqVisualStart = seq.startTime + Math.min(effect.startTime) - seqVisualEnd = seq.startTime + Math.max(effect.endTime) - Wheel event uses { passive: false } to allow preventDefault() - Window resize debouncing not needed (renderTimeline is fast)
Diffstat (limited to 'tools/timeline_editor')
-rw-r--r--tools/timeline_editor/index.html42
1 files changed, 33 insertions, 9 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html
index 4dd37ee..22e5239 100644
--- a/tools/timeline_editor/index.html
+++ b/tools/timeline_editor/index.html
@@ -16,11 +16,16 @@
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
+ margin: 0;
+ min-height: 100vh;
+ box-sizing: border-box;
}
.container {
- max-width: 1400px;
+ max-width: 100%;
+ width: 100%;
margin: 0 auto;
+ box-sizing: border-box;
}
header {
@@ -393,6 +398,7 @@
// DOM elements
const timeline = document.getElementById('timeline');
+ const timelineContainer = document.querySelector('.timeline-container');
const fileInput = document.getElementById('fileInput');
const saveBtn = document.getElementById('saveBtn');
const addSequenceBtn = document.getElementById('addSequenceBtn');
@@ -573,30 +579,37 @@
seqDiv.className = 'sequence';
seqDiv.dataset.index = seqIndex;
- // Calculate sequence duration based on effects
- let seqDuration = 10; // Default
+ // Calculate sequence bounds based on effects (dynamic start/end)
+ let seqVisualStart = seq.startTime;
+ let seqVisualEnd = seq.startTime + 10; // Default 10s duration
+
if (seq.effects.length > 0) {
- seqDuration = Math.max(...seq.effects.map(e => e.endTime));
+ const minEffectStart = Math.min(...seq.effects.map(e => e.startTime));
+ const maxEffectEnd = Math.max(...seq.effects.map(e => e.endTime));
+ seqVisualStart = seq.startTime + minEffectStart;
+ seqVisualEnd = seq.startTime + maxEffectEnd;
}
+ const seqVisualWidth = seqVisualEnd - seqVisualStart;
+
// Calculate sequence height based on number of effects (stacked vertically)
const numEffects = seq.effects.length;
const effectSpacing = 30; // Reduced from 40px
const seqHeight = Math.max(70, 20 + numEffects * effectSpacing + 5);
- seqDiv.style.left = `${seq.startTime * pixelsPerSecond}px`;
+ seqDiv.style.left = `${seqVisualStart * pixelsPerSecond}px`;
seqDiv.style.top = `${seqIndex * 80}px`;
- seqDiv.style.width = `${seqDuration * pixelsPerSecond}px`;
+ seqDiv.style.width = `${seqVisualWidth * pixelsPerSecond}px`;
seqDiv.style.height = `${seqHeight}px`;
- // Format time display based on mode
+ // Format time display based on mode (show visual start, not seq.startTime)
let seqTimeDisplay;
if (showBeats) {
const beatDuration = 60.0 / bpm;
- const startBeat = (seq.startTime / beatDuration).toFixed(1);
+ const startBeat = (seqVisualStart / beatDuration).toFixed(1);
seqTimeDisplay = `Start: ${startBeat}b`;
} else {
- seqTimeDisplay = `Start: ${seq.startTime.toFixed(2)}s`;
+ seqTimeDisplay = `Start: ${seqVisualStart.toFixed(2)}s`;
}
// Create sequence name overlay (large, centered, fades on hover)
@@ -916,6 +929,17 @@
updateProperties();
});
+ // Mouse wheel horizontal scroll
+ timelineContainer.addEventListener('wheel', (e) => {
+ e.preventDefault();
+ timelineContainer.scrollLeft += e.deltaY;
+ }, { passive: false });
+
+ // Window resize handler
+ window.addEventListener('resize', () => {
+ renderTimeline();
+ });
+
// Utilities
function showMessage(text, type) {
messageArea.innerHTML = `<div class="${type}">${text}</div>`;