diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 556a9b4..3d65a48 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -7315,9 +7315,15 @@ const App = () => { } }; const activeTracks = useMemo(() => { - const st = sessionTabs.find(s => s.id === activeTab); - return st ? st.tracks : tracks; - }, [activeTab, sessionTabs, tracks]); + var st = sessionTabs.find(s => s.id === activeTab); + if (st) return st.tracks; + var pr = subTabs.find(s => s.id === activeTab && s.type === 'PIANO_ROLL'); + if (pr && pr.parent_tab_id && pr.parent_tab_id.startsWith('session_')) { + var parentSt = sessionTabs.find(s => s.id === pr.parent_tab_id); + if (parentSt) return parentSt.tracks; + } + return tracks; + }, [activeTab, sessionTabs, subTabs, tracks]); const activeTracksRef = useRef([]); activeTracksRef.current = activeTracks; const sessionTabsRef = useRef(sessionTabs); diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 8c070d1..5bb0f16 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -216,7 +216,7 @@ buffer:track.buffer,startTime:track.startTime||0,clips:track.clips?track.clips.m const[activeTab,setActiveTab]=useState('main');const[subTabSelectedNodeTime,setSubTabSelectedNodeTime]=useState(null);const[subTabNormVal,setSubTabNormVal]=useState(0);const[subTabGainVal,setSubTabGainVal]=useState(100);const[subTabPitchVal,setSubTabPitchVal]=useState(0);const[subTabs,setSubTabs]=useState([]);// [{id, label, trackId, startTime, endTime, buffer, effects, currentTime, selectionStart, selectionEnd, isPlaying}, ...] const[sessionTabs,setSessionTabs]=useState([]);// [{id, name, tracks}, ...] const[tabContextMenu,setTabContextMenu]=useState(null);// { x, y, tabId, tabType } -const handleSetTabColor=(tabId,tabType,color)=>{if(tabType==='session'){setSessionTabs(prev=>prev.map(s=>s.id===tabId?{...s,color:color}:s));const tab=sessionTabs.find(s=>s.id===tabId);if(tab){setTracks(prev=>prev.map(t=>{if(!t.sections||t.sections.length===0)return t;return{...t,sections:t.sections.map(s=>{if(s.sectionId===tab.sectionId){return{...s,color:color};}return s;})};}));}}else{setSubTabs(prev=>prev.map(s=>s.id===tabId?{...s,color:color}:s));}};const activeTracks=useMemo(()=>{const st=sessionTabs.find(s=>s.id===activeTab);return st?st.tracks:tracks;},[activeTab,sessionTabs,tracks]);const activeTracksRef=useRef([]);activeTracksRef.current=activeTracks;const sessionTabsRef=useRef(sessionTabs);sessionTabsRef.current=sessionTabs;const[selectionCleared,setSelectionCleared]=useState(false);// LOOP_EDITOR_2.md §4.2 +const handleSetTabColor=(tabId,tabType,color)=>{if(tabType==='session'){setSessionTabs(prev=>prev.map(s=>s.id===tabId?{...s,color:color}:s));const tab=sessionTabs.find(s=>s.id===tabId);if(tab){setTracks(prev=>prev.map(t=>{if(!t.sections||t.sections.length===0)return t;return{...t,sections:t.sections.map(s=>{if(s.sectionId===tab.sectionId){return{...s,color:color};}return s;})};}));}}else{setSubTabs(prev=>prev.map(s=>s.id===tabId?{...s,color:color}:s));}};const activeTracks=useMemo(()=>{var st=sessionTabs.find(s=>s.id===activeTab);if(st)return st.tracks;var pr=subTabs.find(s=>s.id===activeTab&&s.type==='PIANO_ROLL');if(pr&&pr.parent_tab_id&&pr.parent_tab_id.startsWith('session_')){var parentSt=sessionTabs.find(s=>s.id===pr.parent_tab_id);if(parentSt)return parentSt.tracks;}return tracks;},[activeTab,sessionTabs,subTabs,tracks]);const activeTracksRef=useRef([]);activeTracksRef.current=activeTracks;const sessionTabsRef=useRef(sessionTabs);sessionTabsRef.current=sessionTabs;const[selectionCleared,setSelectionCleared]=useState(false);// LOOP_EDITOR_2.md §4.2 // ── Temp Edit Tab (LOOP_EDITOR_2.md §1.2 Sub Tab) ── const[tempTabActive,setTempTabActive]=useState(false);const[tempTabBuffer,setTempTabBuffer]=useState(null);const[tempTabTrackId,setTempTabTrackId]=useState(null);const[tempTabOrigStart,setTempTabOrigStart]=useState(0);const[tempTabOrigEnd,setTempTabOrigEnd]=useState(0);const tempTabCanvasRef=useRef(null);// Effect parameters for temp tab const[tempTabEffects,setTempTabEffects]=useState({reverse:false,gainDb:0,fadeInMs:0,fadeOutMs:0});// ── Auth / User State ── diff --git a/wiki.md b/wiki.md index 883fa94..94c6762 100644 --- a/wiki.md +++ b/wiki.md @@ -1,3 +1,9 @@ +### [2026-07-28 07:56] Task: Fix PIANO_ROLL activeTracks + instrument auto-assign trong SECTION-TAB +- **Tóm tắt thay đổi:** Fix `activeTracks` useMemo: khi `activeTab` là PIANO_ROLL sub-tab từ SECTION-TAB (`parent_tab_id` bắt đầu `session_`), trả về tracks của section tab thay vì main tracks. Trước đây `activeTracks` luôn trả về main tracks khi activeTab !== session_xxx → dropdown MIDI item rỗng, track name không hiển thị, instrument lookup sai. Fix: check `subTabs` cho PIANO_ROLL type để resolve đúng parent session tracks. +- **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. Cần restart server + clear browser cache. +--- + ### [2026-07-28 07:47] Task: Sync MIDI item editing + ghost notes từ MAIN vào SECTION-TAB - **Tóm tắt thay đổi:** Fix `handleEditSectionInTab` xoá `midiItems: []` khi clone section tracks. SECTION-TAB giờ giữ nguyên MIDI items từ section data → dropdown MIDI item switcher hoạt động, ghost notes extract từ các track khác trong section, instrument settings được preserve. Piano Roll Tab dùng chung component `PianoRollTabEditor` nên tất cả tính năng (ghost, session sync, CC lane, export, AI) đều hoạt động trên cả MAIN và SECTION-TAB. - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`