FIX: sửa lỗi không hiển thị đúng nội dung của SECTION-TAB trong section item

This commit is contained in:
2026-08-07 18:58:29 +07:00
parent 1d9b426d15
commit c3182f6baa
4 changed files with 414 additions and 53 deletions
+89
View File
@@ -2694,3 +2694,92 @@
(3) contextMenuCopy/Cut — xử lý ITEM TRƯỚC track: MIDI → clipboard {type:'midi', notes, duration(sec), name, color} — paste = MIDI item (doPaste nhánh midi đã có); clip → clipboard {buffer,...}; Cut = copy + XÓA item (updateActiveTracks filter).
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070725), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → chuột phải MIDI item → Cut → Paste → ra MIDI ITEM (không phải audio); chuột phải clip → Copy → Paste → clip; chuột phải track (không item) → Copy → track buffer như cũ.
### [2026-08-07 07:30] Task: (1) Lưu project phải lưu tất cả tab — XÁC NHẬN đã đúng (2) Chuyển PIANO ROLL tab → stop âm tab cũ
- **Yêu cầu (1):** ở MAIN bấm lưu/Ctrl+S phải lưu tất cả các tab.
- **Xác nhận:** serializeProjectToSchema ĐÃ lưu đủ — sessionTabs → section_store (tracks đầy đủ) + sub_tabs (id/trackId/target_id/parent_tab_id/notes/currentTime); deserialize restore đủ (9327-9358). Ctrl+S (16295) + nút Save Project (25368) → handleSaveProject → serialize đủ. KHÔNG cần sửa — nếu user thấy thiếu tab sau load → dán schema đã lưu.
- **Yêu cầu (2):** chuyển PIANO ROLL TAB 1 → TAB 2 phải play đúng nội dung từng tab.
- **Nguyên nhân:** rời piano roll tab đang play (luật 04:15 "không stop") → sang tab 2 → âm tab 1 tiếp tục (nghe nhầm nội dung tab 1 khi ở tab 2).
- **FIX (app.jsx effect 14814):** prevSub PIANO_ROLL đang play + activeTab MỚI là SUB-TAB (midi_*/không main/session) → stopAllPlayback (dừng âm tab cũ — play tab mới đúng nội dung). Về MAIN/SECTION → giữ luật cũ (âm tiếp/resume).
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070730), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → play piano roll tab 1 → chuyển tab 2 → ÂM TAB 1 DỪNG (im — bấm play tab 2 → nghe tab 2); về MAIN → hành vi cũ.
### [2026-08-07 07:35] Task: (1) Lưu ở MAIN phải lưu dirty tabs (2) Xóa midi item → close piano roll tab tương ứng
- **Báo cáo user:** (1) nhấn lưu ở MAIN SESSION không lưu dirty tab (PIANO ROLL đã sửa notes); (2) xóa midi item phải close PIANO ROLL TAB đã mở của item đó.
- **Nguyên nhân (1):** keydown Ctrl+S handler thứ 2 (16388) — nhánh `curTab === 'main'` RỖNG ("handled by main handler" — không làm gì) → MAIN Ctrl+S không save project + không lưu dirty; nút menu Save Project (25370) chỉ gọi handleSaveProject (không lưu dirty).
- **FIX (app.jsx):**
(1) Ctrl+S (16388): nhánh main/session → `handleSaveProject()` + save TẤT CẢ dirty sub-tabs (PIANO_ROLL → handleSaveMidiNotes; SECTION → saveSectionTab; audio → buffer về track); nút Save Project → cùng logic.
(2) Xóa midi item (contextMenuDelete 17480 + contextMenuCut 17666) → `setSubTabs(filter target_id !== itemId)` — close piano roll tab của item; nếu tab đang active bị close → `setActiveTab('main')`.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070735), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → sửa notes piano roll (dirty) → về MAIN → Ctrl+S / Save Project → reload → notes ĐÃ LƯU (tab vẫn mở + nội dung mới); xóa midi item → piano roll tab của item đó tự close (+ về main nếu đang active tab đó).
### [2026-08-07 07:40] Task: SECTION-TAB — xóa midi item (Delete key) → close piano roll tab tương ứng
- **Báo cáo user:** trong SECTION-TAB, xóa midi item phải close PIANO ROLL TAB đã mở của item đó.
- **Nguyên nhân:** đường xóa chính là `handleDeleteSelectedItemsRef` (Delete key — chọn item → Delete) — xóa midiItems nhưng KHÔNG close subTab (target_id = item id) — áp cho cả MAIN lẫn SECTION (updateActiveTracks theo context).
- **FIX (app.jsx handleDeleteSelectedItemsRef):** đầu hàm — `setSubTabs(filter: bỏ tab có target_id ∈ idsToDelete)` — close piano roll tab của midi item bị xóa; tab đang active bị close → `setActiveTab('main')`.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070740), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → SECTION-TAB chọn midi item → Delete → piano roll tab của item đó TỰ CLOSE (về main nếu đang ở tab đó); MAIN tương tự.
### [2026-08-07 07:45] Task: (1) FIX Cut midi → paste audio (detect bounds sai) (2) Xóa item SECTION-TAB → sync canvas main + giữ tab
- **Báo cáo (1) [lặp]:** cut midi item (context menu) → paste ra audio item — fix 0725 chưa hiệu lực.
- **Nguyên nhân (1):** onContextMenu detect MIDI bounds dùng `(m.duration || 4)` KHÔNG × secondsPerBar (m.duration tính BEATS — contextMenuDelete 17452 có ×) → detect miss → itemType null → Cut/Copy xử lý TRACK buffer (audio). **FIX:** bounds × `_spb` (60/bpm*4).
- **Báo cáo (2) [mới]:** xóa item trong SECTION-TAB → phải cập nhật canvas section item ở MAIN + giữ vị trí SECTION-TAB (không quay về MAIN khi close tab).
- **FIX (2):** (a) helper `syncSectionTabToMain(tabId)` — sync `tab.tracks``sec.tracks` trên MAIN (canvas section item vẽ lại — KHÔNG đổi duration 0505); gọi sau mọi xóa item (Delete key handleDeleteSelectedItemsRef + contextMenuDelete: section/midi/clip + contextMenuCut). (b) điều kiện "về main" khi close tab — CHỈ khi activeTab là `midi_*` (piano roll) bị close — KHÔNG đá SECTION-TAB về main.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070745), `wiki.md`. Rebuild precompiled (build PASS — syncSectionTabToMain ×6).
- **Ghi chú/Test:** hard refresh → (1) chuột phải MIDI item (track có midi + audio) → Cut → Paste → MIDI item (không audio); (2) SECTION-TAB xóa item → section item trên MAIN vẽ lại (nội dung mới) + VẪN Ở SECTION-TAB (không về main).
### [2026-08-07 07:50] Task: (2) Sync canvas MAIN realtime sau cut/delete SECTION-TAB (3) Ctrl+X/C phải cut/copy ITEM
- **Báo cáo user:** (2) cut/delete item SECTION-TAB 1 → quay MAIN → phải cập nhật realtime canvas section item 1 (không cần save); (3) Ctrl+X không cut được item.
- **Nguyên nhân:** (2) syncSectionTabToMain (0745) chạy NGAY (TRƯỚC updateActiveTracks — state chưa áp) → sync data CŨ → canvas cũ; (3) Ctrl+C/X (16344/16349) LUÔN handleCopyTrack/handleCutTrack (track) — không xử lý item được chọn.
- **FIX (app.jsx):**
(2) syncSectionTabToMain — gọi qua `setTimeout(0)` (SAU updateActiveTracks — data mới) ở mọi điểm xóa (handleDeleteSelectedItemsRef + contextMenuDelete section/midi/clip + contextMenuCut).
(3) Ctrl+C/X — có ITEM được chọn (selectedItemIds) → `findItemContext` + `copyItemToClipboard` (MIDI notes / clip buffer — helpers dùng chung) → copy item; Ctrl+X → copy + `handleDeleteSelectedItemsRef` (xóa + close piano roll tab + sync section); không chọn item → fallback track cũ.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070750), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → (2) SECTION-TAB cut/delete item → quay MAIN → section item canvas CẬP NHẬT realtime (nội dung mới — không save); (3) chọn MIDI item → Ctrl+C → Ctrl+V → paste MIDI item; chọn item → Ctrl+X → item bị cắt (clipboard) + tab close + canvas sync.
### [2026-08-07 07:55] Task: (2) Paste midi block ngắn — duration sai (3) Ctrl+V paste vào track 1
- **Báo cáo user:** (1) [lặp] cut midi → paste audio — fix 0745 chờ xác nhận bản mới; (2) context menu cut midi → paste → item block NGẮN (không giữ duration); (3) Ctrl+V paste vào TRACK 1 (không phải track được chọn).
- **Nguyên nhân:** (2) midiItem.duration tính GIÂY (insertMidiItem = 4*spb; scheduling 19331 dùng seconds) — nhưng copy midi (copyItemToClipboard + contextMenuCopy/Cut) nhân thêm `(60.0/bpm)` → duration gấp đôi → block sai. (3) handlePasteTrack = `doPaste(selectedTrackId, ...)` — chọn ITEM không set selectedTrackId (giữ '1') → paste track 1.
- **FIX (app.jsx):**
(2) copy midi duration = `item.duration || 4` (GIÂY — bỏ × 60/bpm) — 3 chỗ (helper + contextMenuCopy + contextMenuCut) + bỏ biến bpmVal thừa.
(3) handlePasteTrack — có ITEM được chọn (selectedItemIds) → target = track chứa item (findItemContext); ngược lại selectedTrackId.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070755), `wiki.md`. Rebuild precompiled (build PASS — type:'midi' ×4, duration ×3).
- **Ghi chú/Test:** hard refresh → (2) context menu cut midi → paste → block GIỮ ĐÚNG duration; (3) chọn item trên track 2 → Ctrl+V → paste vào track 2 (không phải track 1); (1) cut midi → paste → MIDI item.
### [2026-08-07 08:00] Task: (1) Ctrl+X bị browser capture (2) Ctrl+V paste sai track 1
- **Báo cáo user:** (1) Ctrl-X bị browser capture — không cut item được; (2) Ctrl+V vẫn paste track 1 — phải paste vào track đang select.
- **Nguyên nhân:** (1) Ctrl+X handler thiếu stopPropagation (browser cut text chiếm); (2) click ITEM — mousedown nhánh hitItem `return` sớm (2919) → `onTrackLaneMouseDown` (setSelectedTrackId) KHÔNG chạy → selectedTrackId giữ track cũ ('1') → Ctrl+V paste sai.
- **FIX (app.jsx):** (1) Ctrl+X — thêm `e.stopPropagation()` (cùng preventDefault). (2) click item (mousedown hitItem) → `onSelectTrack(track.id)` — chọn track chứa item → Ctrl+V paste đúng track (handlePasteTrack đã ưu tiên item → fallback selectedTrackId).
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070800), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → (1) chọn item → Ctrl+X → item bị cắt (không bị browser chiếm); (2) chọn item trên track 2 → Ctrl+V → paste vào track 2 (đúng track đang select).
### [2026-08-07 08:05] Task: (1) Ctrl-X không cắt item (2) paste tại vị trí click (3) paste sai track — closure stale
- **Báo cáo user:** (1) click item → Ctrl-X không thấy cắt; (2) paste nên tại vị trí con trỏ CLICK (bên phải playhead → paste tại đó; bên trái → paste tại playhead); (3) Ctrl+V vẫn paste track đầu tiên.
- **Nguyên nhân:** (3) keydown effect `deps []` — closure giữ handlePasteTrack RENDER ĐẦU → `selectedTrackId` stale ('1') → paste track 1 LUÔN (bất kể track đang chọn). (2) doPaste luôn dùng currentTime (playhead). (1) chưa rõ — thêm log.
- **FIX (app.jsx):**
(1) Ctrl+X — thêm `console.log('[CutItem] Ctrl+X — itemId=... found=...')` — xác định đường chạy (item hay fallback track).
(2) `lastClickTimeRef` — set trong handlePlayheadSet (click timeline/ruler) — handlePasteTrack: pasteTime = click (nếu > playhead) else playhead.
(3) `handlePasteTrackRef` (ref mới mỗi render) — Ctrl+V gọi ref (hết stale selectedTrackId).
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070805), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → (1) chọn item → Ctrl+X → nếu không cắt → dán log `[CutItem]`; (2) click timeline bên phải playhead → Ctrl+V → paste tại chỗ click; click bên trái → paste tại playhead; (3) chọn item track 2 → Ctrl+V → paste track 2.
### [2026-08-07 08:10] Task: Ctrl+X không cắt item — copyItemToClipboard closure stale (activeTracks)
- **Log user (0805):** `[CutItem] Ctrl+X — itemId= midi_1786099252336 found= 2/midi selSize= 1` — handler chạy, detect đúng item — NHƯNG timeline không cắt.
- **Nguyên nhân:** Ctrl+C/X chạy trong keydown effect `deps []` → closure giữ `copyItemToClipboard` RENDER ĐẦU — `activeTracks` (state) là ARRAY CŨ — item `midi_1786099252336` TẠO SAU render đầu → `activeTracks.find` KHÔNG có item → copy fail → fallback `handleCutTrack` (cắt track — không cắt item).
- **FIX (app.jsx copyItemToClipboard):** `const trk = (activeTracksRef.current || []).find(...)` — dùng REF (luôn mới) thay closure state.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070810), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → chọn item (mới tạo) → Ctrl+X → item bị CẮT (log [CutItem] found= track/type + toast 'Đã cắt item'); Ctrl+C → copy item → Ctrl+V paste đúng.
### [2026-08-07 08:15] Task: Kéo ngắn midi item SECTION-TAB → MAIN canvas vẫn 8 bars
- **Báo cáo user:** midi item 2 (8 bars content) kéo NGẮN còn 4 bars trong SECTION-TAB — MAIN section item canvas vẫn hiển thị 8 bars.
- **Nguyên nhân:** syncSectionTabToMain (0745) chỉ chạy khi XÓA item — kéo NGẮN (resize) / MOVE item KHÔNG sync → sec.tracks giữ duration/vị trí cũ → canvas MAIN vẽ 8 bars.
- **FIX (app.jsx):** thêm sync sau thao tác (activeTab session → setTimeout(0) → syncSectionTabToMain): (a) handleMouseUp của RESIZE item (21651 — kéo edge — duration mới); (b) handleMouseUp của MOVE item (21563 — vị trí mới).
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070815), `wiki.md`. Rebuild precompiled (build PASS — syncSectionTabToMain ×8).
- **Ghi chú/Test:** hard refresh → SECTION-TAB kéo ngắn midi item 2 (4 bars) → quay MAIN → section item canvas hiển thị ĐÚNG 4 bars (không còn 8 bars); kéo di chuyển item → MAIN canvas theo vị trí mới.
### [2026-08-07 08:20] Task: MAIN canvas section item vẫn 8 bars — notes không clip theo duration
- **Test user:** kéo ngắn midi item 2 (4 bars) SECTION-TAB → MAIN canvas KHÔNG hiển thị 4 bars (vẫn 8).
- **Nguyên nhân:** trim (kéo ngắn) KHÔNG cắt notes (notes 8 bars vẫn trong item — duration giảm 4) — canvas MAIN vẽ section content: `notes.forEach` vẽ MỌI note (2579) KHÔNG clip theo `item.duration` → 8 bars vẫn hiển thị (sync 08:15 đã cập nhật sec.tracks — nhưng vẽ không clip).
- **FIX (app.jsx canvas MAIN — drawSection subMidi notes):** skip note `noteStartSec >= (item.duration || 0) - 0.01` — note ngoài duration (phần đã trim) KHÔNG vẽ.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070820), `wiki.md`. Rebuild precompiled (build PASS).
- **Ghi chú/Test:** hard refresh → SECTION-TAB kéo ngắn midi item (4 bars) → quay MAIN → section item canvas hiển thị ĐÚNG 4 bars (phần 5-8 trống); scheduling đã skip note ngoài duration (19331).