### [2026-07-25 06:57] Task: Fix Piano Roll runtime errors (rollBeats TDZ + handleCCMouseMove) - **Tóm tắt thay đổi:** Sửa lỗi `Cannot access 'rollBeats' before initialization` bằng cách di chuyển khai báo `rollBeats`/`rollBeatsRef` lên trước `totalBeats`. Thêm hàm `handleCCMouseMove` bị thiếu và cập nhật `handleCCMouseDown` hỗ trợ Ctrl+Click paint velocity. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `cd /home/locpham/SonicForgeStudio && npm run build` — build passes. --- ### [2026-07-25 07:25] Task: 4 tính năng mới (Piano Roll zoom fill + Shift+scroll playhead + auto-scroll drag + SoundFont cache) - **Tóm tắt thay đổi:** (1) Piano Roll zoom-out không còn màn hình đen — bars fill toàn bộ viewport. (2) Shift+scroll trong Piano Roll di chuyển playhead và play notes MIDI như fast-forward. (3) Khi drag section/MIDI/clip đến cạnh phải timeline, auto-scroll container. (4) Server-side cache FluidSynth instances + PluginManager singleton + list_soundfont_instruments cache. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/core/vst_engine.py` - **Ghi chú/Test (nếu có):** `npm run build` pass. `vst_engine.py` thêm `load_soundfont_cached`, `release_soundfont`, `get_plugin_manager`, `_SF_INSTRUMENTS_CACHE` — refcount-based cache. ### [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` - **Ghi chú/Test (nếu có):** `npx babel app/static/js/app.jsx` — compile pass. ### [2026-07-25 07:31] Task: Fix zoom cursor + right edge black + AI Copilot for Piano Roll - **Tóm tắt thay đổi:** (1) Tách `drawWidth` (content) và `cssWidth` (CSS) — canvas coordinate system không còn bị sai khi zoom out. (2) Thêm `min-width` fill viewport, không còn mảng đen phải. (3) Thêm AI Copilot input + handler trong Piano Roll — gõ prompt tạo MIDI notes trực tiếp. - **Các file ảnh hưởng:** `app/static/js/app.jsx` - **Ghi chú/Test (nếu có):** `npm run build` pass. ### [2026-07-25 08:08] Task: Fix velocity draw position, shift+scroll play, canvas redraw deps - **Tóm tắt thay đổi:** (1) CC lane velocity stem vẽ tại start note thay vì center. (2) Shift+scroll chỉ play note khi playhead vượt qua start_beat. (3) Thêm `viewWidth`/`viewBeats` vào dependency array grid canvas + CC canvas để auto-redraw khi resize/zoom. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` pass. ### [2026-07-25 08:31] Task: Fix AI Copilot tab scope + hide bottom panels + context menu Insert + autoscroll - **Tóm tắt thay đổi:** (1) AI Copilot context dùng `activeTracks` thay `tracks` — chỉ tác động tab đang active. (2) Bottom panels mặc định ẩn. (3) Right-click track timeline có 4 mục Insert. (4) Thêm auto-scroll vào stretched clip resize. Sửa `insertSectionAtPlayhead` dùng `updateActiveTracks`. - **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 08:36] 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` - **Ghi chú/Test (nếu có):** `npx babel app/static/js/app.jsx` — compile pass. ### [2026-07-25 08:53] Task: Add timeline left/right margin + dynamic buffer expansion - **Tóm tắt thay đổi:** (1) PADDING_LEFT thay đổi từ 2s thành 2-bar margin ở cả WaveformLane và TempoTrackLane. (2) Thêm state `scrollBufferExtra` + auto-expand 4 bars khi auto-scroll gần đến rìa timeline. (3) `maxDuration` thêm `scrollBufferExtra` + 6 bars static buffer. (4) Clip drag/stretch dùng `activeTracks` + `updateActiveTracks` thay `tracks`/`setTracks`. - **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:04] Task: Fix left margin 1-beat, items always visible, bar/timeline sync - **Tóm tắt thay đổi:** (1) PADDING_LEFT từ 2-bar xuống 1-beat ở cả WaveformLane và TempoTrackLane. (2) Drag clamp left ≥ 1 beat, right ≤ maxDuration - 1 bar. (3) Bar numbers + grid vẽ lại đồng bộ với scroll+zoom. - **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:08] Task: Fix Piano Roll CC lane always visible + resizable height - **Tóm tắt thay đổi:** (1) `showCC` default `true` — CC lane luôn hiển thị. (2) Thêm `ccHeight` state + resize handle (drag top edge) — cho phép thay đổi độ cao CC lane. (3) Canvas dùng `ccHeight` thay hardcoded 80px. - **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:18] Task: Fix brush velocity isolation from CC lane - **Tóm tắt thay đổi:** Thêm `brushVelocityRef` riêng biệt (0.8) — brush không còn bị ảnh hưởng bởi velocity paint từ CC lane. - **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 09:23] Task: Sync bar ruler redraw during drag - **Tóm tắt thay đổi:** (1) TempoTrackLane nhận `canvasRedrawCount` prop + deps — force redraw bar numbers khi drag. (2) Cả 3 drag handler increment `setCanvasRedrawCount` sau auto-follow scroll. (3) Sub-tab TempoTrackLane thêm `viewportWidth` + `scrollLeft` + `canvasRedrawCount` — đồng bộ ruler với timeline. - **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:30] Task: Fix bar numbers disappear past bar 12 during drag - **Tóm tắt thay đổi:** TempoTrackLane đọc `scrollLeft` từ DOM (walk parent chain tìm scroll container) thay vì React prop — canvas luôn dùng scroll position thực tế, không lag do async state. - **Các file ảnh hưởng:** `app/static/js/app.jsx` - **Ghi chú/Test (nếu có):** ### [2026-07-25 09:58] Task: Fix drag speed + bar numbers visibility + snap section items - **Tóm tắt thay đổi:** (1) Xoá auto-follow phải (gây jump), chỉ giữ auto-follow trái 60px — scroll phải nhờ `autoScrollTimeline` mượt. (2) Thêm `PADDING_RIGHT` vào `tEnd` ở TempoTrackLane và WaveformLane — bar numbers + grid hiển thị đến sát rìa phải. (3) `if (localX < 0 || localX > drawWidth) continue;` — skip phần tử ngoài canvas. (4) Buffer 12 bars cho `maxDuration`. (5) Thêm `snapTime` vào section/MIDI item drag. (6) Thêm snap option `'4'` (1 bar). (7) Đồng bộ `PADDING_LEFT = barDuration` ở cả 2 lane. - **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 10:08] Task: Merge tempo bar + timebar, beat-based ruler, snap grid - **Tóm tắt thay đổi:** Thay thế seconds-based ruler (0.00s, 1.00s...) + TempoTrackLane bằng TimelineRuler (48px canvas) hiển thị bar:beat grid lines + snap sub-ticks + BPM label. Grid WaveformLane khớp ruler, snapTime() đã dùng grid spacing beat — snap items đúng vị trí ruler. - **Các file ảnh hưởng:** `app/static/js/app.jsx` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:35] Task: Restore time duration labels on TimelineRuler + TempoTrackLane - **Tóm tắt thay đổi:** Thêm time duration labels (formatTimeSimple: "0.00s", "1.00s",...) vào TimelineRuler và TempoTrackLane. Hiển thị dạng màu cam (#ffb464) ở chân ruler, động điều chỉnh interval (1s/2s/5s/10s...) theo zoom level. Time labels đã bị mất từ commit ff44fd4 khi merge time bar + tempo bar thành TimelineRuler. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:39] Task: Sync TempoTrackLane leadIn + time labels on ruler top - **Tóm tắt thay đổi:** Thêm `leadInMargin` prop vào TempoTrackLane để đồng bộ column-0 (time=0) với vị trí leadIn của TimelineRuler. Thêm CLIP_BUFFER vào TempoTrackLane. Di chuyển time duration labels trong TimelineRuler từ dưới đáy (y=40) lên phía trên (y=11). - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 11:09] Task: Fix TempoTrackLane scroll sync during drag auto-scroll - **Tóm tắt thay đổi:** TempoTrackLane/WaveformLane/TimelineRuler hiện đọc scrollLeft trực tiếp từ DOM wrapper (canvases tìm parent scrollable) thay vì React prop. Tránh lệch vị trí khi auto-scroll nhanh trong clip drag do React state batching. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 11:04] Task: Fix PADDING_LEFT alignment + right-side bar rendering - **Tóm tắt thay đổi:** Set PADDING_LEFT=0 trong TimelineRuler/WaveformLane/TempoTrackLane để grid column 0 khớp với clip position 0 (trước đây lệch 8px). TempoTrackLane dùng CLIP_BUFFER/zoom cho tEnd thay vì PADDING_LEFT để bar/beat vẽ đủ xa về phía phải khi scroll. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:58] Task: Fix clip/section snap to column 0 - **Tóm tắt thay đổi:** Xóa `Math.max(beatSec, ...)` trong clip drag (10325), section drag (10495), section resize (10576) — thay bằng `Math.max(0, ...)` để item có thể snap vào column 0. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:54] Task: Fix TempoTrackLane height + clamp selection at column 0 - **Tóm tắt thay đổi:** Fix TempoTrackLane canvas height = 40px cố định (thay vì parentElement.clientHeight gây tràn). Wrapper `h-10 shrink-0 overflow-hidden` để không đẩy track lanes lệch TCP. Clamp selLeft/selRight/ruler handlers >= 0 để selection không vào vùng âm. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:44] Task: Zero lead-in, time ruler + tempo track split layout - **Tóm tắt thay đổi:** Xóa leadInMargin (về 0) — time 0 tại mép trái, chỉ còn 8px PADDING_LEFT. TimelineRuler đơn giản thành time-only ruler (40px, chỉ hiển thị time labels). Thêm TempoTrackLane ngay dưới (40px, bars/beats/BPM). Selection overlay top từ 48→80px. Đồng bộ WaveformLane PADDING_LEFT=8/zoom. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 10:18] Task: Lead-in margin, bar 0-indexed, grid clip fix - **Tóm tắt thay đổi:** Thêm leadInMargin (1 bar) đẩy timeline content sang phải — bar 0, time 0s tại mép margin. Playhead bắt đầu từ đó. Bar numbering 0-indexed (bar 0 thay vì 1). Sửa CLIP_BUFFER từ 200px → max(400, barWidth+200) để bar number không bị che. Đồng bộ leadInMargin vào tStart/tEnd canvas + mouse handlers (ruler, clip drag, section resize). - **Các file ảnh hưởng:** `app/static/js/app.jsx` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 11:32] Task: Redraw canvas on section item duration resize - **Tóm tắt thay đổi:** Thêm `setCanvasRedrawCount(n => n + 1)` trong resize mousemove handler để WaveformLane redraw khi section/MIDI item duration thay đổi. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 17:22] Task: Piano roll toolbar - transport, ARM/INPUT, AI bar range - **Tóm tắt thay đổi:** Thêm transport (play/stop/back/next/record), ARM+INPUT select MIDI, AI bar range (bar x-y) vào piano roll toolbar. handleAISend phát hiện piano roll active → MIDI prompt. Thêm selectedMidiInputId + handleMidiInputSelect. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `npm run build` — build passes. --- ### [2026-07-25 22:05] Task: Fix piano roll selection, playback, marquee drag/resize/move - **Tóm tắt thay đổi:** (1) Fix playback duration — min 16 beats when no notes exist. (2) Selection marquee: green transparent (#22c55e), full-height from top to CC lane. (3) Document-level mousemove/mouseup for marquee drag (không dừng khi ra khỏi grid). (4) Edge resize handles (left/right) cho marquee. (5) Body drag (move) cho marquee. Hiển thị marquee trên cả grid canvas và CC canvas. - **Các file ảnh hưởng:** `app/static/js/app.precompiled.js` - **Ghi chú/Test (nếu có):** `node --check app/static/js/app.precompiled.js` — syntax OK. ---