From f1057ab7f2a4b71a0766bf4f0fd04de88ea7c4cc Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sat, 25 Jul 2026 09:21:19 +0700 Subject: [PATCH] feat: auto-follow dragged item in viewport for clip/section/MIDI drag and resize --- app/static/js/app.jsx | 21 +++++++++++++++++++++ wiki.md | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 7fba6a1..27987a3 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -10218,6 +10218,13 @@ const App = () => { const marginBar = maxDurationRef.current - secPerBar; const clampedStart = Math.min(rawStart, marginBar); const newStart = snapTime(clampedStart, snapValueRef.current, bpmRef.current); + const itemPx = newStart * zoom; + const followMargin = 120; + if (itemPx > scrollLeft + rect.width - followMargin) { + wrapper.scrollLeft = itemPx - rect.width + followMargin; + } else if (itemPx < scrollLeft + followMargin) { + wrapper.scrollLeft = Math.max(0, itemPx - followMargin); + } const targetTrackId = hoveredTrackIdRef.current || drag.trackId; updateActiveTracks(prev => prev.map(t => { // Clear the clip from its previous track if it moved to a new track @@ -10375,6 +10382,13 @@ const App = () => { const secondsPerBar = beatSec * 4; const marginBar = maxDurationRef.current - secondsPerBar; const newStart = Math.max(beatSec, Math.min(time - drag.clickOffset, marginBar)); + const itemPx = newStart * zoom; + const followMargin = 120; + if (itemPx > scrollLeft + rect.width - followMargin) { + wrapper.scrollLeft = itemPx - rect.width + followMargin; + } else if (itemPx < scrollLeft + followMargin) { + wrapper.scrollLeft = Math.max(0, itemPx - followMargin); + } const targetTrackId = hoveredTrackIdRef.current || drag.trackId; updateActiveTracks(prev => { let movedItem = null; @@ -10463,6 +10477,13 @@ const App = () => { ? { ...t, sections: items } : { ...t, midiItems: items }; })); + const edgePx = (resize.side === 'left' ? Math.max(0, time) : time) * zoom; + const followMargin = 120; + if (edgePx > scrollLeft + rect.width - followMargin) { + wrapper.scrollLeft = edgePx - rect.width + followMargin; + } else if (edgePx < scrollLeft + followMargin) { + wrapper.scrollLeft = Math.max(0, edgePx - followMargin); + } }; const handleMouseUp = () => { const resize = resizedSectionItemRef.current; diff --git a/wiki.md b/wiki.md index c0e1f05..01d9da0 100644 --- a/wiki.md +++ b/wiki.md @@ -49,6 +49,11 @@ - **Các file ảnh hưởng:** `app/static/js/app.jsx` - **Ghi chú/Test (nếu có):** `npx babel app/static/js/app.jsx` — compile pass. +### [2026-07-25 09:20] Task: Auto-follow dragged item in viewport +- **Tóm tắt thay đổi:** (1) Clip drag: sau mỗi update, nếu item ra khỏi vùng 120px viewport edge, tự scroll để item luôn thấy. (2) Section/MIDI drag: tương tự. (3) Section/MIDI resize: theo dõi edge đang kéo. +- **Các file ảnh hưởng:** `app/static/js/app.jsx` +- **Ghi chú/Test (nếu có):** `npx babel app/static/js/app.jsx` — compile pass. + ### [2026-07-25 07:25] Task: Fix auto-scroll + maxDuration tab isolation + 1-bar margin - **Tóm tắt thay đổi:** (1) `maxDuration` dùng `activeTracks` + 4-bar buffer. (2) Cách ly MAIN vs SECTION-TAB. (3) Clip/section/MIDI drag/stretch/resize clamp 1-bar from right. (4) Clip drag + stretched clip dùng `updateActiveTracks`. (5) Stretched clip handler thêm auto-scroll. - **Các file ảnh hưởng:** `app/static/js/app.jsx`