diff --git a/services/web-ui/public/js/timeline.js b/services/web-ui/public/js/timeline.js index 8598863..f050674 100644 --- a/services/web-ui/public/js/timeline.js +++ b/services/web-ui/public/js/timeline.js @@ -94,7 +94,7 @@ var area = document.createElement('div'); area.className = 'tl-clip-area'; area.dataset.trackId = t.id; - area.style.cssText = 'flex:1;position:relative;overflow:hidden;'; + area.style.cssText = 'flex:1;position:relative;overflow:visible;'; area.addEventListener('click', _onAreaClick); row.appendChild(area); @@ -269,8 +269,12 @@ // Drag to move (body only) el.addEventListener('mousedown', function (e) { if (e.target === lh || e.target === rh) return; + e.preventDefault(); s.selectedId = clip._id; - _renderClips(); + // Update selection highlight without destroying the element we're dragging + s.tracksEl.querySelectorAll('.tl-clip').forEach(function (c) { + c.style.borderColor = c.dataset.clipId === clip._id ? 'var(--accent)' : 'var(--border-strong)'; + }); _onMoveStart(e, clip); }); diff --git a/services/web-ui/public/screens-editor.jsx b/services/web-ui/public/screens-editor.jsx index e532141..282ba28 100644 --- a/services/web-ui/public/screens-editor.jsx +++ b/services/web-ui/public/screens-editor.jsx @@ -34,6 +34,9 @@ function Editor() { const streamCacheRef = React.useRef({}); const tlInitRef = React.useRef(false); + // Refs so Timeline callbacks always read current values without stale closure issues + const historyRef = React.useRef([[]]); + const historyIdxRef = React.useRef(0); React.useEffect(() => { const data = window.ZAMPP_DATA; @@ -78,6 +81,8 @@ function Editor() { setCurrentSeq(seq); setPlayheadFrames(0); setSelectedClipId(null); + historyRef.current = [clips]; + historyIdxRef.current = 0; setHistory([clips]); setHistoryIdx(0); setIsDirty(false); @@ -130,9 +135,11 @@ function Editor() { function handleClipsChanged(clips) { setCurrentSeq(prev => prev ? { ...prev, clips } : prev); - const newHistory = history.slice(0, historyIdx + 1); + const newHistory = historyRef.current.slice(0, historyIdxRef.current + 1); newHistory.push(clips.map(c => ({ ...c }))); if (newHistory.length > 50) newHistory.shift(); + historyRef.current = newHistory; + historyIdxRef.current = newHistory.length - 1; setHistory(newHistory); setHistoryIdx(newHistory.length - 1); markDirty(); @@ -173,20 +180,22 @@ function Editor() { } function undo() { - if (historyIdx <= 0) return; - const idx = historyIdx - 1; + if (historyIdxRef.current <= 0) return; + const idx = historyIdxRef.current - 1; + historyIdxRef.current = idx; setHistoryIdx(idx); - const clips = (history[idx] || []).map(c => ({ ...c, _id: _uid() })); + const clips = (historyRef.current[idx] || []).map(c => ({ ...c, _id: _uid() })); setCurrentSeq(prev => prev ? { ...prev, clips } : prev); renderTimelineClips(clips); markDirty(); } function redo() { - if (historyIdx >= history.length - 1) return; - const idx = historyIdx + 1; + if (historyIdxRef.current >= historyRef.current.length - 1) return; + const idx = historyIdxRef.current + 1; + historyIdxRef.current = idx; setHistoryIdx(idx); - const clips = (history[idx] || []).map(c => ({ ...c, _id: _uid() })); + const clips = (historyRef.current[idx] || []).map(c => ({ ...c, _id: _uid() })); setCurrentSeq(prev => prev ? { ...prev, clips } : prev); renderTimelineClips(clips); markDirty(); @@ -356,7 +365,7 @@ function Editor() { }} /> {scale}px -
+