From cdbff49ea26f3465fd8fe2692d4c6eafd7a737c1 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Sun, 16 Aug 2026 17:56:55 +0700 Subject: [PATCH] Remove LAN access badge; show server IP in SERVER label --- app/static/js/app.jsx | 35 ++++---------------------------- app/static/js/app.precompiled.js | 10 ++++----- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index cd922e5..e2e7395 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -15444,36 +15444,7 @@ const App = () => { }) }))); }, [bpm]); - // LAN access badge (standalone): server bind 0.0.0.0 — client browser ở máy - // khác trên mạng mở app qua http://:port. Hiện URL trên máy chủ; - // client LAN thấy origin đang dùng. - useEffect(() => { - let mounted = true; - let bar = null; - let iv = null; - const tryShow = () => { - const caps = window.SonicRuntime && window.SonicRuntime.capabilities; - const lan = caps && caps.lan; - if (!lan || !lan.urls || !lan.urls.length) return false; - if (document.getElementById('sf-lan-bar')) return true; - const hostname = window.location.hostname; - const isLocal = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1'; - bar = document.createElement('div'); - bar.id = 'sf-lan-bar'; - bar.style.cssText = 'position:fixed;top:8px;right:12px;z-index:9999;background:rgba(16,185,129,0.12);border:1px solid #10b981;color:#a7f3d0;font-size:11px;line-height:1.5;padding:6px 10px;border-radius:8px;max-width:min(70vw,420px);cursor:default;'; - bar.title = 'Client browser ở máy khác trên LAN mở địa chỉ này để làm việc realtime với máy chạy SonicForge'; - bar.innerHTML = isLocal - ? 'LAN: ' + lan.urls.map(function (u) { return '' + u + ''; }).join('  ·  ') - : 'Kết nối: ' + window.location.origin; - document.body.appendChild(bar); - return true; - }; - if (!tryShow()) { - iv = setInterval(function () { if (mounted && tryShow()) clearInterval(iv); }, 700); - setTimeout(function () { if (iv) clearInterval(iv); }, 20000); - } - return () => { mounted = false; if (iv) clearInterval(iv); if (bar && bar.parentNode) bar.parentNode.removeChild(bar); }; - }, []); + // LAN badge removed per request: server IP now shown in SERVER label. const openPanel = id => { if (id === 'export') setShowExportPanel(true); else if (id === 'ai') setShowAIPanel(true); else if (id === 'python_tools') setShowPythonToolsPanel(true); else if (id === 'selection') setShowSelectionPanel(true); }; @@ -16344,6 +16315,8 @@ const App = () => { channels: 'stereo' }); const [serverStatus, setServerStatus] = useState('checking...'); + const _lan = window.SonicRuntime && window.SonicRuntime.capabilities && window.SonicRuntime.capabilities.lan; + const serverHost = _lan && _lan.urls && _lan.urls[0] ? (function () { try { return new URL(_lan.urls[0]).hostname; } catch (e) { return null; } })() : window.location.hostname; const [menuOpen, setMenuOpen] = useState(null); const [selectedClipId, setSelectedClipId] = useState(null); // { trackId, clipId } const [stretchedClip, setStretchedClip] = useState(null); // { trackId, clipId, originalDuration, startTime, originalSpeed, beforeSnap } @@ -28488,7 +28461,7 @@ STRICT CONSTRAINTS: className: "flex items-center gap-2 px-2" }, /*#__PURE__*/React.createElement("span", { className: `text-xs font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus === 'connected' ? 'bg-emerald-950 text-emerald-400' : serverStatus === 'checking' ? 'bg-amber-950 text-amber-400' : 'bg-red-950 text-red-400'}` - }, "Server: ", serverStatus), /*#__PURE__*/React.createElement("button", { + }, "Server: ", serverStatus, serverHost ? " · " + serverHost : ""), /*#__PURE__*/React.createElement("button", { onClick: () => setShowAIConfig(!showAIConfig), className: `px-1.5 py-0.5 rounded text-xs border transition ${showAIConfig ? 'bg-purple-900 text-purple-200 border-purple-700' : 'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}` }, /*#__PURE__*/React.createElement("span", { diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 10c6a69..fafb763 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -805,10 +805,8 @@ useEffect(()=>{const oldSpb=prevBpmRef.current?60.0/parseFloat(prevBpmRef.curren if(oldSpb&&selectionFollowsTempo&&selectionStart!==null&&selectionEnd!==null&&selectionEnd>selectionStart){const startBar=selectionStart/oldSpb;const endBar=selectionEnd/oldSpb;if(endBar-startBar>0.01){setSelectionStart(startBar*secondsPerBar);setSelectionEnd(endBar*secondsPerBar);}}prevBpmRef.current=bpm;// Force canvas redraw setCanvasRedrawCount(n=>n+1);// Recalculate item/section durations updateActiveTracks(prev=>prev.map(t=>({...t,midiItems:(t.midiItems||[]).map(m=>{if(m.length_bars)return{...m,duration:m.length_bars*secondsPerBar};if(m.duration){// Legacy item without length_bars: compute bars from current duration/BPM -const bars=Math.max(0.25,Math.round(m.duration/secondsPerBar*4)/4);return{...m,length_bars:bars,duration:bars*secondsPerBar};}return m;}),sections:(t.sections||[]).map(s=>{if(s.length_bars)return{...s,duration:s.length_bars*secondsPerBar};if(s.duration){const bars=Math.max(0.25,Math.round(s.duration/secondsPerBar*4)/4);return{...s,length_bars:bars,duration:bars*secondsPerBar};}return s;})})));},[bpm]);// LAN access badge (standalone): server bind 0.0.0.0 — client browser ở máy -// khác trên mạng mở app qua http://:port. Hiện URL trên máy chủ; -// client LAN thấy origin đang dùng. -useEffect(()=>{let mounted=true;let bar=null;let iv=null;const tryShow=()=>{const caps=window.SonicRuntime&&window.SonicRuntime.capabilities;const lan=caps&&caps.lan;if(!lan||!lan.urls||!lan.urls.length)return false;if(document.getElementById('sf-lan-bar'))return true;const hostname=window.location.hostname;const isLocal=hostname==='localhost'||hostname==='127.0.0.1'||hostname==='::1';bar=document.createElement('div');bar.id='sf-lan-bar';bar.style.cssText='position:fixed;top:8px;right:12px;z-index:9999;background:rgba(16,185,129,0.12);border:1px solid #10b981;color:#a7f3d0;font-size:11px;line-height:1.5;padding:6px 10px;border-radius:8px;max-width:min(70vw,420px);cursor:default;';bar.title='Client browser ở máy khác trên LAN mở địa chỉ này để làm việc realtime với máy chạy SonicForge';bar.innerHTML=isLocal?'LAN: '+lan.urls.map(function(u){return''+u+'';}).join('  ·  '):'Kết nối: '+window.location.origin;document.body.appendChild(bar);return true;};if(!tryShow()){iv=setInterval(function(){if(mounted&&tryShow())clearInterval(iv);},700);setTimeout(function(){if(iv)clearInterval(iv);},20000);}return()=>{mounted=false;if(iv)clearInterval(iv);if(bar&&bar.parentNode)bar.parentNode.removeChild(bar);};},[]);const openPanel=id=>{if(id==='export')setShowExportPanel(true);else if(id==='ai')setShowAIPanel(true);else if(id==='python_tools')setShowPythonToolsPanel(true);else if(id==='selection')setShowSelectionPanel(true);};const[instrumentSelectorTrackId,setInstrumentSelectorTrackId]=useState(null);const[synthTrackDropdownId,setSynthTrackDropdownId]=useState(null);const[fxSelectorTrackId,setFxSelectorTrackId]=useState(null);const handleSetTrackFx=(trackId,fxType)=>{updateActiveTracks(prev=>prev.map(t=>t.id===trackId?{...t,fxType}:t));setFxSelectorTrackId(null);setTimeout(()=>lucide.createIcons(),50);};const[instrumentSelectorData,setInstrumentSelectorData]=useState(null);const[instrumentRefreshKey,setInstrumentRefreshKey]=useState(0);const openInstrumentSelector=trackId=>{setInstrumentSelectorTrackId(trackId);setSfPresetSearchQuery('');setInstrumentRefreshKey(k=>k+1);setSfPresets(null);// Fetch instruments fresh from API for all soundfonts +const bars=Math.max(0.25,Math.round(m.duration/secondsPerBar*4)/4);return{...m,length_bars:bars,duration:bars*secondsPerBar};}return m;}),sections:(t.sections||[]).map(s=>{if(s.length_bars)return{...s,duration:s.length_bars*secondsPerBar};if(s.duration){const bars=Math.max(0.25,Math.round(s.duration/secondsPerBar*4)/4);return{...s,length_bars:bars,duration:bars*secondsPerBar};}return s;})})));},[bpm]);// LAN badge removed per request: server IP now shown in SERVER label. +const openPanel=id=>{if(id==='export')setShowExportPanel(true);else if(id==='ai')setShowAIPanel(true);else if(id==='python_tools')setShowPythonToolsPanel(true);else if(id==='selection')setShowSelectionPanel(true);};const[instrumentSelectorTrackId,setInstrumentSelectorTrackId]=useState(null);const[synthTrackDropdownId,setSynthTrackDropdownId]=useState(null);const[fxSelectorTrackId,setFxSelectorTrackId]=useState(null);const handleSetTrackFx=(trackId,fxType)=>{updateActiveTracks(prev=>prev.map(t=>t.id===trackId?{...t,fxType}:t));setFxSelectorTrackId(null);setTimeout(()=>lucide.createIcons(),50);};const[instrumentSelectorData,setInstrumentSelectorData]=useState(null);const[instrumentRefreshKey,setInstrumentRefreshKey]=useState(0);const openInstrumentSelector=trackId=>{setInstrumentSelectorTrackId(trackId);setSfPresetSearchQuery('');setInstrumentRefreshKey(k=>k+1);setSfPresets(null);// Fetch instruments fresh from API for all soundfonts window.SonicAPI.listPlugins().then(async data=>{var sfonts=data.soundfonts||[];if(sfonts.length===0){setSfPresets([]);return;}try{var results=await Promise.all(sfonts.map(function(sf){var sfId=sf.id.replace('sf_','');return window.SonicAPI.listSoundfontInstruments(sfId).then(function(r){return{sf:sf,presets:r.presets||[]};}).catch(function(){return{sf:sf,presets:[]};});}));var all=[];results.forEach(function(r){var sf=r.sf;var sfId=sf.id.startsWith('sf_')?sf.id:'sf_'+sf.id;var sfName=sf.display||sf.name||sf.id;(r.presets||[]).forEach(function(p){all.push({...p,_sfId:sfId,_sfName:sfName,_sfDisplay:sfName.substring(0,30)});});});setSfPresets(all.length>0?all:[]);}catch(e){setSfPresets([]);}}).catch(function(){setSfPresets([]);});};const closeInstrumentSelector=()=>{setInstrumentSelectorTrackId(null);setSynthCategory(null);setSelectedSoundFontId(null);setSfPresetSearchQuery('');};const[synthCategory,setSynthCategory]=useState(null);// 'vst' | 'soundfont' const[selectedSoundFontId,setSelectedSoundFontId]=useState(null);const[sfPresets,setSfPresets]=useState(null);// presets from SoundFont const[instrumentDropdownTrackId,setInstrumentDropdownTrackId]=useState(null);const[instrumentDropdownBtnRect,setInstrumentDropdownBtnRect]=useState(null);const[instrumentSearchQuery,setInstrumentSearchQuery]=useState('');const[sfPresetSearchQuery,setSfPresetSearchQuery]=useState('');const filteredInstruments=useMemo(()=>{if(!instrumentSelectorData||!instrumentSearchQuery)return{soundfonts:instrumentSelectorData?.soundfonts||[],vst:instrumentSelectorData?.vst_instruments||[]};const q=instrumentSearchQuery.toLowerCase();return{soundfonts:(instrumentSelectorData.soundfonts||[]).filter(sf=>(sf.display||sf.name||sf.id).toLowerCase().includes(q)),vst:(instrumentSelectorData.vst_instruments||[]).filter(v=>(v.name||v.id).toLowerCase().includes(q))};},[instrumentSearchQuery,instrumentSelectorData]);useEffect(()=>{window.SonicAPI.listPlugins().then(async data=>{try{const catResp=(await window.SonicAPI.getSoundfontCatalog?.())??(await fetch('/api/v1/plugins/soundfonts/catalog').then(r=>r.json()));const catalog=catResp.full_catalog||{};data.soundfonts=(data.soundfonts||[]).map(sf=>{const sfId=sf.id.replace('sf_','');const catEntry=catalog[sfId.toLowerCase()]||catalog[sfId];if(catEntry&&catEntry.instruments){return{...sf,presets:catEntry.instruments};}return sf;});}catch(e){console.warn('Catalog fetch error:',e);}setInstrumentSelectorData(data);}).catch(()=>{});},[instrumentRefreshKey]);useEffect(()=>{if(!instrumentDropdownTrackId)return;const handler=e=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setInstrumentSearchQuery('');};document.addEventListener('click',handler);return()=>document.removeEventListener('click',handler);},[instrumentDropdownTrackId]);const GM_INSTRUMENTS=["Acoustic Grand Piano","Bright Acoustic Piano","Electric Grand Piano","Honky-tonk Piano","Electric Piano 1","Electric Piano 2","Harpsichord","Clavi","Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer","Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accordion","Harmonica","Tango Accordion","Acoustic Guitar (nylon)","Acoustic Guitar (steel)","Electric Guitar (jazz)","Electric Guitar (clean)","Electric Guitar (muted)","Overdriven Guitar","Distortion Guitar","Guitar harmonics","Acoustic Bass","Electric Bass (finger)","Electric Bass (pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Harp","Timpani","String Ensemble 1","String Ensemble 2","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet","Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Shakuhachi","Whistle","Ocarina","Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass+lead)","Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)","FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag pipe","Fiddle","Shanai","Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal","Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"];// D7: bridge active → load instrument vào bridge tại channel router dùng cho @@ -919,7 +917,7 @@ if(isMidiSoundTrack(t)){try{sendMidiVolumeCC7(t,effective,audible);}catch(e){}}i // already uses for solo. if(becameAudible&&isPlaying&&recordingStateRef.current!=='RECORDING'&&activeTracksRef.current&&activeTracksRef.current.length){try{stopAllPlayback();const ctx=getAudioContext();const resumeAt=currentTimeRef.current||0;startOffsetTimeRef.current=resumeAt;startAudioTimeRef.current=ctx.currentTime;startBufferOffsetRef.current=resumeAt;setIsPlaying(true);startTrackPlayback(resumeAt);}catch(e){console.warn('mute/solo resume error:',e);}}updateSfRouting();};window.__applyTrackMuteSolo=applyAllTrackMuteSolo;window.__toggleMediaExplorerRef=function(){setShowMediaExplorer(function(p){const next=!p;if(next)setShowMixerRef.current(false);return next;});};const[mixerHeight,setMixerHeight]=useState(function(){var saved=localStorage.getItem('studio_mixer_height');return saved?parseInt(saved):200;}());const[mediaExplorerPanelHeight,setMediaExplorerPanelHeight]=useState(function(){var saved=localStorage.getItem('studio_media_explorer_height');return saved?parseInt(saved):240;}());const[masterVolume,setMasterVolume]=useState(0);// dB const[masterVU,setMasterVU]=useState(0);// 0-1 -const[masterMeterPeak,setMasterMeterPeak]=useState(0);const[rightSidebarWidth,setRightSidebarWidth]=useState(320);const[tcpWidth,setTcpWidth]=useState(320);const[mediaExplorerHeight,setMediaExplorerHeight]=useState(50);const[panelPositions,setPanelPositions]=useState({export:'bottom',ai:'right',python_tools:'bottom',selection:'bottom',media_explorer:'bottom',fx_rack:'bottom',midi_events:'bottom'});const[panelDropZone,setPanelDropZone]=useState(null);const[dragGhostPos,setDragGhostPos]=useState(null);const[dragGhostPanel,setDragGhostPanel]=useState(null);const panelDragRef=useRef(null);const trackVuRefs=useRef({});const workspaceRef=useRef(null);const colResizerRef=useRef(null);const rowResizerRef=useRef(null);const[aiConfig,setAiConfig]=useState({baseUrl:localStorage.getItem('ai_base_url')||`${API_BASE_URL}`,apiKey:localStorage.getItem('ai_api_key')||'',model:localStorage.getItem('ai_model')||'deepseek-chat'});const[aiProviders,setAiProviders]=useState([]);useEffect(()=>{(async()=>{try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setAiProviders(data.providers);const active=data.providers.find(p=>p.is_active)||data.providers[0];if(active)setSelectedProviderId(active.id);}}catch(e){/* server may not have config endpoint */}})();},[]);const[analysisState,setAnalysisState]=useState({status:'Sẵn sàng. Chạy AI để phân tích nhịp.',data:null,isRunning:false});const[aiPrompt,setAiPrompt]=useState('');const[aiComposeMode,setAiComposeMode]=useState('SIMILAR_VARIATION');const[promptHistory,setPromptHistory]=useState([]);const[promptHistIdx,setPromptHistIdx]=useState(-1);const promptHistRef=useRef([]);const aiPromptUndoRef=useRef({stack:[],idx:-1,max:30});const aiPromptUndoPush=text=>{const u=aiPromptUndoRef.current;u.stack.push(text);if(u.stack.length>u.max)u.stack.shift();u.idx=u.stack.length-1;};const[aiProvider,setAiProvider]=useState('OpenAI');const[aiModel,setAiModel]=useState('GPT-4o');const[aiActionLog,setAiActionLog]=useState([]);const actionLogContainerRef=useRef(null);useEffect(()=>{if(actionLogContainerRef.current){actionLogContainerRef.current.scrollTop=actionLogContainerRef.current.scrollHeight;}},[aiActionLog]);const[aiProcessing,setAiProcessing]=useState(false);const[aiSuggestions,setAiSuggestions]=useState([]);const[showAiTypeahead,setShowAiTypeahead]=useState(false);const[showAISuggestions,setShowAISuggestions]=useState(true);const[showAIActionLog,setShowAIActionLog]=useState(false);const aiPromptMgrRef=useRef(null);const aiTypeaheadRef=useRef(null);const[selectedProviderId,setSelectedProviderId]=useState('');const[exportSettings,setExportSettings]=useState({sampleRate:'44100',bitDepth:'16',format:'wav',source:'project',quality:'44khz',channels:'stereo'});const[serverStatus,setServerStatus]=useState('checking...');const[menuOpen,setMenuOpen]=useState(null);const[selectedClipId,setSelectedClipId]=useState(null);// { trackId, clipId } +const[masterMeterPeak,setMasterMeterPeak]=useState(0);const[rightSidebarWidth,setRightSidebarWidth]=useState(320);const[tcpWidth,setTcpWidth]=useState(320);const[mediaExplorerHeight,setMediaExplorerHeight]=useState(50);const[panelPositions,setPanelPositions]=useState({export:'bottom',ai:'right',python_tools:'bottom',selection:'bottom',media_explorer:'bottom',fx_rack:'bottom',midi_events:'bottom'});const[panelDropZone,setPanelDropZone]=useState(null);const[dragGhostPos,setDragGhostPos]=useState(null);const[dragGhostPanel,setDragGhostPanel]=useState(null);const panelDragRef=useRef(null);const trackVuRefs=useRef({});const workspaceRef=useRef(null);const colResizerRef=useRef(null);const rowResizerRef=useRef(null);const[aiConfig,setAiConfig]=useState({baseUrl:localStorage.getItem('ai_base_url')||`${API_BASE_URL}`,apiKey:localStorage.getItem('ai_api_key')||'',model:localStorage.getItem('ai_model')||'deepseek-chat'});const[aiProviders,setAiProviders]=useState([]);useEffect(()=>{(async()=>{try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setAiProviders(data.providers);const active=data.providers.find(p=>p.is_active)||data.providers[0];if(active)setSelectedProviderId(active.id);}}catch(e){/* server may not have config endpoint */}})();},[]);const[analysisState,setAnalysisState]=useState({status:'Sẵn sàng. Chạy AI để phân tích nhịp.',data:null,isRunning:false});const[aiPrompt,setAiPrompt]=useState('');const[aiComposeMode,setAiComposeMode]=useState('SIMILAR_VARIATION');const[promptHistory,setPromptHistory]=useState([]);const[promptHistIdx,setPromptHistIdx]=useState(-1);const promptHistRef=useRef([]);const aiPromptUndoRef=useRef({stack:[],idx:-1,max:30});const aiPromptUndoPush=text=>{const u=aiPromptUndoRef.current;u.stack.push(text);if(u.stack.length>u.max)u.stack.shift();u.idx=u.stack.length-1;};const[aiProvider,setAiProvider]=useState('OpenAI');const[aiModel,setAiModel]=useState('GPT-4o');const[aiActionLog,setAiActionLog]=useState([]);const actionLogContainerRef=useRef(null);useEffect(()=>{if(actionLogContainerRef.current){actionLogContainerRef.current.scrollTop=actionLogContainerRef.current.scrollHeight;}},[aiActionLog]);const[aiProcessing,setAiProcessing]=useState(false);const[aiSuggestions,setAiSuggestions]=useState([]);const[showAiTypeahead,setShowAiTypeahead]=useState(false);const[showAISuggestions,setShowAISuggestions]=useState(true);const[showAIActionLog,setShowAIActionLog]=useState(false);const aiPromptMgrRef=useRef(null);const aiTypeaheadRef=useRef(null);const[selectedProviderId,setSelectedProviderId]=useState('');const[exportSettings,setExportSettings]=useState({sampleRate:'44100',bitDepth:'16',format:'wav',source:'project',quality:'44khz',channels:'stereo'});const[serverStatus,setServerStatus]=useState('checking...');const _lan=window.SonicRuntime&&window.SonicRuntime.capabilities&&window.SonicRuntime.capabilities.lan;const serverHost=_lan&&_lan.urls&&_lan.urls[0]?function(){try{return new URL(_lan.urls[0]).hostname;}catch(e){return null;}}():window.location.hostname;const[menuOpen,setMenuOpen]=useState(null);const[selectedClipId,setSelectedClipId]=useState(null);// { trackId, clipId } const[stretchedClip,setStretchedClip]=useState(null);// { trackId, clipId, originalDuration, startTime, originalSpeed, beforeSnap } const[draggedSectionItem,setDraggedSectionItem]=useState(null);const[resizedSectionItem,setResizedSectionItem]=useState(null);const[editingTrackName,setEditingTrackName]=useState(null);// trackId being edited const[editingClipName,setEditingClipName]=useState(null);// { trackId, clipId } @@ -1894,7 +1892,7 @@ trackMidiChannelsRef.current={};Object.keys(trackMidiBypassMap).forEach(function // MAIN phải lưu cả piano roll/audio tab đã sửa) handleSaveProject();subTabsRef.current.filter(s=>s.isDirty).forEach(st=>{if(st.type==='PIANO_ROLL'){handleSaveMidiNotes(st.id,st.trackId,st.target_id,st.notes||[]);}else if(st.type==='SECTION'){handleSaveSectionTab(st.id);}else if(st.buffer){const subTrack=activeTracksRef.current.find(t=>t.id===st.trackId);if(subTrack){updateActiveTracks(prev=>prev.map(t=>t.id===st.trackId?{...t,buffer:st.buffer}:t));}}});showToast('Đã lưu dự án + các tab đã sửa','success');}},{label:'Save As...',icon:'download',shortcut:'Ctrl+Alt+S',action:()=>setSaveAsModalOpen(true)},{sep:true},{label:'Config AI Providers...',icon:'settings',action:()=>setAiConfigModalOpen(true)},{label:'Import Audio...',icon:'file-input',shortcut:'Ctrl+Alt+I',action:()=>{const input=document.createElement('input');input.type='file';input.accept='audio/*';input.onchange=async e=>{if(e.target.files[0]){addNewTrack();const rearrangeNewId=(tracks.length+1).toString();setTimeout(()=>loadFileOnTrack(rearrangeNewId,e.target.files[0]),100);}};input.click();showToast('Import audio','info');}},{label:'Export Mix...',icon:'file-output',action:()=>triggerWavExport()},{label:'Export MIDI...',icon:'music',action:()=>triggerMidiExport()},{label:'Mastering Suite',icon:'wand-2',shortcut:'Ctrl+Shift+M',action:()=>setShowMasteringModal(true)},{sep:true},...(currentUser?[{label:'Profile',icon:'user',action:()=>setProfileModalOpen(true)}]:[]),...(currentUser&¤tUser.role==='admin'?[{label:'System Manager',icon:'settings',action:()=>setSystemManagerModalOpen(true)}]:[]),{label:'Logout',icon:'log-out',action:()=>handleLogout()}]},{label:'Edit',items:[{label:'Insert New Track',icon:'plus',shortcut:'Ctrl+I',action:addNewTrack},{label:'Insert Music to Track',icon:'music',shortcut:'Ctrl+Alt+I',action:()=>showToast('Select music file to insert','info')},{sep:true},{label:'Edit in New Tab',icon:'file-edit',shortcut:'Ctrl+E',action:()=>openTempTab()},{label:'Split at Playhead',icon:'scissors',shortcut:'S',action:()=>handleSplitTrack(selectedTrackId)},{label:'Merge Tracks',icon:'combine',shortcut:'Ctrl+M',action:()=>{handleMergeTracks();}},{sep:true},{label:'Undo',icon:'undo',shortcut:'Ctrl+Z',action:()=>{handleUndo();}},{label:'Redo',icon:'redo',shortcut:'Ctrl+Y',action:()=>{handleRedo();}},{sep:true},{label:'Copy',icon:'copy',shortcut:'Ctrl+C',action:()=>{handleCopyTrack();}},{label:'Cut',icon:'scissors',shortcut:'Ctrl+X',action:()=>{handleCutTrack();}},{label:'Paste',icon:'clipboard',shortcut:'Ctrl+V',action:handlePasteTrack},{sep:true},{label:'Delete Track',icon:'trash-2',shortcut:'Del',action:()=>{handleDeleteTrack();}}]},{label:'Insert',items:[...(!sessionTabs.some(s=>s.id===activeTab)?[{label:'Insert Section',icon:'folder-plus',action:insertSectionAtPlayhead}]:[]),{label:'Insert MIDI item',icon:'music',action:insertMidiItemAtPlayhead},{label:'Insert sound clip',icon:'file-input',action:insertSoundClipAtCursor},{label:'Insert track',icon:'plus',action:insertTrackBelow}]},{label:'View',items:[{label:'Master Track',icon:'disc',action:()=>showToast('Master track view','info')},{label:'Maker View',icon:'layout',action:()=>showToast('Maker view','info')},{label:'Mixer',icon:'sliders',action:()=>window.__toggleMixerRef()},{label:'Tempo Track',icon:'timer',action:()=>showToast('Tempo track','info')},{label:'Video',icon:'film',action:()=>showToast('Video panel','info')},{label:'Media Explorer',icon:'folder-search',action:()=>window.__toggleMediaExplorerRef()}]},{label:'Tools',items:[{label:'Config AI Providers...',icon:'settings',action:()=>setAiConfigModalOpen(true)},{label:'AI MIDI Preset Manager...',icon:'sliders',action:()=>setAiPresetModalOpen(true)},{label:'DSP Tools Panel',icon:'wrench',action:()=>{// DSP Tool đã được move vào SUB-TAB editor (audioclip). Mở sub-tab // edit cho track/clip đang chọn — nếu chưa có clip → mở panel cũ. -const t=activeTracks.find(x=>x.id===selectedTrackId);if(t&&(t.buffer||t.clips&&t.clips.length)){const clipId=t.clips&&t.clips[0]?t.clips[0].id:'default';handleEditClipInSubTab(t.id,clipId);}else{openPanel('python_tools');}}},{sep:true},{label:'Plugin Manager (SoundFont/VSTi)',icon:'zap',action:()=>{setPluginManagerModalOpen(true);window.SonicAPI.listPlugins().then(data=>setPluginsData(data)).catch(()=>{});}},{sep:true},{label:'Preferences...',icon:'settings-2',action:()=>setPreferencesModalOpen(true)}]},{label:'Help',items:[{label:'Hướng dẫn sử dụng...',icon:'book-open',action:()=>setHelpModalOpen(true)},{sep:true},{label:'About SonicForge Studio...',icon:'info',action:()=>setAboutModalOpen(true)}]}].map(menu=>/*#__PURE__*/React.createElement("div",{key:menu.label,className:"relative"},/*#__PURE__*/React.createElement("button",{onClick:()=>setMenuOpen(menuOpen===menu.label?null:menu.label),className:`px-3 py-1 text-xs font-medium transition rounded ${menuOpen===menu.label?'bg-zinc-700 text-zinc-100':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`},menu.label),menuOpen===menu.label&&/*#__PURE__*/React.createElement("div",{className:`absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 z-50 ${menu.label==='Edit'?'w-72':'w-64'}`,onClick:()=>setMenuOpen(null)},menu.items.map((item,i)=>item.sep?/*#__PURE__*/React.createElement("div",{key:i,className:"h-px bg-zinc-700 my-1"}):/*#__PURE__*/React.createElement("button",{key:item.label,onClick:e=>{e.stopPropagation();item.action();setMenuOpen(null);},className:"w-full px-3 py-1.5 text-xs text-zinc-200 hover:bg-zinc-700 text-left flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":item.icon,className:"w-3.5 h-3.5 text-zinc-500 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},item.label),item.shortcut&&/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},item.shortcut)))))),/*#__PURE__*/React.createElement("div",{className:"flex-1"}),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 px-2"},/*#__PURE__*/React.createElement("span",{className:`text-xs font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus==='connected'?'bg-emerald-950 text-emerald-400':serverStatus==='checking'?'bg-amber-950 text-amber-400':'bg-red-950 text-red-400'}`},"Server: ",serverStatus),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowAIConfig(!showAIConfig),className:`px-1.5 py-0.5 rounded text-xs border transition ${showAIConfig?'bg-purple-900 text-purple-200 border-purple-700':'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3 h-3"}))))),menuOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-40",onClick:()=>setMenuOpen(null)}),/*#__PURE__*/React.createElement("div",{className:"h-7 bg-[#222] border-b border-zinc-800 flex items-stretch px-2 gap-0.5 shrink-0 overflow-x-auto"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab('main'),className:`px-3 text-xs font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab==='main'?'text-cyan-400 border-cyan-500 bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"layout-dashboard",className:"w-3 h-3"}))," Main Session"),sessionTabs.map(st=>{const isTabActive=activeTab===st.id;const tabColor=st.color||(isTabActive?'#06b6d4':null);const borderStyle=isTabActive?{borderBottomColor:tabColor,color:tabColor}:st.color?{color:st.color,borderBottomColor:'transparent'}:{};return/*#__PURE__*/React.createElement("div",{key:st.id,className:"flex items-stretch"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab(st.id),onAuxClick:e=>{if(e.button===1){e.preventDefault();closeSessionTab(st.id);}},onContextMenu:e=>{e.preventDefault();setTabContextMenu({x:e.clientX,y:e.clientY,tabId:st.id,tabType:'session'});},style:borderStyle,className:`px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${isTabActive?'bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"layers",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"max-w-[120px] truncate"},st.name),st.isDirty&&/*#__PURE__*/React.createElement("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-500 ml-1",title:"Chưa lưu thay đổi"})),/*#__PURE__*/React.createElement("button",{onClick:()=>closeSessionTab(st.id),className:"px-1 text-zinc-600 hover:text-red-400 transition text-xs",title:"Close tab"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));}),subTabs.map(st=>{const isTabActive=activeTab===st.id;const tabColor=st.color||(isTabActive?'#f59e0b':null);const borderStyle=isTabActive?{borderBottomColor:tabColor,color:tabColor}:st.color?{color:st.color,borderBottomColor:'transparent'}:{};const iconName=st.type==='PIANO_ROLL'?'music':'file-edit';return/*#__PURE__*/React.createElement("div",{key:st.id,className:"flex items-stretch"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab(st.id),onAuxClick:e=>{if(e.button===1){e.preventDefault();closeSubTab(st.id);}},onContextMenu:e=>{e.preventDefault();setTabContextMenu({x:e.clientX,y:e.clientY,tabId:st.id,tabType:'sub'});},style:borderStyle,className:`px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${isTabActive?'bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":iconName,className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"max-w-[100px] truncate"},st.label),st.isDirty&&/*#__PURE__*/React.createElement("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-500 ml-1",title:"Chưa lưu thay đổi"})),/*#__PURE__*/React.createElement("button",{onClick:()=>closeSubTab(st.id),className:"px-1 text-zinc-600 hover:text-red-400 transition text-xs",title:"Close tab"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));})),showAIConfig&&/*#__PURE__*/React.createElement("div",{className:"bg-zinc-900 border-b border-purple-900 p-3 flex flex-col gap-2 transition-all"},/*#__PURE__*/React.createElement("div",{className:"text-xs font-bold text-purple-400 uppercase tracking-wider flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-4 h-4"}))," Cấu hình cổng kết nối API"),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-2 text-xs"},/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"Endpoint Base URL"),/*#__PURE__*/React.createElement("input",{type:"text",value:aiConfig.baseUrl,onChange:e=>setAiConfig(prev=>({...prev,baseUrl:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"https://api.openai.com/v1"})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"API Token Key"),/*#__PURE__*/React.createElement("input",{type:"password",value:aiConfig.apiKey,onChange:e=>setAiConfig(prev=>({...prev,apiKey:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"sk-..."})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"Model Name"),/*#__PURE__*/React.createElement("input",{type:"text",value:aiConfig.model,onChange:e=>setAiConfig(prev=>({...prev,model:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"gpt-4o-mini"})))),/*#__PURE__*/React.createElement("div",{className:"h-9 bg-[#222] border-b border-zinc-800 flex items-center px-3 gap-2 shrink-0 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 shadow-lg",title:"Kéo để di chuyển toolbar",style:{cursor:'grab'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-0.5 mr-1 text-zinc-600"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('select');showToast('Select Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='select'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Select Tool (V)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"mouse-pointer",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('grab');showToast('Grab Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='grab'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Grab Tool (H)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"hand",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-0.5 border border-zinc-700 rounded bg-zinc-850 px-0.5"},/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('razor');showToast('Razor Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='razor'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Razor Tool (C)"},/*#__PURE__*/React.createElement("svg",{className:"w-3.5 h-3.5 text-orange-400",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"},/*#__PURE__*/React.createElement("path",{d:"M6 3h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"}),/*#__PURE__*/React.createElement("path",{d:"M4 9h16l-3 9H7z"}),/*#__PURE__*/React.createElement("circle",{cx:"12",cy:"6",r:"1"}))),/*#__PURE__*/React.createElement("button",{onClick:handleGlueTracks,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-purple-400 hover:bg-zinc-800",title:"Glue Clips"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"link",className:"w-3.5 h-3.5"})))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('pen');showToast('Pen Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='pen'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Pen Tool (P)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"pen-tool",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:handleCutTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-red-400 hover:bg-zinc-800",title:"Cut (Ctrl+X)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"scissors",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handleCopyTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-blue-400 hover:bg-zinc-800",title:"Copy (Ctrl+C)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"copy",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handlePasteTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-emerald-400 hover:bg-zinc-800",title:"Paste (Ctrl+V)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"clipboard",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:addNewTrack,className:"px-2 py-1 bg-cyan-600 hover:bg-cyan-500 text-white font-semibold rounded text-xs flex items-center gap-1 shadow transition",title:"Thêm Track Mới (Ctrl+I)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("span",null,"Track")),sessionTabs.some(s=>s.id===activeTab)&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSaveSectionTab(activeTab),className:"px-2 py-1 bg-emerald-600 hover:bg-emerald-500 text-white font-semibold rounded text-xs flex items-center gap-1 shadow transition",title:"Lưu Section vào Main Session"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"save",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("span",null,"Lưu Section"))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:handleUndo,disabled:undoStack.length===0&&(!window.UndoRedoEngine||!window.UndoRedoEngine.canUndo()),className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 disabled:opacity-30",title:"Undo (Ctrl+Z)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"undo",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handleRedo,disabled:redoStack.length===0&&(!window.UndoRedoEngine||!window.UndoRedoEngine.canRedo()),className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 disabled:opacity-30",title:"Redo (Ctrl+Y)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"redo",className:"w-3.5 h-3.5"})))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-800 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>seekPlaybackTo(0),className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Quay lại đầu"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"skip-back",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){const st=subTabs.find(s=>s.id===activeTab);if(!st)return;const left=st.selectionStart!==null&&st.selectionEnd!==null?Math.min(st.selectionStart,st.selectionEnd):null;if(left!==null)seekPlaybackTo(left);}else{if(selLeft!==null)seekPlaybackTo(selLeft);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Đầu vùng chọn"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"step-back",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handlePlayPause,className:`w-7 h-7 flex items-center justify-center rounded border transition ${isPlaying?'bg-emerald-600 text-black border-emerald-500 hover:bg-emerald-500':'bg-cyan-600 text-white border-cyan-500 hover:bg-cyan-500'}`,title:isPlaying?"Tạm dừng":"Play"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":isPlaying?"pause":"play",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:handleStop,className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Stop"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"square",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:handleRecordClick,className:`w-7 h-7 flex items-center justify-center rounded border transition ${recordingState==='RECORDING'?'bg-red-600 text-white border-red-500 hover:bg-red-500 animate-pulse':recordingState==='COUNT_IN'?'bg-amber-500 text-black border-amber-400 hover:bg-amber-400 animate-pulse':'bg-zinc-800 text-red-500 border-zinc-700 hover:bg-zinc-700 hover:text-red-400'}`,title:recordingState==='RECORDING'?"Đang ghi âm...":recordingState==='COUNT_IN'?"Chuẩn bị ghi âm...":"Ghi âm (Record)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"circle",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){setSubTabs(prev=>prev.map(s=>{if(s.id!==activeTab)return s;const right=s.selectionStart!==null&&s.selectionEnd!==null?Math.max(s.selectionStart,s.selectionEnd):null;return right!==null?{...s,currentTime:right}:s;}));}else{if(selRight!==null)setCurrentTime(selRight);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Cuối vùng chọn"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"step-forward",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){setSubTabs(prev=>prev.map(s=>{if(s.id!==activeTab)return s;const duration=s.buffer?s.buffer.duration/(s.speed||1.0):0;return{...s,currentTime:duration};}));}else{setCurrentTime(maxDuration);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Đến cuối"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"skip-forward",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-800 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{setIsLoopingSelection(prev=>!prev);// Sync loop state with active sub-tab (piano roll / audio editor) +const t=activeTracks.find(x=>x.id===selectedTrackId);if(t&&(t.buffer||t.clips&&t.clips.length)){const clipId=t.clips&&t.clips[0]?t.clips[0].id:'default';handleEditClipInSubTab(t.id,clipId);}else{openPanel('python_tools');}}},{sep:true},{label:'Plugin Manager (SoundFont/VSTi)',icon:'zap',action:()=>{setPluginManagerModalOpen(true);window.SonicAPI.listPlugins().then(data=>setPluginsData(data)).catch(()=>{});}},{sep:true},{label:'Preferences...',icon:'settings-2',action:()=>setPreferencesModalOpen(true)}]},{label:'Help',items:[{label:'Hướng dẫn sử dụng...',icon:'book-open',action:()=>setHelpModalOpen(true)},{sep:true},{label:'About SonicForge Studio...',icon:'info',action:()=>setAboutModalOpen(true)}]}].map(menu=>/*#__PURE__*/React.createElement("div",{key:menu.label,className:"relative"},/*#__PURE__*/React.createElement("button",{onClick:()=>setMenuOpen(menuOpen===menu.label?null:menu.label),className:`px-3 py-1 text-xs font-medium transition rounded ${menuOpen===menu.label?'bg-zinc-700 text-zinc-100':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`},menu.label),menuOpen===menu.label&&/*#__PURE__*/React.createElement("div",{className:`absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 z-50 ${menu.label==='Edit'?'w-72':'w-64'}`,onClick:()=>setMenuOpen(null)},menu.items.map((item,i)=>item.sep?/*#__PURE__*/React.createElement("div",{key:i,className:"h-px bg-zinc-700 my-1"}):/*#__PURE__*/React.createElement("button",{key:item.label,onClick:e=>{e.stopPropagation();item.action();setMenuOpen(null);},className:"w-full px-3 py-1.5 text-xs text-zinc-200 hover:bg-zinc-700 text-left flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":item.icon,className:"w-3.5 h-3.5 text-zinc-500 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},item.label),item.shortcut&&/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},item.shortcut)))))),/*#__PURE__*/React.createElement("div",{className:"flex-1"}),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 px-2"},/*#__PURE__*/React.createElement("span",{className:`text-xs font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus==='connected'?'bg-emerald-950 text-emerald-400':serverStatus==='checking'?'bg-amber-950 text-amber-400':'bg-red-950 text-red-400'}`},"Server: ",serverStatus,serverHost?" · "+serverHost:""),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowAIConfig(!showAIConfig),className:`px-1.5 py-0.5 rounded text-xs border transition ${showAIConfig?'bg-purple-900 text-purple-200 border-purple-700':'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3 h-3"}))))),menuOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-40",onClick:()=>setMenuOpen(null)}),/*#__PURE__*/React.createElement("div",{className:"h-7 bg-[#222] border-b border-zinc-800 flex items-stretch px-2 gap-0.5 shrink-0 overflow-x-auto"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab('main'),className:`px-3 text-xs font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab==='main'?'text-cyan-400 border-cyan-500 bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"layout-dashboard",className:"w-3 h-3"}))," Main Session"),sessionTabs.map(st=>{const isTabActive=activeTab===st.id;const tabColor=st.color||(isTabActive?'#06b6d4':null);const borderStyle=isTabActive?{borderBottomColor:tabColor,color:tabColor}:st.color?{color:st.color,borderBottomColor:'transparent'}:{};return/*#__PURE__*/React.createElement("div",{key:st.id,className:"flex items-stretch"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab(st.id),onAuxClick:e=>{if(e.button===1){e.preventDefault();closeSessionTab(st.id);}},onContextMenu:e=>{e.preventDefault();setTabContextMenu({x:e.clientX,y:e.clientY,tabId:st.id,tabType:'session'});},style:borderStyle,className:`px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${isTabActive?'bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"layers",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"max-w-[120px] truncate"},st.name),st.isDirty&&/*#__PURE__*/React.createElement("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-500 ml-1",title:"Chưa lưu thay đổi"})),/*#__PURE__*/React.createElement("button",{onClick:()=>closeSessionTab(st.id),className:"px-1 text-zinc-600 hover:text-red-400 transition text-xs",title:"Close tab"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));}),subTabs.map(st=>{const isTabActive=activeTab===st.id;const tabColor=st.color||(isTabActive?'#f59e0b':null);const borderStyle=isTabActive?{borderBottomColor:tabColor,color:tabColor}:st.color?{color:st.color,borderBottomColor:'transparent'}:{};const iconName=st.type==='PIANO_ROLL'?'music':'file-edit';return/*#__PURE__*/React.createElement("div",{key:st.id,className:"flex items-stretch"},/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab(st.id),onAuxClick:e=>{if(e.button===1){e.preventDefault();closeSubTab(st.id);}},onContextMenu:e=>{e.preventDefault();setTabContextMenu({x:e.clientX,y:e.clientY,tabId:st.id,tabType:'sub'});},style:borderStyle,className:`px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${isTabActive?'bg-zinc-800/50':'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":iconName,className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"max-w-[100px] truncate"},st.label),st.isDirty&&/*#__PURE__*/React.createElement("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-500 ml-1",title:"Chưa lưu thay đổi"})),/*#__PURE__*/React.createElement("button",{onClick:()=>closeSubTab(st.id),className:"px-1 text-zinc-600 hover:text-red-400 transition text-xs",title:"Close tab"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));})),showAIConfig&&/*#__PURE__*/React.createElement("div",{className:"bg-zinc-900 border-b border-purple-900 p-3 flex flex-col gap-2 transition-all"},/*#__PURE__*/React.createElement("div",{className:"text-xs font-bold text-purple-400 uppercase tracking-wider flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-4 h-4"}))," Cấu hình cổng kết nối API"),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-2 text-xs"},/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"Endpoint Base URL"),/*#__PURE__*/React.createElement("input",{type:"text",value:aiConfig.baseUrl,onChange:e=>setAiConfig(prev=>({...prev,baseUrl:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"https://api.openai.com/v1"})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"API Token Key"),/*#__PURE__*/React.createElement("input",{type:"password",value:aiConfig.apiKey,onChange:e=>setAiConfig(prev=>({...prev,apiKey:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"sk-..."})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase"},"Model Name"),/*#__PURE__*/React.createElement("input",{type:"text",value:aiConfig.model,onChange:e=>setAiConfig(prev=>({...prev,model:e.target.value})),className:"bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs",placeholder:"gpt-4o-mini"})))),/*#__PURE__*/React.createElement("div",{className:"h-9 bg-[#222] border-b border-zinc-800 flex items-center px-3 gap-2 shrink-0 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 shadow-lg",title:"Kéo để di chuyển toolbar",style:{cursor:'grab'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-0.5 mr-1 text-zinc-600"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('select');showToast('Select Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='select'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Select Tool (V)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"mouse-pointer",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('grab');showToast('Grab Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='grab'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Grab Tool (H)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"hand",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-0.5 border border-zinc-700 rounded bg-zinc-850 px-0.5"},/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('razor');showToast('Razor Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='razor'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Razor Tool (C)"},/*#__PURE__*/React.createElement("svg",{className:"w-3.5 h-3.5 text-orange-400",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"},/*#__PURE__*/React.createElement("path",{d:"M6 3h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"}),/*#__PURE__*/React.createElement("path",{d:"M4 9h16l-3 9H7z"}),/*#__PURE__*/React.createElement("circle",{cx:"12",cy:"6",r:"1"}))),/*#__PURE__*/React.createElement("button",{onClick:handleGlueTracks,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-purple-400 hover:bg-zinc-800",title:"Glue Clips"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"link",className:"w-3.5 h-3.5"})))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setActiveTool('pen');showToast('Pen Tool','info');},className:`w-7 h-7 flex items-center justify-center rounded ${activeTool==='pen'?'bg-cyan-700 text-white':'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`,title:"Pen Tool (P)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"pen-tool",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:handleCutTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-red-400 hover:bg-zinc-800",title:"Cut (Ctrl+X)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"scissors",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handleCopyTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-blue-400 hover:bg-zinc-800",title:"Copy (Ctrl+C)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"copy",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handlePasteTrack,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-emerald-400 hover:bg-zinc-800",title:"Paste (Ctrl+V)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"clipboard",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:addNewTrack,className:"px-2 py-1 bg-cyan-600 hover:bg-cyan-500 text-white font-semibold rounded text-xs flex items-center gap-1 shadow transition",title:"Thêm Track Mới (Ctrl+I)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("span",null,"Track")),sessionTabs.some(s=>s.id===activeTab)&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSaveSectionTab(activeTab),className:"px-2 py-1 bg-emerald-600 hover:bg-emerald-500 text-white font-semibold rounded text-xs flex items-center gap-1 shadow transition",title:"Lưu Section vào Main Session"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"save",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("span",null,"Lưu Section"))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-700 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:handleUndo,disabled:undoStack.length===0&&(!window.UndoRedoEngine||!window.UndoRedoEngine.canUndo()),className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 disabled:opacity-30",title:"Undo (Ctrl+Z)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"undo",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handleRedo,disabled:redoStack.length===0&&(!window.UndoRedoEngine||!window.UndoRedoEngine.canRedo()),className:"w-7 h-7 flex items-center justify-center rounded text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 disabled:opacity-30",title:"Redo (Ctrl+Y)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"redo",className:"w-3.5 h-3.5"})))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-800 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>seekPlaybackTo(0),className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Quay lại đầu"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"skip-back",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){const st=subTabs.find(s=>s.id===activeTab);if(!st)return;const left=st.selectionStart!==null&&st.selectionEnd!==null?Math.min(st.selectionStart,st.selectionEnd):null;if(left!==null)seekPlaybackTo(left);}else{if(selLeft!==null)seekPlaybackTo(selLeft);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Đầu vùng chọn"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"step-back",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:handlePlayPause,className:`w-7 h-7 flex items-center justify-center rounded border transition ${isPlaying?'bg-emerald-600 text-black border-emerald-500 hover:bg-emerald-500':'bg-cyan-600 text-white border-cyan-500 hover:bg-cyan-500'}`,title:isPlaying?"Tạm dừng":"Play"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":isPlaying?"pause":"play",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:handleStop,className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Stop"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"square",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:handleRecordClick,className:`w-7 h-7 flex items-center justify-center rounded border transition ${recordingState==='RECORDING'?'bg-red-600 text-white border-red-500 hover:bg-red-500 animate-pulse':recordingState==='COUNT_IN'?'bg-amber-500 text-black border-amber-400 hover:bg-amber-400 animate-pulse':'bg-zinc-800 text-red-500 border-zinc-700 hover:bg-zinc-700 hover:text-red-400'}`,title:recordingState==='RECORDING'?"Đang ghi âm...":recordingState==='COUNT_IN'?"Chuẩn bị ghi âm...":"Ghi âm (Record)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"circle",className:"w-3.5 h-3.5 fill-current"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){setSubTabs(prev=>prev.map(s=>{if(s.id!==activeTab)return s;const right=s.selectionStart!==null&&s.selectionEnd!==null?Math.max(s.selectionStart,s.selectionEnd):null;return right!==null?{...s,currentTime:right}:s;}));}else{if(selRight!==null)setCurrentTime(selRight);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Cuối vùng chọn"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"step-forward",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{const isSubTab=subTabs.some(sub=>sub.id===activeTab);if(isSubTab){setSubTabs(prev=>prev.map(s=>{if(s.id!==activeTab)return s;const duration=s.buffer?s.buffer.duration/(s.speed||1.0):0;return{...s,currentTime:duration};}));}else{setCurrentTime(maxDuration);}},className:"w-7 h-7 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700 transition",title:"Đến cuối"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"skip-forward",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-5 bg-zinc-800 mx-0.5"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{setIsLoopingSelection(prev=>!prev);// Sync loop state with active sub-tab (piano roll / audio editor) const activeSub=activeTab&&subTabs.find(s=>s.id===activeTab&&['PIANO_ROLL','AUDIO_CLIP_EDITOR','SECTION_EDITOR'].includes(s.type));if(activeSub){const newLoop=!activeSub.isLooping;setSubTabs(prev=>prev.map(s=>s.id===activeTab?{...s,isLooping:newLoop}:s));if(newLoop){const bpmVal=parseInt(bpm)||120;const beatSec=60.0/bpmVal;let maxEnd=0;if(activeSub.type==='PIANO_ROLL'){(activeSub.notes||[]).forEach(n=>{const end=(n.start_beat||0)+(n.duration_beats||1);if(end>maxEnd)maxEnd=end;});}else if(activeSub.type==='SECTION_EDITOR'){(activeSub.clips||[]).forEach(c=>{const end=(c.startTime||0)+(c.duration||0);if(end>maxEnd)maxEnd=end;});}else if(activeSub.type==='AUDIO_CLIP_EDITOR'){const dur=activeSub.buffer?.duration||0;if(dur>maxEnd)maxEnd=dur;}const loopEndTime=activeSub.type==='PIANO_ROLL'?Math.max(maxEnd,16)*beatSec+1.0:Math.max(maxEnd,1);setSubTabs(prev=>prev.map(s=>s.id===activeTab?{...s,selectionStart:0,selectionEnd:loopEndTime}:s));}}else{// Main timeline / section tab: nếu user đã chọn vùng loop riêng → // giữ nguyên (loop vùng chọn — nhánh updatePlayhead selLeft/ // selRight). KHÔNG auto-derive selection: LOOP bật → loop tại