From fc663921ffc39eb7d9565fa2e9b8b68e91dedd71 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 4 Aug 2026 12:16:53 +0700 Subject: [PATCH] =?UTF-8?q?IMPROVE:=20khi=20zoom=20in=20th=C3=AC=20gi?= =?UTF-8?q?=E1=BB=AF=20nguy=C3=AAn=20k=C3=ADch=20th=C6=B0=E1=BB=9Bc=20sau?= =?UTF-8?q?=20khi=20reload=20page,=20drag=20Audioclip=20item=20=C4=91i=20v?= =?UTF-8?q?=E1=BB=8B=20tr=C3=AD=20kh=C3=A1c=20th=C3=AC=20playhead=20v?= =?UTF-8?q?=E1=BA=ABn=20play=20=C4=91=C3=BAng=20v=E1=BB=8B=20tr=C3=AD=20?= =?UTF-8?q?=C3=A2m=20thanh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/app.jsx | 399 ++++++++++++++++++++++++------- app/static/js/app.precompiled.js | 94 ++++++-- app/templates/index.html | 2 +- wiki.md | 55 ++++- 4 files changed, 445 insertions(+), 105 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 4933c54..3fd9fa7 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -136,6 +136,51 @@ function setTrackNodeGain(node, gainLinear) { node.gainNode.gain.setTargetAtTime(gainLinear, t, 0.02); } +// MAIN SESSION end-time (seconds): endtime of the items ON the session's own +// tracks only — audio clips, MIDI items, and section-item bounds. Section-TAB +// content is deliberately IGNORED here: when played inside the main session the +// sub-track items are clamped to their section bounds, so a long SECTION-TAB +// must NOT stretch the main project. The SECTION-TAB duration is computed +// separately from ITS OWN tracks (see maxDuration useMemo). +function computeMainSessionEndTime(tracksList) { + let max = 0; + const midiEnd = m => { + if (m && typeof m.endTime === 'number') return m.endTime; + return (m && m.startTime || 0) + (m && typeof m.duration === 'number' ? m.duration : 4); + }; + (tracksList || []).forEach(t => { + const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{ + id: 'default', buffer: t.buffer, startTime: t.startTime || 0, speed: t.speed || 1.0 + }] : []; + clips.forEach(c => { + if (c.buffer) max = Math.max(max, (c.startTime || 0) + c.buffer.duration / (c.speed || 1.0)); + }); + (t.midiItems || []).forEach(m => { max = Math.max(max, midiEnd(m)); }); + (t.sections || []).forEach(s => { max = Math.max(max, (s.start || 0) + (s.duration || 0)); }); + }); + return max; +} + +// Signature of all item positions/speeds/durations on the given tracks (+ the +// content inside section tabs). Compared against the signature captured at +// schedule time: when they differ mid-playback the loop re-schedules so moved +// items play at their NEW position instead of the stale one. +function buildItemsSignature(tracksList, tabsList) { + const tSig = t => { + const clips = (t.clips || []).map(c => c.id + ':' + Math.round((c.startTime || 0) * 100) + ':' + Math.round((c.speed || 1) * 100)).join(','); + const midi = (t.midiItems || []).map(m => m.id + ':' + Math.round((m.startTime || 0) * 100)).join(','); + const secs = (t.sections || []).map(s => s.id + ':' + Math.round((s.start || 0) * 100) + ':' + Math.round((s.duration || 0) * 100)).join(','); + // Track-level default clip (track.buffer): vị trí lưu ở track.startTime — + // KHÔNG nằm trong t.clips, phải đưa vào signature nếu không kéo default + // clip sẽ không kích hoạt re-schedule (vẫn phát nội dung cũ). + const defClip = (t.buffer ? 'def:' + Math.round((t.startTime || 0) * 100) + ':' + Math.round((t.speed || 1) * 100) : ''); + return clips + '|' + midi + '|' + secs + '|' + defClip; + }; + let s = (tracksList || []).map(tSig).join(';'); + s += '##' + (tabsList || []).map(st => (st.tracks || []).map(tSig).join(';')).join('|'); + return s; +} + // Reusable time-domain buffers for the imager vectorscope/correlation meter // (leftAnalyser/rightAnalyser are fixed at fftSize 2048) — allocated once so // the 60fps render loop does not churn the GC. @@ -3799,7 +3844,9 @@ const SubTabWaveform = ({ stretchStartRef.current = { mouseX, originalDuration: bufDuration, - originalSpeed: speed + originalSpeed: speed, + finalSpeed: speed, + clipName: name || 'clip' }; canvas.style.cursor = 'ew-resize'; const handleMouseMove = moveEvent => { @@ -3808,13 +3855,33 @@ const SubTabWaveform = ({ const deltaX = currentX - stretchStartRef.current.mouseX; const newWClip = Math.max(10, wClipPx + deltaX); const newSpeed = stretchStartRef.current.originalDuration / (newWClip / zoom); - if (onSpeedChange) onSpeedChange(Math.max(0.05, Math.min(10, newSpeed))); + stretchStartRef.current.finalSpeed = Math.max(0.05, Math.min(10, newSpeed)); + if (onSpeedChange) onSpeedChange(stretchStartRef.current.finalSpeed); }; const handleMouseUp = () => { isStretchingRef.current = false; canvas.style.cursor = 'default'; document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); + // UNDO/REDO cho alt-click-drag speed stretch: entry SET_CLIP_SPEED — + // undo về speed gốc, redo về speed cuối (onSpeedChange tự tính lại + // volumeNodes/panningNodes/fade/label theo ratio nên khớp 2 chiều). + const st = stretchStartRef.current; + if (st && typeof st.originalSpeed === 'number' && window.UndoRedoEngine) { + const finalSpeed = st.finalSpeed || st.originalSpeed; + if (Math.abs(finalSpeed - st.originalSpeed) > 0.001) { + window.UndoRedoEngine.execute({ + type: 'SET_CLIP_SPEED', + scope: 'section_tab', + label: `Speed ${Math.round(st.originalSpeed * 100)}% → ${Math.round(finalSpeed * 100)}% (${st.clipName})`, + before: st.originalSpeed, + after: finalSpeed, + undo: e => { if (onSpeedChange) onSpeedChange(e.before); }, + redo: e => { if (onSpeedChange) onSpeedChange(e.after); } + }); + } + } + stretchStartRef.current = null; }; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); @@ -9426,6 +9493,101 @@ const InteractiveEqPro = ({ track, params, onChange, getModule, applyTo, spectru ); }; +const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExporting, onExport, onBounce }) => { + if (!open) return null; + return ( +
+
e.stopPropagation()}> +
+ + EXPORT + + +
+
+
+
+ + +
+
+ + +
+
+ {exportSettings.format === 'wav' ? ( +
+
+ + +
+
+ + +
+
+ ) : ( +
+
+ + +
+
+
+ )} +
+
+ + +
+
+
+
+ + +
+
+
+
+ ); +}; + const FXRackModal = ({ track, onUpdateTrack, onClose }) => { const [activeType, setActiveType] = React.useState('eq'); const [addModuleOpen, setAddModuleOpen] = React.useState(false); @@ -13403,7 +13565,17 @@ const App = () => { const [localSelectionTrackId, setLocalSelectionTrackId] = useState(null); const [localSelectionStart, setLocalSelectionStart] = useState(null); const [localSelectionEnd, setLocalSelectionEnd] = useState(null); - const [zoom, setZoom] = useState(100); + const [zoom, setZoom] = useState(() => { + // Persist zoom qua reload (user yêu cầu giữ kích thước items sau refresh) + try { + const v = parseFloat(localStorage.getItem('sf_zoom')); + if (isFinite(v) && v > 0) return v; + } catch (e) { } + return 100; + }); + React.useEffect(() => { + try { localStorage.setItem('sf_zoom', String(zoom)); } catch (e) { } + }, [zoom]); const [isLoopingSelection, setIsLoopingSelection] = useState(false); const [beginBar, setBeginBar] = useState(0); const [endBar, setEndBar] = useState(0); @@ -17065,38 +17237,60 @@ const App = () => { leadInMarginRef.current = 0; const maxDuration = useMemo(() => { const secPerBar = (60.0 / (parseInt(bpm) || 120)) * 4; - const cur = activeTracks; - let max = 10; - cur.forEach(t => { - const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{ - id: 'default', - buffer: t.buffer, - startTime: t.startTime || 0, - name: t.name, - speed: t.speed || 1.0 - }] : []; - clips.forEach(c => { - if (c.buffer) { - const cStart = c.startTime || 0; - const cDur = c.buffer.duration / (c.speed || 1.0); - max = Math.max(max, cStart + cDur); - } - }); - (t.midiItems || []).forEach(m => { - max = Math.max(max, (m.startTime || 0) + (m.duration || 4)); - }); - (t.sections || []).forEach(s => { - max = Math.max(max, (s.start || 0) + (s.duration || 4)); - }); - }); + // MAIN SESSION: endtime của items trên track MAIN (mặc kệ SECTION-TAB dài bao nhiêu — + // sub-track items bị clamp trong section bounds khi play trong main session). + // SECTION-TAB: endtime của items trên track CỦA TAB (tính theo vị trí section trên main). + let max; + if (activeTab === 'main') { + max = computeMainSessionEndTime(activeTracks); + } else { + const tab = sessionTabs.find(st => st.id === activeTab); + const tabMax = tab ? computeMainSessionEndTime(tab.tracks || []) : 0; + let secStart = 0; + const secRef = tab ? tab.sectionId : null; + if (secRef) { + activeTracks.forEach(t => (t.sections || []).forEach(s => { + if (s.sectionId === secRef || s.id === secRef) secStart = s.start || 0; + })); + } + max = secStart + tabMax; + } if (recordingState === 'RECORDING' || recordingState === 'COUNT_IN') { max = Math.max(max, currentTime + 60); } max += secPerBar * 12 + scrollBufferExtra; return max; - }, [activeTracks, recordingState, currentTime, bpm, scrollBufferExtra]); + }, [activeTab, activeTracks, sessionTabs, recordingState, currentTime, bpm, scrollBufferExtra]); const maxDurationRef = useRef(maxDuration); maxDurationRef.current = maxDuration; + // ── Project REAL end-time (KHÔNG buffer) ── + // maxDuration ở trên cộng 12 bars + scrollBuffer cho vùng SCROLL/ZOOM; nếu + // dùng nó làm điểm dừng LOOP thì loop kéo dài quá duration thật. projectEnd + // = endtime của session (main: items trên track main; section-tab: secStart + + // items của tab) — dùng cho updatePlayhead dừng/loop lại đúng cuối bài. + const projectEnd = useMemo(() => { + let max; + if (activeTab === 'main') { + max = computeMainSessionEndTime(activeTracks); + } else { + const tab = sessionTabs.find(st => st.id === activeTab); + const tabMax = tab ? computeMainSessionEndTime(tab.tracks || []) : 0; + let secStart = 0; + const secRef = tab ? tab.sectionId : null; + if (secRef) { + activeTracks.forEach(t => (t.sections || []).forEach(s => { + if (s.sectionId === secRef || s.id === secRef) secStart = s.start || 0; + })); + } + max = secStart + tabMax; + } + if (recordingState === 'RECORDING' || recordingState === 'COUNT_IN') { + max = Math.max(max, currentTime + 60); + } + return Math.max(1, max); + }, [activeTab, activeTracks, sessionTabs, recordingState, currentTime]); + const projectEndRef = useRef(projectEnd); + projectEndRef.current = projectEnd; const minZoom = useMemo(() => { return viewportWidth / maxDuration; }, [viewportWidth, maxDuration]); @@ -17377,7 +17571,64 @@ const App = () => { } }; + // Realtime re-schedule tracking (loop play cập nhật khi items đổi vị trí) + const scheduledItemsSigRef = React.useRef(''); + const playheadFrameCountRef = React.useRef(0); + const pendingRescheduleRef = React.useRef(null); + const lastRescheduleTimeRef = React.useRef(0); + // Re-schedule NGAY (reset cooldown) sau khi THẢ chuột — check kế tiếp trong + // updatePlayhead (~100ms) re-schedule luôn. + const triggerRescheduleNow = () => { + try { + lastRescheduleTimeRef.current = 0; + } catch (e) { } + }; + const updatePlayhead = () => { + // Realtime re-schedule: khi đang play (loop play) mà items (vị trí/speed/ + // duration — kể cả nội dung section tab) thay đổi → dừng + schedule lại từ + // playhead hiện tại để item mới phát đúng vị trí mới (không phát nội dung + // cũ ở vị trí cũ). Kiểm tra ~10fps (mỗi 6 frame) để kéo item cập nhật gần + // như realtime mà không tốn CPU mỗi frame. + if (isPlaying && activeTabRef.current === 'main' && recordingStateRef.current !== 'RECORDING') { + playheadFrameCountRef.current++; + if (playheadFrameCountRef.current % 6 === 0) { + // ⚠️ Dùng REFS (không phải state closure) — rAF loop giữ updatePlayhead + // của render cũ nên activeTracks/sessionTabs trong closure là STALE → + // signature không bao giờ đổi khi kéo item → không re-schedule. + const sig = buildItemsSignature(activeTracksRef.current, sessionTabsRef.current); + if (sig !== scheduledItemsSigRef.current) { + // THROTTLE ~300ms: re-schedule định kỳ NGAY CẢ TRONG LÚC KÉO (clip + // kéo đi → âm thanh cũ dừng ≤300ms, clip mới phát khi playhead tới + // vị trí mới — realtime) mà không stop/start mỗi frame (giật). Sau + // khi thả chuột triggerRescheduleNow reset cooldown → re-schedule + // ở check kế tiếp (~100ms). + const nowT = performance.now(); + if (nowT - (lastRescheduleTimeRef.current || 0) > 300) { + lastRescheduleTimeRef.current = nowT; + scheduledItemsSigRef.current = sig; + const pt = currentTimeRef.current; + stopAllPlayback(); + setIsPlaying(true); + // Re-schedule theo ĐÚNG chế độ play hiện tại: loop local / solo chỉ + // phát track liên quan (không phát nhầm track khác); ngược lại play + // toàn session. Cả 2 hàm đều tính playOffset = pt − clip.startTime + // → clip vừa kéo tới đúng playhead phát TỪ ĐẦU clip (realtime). + const curTracks = activeTracksRef.current || activeTracks; + const soloed = curTracks.some(t => t.solo); + if (soloed) { + curTracks.filter(t => t.solo).forEach(t => startLocalTrackPlayback(t.id, pt)); + } else if (selectionMode === 'local' && localSelectionTrackId) { + startLocalTrackPlayback(localSelectionTrackId, pt); + } else { + startTrackPlayback(pt); + } + } + } else { + pendingRescheduleRef.current = null; + } + } + } if (recordingStateRef.current === 'RECORDING') { const audioCtx = getAudioContext(); const lookahead = 0.1; // 100ms @@ -17532,7 +17783,9 @@ const App = () => { return; } } - if (updatedTime >= maxDurationRef.current) { + // Hết bài: dừng/loop lại tại ENDTIME THẬT của session (projectEnd — không + // buffer 12 bars như maxDuration dùng cho scroll/zoom). + if (updatedTime >= projectEndRef.current) { if (recordingStateRef.current === 'RECORDING') { setCurrentTime(updatedTime); animationFrameIdRef.current = requestAnimationFrame(updatePlayhead); @@ -17949,6 +18202,10 @@ const App = () => { const startTrackPlayback = offsetTime => { const context = getAudioContext(); const allPlayTracks = activeTracksRef.current && activeTracksRef.current.length ? activeTracksRef.current : activeTracks; + // Capture items signature at schedule time — updatePlayhead so sánh với + // signature này để phát hiện items đổi vị trí giữa lúc play (re-schedule). + scheduledItemsSigRef.current = buildItemsSignature(allPlayTracks, sessionTabs); + console.log('[Play] offset=' + offsetTime + ' tracks=' + allPlayTracks.length + ' masterBus=' + !!masterBus + ' dest=' + (context.destination ? 'ok' : 'MISSING')); const hasSolo = allPlayTracks.some(t => t.solo); allPlayTracks.forEach(track => { const isPlayable = hasSolo ? track.solo : !track.muted; @@ -17956,6 +18213,7 @@ const App = () => { const gainNode = getOrCreateTrackNode(track, context); const pannerNode = activeTrackNodesRef.current[track.id].pannerNode; + console.log('[Play] track', track.id, track.name, 'node ok:', !!(gainNode && pannerNode), 'muted:', track.muted); const clips = track.clips && track.clips.length > 0 ? track.clips : track.buffer ? [{ id: 'default', buffer: track.buffer, @@ -19396,6 +19654,9 @@ const App = () => { pushAction('MOVE_CLIP', drag.trackId, drag.beforeSnap, afterSnap); setDraggedClip(null); showToast('Đã di chuyển clip.', 'success'); + // Realtime: clip vừa thả — re-schedule NGAY để phát theo vị trí mới + // (kéo clip về đúng playhead → phát ngay, không chờ debounce). + triggerRescheduleNow(); }; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); @@ -19789,6 +20050,8 @@ const App = () => { var afterSnap = captureAllTracksSnapshot(); pushAction('MOVE_ITEM', 'ALL_TRACKS', drag.beforeSnap || beforeSnap, afterSnap); } + // Realtime: items vừa thả — re-schedule NGAY theo vị trí mới. + triggerRescheduleNow(); if (drag.itemType === 'midiItem') { let finalTrackId = null; const curTrks = activeTracksRef.current || activeTracks; @@ -21010,15 +21273,9 @@ const App = () => { const audioCtx = getAudioContext(); try { const allTracks = (activeTracksRef.current && activeTracksRef.current.length) ? activeTracksRef.current : tracks; - let durationLimit = 1.0; - allTracks.forEach(t => { - const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{ startTime: t.startTime || 0, buffer: t.buffer, speed: t.speed || 1.0 }] : []; - clips.forEach(c => { if (c.buffer) durationLimit = Math.max(durationLimit, (c.startTime || 0) + c.buffer.duration / (c.speed || 1.0)); }); - (t.midiItems || []).forEach(m => { if (m.endTime) durationLimit = Math.max(durationLimit, m.endTime); else if (m.startTime) durationLimit = Math.max(durationLimit, m.startTime + (m.duration || 8)); }); - }); - (sessionTabs || []).forEach(s => (s.tracks || []).forEach(t => { - (t.midiItems || []).forEach(m => { if (m.endTime) durationLimit = Math.max(durationLimit, m.endTime); else if (m.startTime) durationLimit = Math.max(durationLimit, m.startTime + (m.duration || 8)); }); - })); + // MAIN SESSION end time (items trên track main — sections tính theo bounds, + // không kéo dài theo nội dung SECTION-TAB) — bounce không cắt sớm/cắt thiếu. + let durationLimit = Math.max(1.0, computeMainSessionEndTime(allTracks)); durationLimit += 1.2; // FX/mastering tail const captureRate = audioCtx.sampleRate || 44100; @@ -21244,17 +21501,9 @@ const App = () => { try { const targetRate = parseInt(exportSettings.sampleRate); const bitDepth = parseInt(exportSettings.bitDepth); - const durationLimit = Math.max(...activeTracks.map(t => { - const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{ - id: 'default', - buffer: t.buffer, - startTime: t.startTime || 0, - name: t.name, - speed: t.speed || 1.0 - }] : []; - if (clips.length === 0) return 0; - return Math.max(...clips.map(c => (c.startTime || 0) + c.buffer.duration / (c.speed || 1.0))); - })); + // MAIN SESSION end time (items trên track main — sections theo bounds) — + // offline render không cắt sớm; MIDI cache (preview) giữ max với cache. + let durationLimit = Math.max(0.1, computeMainSessionEndTime(activeTracks)); // Include preview-captured MIDI cache length (soundfont tracks have no clips) Object.keys(midiCacheRef.current).forEach(id => { const c = midiCacheRef.current[id]; @@ -24139,23 +24388,14 @@ const App = () => { const hasSel = (selectionMode === 'local' && selLeft !== null && selRight !== null && selRight > selLeft) || (selectionStart !== null && selectionEnd !== null); if (!hasSel) { - const bpmVal = parseInt(bpm) || 120; - const secPerBar = (60.0 / bpmVal) * 4; - let maxEnd = 0; - activeTracks.forEach(t => { - (t.clips || []).forEach(c => { - const end = (c.startTime || 0) + (c.duration || 0); - if (end > maxEnd) maxEnd = end; - }); - (t.items || []).forEach(it => { - const end = (it.start || 0) + (it.duration || 4); - if (end > maxEnd) maxEnd = end; - }); - }); + // Auto-derive loop region = ĐÚNG endtime của session (items trên + // track main: clips theo buffer.duration/speed, midiItems, section + // bounds) — KHÔNG cộng thêm bars buffer (loop không được dài hơn + // duration hiện có). + const maxEnd = computeMainSessionEndTime(activeTracks); if (maxEnd > 0) { - const loopEnd = maxEnd + secPerBar * 2; setSelectionStart(0); - setSelectionEnd(loopEnd); + setSelectionEnd(maxEnd); } } } @@ -26054,28 +26294,7 @@ const App = () => { })())), /*#__PURE__*/React.createElement("div", { className: "w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10", onMouseDown: startColResize - }), renderDock('right', 'Right')), renderDock('bottom', 'Bottom'), (showExportPanel && /*#__PURE__*/React.createElement("div", { - className: "fixed inset-0 z-[300] bg-black/70 backdrop-blur-sm flex items-center justify-center p-6", - onClick: () => setShowExportPanel(false) - }, /*#__PURE__*/React.createElement("div", { - className: "w-full max-w-2xl bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl overflow-hidden", - onClick: e => e.stopPropagation() - }, /*#__PURE__*/React.createElement("div", { - className: "h-9 bg-slate-800/80 border-b border-slate-700 flex items-center justify-between px-3 select-none" - }, /*#__PURE__*/React.createElement("span", { - className: "text-xs font-bold text-cyan-300 font-mono flex items-center gap-1.5" - }, /*#__PURE__*/React.createElement("i", { - "data-lucide": "download-cloud", - className: "w-3.5 h-3.5" - }), " EXPORT"), /*#__PURE__*/React.createElement("button", { - onClick: () => setShowExportPanel(false), - className: "text-zinc-400 hover:text-white" - }, /*#__PURE__*/React.createElement("i", { - "data-lucide": "x", - className: "w-4 h-4" - }))), /*#__PURE__*/React.createElement("div", { - className: "p-4 max-h-[72vh] overflow-y-auto" - }, renderPanelContent('export'))))), showMixer && /*#__PURE__*/React.createElement("div", { + }), renderDock('right', 'Right')), renderDock('bottom', 'Bottom'), showMixer && /*#__PURE__*/React.createElement("div", { className: "flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none", style: { height: mixerHeight + 'px' } }, /*#__PURE__*/React.createElement("div", { @@ -26643,6 +26862,14 @@ const App = () => { track: tracks.find(t => t.id === fxRackTarget.trackId) || null, onUpdateTrack: updateTrackProp, onClose: () => setFxRackTarget(null) + }), /*#__PURE__*/React.createElement(ExportModal, { + open: showExportPanel, + onClose: () => setShowExportPanel(false), + exportSettings: exportSettings, + setExportSettings: setExportSettings, + isExporting: isExporting, + onExport: triggerWavExport, + onBounce: triggerBounceExport }), instrumentSelectorTrackId && /*#__PURE__*/React.createElement("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm", onClick: closeInstrumentSelector diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 3419dc8..0a94a43 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -23,7 +23,20 @@ function setMasteringRoute(route,bypass){if(!route)return;const on=!!bypass;try{ // of the CURRENT context. Solo semantics: if ANY track is soloed, only soloed // tracks are audible; muted tracks are always silent. function computeTrackAudibleGain(trackList,track){if(!track)return 0;if(track.muted)return 0;const hasSolo=(trackList||[]).some(t=>t.solo);if(hasSolo&&!track.solo)return 0;const volDb=track.volumeDb??0;return volDb<=-50?0:Math.pow(10,volDb/20);}// Apply a gain to a track node's gain with a short crossfade (click-free). -function setTrackNodeGain(node,gainLinear){if(!node||!node.gainNode||!audioCtx)return;const t=audioCtx.currentTime;node.gainNode.gain.cancelScheduledValues(t);node.gainNode.gain.setTargetAtTime(gainLinear,t,0.02);}// Reusable time-domain buffers for the imager vectorscope/correlation meter +function setTrackNodeGain(node,gainLinear){if(!node||!node.gainNode||!audioCtx)return;const t=audioCtx.currentTime;node.gainNode.gain.cancelScheduledValues(t);node.gainNode.gain.setTargetAtTime(gainLinear,t,0.02);}// MAIN SESSION end-time (seconds): endtime of the items ON the session's own +// tracks only — audio clips, MIDI items, and section-item bounds. Section-TAB +// content is deliberately IGNORED here: when played inside the main session the +// sub-track items are clamped to their section bounds, so a long SECTION-TAB +// must NOT stretch the main project. The SECTION-TAB duration is computed +// separately from ITS OWN tracks (see maxDuration useMemo). +function computeMainSessionEndTime(tracksList){let max=0;const midiEnd=m=>{if(m&&typeof m.endTime==='number')return m.endTime;return(m&&m.startTime||0)+(m&&typeof m.duration==='number'?m.duration:4);};(tracksList||[]).forEach(t=>{const clips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default',buffer:t.buffer,startTime:t.startTime||0,speed:t.speed||1.0}]:[];clips.forEach(c=>{if(c.buffer)max=Math.max(max,(c.startTime||0)+c.buffer.duration/(c.speed||1.0));});(t.midiItems||[]).forEach(m=>{max=Math.max(max,midiEnd(m));});(t.sections||[]).forEach(s=>{max=Math.max(max,(s.start||0)+(s.duration||0));});});return max;}// Signature of all item positions/speeds/durations on the given tracks (+ the +// content inside section tabs). Compared against the signature captured at +// schedule time: when they differ mid-playback the loop re-schedules so moved +// items play at their NEW position instead of the stale one. +function buildItemsSignature(tracksList,tabsList){const tSig=t=>{const clips=(t.clips||[]).map(c=>c.id+':'+Math.round((c.startTime||0)*100)+':'+Math.round((c.speed||1)*100)).join(',');const midi=(t.midiItems||[]).map(m=>m.id+':'+Math.round((m.startTime||0)*100)).join(',');const secs=(t.sections||[]).map(s=>s.id+':'+Math.round((s.start||0)*100)+':'+Math.round((s.duration||0)*100)).join(',');// Track-level default clip (track.buffer): vị trí lưu ở track.startTime — +// KHÔNG nằm trong t.clips, phải đưa vào signature nếu không kéo default +// clip sẽ không kích hoạt re-schedule (vẫn phát nội dung cũ). +const defClip=t.buffer?'def:'+Math.round((t.startTime||0)*100)+':'+Math.round((t.speed||1)*100):'';return clips+'|'+midi+'|'+secs+'|'+defClip;};let s=(tracksList||[]).map(tSig).join(';');s+='##'+(tabsList||[]).map(st=>(st.tracks||[]).map(tSig).join(';')).join('|');return s;}// Reusable time-domain buffers for the imager vectorscope/correlation meter // (leftAnalyser/rightAnalyser are fixed at fftSize 2048) — allocated once so // the 60fps render loop does not churn the GC. const _imagerBufL=new Float32Array(2048);const _imagerBufR=new Float32Array(2048);// Real-time stereo correlation (−1.0 … +1.0) from the master output L/R @@ -267,7 +280,10 @@ if(selectionStart!==null&&selectionEnd!==null&&selectionStart!==selectionEnd){co if(currentTime!==null&¤tTime>=0&¤tTime<=buffer.duration/speed){const playheadPx=currentTime*zoom;ctx.strokeStyle='#ef4444';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(playheadPx,0);ctx.lineTo(playheadPx,h);ctx.stroke();}// Update TCP slider values in real-time to match the curve at currentTime const volInput=document.getElementById(`tcp-vol-${subTabId}`);const volLabel=document.getElementById(`tcp-vol-label-${subTabId}`);if(volInput&&volLabel){const dbVal=getVolumeDbAtTime(currentTime||0);volInput.value=dbVal.toFixed(1);volLabel.textContent=`${dbVal.toFixed(1)}dB`;}const panInput=document.getElementById(`tcp-pan-${subTabId}`);const panLabel=document.getElementById(`tcp-pan-label-${subTabId}`);if(panInput&&panLabel){const panVal=getPanningValueAtTime(currentTime||0);panInput.value=panVal;panLabel.textContent=panVal>0?'R':panVal<0?'L':'C';const panLabelDetailed=document.getElementById(`tcp-pan-label-detailed-${subTabId}`);if(panLabelDetailed){panLabelDetailed.textContent=panVal>0?'R'+panVal:panVal<0?'L'+Math.abs(panVal):'C';}}},[buffer,currentTime,selectionStart,selectionEnd,zoom,timelineWidth,color,name,speed,volumeNodes,panningNodes,fadeInLen,fadeOutLen,graphMode,selectedNodeTime,subTabId]);const handleMouseDown=e=>{if(e.button===2)return;const canvas=canvasRef.current;const rect=canvas.getBoundingClientRect();const parent=canvas.parentElement;const scrollContainer=parent?parent.parentElement:null;const scrollLeft=scrollContainer?scrollContainer.scrollLeft:0;const bufDuration=buffer.duration;const wallDuration=bufDuration/(speed||1.0);const mouseX=e.clientX-rect.left+scrollLeft;const startTime=Math.max(0,Math.min(wallDuration,mouseX/zoom));// Shift + Click range selection in SubTab Waveform if(e.shiftKey){e.preventDefault();e.stopPropagation();const anchor=subTabAnchorRef.current!==null&&subTabAnchorRef.current!==undefined?subTabAnchorRef.current:selectionStart!==null&&selectionStart!==undefined?selectionStart:currentTime;const selS=Math.min(anchor,startTime);const selE=Math.max(anchor,startTime);onSelectRange(selS,selE);onPlayheadSet(startTime);return;}// Alt+Click near right edge → speed stretch -if(e.altKey&&onSpeedChange){const clipRightEdge=bufDuration/(speed||1.0)*zoom;const tolerance=8;if(Math.abs(mouseX-clipRightEdge)<=tolerance){isStretchingRef.current=true;stretchStartRef.current={mouseX,originalDuration:bufDuration,originalSpeed:speed};canvas.style.cursor='ew-resize';const handleMouseMove=moveEvent=>{const currentX=moveEvent.clientX-rect.left+scrollLeft;const wClipPx=stretchStartRef.current.originalDuration/stretchStartRef.current.originalSpeed*zoom;const deltaX=currentX-stretchStartRef.current.mouseX;const newWClip=Math.max(10,wClipPx+deltaX);const newSpeed=stretchStartRef.current.originalDuration/(newWClip/zoom);if(onSpeedChange)onSpeedChange(Math.max(0.05,Math.min(10,newSpeed)));};const handleMouseUp=()=>{isStretchingRef.current=false;canvas.style.cursor='default';document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return;}}// Mode toggle button click (bottom-right VOL/PAN) +if(e.altKey&&onSpeedChange){const clipRightEdge=bufDuration/(speed||1.0)*zoom;const tolerance=8;if(Math.abs(mouseX-clipRightEdge)<=tolerance){isStretchingRef.current=true;stretchStartRef.current={mouseX,originalDuration:bufDuration,originalSpeed:speed,finalSpeed:speed,clipName:name||'clip'};canvas.style.cursor='ew-resize';const handleMouseMove=moveEvent=>{const currentX=moveEvent.clientX-rect.left+scrollLeft;const wClipPx=stretchStartRef.current.originalDuration/stretchStartRef.current.originalSpeed*zoom;const deltaX=currentX-stretchStartRef.current.mouseX;const newWClip=Math.max(10,wClipPx+deltaX);const newSpeed=stretchStartRef.current.originalDuration/(newWClip/zoom);stretchStartRef.current.finalSpeed=Math.max(0.05,Math.min(10,newSpeed));if(onSpeedChange)onSpeedChange(stretchStartRef.current.finalSpeed);};const handleMouseUp=()=>{isStretchingRef.current=false;canvas.style.cursor='default';document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);// UNDO/REDO cho alt-click-drag speed stretch: entry SET_CLIP_SPEED — +// undo về speed gốc, redo về speed cuối (onSpeedChange tự tính lại +// volumeNodes/panningNodes/fade/label theo ratio nên khớp 2 chiều). +const st=stretchStartRef.current;if(st&&typeof st.originalSpeed==='number'&&window.UndoRedoEngine){const finalSpeed=st.finalSpeed||st.originalSpeed;if(Math.abs(finalSpeed-st.originalSpeed)>0.001){window.UndoRedoEngine.execute({type:'SET_CLIP_SPEED',scope:'section_tab',label:`Speed ${Math.round(st.originalSpeed*100)}% → ${Math.round(finalSpeed*100)}% (${st.clipName})`,before:st.originalSpeed,after:finalSpeed,undo:e=>{if(onSpeedChange)onSpeedChange(e.before);},redo:e=>{if(onSpeedChange)onSpeedChange(e.after);}});}}stretchStartRef.current=null;};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return;}}// Mode toggle button click (bottom-right VOL/PAN) const wClipPx=bufDuration/(speed||1.0)*zoom;const cTop=8;const cSize=16;const cHeight=rect.height-16;const modeBtnX=wClipPx-28-4;const modeBtnY=cTop+cHeight-14-2;if(mouseX>=modeBtnX&&mouseX<=modeBtnX+28&&e.clientY-rect.top>=modeBtnY&&e.clientY-rect.top<=modeBtnY+14){if(onModeToggle)onModeToggle();return;}// Fade endpoint handle drag (click on FI/FO handle circles) const HANDLE_R=5;const fiEndPx=fadeInLen*zoom;const foStartPx=wClipPx-fadeOutLen*zoom;const distToFiHandle=Math.abs(mouseX-fiEndPx)+Math.abs(e.clientY-rect.top-cTop);const distToFoHandle=Math.abs(mouseX-foStartPx)+Math.abs(e.clientY-rect.top-cTop);if(distToFiHandle<=HANDLE_R+6){const handleMouseMove=moveEvent=>{const x=moveEvent.clientX-rect.left+scrollLeft;const t=Math.max(0,Math.min(wallDuration,x/zoom));if(onUpdateFade)onUpdateFade({fadeInLen:t});};const handleMouseUp=()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return;}if(distToFoHandle<=HANDLE_R+6){const handleMouseMove=moveEvent=>{const x=moveEvent.clientX-rect.left+scrollLeft;const t=Math.max(0,Math.min(wallDuration,(wClipPx-x)/zoom));if(onUpdateFade)onUpdateFade({fadeOutLen:t});};const handleMouseUp=()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return;}// Tool-specific behavior if(activeTool==='grab'){setSelectedNodeTime(null);onPlayheadSet(startTime);const handleMouseMove=moveEvent=>{const currentX=moveEvent.clientX-rect.left+scrollLeft;const ct=Math.max(0,Math.min(wallDuration,currentX/zoom));onPlayheadSet(ct);};const handleMouseUp=()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return;}if(activeTool==='razor'){setSelectedNodeTime(null);onPlayheadSet(startTime);showToast(`Cut point at ${formatTime(startTime)}`,'info');return;}if(activeTool==='pen'){// Deduplicate by time: keep last occurrence per time key @@ -377,7 +393,7 @@ const specMods=(spectrumModules?spectrumModules():null)||(getModule?[getModule() const applyAll=applyTo||(fn=>{if(typeof window!=='undefined'&&window.__getTrackFxModule){const a=window.__getTrackFxModule(track.id,'eqpro');if(a)fn(a);}if(typeof window!=='undefined'&&window.__getTrackSfFxModule){const b=window.__getTrackSfFxModule(track.id,'eqpro');if(b)fn(b);}});const onDown=e=>{const cv=canvasRef.current,w=cv.clientWidth,h=cv.clientHeight;const{x,y}=toLocal(e);const s=st.current;let hit=false;for(let i=0;it+1);};const onMove=e=>{const s=st.current;if(s.dragId===null)return;const cv=canvasRef.current,w=cv.clientWidth,h=cv.clientHeight;const{x,y}=toLocal(e);const mx=Math.max(0,Math.min(w,x)),my=Math.max(0,Math.min(h,y));const b=s.bands[s.dragId];if(s.mode==='center'){const f=eqproClamp(parseFloat(eqproXToFreq(mx,w).toFixed(1)),EQPRO_F_MIN,EQPRO_F_MAX);// Filters without a Gain control (highpass/lowpass/notch/bandpass) stay on // the 0 dB axis — only frequency is draggable for them. const g=eqproBandHasGain(b.type)?eqproClamp(parseFloat(eqproYToGain(my,h).toFixed(1)),-EQPRO_MAX_DB,EQPRO_MAX_DB):0;b.freq=f;b.gain=g;applyAll(mm=>mm.setBand(s.dragId,{freq:f,gain:g}));}else if(s.mode==='wing'){const q=eqproWingToQ(Math.abs(mx-eqproFreqToX(b.freq,w)));b.q=q;applyAll(mm=>mm.setBand(s.dragId,{q}));}setTick(t=>t+1);};const onUp=e=>{const s=st.current;if(s.dragId!==null)commit();s.dragId=null;s.mode=null;try{canvasRef.current.releasePointerCapture(e.pointerId);}catch(err){}};const onWheel=e=>{e.preventDefault();const s=st.current;if(s.selected===null)return;const b=s.bands[s.selected];b.q=eqproClamp(parseFloat((b.q+(e.deltaY<0?0.2:-0.2)).toFixed(1)),0.1,18);applyAll(mm=>mm.setBand(s.selected,{q:b.q}));setTick(t=>t+1);};const onDblClick=e=>{const cv=canvasRef.current,w=cv.clientWidth,h=cv.clientHeight;const{x,y}=toLocal(e);const s=st.current;const hitIdx=s.bands.findIndex(b=>Math.hypot(x-eqproFreqToX(b.freq,w),y-eqproGainToY(b.gain,h))<=10);if(hitIdx!==-1){s.bands.splice(hitIdx,1);if(s.selected===hitIdx)s.selected=null;else if(s.selected!==null&&s.selected>hitIdx)s.selected--;}else{if(s.bands.length>=EQPRO_MAX_BANDS)return;s.bands.push({type:'peaking',freq:eqproClamp(parseFloat(eqproXToFreq(x,w).toFixed(1)),EQPRO_F_MIN,EQPRO_F_MAX),gain:eqproClamp(parseFloat(eqproYToGain(y,h).toFixed(1)),-EQPRO_MAX_DB,EQPRO_MAX_DB),q:1.2,active:true});s.selected=s.bands.length-1;}applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);};const sel=st.current.selected!==null?st.current.bands[st.current.selected]:null;const selIdx=st.current.selected;const cvW=canvasRef.current?canvasRef.current.clientWidth:460;const cvH=240;const hudColor=selIdx!==null?EQPRO_BAND_COLORS[selIdx%EQPRO_BAND_COLORS.length]:EQPRO_BAND_COLORS[0];const hudXY=(()=>{if(st.current.hudPos){// Clamp so the HUD always stays inside the canvas area (buttons clickable). -return{hx:Math.max(0,Math.min((canvasRef.current?canvasRef.current.clientWidth:460)-268,st.current.hudPos.hx)),hy:Math.max(0,Math.min((canvasRef.current?canvasRef.current.clientHeight:240)-225,st.current.hudPos.hy))};}if(!sel)return null;const nx=eqproFreqToX(sel.freq,cvW),ny=eqproGainToY(sel.gain,cvH);let hx=nx-130,hy=ny-195;if(hx<8)hx=8;if(hx>cvW-268)hx=cvW-268;if(hy<8)hy=ny+24;return{hx,hy};})();return/*#__PURE__*/React.createElement("div",{className:"space-y-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[11px] font-mono"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Bands: ",/*#__PURE__*/React.createElement("span",{className:"text-teal-300 font-bold"},st.current.bands.length),"/8"),/*#__PURE__*/React.createElement("label",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Amount:"),/*#__PURE__*/React.createElement("input",{type:"range",min:0,max:200,value:st.current.amount,onChange:e=>{st.current.amount=parseInt(e.target.value);applyAll(mm=>mm.setAmount(st.current.amount));commit();setTick(t=>t+1);},className:"w-20 h-1 cursor-pointer",style:{accentColor:'#2dd4bf'}}),/*#__PURE__*/React.createElement("span",{className:"text-teal-300 font-bold w-10"},st.current.amount,"%"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{onClick:()=>{const s=st.current;s.bands=JSON.parse(JSON.stringify(EQPRO_DEFAULT_BANDS));s.selected=null;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"px-2 py-1 bg-slate-800 hover:bg-red-900/60 text-slate-300 border border-slate-700 rounded text-[10px]"},"Reset"),/*#__PURE__*/React.createElement("button",{onClick:()=>{const s=st.current;if(s.bands.length>=EQPRO_MAX_BANDS)return;s.bands.push({type:'peaking',freq:1000,gain:0,q:1.0,active:true});s.selected=s.bands.length-1;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"px-2 py-1 bg-teal-900/50 hover:bg-teal-800 text-teal-300 border border-teal-700 rounded text-[10px]"},"+ Band"))),/*#__PURE__*/React.createElement("div",{className:"relative"},/*#__PURE__*/React.createElement("canvas",{ref:canvasRef,className:"w-full h-60 block cursor-crosshair rounded-lg border border-slate-800 bg-slate-950",onPointerDown:onDown,onPointerMove:onMove,onPointerUp:onUp,onWheel:onWheel,onDoubleClick:onDblClick}),sel&&hudXY&&/*#__PURE__*/React.createElement("div",{className:"absolute z-10 w-64 rounded-xl p-3 text-[11px] font-mono space-y-2 pointer-events-auto cursor-move",style:{left:hudXY.hx,top:hudXY.hy,background:'rgba(15,23,42,0.94)',border:'1px solid rgba(45,212,191,0.3)',boxShadow:'0 10px 30px rgba(0,0,0,0.8)',backdropFilter:'blur(12px)'},onPointerDown:e=>{const s=st.current;s.dragHud=true;const r=e.currentTarget.getBoundingClientRect();s.hudOffsetX=e.clientX-r.left;s.hudOffsetY=e.clientY-r.top;e.currentTarget.setPointerCapture(e.pointerId);},onPointerMove:e=>{const s=st.current;if(!s.dragHud)return;const cv=canvasRef.current;const w=cv?cv.clientWidth:460;s.hudPos={hx:Math.max(0,Math.min(w-268,e.clientX-s.hudOffsetX-(cv?cv.getBoundingClientRect().left:0))),hy:Math.max(0,e.clientY-s.hudOffsetY-(cv?cv.getBoundingClientRect().top:0))};setTick(t=>t+1);},onPointerUp:e=>{st.current.dragHud=false;try{e.currentTarget.releasePointerCapture(e.pointerId);}catch(err){}}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-700/80 pb-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"w-4 h-4 rounded-full text-slate-950 font-bold flex items-center justify-center text-[10px]",style:{background:hudColor.badge}},selIdx+1),/*#__PURE__*/React.createElement("span",{className:"font-bold text-white uppercase tracking-wider"},"Band ",selIdx+1)),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{title:"Bypass band",onClick:()=>{const s=st.current;s.bands[s.selected].active=!s.bands[s.selected].active;applyAll(mm=>mm.setBand(s.selected,{active:s.bands[s.selected].active}));commit();setTick(t=>t+1);},className:`w-5 h-5 rounded flex items-center justify-center text-[10px] ${sel.active?'bg-slate-800 text-slate-400':'bg-amber-600 text-white'}`},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-power-off"})),/*#__PURE__*/React.createElement("button",{title:"Delete band",onClick:()=>{const s=st.current;s.bands.splice(s.selected,1);s.selected=null;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"w-5 h-5 rounded bg-slate-800 hover:bg-red-600 text-slate-400 hover:text-white flex items-center justify-center text-[10px]"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"})))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[9px] text-slate-400 uppercase tracking-widest mb-1"},"Filter Shape"),/*#__PURE__*/React.createElement("select",{value:sel.type,onChange:ev=>{const s=st.current;s.bands[s.selected].type=ev.target.value;applyAll(mm=>mm.setBand(s.selected,{type:ev.target.value}));commit();setTick(t=>t+1);},className:"w-full bg-slate-950 border border-slate-700 text-teal-300 rounded px-2 py-1 text-[11px] outline-none"},['peaking','highpass','lowpass','lowshelf','highshelf','notch','bandpass'].map(t=>/*#__PURE__*/React.createElement("option",{key:t,value:t},t==='peaking'?'Bell / Peaking':t==='highpass'?'Low Cut / High Pass':t==='lowpass'?'High Cut / Low Pass':t==='lowshelf'?'Low Shelf':t==='highshelf'?'High Shelf':t==='notch'?'Notch / Band Stop':'Band Pass')))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-2 text-center bg-slate-950/80 p-2 rounded-lg border border-slate-800"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"FREQ"),/*#__PURE__*/React.createElement("div",{className:"text-teal-300 font-bold text-[11px]"},sel.freq>=1000?(sel.freq/1000).toFixed(2)+' kHz':Math.round(sel.freq)+' Hz')),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"GAIN"),/*#__PURE__*/React.createElement("div",{className:"text-amber-400 font-bold text-[11px]"},eqproBandHasGain(sel.type)?(sel.gain>0?'+':'')+sel.gain.toFixed(1)+' dB':'0.0 dB')),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"Q"),/*#__PURE__*/React.createElement("div",{className:"text-purple-400 font-bold text-[11px]"},sel.q.toFixed(1)))),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 text-center"},"Drag center: Freq & Gain · Wings/Wheel: Q · Dbl-click: add/delete"))));};const FXRackModal=({track,onUpdateTrack,onClose})=>{const[activeType,setActiveType]=React.useState('eq');const[addModuleOpen,setAddModuleOpen]=React.useState(false);const dragChainIndexRef=React.useRef(null);// Wave Observer scope state (unified_fx_rack_panel_update.md §III.3) +return{hx:Math.max(0,Math.min((canvasRef.current?canvasRef.current.clientWidth:460)-268,st.current.hudPos.hx)),hy:Math.max(0,Math.min((canvasRef.current?canvasRef.current.clientHeight:240)-225,st.current.hudPos.hy))};}if(!sel)return null;const nx=eqproFreqToX(sel.freq,cvW),ny=eqproGainToY(sel.gain,cvH);let hx=nx-130,hy=ny-195;if(hx<8)hx=8;if(hx>cvW-268)hx=cvW-268;if(hy<8)hy=ny+24;return{hx,hy};})();return/*#__PURE__*/React.createElement("div",{className:"space-y-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[11px] font-mono"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Bands: ",/*#__PURE__*/React.createElement("span",{className:"text-teal-300 font-bold"},st.current.bands.length),"/8"),/*#__PURE__*/React.createElement("label",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Amount:"),/*#__PURE__*/React.createElement("input",{type:"range",min:0,max:200,value:st.current.amount,onChange:e=>{st.current.amount=parseInt(e.target.value);applyAll(mm=>mm.setAmount(st.current.amount));commit();setTick(t=>t+1);},className:"w-20 h-1 cursor-pointer",style:{accentColor:'#2dd4bf'}}),/*#__PURE__*/React.createElement("span",{className:"text-teal-300 font-bold w-10"},st.current.amount,"%"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{onClick:()=>{const s=st.current;s.bands=JSON.parse(JSON.stringify(EQPRO_DEFAULT_BANDS));s.selected=null;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"px-2 py-1 bg-slate-800 hover:bg-red-900/60 text-slate-300 border border-slate-700 rounded text-[10px]"},"Reset"),/*#__PURE__*/React.createElement("button",{onClick:()=>{const s=st.current;if(s.bands.length>=EQPRO_MAX_BANDS)return;s.bands.push({type:'peaking',freq:1000,gain:0,q:1.0,active:true});s.selected=s.bands.length-1;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"px-2 py-1 bg-teal-900/50 hover:bg-teal-800 text-teal-300 border border-teal-700 rounded text-[10px]"},"+ Band"))),/*#__PURE__*/React.createElement("div",{className:"relative"},/*#__PURE__*/React.createElement("canvas",{ref:canvasRef,className:"w-full h-60 block cursor-crosshair rounded-lg border border-slate-800 bg-slate-950",onPointerDown:onDown,onPointerMove:onMove,onPointerUp:onUp,onWheel:onWheel,onDoubleClick:onDblClick}),sel&&hudXY&&/*#__PURE__*/React.createElement("div",{className:"absolute z-10 w-64 rounded-xl p-3 text-[11px] font-mono space-y-2 pointer-events-auto cursor-move",style:{left:hudXY.hx,top:hudXY.hy,background:'rgba(15,23,42,0.94)',border:'1px solid rgba(45,212,191,0.3)',boxShadow:'0 10px 30px rgba(0,0,0,0.8)',backdropFilter:'blur(12px)'},onPointerDown:e=>{const s=st.current;s.dragHud=true;const r=e.currentTarget.getBoundingClientRect();s.hudOffsetX=e.clientX-r.left;s.hudOffsetY=e.clientY-r.top;e.currentTarget.setPointerCapture(e.pointerId);},onPointerMove:e=>{const s=st.current;if(!s.dragHud)return;const cv=canvasRef.current;const w=cv?cv.clientWidth:460;s.hudPos={hx:Math.max(0,Math.min(w-268,e.clientX-s.hudOffsetX-(cv?cv.getBoundingClientRect().left:0))),hy:Math.max(0,e.clientY-s.hudOffsetY-(cv?cv.getBoundingClientRect().top:0))};setTick(t=>t+1);},onPointerUp:e=>{st.current.dragHud=false;try{e.currentTarget.releasePointerCapture(e.pointerId);}catch(err){}}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-700/80 pb-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"w-4 h-4 rounded-full text-slate-950 font-bold flex items-center justify-center text-[10px]",style:{background:hudColor.badge}},selIdx+1),/*#__PURE__*/React.createElement("span",{className:"font-bold text-white uppercase tracking-wider"},"Band ",selIdx+1)),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{title:"Bypass band",onClick:()=>{const s=st.current;s.bands[s.selected].active=!s.bands[s.selected].active;applyAll(mm=>mm.setBand(s.selected,{active:s.bands[s.selected].active}));commit();setTick(t=>t+1);},className:`w-5 h-5 rounded flex items-center justify-center text-[10px] ${sel.active?'bg-slate-800 text-slate-400':'bg-amber-600 text-white'}`},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-power-off"})),/*#__PURE__*/React.createElement("button",{title:"Delete band",onClick:()=>{const s=st.current;s.bands.splice(s.selected,1);s.selected=null;applyAll(mm=>mm.syncBands(s.bands));commit();setTick(t=>t+1);},className:"w-5 h-5 rounded bg-slate-800 hover:bg-red-600 text-slate-400 hover:text-white flex items-center justify-center text-[10px]"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"})))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[9px] text-slate-400 uppercase tracking-widest mb-1"},"Filter Shape"),/*#__PURE__*/React.createElement("select",{value:sel.type,onChange:ev=>{const s=st.current;s.bands[s.selected].type=ev.target.value;applyAll(mm=>mm.setBand(s.selected,{type:ev.target.value}));commit();setTick(t=>t+1);},className:"w-full bg-slate-950 border border-slate-700 text-teal-300 rounded px-2 py-1 text-[11px] outline-none"},['peaking','highpass','lowpass','lowshelf','highshelf','notch','bandpass'].map(t=>/*#__PURE__*/React.createElement("option",{key:t,value:t},t==='peaking'?'Bell / Peaking':t==='highpass'?'Low Cut / High Pass':t==='lowpass'?'High Cut / Low Pass':t==='lowshelf'?'Low Shelf':t==='highshelf'?'High Shelf':t==='notch'?'Notch / Band Stop':'Band Pass')))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-2 text-center bg-slate-950/80 p-2 rounded-lg border border-slate-800"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"FREQ"),/*#__PURE__*/React.createElement("div",{className:"text-teal-300 font-bold text-[11px]"},sel.freq>=1000?(sel.freq/1000).toFixed(2)+' kHz':Math.round(sel.freq)+' Hz')),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"GAIN"),/*#__PURE__*/React.createElement("div",{className:"text-amber-400 font-bold text-[11px]"},eqproBandHasGain(sel.type)?(sel.gain>0?'+':'')+sel.gain.toFixed(1)+' dB':'0.0 dB')),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500"},"Q"),/*#__PURE__*/React.createElement("div",{className:"text-purple-400 font-bold text-[11px]"},sel.q.toFixed(1)))),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 text-center"},"Drag center: Freq & Gain \xB7 Wings/Wheel: Q \xB7 Dbl-click: add/delete"))));};const ExportModal=({open,onClose,exportSettings,setExportSettings,isExporting,onExport,onBounce})=>{if(!open)return null;return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[300] bg-black/70 backdrop-blur-sm flex items-center justify-center p-6",onClick:onClose},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-2xl bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl overflow-hidden",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"h-9 bg-slate-800/80 border-b border-slate-700 flex items-center justify-between px-3 select-none"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-cyan-300 font-mono flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"download-cloud",className:"w-3.5 h-3.5"})," EXPORT"),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-zinc-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"p-4 max-h-[72vh] overflow-y-auto space-y-3"},/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Ngu\u1ED3n"),/*#__PURE__*/React.createElement("select",{value:exportSettings.source,onChange:e=>setExportSettings(p=>({...p,source:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"project"},"Project (Mix)"),/*#__PURE__*/React.createElement("option",{value:"track_mix"},"Track Selection"),/*#__PURE__*/React.createElement("option",{value:"active_clip"},"Active Clip"),/*#__PURE__*/React.createElement("option",{value:"clip_selection"},"Clip Selection"))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"\u0110\u1ECBnh d\u1EA1ng"),/*#__PURE__*/React.createElement("select",{value:exportSettings.format,onChange:e=>setExportSettings(p=>({...p,format:e.target.value,sampleRate:'44100',bitDepth:'16',quality:'44khz'})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"wav"},"WAV"),/*#__PURE__*/React.createElement("option",{value:"mp3"},"MP3"),/*#__PURE__*/React.createElement("option",{value:"ogg"},"OGG")))),exportSettings.format==='wav'?/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"SR (Hz)"),/*#__PURE__*/React.createElement("select",{value:exportSettings.sampleRate,onChange:e=>setExportSettings(p=>({...p,sampleRate:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"22500"},"22500"),/*#__PURE__*/React.createElement("option",{value:"44100"},"44100"))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Bit"),/*#__PURE__*/React.createElement("select",{value:exportSettings.bitDepth,onChange:e=>setExportSettings(p=>({...p,bitDepth:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"8"},"8"),/*#__PURE__*/React.createElement("option",{value:"16"},"16"),/*#__PURE__*/React.createElement("option",{value:"24"},"24")))):/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Ch\u1EA5t l\u01B0\u1EE3ng"),/*#__PURE__*/React.createElement("select",{value:exportSettings.quality,onChange:e=>setExportSettings(p=>({...p,quality:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"44khz"},"44kHz"),/*#__PURE__*/React.createElement("option",{value:"lossless"},"Lossless"))),/*#__PURE__*/React.createElement("div",null)),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"K\xEAnh"),/*#__PURE__*/React.createElement("select",{value:exportSettings.channels,onChange:e=>setExportSettings(p=>({...p,channels:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"mono"},"Mono"),/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"))),/*#__PURE__*/React.createElement("div",null)),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2 pt-1"},/*#__PURE__*/React.createElement("button",{onClick:onBounce,disabled:isExporting,title:"Bounce realtime \u2014 file WAV \u0111\u1EA7y \u0111\u1EE7 MIDI + FX Rack + Mastering Chain (ch\u1EA1y l\u1EA1i project th\u1EADt)",className:"w-full py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"radio",className:"w-3 h-3"})," ",isExporting?'...':'Bounce MIDI'),/*#__PURE__*/React.createElement("button",{onClick:onExport,disabled:isExporting,className:"w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"download-cloud",className:"w-3 h-3"})," ",isExporting?'...':'Export')))));};const FXRackModal=({track,onUpdateTrack,onClose})=>{const[activeType,setActiveType]=React.useState('eq');const[addModuleOpen,setAddModuleOpen]=React.useState(false);const dragChainIndexRef=React.useRef(null);// Wave Observer scope state (unified_fx_rack_panel_update.md §III.3) const[scopeChannel,setScopeChannel]=React.useState('stereo');const[scopeMode,setScopeMode]=React.useState('waveform');const[scopeDuration,setScopeDuration]=React.useState(2.0);const[scopeZoom,setScopeZoom]=React.useState(0);const[scopePaused,setScopePaused]=React.useState(false);const scopeCanvasRef=React.useRef(null);const scopeMeterLRef=React.useRef(null);const scopeMeterRRef=React.useRef(null);const eqCurveRef=React.useRef(null);const scopeStateRef=React.useRef({L:null,R:null,head:0,len:0,tmpL:null,tmpR:null,freq:null});if(!track)return null;const chain=track.fxChain||[];const setChain=next=>{if(onUpdateTrack)onUpdateTrack(track.id,{fxChain:next});if(window.__rebuildTrackFxGraph)window.__rebuildTrackFxGraph(track.id);};const paramsOf=m=>({...(TRACK_FX_DEFAULTS[m.type]||{}),...(m.params||{})});const setParams=(idx,patch)=>{const next=chain.map((m,i)=>i===idx?{...m,params:{...paramsOf(m),...patch}}:m);setChain(next);};const toggleMod=idx=>{const next=chain.map((m,i)=>i===idx?{...m,active:!(m.active!==false)}:m);setChain(next);};const removeMod=idx=>setChain(chain.filter((_,i)=>i!==idx));const addMod=type=>{const def=TRACK_FX_DEFAULTS[type]||{};// Deep-clone default params so each chain entry owns its data (bands array // especially — shared references would corrupt other modules' state). const params=Array.isArray(def.bands)?{...def,bands:JSON.parse(JSON.stringify(def.bands))}:{...def};setChain([...chain,{type,active:true,params}]);setActiveType(type);setAddModuleOpen(false);};const applyPreset=key=>{const preset=TRACK_EQ_PRESETS[key];if(!preset)return;const idx=chain.findIndex(m=>m.type==='eq'&&m.active!==false);if(idx>=0)setParams(idx,{g1:preset.g[0],g2:preset.g[1],g3:preset.g[2],g4:preset.g[3]});};const activeMod=chain.find(m=>m.type===activeType)||chain[chain.length-1]||null;const activeIdx=chain.findIndex(m=>m===activeMod);const ap=activeMod?paramsOf(activeMod):{};const slider=(label,val,min,max,step,color,onChange,fmt)=>/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1"},/*#__PURE__*/React.createElement("div",{className:"flex justify-between text-[11px] font-mono"},/*#__PURE__*/React.createElement("span",{style:{color,fontWeight:700}},label),/*#__PURE__*/React.createElement("span",{className:"text-slate-300"},fmt?fmt(val):val)),/*#__PURE__*/React.createElement("input",{type:"range",min:min,max:max,step:step||0.1,value:val,onChange:e=>onChange(parseFloat(e.target.value)),className:"w-full h-1 cursor-pointer",style:{accentColor:color}}));// ── Wave Observer real-time scope render loop ── @@ -391,7 +407,7 @@ ctx.strokeStyle='rgba(51, 65, 85, 0.35)';for(let i=1;i<5;i++){const y=i/5*h;ctx. const bands=[{type:'lowshelf',f0:100,gain:ap.g1||0,q:0.7},{type:'peaking',f0:800,gain:ap.g2||0,q:0.7},{type:'peaking',f0:3200,gain:ap.g3||0,q:1.2},{type:'highshelf',f0:10000,gain:ap.g4||0,q:0.7}];const pts=[];const N=120;for(let i=0;i<=N;i++){const f=20*Math.pow(20000/20,i/N);let db=0;bands.forEach(b=>{const lf=Math.log(f/b.f0);if(b.type==='peaking'){db+=b.gain/(1+Math.pow(lf*b.q,2));}else if(b.type==='highshelf'){db+=b.gain/2*(1+2/Math.PI*Math.atan(lf/(1/b.q)));}else{db+=b.gain/2*(1-2/Math.PI*Math.atan(lf/(1/b.q)));}});const x=i/N*w;const y=midY-db/12*(h/2);pts.push([x,y]);}// fill ctx.beginPath();pts.forEach(([x,y],i)=>i===0?ctx.moveTo(x,y):ctx.lineTo(x,y));ctx.lineTo(w,h);ctx.lineTo(0,h);ctx.closePath();ctx.fillStyle='rgba(34, 211, 238, 0.10)';ctx.fill();// curve ctx.beginPath();pts.forEach(([x,y],i)=>i===0?ctx.moveTo(x,y):ctx.lineTo(x,y));ctx.strokeStyle='#22d3ee';ctx.lineWidth=2;ctx.stroke();// dB labels -ctx.fillStyle='#475569';ctx.font='9px monospace';ctx.fillText('+12 dB',4,midY-h/2+10);ctx.fillText('0 dB',4,midY+3);ctx.fillText('-12 dB',4,midY+h/2-4);ctx.fillText('20Hz',4,h-2);ctx.fillText('20kHz',w-38,h-2);},[ap.g1,ap.g2,ap.g3,ap.g4,activeMod]);return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[115] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:onClose},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-4xl bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl flex flex-col max-h-[90vh] text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("div",{className:"w-3 h-3 rounded-full bg-cyan-400 animate-pulse"}),/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider font-mono"},track.name," — FX RACK PANEL")),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-400 hover:text-white text-base"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"h-16 bg-slate-950 border border-slate-800 rounded-xl px-3 flex items-center gap-2 overflow-x-auto shrink-0 select-none"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-500 uppercase tracking-widest mr-1 font-mono shrink-0"},"CHAIN:"),chain.length===0&&/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 font-mono"},"Chưa có FX — bấm [+] để thêm (signal đi thẳng: Source → Fader)"),chain.map((m,idx)=>{const meta=TRACK_FX_META[m.type]||{name:m.type,icon:'circle',color:'#94a3b8',sub:''};const on=m.active!==false;return/*#__PURE__*/React.createElement("div",{key:idx,draggable:true,onDragStart:e=>{dragChainIndexRef.current=idx;e.dataTransfer.effectAllowed='move';},onDragOver:e=>{e.preventDefault();e.dataTransfer.dropEffect='move';},onDrop:e=>{e.preventDefault();const from=dragChainIndexRef.current;if(from!==null&&from!==idx){const next=[...chain];const mv=next.splice(from,1)[0];next.splice(idx,0,mv);setChain(next);}dragChainIndexRef.current=null;},onClick:()=>setActiveType(m.type),className:`w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${activeType===m.type?'border-2 border-cyan-400 bg-slate-800':'bg-slate-900 border border-slate-800 hover:border-slate-600'}`},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 min-w-0"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleMod(idx);},className:"w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",style:{backgroundColor:on?'#38bdf8':'#334155',color:on?'#0f172a':'#94a3b8'}},/*#__PURE__*/React.createElement("i",{"data-lucide":"power",className:"w-2.5 h-2.5"})),/*#__PURE__*/React.createElement("div",{className:"min-w-0"},/*#__PURE__*/React.createElement("div",{className:"text-[11px] font-bold text-slate-200 truncate"},meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[9px] font-mono truncate",style:{color:meta.color}},idx+1,". ",meta.sub))),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();removeMod(idx);},className:"text-slate-600 hover:text-red-400 text-xs px-0.5 shrink-0",title:"Xóa module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})));}),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(true),className:"w-16 h-12 rounded-lg border border-dashed border-slate-700 hover:border-cyan-500 flex items-center justify-center text-slate-500 hover:text-cyan-400 cursor-pointer transition-all bg-slate-900/40 shrink-0",title:"Thêm module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-slate-950 border border-slate-800 rounded-xl p-4 flex flex-col gap-4 min-h-[240px] overflow-y-auto oz-scrollbar"},/*#__PURE__*/React.createElement("div",{className:"h-8 bg-slate-900/80 border-b border-slate-800/80 px-3 flex items-center justify-between text-xs font-mono rounded-t-lg shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Active Module: ",/*#__PURE__*/React.createElement("strong",{className:"text-cyan-400"},activeMod?TRACK_FX_META[activeMod.type]?.name||activeMod.type:'—')),activeMod&&activeMod.type==='eq'&&/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"EQ Preset:"),/*#__PURE__*/React.createElement("select",{value:"flat",onChange:e=>applyPreset(e.target.value),className:"bg-slate-950 border border-slate-700 text-cyan-300 rounded px-2 py-0.5 text-xs outline-none focus:border-cyan-500"},Object.keys(TRACK_EQ_PRESETS).map(k=>/*#__PURE__*/React.createElement("option",{key:k,value:k},TRACK_EQ_PRESETS[k].name))))),!activeMod&&/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-500 font-mono text-center py-10"},"Chưa có module. Bấm [+] để thêm EQ / Compressor / Limiter / Exciter / Rebalance."),activeMod&&activeMod.type==='eq'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"space-y-3"},/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-4 gap-4"},[['g1','BAND 1 (LOW)','100 Hz','#22d3ee'],['g2','BAND 2 (MID LOW)','800 Hz','#fbbf24'],['g3','BAND 3 (MID HIGH)','3.2 kHz','#a855f7'],['g4','BAND 4 (HIGH)','10 kHz','#34d399']].map(([key,label,freq,color])=>/*#__PURE__*/React.createElement("div",{key:key,className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg space-y-2"},slider(label,ap[key]!==undefined?ap[key]:0,-12,12,0.1,color,v=>setParams(activeIdx,{[key]:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono text-center"},freq)))),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 rounded-lg p-2"},/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono mb-1 flex justify-between"},/*#__PURE__*/React.createElement("span",null,"VECTOR DISPLAY — EQ RESPONSE"),/*#__PURE__*/React.createElement("span",null,"20Hz – 20kHz")),/*#__PURE__*/React.createElement("canvas",{ref:eqCurveRef,className:"w-full h-[88px] block"}))),activeMod&&activeMod.type==='eqpro'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"space-y-3"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-teal-400 font-mono uppercase tracking-widest"},"PARAMETRIC / GRAPHIC EQ PRO — Pro-Q style interactive"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 font-mono"},"20Hz – 20kHz · ±24dB · ",EQPRO_MAX_BANDS," bands max")),/*#__PURE__*/React.createElement(InteractiveEqPro,{track:track,params:ap,onChange:next=>setParams(activeIdx,next),getModule:()=>window.__getTrackFxModule?window.__getTrackFxModule(track.id,'eqpro'):null,spectrumModules:()=>{const a=window.__getTrackFxModule?window.__getTrackFxModule(track.id,'eqpro'):null;const b=window.__getTrackSfFxModule?window.__getTrackSfFxModule(track.id,'eqpro'):null;return[a,b].filter(Boolean);}})),activeMod&&activeMod.type==='compressor'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('THRESHOLD',ap.threshold,-60,0,0.5,'#fbbf24',v=>setParams(activeIdx,{threshold:v}),v=>`${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('RATIO',ap.ratio,1,20,0.5,'#f59e0b',v=>setParams(activeIdx,{ratio:v}),v=>`${v.toFixed(1)} : 1`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('MAKE-UP',ap.makeup,0,12,0.1,'#f59e0b',v=>setParams(activeIdx,{makeup:v}),v=>`${v.toFixed(1)} dB`))),activeMod&&activeMod.type==='limiter'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('CEILING',ap.ceiling,-24,0,0.1,'#f43f5e',v=>setParams(activeIdx,{ceiling:v}),v=>`${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg flex items-center justify-center text-[10px] text-slate-500 font-mono text-center leading-snug"},"True-Peak limiting",/*#__PURE__*/React.createElement("br",null),"Ratio 20:1 · Knee 0dB")),activeMod&&activeMod.type==='exciter'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('DRIVE / MIX',ap.drive,0,100,1,'#c084fc',v=>setParams(activeIdx,{drive:v}),v=>`${v}%`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg flex items-center justify-center text-[10px] text-slate-500 font-mono text-center leading-snug"},"WaveShaper saturation",/*#__PURE__*/React.createElement("br",null),"High-pass 2kHz · 4× oversampled")),activeMod&&activeMod.type==='rebalance'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('MID GAIN',ap.mid,-12,12,0.1,'#38bdf8',v=>setParams(activeIdx,{mid:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('SIDE GAIN',ap.side,-12,12,0.1,'#22d3ee',v=>setParams(activeIdx,{side:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`)))),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800 rounded-xl p-3 flex flex-col gap-2 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-xs font-mono border-b border-slate-800/80 pb-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"font-bold text-white tracking-wider"},"WAVE OBSERVER"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 border border-cyan-800 text-cyan-400 px-1.5 py-0.5 rounded"},"Real-time Oscilloscope")),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 truncate max-w-[200px]"},"Context: ",track.name)),/*#__PURE__*/React.createElement("div",{className:"relative w-full h-36 bg-slate-950 border border-slate-900 rounded-lg overflow-hidden"},/*#__PURE__*/React.createElement("canvas",{ref:scopeCanvasRef,className:"w-full h-full block cursor-crosshair"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between bg-slate-900/90 border border-slate-800 rounded-lg px-3 py-1.5 text-xs font-mono flex-wrap gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 pr-3 border-r border-slate-800"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-400"},"Input"),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1 w-12"},/*#__PURE__*/React.createElement("div",{className:"h-1.5 bg-slate-950 rounded overflow-hidden flex"},/*#__PURE__*/React.createElement("div",{ref:scopeMeterLRef,className:"h-full bg-cyan-400 w-0 transition-all"})),/*#__PURE__*/React.createElement("div",{className:"h-1.5 bg-slate-950 rounded overflow-hidden flex"},/*#__PURE__*/React.createElement("div",{ref:scopeMeterRRef,className:"h-full bg-cyan-400 w-0 transition-all"})))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Channel:"),/*#__PURE__*/React.createElement("select",{value:scopeChannel,onChange:e=>setScopeChannel(e.target.value),className:"bg-slate-950 border border-slate-800 text-slate-300 rounded px-1.5 py-0.5 text-[11px] outline-none"},/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"),/*#__PURE__*/React.createElement("option",{value:"left"},"Left"),/*#__PURE__*/React.createElement("option",{value:"right"},"Right"),/*#__PURE__*/React.createElement("option",{value:"mid"},"Mid"),/*#__PURE__*/React.createElement("option",{value:"side"},"Side"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Mode:"),/*#__PURE__*/React.createElement("select",{value:scopeMode,onChange:e=>setScopeMode(e.target.value),className:"bg-slate-950 border border-slate-800 text-slate-300 rounded px-1.5 py-0.5 text-[11px] outline-none"},/*#__PURE__*/React.createElement("option",{value:"waveform"},"Waveform"),/*#__PURE__*/React.createElement("option",{value:"lissajous"},"Lissajous"),/*#__PURE__*/React.createElement("option",{value:"spectrum"},"Spectrum"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Duration:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold text-[11px] w-12"},scopeDuration.toFixed(2),"s"),/*#__PURE__*/React.createElement("input",{type:"range",min:"0.1",max:"5.0",step:"0.1",value:scopeDuration,onChange:e=>setScopeDuration(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer",style:{accentColor:'#38bdf8'}})),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"V.Zoom:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold text-[11px] w-12"},scopeZoom>0?'+':'',scopeZoom.toFixed(1)," dB"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"20",step:"0.5",value:scopeZoom,onChange:e=>setScopeZoom(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer",style:{accentColor:'#38bdf8'}})),/*#__PURE__*/React.createElement("button",{onClick:()=>setScopePaused(p=>!p),className:`px-3 py-1 font-semibold rounded text-[11px] border transition-colors ${scopePaused?'bg-amber-600 border-amber-400 text-white':'bg-slate-800 hover:bg-slate-700 text-slate-300 border-slate-700'}`},scopePaused?'Resume':'Pause')))),addModuleOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[120] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:()=>setAddModuleOpen(false)},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-lg bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider font-mono"},"THÊM MODULE VÀO FX CHAIN"),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(false),className:"text-slate-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-3 text-xs"},Object.keys(TRACK_FX_META).map(t=>{const meta=TRACK_FX_META[t];return/*#__PURE__*/React.createElement("button",{key:t,onClick:()=>addMod(t),className:"p-3 bg-slate-950 hover:bg-slate-800 border border-slate-800 rounded-xl text-left space-y-1 transition-colors"},/*#__PURE__*/React.createElement("div",{className:"font-bold flex items-center gap-1.5",style:{color:meta.color}},/*#__PURE__*/React.createElement("i",{"data-lucide":meta.icon,className:"w-3.5 h-3.5"})," ",meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-400"},meta.sub));})))));};// ────────────────────────────────────────────── +ctx.fillStyle='#475569';ctx.font='9px monospace';ctx.fillText('+12 dB',4,midY-h/2+10);ctx.fillText('0 dB',4,midY+3);ctx.fillText('-12 dB',4,midY+h/2-4);ctx.fillText('20Hz',4,h-2);ctx.fillText('20kHz',w-38,h-2);},[ap.g1,ap.g2,ap.g3,ap.g4,activeMod]);return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[115] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:onClose},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-4xl bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl flex flex-col max-h-[90vh] text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("div",{className:"w-3 h-3 rounded-full bg-cyan-400 animate-pulse"}),/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider font-mono"},track.name," \u2014 FX RACK PANEL")),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-400 hover:text-white text-base"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"h-16 bg-slate-950 border border-slate-800 rounded-xl px-3 flex items-center gap-2 overflow-x-auto shrink-0 select-none"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-500 uppercase tracking-widest mr-1 font-mono shrink-0"},"CHAIN:"),chain.length===0&&/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 font-mono"},"Ch\u01B0a c\xF3 FX \u2014 b\u1EA5m [+] \u0111\u1EC3 th\xEAm (signal \u0111i th\u1EB3ng: Source \u2192 Fader)"),chain.map((m,idx)=>{const meta=TRACK_FX_META[m.type]||{name:m.type,icon:'circle',color:'#94a3b8',sub:''};const on=m.active!==false;return/*#__PURE__*/React.createElement("div",{key:idx,draggable:true,onDragStart:e=>{dragChainIndexRef.current=idx;e.dataTransfer.effectAllowed='move';},onDragOver:e=>{e.preventDefault();e.dataTransfer.dropEffect='move';},onDrop:e=>{e.preventDefault();const from=dragChainIndexRef.current;if(from!==null&&from!==idx){const next=[...chain];const mv=next.splice(from,1)[0];next.splice(idx,0,mv);setChain(next);}dragChainIndexRef.current=null;},onClick:()=>setActiveType(m.type),className:`w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${activeType===m.type?'border-2 border-cyan-400 bg-slate-800':'bg-slate-900 border border-slate-800 hover:border-slate-600'}`},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 min-w-0"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleMod(idx);},className:"w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",style:{backgroundColor:on?'#38bdf8':'#334155',color:on?'#0f172a':'#94a3b8'}},/*#__PURE__*/React.createElement("i",{"data-lucide":"power",className:"w-2.5 h-2.5"})),/*#__PURE__*/React.createElement("div",{className:"min-w-0"},/*#__PURE__*/React.createElement("div",{className:"text-[11px] font-bold text-slate-200 truncate"},meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[9px] font-mono truncate",style:{color:meta.color}},idx+1,". ",meta.sub))),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();removeMod(idx);},className:"text-slate-600 hover:text-red-400 text-xs px-0.5 shrink-0",title:"X\xF3a module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})));}),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(true),className:"w-16 h-12 rounded-lg border border-dashed border-slate-700 hover:border-cyan-500 flex items-center justify-center text-slate-500 hover:text-cyan-400 cursor-pointer transition-all bg-slate-900/40 shrink-0",title:"Th\xEAm module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-slate-950 border border-slate-800 rounded-xl p-4 flex flex-col gap-4 min-h-[240px] overflow-y-auto oz-scrollbar"},/*#__PURE__*/React.createElement("div",{className:"h-8 bg-slate-900/80 border-b border-slate-800/80 px-3 flex items-center justify-between text-xs font-mono rounded-t-lg shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Active Module: ",/*#__PURE__*/React.createElement("strong",{className:"text-cyan-400"},activeMod?TRACK_FX_META[activeMod.type]?.name||activeMod.type:'—')),activeMod&&activeMod.type==='eq'&&/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"EQ Preset:"),/*#__PURE__*/React.createElement("select",{value:"flat",onChange:e=>applyPreset(e.target.value),className:"bg-slate-950 border border-slate-700 text-cyan-300 rounded px-2 py-0.5 text-xs outline-none focus:border-cyan-500"},Object.keys(TRACK_EQ_PRESETS).map(k=>/*#__PURE__*/React.createElement("option",{key:k,value:k},TRACK_EQ_PRESETS[k].name))))),!activeMod&&/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-500 font-mono text-center py-10"},"Ch\u01B0a c\xF3 module. B\u1EA5m [+] \u0111\u1EC3 th\xEAm EQ / Compressor / Limiter / Exciter / Rebalance."),activeMod&&activeMod.type==='eq'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"space-y-3"},/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-4 gap-4"},[['g1','BAND 1 (LOW)','100 Hz','#22d3ee'],['g2','BAND 2 (MID LOW)','800 Hz','#fbbf24'],['g3','BAND 3 (MID HIGH)','3.2 kHz','#a855f7'],['g4','BAND 4 (HIGH)','10 kHz','#34d399']].map(([key,label,freq,color])=>/*#__PURE__*/React.createElement("div",{key:key,className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg space-y-2"},slider(label,ap[key]!==undefined?ap[key]:0,-12,12,0.1,color,v=>setParams(activeIdx,{[key]:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono text-center"},freq)))),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 rounded-lg p-2"},/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono mb-1 flex justify-between"},/*#__PURE__*/React.createElement("span",null,"VECTOR DISPLAY \u2014 EQ RESPONSE"),/*#__PURE__*/React.createElement("span",null,"20Hz \u2013 20kHz")),/*#__PURE__*/React.createElement("canvas",{ref:eqCurveRef,className:"w-full h-[88px] block"}))),activeMod&&activeMod.type==='eqpro'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"space-y-3"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-teal-400 font-mono uppercase tracking-widest"},"PARAMETRIC / GRAPHIC EQ PRO \u2014 Pro-Q style interactive"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 font-mono"},"20Hz \u2013 20kHz \xB7 \xB124dB \xB7 ",EQPRO_MAX_BANDS," bands max")),/*#__PURE__*/React.createElement(InteractiveEqPro,{track:track,params:ap,onChange:next=>setParams(activeIdx,next),getModule:()=>window.__getTrackFxModule?window.__getTrackFxModule(track.id,'eqpro'):null,spectrumModules:()=>{const a=window.__getTrackFxModule?window.__getTrackFxModule(track.id,'eqpro'):null;const b=window.__getTrackSfFxModule?window.__getTrackSfFxModule(track.id,'eqpro'):null;return[a,b].filter(Boolean);}})),activeMod&&activeMod.type==='compressor'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('THRESHOLD',ap.threshold,-60,0,0.5,'#fbbf24',v=>setParams(activeIdx,{threshold:v}),v=>`${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('RATIO',ap.ratio,1,20,0.5,'#f59e0b',v=>setParams(activeIdx,{ratio:v}),v=>`${v.toFixed(1)} : 1`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('MAKE-UP',ap.makeup,0,12,0.1,'#f59e0b',v=>setParams(activeIdx,{makeup:v}),v=>`${v.toFixed(1)} dB`))),activeMod&&activeMod.type==='limiter'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('CEILING',ap.ceiling,-24,0,0.1,'#f43f5e',v=>setParams(activeIdx,{ceiling:v}),v=>`${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg flex items-center justify-center text-[10px] text-slate-500 font-mono text-center leading-snug"},"True-Peak limiting",/*#__PURE__*/React.createElement("br",null),"Ratio 20:1 \xB7 Knee 0dB")),activeMod&&activeMod.type==='exciter'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('DRIVE / MIX',ap.drive,0,100,1,'#c084fc',v=>setParams(activeIdx,{drive:v}),v=>`${v}%`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg flex items-center justify-center text-[10px] text-slate-500 font-mono text-center leading-snug"},"WaveShaper saturation",/*#__PURE__*/React.createElement("br",null),"High-pass 2kHz \xB7 4\xD7 oversampled")),activeMod&&activeMod.type==='rebalance'&&activeIdx>=0&&/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('MID GAIN',ap.mid,-12,12,0.1,'#38bdf8',v=>setParams(activeIdx,{mid:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`)),/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-3 rounded-lg"},slider('SIDE GAIN',ap.side,-12,12,0.1,'#22d3ee',v=>setParams(activeIdx,{side:v}),v=>`${v>0?'+':''}${v.toFixed(1)} dB`)))),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800 rounded-xl p-3 flex flex-col gap-2 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-xs font-mono border-b border-slate-800/80 pb-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"font-bold text-white tracking-wider"},"WAVE OBSERVER"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 border border-cyan-800 text-cyan-400 px-1.5 py-0.5 rounded"},"Real-time Oscilloscope")),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 truncate max-w-[200px]"},"Context: ",track.name)),/*#__PURE__*/React.createElement("div",{className:"relative w-full h-36 bg-slate-950 border border-slate-900 rounded-lg overflow-hidden"},/*#__PURE__*/React.createElement("canvas",{ref:scopeCanvasRef,className:"w-full h-full block cursor-crosshair"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between bg-slate-900/90 border border-slate-800 rounded-lg px-3 py-1.5 text-xs font-mono flex-wrap gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 pr-3 border-r border-slate-800"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-400"},"Input"),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1 w-12"},/*#__PURE__*/React.createElement("div",{className:"h-1.5 bg-slate-950 rounded overflow-hidden flex"},/*#__PURE__*/React.createElement("div",{ref:scopeMeterLRef,className:"h-full bg-cyan-400 w-0 transition-all"})),/*#__PURE__*/React.createElement("div",{className:"h-1.5 bg-slate-950 rounded overflow-hidden flex"},/*#__PURE__*/React.createElement("div",{ref:scopeMeterRRef,className:"h-full bg-cyan-400 w-0 transition-all"})))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Channel:"),/*#__PURE__*/React.createElement("select",{value:scopeChannel,onChange:e=>setScopeChannel(e.target.value),className:"bg-slate-950 border border-slate-800 text-slate-300 rounded px-1.5 py-0.5 text-[11px] outline-none"},/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"),/*#__PURE__*/React.createElement("option",{value:"left"},"Left"),/*#__PURE__*/React.createElement("option",{value:"right"},"Right"),/*#__PURE__*/React.createElement("option",{value:"mid"},"Mid"),/*#__PURE__*/React.createElement("option",{value:"side"},"Side"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Mode:"),/*#__PURE__*/React.createElement("select",{value:scopeMode,onChange:e=>setScopeMode(e.target.value),className:"bg-slate-950 border border-slate-800 text-slate-300 rounded px-1.5 py-0.5 text-[11px] outline-none"},/*#__PURE__*/React.createElement("option",{value:"waveform"},"Waveform"),/*#__PURE__*/React.createElement("option",{value:"lissajous"},"Lissajous"),/*#__PURE__*/React.createElement("option",{value:"spectrum"},"Spectrum"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"Duration:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold text-[11px] w-12"},scopeDuration.toFixed(2),"s"),/*#__PURE__*/React.createElement("input",{type:"range",min:"0.1",max:"5.0",step:"0.1",value:scopeDuration,onChange:e=>setScopeDuration(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer",style:{accentColor:'#38bdf8'}})),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-500 text-[10px]"},"V.Zoom:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold text-[11px] w-12"},scopeZoom>0?'+':'',scopeZoom.toFixed(1)," dB"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"20",step:"0.5",value:scopeZoom,onChange:e=>setScopeZoom(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer",style:{accentColor:'#38bdf8'}})),/*#__PURE__*/React.createElement("button",{onClick:()=>setScopePaused(p=>!p),className:`px-3 py-1 font-semibold rounded text-[11px] border transition-colors ${scopePaused?'bg-amber-600 border-amber-400 text-white':'bg-slate-800 hover:bg-slate-700 text-slate-300 border-slate-700'}`},scopePaused?'Resume':'Pause')))),addModuleOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[120] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:()=>setAddModuleOpen(false)},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-lg bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider font-mono"},"TH\xCAM MODULE V\xC0O FX CHAIN"),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(false),className:"text-slate-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-3 text-xs"},Object.keys(TRACK_FX_META).map(t=>{const meta=TRACK_FX_META[t];return/*#__PURE__*/React.createElement("button",{key:t,onClick:()=>addMod(t),className:"p-3 bg-slate-950 hover:bg-slate-800 border border-slate-800 rounded-xl text-left space-y-1 transition-colors"},/*#__PURE__*/React.createElement("div",{className:"font-bold flex items-center gap-1.5",style:{color:meta.color}},/*#__PURE__*/React.createElement("i",{"data-lucide":meta.icon,className:"w-3.5 h-3.5"})," ",meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-400"},meta.sub));})))));};// ────────────────────────────────────────────── // MASTERING MODAL COMPONENT (from md/45_MASTERING_MODULE.md) // ────────────────────────────────────────────── const MasteringModal=({isOpen,onClose,masteringSettings,setMasteringSettings})=>{const ozState=masteringSettings;const setOzState=setMasteringSettings;const[isPlaying,setIsPlaying]=React.useState(false);const masterConnected=ozState.masterConnected;const setMasterConnected=val=>{setOzState(prev=>({...prev,masterConnected:typeof val==='function'?val(prev.masterConnected):val}));};const audioRef=React.useRef({source:null});const eqCanvasRef=React.useRef(null);const imagerCanvasRef=React.useRef(null);const inMeterCanvasRef=React.useRef(null);const outMeterCanvasRef=React.useRef(null);const animFrameRef=React.useRef(null);const lufsAccRef=React.useRef([]);const knobsInitializedRef=React.useRef(false);// Wave Observer Refs & States @@ -429,7 +445,7 @@ const dragChainIndexRef=React.useRef(null);const[addModuleOpen,setAddModuleOpen] const MODULE_META={eq:{name:'Dynamic EQ',sub:'4-Band Peak',icon:'activity',color:'#22d3ee'},eqpro:{name:'Parametric / Graphic EQ PRO',sub:'Pro-Q style · 8 bands · interactive',icon:'chart-area',color:'#2dd4bf'},imager:{name:'Imager',sub:'4-Band Width',icon:'radio',color:'#a855f7'},maximizer:{name:'Maximizer',sub:'IRC IV True Peak',icon:'gauge',color:'#34d399'},compressor:{name:'Bus Compressor',sub:'Glue & Punch',icon:'compress',color:'#fbbf24'},limiter:{name:'Brickwall Limiter',sub:'True-Peak 20:1',icon:'shield-half',color:'#f43f5e'},exciter:{name:'Harmonic Exciter',sub:'Saturation & Air',icon:'wand-2',color:'#c084fc'},rebalance:{name:'Master Rebalance',sub:'M/S Balance',icon:'sliders-horizontal',color:'#38bdf8'}};const chainFlag=type=>type==='eq'?'eqActive':type==='eqpro'?'eqproActive':type==='imager'?'imagerActive':type==='maximizer'?'maximizerActive':type==='compressor'?'compActive':type==='limiter'?'limActive':type==='exciter'?'excActive':'rebalActive';const chainActive=type=>!!ozState[chainFlag(type)];const toggleChainModule=modId=>{setOzState(prev=>{const chain=prev.chain.map(m=>{if(m.id!==modId)return m;const next=!m.active;return{...m,active:next};});const flags={};chain.forEach(m=>{flags[chainFlag(m.type)]=!!m.active;});return{...prev,chain,...flags};});};const removeChainModule=modId=>{setOzState(prev=>{let chain=prev.chain.filter(m=>m.id!==modId);if(chain.length===0)chain=DEFAULT_MASTER_CHAIN.map(m=>({...m}));// keep ≥1 const flags={};chain.forEach(m=>{flags[chainFlag(m.type)]=!!m.active;});const activeModule=prev.activeModule;return{...prev,chain,...flags,activeModule:chain.some(m=>m.type===activeModule)?activeModule:chain[chain.length-1].type};});};const reorderChain=(fromIdx,toIdx)=>{if(fromIdx===toIdx)return;setOzState(prev=>{const chain=[...prev.chain];const moved=chain.splice(fromIdx,1)[0];chain.splice(toIdx,0,moved);return{...prev,chain};});};const addModuleToChain=type=>{const meta=MODULE_META[type];const id='mod_'+type+'_'+Date.now();const entry={id,type,name:meta.name,active:true};if(type==='eqpro')entry.params={amount:100,bands:JSON.parse(JSON.stringify(EQPRO_DEFAULT_BANDS))};setOzState(prev=>({...prev,chain:[...(prev.chain||[]),entry],[chainFlag(type)]:true,activeModule:type}));setAddModuleOpen(false);};const updateChainEntryParams=(modId,patch)=>{setOzState(prev=>({...prev,chain:(prev.chain||[]).map(m=>m.id===modId?{...m,params:{...(m.params||{}),...patch}}:m)}));};// ── EQ Preset Library (mastering_expand.md §II.1) ── const EQ_PRESET_LIBRARY={flat:{name:'Flat / Reset',bands:[{id:1,type:'lowshelf',freq:100,gain:0.0,q:0.7},{id:2,type:'peaking',freq:800,gain:0.0,q:0.7},{id:3,type:'peaking',freq:3200,gain:0.0,q:1.2},{id:4,type:'highshelf',freq:10000,gain:0.0,q:0.7}]},vocal_clarity:{name:'Vocal Unmask & Clarity',bands:[{id:1,type:'lowshelf',freq:90,gain:-2.5,q:0.7},{id:2,type:'peaking',freq:500,gain:-1.8,q:1.0},{id:3,type:'peaking',freq:2800,gain:3.2,q:1.2},{id:4,type:'highshelf',freq:12000,gain:2.0,q:0.7}]},bass_punch:{name:'EDM Low-End Punch',bands:[{id:1,type:'lowshelf',freq:80,gain:4.0,q:0.8},{id:2,type:'peaking',freq:300,gain:-3.0,q:1.4},{id:3,type:'peaking',freq:4000,gain:1.5,q:1.0},{id:4,type:'highshelf',freq:10000,gain:1.0,q:0.7}]},warm_tape:{name:'Warm Vintage Analog',bands:[{id:1,type:'lowshelf',freq:120,gain:2.0,q:0.6},{id:2,type:'peaking',freq:1500,gain:1.0,q:0.5},{id:3,type:'peaking',freq:5000,gain:-2.0,q:1.0},{id:4,type:'highshelf',freq:8000,gain:-3.0,q:0.7}]}};const applyEQPreset=presetKey=>{const preset=EQ_PRESET_LIBRARY[presetKey];if(!preset)return;const bus=masterBus;if(bus&&bus.eqLowFilter){const now=audioCtx?audioCtx.currentTime:0;const filters=[bus.eqLowFilter,bus.eqMid1Filter,bus.eqMid2Filter,bus.eqHighFilter];preset.bands.forEach((bd,i)=>{const f=filters[i];if(!f)return;try{f.frequency.setTargetAtTime(bd.freq,now,0.02);f.gain.setTargetAtTime(bd.gain,now,0.02);f.Q.setTargetAtTime(bd.q,now,0.02);}catch(e){}});}// Sync UI knobs + canvas: [eqLowGain, eqMid1Gain, eqMid2Gain, eqHighGain] -setOzState(prev=>({...prev,eqLowGain:preset.bands[0]?preset.bands[0].gain:prev.eqLowGain,eqMid1Gain:preset.bands[1]?preset.bands[1].gain:prev.eqMid1Gain,eqMid2Gain:preset.bands[2]?preset.bands[2].gain:prev.eqMid2Gain,eqHighGain:preset.bands[3]?preset.bands[3].gain:prev.eqHighGain,eqPreset:presetKey}));};const bandKnob=(param,min,max,val,unit,label,freq,color,filterType)=>/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-2 flex flex-col justify-between items-center rounded-lg"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[10px] font-bold w-full",style:{color}},/*#__PURE__*/React.createElement("span",null,label),/*#__PURE__*/React.createElement("span",{className:"text-[9px] font-mono text-slate-400"},freq)),/*#__PURE__*/React.createElement("div",{className:"my-1"},/*#__PURE__*/React.createElement(MasteringKnob,{param:param,min:min,max:max,value:ozState[param]!==undefined?ozState[param]:val,unit:unit,color:color,onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono text-center mt-1"},filterType));return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm",onClick:onClose},/*#__PURE__*/React.createElement("div",{id:"masteringModalBody",className:"flex flex-col bg-slate-950 text-slate-200 w-[95vw] h-[92vh] max-w-[1400px] rounded-2xl border border-slate-800/80 shadow-2xl overflow-hidden",onClick:e=>e.stopPropagation(),style:{fontFamily:"'Inter', sans-serif"}},/*#__PURE__*/React.createElement("header",{className:"h-12 bg-slate-900/90 border-b border-slate-800/80 flex items-center justify-between px-4 z-30 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("div",{className:"w-7 h-7 rounded-lg bg-cyan-600 flex items-center justify-center font-bold text-white shadow-lg shadow-cyan-950"},/*#__PURE__*/React.createElement("i",{"data-lucide":"zap",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("h1",{className:"text-xs font-bold tracking-wider text-white flex items-center gap-2"},"MASTERING SUITE ",/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 text-cyan-400 border border-cyan-800/80 px-1.5 py-0.5 rounded oz-font-mono"},"WEB MASTERING V10.5")))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 bg-slate-950 px-3 py-1 rounded-lg border border-slate-800/80"},/*#__PURE__*/React.createElement("button",{onClick:startAudioDemo,className:"px-3 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-xs font-semibold flex items-center gap-1.5 transition-all shadow-md shadow-cyan-950"},/*#__PURE__*/React.createElement("i",{"data-lucide":"play",className:"w-3 h-3"})," ",/*#__PURE__*/React.createElement("span",null,"Play Reference")),/*#__PURE__*/React.createElement("button",{onClick:stopAudioDemo,className:"px-3 py-1 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded text-xs font-semibold flex items-center gap-1.5 transition-all"},/*#__PURE__*/React.createElement("i",{"data-lucide":"square",className:"w-3 h-3"})," ",/*#__PURE__*/React.createElement("span",null,"Stop")),/*#__PURE__*/React.createElement("div",{className:"h-4 w-[1px] bg-slate-800 mx-1"}),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 text-xs oz-font-mono"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 text-[11px]"},"Preset:"),/*#__PURE__*/React.createElement("select",{className:"bg-slate-900 border border-slate-700 rounded px-2 py-0.5 text-xs text-cyan-300 outline-none focus:border-cyan-500"},/*#__PURE__*/React.createElement("option",{value:"adaptive"},"Adaptive Dynamic Master"),/*#__PURE__*/React.createElement("option",{value:"edm_club"},"EDM / Club Punch Maximizer"),/*#__PURE__*/React.createElement("option",{value:"wide_space"},"Cinematic Stereo Expansion"),/*#__PURE__*/React.createElement("option",{value:"transparent"},"Transparent High-Clarity Limiter")))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 text-xs oz-font-mono"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 text-[11px]"},"Target LUFS:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold bg-slate-950 border border-cyan-900/60 px-2 py-0.5 rounded"},"-11.0 LUFS"),/*#__PURE__*/React.createElement("button",{onClick:()=>{getAudioContext();setMasterConnected(prev=>!prev);},className:`px-2 py-0.5 rounded text-[10px] font-bold border transition-colors ${masterConnected?'bg-emerald-700 border-emerald-500 text-white':'bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700'}`},masterConnected?'Master ON':'Master OFF'),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"px-2 py-0.5 rounded text-[10px] font-bold border transition-colors bg-slate-800 border-slate-700 text-slate-300 hover:bg-[#c2410c] hover:border-red-600 hover:text-white"},"Đóng"),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-500 hover:text-slate-300 ml-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"})))),/*#__PURE__*/React.createElement("div",{className:"h-16 bg-slate-950 border-b border-slate-800 px-4 flex items-center gap-2 overflow-x-auto shrink-0 oz-scrollbar"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-500 uppercase tracking-widest mr-1 oz-font-mono shrink-0"},"CHAIN:"),(ozState.chain||[]).map((mod,idx)=>{const meta=MODULE_META[mod.type]||{name:mod.type,sub:'',icon:'circle',color:'#94a3b8'};const isEditing=ozState.activeModule===mod.type;const isOn=chainActive(mod.type);return/*#__PURE__*/React.createElement("div",{key:mod.id,draggable:true,onDragStart:e=>{dragChainIndexRef.current=idx;e.dataTransfer.effectAllowed='move';},onDragOver:e=>{e.preventDefault();e.dataTransfer.dropEffect='move';},onDrop:e=>{e.preventDefault();const from=dragChainIndexRef.current;if(from!==null&&from!==idx)reorderChain(from,idx);dragChainIndexRef.current=null;},onClick:()=>switchModule(mod.type),className:`w-40 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${isEditing?'oz-card-active':'oz-card'}`},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 min-w-0"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleChainModule(mod.id);},className:"w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",style:{backgroundColor:isOn?'#38bdf8':'#334155',color:isOn?'#0f172a':'#94a3b8'}},/*#__PURE__*/React.createElement("i",{"data-lucide":"power",className:"w-2.5 h-2.5"})),/*#__PURE__*/React.createElement("div",{className:"min-w-0"},/*#__PURE__*/React.createElement("div",{className:"text-[11px] font-bold text-slate-200 truncate"},meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[9px] oz-font-mono truncate",style:{color:meta.color}},idx+1,". ",meta.sub))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":meta.icon,className:"w-3 h-3 text-slate-500"}),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();removeChainModule(mod.id);},className:"text-slate-600 hover:text-red-400 text-xs px-0.5",title:"Xóa module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));}),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(true),className:"w-24 h-12 rounded-lg border border-dashed border-slate-800 flex items-center justify-center text-slate-600 hover:text-slate-400 hover:border-slate-600 cursor-pointer transition-all shrink-0",title:"Thêm module vào chain"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("main",{className:"flex-1 flex overflow-hidden min-h-0"},/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col bg-slate-950 relative overflow-y-auto oz-scrollbar"},/*#__PURE__*/React.createElement("div",{className:"h-9 bg-slate-900/60 border-b border-slate-800/80 px-4 flex items-center justify-between text-xs oz-font-mono shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"headphones",className:"w-3 h-3 text-cyan-400"})," Delta Listen"),/*#__PURE__*/React.createElement("select",{className:"bg-slate-950 border border-slate-800 rounded px-2 py-0.5 text-[11px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"irc4"},"IRC IV - Classic"),/*#__PURE__*/React.createElement("option",{value:"irc3"},"IRC III - Balanced"),/*#__PURE__*/React.createElement("option",{value:"irc2"},"IRC II - Crisp"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 text-[11px]"},ozState.activeModule==='eq'&&/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"EQ Presets:"),/*#__PURE__*/React.createElement("select",{value:ozState.eqPreset||'flat',onChange:e=>applyEQPreset(e.target.value),className:"bg-slate-950 border border-slate-700 text-cyan-300 rounded px-2 py-0.5 text-[11px] outline-none focus:border-cyan-500"},Object.keys(EQ_PRESET_LIBRARY).map(k=>/*#__PURE__*/React.createElement("option",{key:k,value:k},EQ_PRESET_LIBRARY[k].name)))),/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Learn Input Gain:"),/*#__PURE__*/React.createElement("button",{className:"bg-cyan-950 text-cyan-300 border border-cyan-800/80 px-2 py-0.5 rounded font-bold hover:bg-cyan-900 transition-colors"},"-11.0 LUFS"))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='eq'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"relative w-full h-64 bg-slate-950 border border-slate-800/90 rounded-xl overflow-hidden shadow-inner cursor-crosshair"},/*#__PURE__*/React.createElement("canvas",{ref:eqCanvasRef,className:"w-full h-full block"})),/*#__PURE__*/React.createElement("div",{className:"oz-panel p-3 rounded-xl grid grid-cols-4 gap-3"},bandKnob('eqLowGain',-12,12,1.5,'dB','BAND 1 (LOW)','100 Hz','#22d3ee','Shelf Filter'),bandKnob('eqMid1Gain',-12,12,-1.0,'dB','BAND 2 (MID LOW)','822 Hz','#fbbf24','Dynamic Bell (Q: 0.7)'),bandKnob('eqMid2Gain',-12,12,2.0,'dB','BAND 3 (MID HIGH)','3.2 kHz','#a855f7','Dynamic Bell (Q: 1.2)'),bandKnob('eqHighGain',-12,12,1.8,'dB','BAND 4 (HIGH)','10 kHz','#34d399','High Shelf'))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='eqpro'?'':'hidden'}`},(()=>{const chainMods=ozState.chain||[];const cm=chainMods.find(m=>m.type==='eqpro'&&m.active!==false)||chainMods.find(m=>m.type==='eqpro');if(!cm)return/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-500 font-mono py-10 text-center"},"Chưa có module EQ PRO trong chain — bấm [+] để thêm.");return/*#__PURE__*/React.createElement(InteractiveEqPro,{track:null,params:cm.params||{},onChange:next=>updateChainEntryParams(cm.id,next),getModule:()=>masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null,spectrumModules:()=>{const m=masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null;return m?[m]:[];},applyTo:fn=>{const m=masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null;if(m)fn(m);}});})()),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='imager'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-12 gap-4 flex-1"},/*#__PURE__*/React.createElement("div",{className:"col-span-8 bg-slate-950 border border-slate-800/90 rounded-xl p-3 flex flex-col relative shadow-inner"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-cyan-400 uppercase oz-font-mono mb-2 flex items-center justify-between w-full"},/*#__PURE__*/React.createElement("span",null,/*#__PURE__*/React.createElement("i",{"data-lucide":"radio",className:"w-3.5 h-3.5"})," Polar Vectorscope & Correlation Meter"),/*#__PURE__*/React.createElement("span",{className:"text-[11px] text-slate-400 normal-case"},"Corr: ",/*#__PURE__*/React.createElement("span",{id:"corrText",className:"font-bold text-emerald-400"},"1.00"))),/*#__PURE__*/React.createElement("div",{className:"flex-1 relative w-full h-56 flex items-center justify-center"},/*#__PURE__*/React.createElement("canvas",{ref:imagerCanvasRef,className:"w-full h-full block"}))),/*#__PURE__*/React.createElement("div",{className:"col-span-4 oz-panel p-3 rounded-xl flex flex-col justify-between"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-slate-300 uppercase oz-font-mono"},"4-Band Stereo Width"),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-500 oz-font-mono leading-snug"},"0% = Mono · 100% = Original · 200% = 2× Width"),/*#__PURE__*/React.createElement("div",{className:"space-y-3 my-auto"},[{id:'w1',label:'Band 1 (20-100Hz)',color:'#22d3ee',val:ozState.w1},{id:'w2',label:'Band 2 (100Hz-1kHz)',color:'#fbbf24',val:ozState.w2},{id:'w3',label:'Band 3 (1k-6kHz)',color:'#a855f7',val:ozState.w3},{id:'w4',label:'Band 4 (6k-20kHz)',color:'#34d399',val:ozState.w4}].map(b=>/*#__PURE__*/React.createElement("div",{key:b.id},/*#__PURE__*/React.createElement("div",{className:"flex justify-between text-[11px] font-mono mb-1"},/*#__PURE__*/React.createElement("span",{style:{color:b.color,fontWeight:700}},b.label),/*#__PURE__*/React.createElement("span",{id:b.id+'Val'},b.val,"%")),/*#__PURE__*/React.createElement("input",{type:"range",min:"0",max:"200",value:b.val,onChange:e=>setOzState(prev=>({...prev,[b.id]:parseInt(e.target.value)})),className:"w-full h-1 cursor-pointer",style:{accentColor:b.color}}))))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='maximizer'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-slate-400 uppercase oz-font-mono mb-3"},"Maximizer Gain Boost"),/*#__PURE__*/React.createElement("div",{className:"my-2"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxGain",min:0,max:12,value:ozState.maxGain,unit:"dB",color:"#22d3ee",size:"large",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"w-full mt-4 bg-slate-950 p-2 rounded-lg border border-slate-800 text-xs oz-font-mono flex flex-col gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center w-full"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Ceiling Level:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold"},ozState.ceiling.toFixed(2)," dB")),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"0",step:"0.1",value:ozState.ceiling,onChange:e=>setOzState(prev=>({...prev,ceiling:parseFloat(e.target.value)})),className:"w-full h-1 cursor-pointer accent-cyan-400"}))),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxUpward",min:0,max:10,value:ozState.maxUpward,unit:"dB",label:"UPWARD COMPRESS",color:"#22d3ee",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxSoftClip",min:0,max:100,value:ozState.maxSoftClip,unit:"%",label:"SOFT CLIPPER",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxTransient",min:0,max:100,value:ozState.maxTransient,unit:"%",label:"TRANSIENT EMPHASIS",color:"#34d399",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))}))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='compressor'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-amber-400 uppercase oz-font-mono mb-3"},"Bus Compressor"),/*#__PURE__*/React.createElement("div",{className:"my-2"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compMakeup",min:0,max:12,value:ozState.compMakeup,unit:"dB",label:"MAKE-UP GAIN",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='compressor')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.compActive?'bg-amber-700 border-amber-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.compActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compThreshold",min:-60,max:0,value:ozState.compThreshold,unit:"dB",label:"THRESHOLD",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compRatio",min:1,max:20,value:ozState.compRatio,unit:":1",label:"RATIO",color:"#f59e0b",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"Glue & Punch",/*#__PURE__*/React.createElement("br",null),"Attack 20ms · Release 250ms · Knee 8dB"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='limiter'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-rose-400 uppercase oz-font-mono mb-3"},"Brickwall Limiter"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='limiter')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.limActive?'bg-rose-700 border-rose-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.limActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"limThreshold",min:-24,max:0,value:ozState.limThreshold,unit:"dB",label:"CEILING",color:"#f43f5e",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"True-Peak limiting",/*#__PURE__*/React.createElement("br",null),"Ratio 20:1 · Knee 0dB",/*#__PURE__*/React.createElement("br",null),"Attack 1ms · Release 50ms"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='exciter'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-purple-400 uppercase oz-font-mono mb-3"},"Harmonic Exciter"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='exciter')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.excActive?'bg-purple-700 border-purple-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.excActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"excDrive",min:0,max:100,value:ozState.excDrive,unit:"%",label:"DRIVE / MIX",color:"#c084fc",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"WaveShaper saturation",/*#__PURE__*/React.createElement("br",null),"High-pass 2kHz",/*#__PURE__*/React.createElement("br",null),"4× oversampled · wet/dry mix"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='rebalance'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-sky-400 uppercase oz-font-mono mb-3"},"Master Rebalance (M/S)"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='rebalance')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.rebalActive?'bg-sky-700 border-sky-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.rebalActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"rebalMid",min:-12,max:12,value:ozState.rebalMid,unit:"dB",label:"MID GAIN",color:"#38bdf8",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"rebalSide",min:-12,max:12,value:ozState.rebalSide,unit:"dB",label:"SIDE GAIN",color:"#22d3ee",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"ChannelSplitter + M/S gains",/*#__PURE__*/React.createElement("br",null),"Center (vocal/bass) vs Sides (stereo width)"))))),/*#__PURE__*/React.createElement("div",{className:"border-t border-slate-800 bg-slate-900/60 p-3 flex flex-col shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between pb-1.5 border-b border-slate-800/80 mb-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-[11px] font-bold text-white uppercase tracking-wider"},"Wave Observer"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 text-cyan-400 border border-cyan-800/60 px-1.5 py-0.2 rounded font-mono"},"Real-time Oscilloscope")),/*#__PURE__*/React.createElement("div",{className:"flex gap-2"},['Scope','Settings','Help','About'].map(tab=>/*#__PURE__*/React.createElement("button",{key:tab,className:`px-2 py-0.5 rounded text-[10px] font-bold ${tab==='Scope'?'bg-cyan-950 text-cyan-300 border border-cyan-800/60':'text-slate-400 hover:text-slate-200'}`},tab)))),/*#__PURE__*/React.createElement("div",{className:"relative w-full h-32 bg-[#090d16] border border-slate-800/80 rounded-lg overflow-hidden mb-2"},/*#__PURE__*/React.createElement("canvas",{ref:woCanvasRef,className:"w-full h-full block"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between gap-4 text-[10px] oz-font-mono text-slate-400 mt-1 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 bg-slate-950 px-2.5 py-1.5 rounded border border-slate-800/80 shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-300"},"Input"),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1 w-20"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-[8px] text-slate-500 w-2"},"L"),/*#__PURE__*/React.createElement("div",{className:"w-16 h-1.5 bg-slate-900 rounded-sm overflow-hidden flex items-center"},/*#__PURE__*/React.createElement("div",{ref:woLeftMeterRef,className:"h-full bg-cyan-400 transition-all duration-75",style:{width:'0%'}}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-[8px] text-slate-500 w-2"},"R"),/*#__PURE__*/React.createElement("div",{className:"w-16 h-1.5 bg-slate-900 rounded-sm overflow-hidden flex items-center"},/*#__PURE__*/React.createElement("div",{ref:woRightMeterRef,className:"h-full bg-teal-500 transition-all duration-75",style:{width:'0%'}}))))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-4 bg-slate-950 px-3 py-1 rounded border border-slate-800/80 flex-1 justify-around"},/*#__PURE__*/React.createElement("span",{className:"font-bold text-slate-300 uppercase tracking-widest text-[9px]"},"Scope"),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Channel"),/*#__PURE__*/React.createElement("select",{value:woChannel,onChange:e=>setWoChannel(e.target.value),className:"bg-slate-900 border border-slate-700 rounded px-1.5 py-0.5 text-[9px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"),/*#__PURE__*/React.createElement("option",{value:"left"},"Left Only"),/*#__PURE__*/React.createElement("option",{value:"right"},"Right Only"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Mode"),/*#__PURE__*/React.createElement("select",{value:woMode,onChange:e=>setWoMode(e.target.value),className:"bg-slate-900 border border-slate-700 rounded px-1.5 py-0.5 text-[9px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"waveform"},"Waveform"),/*#__PURE__*/React.createElement("option",{value:"envelope"},"Envelope"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Duration:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold w-10 text-right"},woDuration.toFixed(3),"s"),/*#__PURE__*/React.createElement("input",{type:"range",min:"0.5",max:"5.0",step:"0.1",value:woDuration,onChange:e=>setWoDuration(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer accent-cyan-400"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"V.Zoom:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold w-12 text-right"},woZoom.toFixed(1)," dB"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"24",step:"0.5",value:woZoom,onChange:e=>setWoZoom(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer accent-cyan-400"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>setWoPaused(!woPaused),className:`px-3 py-1 rounded font-bold border transition shadow-md shrink-0 flex items-center gap-1 ${woPaused?'bg-amber-800 hover:bg-amber-700 border-amber-500 text-amber-100':'bg-slate-800 hover:bg-slate-700 border-slate-700 text-slate-200'}`},woPaused?'Resume':'Pause')))),/*#__PURE__*/React.createElement("div",{className:"w-64 bg-slate-900 border-l border-slate-800/80 p-3 flex flex-col justify-between shadow-2xl oz-font-mono text-xs z-20 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-2 mb-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-white tracking-wider uppercase flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders",className:"w-3 h-3 text-cyan-400"})," I/O METERS"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 border border-cyan-800 text-cyan-300 px-1 py-0.5 rounded"},"TRUE PEAK")),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2 text-[10px] text-center mb-2"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"IN PEAK"),/*#__PURE__*/React.createElement("div",{id:"inPeakText",className:"text-cyan-400 font-bold oz-font-mono"},"-inf dB")),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"OUT PEAK"),/*#__PURE__*/React.createElement("div",{id:"outPeakText",className:"text-emerald-400 font-bold oz-font-mono"},"-inf dB"))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2 text-[10px] text-center mb-2"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"OUT RMS"),/*#__PURE__*/React.createElement("div",{id:"outRmsText",className:"text-sky-300 font-bold oz-font-mono"},"-inf")),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"LOUDNESS"),/*#__PURE__*/React.createElement("div",{id:"lufsText",className:"text-fuchsia-300 font-bold oz-font-mono"},"--.-"))),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-slate-950 border border-slate-800/90 rounded-xl p-2 flex justify-around relative overflow-hidden my-1"},/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 mb-1"},"IN"),/*#__PURE__*/React.createElement("div",{className:"flex-1 w-5 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end"},/*#__PURE__*/React.createElement("canvas",{ref:inMeterCanvasRef,className:"w-full h-full block"}))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 mb-1"},"OUT"),/*#__PURE__*/React.createElement("div",{className:"flex-1 w-6 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end"},/*#__PURE__*/React.createElement("canvas",{ref:outMeterCanvasRef,className:"w-full h-full block"})))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1.5 mt-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>setOzState(prev=>({...prev,isBypassed:!prev.isBypassed})),className:`py-1.5 rounded text-[10px] font-bold border transition-colors ${ozState.isBypassed?'bg-cyan-600 border-cyan-500 text-white':'bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700'}`},"Bypass"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-200 py-1.5 rounded text-[10px] font-bold border border-slate-700 transition-colors"},"Gain Match"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] border border-slate-700"},"Codec"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] border border-slate-700"},"Dither"))))),addModuleOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[110] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:()=>setAddModuleOpen(false)},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-lg bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider oz-font-mono"},"THÊM MODULE VÀO MASTERING CHAIN"),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(false),className:"text-slate-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-3 text-xs"},[{t:'eqpro',c:'text-teal-400',i:'chart-area',d:'Parametric / Graphic EQ PRO — Pro-Q style, 8 bands, interactive canvas + spectrum.'},{t:'compressor',c:'text-amber-400',i:'compress',d:'Nén dynamic range, glue & punch cho master.'},{t:'limiter',c:'text-rose-400',i:'shield-half',d:'True-Peak ceiling (ratio 20:1, knee 0) chống clipping.'},{t:'exciter',c:'text-purple-400',i:'wand-2',d:'Saturation hài cho warmth và top-end brilliance.'},{t:'rebalance',c:'text-sky-400',i:'sliders-horizontal',d:'Cân bằng Mid/Side (Vocal/Bass vs stereo width).'},{t:'eq',c:'text-cyan-400',i:'activity',d:'EQ 4-band (lowshelf, 2× peaking, highshelf) + presets.'},{t:'imager',c:'text-fuchsia-400',i:'radio',d:'Stereo width 4-band M/S + vectorscope/correlation.'},{t:'maximizer',c:'text-emerald-400',i:'gauge',d:'Maximizer: boost, soft clip, upward comp, ceiling.'}].map(m=>/*#__PURE__*/React.createElement("button",{key:m.t,onClick:()=>addModuleToChain(m.t),className:"p-3 bg-slate-950 hover:bg-slate-800 border border-slate-800 rounded-xl text-left space-y-1 transition-colors"},/*#__PURE__*/React.createElement("div",{className:`font-bold ${m.c} flex items-center gap-1.5`},/*#__PURE__*/React.createElement("i",{"data-lucide":m.i,className:"w-3.5 h-3.5"})," ",MODULE_META[m.t].name),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-400"},m.d)))))));};// ── Media Explorer Panel (from md/51_MEDIA_EXPLORER.md) ── +setOzState(prev=>({...prev,eqLowGain:preset.bands[0]?preset.bands[0].gain:prev.eqLowGain,eqMid1Gain:preset.bands[1]?preset.bands[1].gain:prev.eqMid1Gain,eqMid2Gain:preset.bands[2]?preset.bands[2].gain:prev.eqMid2Gain,eqHighGain:preset.bands[3]?preset.bands[3].gain:prev.eqHighGain,eqPreset:presetKey}));};const bandKnob=(param,min,max,val,unit,label,freq,color,filterType)=>/*#__PURE__*/React.createElement("div",{className:"bg-slate-900/80 border border-slate-800 p-2 flex flex-col justify-between items-center rounded-lg"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[10px] font-bold w-full",style:{color}},/*#__PURE__*/React.createElement("span",null,label),/*#__PURE__*/React.createElement("span",{className:"text-[9px] font-mono text-slate-400"},freq)),/*#__PURE__*/React.createElement("div",{className:"my-1"},/*#__PURE__*/React.createElement(MasteringKnob,{param:param,min:min,max:max,value:ozState[param]!==undefined?ozState[param]:val,unit:unit,color:color,onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"text-[9px] text-slate-500 font-mono text-center mt-1"},filterType));return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm",onClick:onClose},/*#__PURE__*/React.createElement("div",{id:"masteringModalBody",className:"flex flex-col bg-slate-950 text-slate-200 w-[95vw] h-[92vh] max-w-[1400px] rounded-2xl border border-slate-800/80 shadow-2xl overflow-hidden",onClick:e=>e.stopPropagation(),style:{fontFamily:"'Inter', sans-serif"}},/*#__PURE__*/React.createElement("header",{className:"h-12 bg-slate-900/90 border-b border-slate-800/80 flex items-center justify-between px-4 z-30 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("div",{className:"w-7 h-7 rounded-lg bg-cyan-600 flex items-center justify-center font-bold text-white shadow-lg shadow-cyan-950"},/*#__PURE__*/React.createElement("i",{"data-lucide":"zap",className:"w-3.5 h-3.5"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("h1",{className:"text-xs font-bold tracking-wider text-white flex items-center gap-2"},"MASTERING SUITE ",/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 text-cyan-400 border border-cyan-800/80 px-1.5 py-0.5 rounded oz-font-mono"},"WEB MASTERING V10.5")))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 bg-slate-950 px-3 py-1 rounded-lg border border-slate-800/80"},/*#__PURE__*/React.createElement("button",{onClick:startAudioDemo,className:"px-3 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-xs font-semibold flex items-center gap-1.5 transition-all shadow-md shadow-cyan-950"},/*#__PURE__*/React.createElement("i",{"data-lucide":"play",className:"w-3 h-3"})," ",/*#__PURE__*/React.createElement("span",null,"Play Reference")),/*#__PURE__*/React.createElement("button",{onClick:stopAudioDemo,className:"px-3 py-1 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded text-xs font-semibold flex items-center gap-1.5 transition-all"},/*#__PURE__*/React.createElement("i",{"data-lucide":"square",className:"w-3 h-3"})," ",/*#__PURE__*/React.createElement("span",null,"Stop")),/*#__PURE__*/React.createElement("div",{className:"h-4 w-[1px] bg-slate-800 mx-1"}),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 text-xs oz-font-mono"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 text-[11px]"},"Preset:"),/*#__PURE__*/React.createElement("select",{className:"bg-slate-900 border border-slate-700 rounded px-2 py-0.5 text-xs text-cyan-300 outline-none focus:border-cyan-500"},/*#__PURE__*/React.createElement("option",{value:"adaptive"},"Adaptive Dynamic Master"),/*#__PURE__*/React.createElement("option",{value:"edm_club"},"EDM / Club Punch Maximizer"),/*#__PURE__*/React.createElement("option",{value:"wide_space"},"Cinematic Stereo Expansion"),/*#__PURE__*/React.createElement("option",{value:"transparent"},"Transparent High-Clarity Limiter")))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 text-xs oz-font-mono"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 text-[11px]"},"Target LUFS:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold bg-slate-950 border border-cyan-900/60 px-2 py-0.5 rounded"},"-11.0 LUFS"),/*#__PURE__*/React.createElement("button",{onClick:()=>{getAudioContext();setMasterConnected(prev=>!prev);},className:`px-2 py-0.5 rounded text-[10px] font-bold border transition-colors ${masterConnected?'bg-emerald-700 border-emerald-500 text-white':'bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700'}`},masterConnected?'Master ON':'Master OFF'),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"px-2 py-0.5 rounded text-[10px] font-bold border transition-colors bg-slate-800 border-slate-700 text-slate-300 hover:bg-[#c2410c] hover:border-red-600 hover:text-white"},"\u0110\xF3ng"),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-500 hover:text-slate-300 ml-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"})))),/*#__PURE__*/React.createElement("div",{className:"h-16 bg-slate-950 border-b border-slate-800 px-4 flex items-center gap-2 overflow-x-auto shrink-0 oz-scrollbar"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-500 uppercase tracking-widest mr-1 oz-font-mono shrink-0"},"CHAIN:"),(ozState.chain||[]).map((mod,idx)=>{const meta=MODULE_META[mod.type]||{name:mod.type,sub:'',icon:'circle',color:'#94a3b8'};const isEditing=ozState.activeModule===mod.type;const isOn=chainActive(mod.type);return/*#__PURE__*/React.createElement("div",{key:mod.id,draggable:true,onDragStart:e=>{dragChainIndexRef.current=idx;e.dataTransfer.effectAllowed='move';},onDragOver:e=>{e.preventDefault();e.dataTransfer.dropEffect='move';},onDrop:e=>{e.preventDefault();const from=dragChainIndexRef.current;if(from!==null&&from!==idx)reorderChain(from,idx);dragChainIndexRef.current=null;},onClick:()=>switchModule(mod.type),className:`w-40 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${isEditing?'oz-card-active':'oz-card'}`},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 min-w-0"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleChainModule(mod.id);},className:"w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",style:{backgroundColor:isOn?'#38bdf8':'#334155',color:isOn?'#0f172a':'#94a3b8'}},/*#__PURE__*/React.createElement("i",{"data-lucide":"power",className:"w-2.5 h-2.5"})),/*#__PURE__*/React.createElement("div",{className:"min-w-0"},/*#__PURE__*/React.createElement("div",{className:"text-[11px] font-bold text-slate-200 truncate"},meta.name),/*#__PURE__*/React.createElement("div",{className:"text-[9px] oz-font-mono truncate",style:{color:meta.color}},idx+1,". ",meta.sub))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":meta.icon,className:"w-3 h-3 text-slate-500"}),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();removeChainModule(mod.id);},className:"text-slate-600 hover:text-red-400 text-xs px-0.5",title:"X\xF3a module"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))));}),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(true),className:"w-24 h-12 rounded-lg border border-dashed border-slate-800 flex items-center justify-center text-slate-600 hover:text-slate-400 hover:border-slate-600 cursor-pointer transition-all shrink-0",title:"Th\xEAm module v\xE0o chain"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("main",{className:"flex-1 flex overflow-hidden min-h-0"},/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col bg-slate-950 relative overflow-y-auto oz-scrollbar"},/*#__PURE__*/React.createElement("div",{className:"h-9 bg-slate-900/60 border-b border-slate-800/80 px-4 flex items-center justify-between text-xs oz-font-mono shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400 flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"headphones",className:"w-3 h-3 text-cyan-400"})," Delta Listen"),/*#__PURE__*/React.createElement("select",{className:"bg-slate-950 border border-slate-800 rounded px-2 py-0.5 text-[11px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"irc4"},"IRC IV - Classic"),/*#__PURE__*/React.createElement("option",{value:"irc3"},"IRC III - Balanced"),/*#__PURE__*/React.createElement("option",{value:"irc2"},"IRC II - Crisp"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 text-[11px]"},ozState.activeModule==='eq'&&/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"EQ Presets:"),/*#__PURE__*/React.createElement("select",{value:ozState.eqPreset||'flat',onChange:e=>applyEQPreset(e.target.value),className:"bg-slate-950 border border-slate-700 text-cyan-300 rounded px-2 py-0.5 text-[11px] outline-none focus:border-cyan-500"},Object.keys(EQ_PRESET_LIBRARY).map(k=>/*#__PURE__*/React.createElement("option",{key:k,value:k},EQ_PRESET_LIBRARY[k].name)))),/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Learn Input Gain:"),/*#__PURE__*/React.createElement("button",{className:"bg-cyan-950 text-cyan-300 border border-cyan-800/80 px-2 py-0.5 rounded font-bold hover:bg-cyan-900 transition-colors"},"-11.0 LUFS"))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='eq'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"relative w-full h-64 bg-slate-950 border border-slate-800/90 rounded-xl overflow-hidden shadow-inner cursor-crosshair"},/*#__PURE__*/React.createElement("canvas",{ref:eqCanvasRef,className:"w-full h-full block"})),/*#__PURE__*/React.createElement("div",{className:"oz-panel p-3 rounded-xl grid grid-cols-4 gap-3"},bandKnob('eqLowGain',-12,12,1.5,'dB','BAND 1 (LOW)','100 Hz','#22d3ee','Shelf Filter'),bandKnob('eqMid1Gain',-12,12,-1.0,'dB','BAND 2 (MID LOW)','822 Hz','#fbbf24','Dynamic Bell (Q: 0.7)'),bandKnob('eqMid2Gain',-12,12,2.0,'dB','BAND 3 (MID HIGH)','3.2 kHz','#a855f7','Dynamic Bell (Q: 1.2)'),bandKnob('eqHighGain',-12,12,1.8,'dB','BAND 4 (HIGH)','10 kHz','#34d399','High Shelf'))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='eqpro'?'':'hidden'}`},(()=>{const chainMods=ozState.chain||[];const cm=chainMods.find(m=>m.type==='eqpro'&&m.active!==false)||chainMods.find(m=>m.type==='eqpro');if(!cm)return/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-500 font-mono py-10 text-center"},"Ch\u01B0a c\xF3 module EQ PRO trong chain \u2014 b\u1EA5m [+] \u0111\u1EC3 th\xEAm.");return/*#__PURE__*/React.createElement(InteractiveEqPro,{track:null,params:cm.params||{},onChange:next=>updateChainEntryParams(cm.id,next),getModule:()=>masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null,spectrumModules:()=>{const m=masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null;return m?[m]:[];},applyTo:fn=>{const m=masterBus&&masterBus.eqProInstances?masterBus.eqProInstances[cm.id]:null;if(m)fn(m);}});})()),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='imager'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-12 gap-4 flex-1"},/*#__PURE__*/React.createElement("div",{className:"col-span-8 bg-slate-950 border border-slate-800/90 rounded-xl p-3 flex flex-col relative shadow-inner"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-cyan-400 uppercase oz-font-mono mb-2 flex items-center justify-between w-full"},/*#__PURE__*/React.createElement("span",null,/*#__PURE__*/React.createElement("i",{"data-lucide":"radio",className:"w-3.5 h-3.5"})," Polar Vectorscope & Correlation Meter"),/*#__PURE__*/React.createElement("span",{className:"text-[11px] text-slate-400 normal-case"},"Corr: ",/*#__PURE__*/React.createElement("span",{id:"corrText",className:"font-bold text-emerald-400"},"1.00"))),/*#__PURE__*/React.createElement("div",{className:"flex-1 relative w-full h-56 flex items-center justify-center"},/*#__PURE__*/React.createElement("canvas",{ref:imagerCanvasRef,className:"w-full h-full block"}))),/*#__PURE__*/React.createElement("div",{className:"col-span-4 oz-panel p-3 rounded-xl flex flex-col justify-between"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-slate-300 uppercase oz-font-mono"},"4-Band Stereo Width"),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-500 oz-font-mono leading-snug"},"0% = Mono \xB7 100% = Original \xB7 200% = 2\xD7 Width"),/*#__PURE__*/React.createElement("div",{className:"space-y-3 my-auto"},[{id:'w1',label:'Band 1 (20-100Hz)',color:'#22d3ee',val:ozState.w1},{id:'w2',label:'Band 2 (100Hz-1kHz)',color:'#fbbf24',val:ozState.w2},{id:'w3',label:'Band 3 (1k-6kHz)',color:'#a855f7',val:ozState.w3},{id:'w4',label:'Band 4 (6k-20kHz)',color:'#34d399',val:ozState.w4}].map(b=>/*#__PURE__*/React.createElement("div",{key:b.id},/*#__PURE__*/React.createElement("div",{className:"flex justify-between text-[11px] font-mono mb-1"},/*#__PURE__*/React.createElement("span",{style:{color:b.color,fontWeight:700}},b.label),/*#__PURE__*/React.createElement("span",{id:b.id+'Val'},b.val,"%")),/*#__PURE__*/React.createElement("input",{type:"range",min:"0",max:"200",value:b.val,onChange:e=>setOzState(prev=>({...prev,[b.id]:parseInt(e.target.value)})),className:"w-full h-1 cursor-pointer",style:{accentColor:b.color}}))))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='maximizer'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-slate-400 uppercase oz-font-mono mb-3"},"Maximizer Gain Boost"),/*#__PURE__*/React.createElement("div",{className:"my-2"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxGain",min:0,max:12,value:ozState.maxGain,unit:"dB",color:"#22d3ee",size:"large",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"w-full mt-4 bg-slate-950 p-2 rounded-lg border border-slate-800 text-xs oz-font-mono flex flex-col gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center w-full"},/*#__PURE__*/React.createElement("span",{className:"text-slate-400"},"Ceiling Level:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold"},ozState.ceiling.toFixed(2)," dB")),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"0",step:"0.1",value:ozState.ceiling,onChange:e=>setOzState(prev=>({...prev,ceiling:parseFloat(e.target.value)})),className:"w-full h-1 cursor-pointer accent-cyan-400"}))),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxUpward",min:0,max:10,value:ozState.maxUpward,unit:"dB",label:"UPWARD COMPRESS",color:"#22d3ee",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxSoftClip",min:0,max:100,value:ozState.maxSoftClip,unit:"%",label:"SOFT CLIPPER",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"maxTransient",min:0,max:100,value:ozState.maxTransient,unit:"%",label:"TRANSIENT EMPHASIS",color:"#34d399",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))}))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='compressor'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-amber-400 uppercase oz-font-mono mb-3"},"Bus Compressor"),/*#__PURE__*/React.createElement("div",{className:"my-2"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compMakeup",min:0,max:12,value:ozState.compMakeup,unit:"dB",label:"MAKE-UP GAIN",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='compressor')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.compActive?'bg-amber-700 border-amber-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.compActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compThreshold",min:-60,max:0,value:ozState.compThreshold,unit:"dB",label:"THRESHOLD",color:"#fbbf24",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"compRatio",min:1,max:20,value:ozState.compRatio,unit:":1",label:"RATIO",color:"#f59e0b",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"Glue & Punch",/*#__PURE__*/React.createElement("br",null),"Attack 20ms \xB7 Release 250ms \xB7 Knee 8dB"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='limiter'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-rose-400 uppercase oz-font-mono mb-3"},"Brickwall Limiter"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='limiter')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.limActive?'bg-rose-700 border-rose-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.limActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"limThreshold",min:-24,max:0,value:ozState.limThreshold,unit:"dB",label:"CEILING",color:"#f43f5e",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"True-Peak limiting",/*#__PURE__*/React.createElement("br",null),"Ratio 20:1 \xB7 Knee 0dB",/*#__PURE__*/React.createElement("br",null),"Attack 1ms \xB7 Release 50ms"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='exciter'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-purple-400 uppercase oz-font-mono mb-3"},"Harmonic Exciter"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='exciter')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.excActive?'bg-purple-700 border-purple-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.excActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"excDrive",min:0,max:100,value:ozState.excDrive,unit:"%",label:"DRIVE / MIX",color:"#c084fc",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"WaveShaper saturation",/*#__PURE__*/React.createElement("br",null),"High-pass 2kHz",/*#__PURE__*/React.createElement("br",null),"4\xD7 oversampled \xB7 wet/dry mix"))))),/*#__PURE__*/React.createElement("div",{className:`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule==='rebalance'?'':'hidden'}`},/*#__PURE__*/React.createElement("div",{className:"oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center"},/*#__PURE__*/React.createElement("div",{className:"col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-sky-400 uppercase oz-font-mono mb-3"},"Master Rebalance (M/S)"),/*#__PURE__*/React.createElement("button",{onClick:()=>toggleChainModule((ozState.chain||[]).find(m=>m.type==='rebalance')?.id),className:`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.rebalActive?'bg-sky-700 border-sky-500 text-white':'bg-slate-800 border-slate-700 text-slate-300'}`},ozState.rebalActive?'ON':'OFF')),/*#__PURE__*/React.createElement("div",{className:"col-span-8 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"rebalMid",min:-12,max:12,value:ozState.rebalMid,unit:"dB",label:"MID GAIN",color:"#38bdf8",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement(MasteringKnob,{param:"rebalSide",min:-12,max:12,value:ozState.rebalSide,unit:"dB",label:"SIDE GAIN",color:"#22d3ee",onChange:(p,v)=>setOzState(prev=>({...prev,[p]:v}))})),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2"},"ChannelSplitter + M/S gains",/*#__PURE__*/React.createElement("br",null),"Center (vocal/bass) vs Sides (stereo width)"))))),/*#__PURE__*/React.createElement("div",{className:"border-t border-slate-800 bg-slate-900/60 p-3 flex flex-col shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between pb-1.5 border-b border-slate-800/80 mb-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-[11px] font-bold text-white uppercase tracking-wider"},"Wave Observer"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 text-cyan-400 border border-cyan-800/60 px-1.5 py-0.2 rounded font-mono"},"Real-time Oscilloscope")),/*#__PURE__*/React.createElement("div",{className:"flex gap-2"},['Scope','Settings','Help','About'].map(tab=>/*#__PURE__*/React.createElement("button",{key:tab,className:`px-2 py-0.5 rounded text-[10px] font-bold ${tab==='Scope'?'bg-cyan-950 text-cyan-300 border border-cyan-800/60':'text-slate-400 hover:text-slate-200'}`},tab)))),/*#__PURE__*/React.createElement("div",{className:"relative w-full h-32 bg-[#090d16] border border-slate-800/80 rounded-lg overflow-hidden mb-2"},/*#__PURE__*/React.createElement("canvas",{ref:woCanvasRef,className:"w-full h-full block"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between gap-4 text-[10px] oz-font-mono text-slate-400 mt-1 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 bg-slate-950 px-2.5 py-1.5 rounded border border-slate-800/80 shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-slate-300"},"Input"),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1 w-20"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-[8px] text-slate-500 w-2"},"L"),/*#__PURE__*/React.createElement("div",{className:"w-16 h-1.5 bg-slate-900 rounded-sm overflow-hidden flex items-center"},/*#__PURE__*/React.createElement("div",{ref:woLeftMeterRef,className:"h-full bg-cyan-400 transition-all duration-75",style:{width:'0%'}}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"text-[8px] text-slate-500 w-2"},"R"),/*#__PURE__*/React.createElement("div",{className:"w-16 h-1.5 bg-slate-900 rounded-sm overflow-hidden flex items-center"},/*#__PURE__*/React.createElement("div",{ref:woRightMeterRef,className:"h-full bg-teal-500 transition-all duration-75",style:{width:'0%'}}))))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-4 bg-slate-950 px-3 py-1 rounded border border-slate-800/80 flex-1 justify-around"},/*#__PURE__*/React.createElement("span",{className:"font-bold text-slate-300 uppercase tracking-widest text-[9px]"},"Scope"),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Channel"),/*#__PURE__*/React.createElement("select",{value:woChannel,onChange:e=>setWoChannel(e.target.value),className:"bg-slate-900 border border-slate-700 rounded px-1.5 py-0.5 text-[9px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"),/*#__PURE__*/React.createElement("option",{value:"left"},"Left Only"),/*#__PURE__*/React.createElement("option",{value:"right"},"Right Only"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Mode"),/*#__PURE__*/React.createElement("select",{value:woMode,onChange:e=>setWoMode(e.target.value),className:"bg-slate-900 border border-slate-700 rounded px-1.5 py-0.5 text-[9px] text-slate-300 outline-none"},/*#__PURE__*/React.createElement("option",{value:"waveform"},"Waveform"),/*#__PURE__*/React.createElement("option",{value:"envelope"},"Envelope"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"Duration:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold w-10 text-right"},woDuration.toFixed(3),"s"),/*#__PURE__*/React.createElement("input",{type:"range",min:"0.5",max:"5.0",step:"0.1",value:woDuration,onChange:e=>setWoDuration(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer accent-cyan-400"})),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",null,"V.Zoom:"),/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-bold w-12 text-right"},woZoom.toFixed(1)," dB"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"24",step:"0.5",value:woZoom,onChange:e=>setWoZoom(parseFloat(e.target.value)),className:"w-16 h-1 cursor-pointer accent-cyan-400"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>setWoPaused(!woPaused),className:`px-3 py-1 rounded font-bold border transition shadow-md shrink-0 flex items-center gap-1 ${woPaused?'bg-amber-800 hover:bg-amber-700 border-amber-500 text-amber-100':'bg-slate-800 hover:bg-slate-700 border-slate-700 text-slate-200'}`},woPaused?'Resume':'Pause')))),/*#__PURE__*/React.createElement("div",{className:"w-64 bg-slate-900 border-l border-slate-800/80 p-3 flex flex-col justify-between shadow-2xl oz-font-mono text-xs z-20 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-2 mb-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-white tracking-wider uppercase flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders",className:"w-3 h-3 text-cyan-400"})," I/O METERS"),/*#__PURE__*/React.createElement("span",{className:"text-[9px] bg-cyan-950 border border-cyan-800 text-cyan-300 px-1 py-0.5 rounded"},"TRUE PEAK")),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2 text-[10px] text-center mb-2"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"IN PEAK"),/*#__PURE__*/React.createElement("div",{id:"inPeakText",className:"text-cyan-400 font-bold oz-font-mono"},"-inf dB")),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"OUT PEAK"),/*#__PURE__*/React.createElement("div",{id:"outPeakText",className:"text-emerald-400 font-bold oz-font-mono"},"-inf dB"))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-2 text-[10px] text-center mb-2"},/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"OUT RMS"),/*#__PURE__*/React.createElement("div",{id:"outRmsText",className:"text-sky-300 font-bold oz-font-mono"},"-inf")),/*#__PURE__*/React.createElement("div",{className:"bg-slate-950 border border-slate-800/80 p-1.5 rounded"},/*#__PURE__*/React.createElement("div",{className:"text-slate-500 font-bold"},"LOUDNESS"),/*#__PURE__*/React.createElement("div",{id:"lufsText",className:"text-fuchsia-300 font-bold oz-font-mono"},"--.-"))),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-slate-950 border border-slate-800/90 rounded-xl p-2 flex justify-around relative overflow-hidden my-1"},/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 mb-1"},"IN"),/*#__PURE__*/React.createElement("div",{className:"flex-1 w-5 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end"},/*#__PURE__*/React.createElement("canvas",{ref:inMeterCanvasRef,className:"w-full h-full block"}))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center h-full"},/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-slate-500 mb-1"},"OUT"),/*#__PURE__*/React.createElement("div",{className:"flex-1 w-6 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end"},/*#__PURE__*/React.createElement("canvas",{ref:outMeterCanvasRef,className:"w-full h-full block"})))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1.5 mt-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>setOzState(prev=>({...prev,isBypassed:!prev.isBypassed})),className:`py-1.5 rounded text-[10px] font-bold border transition-colors ${ozState.isBypassed?'bg-cyan-600 border-cyan-500 text-white':'bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700'}`},"Bypass"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-200 py-1.5 rounded text-[10px] font-bold border border-slate-700 transition-colors"},"Gain Match"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] border border-slate-700"},"Codec"),/*#__PURE__*/React.createElement("button",{className:"bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] border border-slate-700"},"Dither"))))),addModuleOpen&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[110] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4",onClick:()=>setAddModuleOpen(false)},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-lg bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between border-b border-slate-800 pb-3"},/*#__PURE__*/React.createElement("h3",{className:"text-xs font-bold text-white uppercase tracking-wider oz-font-mono"},"TH\xCAM MODULE V\xC0O MASTERING CHAIN"),/*#__PURE__*/React.createElement("button",{onClick:()=>setAddModuleOpen(false),className:"text-slate-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-3 text-xs"},[{t:'eqpro',c:'text-teal-400',i:'chart-area',d:'Parametric / Graphic EQ PRO — Pro-Q style, 8 bands, interactive canvas + spectrum.'},{t:'compressor',c:'text-amber-400',i:'compress',d:'Nén dynamic range, glue & punch cho master.'},{t:'limiter',c:'text-rose-400',i:'shield-half',d:'True-Peak ceiling (ratio 20:1, knee 0) chống clipping.'},{t:'exciter',c:'text-purple-400',i:'wand-2',d:'Saturation hài cho warmth và top-end brilliance.'},{t:'rebalance',c:'text-sky-400',i:'sliders-horizontal',d:'Cân bằng Mid/Side (Vocal/Bass vs stereo width).'},{t:'eq',c:'text-cyan-400',i:'activity',d:'EQ 4-band (lowshelf, 2× peaking, highshelf) + presets.'},{t:'imager',c:'text-fuchsia-400',i:'radio',d:'Stereo width 4-band M/S + vectorscope/correlation.'},{t:'maximizer',c:'text-emerald-400',i:'gauge',d:'Maximizer: boost, soft clip, upward comp, ceiling.'}].map(m=>/*#__PURE__*/React.createElement("button",{key:m.t,onClick:()=>addModuleToChain(m.t),className:"p-3 bg-slate-950 hover:bg-slate-800 border border-slate-800 rounded-xl text-left space-y-1 transition-colors"},/*#__PURE__*/React.createElement("div",{className:`font-bold ${m.c} flex items-center gap-1.5`},/*#__PURE__*/React.createElement("i",{"data-lucide":m.i,className:"w-3.5 h-3.5"})," ",MODULE_META[m.t].name),/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-slate-400"},m.d)))))));};// ── Media Explorer Panel (from md/51_MEDIA_EXPLORER.md) ── const MEDIA_LIBRARY_SAMPLES=[{name:"MIDI_Loop_01.mid",events:95,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"},{name:"MIDI_Loop_02_Bass.mid",events:48,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"},{name:"MIDI_Loop_03_Lead.mid",events:110,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"},{name:"MIDI_Loop_04.mid",events:76,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"},{name:"MIDI_Loop_05_Bass.mid",events:52,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"},{name:"MIDI_Loop_06.mid",events:88,lengthQn:16,time:"0:08.000",tpqn:480,bpm:130,kind:"midi"}];const MediaExplorerPanel=({height,clipboardRef})=>{const[userFiles,setUserFiles]=React.useState([]);const[folder,setFolder]=React.useState('library');const[selected,setSelected]=React.useState(null);const[filterText,setFilterText]=React.useState('');const[viewMode,setViewMode]=React.useState('details');const[isPlaying,setIsPlaying]=React.useState(false);const[isPaused,setIsPaused]=React.useState(false);const[isLooping,setIsLooping]=React.useState(false);const[autoPlay,setAutoPlay]=React.useState(true);const[pitch,setPitch]=React.useState(0.0);const[rate,setRate]=React.useState(1.0);const[volumeDb,setVolumeDb]=React.useState(0.0);const[currentTime,setCurrentTime]=React.useState(0);const[peaks,setPeaks]=React.useState(null);const[audioBuffer,setAudioBuffer]=React.useState(null);const[audioDuration,setAudioDuration]=React.useState(0);const[midiNotes,setMidiNotes]=React.useState(null);const[midiTotal,setMidiTotal]=React.useState(4);const[midiBars,setMidiBars]=React.useState(1);const[midiTotalBeats,setMidiTotalBeats]=React.useState(16);const[midiFileBpm,setMidiFileBpm]=React.useState(120);const[tempo,setTempo]=React.useState(function(){var saved=localStorage.getItem('studio_media_explorer_tempo');return saved?parseInt(saved):120;});const[tempoText,setTempoText]=React.useState(String(function(){var saved=localStorage.getItem('studio_media_explorer_tempo');return saved?parseInt(saved):120;}()));const[zoom,setZoom]=React.useState(1.0);const[scrollOffset,setScrollOffset]=React.useState(0);const scrollOffsetRef=React.useRef(0);scrollOffsetRef.current=scrollOffset;const[selStart,setSelStart]=React.useState(null);const[selEnd,setSelEnd]=React.useState(null);const[isDragging,setIsDragging]=React.useState(false);const[previewCtxMenu,setPreviewCtxMenu]=React.useState(null);// { x, y } const containerRef=React.useRef(null);// Refs mirror latest state so drawCanvas (also called from rAF clock with a // stale closure) always draws the currently selected file, not the old one. @@ -493,7 +509,7 @@ isLoopingRef.current=next;const cur=selectedRef.current;const st=playStateRef.cu // the loop points to the current selection so it loops continuously // over the selected region until Stop is pressed. if(next&&st.source.buffer){if(hasSelection){st.source.loopStart=Math.min(sStart,sEnd);st.source.loopEnd=Math.max(sStart,sEnd);}else{st.source.loopStart=0;st.source.loopEnd=st.source.buffer.duration;}}}if(next&&isMidiFile(cur)){// Re-schedule loop for the currently previewing MIDI file -if(cur&&(cur.handle||cur.path||cur.file_id||cur.fileId)){selectTokenRef.current++;const token=selectTokenRef.current;try{if(window.SonicSF&&typeof window.SonicSF.stopAll==='function')window.SonicSF.stopAll();}catch(e){}playMidiPreview(cur,token);}}return next;});};const toggleRate=dir=>setRate(prev=>Math.max(0.25,Math.min(4.0,Math.round((prev+dir*0.1)*100)/100)));const togglePitch=dir=>setPitch(prev=>Math.max(-24,Math.min(24,prev+dir*0.5)));const selIsMidi=isMidiFile(selected);const selDur=fileDuration(selected);const selBpm=selected&&selected.bpm||tempo;return/*#__PURE__*/React.createElement("div",{ref:containerRef,className:"flex flex-col w-full h-full text-slate-900 overflow-hidden select-none",style:{fontFamily:"'Inter', sans-serif"}},/*#__PURE__*/React.createElement("div",{className:"h-8 bg-[#d4d0c8] border-b border-[#808080] px-2 flex items-center justify-between text-xs shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 flex-1 mr-2"},/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Back",onClick:()=>folder==='computer'?goComputerParent():setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-left"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Forward",onClick:()=>setFolder('uploads')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-right"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Up Directory",onClick:()=>folder==='computer'?goComputerParent():setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-up"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Refresh",onClick:()=>{if(folder==='computer'){const node=computerTree[computerPath];if(node&&node.handle)browseComputerDir({name:computerPath.split('/').pop()||computerPath,path:computerPath,is_dir:true,handle:node.handle});else if(computerPath)browseComputerDir({name:computerPath.split(/[\\/]/).pop()||computerPath,path:computerPath,is_dir:true});}else if(window.SonicAPI&&window.SonicAPI.listMyFiles)window.SonicAPI.listMyFiles([]).then(d=>setUserFiles(d||[]));}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-rotate-right"})),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex items-center bg-white border border-[#808080] h-5 px-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752] text-[11px] mr-1.5"}),/*#__PURE__*/React.createElement("span",{className:"flex-1 text-xs text-slate-800 truncate"},folder==='computer'?computerPath||'My Computer':folder==='library'?'Media Library':folder==='uploads'?'Uploads':'Processed'),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-slate-600 text-[10px] ml-1"}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center bg-white border border-[#808080] h-5 px-1 w-40"},/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"Filter/Search...",value:filterText,onChange:e=>setFilterText(e.target.value),className:"w-full text-xs outline-none bg-transparent font-sans text-slate-800"}),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-slate-600 text-[10px] ml-1"})),/*#__PURE__*/React.createElement("button",{className:"px-2 py-0.5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[11px] flex items-center gap-1 font-semibold",onClick:()=>setViewMode(viewMode==='details'?'list':'details')},/*#__PURE__*/React.createElement("span",null,viewMode==='details'?'Details':'List')," ",/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-[9px]"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-hidden min-h-0 relative"},/*#__PURE__*/React.createElement("div",{className:"flex bg-white border border-[#808080] m-1 mr-0 text-xs select-none shrink-0 overflow-hidden"},/*#__PURE__*/React.createElement("div",{ref:treePaneRef,className:"overflow-y-auto p-1",style:{width:treeWidth+'px',minWidth:treeWidth+'px',maxWidth:treeWidth+'px'}},/*#__PURE__*/React.createElement("div",{className:"space-y-0.5 font-sans"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 px-1 py-0.5 text-slate-700"},/*#__PURE__*/React.createElement("span",{className:"w-3"})," "),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 px-1 py-0.5 text-slate-700"},/*#__PURE__*/React.createElement("span",{className:"w-3"})," "),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm font-bold ${folder==='computer'&&computerPath==='favorited'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>{openFavorited();setFavoritedExpanded(!favoritedExpanded);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-star text-amber-500 text-[11px] font-bold"})," Favorited"),favoritedExpanded&&/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},favorites.map((fav,fi)=>/*#__PURE__*/React.createElement("div",{key:fav.path+fi,"data-tree-path":fav.path,className:"flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm hover:bg-amber-100 text-slate-800",style:{paddingLeft:20},onClick:()=>openFavorite(fav),onContextMenu:e=>{e.preventDefault();e.stopPropagation();setFavContext(Object.assign({},fav,{x:e.clientX,y:e.clientY}));}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752] shrink-0"}),/*#__PURE__*/React.createElement("span",{className:"truncate flex-1"},fav.name),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-star text-amber-500 text-[9px] shrink-0"}))),favorites.length===0&&/*#__PURE__*/React.createElement("div",{className:"pl-4 py-0.5 text-slate-400 italic text-[11px]"},"No favorites")),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm ${folder==='computer'&&computerPath!=='favorited'?'bg-slate-300 text-slate-900 font-semibold':'hover:bg-slate-200 text-slate-800'}`,onClick:openMyComputer},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-computer text-[11px] text-slate-600"})," My Computer"),folder==='computer'&&computerPath!=='favorited'&&computerRoots&&/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},computerRoots.map(root=>renderComputerNode(root,0,true))),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer font-semibold rounded-sm ${folder==='library'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752]"})," Media Library"),/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer pl-4 rounded-sm ${folder==='uploads'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('uploads')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752]"})," Uploads"),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer pl-4 rounded-sm ${folder==='processed'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('processed')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752]"})," Processed")))),/*#__PURE__*/React.createElement("div",{className:"w-1.5 cursor-ew-resize hover:bg-blue-500/40 active:bg-blue-500/60 transition-colors shrink-0",onMouseDown:startTreeResize,title:"Kéo để thay đổi chiều rộng"})),favContext&&/*#__PURE__*/React.createElement("div",{className:"fixed z-[100] bg-white border border-[#808080] shadow-lg rounded-sm text-xs font-sans text-slate-800 min-w-[180px]",style:{left:favContext.x,top:favContext.y},onMouseLeave:()=>setFavContext(null)},/*#__PURE__*/React.createElement("div",{className:`px-2 py-1 cursor-pointer hover:bg-blue-100 flex items-center gap-1.5 ${isFavorite(favContext)?'text-amber-700':''}`,onClick:()=>{toggleFavorite(favContext);setFavContext(null);}},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${isFavorite(favContext)?'fa-star text-amber-500':'fa-star text-slate-400'} text-[11px]`}),isFavorite(favContext)?'Gỡ khỏi Favorited':'Thêm vào Favorited'),/*#__PURE__*/React.createElement("div",{className:"px-2 py-1 cursor-pointer hover:bg-blue-100 flex items-center gap-1.5",onClick:()=>{if(favContext)browseComputerDir(favContext);setFavContext(null);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752] text-[11px]"})," Mở thư mục")),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-white border border-[#808080] m-1 overflow-y-auto relative"},/*#__PURE__*/React.createElement("table",{className:"w-full text-xs text-left border-collapse",style:{tableLayout:'fixed'}},/*#__PURE__*/React.createElement("thead",{className:"sticky top-0 bg-[#e0e0e0] border-b border-[#a0a0a0] text-slate-800 font-semibold select-none shadow-sm z-10"},/*#__PURE__*/React.createElement("tr",null,/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:viewMode==='details'?{width:colWidths.file}:undefined},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"File"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('file',e)})),viewMode==='details'&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:{width:colWidths.size}},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"Size"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('size',e)})),/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:{width:colWidths.type}},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"Type"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('type',e)}))))),/*#__PURE__*/React.createElement("tbody",{className:"font-sans text-slate-800"},visibleFiles.map((f,i)=>{const isSel=selected&&(selected.name||selected.file_id)===(f.name||f.file_id);const isMidi=isMidiFile(f);const icon=f.is_dir?'fa-folder text-[#d9a752]':isMidi?'fa-music text-purple-600':f.kind==='audio'?'fa-file-audio text-emerald-600':'fa-file text-zinc-500';return/*#__PURE__*/React.createElement("tr",{key:(f.path||f.file_id||f.name)+i,draggable:!f.is_dir,onDragStart:e=>{if(f.is_dir){e.preventDefault();return;}e.dataTransfer.setData('text/plain',f.name||f.original_name||'');e.dataTransfer.effectAllowed='copy';window.__mediaExplorerDragFile=f;},onDragEnd:()=>{window.__mediaExplorerDragFile=null;},className:`cursor-pointer hover:bg-blue-100 ${isSel?'file-row-selected':''}`,onClick:()=>f.is_dir?browseComputerDir(f):handleSelect(f),onDoubleClick:()=>f.is_dir&&browseComputerDir(f),onContextMenu:e=>{if(f.is_dir){e.preventDefault();e.stopPropagation();setFavContext(Object.assign({},f,{x:e.clientX,y:e.clientY}));}}},/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${icon} mr-2`}),f.name||f.original_name),viewMode==='details'&&/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},f.size_mb!=null?f.size_mb.toFixed(2)+' MB':isMidi?(f.tpqn||'MIDI')+' TPQN':'-'),viewMode==='details'&&/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},f.is_dir?'Folder':isMidi?'MIDI':f.kind==='audio'?'Audio':'File'));}),visibleFiles.length===0&&/*#__PURE__*/React.createElement("tr",null,/*#__PURE__*/React.createElement("td",{className:"py-3 px-2 text-slate-400 italic",colSpan:viewMode==='details'?3:1},"No files")))))),/*#__PURE__*/React.createElement("div",{className:"h-[38%] min-h-[110px] bg-[#d4d0c8] border-t border-[#808080] p-1.5 flex flex-col justify-between text-xs shrink-0 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{id:"btnStop",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-slate-800",title:"Stop",onClick:stopMediaPlayback},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-square text-[10px]"})),/*#__PURE__*/React.createElement("button",{id:"btnPlay",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-emerald-700 font-bold",title:"Play",onClick:()=>isPlaying?togglePause():playSelected(selected)},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${isPlaying&&!isPaused?'fa-play':'fa-play'} text-xs`})),/*#__PURE__*/React.createElement("button",{id:"btnPause",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-amber-700",title:"Pause",onClick:togglePause},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-pause text-xs"})),/*#__PURE__*/React.createElement("button",{id:"btnLoop",className:`w-6 h-6 border rounded-sm flex items-center justify-center text-xs ${isLooping?'bg-cyan-600 text-white border-cyan-700':'bg-[#e0e0e0] hover:bg-white text-slate-700 border-[#707070]'}`,title:"Loop / Repeat",onClick:toggleLoop},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-rotate-right"})),/*#__PURE__*/React.createElement("button",{id:"btnAutoPlay",className:`h-6 px-2 border rounded-sm font-bold text-[10px] flex items-center gap-1 ${autoPlay?'bg-gradient-to-r from-cyan-600 to-emerald-600 text-white border-slate-700':'bg-[#e0e0e0] text-slate-600 border-[#707070]'}`,onClick:()=>setAutoPlay(p=>!p)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-bolt text-[9px]"})," ",/*#__PURE__*/React.createElement("span",null,"Auto-Play")),/*#__PURE__*/React.createElement("div",{className:"relative"},/*#__PURE__*/React.createElement("button",{id:"btnSynth",className:`h-6 px-2 border rounded-sm font-bold text-[10px] flex items-center gap-1 ${synthInst?'bg-gradient-to-r from-violet-600 to-fuchsia-600 text-white border-slate-700':'bg-[#e0e0e0] text-slate-600 border-[#707070]'}`,title:"Chọn instrument để preview MIDI",onClick:toggleSynthDropdown},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-music text-[9px]"})," ",/*#__PURE__*/React.createElement("span",null,"Synth",synthInst?': '+(synthInst.name||'?'):''),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-[8px]"})),synthOpen&&/*#__PURE__*/React.createElement("div",{className:"absolute bottom-8 left-0 z-40 w-64 bg-white border border-[#808080] shadow-xl rounded-sm text-xs text-slate-800 font-sans max-h-72 overflow-y-auto"},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 bg-[#e0e0e0] px-2 py-1 font-bold border-b border-[#a0a0a0] flex items-center justify-between z-20"},/*#__PURE__*/React.createElement("span",null,"Select Instrument"),/*#__PURE__*/React.createElement("button",{onClick:()=>setSynthOpen(false),className:"text-slate-500 hover:text-slate-900"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"}))),/*#__PURE__*/React.createElement("div",{className:"sticky top-[23px] bg-white p-1 border-b border-[#c0c0c0] z-20 flex items-center gap-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-magnifying-glass text-slate-400 pl-1 text-[10px]"}),/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"Tìm nhạc cụ...",value:synthFilter,onChange:e=>setSynthFilter(e.target.value),onClick:e=>e.stopPropagation(),className:"w-full px-1 py-0.5 border border-[#c0c0c0] rounded-sm text-xs font-sans focus:outline-none focus:border-blue-500"}),synthFilter&&/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();setSynthFilter('');},className:"text-slate-400 hover:text-slate-700 px-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark text-[10px]"}))),synthLoading&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Loading..."),!synthLoading&&(!synthList||synthList.length===0)&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Không có SoundFont nào"),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-blue-100 ${!synthInst?'bg-slate-200':''}`,onClick:()=>selectSynthInst(null)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-ban text-slate-400"})," None (mặc định)"),!synthLoading&&filteredSynthList&&filteredSynthList.map(group=>/*#__PURE__*/React.createElement("div",{key:group.sf.id||group.sf.name},/*#__PURE__*/React.createElement("div",{className:"px-2 py-1 bg-[#f0f0f0] font-semibold text-slate-600 border-t border-[#e0e0e0] truncate"},group.sf.display||group.sf.name||group.sf.id),(group.presets||[]).slice(0,200).map((p,pi)=>{const progId=p.id||p.name||'preset_'+pi;return/*#__PURE__*/React.createElement("div",{key:progId,className:`flex items-center gap-1 px-2 pl-4 py-0.5 cursor-pointer hover:bg-blue-100 truncate ${synthInst&&synthInst.program===p.program&&synthInst.sfId===(group.sf.id||group.sf.name)?'bg-slate-200':''}`,onClick:()=>selectSynthInst({sfId:group.sf.id,sfName:group.sf.display||group.sf.name||group.sf.id,bank:p.bank||0,program:p.program,name:p.name||'Program '+p.program})},p.bank===128?/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-drum text-slate-400"}):/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-music text-slate-400"})," ",p.name||'Program '+p.program);}))),!synthLoading&&filteredSynthList&&filteredSynthList.length===0&&synthFilter&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Không tìm thấy nhạc cụ trùng khớp"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1",title:"Tempo preview MIDI"},/*#__PURE__*/React.createElement("span",{className:"font-mono text-[10px] text-slate-700"},"Tempo:"),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>commitTempo((tempo||120)-1)},"-"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-14"},/*#__PURE__*/React.createElement("input",{type:"number",min:"40",max:"300",value:tempoText,onChange:e=>{const raw=e.target.value;setTempoText(raw);const n=parseInt(raw);if(n>=40&&n<=300)commitTempo(n);},onBlur:()=>{const n=parseInt(tempoText);commitTempo(n);},onKeyDown:e=>{if(e.key==='Enter'){e.currentTarget.blur();}},className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>commitTempo((tempo||120)+1)},"+"),/*#__PURE__*/React.createElement("span",{className:"text-slate-600 text-[10px]"},"BPM"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 font-mono text-[11px]"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",null,"Pitch:"),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>togglePitch(-0.5)},"-"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-14"},/*#__PURE__*/React.createElement("input",{type:"number",value:pitch.toFixed(1),step:"0.5",onChange:e=>setPitch(parseFloat(e.target.value)||0),className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>togglePitch(0.5)},"+")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",null,"Rate:"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-12"},/*#__PURE__*/React.createElement("input",{type:"number",value:rate.toFixed(2),step:"0.1",onChange:e=>setRate(Math.max(0.25,Math.min(4,parseFloat(e.target.value)||1))),className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[9px]",onClick:()=>toggleRate(-1)},"-"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[9px]",onClick:()=>toggleRate(1)},"+"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"font-sans text-slate-700"},"Volume:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-60",max:"12",step:"0.5",value:volumeDb,onChange:e=>setVolumeDb(parseFloat(e.target.value)),className:"me-fader-slider w-24"}),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center justify-center w-14 font-mono text-[11px]"},volumeDb<=-50?'-inf':volumeDb.toFixed(1)," dB")),/*#__PURE__*/React.createElement("div",{className:`px-2 py-0.5 border font-mono font-bold text-[10px] rounded-sm ${selIsMidi?'bg-purple-950 text-purple-300 border-purple-800':'bg-emerald-950 text-emerald-300 border-emerald-800'}`},selIsMidi?'MIDI':'Audio')),/*#__PURE__*/React.createElement("div",{className:"flex items-stretch gap-2 my-1 flex-1 min-h-0"},/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-[#181818] border border-[#3a3a3a] relative overflow-hidden p-0.5"},/*#__PURE__*/React.createElement("canvas",{ref:canvasRef,className:"w-full h-full block cursor-pointer",onMouseDown:handleCanvasMouseDown,onMouseMove:handleCanvasMouseMove,onMouseUp:handleCanvasMouseUp,onContextMenu:handleCanvasContextMenu}),/*#__PURE__*/React.createElement("div",{className:"absolute top-1.5 right-1.5 flex gap-1 z-10"},/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-xs font-bold select-none cursor-pointer",title:"Zoom In",onClick:()=>setZoom(prev=>Math.min(10.0,prev*1.25))},"+"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-xs font-bold select-none cursor-pointer",title:"Zoom Out",onClick:()=>setZoom(prev=>Math.max(0.2,prev/1.25))},"-"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-[9px] select-none cursor-pointer",title:"Reset Zoom",onClick:()=>setZoom(1.0)},"1x")),previewCtxMenu&&/*#__PURE__*/React.createElement("div",{className:"fixed bg-[#1e1e24] border border-[#3e3e4a] rounded shadow-md z-[9999] py-1 font-sans text-xs text-slate-300 w-32 cursor-pointer select-none",style:{top:previewCtxMenu.y,left:previewCtxMenu.x},onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"px-3 py-1.5 hover:bg-slate-700 hover:text-white flex items-center gap-2",onClick:()=>{handleCopySelection();setPreviewCtxMenu(null);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-copy"})," Copy"),/*#__PURE__*/React.createElement("div",{className:"px-3 py-1.5 hover:bg-slate-700 hover:text-white border-t border-[#3e3e4a] flex items-center gap-2",onClick:()=>setPreviewCtxMenu(null)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"})," Cancel"))),/*#__PURE__*/React.createElement("div",{className:"w-48 bg-[#181818] border border-[#3a3a3a] p-1.5 font-mono text-[10px] text-slate-300 leading-tight overflow-y-auto shrink-0"},selected?selIsMidi&&!selected.path?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,selected.events," MIDI events"),/*#__PURE__*/React.createElement("div",null,"Length: ",selected.lengthQn," quarter notes"),/*#__PURE__*/React.createElement("div",null,"Length: ",selected.time," (est)"),/*#__PURE__*/React.createElement("div",null,"Ticks per quarter note: ",selected.tpqn)):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Size: ",selected.size_mb!=null?selected.size_mb.toFixed(2)+' MB':'-'),selIsMidi?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Bars: ",midiBars||1),/*#__PURE__*/React.createElement("div",null,"Beats: ",Math.round(midiTotalBeats||16)),/*#__PURE__*/React.createElement("div",null,"BPM: ",midiFileBpm||120),/*#__PURE__*/React.createElement("div",null,"Duration: ",selDur.toFixed(2),"s")):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Duration: ",selDur.toFixed(2),"s"),/*#__PURE__*/React.createElement("div",null,"Sample Rate: 44100 Hz"),/*#__PURE__*/React.createElement("div",null,"Type: ",selected.path?selected.kind==='other'?'Local File':'Local Audio':selected.type||'Audio'))):/*#__PURE__*/React.createElement("div",null,"No file selected"))),/*#__PURE__*/React.createElement("div",{className:"h-5 bg-[#c0c0c0] border-t border-white flex items-center justify-between text-[11px] font-mono px-1 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},selIsMidi&&midiNotes&&midiNotes.length?/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1.5 text-slate-900 font-bold"},"Bar ",Math.max(1,Math.floor(currentTime/(4*60/(tempo||120)))+1)," / ",midiBars||1,/*#__PURE__*/React.createElement("span",{className:"text-slate-500 ml-1"},"| ",formatTime(currentTime)," / ",formatTime(selDur))):/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1.5 text-slate-900 font-bold"},formatTime(currentTime)," / ",formatTime(selDur))),/*#__PURE__*/React.createElement("div",{className:"text-slate-800 font-bold truncate max-w-[40%]"},selected?selected.name||selected.original_name:'No file selected'),/*#__PURE__*/React.createElement("div",{className:"text-slate-700"},selBpm," bpm x",rate.toFixed(2)))));};const App=()=>{// ── State Definitions ── +if(cur&&(cur.handle||cur.path||cur.file_id||cur.fileId)){selectTokenRef.current++;const token=selectTokenRef.current;try{if(window.SonicSF&&typeof window.SonicSF.stopAll==='function')window.SonicSF.stopAll();}catch(e){}playMidiPreview(cur,token);}}return next;});};const toggleRate=dir=>setRate(prev=>Math.max(0.25,Math.min(4.0,Math.round((prev+dir*0.1)*100)/100)));const togglePitch=dir=>setPitch(prev=>Math.max(-24,Math.min(24,prev+dir*0.5)));const selIsMidi=isMidiFile(selected);const selDur=fileDuration(selected);const selBpm=selected&&selected.bpm||tempo;return/*#__PURE__*/React.createElement("div",{ref:containerRef,className:"flex flex-col w-full h-full text-slate-900 overflow-hidden select-none",style:{fontFamily:"'Inter', sans-serif"}},/*#__PURE__*/React.createElement("div",{className:"h-8 bg-[#d4d0c8] border-b border-[#808080] px-2 flex items-center justify-between text-xs shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 flex-1 mr-2"},/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Back",onClick:()=>folder==='computer'?goComputerParent():setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-left"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Forward",onClick:()=>setFolder('uploads')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-right"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Up Directory",onClick:()=>folder==='computer'?goComputerParent():setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-arrow-up"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-[10px]",title:"Refresh",onClick:()=>{if(folder==='computer'){const node=computerTree[computerPath];if(node&&node.handle)browseComputerDir({name:computerPath.split('/').pop()||computerPath,path:computerPath,is_dir:true,handle:node.handle});else if(computerPath)browseComputerDir({name:computerPath.split(/[\\/]/).pop()||computerPath,path:computerPath,is_dir:true});}else if(window.SonicAPI&&window.SonicAPI.listMyFiles)window.SonicAPI.listMyFiles([]).then(d=>setUserFiles(d||[]));}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-rotate-right"})),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex items-center bg-white border border-[#808080] h-5 px-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752] text-[11px] mr-1.5"}),/*#__PURE__*/React.createElement("span",{className:"flex-1 text-xs text-slate-800 truncate"},folder==='computer'?computerPath||'My Computer':folder==='library'?'Media Library':folder==='uploads'?'Uploads':'Processed'),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-slate-600 text-[10px] ml-1"}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center bg-white border border-[#808080] h-5 px-1 w-40"},/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"Filter/Search...",value:filterText,onChange:e=>setFilterText(e.target.value),className:"w-full text-xs outline-none bg-transparent font-sans text-slate-800"}),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-slate-600 text-[10px] ml-1"})),/*#__PURE__*/React.createElement("button",{className:"px-2 py-0.5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[11px] flex items-center gap-1 font-semibold",onClick:()=>setViewMode(viewMode==='details'?'list':'details')},/*#__PURE__*/React.createElement("span",null,viewMode==='details'?'Details':'List')," ",/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-[9px]"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-hidden min-h-0 relative"},/*#__PURE__*/React.createElement("div",{className:"flex bg-white border border-[#808080] m-1 mr-0 text-xs select-none shrink-0 overflow-hidden"},/*#__PURE__*/React.createElement("div",{ref:treePaneRef,className:"overflow-y-auto p-1",style:{width:treeWidth+'px',minWidth:treeWidth+'px',maxWidth:treeWidth+'px'}},/*#__PURE__*/React.createElement("div",{className:"space-y-0.5 font-sans"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 px-1 py-0.5 text-slate-700"},/*#__PURE__*/React.createElement("span",{className:"w-3"})," "),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 px-1 py-0.5 text-slate-700"},/*#__PURE__*/React.createElement("span",{className:"w-3"})," "),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm font-bold ${folder==='computer'&&computerPath==='favorited'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>{openFavorited();setFavoritedExpanded(!favoritedExpanded);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-star text-amber-500 text-[11px] font-bold"})," Favorited"),favoritedExpanded&&/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},favorites.map((fav,fi)=>/*#__PURE__*/React.createElement("div",{key:fav.path+fi,"data-tree-path":fav.path,className:"flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm hover:bg-amber-100 text-slate-800",style:{paddingLeft:20},onClick:()=>openFavorite(fav),onContextMenu:e=>{e.preventDefault();e.stopPropagation();setFavContext(Object.assign({},fav,{x:e.clientX,y:e.clientY}));}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752] shrink-0"}),/*#__PURE__*/React.createElement("span",{className:"truncate flex-1"},fav.name),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-star text-amber-500 text-[9px] shrink-0"}))),favorites.length===0&&/*#__PURE__*/React.createElement("div",{className:"pl-4 py-0.5 text-slate-400 italic text-[11px]"},"No favorites")),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer rounded-sm ${folder==='computer'&&computerPath!=='favorited'?'bg-slate-300 text-slate-900 font-semibold':'hover:bg-slate-200 text-slate-800'}`,onClick:openMyComputer},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-computer text-[11px] text-slate-600"})," My Computer"),folder==='computer'&&computerPath!=='favorited'&&computerRoots&&/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},computerRoots.map(root=>renderComputerNode(root,0,true))),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer font-semibold rounded-sm ${folder==='library'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('library')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752]"})," Media Library"),/*#__PURE__*/React.createElement("div",{className:"pl-3 space-y-0.5"},/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer pl-4 rounded-sm ${folder==='uploads'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('uploads')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752]"})," Uploads"),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-1 py-0.5 cursor-pointer pl-4 rounded-sm ${folder==='processed'?'bg-slate-300 text-slate-900':'hover:bg-slate-200 text-slate-800'}`,onClick:()=>setFolder('processed')},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder text-[#d9a752]"})," Processed")))),/*#__PURE__*/React.createElement("div",{className:"w-1.5 cursor-ew-resize hover:bg-blue-500/40 active:bg-blue-500/60 transition-colors shrink-0",onMouseDown:startTreeResize,title:"K\xE9o \u0111\u1EC3 thay \u0111\u1ED5i chi\u1EC1u r\u1ED9ng"})),favContext&&/*#__PURE__*/React.createElement("div",{className:"fixed z-[100] bg-white border border-[#808080] shadow-lg rounded-sm text-xs font-sans text-slate-800 min-w-[180px]",style:{left:favContext.x,top:favContext.y},onMouseLeave:()=>setFavContext(null)},/*#__PURE__*/React.createElement("div",{className:`px-2 py-1 cursor-pointer hover:bg-blue-100 flex items-center gap-1.5 ${isFavorite(favContext)?'text-amber-700':''}`,onClick:()=>{toggleFavorite(favContext);setFavContext(null);}},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${isFavorite(favContext)?'fa-star text-amber-500':'fa-star text-slate-400'} text-[11px]`}),isFavorite(favContext)?'Gỡ khỏi Favorited':'Thêm vào Favorited'),/*#__PURE__*/React.createElement("div",{className:"px-2 py-1 cursor-pointer hover:bg-blue-100 flex items-center gap-1.5",onClick:()=>{if(favContext)browseComputerDir(favContext);setFavContext(null);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-folder-open text-[#d9a752] text-[11px]"})," M\u1EDF th\u01B0 m\u1EE5c")),/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-white border border-[#808080] m-1 overflow-y-auto relative"},/*#__PURE__*/React.createElement("table",{className:"w-full text-xs text-left border-collapse",style:{tableLayout:'fixed'}},/*#__PURE__*/React.createElement("thead",{className:"sticky top-0 bg-[#e0e0e0] border-b border-[#a0a0a0] text-slate-800 font-semibold select-none shadow-sm z-10"},/*#__PURE__*/React.createElement("tr",null,/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:viewMode==='details'?{width:colWidths.file}:undefined},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"File"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('file',e)})),viewMode==='details'&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:{width:colWidths.size}},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"Size"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('size',e)})),/*#__PURE__*/React.createElement("th",{className:"py-1 px-2 border-r border-[#b0b0b0] relative",style:{width:colWidths.type}},/*#__PURE__*/React.createElement("div",{className:"truncate pr-2"},"Type"),/*#__PURE__*/React.createElement("div",{className:"absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-slate-400",onMouseDown:e=>startColResize('type',e)}))))),/*#__PURE__*/React.createElement("tbody",{className:"font-sans text-slate-800"},visibleFiles.map((f,i)=>{const isSel=selected&&(selected.name||selected.file_id)===(f.name||f.file_id);const isMidi=isMidiFile(f);const icon=f.is_dir?'fa-folder text-[#d9a752]':isMidi?'fa-music text-purple-600':f.kind==='audio'?'fa-file-audio text-emerald-600':'fa-file text-zinc-500';return/*#__PURE__*/React.createElement("tr",{key:(f.path||f.file_id||f.name)+i,draggable:!f.is_dir,onDragStart:e=>{if(f.is_dir){e.preventDefault();return;}e.dataTransfer.setData('text/plain',f.name||f.original_name||'');e.dataTransfer.effectAllowed='copy';window.__mediaExplorerDragFile=f;},onDragEnd:()=>{window.__mediaExplorerDragFile=null;},className:`cursor-pointer hover:bg-blue-100 ${isSel?'file-row-selected':''}`,onClick:()=>f.is_dir?browseComputerDir(f):handleSelect(f),onDoubleClick:()=>f.is_dir&&browseComputerDir(f),onContextMenu:e=>{if(f.is_dir){e.preventDefault();e.stopPropagation();setFavContext(Object.assign({},f,{x:e.clientX,y:e.clientY}));}}},/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${icon} mr-2`}),f.name||f.original_name),viewMode==='details'&&/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},f.size_mb!=null?f.size_mb.toFixed(2)+' MB':isMidi?(f.tpqn||'MIDI')+' TPQN':'-'),viewMode==='details'&&/*#__PURE__*/React.createElement("td",{className:"py-1 px-2 truncate"},f.is_dir?'Folder':isMidi?'MIDI':f.kind==='audio'?'Audio':'File'));}),visibleFiles.length===0&&/*#__PURE__*/React.createElement("tr",null,/*#__PURE__*/React.createElement("td",{className:"py-3 px-2 text-slate-400 italic",colSpan:viewMode==='details'?3:1},"No files")))))),/*#__PURE__*/React.createElement("div",{className:"h-[38%] min-h-[110px] bg-[#d4d0c8] border-t border-[#808080] p-1.5 flex flex-col justify-between text-xs shrink-0 select-none"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between gap-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{id:"btnStop",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-slate-800",title:"Stop",onClick:stopMediaPlayback},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-square text-[10px]"})),/*#__PURE__*/React.createElement("button",{id:"btnPlay",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-emerald-700 font-bold",title:"Play",onClick:()=>isPlaying?togglePause():playSelected(selected)},/*#__PURE__*/React.createElement("i",{className:`fa-solid ${isPlaying&&!isPaused?'fa-play':'fa-play'} text-xs`})),/*#__PURE__*/React.createElement("button",{id:"btnPause",className:"w-6 h-6 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm flex items-center justify-center text-amber-700",title:"Pause",onClick:togglePause},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-pause text-xs"})),/*#__PURE__*/React.createElement("button",{id:"btnLoop",className:`w-6 h-6 border rounded-sm flex items-center justify-center text-xs ${isLooping?'bg-cyan-600 text-white border-cyan-700':'bg-[#e0e0e0] hover:bg-white text-slate-700 border-[#707070]'}`,title:"Loop / Repeat",onClick:toggleLoop},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-rotate-right"})),/*#__PURE__*/React.createElement("button",{id:"btnAutoPlay",className:`h-6 px-2 border rounded-sm font-bold text-[10px] flex items-center gap-1 ${autoPlay?'bg-gradient-to-r from-cyan-600 to-emerald-600 text-white border-slate-700':'bg-[#e0e0e0] text-slate-600 border-[#707070]'}`,onClick:()=>setAutoPlay(p=>!p)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-bolt text-[9px]"})," ",/*#__PURE__*/React.createElement("span",null,"Auto-Play")),/*#__PURE__*/React.createElement("div",{className:"relative"},/*#__PURE__*/React.createElement("button",{id:"btnSynth",className:`h-6 px-2 border rounded-sm font-bold text-[10px] flex items-center gap-1 ${synthInst?'bg-gradient-to-r from-violet-600 to-fuchsia-600 text-white border-slate-700':'bg-[#e0e0e0] text-slate-600 border-[#707070]'}`,title:"Ch\u1ECDn instrument \u0111\u1EC3 preview MIDI",onClick:toggleSynthDropdown},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-music text-[9px]"})," ",/*#__PURE__*/React.createElement("span",null,"Synth",synthInst?': '+(synthInst.name||'?'):''),/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-caret-down text-[8px]"})),synthOpen&&/*#__PURE__*/React.createElement("div",{className:"absolute bottom-8 left-0 z-40 w-64 bg-white border border-[#808080] shadow-xl rounded-sm text-xs text-slate-800 font-sans max-h-72 overflow-y-auto"},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 bg-[#e0e0e0] px-2 py-1 font-bold border-b border-[#a0a0a0] flex items-center justify-between z-20"},/*#__PURE__*/React.createElement("span",null,"Select Instrument"),/*#__PURE__*/React.createElement("button",{onClick:()=>setSynthOpen(false),className:"text-slate-500 hover:text-slate-900"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"}))),/*#__PURE__*/React.createElement("div",{className:"sticky top-[23px] bg-white p-1 border-b border-[#c0c0c0] z-20 flex items-center gap-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-magnifying-glass text-slate-400 pl-1 text-[10px]"}),/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"T\xECm nh\u1EA1c c\u1EE5...",value:synthFilter,onChange:e=>setSynthFilter(e.target.value),onClick:e=>e.stopPropagation(),className:"w-full px-1 py-0.5 border border-[#c0c0c0] rounded-sm text-xs font-sans focus:outline-none focus:border-blue-500"}),synthFilter&&/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();setSynthFilter('');},className:"text-slate-400 hover:text-slate-700 px-1"},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark text-[10px]"}))),synthLoading&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Loading..."),!synthLoading&&(!synthList||synthList.length===0)&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Kh\xF4ng c\xF3 SoundFont n\xE0o"),/*#__PURE__*/React.createElement("div",{className:`flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-blue-100 ${!synthInst?'bg-slate-200':''}`,onClick:()=>selectSynthInst(null)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-ban text-slate-400"})," None (m\u1EB7c \u0111\u1ECBnh)"),!synthLoading&&filteredSynthList&&filteredSynthList.map(group=>/*#__PURE__*/React.createElement("div",{key:group.sf.id||group.sf.name},/*#__PURE__*/React.createElement("div",{className:"px-2 py-1 bg-[#f0f0f0] font-semibold text-slate-600 border-t border-[#e0e0e0] truncate"},group.sf.display||group.sf.name||group.sf.id),(group.presets||[]).slice(0,200).map((p,pi)=>{const progId=p.id||p.name||'preset_'+pi;return/*#__PURE__*/React.createElement("div",{key:progId,className:`flex items-center gap-1 px-2 pl-4 py-0.5 cursor-pointer hover:bg-blue-100 truncate ${synthInst&&synthInst.program===p.program&&synthInst.sfId===(group.sf.id||group.sf.name)?'bg-slate-200':''}`,onClick:()=>selectSynthInst({sfId:group.sf.id,sfName:group.sf.display||group.sf.name||group.sf.id,bank:p.bank||0,program:p.program,name:p.name||'Program '+p.program})},p.bank===128?/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-drum text-slate-400"}):/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-music text-slate-400"})," ",p.name||'Program '+p.program);}))),!synthLoading&&filteredSynthList&&filteredSynthList.length===0&&synthFilter&&/*#__PURE__*/React.createElement("div",{className:"px-2 py-2 text-slate-400 italic"},"Kh\xF4ng t\xECm th\u1EA5y nh\u1EA1c c\u1EE5 tr\xF9ng kh\u1EDBp"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1",title:"Tempo preview MIDI"},/*#__PURE__*/React.createElement("span",{className:"font-mono text-[10px] text-slate-700"},"Tempo:"),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>commitTempo((tempo||120)-1)},"-"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-14"},/*#__PURE__*/React.createElement("input",{type:"number",min:"40",max:"300",value:tempoText,onChange:e=>{const raw=e.target.value;setTempoText(raw);const n=parseInt(raw);if(n>=40&&n<=300)commitTempo(n);},onBlur:()=>{const n=parseInt(tempoText);commitTempo(n);},onKeyDown:e=>{if(e.key==='Enter'){e.currentTarget.blur();}},className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>commitTempo((tempo||120)+1)},"+"),/*#__PURE__*/React.createElement("span",{className:"text-slate-600 text-[10px]"},"BPM"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3 font-mono text-[11px]"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",null,"Pitch:"),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>togglePitch(-0.5)},"-"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-14"},/*#__PURE__*/React.createElement("input",{type:"number",value:pitch.toFixed(1),step:"0.5",onChange:e=>setPitch(parseFloat(e.target.value)||0),className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"px-1 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[10px]",onClick:()=>togglePitch(0.5)},"+")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",null,"Rate:"),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center w-12"},/*#__PURE__*/React.createElement("input",{type:"number",value:rate.toFixed(2),step:"0.1",onChange:e=>setRate(Math.max(0.25,Math.min(4,parseFloat(e.target.value)||1))),className:"w-full text-xs text-right outline-none bg-transparent"})),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[9px]",onClick:()=>toggleRate(-1)},"-"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#e0e0e0] hover:bg-white border border-[#707070] rounded-sm text-[9px]",onClick:()=>toggleRate(1)},"+"))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"font-sans text-slate-700"},"Volume:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-60",max:"12",step:"0.5",value:volumeDb,onChange:e=>setVolumeDb(parseFloat(e.target.value)),className:"me-fader-slider w-24"}),/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1 h-5 flex items-center justify-center w-14 font-mono text-[11px]"},volumeDb<=-50?'-inf':volumeDb.toFixed(1)," dB")),/*#__PURE__*/React.createElement("div",{className:`px-2 py-0.5 border font-mono font-bold text-[10px] rounded-sm ${selIsMidi?'bg-purple-950 text-purple-300 border-purple-800':'bg-emerald-950 text-emerald-300 border-emerald-800'}`},selIsMidi?'MIDI':'Audio')),/*#__PURE__*/React.createElement("div",{className:"flex items-stretch gap-2 my-1 flex-1 min-h-0"},/*#__PURE__*/React.createElement("div",{className:"flex-1 bg-[#181818] border border-[#3a3a3a] relative overflow-hidden p-0.5"},/*#__PURE__*/React.createElement("canvas",{ref:canvasRef,className:"w-full h-full block cursor-pointer",onMouseDown:handleCanvasMouseDown,onMouseMove:handleCanvasMouseMove,onMouseUp:handleCanvasMouseUp,onContextMenu:handleCanvasContextMenu}),/*#__PURE__*/React.createElement("div",{className:"absolute top-1.5 right-1.5 flex gap-1 z-10"},/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-xs font-bold select-none cursor-pointer",title:"Zoom In",onClick:()=>setZoom(prev=>Math.min(10.0,prev*1.25))},"+"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-xs font-bold select-none cursor-pointer",title:"Zoom Out",onClick:()=>setZoom(prev=>Math.max(0.2,prev/1.25))},"-"),/*#__PURE__*/React.createElement("button",{className:"w-5 h-5 bg-[#2a2a2a] hover:bg-slate-700 border border-[#444] rounded flex items-center justify-center text-white text-[9px] select-none cursor-pointer",title:"Reset Zoom",onClick:()=>setZoom(1.0)},"1x")),previewCtxMenu&&/*#__PURE__*/React.createElement("div",{className:"fixed bg-[#1e1e24] border border-[#3e3e4a] rounded shadow-md z-[9999] py-1 font-sans text-xs text-slate-300 w-32 cursor-pointer select-none",style:{top:previewCtxMenu.y,left:previewCtxMenu.x},onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"px-3 py-1.5 hover:bg-slate-700 hover:text-white flex items-center gap-2",onClick:()=>{handleCopySelection();setPreviewCtxMenu(null);}},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-copy"})," Copy"),/*#__PURE__*/React.createElement("div",{className:"px-3 py-1.5 hover:bg-slate-700 hover:text-white border-t border-[#3e3e4a] flex items-center gap-2",onClick:()=>setPreviewCtxMenu(null)},/*#__PURE__*/React.createElement("i",{className:"fa-solid fa-xmark"})," Cancel"))),/*#__PURE__*/React.createElement("div",{className:"w-48 bg-[#181818] border border-[#3a3a3a] p-1.5 font-mono text-[10px] text-slate-300 leading-tight overflow-y-auto shrink-0"},selected?selIsMidi&&!selected.path?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,selected.events," MIDI events"),/*#__PURE__*/React.createElement("div",null,"Length: ",selected.lengthQn," quarter notes"),/*#__PURE__*/React.createElement("div",null,"Length: ",selected.time," (est)"),/*#__PURE__*/React.createElement("div",null,"Ticks per quarter note: ",selected.tpqn)):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Size: ",selected.size_mb!=null?selected.size_mb.toFixed(2)+' MB':'-'),selIsMidi?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Bars: ",midiBars||1),/*#__PURE__*/React.createElement("div",null,"Beats: ",Math.round(midiTotalBeats||16)),/*#__PURE__*/React.createElement("div",null,"BPM: ",midiFileBpm||120),/*#__PURE__*/React.createElement("div",null,"Duration: ",selDur.toFixed(2),"s")):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",null,"Duration: ",selDur.toFixed(2),"s"),/*#__PURE__*/React.createElement("div",null,"Sample Rate: 44100 Hz"),/*#__PURE__*/React.createElement("div",null,"Type: ",selected.path?selected.kind==='other'?'Local File':'Local Audio':selected.type||'Audio'))):/*#__PURE__*/React.createElement("div",null,"No file selected"))),/*#__PURE__*/React.createElement("div",{className:"h-5 bg-[#c0c0c0] border-t border-white flex items-center justify-between text-[11px] font-mono px-1 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-3"},selIsMidi&&midiNotes&&midiNotes.length?/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1.5 text-slate-900 font-bold"},"Bar ",Math.max(1,Math.floor(currentTime/(4*60/(tempo||120)))+1)," / ",midiBars||1,/*#__PURE__*/React.createElement("span",{className:"text-slate-500 ml-1"},"| ",formatTime(currentTime)," / ",formatTime(selDur))):/*#__PURE__*/React.createElement("div",{className:"bg-white border border-[#808080] px-1.5 text-slate-900 font-bold"},formatTime(currentTime)," / ",formatTime(selDur))),/*#__PURE__*/React.createElement("div",{className:"text-slate-800 font-bold truncate max-w-[40%]"},selected?selected.name||selected.original_name:'No file selected'),/*#__PURE__*/React.createElement("div",{className:"text-slate-700"},selBpm," bpm x",rate.toFixed(2)))));};const App=()=>{// ── State Definitions ── const[tracks,setTracks]=useState([{id:'1',name:'Track 01',buffer:null,startTime:0,volumeDb:0,pan:0,muted:false,solo:false,color:'#0f766e',markers:[],serverFileId:null,clips:[],sections:[],midiItems:[],isArmed:false,monitoringEnabled:true,inputSource:{deviceType:'NONE',deviceId:''}},{id:'2',name:'Track 02',buffer:null,startTime:0,volumeDb:0,pan:0,muted:false,solo:false,color:'#1d4ed8',markers:[],serverFileId:null,clips:[],sections:[],midiItems:[],isArmed:false,monitoringEnabled:true,inputSource:{deviceType:'NONE',deviceId:''}}]);const[appWarningModal,setAppWarningModal]=useState(null);const[bpm,setBpm]=useState(localStorage.getItem('studio_bpm')||'120');const prevBpmRef=useRef(bpm);const[draggedClip,setDraggedClip]=useState(null);const[hoveredTrackId,setHoveredTrackId]=useState(null);// Recalculate item/section/selection durations when BPM changes useEffect(()=>{const oldSpb=prevBpmRef.current?60.0/parseFloat(prevBpmRef.current)*4:null;const bpmVal=parseFloat(bpm)||120;const secondsPerBar=60.0/bpmVal*4;// Recalculate range loop selection to maintain bar count (tempo mode only) 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 @@ -510,7 +526,8 @@ const[snapValue,onSnapChangeue]=useState('1');// 'free', '1', '1/2', '1/4', '1/8 const snapTime=(time,snapValue,bpmVal)=>{if(snapValue==='free')return time;const beatDuration=60/parseFloat(bpmVal||120);let divisor=1;if(snapValue==='4')divisor=4;else if(snapValue==='1')divisor=1;else if(snapValue==='1/2')divisor=0.5;else if(snapValue==='1/4')divisor=0.25;else if(snapValue==='1/8')divisor=0.125;else if(snapValue==='1/16')divisor=0.0625;else if(snapValue==='1/32')divisor=0.03125;const gridSpacing=beatDuration*divisor;return Math.round(time/gridSpacing)*gridSpacing;};const snapValueRef=useRef(snapValue);snapValueRef.current=snapValue;const bpmRef=useRef(bpm);bpmRef.current=bpm;// BMP for Tempo Track - LOOP_EDITOR_2.md §6 const[selectedTrackId,setSelectedTrackId]=useState('1');const[selectedItemIds,setSelectedItemIds]=useState(new Set());const selectedItemIdsRef=useRef(new Set());selectedItemIdsRef.current=selectedItemIds;const[currentTime,setCurrentTime]=useState(0);const[isPlaying,setIsPlaying]=useState(false);const[selectionStart,setSelectionStart]=useState(null);const[selectionEnd,setSelectionEnd]=useState(null);const[selectionFollowsTempo,setSelectionFollowsTempo]=useState(true);const selectionRef=useRef({start:null,end:null});selectionRef.current={start:selectionStart,end:selectionEnd};const[selectionMode,setSelectionMode]=useState(null);// 'global' (from ruler) | 'local' (from track) const[sweepSelect,setSweepSelect]=useState(null);const isSweepingRef=useRef(false);const sweepStartRef=useRef(0);const sweepTrackIdRef=useRef(null);const sweepStartYRef=useRef(0);const sweepEndYRef=useRef(0);const sweepSelectRef=useRef(null);const pendingDragRef=useRef(null);// { trackId, itemType, itemId, clickOffset, startX, startY } -const handleSectionItemDragStartRef=useRef(null);const[localSelectionTrackId,setLocalSelectionTrackId]=useState(null);const[localSelectionStart,setLocalSelectionStart]=useState(null);const[localSelectionEnd,setLocalSelectionEnd]=useState(null);const[zoom,setZoom]=useState(100);const[isLoopingSelection,setIsLoopingSelection]=useState(false);const[beginBar,setBeginBar]=useState(0);const[endBar,setEndBar]=useState(0);const[numberBar,setNumberBar]=useState(1);const[subTabHeight,setSubTabHeight]=useState(96);const[isExporting,setIsExporting]=useState(false);const[projectName,setProjectName]=useState(()=>localStorage.getItem('sonic_project_name')||'');const[currentProjectId,setCurrentProjectId]=useState(()=>localStorage.getItem('sonic_project_id')||null);const[saveProjectModalOpen,setSaveProjectModalOpen]=useState(false);const[saveAsModalOpen,setSaveAsModalOpen]=useState(false);const[openProjectModalOpen,setOpenProjectModalOpen]=useState(false);const hasAnySolo=tracks.some(t=>t.solo);const[toastMessage,setToastMessage]=useState(null);const[audioDevices,setAudioDevices]=useState([]);const[midiDevices,setMidiDevices]=useState([]);const[selectedMidiInputId,setSelectedMidiInputId]=useState('');const selectedMidiInputIdRef=useRef('');const handleMidiInputSelect=id=>{setSelectedMidiInputId(id);selectedMidiInputIdRef.current=id;if(window.SonicRecorderManager){window.SonicRecorderManager.setSelectedMidiInputId(id);}};useEffect(()=>{if(navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices){navigator.mediaDevices.enumerateDevices().then(devices=>{setAudioDevices(devices.filter(d=>d.kind==='audioinput'));}).catch(err=>console.log('Enumerate audio devices error:',err));}if(navigator.requestMIDIAccess){navigator.requestMIDIAccess().then(access=>{const inputs=[];function attachMidiHandler(input){input.onmidimessage=msg=>{// Filter by selected MIDI input device +const handleSectionItemDragStartRef=useRef(null);const[localSelectionTrackId,setLocalSelectionTrackId]=useState(null);const[localSelectionStart,setLocalSelectionStart]=useState(null);const[localSelectionEnd,setLocalSelectionEnd]=useState(null);const[zoom,setZoom]=useState(()=>{// Persist zoom qua reload (user yêu cầu giữ kích thước items sau refresh) +try{const v=parseFloat(localStorage.getItem('sf_zoom'));if(isFinite(v)&&v>0)return v;}catch(e){}return 100;});React.useEffect(()=>{try{localStorage.setItem('sf_zoom',String(zoom));}catch(e){}},[zoom]);const[isLoopingSelection,setIsLoopingSelection]=useState(false);const[beginBar,setBeginBar]=useState(0);const[endBar,setEndBar]=useState(0);const[numberBar,setNumberBar]=useState(1);const[subTabHeight,setSubTabHeight]=useState(96);const[isExporting,setIsExporting]=useState(false);const[projectName,setProjectName]=useState(()=>localStorage.getItem('sonic_project_name')||'');const[currentProjectId,setCurrentProjectId]=useState(()=>localStorage.getItem('sonic_project_id')||null);const[saveProjectModalOpen,setSaveProjectModalOpen]=useState(false);const[saveAsModalOpen,setSaveAsModalOpen]=useState(false);const[openProjectModalOpen,setOpenProjectModalOpen]=useState(false);const hasAnySolo=tracks.some(t=>t.solo);const[toastMessage,setToastMessage]=useState(null);const[audioDevices,setAudioDevices]=useState([]);const[midiDevices,setMidiDevices]=useState([]);const[selectedMidiInputId,setSelectedMidiInputId]=useState('');const selectedMidiInputIdRef=useRef('');const handleMidiInputSelect=id=>{setSelectedMidiInputId(id);selectedMidiInputIdRef.current=id;if(window.SonicRecorderManager){window.SonicRecorderManager.setSelectedMidiInputId(id);}};useEffect(()=>{if(navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices){navigator.mediaDevices.enumerateDevices().then(devices=>{setAudioDevices(devices.filter(d=>d.kind==='audioinput'));}).catch(err=>console.log('Enumerate audio devices error:',err));}if(navigator.requestMIDIAccess){navigator.requestMIDIAccess().then(access=>{const inputs=[];function attachMidiHandler(input){input.onmidimessage=msg=>{// Filter by selected MIDI input device const selId=selectedMidiInputIdRef.current;if(selId&&selId!=='ALL'&&input.id!==selId)return;console.log(`[DevLog] [Raw MIDI] Received from "${input.name}" (ID: ${input.id}) - Data:`,Array.from(msg.data));if(msg.data.length<3)return;const cmd=msg.data[0]>>4;const pitch=msg.data[1];const rawVel=msg.data[2];// Scale low MIDI velocity naturally (min 1, max 127) const scaledVel=Math.min(127,Math.max(1,Math.round(rawVel)));if(cmd===0x9&&rawVel>0){lastMidiNoteRef.current={pitch,velocity:scaledVel,startTime:performance.now(),length:0};setLastMidiNote({pitch,velocity:scaledVel,length:0,time:Date.now()});activeMidiPitchesRef.current.add(pitch);setActiveMidiPitches(new Set(activeMidiPitchesRef.current));// Route MIDI input to ALL armed tracks on their dedicated channels // Do NOT use raw MIDI hardware channel (msg.data[0] & 0x0F) @@ -650,7 +667,15 @@ const ctx=getAudioContext();const clipBuffer=ctx.createBuffer(1,len,sr);clipBuff const newLen=data.length-len;const newBuffer=ctx.createBuffer(1,newLen,sr);const newData=newBuffer.getChannelData(0);let idx=0;for(let i=0;ip.map(tr=>tr.id===selectedTrackId?{...tr,buffer:newBuffer}:tr));const afterSnap=captureTrackSnapshot(selectedTrackId);pushAction('CUT',selectedTrackId,beforeSnap,afterSnap);showToast('Cut selection to clipboard.','info');return;}}// No selection: cut entire track (copy + delete) handleCopyTrack();handleDeleteTrack();};const handleDeleteTrack=()=>{const tid=selectedTrackId;const sessionTab=sessionTabs.find(s=>s.id===activeTab);if(sessionTab){const trackData=activeTracks.find(t=>t.id===tid);if(trackData)deleteTrackWithUndo(tid,JSON.parse(JSON.stringify(trackData)));updateActiveTracks(prev=>prev.filter(t=>t.id!==tid));setSelectedTrackId(activeTracks.filter(t=>t.id!==tid)[0]?.id||'1');}else{const curTracks=tracks;const track=curTracks.find(t=>t.id===tid);if(track&&track.sections&&track.sections.length>0){showToast('Không thể xoá track chứa Section item.','warning');return;}if(track)deleteTrackWithUndo(tid,JSON.parse(JSON.stringify(track)));setTracks(p=>p.filter(t=>t.id!==tid));setSelectedTrackId(curTracks.filter(t=>t.id!==tid)[0]?.id||'1');}showToast('Deleted track.','info');};handleDeleteTrackRef.current=handleDeleteTrack;// ── Server Health Check ── const[viewportWidth,setViewportWidth]=useState(1200);useEffect(()=>{if(!timelineWrapperNode)return;const observer=new ResizeObserver(entries=>{for(let entry of entries){setViewportWidth(entry.contentRect.width);}});observer.observe(timelineWrapperNode);return()=>observer.disconnect();},[timelineWrapperNode]);useEffect(()=>{fetch(API_BASE_URL).then(r=>{if(r.ok)setServerStatus('connected');else setServerStatus('error');}).catch(()=>setServerStatus('offline'));},[]);// ── Computed Values ── -const leadInMargin=0;const leadInMarginRef=useRef(0);leadInMarginRef.current=0;const maxDuration=useMemo(()=>{const secPerBar=60.0/(parseInt(bpm)||120)*4;const cur=activeTracks;let max=10;cur.forEach(t=>{const clips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default',buffer:t.buffer,startTime:t.startTime||0,name:t.name,speed:t.speed||1.0}]:[];clips.forEach(c=>{if(c.buffer){const cStart=c.startTime||0;const cDur=c.buffer.duration/(c.speed||1.0);max=Math.max(max,cStart+cDur);}});(t.midiItems||[]).forEach(m=>{max=Math.max(max,(m.startTime||0)+(m.duration||4));});(t.sections||[]).forEach(s=>{max=Math.max(max,(s.start||0)+(s.duration||4));});});if(recordingState==='RECORDING'||recordingState==='COUNT_IN'){max=Math.max(max,currentTime+60);}max+=secPerBar*12+scrollBufferExtra;return max;},[activeTracks,recordingState,currentTime,bpm,scrollBufferExtra]);const maxDurationRef=useRef(maxDuration);maxDurationRef.current=maxDuration;const minZoom=useMemo(()=>{return viewportWidth/maxDuration;},[viewportWidth,maxDuration]);const timelineWidth=useMemo(()=>{return Math.max(zoom*(maxDuration+leadInMargin),viewportWidth);},[zoom,maxDuration,viewportWidth,leadInMargin]);useEffect(()=>{if(zoom(currentTime+leadInMargin)*zoom,[currentTime,zoom,leadInMargin]);const selLeft=useMemo(()=>{if(selectionMode==='local'&&localSelectionStart!==null&&localSelectionEnd!==null){return Math.max(0,Math.min(localSelectionStart,localSelectionEnd));}if(selectionStart===null||selectionEnd===null)return null;return Math.max(0,Math.min(selectionStart,selectionEnd));},[selectionStart,selectionEnd,selectionMode,localSelectionStart,localSelectionEnd]);const selRight=useMemo(()=>{if(selectionMode==='local'&&localSelectionStart!==null&&localSelectionEnd!==null){return Math.max(0,Math.max(localSelectionStart,localSelectionEnd));}if(selectionStart===null||selectionEnd===null)return null;return Math.max(0,Math.max(selectionStart,selectionEnd));},[selectionStart,selectionEnd,selectionMode,localSelectionStart,localSelectionEnd]);const dspSelectionStats=useMemo(()=>{const track=tracks.find(t=>t.id===selectedTrackId);if(!track)return null;const numChannels=track.buffer?track.buffer.numberOfChannels||1:0;if(selLeft===null||selRight===null||selRight<=selLeft||!track.buffer){return{trackName:track.name,channels:numChannels,timeRange:'Chưa chọn vùng',peakVolume:'N/A'};}const buffer=track.buffer;const sampleRate=buffer.sampleRate;const startSample=Math.max(0,Math.min(buffer.length-1,Math.floor(selLeft*sampleRate)));const endSample=Math.max(0,Math.min(buffer.length,Math.floor(selRight*sampleRate)));let maxVal=0;for(let c=0;cmaxVal)maxVal=val;}}let peakDb='N/A';if(maxVal>0){const db=20*Math.log10(maxVal);peakDb=db.toFixed(2)+' dB';}else{peakDb='-∞ dB';}return{trackName:track.name,channels:numChannels,timeRange:`${selLeft.toFixed(2)}s - ${selRight.toFixed(2)}s (${(selRight-selLeft).toFixed(2)}s)`,peakVolume:peakDb};},[tracks,selectedTrackId,selLeft,selRight]);// ── Toast helper ── +const leadInMargin=0;const leadInMarginRef=useRef(0);leadInMarginRef.current=0;const maxDuration=useMemo(()=>{const secPerBar=60.0/(parseInt(bpm)||120)*4;// MAIN SESSION: endtime của items trên track MAIN (mặc kệ SECTION-TAB dài bao nhiêu — +// sub-track items bị clamp trong section bounds khi play trong main session). +// SECTION-TAB: endtime của items trên track CỦA TAB (tính theo vị trí section trên main). +let max;if(activeTab==='main'){max=computeMainSessionEndTime(activeTracks);}else{const tab=sessionTabs.find(st=>st.id===activeTab);const tabMax=tab?computeMainSessionEndTime(tab.tracks||[]):0;let secStart=0;const secRef=tab?tab.sectionId:null;if(secRef){activeTracks.forEach(t=>(t.sections||[]).forEach(s=>{if(s.sectionId===secRef||s.id===secRef)secStart=s.start||0;}));}max=secStart+tabMax;}if(recordingState==='RECORDING'||recordingState==='COUNT_IN'){max=Math.max(max,currentTime+60);}max+=secPerBar*12+scrollBufferExtra;return max;},[activeTab,activeTracks,sessionTabs,recordingState,currentTime,bpm,scrollBufferExtra]);const maxDurationRef=useRef(maxDuration);maxDurationRef.current=maxDuration;// ── Project REAL end-time (KHÔNG buffer) ── +// maxDuration ở trên cộng 12 bars + scrollBuffer cho vùng SCROLL/ZOOM; nếu +// dùng nó làm điểm dừng LOOP thì loop kéo dài quá duration thật. projectEnd +// = endtime của session (main: items trên track main; section-tab: secStart + +// items của tab) — dùng cho updatePlayhead dừng/loop lại đúng cuối bài. +const projectEnd=useMemo(()=>{let max;if(activeTab==='main'){max=computeMainSessionEndTime(activeTracks);}else{const tab=sessionTabs.find(st=>st.id===activeTab);const tabMax=tab?computeMainSessionEndTime(tab.tracks||[]):0;let secStart=0;const secRef=tab?tab.sectionId:null;if(secRef){activeTracks.forEach(t=>(t.sections||[]).forEach(s=>{if(s.sectionId===secRef||s.id===secRef)secStart=s.start||0;}));}max=secStart+tabMax;}if(recordingState==='RECORDING'||recordingState==='COUNT_IN'){max=Math.max(max,currentTime+60);}return Math.max(1,max);},[activeTab,activeTracks,sessionTabs,recordingState,currentTime]);const projectEndRef=useRef(projectEnd);projectEndRef.current=projectEnd;const minZoom=useMemo(()=>{return viewportWidth/maxDuration;},[viewportWidth,maxDuration]);const timelineWidth=useMemo(()=>{return Math.max(zoom*(maxDuration+leadInMargin),viewportWidth);},[zoom,maxDuration,viewportWidth,leadInMargin]);useEffect(()=>{if(zoom(currentTime+leadInMargin)*zoom,[currentTime,zoom,leadInMargin]);const selLeft=useMemo(()=>{if(selectionMode==='local'&&localSelectionStart!==null&&localSelectionEnd!==null){return Math.max(0,Math.min(localSelectionStart,localSelectionEnd));}if(selectionStart===null||selectionEnd===null)return null;return Math.max(0,Math.min(selectionStart,selectionEnd));},[selectionStart,selectionEnd,selectionMode,localSelectionStart,localSelectionEnd]);const selRight=useMemo(()=>{if(selectionMode==='local'&&localSelectionStart!==null&&localSelectionEnd!==null){return Math.max(0,Math.max(localSelectionStart,localSelectionEnd));}if(selectionStart===null||selectionEnd===null)return null;return Math.max(0,Math.max(selectionStart,selectionEnd));},[selectionStart,selectionEnd,selectionMode,localSelectionStart,localSelectionEnd]);const dspSelectionStats=useMemo(()=>{const track=tracks.find(t=>t.id===selectedTrackId);if(!track)return null;const numChannels=track.buffer?track.buffer.numberOfChannels||1:0;if(selLeft===null||selRight===null||selRight<=selLeft||!track.buffer){return{trackName:track.name,channels:numChannels,timeRange:'Chưa chọn vùng',peakVolume:'N/A'};}const buffer=track.buffer;const sampleRate=buffer.sampleRate;const startSample=Math.max(0,Math.min(buffer.length-1,Math.floor(selLeft*sampleRate)));const endSample=Math.max(0,Math.min(buffer.length,Math.floor(selRight*sampleRate)));let maxVal=0;for(let c=0;cmaxVal)maxVal=val;}}let peakDb='N/A';if(maxVal>0){const db=20*Math.log10(maxVal);peakDb=db.toFixed(2)+' dB';}else{peakDb='-∞ dB';}return{trackName:track.name,channels:numChannels,timeRange:`${selLeft.toFixed(2)}s - ${selRight.toFixed(2)}s (${(selRight-selLeft).toFixed(2)}s)`,peakVolume:peakDb};},[tracks,selectedTrackId,selLeft,selRight]);// ── Toast helper ── const showToast=(text,type='info',actionText=null,onActionClick=null)=>{if(toastTimeoutRef.current)clearTimeout(toastTimeoutRef.current);setToastMessage({text,type,actionText,onActionClick});toastTimeoutRef.current=setTimeout(()=>setToastMessage(null),actionText?8000:3500);};window.showToast=showToast;// ── Server-side upload ── const uploadToServer=async(file,trackId)=>{const formData=new FormData();formData.append('file',file);try{const resp=await fetch(`${API_AUDIO}/upload`,{method:'POST',body:formData});if(!resp.ok)throw new Error(`Upload failed: ${resp.status}`);const data=await resp.json();serverFileIdMap[trackId]=data.file_id;return data;}catch(err){console.warn('Server upload failed, using client-side only:',err.message);return null;}};// ── Server-side waveform loading ── const loadServerWaveform=async fileId=>{try{const resp=await fetch(`${API_AUDIO}/waveform/${fileId}?num_peaks=800`);if(!resp.ok)return null;return await resp.json();}catch{return null;}};// ── Check Celery task result ── @@ -663,7 +688,27 @@ const volNodes=st.volumeNodes||[];if(volNodes.length>0){volumeGainNode.gain.canc const panNodes=st.panningNodes||[];if(panNodes.length>0){pannerNode.pan.cancelScheduledValues(context.currentTime);panNodes.forEach((n,i)=>{const t=context.currentTime+n.time/speed;const clamped=Math.max(-1,Math.min(1,n.pan));if(i===0)pannerNode.pan.setValueAtTime(clamped,t);else pannerNode.pan.linearRampToValueAtTime(clamped,t);});}// Schedule fade curves const duration=st.buffer.duration;const fIn=st.fadeInLen||0;const fOut=st.fadeOutLen||0;if(fIn>0){fadeGainNode.gain.setValueAtTime(0.0,context.currentTime);fadeGainNode.gain.linearRampToValueAtTime(1.0,context.currentTime+fIn/speed);}if(fOut>0){const fadeOutStart=(duration-fOut)/speed;fadeGainNode.gain.setValueAtTime(1.0,context.currentTime+Math.max(0,fadeOutStart));fadeGainNode.gain.linearRampToValueAtTime(0.0,context.currentTime+duration/speed);}source.connect(volumeGainNode);volumeGainNode.connect(pannerNode);pannerNode.connect(fadeGainNode);// Route through mastering chain unless this track has mastering bypass ON. const route=createMasteringRoute(context,{masteringBypass:!!trackAudioBypassMap[st.trackId]},masterBus);fadeGainNode.connect(route.routeGain);fadeGainNode.connect(route.dryGain);source.start(context.currentTime,offsetBuffer);activeSourcesRef.current=[source];activeTrackNodesRef.current[st.trackId]={gainNode:volumeGainNode,pannerNode,source};// Realtime mute/solo for the newly created playback chain. -if(window.__applyTrackMuteSolo)window.__applyTrackMuteSolo(st.trackId,null);startOffsetTimeRef.current=offsetWallTime;startBufferOffsetRef.current=offsetBuffer;startAudioTimeRef.current=context.currentTime;};const playMetronomeClick=(time,isDownbeat=false)=>{try{const ctx=getAudioContext();const osc=ctx.createOscillator();const gainNode=ctx.createGain();osc.connect(gainNode);gainNode.connect(masterBus?masterBus.input:ctx.destination);osc.frequency.setValueAtTime(isDownbeat?1000:800,time);gainNode.gain.setValueAtTime(0.08,time);gainNode.gain.exponentialRampToValueAtTime(0.001,time+0.08);osc.start(time);osc.stop(time+0.1);}catch(e){console.log('Metronome click play error:',e);}};const updatePlayhead=()=>{if(recordingStateRef.current==='RECORDING'){const audioCtx=getAudioContext();const lookahead=0.1;// 100ms +if(window.__applyTrackMuteSolo)window.__applyTrackMuteSolo(st.trackId,null);startOffsetTimeRef.current=offsetWallTime;startBufferOffsetRef.current=offsetBuffer;startAudioTimeRef.current=context.currentTime;};const playMetronomeClick=(time,isDownbeat=false)=>{try{const ctx=getAudioContext();const osc=ctx.createOscillator();const gainNode=ctx.createGain();osc.connect(gainNode);gainNode.connect(masterBus?masterBus.input:ctx.destination);osc.frequency.setValueAtTime(isDownbeat?1000:800,time);gainNode.gain.setValueAtTime(0.08,time);gainNode.gain.exponentialRampToValueAtTime(0.001,time+0.08);osc.start(time);osc.stop(time+0.1);}catch(e){console.log('Metronome click play error:',e);}};// Realtime re-schedule tracking (loop play cập nhật khi items đổi vị trí) +const scheduledItemsSigRef=React.useRef('');const playheadFrameCountRef=React.useRef(0);const pendingRescheduleRef=React.useRef(null);const lastRescheduleTimeRef=React.useRef(0);// Re-schedule NGAY (reset cooldown) sau khi THẢ chuột — check kế tiếp trong +// updatePlayhead (~100ms) re-schedule luôn. +const triggerRescheduleNow=()=>{try{lastRescheduleTimeRef.current=0;}catch(e){}};const updatePlayhead=()=>{// Realtime re-schedule: khi đang play (loop play) mà items (vị trí/speed/ +// duration — kể cả nội dung section tab) thay đổi → dừng + schedule lại từ +// playhead hiện tại để item mới phát đúng vị trí mới (không phát nội dung +// cũ ở vị trí cũ). Kiểm tra ~10fps (mỗi 6 frame) để kéo item cập nhật gần +// như realtime mà không tốn CPU mỗi frame. +if(isPlaying&&activeTabRef.current==='main'&&recordingStateRef.current!=='RECORDING'){playheadFrameCountRef.current++;if(playheadFrameCountRef.current%6===0){// ⚠️ Dùng REFS (không phải state closure) — rAF loop giữ updatePlayhead +// của render cũ nên activeTracks/sessionTabs trong closure là STALE → +// signature không bao giờ đổi khi kéo item → không re-schedule. +const sig=buildItemsSignature(activeTracksRef.current,sessionTabsRef.current);if(sig!==scheduledItemsSigRef.current){// THROTTLE ~300ms: re-schedule định kỳ NGAY CẢ TRONG LÚC KÉO (clip +// kéo đi → âm thanh cũ dừng ≤300ms, clip mới phát khi playhead tới +// vị trí mới — realtime) mà không stop/start mỗi frame (giật). Sau +// khi thả chuột triggerRescheduleNow reset cooldown → re-schedule +// ở check kế tiếp (~100ms). +const nowT=performance.now();if(nowT-(lastRescheduleTimeRef.current||0)>300){lastRescheduleTimeRef.current=nowT;scheduledItemsSigRef.current=sig;const pt=currentTimeRef.current;stopAllPlayback();setIsPlaying(true);// Re-schedule theo ĐÚNG chế độ play hiện tại: loop local / solo chỉ +// phát track liên quan (không phát nhầm track khác); ngược lại play +// toàn session. Cả 2 hàm đều tính playOffset = pt − clip.startTime +// → clip vừa kéo tới đúng playhead phát TỪ ĐẦU clip (realtime). +const curTracks=activeTracksRef.current||activeTracks;const soloed=curTracks.some(t=>t.solo);if(soloed){curTracks.filter(t=>t.solo).forEach(t=>startLocalTrackPlayback(t.id,pt));}else if(selectionMode==='local'&&localSelectionTrackId){startLocalTrackPlayback(localSelectionTrackId,pt);}else{startTrackPlayback(pt);}}}else{pendingRescheduleRef.current=null;}}}if(recordingStateRef.current==='RECORDING'){const audioCtx=getAudioContext();const lookahead=0.1;// 100ms const secondsPerBeat=60.0/(parseInt(bpmRef.current)||120);// Metronome Click Scheduler while(true){const beatNum=nextMetronomeBeatRef.current;const elapsedBeats=beatNum-recordingStartTimeRef.current/secondsPerBeat;const beatTime=startAudioTimeRef.current+elapsedBeats*secondsPerBeat;if(beatTime200){lastTempCompileTimeRef.current=now;// Audio preview @@ -672,7 +717,9 @@ let combinedNotes=[];const armedTracks=activeTracksRef.current.filter(t=>t.isArm if(st.selectionStart!==null&&st.selectionEnd!==null&&st.selectionStart!==st.selectionEnd&&st.isLooping){const start=Math.min(st.selectionStart,st.selectionEnd);const end=Math.max(st.selectionStart,st.selectionEnd);if(bufferPos>=end){stopAllPlayback();setSubTabs(prev=>prev.map(s=>s.id===activeTabRef.current?{...s,currentTime:start,isPlaying:true}:s));schedulePianoRollMidi(st,start);startSubTabPlayback(st,start);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}}const effectiveDuration=st.type==='PIANO_ROLL'?(()=>{const notes=st.notes||[];const bpmVal=parseInt(bpmRef?.current||bpm)||120;const beatSec=60.0/bpmVal;if(recordingStateRef.current==='RECORDING')return 600.0;// 10 min during recording let maxEnd=0;notes.forEach(n=>{const end=(n.start_beat||0)+(n.duration_beats||1);if(end>maxEnd)maxEnd=end;});return Math.max(maxEnd*beatSec,16*beatSec*4)+1.0;})():st.buffer.duration;if(bufferPos>=effectiveDuration){stopAllPlayback();if(st.isLooping){setSubTabs(prev=>prev.map(s=>s.id===activeTabRef.current?{...s,currentTime:0,isPlaying:true}:s));startSubTabPlayback(st,0);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);}else{setSubTabs(prev=>prev.map(s=>s.id===activeTabRef.current?{...s,currentTime:0,isPlaying:false}:s));}return;}setSubTabs(prev=>prev.map(s=>s.id===activeTabRef.current?{...s,currentTime:wallTime}:s));animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}if(!isPlaying)return;const context=getAudioContext();const elapsed=context.currentTime-startAudioTimeRef.current;const updatedTime=startOffsetTimeRef.current+elapsed;// Selection Loop - LOOP_MAKER.md + LOOP_EDITOR_2.md §4.2 // If selection cleared by user, play linearly (don't loop) -if(!selectionCleared&&isLoopingSelection&&selLeft!==null&&selRight!==null){if(selRight>selLeft&&updatedTime>=selRight){if(hasAnySolo||selectionMode==='local'){stopAllPlayback();startOffsetTimeRef.current=selLeft;startAudioTimeRef.current=context.currentTime;if(hasAnySolo){const soloed=tracks.filter(t=>t.solo);soloed.forEach(t=>startLocalTrackPlayback(t.id,selLeft));}else{startLocalTrackPlayback(localSelectionTrackId,selLeft);}setCurrentTime(selLeft);setIsPlaying(true);}else{stopAllPlayback();startOffsetTimeRef.current=selLeft;startAudioTimeRef.current=context.currentTime;startTrackPlayback(selLeft);setCurrentTime(selLeft);setIsPlaying(true);}animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}}if(updatedTime>=maxDurationRef.current){if(recordingStateRef.current==='RECORDING'){setCurrentTime(updatedTime);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}if(isLoopingSelection){stopAllPlayback();startOffsetTimeRef.current=0;startAudioTimeRef.current=context.currentTime;startTrackPlayback(0);setCurrentTime(0);setIsPlaying(true);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}stopAllPlayback();setCurrentTime(0);return;}setCurrentTime(updatedTime);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);};useEffect(()=>{if(isPlaying||subTabs.some(s=>s.isPlaying)){animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);}else{cancelAnimationFrame(animationFrameIdRef.current);}return()=>cancelAnimationFrame(animationFrameIdRef.current);},[isPlaying,subTabs,isLoopingSelection,selLeft,selRight,selectionMode,localSelectionTrackId,selectionCleared,activeTab]);// ── FX Nodes ── +if(!selectionCleared&&isLoopingSelection&&selLeft!==null&&selRight!==null){if(selRight>selLeft&&updatedTime>=selRight){if(hasAnySolo||selectionMode==='local'){stopAllPlayback();startOffsetTimeRef.current=selLeft;startAudioTimeRef.current=context.currentTime;if(hasAnySolo){const soloed=tracks.filter(t=>t.solo);soloed.forEach(t=>startLocalTrackPlayback(t.id,selLeft));}else{startLocalTrackPlayback(localSelectionTrackId,selLeft);}setCurrentTime(selLeft);setIsPlaying(true);}else{stopAllPlayback();startOffsetTimeRef.current=selLeft;startAudioTimeRef.current=context.currentTime;startTrackPlayback(selLeft);setCurrentTime(selLeft);setIsPlaying(true);}animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}}// Hết bài: dừng/loop lại tại ENDTIME THẬT của session (projectEnd — không +// buffer 12 bars như maxDuration dùng cho scroll/zoom). +if(updatedTime>=projectEndRef.current){if(recordingStateRef.current==='RECORDING'){setCurrentTime(updatedTime);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}if(isLoopingSelection){stopAllPlayback();startOffsetTimeRef.current=0;startAudioTimeRef.current=context.currentTime;startTrackPlayback(0);setCurrentTime(0);setIsPlaying(true);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);return;}stopAllPlayback();setCurrentTime(0);return;}setCurrentTime(updatedTime);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);};useEffect(()=>{if(isPlaying||subTabs.some(s=>s.isPlaying)){animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);}else{cancelAnimationFrame(animationFrameIdRef.current);}return()=>cancelAnimationFrame(animationFrameIdRef.current);},[isPlaying,subTabs,isLoopingSelection,selLeft,selRight,selectionMode,localSelectionTrackId,selectionCleared,activeTab]);// ── FX Nodes ── const createChorusNode=(context,inputNode,outputNode)=>{const dryGain=context.createGain();dryGain.gain.value=0.6;const wetGain=context.createGain();wetGain.gain.value=0.5;const delayNode=context.createDelay();delayNode.delayTime.value=0.02;const lfo=context.createOscillator();lfo.type='sine';lfo.frequency.value=1.5;const lfoGain=context.createGain();lfoGain.gain.value=0.002;lfo.connect(lfoGain);lfoGain.connect(delayNode.delayTime);lfo.start();inputNode.connect(dryGain);inputNode.connect(delayNode);delayNode.connect(wetGain);dryGain.connect(outputNode);wetGain.connect(outputNode);return{stop:()=>{try{lfo.stop();}catch(e){}}};};const createReverbNode=(context,inputNode,outputNode)=>{const dryGain=context.createGain();dryGain.gain.value=0.6;const wetGain=context.createGain();wetGain.gain.value=0.4;const convolver=context.createConvolver();const rate=context.sampleRate;const len=rate*2.0;const impulse=context.createBuffer(2,len,rate);const left=impulse.getChannelData(0);const right=impulse.getChannelData(1);for(let i=0;i{if(!track)return null;let node=activeTrackNodesRef.current[track.id];if(!node){const gainNode=context.createGain();const volDb=track.volumeDb??0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);gainNode.gain.setValueAtTime(volLinear,context.currentTime);const pannerNode=context.createStereoPanner();pannerNode.pan.setValueAtTime((track.pan??0)/100,context.currentTime);// Ensure master bus is initialized for MAIN OUT routing if(!masterBus)initMasterBus(context);const analyserNode=context.createAnalyser();analyserNode.fftSize=2048;pannerNode.connect(analyserNode);// Wave Observer scope analysers (unified_fx_rack_panel_update.md §III.3): @@ -735,7 +782,9 @@ if(!node.scopeAnalyserL||!node.scopeAnalyserR)return null;return{L:node.scopeAna // node so interactive panels can drive its DSP params in realtime. window.__getTrackFxModule=(trackId,type)=>{const node=activeTrackNodesRef.current[trackId];if(!node)return null;const found=(node.fxMods||[]).find(m=>m&&m.type===type);return found||null;};// Same for the soundfont (MIDI) module instances — lets the EQ PRO panel drive // BOTH the audio-clip chain and the SF chain in realtime. -window.__getTrackSfFxModule=(trackId,type)=>{const node=activeTrackNodesRef.current[trackId];if(!node)return null;const found=(node.sfMods||[]).find(m=>m&&m.type===type);return found||null;};const getOrCreateSubTrackNode=(track,subTrack,context)=>{if(!track||!subTrack)return null;const subKey=track.id+'_sub_'+subTrack.id;let node=activeTrackNodesRef.current[subKey];if(!node){const gainNode=context.createGain();const volDb=subTrack.volumeDb??0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);gainNode.gain.setValueAtTime(volLinear,context.currentTime);const parentNode=getOrCreateTrackNode(track,context);gainNode.connect(parentNode);node={gainNode,pannerNode:null};activeTrackNodesRef.current[subKey]=node;}return node.gainNode;};const playMidiPreviewNote=(pitch,velocity=0.8,durationMs=500)=>{if(!window.SonicSF)return;const context=getAudioContext();const tab=subTabs.find(s=>s.id===activeTabRef.current);const trackId=tab?tab.trackId:selectedTrackId;const track=activeTracks.find(t=>t.id===trackId);const destNode=getOrCreateTrackNode(track,context);const program=track?track.instrumentProgram:undefined;var prevCh=track?assignTrackMidiChannel(track,activeTracks):0;window.SonicSF.playNote(pitch,velocity,durationMs,context.currentTime,program,destNode,prevCh,track?track.synth_engine:undefined);};const startTrackPlayback=offsetTime=>{const context=getAudioContext();const allPlayTracks=activeTracksRef.current&&activeTracksRef.current.length?activeTracksRef.current:activeTracks;const hasSolo=allPlayTracks.some(t=>t.solo);allPlayTracks.forEach(track=>{const isPlayable=hasSolo?track.solo:!track.muted;if(!isPlayable)return;const gainNode=getOrCreateTrackNode(track,context);const pannerNode=activeTrackNodesRef.current[track.id].pannerNode;const clips=track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default',buffer:track.buffer,startTime:track.startTime||0,name:track.name,speed:track.speed||1.0}]:[];clips.forEach(clip=>{if(!clip.buffer)return;const source=context.createBufferSource();source.buffer=clip.buffer;source.playbackRate.value=clip.speed||1.0;source.connect(gainNode);const clipStart=clip.startTime||0;const clipDuration=clip.buffer.duration/(clip.speed||1.0);const clipEnd=clipStart+clipDuration;if(offsetTime{const node=activeTrackNodesRef.current[trackId];if(!node)return null;const found=(node.sfMods||[]).find(m=>m&&m.type===type);return found||null;};const getOrCreateSubTrackNode=(track,subTrack,context)=>{if(!track||!subTrack)return null;const subKey=track.id+'_sub_'+subTrack.id;let node=activeTrackNodesRef.current[subKey];if(!node){const gainNode=context.createGain();const volDb=subTrack.volumeDb??0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);gainNode.gain.setValueAtTime(volLinear,context.currentTime);const parentNode=getOrCreateTrackNode(track,context);gainNode.connect(parentNode);node={gainNode,pannerNode:null};activeTrackNodesRef.current[subKey]=node;}return node.gainNode;};const playMidiPreviewNote=(pitch,velocity=0.8,durationMs=500)=>{if(!window.SonicSF)return;const context=getAudioContext();const tab=subTabs.find(s=>s.id===activeTabRef.current);const trackId=tab?tab.trackId:selectedTrackId;const track=activeTracks.find(t=>t.id===trackId);const destNode=getOrCreateTrackNode(track,context);const program=track?track.instrumentProgram:undefined;var prevCh=track?assignTrackMidiChannel(track,activeTracks):0;window.SonicSF.playNote(pitch,velocity,durationMs,context.currentTime,program,destNode,prevCh,track?track.synth_engine:undefined);};const startTrackPlayback=offsetTime=>{const context=getAudioContext();const allPlayTracks=activeTracksRef.current&&activeTracksRef.current.length?activeTracksRef.current:activeTracks;// Capture items signature at schedule time — updatePlayhead so sánh với +// signature này để phát hiện items đổi vị trí giữa lúc play (re-schedule). +scheduledItemsSigRef.current=buildItemsSignature(allPlayTracks,sessionTabs);console.log('[Play] offset='+offsetTime+' tracks='+allPlayTracks.length+' masterBus='+!!masterBus+' dest='+(context.destination?'ok':'MISSING'));const hasSolo=allPlayTracks.some(t=>t.solo);allPlayTracks.forEach(track=>{const isPlayable=hasSolo?track.solo:!track.muted;if(!isPlayable)return;const gainNode=getOrCreateTrackNode(track,context);const pannerNode=activeTrackNodesRef.current[track.id].pannerNode;console.log('[Play] track',track.id,track.name,'node ok:',!!(gainNode&&pannerNode),'muted:',track.muted);const clips=track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default',buffer:track.buffer,startTime:track.startTime||0,name:track.name,speed:track.speed||1.0}]:[];clips.forEach(clip=>{if(!clip.buffer)return;const source=context.createBufferSource();source.buffer=clip.buffer;source.playbackRate.value=clip.speed||1.0;source.connect(gainNode);const clipStart=clip.startTime||0;const clipDuration=clip.buffer.duration/(clip.speed||1.0);const clipEnd=clipStart+clipDuration;if(offsetTime0)&&window.SonicSF){// Preview cache: capture the soundfont (pre track-FX) for fast offline export ensureMidiCapture(track,activeTrackNodesRef.current[track.id]);var trkCh=assignTrackMidiChannel(track,allPlayTracks);// Ensure instrument is loaded in FluidSynth if(track.synth_engine&&track.synth_engine.type==='soundfont'&&track.synth_engine.soundfont_id){window.SonicSF.selectInstrument(trkCh,track.synth_engine.soundfont_bank||0,track.synth_engine.soundfont_program||0,track.synth_engine.soundfont_id);}const bpmVal=parseInt(bpm)||120;const secondsPerBeat=60.0/bpmVal;midiItems.forEach(item=>{const itemEndSec=item.startTime+(item.duration||4);const notes=item.notes||[];notes.forEach(note=>{// note start/duration is in beats (for MIDI items) @@ -776,7 +825,9 @@ setSelectionCleared(false);localSelectionAnchorRef.current=time;setSelectionMode useEffect(()=>{const handleMouseMove=e=>{if(!localDragInProgressRef.current)return;const wrapper=timelineWrapperRef.current;if(!wrapper)return;const rect=wrapper.getBoundingClientRect();const scrollLeft=wrapper.scrollLeft;const mouseX=e.clientX-rect.left+scrollLeft;const time=Math.max(0,Math.min(maxDuration,mouseX/zoom));const anchor=localSelectionAnchorRef.current??localDragStartTimeRef.current;const selS=Math.min(anchor,time);const selE=Math.max(anchor,time);setLocalSelectionStart(selS);setLocalSelectionEnd(selE);setSelectionStart(selS);setSelectionEnd(selE);};const handleMouseUp=()=>{if(localDragInProgressRef.current){localDragInProgressRef.current=false;localDragTrackRef.current=null;localDragStartTimeRef.current=0;pushSelectionUndo();}};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom,maxDuration]);const draggedClipRef=useRef(null);draggedClipRef.current=draggedClip;const hoveredTrackIdRef=useRef(null);hoveredTrackIdRef.current=hoveredTrackId;const captureTrackSnapshotRef=useRef(null);captureTrackSnapshotRef.current=captureTrackSnapshot;const draggedSectionItemRef=useRef(null);draggedSectionItemRef.current=draggedSectionItem;const resizedSectionItemRef=useRef(null);resizedSectionItemRef.current=resizedSectionItem;const selectionUndoRef=useRef(null);const pushSelectionUndo=()=>{var cur=selectionRef.current;var before=selectionUndoRef.current;if(!before||before.start===cur.start&&before.end===cur.end&&before.mode===cur.mode)return;var entry={type:'SELECTION',scope:'global',label:'Selection',before:before,after:{start:cur.start,end:cur.end,mode:cur.mode},undo:e=>{setSelectionStart(e.before.start);setSelectionEnd(e.before.end);setSelectionMode(e.before.mode);showToast('Undo: Selection','info');},redo:e=>{setSelectionStart(e.after.start);setSelectionEnd(e.after.end);setSelectionMode(e.after.mode);showToast('Redo: Selection','info');}};if(window.UndoRedoEngine)window.UndoRedoEngine.execute(entry);selectionUndoRef.current=null;};const captureSelectionUndo=()=>{if(selectionUndoRef.current!==null)return;selectionUndoRef.current={start:selectionRef.current.start,end:selectionRef.current.end,mode:selectionMode};};let clipSeqCounter=0;const nextClipId=()=>`clip_${Date.now()}_${++clipSeqCounter}`;const handleClipDragStart=(trackId,clipId,clickOffset,isDuplicate=false)=>{const curTracks=activeTracksRef.current||activeTracks;const track=curTracks.find(t=>t.id===trackId);if(!track)return;const existingClips=track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default_'+track.id,buffer:track.buffer,startTime:track.startTime||0,name:track.name}]:[];const clip=existingClips.find(c=>c.id===clipId||clipId==='default'&&c.id==='default_'+track.id);if(!clip)return;const beforeSnap=captureTrackSnapshot(trackId);if(isDuplicate){const cloneId=nextClipId();const clone={...clip,id:cloneId,startTime:clip.startTime||0,name:clip.name+' (Copy)'};updateActiveTracks(prev=>prev.map(t=>{if(t.id===trackId){const newClips=[...existingClips,clone];return{...t,clips:newClips,buffer:newClips[0].buffer,startTime:newClips[0].startTime,name:newClips[0].name};}return t;}));setDraggedClip({trackId,clipId:cloneId,clickOffset,buffer:clip.buffer,name:clip.name+' (Copy)',beforeSnap,isDuplicate:false});return;}if(!track.clips||track.clips.length===0){updateActiveTracks(prev=>prev.map(t=>{if(t.id===trackId){return{...t,clips:existingClips};}return t;}));}setDraggedClip({trackId,clipId:clip.id,clickOffset,buffer:clip.buffer,name:clip.name,beforeSnap,isDuplicate:false});};const handleClipDragStartRef=useRef(null);handleClipDragStartRef.current=handleClipDragStart;const stretchedClipRef=useRef(null);stretchedClipRef.current=stretchedClip;const handleClipStretchStart=(trackId,clipId,clickTime)=>{const curTracks=activeTracksRef.current||activeTracks;const track=curTracks.find(t=>t.id===trackId);if(!track)return;const clips=track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default_'+track.id,buffer:track.buffer,startTime:track.startTime||0,name:track.name,speed:track.speed||1.0}]:[];const clip=clips.find(c=>c.id===clipId||clipId==='default'&&c.id==='default_'+trackId);if(!clip||!clip.buffer)return;const beforeSnap=captureTrackSnapshot(trackId);if(!track.clips||track.clips.length===0){updateActiveTracks(prev=>prev.map(t=>t.id===trackId?{...t,clips}:t));}setStretchedClip({trackId,clipId:clip.id==='default'?'default_'+trackId:clip.id,originalDuration:clip.buffer.duration,startTime:clip.startTime,originalSpeed:clip.speed||1.0,beforeSnap});};const handleSelectionEdgeDragStart=(e,trackId,side)=>{const startX=e.clientX;const initialLeft=Math.min(localSelectionStart,localSelectionEnd);const initialRight=Math.max(localSelectionStart,localSelectionEnd);const handleMouseMove=moveEvent=>{const deltaX=moveEvent.clientX-startX;const deltaSec=deltaX/zoom;if(side==='left'){const newLeft=Math.max(0,Math.min(initialRight-0.05,initialLeft+deltaSec));setLocalSelectionStart(newLeft);setLocalSelectionEnd(initialRight);}else{const newRight=Math.max(initialLeft+0.05,Math.min(maxDuration,initialRight+deltaSec));setLocalSelectionStart(initialLeft);setLocalSelectionEnd(newRight);}};const handleMouseUp=()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);};const handleTrackResizeMouseDown=(e,trackId)=>{e.preventDefault();e.stopPropagation();const startY=e.clientY;const track=tracks.find(t=>t.id===trackId);const startHeight=track?track.height||140:140;const handleMouseMove=moveEvent=>{const deltaY=moveEvent.clientY-startY;const newHeight=Math.max(110,Math.min(300,startHeight+deltaY));setTracks(prev=>prev.map(t=>t.id===trackId?{...t,height:newHeight}:t));};const handleMouseUp=()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);};const deleteTrack=trackId=>{const sessionTab=sessionTabs.find(s=>s.id===activeTab);const trackList=sessionTab?sessionTab.tracks:tracks;const track=trackList.find(t=>t.id===trackId);if(!track)return;const hasClips=track.clips&&track.clips.length>0||!!track.buffer;const hasMidi=track.midiItems&&track.midiItems.length>0;const hasSections=track.sections&&track.sections.length>0;const isTrackEmpty=!hasClips&&!hasMidi&&!hasSections;if(!isTrackEmpty){showToast('Không thể xoá track chứa dữ liệu. Vui lòng xoá hết các item trước khi xoá track.','warning');return;}if(sessionTab){updateActiveTracks(prev=>{const filtered=prev.filter(t=>t.id!==trackId);if(filtered.length>0)setSelectedTrackId(filtered[0].id);return filtered;});}else{const beforeSnap=captureTrackSnapshot(trackId);setTracks(prev=>{const filtered=prev.filter(t=>t.id!==trackId);if(filtered.length>0)setSelectedTrackId(filtered[0].id);return filtered;});}showToast('Đã xóa track.','info');};// Shared auto-scroll: when mouse near right edge, scroll container right const autoScrollTimeline=clientX=>{const wrapper=timelineWrapperRef.current;if(!wrapper)return;const wr=wrapper.getBoundingClientRect();const margin=200;if(clientX>wr.right-margin){const depth=(clientX-(wr.right-margin))/margin;const speed=Math.round(5+depth*depth*40);wrapper.scrollLeft+=speed;if(wrapper.scrollLeft>wrapper.scrollWidth-wrapper.clientWidth-100){const secPerBar=60.0/(parseInt(bpm)||120)*4;setScrollBufferExtra(prev=>prev+secPerBar*4);}}else if(clientX{const handleMouseMove=e=>{const drag=draggedClipRef.current;if(!drag)return;const wrapper=timelineWrapperRef.current;if(!wrapper)return;autoScrollTimeline(e.clientX);const rect=wrapper.getBoundingClientRect();const scrollLeft=wrapper.scrollLeft;const mouseX=e.clientX-rect.left+scrollLeft;const time=Math.max(0,mouseX/zoom-leadInMarginRef.current);const beatSec=60.0/(parseInt(bpmRef.current)||120);const rawStart=Math.max(0,time-drag.clickOffset);const secPerBar=beatSec*4;const marginBar=maxDurationRef.current-secPerBar;const clampedStart=Math.min(rawStart,marginBar);const newStart=snapTime(Math.max(0,clampedStart),snapValueRef.current,bpmRef.current);const itemPx=newStart*zoom;const keepMargin=80;if(itemPx>scrollLeft+rect.width-keepMargin){wrapper.scrollLeft=itemPx-rect.width+keepMargin;setCanvasRedrawCount(n=>n+1);}else if(itemPxn+1);}const targetTrackId=hoveredTrackIdRef.current||drag.trackId;updateActiveTracks(prev=>prev.map(t=>{// Clear the clip from its previous track if it moved to a new track if(t.id===drag.trackId&&drag.trackId!==targetTrackId){const updatedClips=(t.clips||[]).filter(c=>c.id!==drag.clipId);return{...t,clips:updatedClips,buffer:updatedClips.length>0?updatedClips[0].buffer:null,startTime:updatedClips.length>0?updatedClips[0].startTime:0,name:updatedClips.length>0?updatedClips[0].name:`Track ${t.id}`};}// Update/set clip on target track -if(t.id===targetTrackId){const existingClips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default_'+t.id,buffer:t.buffer,startTime:t.startTime||0,name:t.name}]:[];const hasClip=existingClips.some(c=>c.id===drag.clipId);let updatedClips;if(hasClip){updatedClips=existingClips.map(c=>c.id===drag.clipId?{...c,startTime:newStart}:c);}else{updatedClips=[...existingClips,{id:drag.clipId,buffer:drag.buffer,startTime:newStart,name:drag.name}];}return{...t,clips:updatedClips,buffer:updatedClips[0].buffer,startTime:updatedClips[0].startTime,name:updatedClips[0].name};}return t;}));if(drag.trackId!==targetTrackId){setDraggedClip(prev=>({...prev,trackId:targetTrackId}));}};const handleMouseUp=()=>{const drag=draggedClipRef.current;if(!drag)return;const afterSnap=captureTrackSnapshotRef.current(drag.trackId);pushAction('MOVE_CLIP',drag.trackId,drag.beforeSnap,afterSnap);setDraggedClip(null);showToast('Đã di chuyển clip.','success');};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom]);// Document-level mousemove/mouseup for clip stretching +if(t.id===targetTrackId){const existingClips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default_'+t.id,buffer:t.buffer,startTime:t.startTime||0,name:t.name}]:[];const hasClip=existingClips.some(c=>c.id===drag.clipId);let updatedClips;if(hasClip){updatedClips=existingClips.map(c=>c.id===drag.clipId?{...c,startTime:newStart}:c);}else{updatedClips=[...existingClips,{id:drag.clipId,buffer:drag.buffer,startTime:newStart,name:drag.name}];}return{...t,clips:updatedClips,buffer:updatedClips[0].buffer,startTime:updatedClips[0].startTime,name:updatedClips[0].name};}return t;}));if(drag.trackId!==targetTrackId){setDraggedClip(prev=>({...prev,trackId:targetTrackId}));}};const handleMouseUp=()=>{const drag=draggedClipRef.current;if(!drag)return;const afterSnap=captureTrackSnapshotRef.current(drag.trackId);pushAction('MOVE_CLIP',drag.trackId,drag.beforeSnap,afterSnap);setDraggedClip(null);showToast('Đã di chuyển clip.','success');// Realtime: clip vừa thả — re-schedule NGAY để phát theo vị trí mới +// (kéo clip về đúng playhead → phát ngay, không chờ debounce). +triggerRescheduleNow();};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom]);// Document-level mousemove/mouseup for clip stretching useEffect(()=>{const handleMouseMove=e=>{const stretch=stretchedClipRef.current;if(!stretch)return;autoScrollTimeline(e.clientX);const wrapper=timelineWrapperRef.current;if(!wrapper)return;const rect=wrapper.getBoundingClientRect();const scrollLeft=wrapper.scrollLeft;const mouseX=e.clientX-rect.left+scrollLeft;const time=mouseX/zoom;const secPerBar=60.0/(parseInt(bpmRef.current)||120)*4;const marginBar=maxDurationRef.current-secPerBar;const maxEnd=Math.min(time,marginBar);const newDuration=Math.max(0.1,maxEnd-stretch.startTime);const speedRatio=stretch.originalDuration/newDuration;updateActiveTracks(prev=>prev.map(t=>{if(t.id===stretch.trackId){const updatedClips=(t.clips||[]).map(c=>{if(c.id===stretch.clipId){return{...c,speed:speedRatio};}return c;});return{...t,clips:updatedClips,buffer:updatedClips[0]?.buffer,startTime:updatedClips[0]?.startTime||0,speed:updatedClips[0]?.speed||1.0};}return t;}));};const handleMouseUp=()=>{const stretch=stretchedClipRef.current;if(!stretch)return;const afterSnap=captureTrackSnapshotRef.current(stretch.trackId);pushAction('STRETCH_CLIP',stretch.trackId,stretch.beforeSnap,afterSnap);setStretchedClip(null);showToast('Đã giãn thời gian clip.','success');};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom]);// ── Deselect single item ── const handleDeselectItem=itemId=>{captureSelectionUndo();setSelectedItemIds(prev=>{var next=new Set(prev);next.delete(itemId);return next;});};// ── Add item to selectedItemIds ── const handleSaveSectionTabRef=useRef(handleSaveSectionTab);handleSaveSectionTabRef.current=handleSaveSectionTab;const handleAddToSelection=itemId=>{captureSelectionUndo();setSelectedItemIds(prev=>{var next=new Set(prev);next.add(itemId);return next;});};const handleSetPendingDrag=(trackId,itemType,itemId,clickOffset,e,preToggleSnapshot)=>{// Compute selectedIds AFTER the toggle that just happened: @@ -797,7 +848,8 @@ var dragBaseTrackId=drag.multiIds&&drag.multiIds[drag.itemId]?drag.multiIds[drag var baseIdx=allTrks.findIndex(function(tr){return tr.id===dragBaseTrackId;});if(baseIdx<0)baseIdx=0;var trgIdx=allTrks.findIndex(function(tr){return tr.id===targetTrackId;});if(trgIdx<0)trgIdx=allTrks.length-1;if(trgIdx<0)trgIdx=0;var crossOffset=trgIdx-baseIdx;// Special path for multi-select drag: handle items from multiple tracks if(drag.multiIds){var dragOrigStart=drag.multiIds[drag.itemId]?drag.multiIds[drag.itemId].start:0;var delta=newStart-dragOrigStart;// Extend activeTracks if target track index exceeds track count var outTracks=[];Object.keys(drag.multiIds).forEach(function(mid){var inf=drag.multiIds[mid];var oIdx=allTrks.findIndex(function(tr){return tr.id===inf.trackId;});if(oIdx<0)oIdx=baseIdx;var needIdx=oIdx+crossOffset;if(needIdx>=allTrks.length){for(var ai=allTrks.length;ai<=needIdx;ai++){var exists=outTracks.some(function(ot){return ot.id===(ai+1).toString();});if(!exists){var colors=['#0f766e','#1d4ed8','#701a75','#a21caf','#b45309'];outTracks.push({id:(ai+1).toString(),name:'Track '+(ai+1),buffer:null,startTime:0,volumeDb:0,pan:0,muted:false,solo:false,color:colors[ai%colors.length],clips:[],midiItems:[],sections:[],markers:[],isArmed:false,height:140});}}}});updateActiveTracks(function(prev){var trkIds=prev.map(function(tr){return tr.id;});var merged=outTracks.length>0?prev.concat(outTracks.filter(function(ot){return!trkIds.includes(ot.id);})):prev;var allIds=merged.map(function(tr){return tr.id;});return merged.map(function(t){var resS=(t.sections||[]).slice();var resM=(t.midiItems||[]).slice();var resC=(t.clips||[]).slice();Object.keys(drag.multiIds).forEach(function(mid){var inf=drag.multiIds[mid];var newVal=inf.start+delta;var oIdx=allIds.indexOf(inf.trackId);if(oIdx<0)oIdx=baseIdx;var needIdx=Math.max(0,oIdx+crossOffset);var targetTid=needIdx{const drag=draggedSectionItemRef.current;if(!drag)return;var changed=false;var trackId=drag.trackId;var beforeSnap=drag.beforeSnap;var origs=drag.originalPositions||{};updateActiveTracks(function(prev){return prev.map(function(t){var allItemIds=Object.keys(origs);var updatedSections=(t.sections||[]).slice();var updatedMidi=(t.midiItems||[]).slice();var hasChange=false;for(var i=0;i0.001){hasChange=true;changed=true;}}}}if(!hasChange)return t;return{...t,sections:updatedSections,midiItems:updatedMidi};});});if(changed||drag.beforeSnap){var afterSnap=captureAllTracksSnapshot();pushAction('MOVE_ITEM','ALL_TRACKS',drag.beforeSnap||beforeSnap,afterSnap);}if(drag.itemType==='midiItem'){let finalTrackId=null;const curTrks=activeTracksRef.current||activeTracks;for(const t of curTrks){if((t.midiItems||[]).some(m=>m.id===drag.itemId)){finalTrackId=t.id;break;}}if(finalTrackId){const targetTrack=curTrks.find(t=>t.id===finalTrackId);if(targetTrack){setSubTabs(prev=>prev.map(st=>{if(st.target_id===drag.itemId){return{...st,trackId:finalTrackId,instrumentProgram:targetTrack.instrumentProgram!==undefined?targetTrack.instrumentProgram:st.instrumentProgram,instrumentName:targetTrack.instrumentName||st.instrumentName};}return st;}));}}}setDraggedSectionItem(null);showToast('Đã di chuyển '+(drag.itemType==='section'?'section':'MIDI item')+'.','success');};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom,activeTab,sessionTabs]);// ── Pending drag: click selects; mousemove > 5px threshold starts the real drag ── +updateActiveTracks(function(prev){let movedItem=null;for(let track of prev){const sec=(track.sections||[]).find(function(it){return it.id===drag.itemId;});const mid=(track.midiItems||[]).find(function(it){return it.id===drag.itemId;});if(sec||mid){movedItem=sec||mid;break;}}return prev.map(function(tr){var its=drag.itemType==='section'?(tr.sections||[]).filter(function(it){return it.id!==drag.itemId;}):(tr.midiItems||[]).filter(function(it){return it.id!==drag.itemId;});if(tr.id===targetTrackId&&movedItem){its.push(drag.itemType==='section'?{...movedItem,start:newStart}:{...movedItem,startTime:newStart});}return drag.itemType==='section'?{...tr,sections:its}:{...tr,midiItems:its};});});}};const handleMouseUp=()=>{const drag=draggedSectionItemRef.current;if(!drag)return;var changed=false;var trackId=drag.trackId;var beforeSnap=drag.beforeSnap;var origs=drag.originalPositions||{};updateActiveTracks(function(prev){return prev.map(function(t){var allItemIds=Object.keys(origs);var updatedSections=(t.sections||[]).slice();var updatedMidi=(t.midiItems||[]).slice();var hasChange=false;for(var i=0;i0.001){hasChange=true;changed=true;}}}}if(!hasChange)return t;return{...t,sections:updatedSections,midiItems:updatedMidi};});});if(changed||drag.beforeSnap){var afterSnap=captureAllTracksSnapshot();pushAction('MOVE_ITEM','ALL_TRACKS',drag.beforeSnap||beforeSnap,afterSnap);}// Realtime: items vừa thả — re-schedule NGAY theo vị trí mới. +triggerRescheduleNow();if(drag.itemType==='midiItem'){let finalTrackId=null;const curTrks=activeTracksRef.current||activeTracks;for(const t of curTrks){if((t.midiItems||[]).some(m=>m.id===drag.itemId)){finalTrackId=t.id;break;}}if(finalTrackId){const targetTrack=curTrks.find(t=>t.id===finalTrackId);if(targetTrack){setSubTabs(prev=>prev.map(st=>{if(st.target_id===drag.itemId){return{...st,trackId:finalTrackId,instrumentProgram:targetTrack.instrumentProgram!==undefined?targetTrack.instrumentProgram:st.instrumentProgram,instrumentName:targetTrack.instrumentName||st.instrumentName};}return st;}));}}}setDraggedSectionItem(null);showToast('Đã di chuyển '+(drag.itemType==='section'?'section':'MIDI item')+'.','success');};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom,activeTab,sessionTabs]);// ── Pending drag: click selects; mousemove > 5px threshold starts the real drag ── useEffect(()=>{const handleMouseMove=e=>{var pd=pendingDragRef.current;if(!pd)return;var dx=e.clientX-pd.startX;if(Math.abs(dx)>5){var pdSnap=pendingDragRef.current;pendingDragRef.current=null;if(pdSnap.itemType==='clip'&&!pdSnap.duplicate&&(!pdSnap.selectedIds||pdSnap.selectedIds.size<=1)){// Single clip move → clip drag machinery if(handleClipDragStartRef.current)handleClipDragStartRef.current(pdSnap.trackId,pdSnap.itemId,pdSnap.clickOffset,false);}else if(handleSectionItemDragStartRef.current){handleSectionItemDragStartRef.current(pdSnap.trackId,pdSnap.itemType,pdSnap.itemId,pdSnap.clickOffset,!!pdSnap.duplicate,pdSnap.selectedIds);}}};document.addEventListener('mousemove',handleMouseMove);var handleMouseUp=function(){pendingDragRef.current=null;};document.addEventListener('mouseup',handleMouseUp);return function(){document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);pendingDragRef.current=null;};},[]);// ── Document-level mousemove/mouseup for Section/MIDI item resize ── useEffect(()=>{const handleMouseMove=e=>{const resize=resizedSectionItemRef.current;if(!resize)return;const wrapper=timelineWrapperRef.current;if(!wrapper)return;autoScrollTimeline(e.clientX);const rect=wrapper.getBoundingClientRect();const scrollLeft=wrapper.scrollLeft;const mouseX=e.clientX-rect.left+scrollLeft;const time=Math.max(0,mouseX/zoom-leadInMarginRef.current);updateActiveTracks(prev=>prev.map(t=>{if(t.id!==resize.trackId)return t;const items=resize.itemType==='section'?[...(t.sections||[])]:[...(t.midiItems||[])];const idx=items.findIndex(it=>it.id===resize.itemId);if(idx===-1)return t;const item=items[idx];if(resize.side==='left'){const beatSec=60.0/(parseInt(bpm)||120);const newStart=Math.max(0,Math.min(time,resize.originalStart+resize.originalDuration-0.1));const end=resize.originalStart+resize.originalDuration;const newDuration=end-newStart;if(newDuration<0.1)return t;items[idx]=resize.itemType==='section'?{...item,start:newStart,duration:newDuration}:{...item,startTime:newStart,duration:newDuration};}else{const secondsPerBar=60.0/(parseInt(bpm)||120)*4;const marginBar=maxDurationRef.current-secondsPerBar;const clampedTime=Math.min(time,marginBar);const snappedDuration=snapValueRef.current!=='free'?snapTime(clampedTime-resize.originalStart,snapValueRef.current,bpm):clampedTime-resize.originalStart;const newDuration=Math.max(0.1,snappedDuration);items[idx]={...item,duration:newDuration};}return resize.itemType==='section'?{...t,sections:items}:{...t,midiItems:items};}));setCanvasRedrawCount(n=>n+1);const edgePx=(resize.side==='left'?Math.max(0,time):time)*zoom;const keepMargin=80;if(edgePx>scrollLeft+rect.width-keepMargin){wrapper.scrollLeft=edgePx-rect.width+keepMargin;setCanvasRedrawCount(n=>n+1);}else if(edgePxn+1);}};const handleMouseUp=()=>{const resize=resizedSectionItemRef.current;if(!resize)return;setResizedSectionItem(null);};document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);return()=>{document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);};},[zoom,activeTab,sessionTabs]);// ── Sweep Select mousemove/mouseup ── @@ -852,14 +904,18 @@ clientSideExport(exportTracks);}};// ── Realtime Bounce Export (đầy đủ // trong OfflineAudioContext. Vì vậy bounce REALTIME: chạy lại toàn bộ project // (audio + MIDI + sections qua FX racks + mastering chain, đúng như nghe) và // capture PCM từ master bus, sau đó encode WAV trung thực. -const triggerBounceExport=async()=>{if(!masterBus){showToast("Hãy phát thử một lần để khởi tạo audio engine trước.","warning");return;}if(isExporting)return;setIsExporting(true);const audioCtx=getAudioContext();try{const allTracks=activeTracksRef.current&&activeTracksRef.current.length?activeTracksRef.current:tracks;let durationLimit=1.0;allTracks.forEach(t=>{const clips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{startTime:t.startTime||0,buffer:t.buffer,speed:t.speed||1.0}]:[];clips.forEach(c=>{if(c.buffer)durationLimit=Math.max(durationLimit,(c.startTime||0)+c.buffer.duration/(c.speed||1.0));});(t.midiItems||[]).forEach(m=>{if(m.endTime)durationLimit=Math.max(durationLimit,m.endTime);else if(m.startTime)durationLimit=Math.max(durationLimit,m.startTime+(m.duration||8));});});(sessionTabs||[]).forEach(s=>(s.tracks||[]).forEach(t=>{(t.midiItems||[]).forEach(m=>{if(m.endTime)durationLimit=Math.max(durationLimit,m.endTime);else if(m.startTime)durationLimit=Math.max(durationLimit,m.startTime+(m.duration||8));});}));durationLimit+=1.2;// FX/mastering tail +const triggerBounceExport=async()=>{if(!masterBus){showToast("Hãy phát thử một lần để khởi tạo audio engine trước.","warning");return;}if(isExporting)return;setIsExporting(true);const audioCtx=getAudioContext();try{const allTracks=activeTracksRef.current&&activeTracksRef.current.length?activeTracksRef.current:tracks;// MAIN SESSION end time (items trên track main — sections tính theo bounds, +// không kéo dài theo nội dung SECTION-TAB) — bounce không cắt sớm/cắt thiếu. +let durationLimit=Math.max(1.0,computeMainSessionEndTime(allTracks));durationLimit+=1.2;// FX/mastering tail const captureRate=audioCtx.sampleRate||44100;const bitDepth=parseInt(exportSettings.bitDepth)||16;const outChannels=exportSettings.channels==='mono'?1:2;showToast(`Bounce realtime ~${Math.round(durationLimit)}s — giữ nguyên âm thanh đang phát...`,"info");// Capture tap on the master bus output (post-FX + post-mastering) const chunks=[];const script=audioCtx.createScriptProcessor(4096,2,2);script.onaudioprocess=e=>{const L=e.inputBuffer.getChannelData(0);const R=e.inputBuffer.getChannelData(1);const n=L.length;const chunk=new Float32Array(n*2);for(let i=0;isetTimeout(res,Math.ceil(durationLimit*1000)+900));stopAllPlayback();try{if(window.SonicSF&&window.SonicSF.stopAllNotes)window.SonicSF.stopAllNotes();}catch(e){}try{script.disconnect();}catch(e){}try{masterBus.output.disconnect(script);}catch(e){}// Encode interleaved PCM chunks → WAV const totalFrames=chunks.reduce((a,c)=>a+c.length/2,0);const stereoBuf=new Float32Array(totalFrames*2);let off=0;chunks.forEach(c=>{stereoBuf.set(c,off);off+=c.length;});const frames=totalFrames;const bytesPerSample=bitDepth/8;const headerSize=44;const fileSizeBytes=headerSize+frames*bytesPerSample*outChannels;const view=new DataView(new ArrayBuffer(fileSizeBytes));const writeString=(o,s)=>{for(let i=0;i{const finalName=name||projectName||'Dự án mới';const localId=currentProjectId&¤tProjectId.startsWith('local_')?currentProjectId:'local_'+Date.now();const projectSchemaObj=serializeProjectToSchema(localId,finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings);const dataStr=JSON.stringify(projectSchemaObj);localStorage.setItem('sonic_local_project_data',dataStr);localStorage.setItem('sonic_project_id',localId);localStorage.setItem('sonic_project_name',finalName);setCurrentProjectId(localId);setProjectName(finalName);window.SonicStorage.exportProjectToSFS(projectSchemaObj);showToast(`Đã lưu dự án local "${finalName}" thành công!`,"success");};const handleSaveCloudProject=async(name,existingProjectId)=>{const finalName=name||projectName||'Dự án mới';var useProjectId=existingProjectId||currentProjectId;const projectSchemaObj=serializeProjectToSchema(useProjectId||'project_'+Date.now(),finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings);// Diagnostic: count source vs serialized items -var srcMidi=0,srcClip=0,srcSec=0;tracks.forEach(function(t){srcMidi+=(t.midiItems||[]).length;srcClip+=(t.clips||[]).length;srcSec+=(t.sections||[]).length;});var serItems=0;if(projectSchemaObj.main_session&&projectSchemaObj.main_session.tracks){projectSchemaObj.main_session.tracks.forEach(function(st){if(st.items)serItems+=st.items.length;});}Object.keys(projectSchemaObj.section_store||{}).forEach(function(sk){(projectSchemaObj.section_store[sk].tracks||[]).forEach(function(st){if(st.items)serItems+=st.items.length;});});const dataJson=JSON.stringify(projectSchemaObj);try{if(existingProjectId){await window.SonicAPI.updateCloudProject(existingProjectId,finalName,dataJson);setProjectName(finalName);setCurrentProjectId(existingProjectId);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',existingProjectId);showToast('Đã ghi đè Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').','success');}else if(currentProjectId&&!currentProjectId.startsWith('local_')){await window.SonicAPI.updateCloudProject(currentProjectId,finalName,dataJson);setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',currentProjectId);showToast('Đã lưu Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+' id='+currentProjectId+').',serItems===0?'warning':'success');}else{const res=await window.SonicAPI.saveCloudProject(finalName,dataJson);const newProjId=res.project_id;setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);if(newProjId){setCurrentProjectId(newProjId);localStorage.setItem('sonic_project_id',newProjId);}showToast('Đã lưu Cloud mới "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').',serItems===0?'warning':'success');}}catch(err){showToast(err.message||"Lỗi lưu dự án lên Cloud","error");}};const handleSaveProject=async()=>{if(!currentProjectId){setSaveProjectModalOpen(true);return;}if(currentProjectId.startsWith('local_')){handleSaveLocalProject(projectName);}else{if(!currentUser){showToast('Cần đăng nhập để lưu Cloud. currentUser='+(currentUser?'OK':'NULL')+' token='+(localStorage.getItem('sonic_token')?'exists':'missing'),'warning');return;}handleSaveCloudProject(projectName);}};const handleSaveProjectRef=useRef(handleSaveProject);handleSaveProjectRef.current=handleSaveProject;const handleSaveAsCloud=async newName=>{const projectSchemaObj=serializeProjectToSchema('project_'+Date.now(),newName,bpm,tracks,subTabs,sessionTabs,masteringSettings);const dataJson=JSON.stringify(projectSchemaObj);try{const res=await window.SonicAPI.saveCloudProject(newName,dataJson);const newProjId=res.project_id;setProjectName(newName);localStorage.setItem('sonic_project_name',newName);if(newProjId){setCurrentProjectId(newProjId);localStorage.setItem('sonic_project_id',newProjId);}showToast(`Đã lưu dự án dưới tên mới "${newName}" lên server!`,"success");}catch(err){showToast(err.message||"Lỗi Save As lên server","error");}};const handleSaveCloud=handleSaveProject;const handleExportSFS=(customName=null)=>{const finalName=customName||projectName||'Dự án SonicForge';const projectSchemaObj=serializeProjectToSchema(currentProjectId||'proj_'+Date.now(),finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings);window.SonicStorage.exportProjectToSFS(projectSchemaObj);showToast("Đã xuất dự án (.sfs) thành công!","success");};const handleImportSFS=()=>{const input=document.createElement('input');input.type='file';input.accept='.sfs,application/json';input.onchange=async e=>{if(!e.target.files[0])return;try{const proj=await window.SonicStorage.importProjectFromSFSFile(e.target.files[0]);let restored=[];let restoredBpm=bpm;let restoredSessionTabs=[];let restoredSubTabs=[];if(proj.main_session){const result=deserializeProjectFromSchema(proj);restored=result.tracks;restoredBpm=result.bpm;restoredSessionTabs=result.sessionTabs;restoredSubTabs=result.subTabs;if(result.masteringSettings)setMasteringSettings(result.masteringSettings);}else{restored=(proj.tracks||[]).map(t=>({...t,buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null}));}if(restored.length>0){setTracks(restored);loadAudioBuffersForTracks(restored);setBpm(restoredBpm.toString());setProjectName(proj.name||'Dự án mới');setCurrentProjectId(null);if(restoredSessionTabs.length>0)setSessionTabs(restoredSessionTabs);if(restoredSubTabs.length>0)setSubTabs(restoredSubTabs);localStorage.setItem('sonic_project_name',proj.name||'Dự án mới');localStorage.removeItem('sonic_project_id');showToast(`Đã nạp dự án "${proj.name||'Dự án mới'}" từ tệp .sfs thành công!`,"success");}}catch(err){showToast(err.message||"Lỗi mở tệp .sfs","error");}};input.click();};const clientSideExport=async activeTracks=>{setIsExporting(true);showToast("Đang trộn âm thanh đa kênh (Offline Mixdown)...","info");try{const targetRate=parseInt(exportSettings.sampleRate);const bitDepth=parseInt(exportSettings.bitDepth);const durationLimit=Math.max(...activeTracks.map(t=>{const clips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default',buffer:t.buffer,startTime:t.startTime||0,name:t.name,speed:t.speed||1.0}]:[];if(clips.length===0)return 0;return Math.max(...clips.map(c=>(c.startTime||0)+c.buffer.duration/(c.speed||1.0)));}));// Include preview-captured MIDI cache length (soundfont tracks have no clips) +var srcMidi=0,srcClip=0,srcSec=0;tracks.forEach(function(t){srcMidi+=(t.midiItems||[]).length;srcClip+=(t.clips||[]).length;srcSec+=(t.sections||[]).length;});var serItems=0;if(projectSchemaObj.main_session&&projectSchemaObj.main_session.tracks){projectSchemaObj.main_session.tracks.forEach(function(st){if(st.items)serItems+=st.items.length;});}Object.keys(projectSchemaObj.section_store||{}).forEach(function(sk){(projectSchemaObj.section_store[sk].tracks||[]).forEach(function(st){if(st.items)serItems+=st.items.length;});});const dataJson=JSON.stringify(projectSchemaObj);try{if(existingProjectId){await window.SonicAPI.updateCloudProject(existingProjectId,finalName,dataJson);setProjectName(finalName);setCurrentProjectId(existingProjectId);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',existingProjectId);showToast('Đã ghi đè Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').','success');}else if(currentProjectId&&!currentProjectId.startsWith('local_')){await window.SonicAPI.updateCloudProject(currentProjectId,finalName,dataJson);setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',currentProjectId);showToast('Đã lưu Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+' id='+currentProjectId+').',serItems===0?'warning':'success');}else{const res=await window.SonicAPI.saveCloudProject(finalName,dataJson);const newProjId=res.project_id;setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);if(newProjId){setCurrentProjectId(newProjId);localStorage.setItem('sonic_project_id',newProjId);}showToast('Đã lưu Cloud mới "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').',serItems===0?'warning':'success');}}catch(err){showToast(err.message||"Lỗi lưu dự án lên Cloud","error");}};const handleSaveProject=async()=>{if(!currentProjectId){setSaveProjectModalOpen(true);return;}if(currentProjectId.startsWith('local_')){handleSaveLocalProject(projectName);}else{if(!currentUser){showToast('Cần đăng nhập để lưu Cloud. currentUser='+(currentUser?'OK':'NULL')+' token='+(localStorage.getItem('sonic_token')?'exists':'missing'),'warning');return;}handleSaveCloudProject(projectName);}};const handleSaveProjectRef=useRef(handleSaveProject);handleSaveProjectRef.current=handleSaveProject;const handleSaveAsCloud=async newName=>{const projectSchemaObj=serializeProjectToSchema('project_'+Date.now(),newName,bpm,tracks,subTabs,sessionTabs,masteringSettings);const dataJson=JSON.stringify(projectSchemaObj);try{const res=await window.SonicAPI.saveCloudProject(newName,dataJson);const newProjId=res.project_id;setProjectName(newName);localStorage.setItem('sonic_project_name',newName);if(newProjId){setCurrentProjectId(newProjId);localStorage.setItem('sonic_project_id',newProjId);}showToast(`Đã lưu dự án dưới tên mới "${newName}" lên server!`,"success");}catch(err){showToast(err.message||"Lỗi Save As lên server","error");}};const handleSaveCloud=handleSaveProject;const handleExportSFS=(customName=null)=>{const finalName=customName||projectName||'Dự án SonicForge';const projectSchemaObj=serializeProjectToSchema(currentProjectId||'proj_'+Date.now(),finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings);window.SonicStorage.exportProjectToSFS(projectSchemaObj);showToast("Đã xuất dự án (.sfs) thành công!","success");};const handleImportSFS=()=>{const input=document.createElement('input');input.type='file';input.accept='.sfs,application/json';input.onchange=async e=>{if(!e.target.files[0])return;try{const proj=await window.SonicStorage.importProjectFromSFSFile(e.target.files[0]);let restored=[];let restoredBpm=bpm;let restoredSessionTabs=[];let restoredSubTabs=[];if(proj.main_session){const result=deserializeProjectFromSchema(proj);restored=result.tracks;restoredBpm=result.bpm;restoredSessionTabs=result.sessionTabs;restoredSubTabs=result.subTabs;if(result.masteringSettings)setMasteringSettings(result.masteringSettings);}else{restored=(proj.tracks||[]).map(t=>({...t,buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null}));}if(restored.length>0){setTracks(restored);loadAudioBuffersForTracks(restored);setBpm(restoredBpm.toString());setProjectName(proj.name||'Dự án mới');setCurrentProjectId(null);if(restoredSessionTabs.length>0)setSessionTabs(restoredSessionTabs);if(restoredSubTabs.length>0)setSubTabs(restoredSubTabs);localStorage.setItem('sonic_project_name',proj.name||'Dự án mới');localStorage.removeItem('sonic_project_id');showToast(`Đã nạp dự án "${proj.name||'Dự án mới'}" từ tệp .sfs thành công!`,"success");}}catch(err){showToast(err.message||"Lỗi mở tệp .sfs","error");}};input.click();};const clientSideExport=async activeTracks=>{setIsExporting(true);showToast("Đang trộn âm thanh đa kênh (Offline Mixdown)...","info");try{const targetRate=parseInt(exportSettings.sampleRate);const bitDepth=parseInt(exportSettings.bitDepth);// MAIN SESSION end time (items trên track main — sections theo bounds) — +// offline render không cắt sớm; MIDI cache (preview) giữ max với cache. +let durationLimit=Math.max(0.1,computeMainSessionEndTime(activeTracks));// Include preview-captured MIDI cache length (soundfont tracks have no clips) Object.keys(midiCacheRef.current).forEach(id=>{const c=midiCacheRef.current[id];if(c&&c.duration>durationLimit)durationLimit=c.duration;});const outChannels=exportSettings.channels==='mono'?1:2;const offlineCtx=new OfflineAudioContext(outChannels,Math.ceil(targetRate*Math.max(0.1,durationLimit)),targetRate);// Build the FULL graph (per-track FX Rack chains + mastering chain — the // same DSP as playback), so the exported file matches what you hear. const savedMasterBus=masterBus;const offlineNodes={};try{window.currentMasteringSettings=masteringSettings||window.currentMasteringSettings;try{if(typeof stopAllPlayback==='function')stopAllPlayback();}catch(e){}masterBus=null;initMasterBus(offlineCtx);try{toggleMasteringOnMaster(!!(masteringSettings&&masteringSettings.masterConnected),!!(masteringSettings&&masteringSettings.isBypassed));}catch(e){console.warn('offline mastering toggle error:',e);}activeTracks.forEach(t=>{const node=buildOfflineTrackNode(t,offlineCtx,offlineNodes);if(!node)return;const clips=t.clips&&t.clips.length>0?t.clips:t.buffer?[{id:'default',buffer:t.buffer,startTime:t.startTime||0,name:t.name,speed:t.speed||1.0}]:[];clips.forEach(clip=>{if(!clip.buffer)return;const source=offlineCtx.createBufferSource();source.buffer=clip.buffer;source.playbackRate.value=clip.speed||1.0;source.connect(node.gainNode);const clipStart=clip.startTime||0;source.start(clipStart);});// MIDI preview cache → schedule through the SF chain (track FX + mastering) @@ -912,11 +968,15 @@ const activeSub=activeTab&&subTabs.find(s=>s.id===activeTab&&['PIANO_ROLL','AUDI // region, KEEP it — only auto-derive (0 → content end) when no // selection exists yet. Previously this always overwrote the // selection with 0 → (contentEnd + 2 bars). -const hasSel=selectionMode==='local'&&selLeft!==null&&selRight!==null&&selRight>selLeft||selectionStart!==null&&selectionEnd!==null;if(!hasSel){const bpmVal=parseInt(bpm)||120;const secPerBar=60.0/bpmVal*4;let maxEnd=0;activeTracks.forEach(t=>{(t.clips||[]).forEach(c=>{const end=(c.startTime||0)+(c.duration||0);if(end>maxEnd)maxEnd=end;});(t.items||[]).forEach(it=>{const end=(it.start||0)+(it.duration||4);if(end>maxEnd)maxEnd=end;});});if(maxEnd>0){const loopEnd=maxEnd+secPerBar*2;setSelectionStart(0);setSelectionEnd(loopEnd);}}}},className:`w-7 h-7 flex items-center justify-center rounded border transition ${isLoopingSelection?'bg-amber-600 text-black border-amber-500 hover:bg-amber-500':'bg-zinc-800 text-zinc-200 border-zinc-700 hover:bg-zinc-700'}`,title:isLoopingSelection?selLeft!==null&&selRight!==null?"Loop vùng chọn":"Loop timeline":"Bật loop"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"repeat",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500 font-bold uppercase ml-2"},"Snap"),/*#__PURE__*/React.createElement("select",{value:snapValue,onChange:e=>onSnapChangeue(e.target.value),className:"bg-black text-white text-[14px] px-1.5 py-0.5 rounded border border-zinc-700 focus:outline-none focus:border-cyan-500 font-mono cursor-pointer"},/*#__PURE__*/React.createElement("option",{value:"free"},"Free"),/*#__PURE__*/React.createElement("option",{value:"1"},"1"),/*#__PURE__*/React.createElement("option",{value:"1/2"},"1/2"),/*#__PURE__*/React.createElement("option",{value:"1/4"},"1/4"),/*#__PURE__*/React.createElement("option",{value:"1/8"},"1/8"),/*#__PURE__*/React.createElement("option",{value:"1/16"},"1/16"),/*#__PURE__*/React.createElement("option",{value:"1/32"},"1/32"),/*#__PURE__*/React.createElement("option",{value:"4"},"4")),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-6 bg-zinc-800 mx-1.5"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500 font-bold"},"Bars:"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:beginBar,onChange:e=>{const b=parseInt(e.target.value)||0;setBeginBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;clearLocalSelection();setSelectionMode('global');setSelectionStart(t);setSelectionEnd(t+beatDuration*4);},className:"w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"-"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:endBar,onChange:e=>{const b=parseInt(e.target.value)||0;setEndBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;setSelectionEnd(t+beatDuration*4);setNumberBar(b-beginBar+1);},className:"w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"#"),/*#__PURE__*/React.createElement("input",{type:"number",min:"1",value:numberBar,readOnly:true,className:"w-14 bg-black text-zinc-400 text-[14px] px-1 py-0.5 rounded border border-zinc-800 text-center font-mono"}),/*#__PURE__*/React.createElement("button",{onClick:()=>setSelectionFollowsTempo(prev=>!prev),className:`px-1.5 py-0.5 text-[14px] rounded border font-bold ${selectionFollowsTempo?'bg-amber-700 text-white border-amber-500':'bg-zinc-800 text-zinc-400 border-zinc-700'}`,title:selectionFollowsTempo?"Selection theo tempo (đổi BPM → selection thay đổi)":"Selection theo thời gian (cố định)"},selectionFollowsTempo?"♪T":"⏱T"),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-6 bg-zinc-800 mx-1.5"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"Start:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selLeft!==null?formatTime(selLeft):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));clearLocalSelection();setSelectionMode('global');setSelectionStart(secs);}},className:"w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"End:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selRight!==null?formatTime(selRight):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));setSelectionEnd(secs);}},className:"w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"Len:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selLeft!==null&&selRight!==null?formatTime(Math.abs(selRight-selLeft)):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3&&selLeft!==null){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));setSelectionEnd(selLeft+secs);}},className:"w-20 bg-black text-amber-300 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("div",{className:"flex-1"}),/*#__PURE__*/React.createElement("div",{className:"text-xs font-bold font-mono text-zinc-100"},formatTime(currentTime))),(()=>{const dockPanels={top:[],right:[],bottom:[],left:[]};const addPanel=(id,pos,visible)=>{if(visible)dockPanels[pos].push(id);};// Export panel giờ là MODAL FLOAT giữa màn hình (không nằm trong dock rows). +const hasSel=selectionMode==='local'&&selLeft!==null&&selRight!==null&&selRight>selLeft||selectionStart!==null&&selectionEnd!==null;if(!hasSel){// Auto-derive loop region = ĐÚNG endtime của session (items trên +// track main: clips theo buffer.duration/speed, midiItems, section +// bounds) — KHÔNG cộng thêm bars buffer (loop không được dài hơn +// duration hiện có). +const maxEnd=computeMainSessionEndTime(activeTracks);if(maxEnd>0){setSelectionStart(0);setSelectionEnd(maxEnd);}}}},className:`w-7 h-7 flex items-center justify-center rounded border transition ${isLoopingSelection?'bg-amber-600 text-black border-amber-500 hover:bg-amber-500':'bg-zinc-800 text-zinc-200 border-zinc-700 hover:bg-zinc-700'}`,title:isLoopingSelection?selLeft!==null&&selRight!==null?"Loop vùng chọn":"Loop timeline":"Bật loop"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"repeat",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500 font-bold uppercase ml-2"},"Snap"),/*#__PURE__*/React.createElement("select",{value:snapValue,onChange:e=>onSnapChangeue(e.target.value),className:"bg-black text-white text-[14px] px-1.5 py-0.5 rounded border border-zinc-700 focus:outline-none focus:border-cyan-500 font-mono cursor-pointer"},/*#__PURE__*/React.createElement("option",{value:"free"},"Free"),/*#__PURE__*/React.createElement("option",{value:"1"},"1"),/*#__PURE__*/React.createElement("option",{value:"1/2"},"1/2"),/*#__PURE__*/React.createElement("option",{value:"1/4"},"1/4"),/*#__PURE__*/React.createElement("option",{value:"1/8"},"1/8"),/*#__PURE__*/React.createElement("option",{value:"1/16"},"1/16"),/*#__PURE__*/React.createElement("option",{value:"1/32"},"1/32"),/*#__PURE__*/React.createElement("option",{value:"4"},"4")),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-6 bg-zinc-800 mx-1.5"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500 font-bold"},"Bars:"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:beginBar,onChange:e=>{const b=parseInt(e.target.value)||0;setBeginBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;clearLocalSelection();setSelectionMode('global');setSelectionStart(t);setSelectionEnd(t+beatDuration*4);},className:"w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"-"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:endBar,onChange:e=>{const b=parseInt(e.target.value)||0;setEndBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;setSelectionEnd(t+beatDuration*4);setNumberBar(b-beginBar+1);},className:"w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"#"),/*#__PURE__*/React.createElement("input",{type:"number",min:"1",value:numberBar,readOnly:true,className:"w-14 bg-black text-zinc-400 text-[14px] px-1 py-0.5 rounded border border-zinc-800 text-center font-mono"}),/*#__PURE__*/React.createElement("button",{onClick:()=>setSelectionFollowsTempo(prev=>!prev),className:`px-1.5 py-0.5 text-[14px] rounded border font-bold ${selectionFollowsTempo?'bg-amber-700 text-white border-amber-500':'bg-zinc-800 text-zinc-400 border-zinc-700'}`,title:selectionFollowsTempo?"Selection theo tempo (đổi BPM → selection thay đổi)":"Selection theo thời gian (cố định)"},selectionFollowsTempo?"♪T":"⏱T"),/*#__PURE__*/React.createElement("div",{className:"w-[1px] h-6 bg-zinc-800 mx-1.5"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"Start:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selLeft!==null?formatTime(selLeft):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));clearLocalSelection();setSelectionMode('global');setSelectionStart(secs);}},className:"w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"End:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selRight!==null?formatTime(selRight):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));setSelectionEnd(secs);}},className:"w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] text-zinc-500"},"Len:"),/*#__PURE__*/React.createElement("input",{type:"text",value:selLeft!==null&&selRight!==null?formatTime(Math.abs(selRight-selLeft)):'',onChange:e=>{const parts=e.target.value.split(/[:.]/);if(parts.length===3&&selLeft!==null){const secs=parseInt(parts[0])*60+parseInt(parts[1])+parseFloat('0.'+(parts[2]||'0'));setSelectionEnd(selLeft+secs);}},className:"w-20 bg-black text-amber-300 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"}),/*#__PURE__*/React.createElement("div",{className:"flex-1"}),/*#__PURE__*/React.createElement("div",{className:"text-xs font-bold font-mono text-zinc-100"},formatTime(currentTime))),(()=>{const dockPanels={top:[],right:[],bottom:[],left:[]};const addPanel=(id,pos,visible)=>{if(visible)dockPanels[pos].push(id);};// Export panel giờ là MODAL FLOAT giữa màn hình (không nằm trong dock rows). // Selection / FX Rack / MIDI Events panels đã REMOVE khỏi dock (theo yêu cầu // user — FX Rack dùng modal float; Selection & MIDI Events không cần). addPanel('ai',panelPositions.ai,showAIPanel);addPanel('python_tools',panelPositions.python_tools||'bottom',showPythonToolsPanel);const closePanel=id=>{if(id==='export')setShowExportPanel(false);else if(id==='ai')setShowAIPanel(false);else if(id==='python_tools')setShowPythonToolsPanel(false);else if(id==='selection')setShowSelectionPanel(false);else if(id==='fx_rack')setShowFxRack(false);else if(id==='midi_events')setShowMidiEvents(false);};const renderPanelContent=panelId=>{const h=id=>e=>{startPanelDrag(id,e);};if(panelId==='export')return/*#__PURE__*/React.createElement("div",{className:"flex flex-col h-full gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('export',e)},/*#__PURE__*/React.createElement("h3",{className:"font-bold text-xs text-zinc-200 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"})),/*#__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 text-cyan-400"}))," Export"),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('export'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Ngu\u1ed3n"),/*#__PURE__*/React.createElement("select",{value:exportSettings.source,onChange:e=>setExportSettings(p=>({...p,source:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"project"},"Project (Mix)"),/*#__PURE__*/React.createElement("option",{value:"track_mix"},"Track Selection"),/*#__PURE__*/React.createElement("option",{value:"active_clip"},"Active Clip"),/*#__PURE__*/React.createElement("option",{value:"clip_selection"},"Clip Selection"))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"\u0110\u1ecbnh d\u1ea1ng"),/*#__PURE__*/React.createElement("select",{value:exportSettings.format,onChange:e=>setExportSettings(p=>({...p,format:e.target.value,sampleRate:e.target.value==='wav'?'44100':e.target.value==='mp3'?'44100':'44100',bitDepth:e.target.value==='wav'?'16':'16',quality:'44khz'})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"wav"},"WAV"),/*#__PURE__*/React.createElement("option",{value:"mp3"},"MP3"),/*#__PURE__*/React.createElement("option",{value:"ogg"},"OGG")))),exportSettings.format==='wav'?/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"SR (Hz)"),/*#__PURE__*/React.createElement("select",{value:exportSettings.sampleRate,onChange:e=>setExportSettings(p=>({...p,sampleRate:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"22500"},"22500"),/*#__PURE__*/React.createElement("option",{value:"44100"},"44100"))),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Bit"),/*#__PURE__*/React.createElement("select",{value:exportSettings.bitDepth,onChange:e=>setExportSettings(p=>({...p,bitDepth:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"8"},"8"),/*#__PURE__*/React.createElement("option",{value:"16"},"16"),/*#__PURE__*/React.createElement("option",{value:"24"},"24")))):/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Ch\u1ea5t l\u01b0\u1ee3ng"),/*#__PURE__*/React.createElement("select",{value:exportSettings.quality,onChange:e=>setExportSettings(p=>({...p,quality:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"44khz"},"44kHz"),/*#__PURE__*/React.createElement("option",{value:"lossless"},"Lossless"))),/*#__PURE__*/React.createElement("div",null)),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Kênh"),/*#__PURE__*/React.createElement("select",{value:exportSettings.channels,onChange:e=>setExportSettings(p=>({...p,channels:e.target.value})),className:"w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-xs text-zinc-300 focus:outline-none"},/*#__PURE__*/React.createElement("option",{value:"mono"},"Mono"),/*#__PURE__*/React.createElement("option",{value:"stereo"},"Stereo"))),/*#__PURE__*/React.createElement("div",null)),/*#__PURE__*/React.createElement("button",{onClick:triggerBounceExport,disabled:isExporting,title:"Bounce realtime - file WAV day du MIDI + FX Rack + Mastering Chain (chay lai project that, thoi gian = do dai bai)",className:"w-full py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"radio",className:"w-3 h-3"})),isExporting?'...':'Bounce MIDI'),/*#__PURE__*/React.createElement("button",{onClick:triggerWavExport,disabled:isExporting,className:"w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"download-cloud",className:"w-3 h-3"})),isExporting?'...':'Export'));if(panelId==='ai'){const selMidiInfo=getSelectedMidiItemInfo();const hasSelItem=!!selMidiInfo;if(window.PromptTemplateManager&&!aiPromptMgrRef.current){aiPromptMgrRef.current=new window.PromptTemplateManager();}// Re-read from localStorage when presets change (e.g., AIPresetModal saved) if(aiPromptMgrRef.current&&window.__aiPresetVersion!==aiPresetVersion){window.__aiPresetVersion=aiPresetVersion;aiPromptMgrRef.current.loadPresets();}const promptMgr=aiPromptMgrRef.current;const suggestions=promptMgr?promptMgr.presets:[];const handleApplySuggestion=preset=>{if(hasSelItem){setAiPrompt(`Rearrange this melody line in ${preset.name} style`);}else{setAiPrompt(preset.system_instruction_template);}setShowAiTypeahead(false);setAiSuggestions([]);};return/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1.5 flex-1 min-h-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('ai',e)},/*#__PURE__*/React.createElement("h3",{className:"font-bold text-xs text-zinc-200 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"})),/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3.5 h-3.5 text-purple-400"}))," AI Copilot"),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{onClick:()=>setAiPresetModalOpen(true),className:"text-zinc-600 hover:text-zinc-300 mr-0.5",title:"Preset Manager"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>{setAiActionLog([]);showToast('Đã xoá nhật ký AI.','info');},className:"text-zinc-600 hover:text-zinc-300",title:"Clear log"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"trash-2",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('ai'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5 w-full min-w-0 pb-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3 h-3 text-purple-400"})),/*#__PURE__*/React.createElement("select",{value:selectedProviderId,onChange:e=>setSelectedProviderId(e.target.value),className:"flex-1 min-w-0 max-w-full bg-[#141414] text-zinc-200 border border-zinc-700 rounded px-1 py-0.5 text-xs font-mono focus:outline-none focus:border-purple-600 truncate"},aiProviders.length===0?/*#__PURE__*/React.createElement("option",{value:""},"Chưa có provider"):aiProviders.map(p=>/*#__PURE__*/React.createElement("option",{key:p.id,value:p.id},p.name,p.is_active?'':' (inactive)')))),/*#__PURE__*/React.createElement("div",{className:"border-t border-zinc-800 pt-1 mt-1 shrink-0"},/*#__PURE__*/React.createElement("button",{onClick:()=>{if(window.DAWCommandDispatcher&&window.DAWCommandDispatcher.undo){const entry=window.DAWCommandDispatcher.undo();if(entry){setAiActionLog(prev=>[...prev,{type:'undo',text:`Undo: ${entry.name}`,time:Date.now()}]);showToast(`Undo AI: ${entry.name}`,'info');}}else{handleUndo();setAiActionLog(prev=>[...prev,{type:'undo',text:'Undo (Ctrl+Z)',time:Date.now()}]);}},className:"w-full text-[10px] text-amber-400 hover:text-amber-300 border border-amber-800 rounded py-0.5"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"rotate-ccw",className:"w-3 h-3"}),"Undo"))),/*#__PURE__*/React.createElement("div",{className:"flex-1 min-h-0 flex flex-col overflow-hidden mt-1"},/*#__PURE__*/React.createElement("div",{className:"text-[10px] font-bold text-zinc-400 uppercase shrink-0 pb-0.5 cursor-pointer hover:text-zinc-200 select-none",onClick:()=>setShowAIActionLog(!showAIActionLog)},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center gap-1"},/*#__PURE__*/React.createElement("i",{"data-lucide":"list",className:"w-3 h-3"})," Action Log",showAIActionLog?" \u2212":" +")),/*#__PURE__*/React.createElement("div",{ref:actionLogContainerRef,className:"flex-1 overflow-y-auto no-scrollbar bg-[#0f0f0f] rounded border border-zinc-800 select-text"+(showAIActionLog?'':' hidden')},aiActionLog.length===0?/*#__PURE__*/React.createElement("div",{className:"text-xs text-zinc-600 italic select-text p-1"},"Ch\u01B0a c\u00F3 h\u00E0nh \u0111\u1ED9ng n\u00E0o."):aiActionLog.map(function(entry,i){return/*#__PURE__*/React.createElement("div",{key:i,className:'text-xs font-mono py-0.5 border-b border-zinc-900 last:border-0 '+(entry.type==='error'?'text-red-400':entry.type==='status'?'text-zinc-400 italic':entry.type==='undo'?'text-amber-400':'text-zinc-300')},new Date(entry.time).toLocaleTimeString(),entry.text);}))),showAISuggestions?/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-1 mb-1 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 text-[10px] font-bold text-zinc-400 uppercase"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sparkles",className:"w-3 h-3 text-purple-400"}))," AI Suggestion",hasSelItem?/*#__PURE__*/React.createElement("span",{className:"flex-1 text-right text-[10px] text-amber-400 font-semibold uppercase normal-case truncate ml-2"},"MIDI: ",selMidiInfo.itemName||selMidiInfo.itemId):null),/*#__PURE__*/React.createElement("div",{className:"overflow-y-auto no-scrollbar max-h-36 bg-[#0f0f0f] rounded border border-zinc-800"},suggestions.slice(0,50).map(p=>/*#__PURE__*/React.createElement("button",{key:p.id,onClick:()=>handleApplySuggestion(p),className:"w-full text-left px-2 py-1 text-[11px] hover:bg-zinc-800 border-b border-zinc-900 last:border-0 flex items-center justify-between gap-2"},/*#__PURE__*/React.createElement("span",{className:"truncate flex-1"},/*#__PURE__*/React.createElement("span",{className:"text-zinc-400"},p.is_favorite?"★ ":"✨ "),/*#__PURE__*/React.createElement("span",{className:"text-zinc-200 font-semibold"},p.name)),/*#__PURE__*/React.createElement("span",{className:"text-[9px] text-zinc-500 shrink-0"},p.category))))):null,/*#__PURE__*/React.createElement("div",{className:"border-t border-zinc-800 pt-1.5 mt-1 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"text-xs font-bold text-zinc-400 uppercase mb-1 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"message-square",className:"w-3 h-3"}))," Copilot Prompt",/*#__PURE__*/React.createElement("button",{onClick:()=>setShowAISuggestions(!showAISuggestions),className:"ml-auto text-[9px] px-1.5 py-0.5 rounded border font-semibold "+(showAISuggestions?'bg-zinc-800 text-zinc-400 border-zinc-700 hover:bg-zinc-700':'bg-indigo-950/40 text-indigo-400 border-indigo-800/50 hover:bg-indigo-900/50'),title:showAISuggestions?'Ẩn AI Suggestion':'Hiện AI Suggestion'},"Sug")),/*#__PURE__*/React.createElement("textarea",{value:aiPrompt,onChange:e=>{const v=e.target.value;aiPromptUndoPush(v);setAiPrompt(v);if(v.trim().length>=2&&promptMgr){const matches=promptMgr.presets.filter(p=>p.keywords.some(kw=>kw.toLowerCase().includes(v.toLowerCase()))||p.name.toLowerCase().includes(v.toLowerCase()));setAiSuggestions(matches);setShowAiTypeahead(matches.length>0);}else{setShowAiTypeahead(false);}},placeholder:hasSelItem?"Nhập lệnh rearrange... (VD: Jazz Swing, Arpeggio)":"Nhập lệnh điều khiển DAW... (VD: Tạo track mới tên Beat, đặt BPM 128)",className:"w-full bg-[#1e1e1e] text-zinc-200 p-1.5 rounded border border-zinc-700 focus:outline-none focus:border-purple-600 font-mono text-xs resize-y",rows:8,onKeyDown:e=>{if((e.ctrlKey||e.metaKey)&&e.key==='z'&&!e.shiftKey){e.preventDefault();e.stopPropagation();const u=aiPromptUndoRef.current;if(u.idx>0){u.idx--;setAiPrompt(u.stack[u.idx]);showToast('Undo: AI Prompt','info');}return;}if((e.ctrlKey||e.metaKey)&&(e.key==='y'||e.key==='z'&&e.shiftKey)){e.preventDefault();e.stopPropagation();const u=aiPromptUndoRef.current;if(u.idx0){e.preventDefault();handleApplySuggestion(aiSuggestions[0]);}else if(e.key==='ArrowUp'&&promptHistRef.current.length>0&&e.target.selectionStart===0){e.preventDefault();const idx=promptHistIdx===-1?promptHistRef.current.length-1:Math.max(0,promptHistIdx-1);setPromptHistIdx(idx);setAiPrompt(promptHistRef.current[idx]);}else if(e.key==='ArrowDown'&&e.target.selectionStart===aiPrompt.length){e.preventDefault();if(promptHistIdx===-1)return;const idx=promptHistIdx+1;if(idx>=promptHistRef.current.length){setPromptHistIdx(-1);setAiPrompt('');}else{setPromptHistIdx(idx);setAiPrompt(promptHistRef.current[idx]);}}}}),showAiTypeahead&&aiSuggestions.length>0&&/*#__PURE__*/React.createElement("div",{ref:aiTypeaheadRef,className:"absolute bottom-full left-0 right-0 bg-[#1e1e1e] border border-indigo-600/50 rounded-lg shadow-2xl z-50 max-h-36 overflow-y-auto mb-1"},/*#__PURE__*/React.createElement("div",{className:"px-2 py-1 text-[10px] uppercase tracking-wider font-semibold text-indigo-400 bg-[#141414] border-b border-zinc-800"},"Gợi ý (",aiSuggestions.length,")"),aiSuggestions.slice(0,8).map(p=>/*#__PURE__*/React.createElement("div",{key:p.id,onClick:()=>handleApplySuggestion(p),className:"px-2 py-1 hover:bg-indigo-700/30 cursor-pointer border-b border-zinc-800/30 flex items-center justify-between text-[11px]"},/*#__PURE__*/React.createElement("span",null,/*#__PURE__*/React.createElement("span",{className:"font-semibold text-zinc-200"},p.name),/*#__PURE__*/React.createElement("span",{className:"ml-1.5 text-zinc-500"},"(",p.category,")")),/*#__PURE__*/React.createElement("span",{className:"text-[10px] bg-zinc-800 text-zinc-400 px-1 py-0.5 rounded"},"Tab"))))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 mt-1 shrink-0"},/*#__PURE__*/React.createElement("button",{onClick:handleAISend,disabled:aiProcessing,className:"flex-1 py-1 bg-purple-700 hover:bg-purple-600 text-white font-bold rounded text-xs border border-purple-500 flex items-center justify-center gap-1"},aiProcessing?'Đang suy luận...':/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"send",className:"w-3 h-3"}))," Gửi")),/*#__PURE__*/React.createElement("button",{onClick:()=>{setAiPrompt('');setAiActionLog([]);},className:"px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded text-xs border border-zinc-700"},"Clear")),/*#__PURE__*/React.createElement("div",{className:"text-xs text-zinc-600 shrink-0"},hasSelItem?"Enter gửi rearrange | Tab chọn gợi ý":"Enter để gửi nhanh"));}if(panelId==='python_tools')return/*#__PURE__*/React.createElement("div",{className:"flex flex-col h-full gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('python_tools',e)},/*#__PURE__*/React.createElement("h3",{className:"font-bold text-xs text-amber-300 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"})),/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"wrench",className:"w-3.5 h-3.5 text-amber-400"}))," DSP Tools"),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('python_tools'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"p-1.5 bg-[#141414] rounded border border-zinc-800 text-[10px] font-mono text-zinc-400 space-y-0.5 leading-relaxed"},dspSelectionStats?[/*#__PURE__*/React.createElement("div",{key:"track"},`Track: ${dspSelectionStats.trackName}`),/*#__PURE__*/React.createElement("div",{key:"range"},`Range: ${dspSelectionStats.timeRange}`),/*#__PURE__*/React.createElement("div",{key:"ch"},`Channels: ${dspSelectionStats.channels}`),/*#__PURE__*/React.createElement("div",{key:"peak"},`Peak Vol: ${dspSelectionStats.peakVolume}`)]:"Chưa chọn track"),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1 text-xs"},/*#__PURE__*/React.createElement("button",{onClick:()=>runPythonTool('normalize'),className:"py-1 bg-amber-950/80 hover:bg-amber-900 text-amber-300 border border-amber-800/80 rounded font-bold flex items-center justify-center gap-1"},"⚡ Peak Norm (0dB)"),/*#__PURE__*/React.createElement("button",{onClick:()=>runPythonTool('invert_phase'),className:"py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 border border-zinc-700 rounded font-bold flex items-center justify-center gap-1"},"🔄 Phase Invert"),/*#__PURE__*/React.createElement("button",{onClick:()=>runPythonTool('swap_channels'),className:"py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 border border-zinc-700 rounded font-bold flex items-center justify-center gap-1"},"🔀 Swap L/R"),/*#__PURE__*/React.createElement("button",{onClick:()=>runPythonTool('synth_wave'),className:"py-1 bg-teal-950/80 hover:bg-teal-900 text-teal-300 border border-teal-800/80 rounded font-bold flex items-center justify-center gap-1"},"🎹 Gen Synth Tone")));if(panelId==='selection')return/*#__PURE__*/React.createElement("div",{className:"flex flex-col h-full gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('selection',e)},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 font-bold uppercase flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"}))," Selection"),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('selection'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-1 bg-[#141414] p-1.5 rounded border border-zinc-800"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("span",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Start"),/*#__PURE__*/React.createElement("input",{type:"number",step:"0.01",value:selectionStats.start,onChange:e=>handleSelectionInputChange('start',e.target.value),className:"w-full bg-[#242424] text-amber-400 text-center font-mono text-xs rounded py-0.5 border border-zinc-700 focus:outline-none"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("span",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"End"),/*#__PURE__*/React.createElement("input",{type:"number",step:"0.01",value:selectionStats.end,onChange:e=>handleSelectionInputChange('end',e.target.value),className:"w-full bg-[#242424] text-amber-400 text-center font-mono text-xs rounded py-0.5 border border-zinc-700 focus:outline-none"})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col justify-center"},/*#__PURE__*/React.createElement("span",{className:"text-[7px] text-zinc-500 font-bold uppercase"},"Len"),/*#__PURE__*/React.createElement("span",{className:"text-zinc-200 font-mono text-xs font-semibold mt-0.5"},selectionStats.length,"s"))),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-3 gap-1 bg-[#141414] p-1.5 rounded border border-zinc-800 mt-1"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("span",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"Begin Bar"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:beginBar,onChange:e=>{const b=parseInt(e.target.value)||0;setBeginBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;clearLocalSelection();setSelectionMode('global');setSelectionStart(t);setSelectionEnd(t+beatDuration*4);},className:"w-full bg-[#242424] text-white text-center font-mono text-xs rounded py-0.5 border border-zinc-700 focus:outline-none"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("span",{className:"block text-[7px] text-zinc-500 font-bold uppercase mb-0.5"},"End Bar"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",value:endBar,onChange:e=>{const b=parseInt(e.target.value)||0;setEndBar(b);const beatDuration=60/parseInt(bpm||120);const t=b*beatDuration*4;setSelectionEnd(t+beatDuration*4);setNumberBar(b-beginBar+1);},className:"w-full bg-[#242424] text-white text-center font-mono text-xs rounded py-0.5 border border-zinc-700 focus:outline-none"})),/*#__PURE__*/React.createElement("div",{className:"flex flex-col justify-center"},/*#__PURE__*/React.createElement("span",{className:"text-[7px] text-zinc-500 font-bold uppercase"},"# Bars"),/*#__PURE__*/React.createElement("span",{className:"text-zinc-200 font-mono text-xs font-semibold mt-0.5"},numberBar))));if(panelId==='fx_rack')return/*#__PURE__*/React.createElement("div",{className:"flex flex-col h-full gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('fx_rack',e)},/*#__PURE__*/React.createElement("h3",{className:"font-bold text-xs text-rose-300 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"})),/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders",className:"w-3.5 h-3.5 text-rose-400"}))," Plugin FX Rack"),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('fx_rack'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 text-xs text-zinc-500 italic flex items-center justify-center"},"No FX plugins loaded"));if(panelId==='midi_events')return/*#__PURE__*/React.createElement("div",{className:"flex flex-col h-full gap-1.5"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between shrink-0 cursor-grab active:cursor-grabbing select-none",onMouseDown:e=>startPanelDrag('midi_events',e)},/*#__PURE__*/React.createElement("h3",{className:"font-bold text-xs text-sky-300 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-vertical",className:"w-3 h-3 text-zinc-500"})),/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"music",className:"w-3.5 h-3.5 text-sky-400"}))," MIDI Event List"),/*#__PURE__*/React.createElement("button",{onClick:()=>closePanel('midi_events'),className:"text-zinc-600 hover:text-zinc-300"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 text-xs text-zinc-500 italic flex items-center justify-center"},"No MIDI events selected"));return null;};const renderDock=(pos,title)=>{const panels=dockPanels[pos];if(panels.length===0)return null;const isSide=pos==='left'||pos==='right';const borderClass=pos==='left'?'border-r':pos==='right'?'border-l':pos==='top'?'border-b':'border-t';const bgClass='bg-[#1e1e1e]';const highlight=panelDragRef.current&&panelDropZone===pos;if(pos==='right')return/*#__PURE__*/React.createElement("div",{id:"right-sidebar",className:`${borderClass} ${bgClass} flex flex-col overflow-hidden select-none h-full`,style:{width:`${rightSidebarWidth}px`,minWidth:'200px',maxWidth:'600px',flexShrink:0}},/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col gap-2 p-2 overflow-hidden h-full"},panels.map((p,idx)=>/*#__PURE__*/React.createElement(React.Fragment,{key:p},/*#__PURE__*/React.createElement("div",{className:'flex flex-col flex-1 min-h-0 border border-zinc-700 rounded-lg bg-[#262626] shadow-sm p-3'},renderPanelContent(p)),idx/*#__PURE__*/React.createElement("div",{key:p,className:`${isSide?'w-full':'flex-none w-[320px]'} border border-zinc-700 rounded-lg bg-[#262626] shadow-sm p-2`},renderPanelContent(p))));};return/*#__PURE__*/React.createElement("div",{ref:workspaceRef,className:"flex-1 flex flex-col overflow-hidden select-none daw-bg relative"},panelDragRef.current&&panelDropZone&&/*#__PURE__*/React.createElement("div",{className:"absolute inset-0 z-50 pointer-events-none"},panelDropZone==='top'&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 left-0 right-0 h-1 bg-cyan-400 shadow-[0_0_8px_#06b6d4]"}),panelDropZone==='bottom'&&/*#__PURE__*/React.createElement("div",{className:"absolute bottom-0 left-0 right-0 h-1 bg-cyan-400 shadow-[0_0_8px_#06b6d4]"}),panelDropZone==='left'&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 left-0 w-1 bg-cyan-400 shadow-[0_0_8px_#06b6d4]"}),panelDropZone==='right'&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 right-0 w-1 bg-cyan-400 shadow-[0_0_8px_#06b6d4]"})),dragGhostPanel&&dragGhostPos&&/*#__PURE__*/React.createElement("div",{className:"fixed z-[100] pointer-events-none opacity-80 border border-cyan-500 rounded-lg bg-[#262626] shadow-2xl p-3 w-56",style:{left:dragGhostPos.x,top:dragGhostPos.y}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 text-xs text-zinc-200 font-bold"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"move",className:"w-3.5 h-3.5 text-cyan-400"})),dragGhostPanel==='export'?'Export Panel':dragGhostPanel==='ai'?'AI Panel':dragGhostPanel==='python_tools'?'Audio Processing Panel':'Selection Panel'),/*#__PURE__*/React.createElement("div",{className:"text-xs text-zinc-500 mt-1"},"Drop at edge to dock")),renderDock('top','Top'),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-hidden"},renderDock('left','Left'),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col overflow-hidden min-w-0"},/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-hidden"},activeTab==='main'||sessionTabs.some(s=>s.id===activeTab)?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{ref:tcpContainerRef,onScroll:handleTCPScroll,className:"shrink-0 z-20 bg-[#262626] overflow-y-auto flex flex-col border-r border-zinc-900 no-scrollbar",style:{width:tcpWidth+'px',scrollbarWidth:'none',msOverflowStyle:'none'}},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 z-50 flex items-center justify-between h-10 border-b border-zinc-900 bg-[#242424] px-2 shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-zinc-300 flex items-center gap-1.5"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders",className:"w-3.5 h-3.5 text-cyan-400"})),"TRACKS (",activeTracks.length,")"),/*#__PURE__*/React.createElement("button",{onClick:addNewTrack,className:"px-2.5 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-xs font-semibold flex items-center gap-1 shadow transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus",className:"w-3 h-3"}))," Add Track")),/*#__PURE__*/React.createElement("div",{className:"sticky top-10 z-40 flex h-[40px] border-b border-purple-500/30 bg-[#1a1a2e] p-2 border-l-4 border-purple-500 shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between w-full"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-purple-400 font-mono"},"TM"),/*#__PURE__*/React.createElement("span",{className:"text-xs font-semibold text-zinc-300"},"Tempo")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("input",{type:"number",value:bpm,onChange:e=>{setBpm(e.target.value);},onBlur:e=>{const v=e.target.value;if(v&&String(bpm)!==v)setBpmWithUndo(v);localStorage.setItem('studio_bpm',bpm);},onKeyDown:e=>{if(e.key==='Enter'){e.target.blur();}},className:"w-12 bg-zinc-800 border border-zinc-700 rounded text-xs text-zinc-300 text-center font-mono focus:outline-none focus:border-purple-500",min:"40",max:"300"}),/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500"},"BPM")))),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col divide-y divide-[#141414] bg-[#262626]"},activeTracks.length===0?/*#__PURE__*/React.createElement("div",{className:"p-6 text-center text-zinc-400 flex flex-col items-center justify-center space-y-3"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus-circle",className:"w-8 h-8 text-cyan-400 opacity-80"})),/*#__PURE__*/React.createElement("p",{className:"text-xs font-medium"},"Chưa có Track nào trong dự án."),/*#__PURE__*/React.createElement("button",{onClick:addNewTrack,className:"px-3 py-1.5 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-xs font-semibold flex items-center gap-1.5 shadow"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"plus-circle",className:"w-3.5 h-3.5"}))," Thêm Track Mới")):activeTracks.map((track,idx)=>{const isSelected=selectedTrackId===track.id;const autoHeight=track.height||(track.isArmed?164:140);return/*#__PURE__*/React.createElement("div",{key:track.id,style:{height:`${autoHeight}px`},className:`shrink-0 relative flex flex-col justify-between p-2.5 bg-[#1e1e1e] border-r border-zinc-900 cursor-pointer border-l-4 border-b border-[#141414] overflow-hidden ${isSelected?'border-cyan-500 bg-[#252525]':'border-transparent hover:bg-zinc-800/20'}`,onClick:()=>{setSelectedTrackId(track.id);var prTab=subTabs.find(function(s){return s.type==='PIANO_ROLL'&&s.trackId===track.id;});if(prTab&&prTab.notes&&prTab.notes.length>0){stopAllPlayback();setSubTabs(function(prev){return prev.map(function(s){return s.id===prTab.id?Object.assign({},s,{isPlaying:true}):s;});});schedulePianoRollMidi(prTab,0);startSubTabPlayback(prTab,0);}}},/*#__PURE__*/React.createElement("div",{className:"flex items-start justify-between"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-zinc-500 font-mono"},(idx+1).toString().padStart(2,'0')),/*#__PURE__*/React.createElement("label",{onClick:e=>{e.stopPropagation();const el=e.currentTarget.querySelector('input');if(el)el.click();},className:"cursor-pointer"},/*#__PURE__*/React.createElement("input",{type:"color",value:track.color||'#0f766e',onChange:e=>{e.stopPropagation();updateTrackColor(track.id,e.target.value);},className:"w-0 h-0 opacity-0 absolute pointer-events-none"}),/*#__PURE__*/React.createElement("div",{className:"w-3 h-3 rounded-full border border-zinc-600 hover:scale-110 transition-transform",style:{backgroundColor:track.color}})),editingTrackName===track.id?/*#__PURE__*/React.createElement("input",{type:"text",value:editNameInput,autoFocus:true,onChange:e=>setEditNameInput(e.target.value),onBlur:()=>{updateTrackName(track.id,editNameInput||track.name);setEditingTrackName(null);},onKeyDown:e=>{if(e.key==='Enter'){updateTrackName(track.id,editNameInput||track.name);setEditingTrackName(null);}if(e.key==='Escape')setEditingTrackName(null);},onClick:e=>e.stopPropagation(),className:"text-xs font-semibold bg-black text-zinc-200 border border-cyan-500 rounded px-1 py-0 w-24 outline-none"}):/*#__PURE__*/React.createElement("span",{className:"text-xs font-semibold text-zinc-300 truncate max-w-[80px] cursor-text hover:text-cyan-300 transition-colors",title:"Click to rename",onClick:e=>{e.stopPropagation();setEditingTrackName(track.id);setEditNameInput(track.name);}},track.name)),/*#__PURE__*/React.createElement("div",{className:"flex flex-wrap gap-0.5 max-w-[100px] mb-0.5"},(track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default',name:track.name,startTime:track.startTime}]:[]).slice(0,3).map(c=>/*#__PURE__*/React.createElement("span",{key:c.id,className:"text-[9px] font-mono text-zinc-500 bg-zinc-800/60 rounded px-0.5 truncate max-w-[90px] cursor-pointer hover:text-cyan-400 hover:bg-zinc-700",title:c.name||track.name,onClick:e=>{e.stopPropagation();setSelectedTrackId(track.id);clearLocalSelection();setSelectionMode('global');const start=c.startTime||0;const end=start+(c.buffer?c.buffer.duration:2);setSelectionStart(start);setSelectionEnd(end);showToast(`Selected: ${c.name||track.name}`,'info');}},c.name||track.name),editingClipName&&editingClipName.trackId===track.id&&editingClipName.clipId===c.id?/*#__PURE__*/React.createElement("input",{type:"text",value:editNameInput,autoFocus:true,onChange:e=>setEditNameInput(e.target.value),onBlur:()=>{if(editNameInput.trim())updateClipName(track.id,c.id,editNameInput.trim());setEditingClipName(null);},onKeyDown:e=>{if(e.key==='Enter'){if(editNameInput.trim())updateClipName(track.id,c.id,editNameInput.trim());setEditingClipName(null);}if(e.key==='Escape')setEditingClipName(null);},onClick:e=>e.stopPropagation(),className:"w-20 text-[9px] font-mono bg-black text-cyan-300 border border-cyan-500 rounded px-0.5 py-0 outline-none"}):/*#__PURE__*/React.createElement("button",{className:"text-[9px] text-zinc-600 hover:text-cyan-400 ml-0.5 shrink-0",title:"Sửa tên clip",onClick:e=>{e.stopPropagation();setEditingClipName({trackId:track.id,clipId:c.id});setEditNameInput(c.name||track.name);}},/*#__PURE__*/React.createElement("i",{"data-lucide":"pencil",className:"w-2.5 h-2.5"})))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackMute(track.id);},title:"Mute",className:`px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.muted?'bg-red-950 text-red-400 border-red-700':'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`},/*#__PURE__*/React.createElement("i",{"data-lucide":track.muted?"volume-x":"volume-2",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackSoloEvaluate(track.id);},title:"Solo",className:`px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.solo?'bg-amber-950 text-amber-400 border-amber-600':'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`},/*#__PURE__*/React.createElement("i",{"data-lucide":track.solo?"headphones":"headphone-off",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackDrum(track.id);},title:track.is_percussion?"Drum Channel (CH 10) - Click to disable":"Toggle Drum Channel (CH 10)",className:`px-1.5 py-0.5 text-[9px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.is_percussion?'bg-rose-900 text-rose-300 border-rose-700':'bg-zinc-800 text-zinc-500 border-transparent hover:text-zinc-300'}`},/*#__PURE__*/React.createElement("span",{className:"text-[11px]"},"🥁"),track.is_percussion?/*#__PURE__*/React.createElement("span",{className:"text-[9px]"},"D"):null),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackArm(track.id);},title:"ARM (Record)",className:`px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.isArmed?'bg-red-600 text-white border-red-500 hover:bg-red-500':'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`},/*#__PURE__*/React.createElement("i",{"data-lucide":"circle",className:`w-2.5 h-2.5 ${track.isArmed?'fill-white':''}`})),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackMonitor(track.id);},title:"Input Monitor",className:`px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.monitoringEnabled?'bg-amber-600 text-white border-amber-500 hover:bg-amber-500':'bg-zinc-800 text-zinc-500 border-transparent hover:text-zinc-305'}`},/*#__PURE__*/React.createElement("i",{"data-lucide":track.monitoringEnabled?"mic":"mic-off",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();deleteTrack(track.id);},className:"p-0.5 rounded text-red-500 hover:bg-red-950/40 hover:text-red-400 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"trash-2",className:"w-3 h-3"}))))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-0.5 text-xs",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"w-8 text-right text-zinc-500 text-xs"},"Vol:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-50",max:"7",step:"0.5",value:track.volumeDb??0,onChange:e=>updateTrackVolumeDb(track.id,parseFloat(e.target.value)),className:"flex-1 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-cyan-500",style:{height:'4px'}}),/*#__PURE__*/React.createElement("span",{className:"w-12 text-right font-mono text-zinc-300 text-xs"},track.volumeDb??0,"dB")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"w-8 text-right text-zinc-500 text-xs"},"Pan:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-100",max:"100",step:"1",value:track.pan??0,onChange:e=>updateTrackPan(track.id,parseInt(e.target.value)),className:"flex-1 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-purple-500",style:{height:'4px'}}),/*#__PURE__*/React.createElement("span",{className:"w-12 text-right font-mono text-zinc-300 text-xs"},track.pan>0?'R'+track.pan:track.pan<0?'L'+Math.abs(track.pan):'C')),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 mt-0.5"},/*#__PURE__*/React.createElement("span",{className:"w-8 text-right text-zinc-500 text-[10px]"},"In:"),/*#__PURE__*/React.createElement("select",{value:`${track.inputSource?.deviceType||'NONE'}:${track.inputSource?.deviceId||''}`,onChange:e=>{const val=e.target.value;const parts=val.split(':');const type=parts[0];const id=parts.slice(1).join(':');updateTrackInputSource(track.id,type,id);},className:"flex-1 bg-[#18181b] text-zinc-300 text-[10px] rounded border border-zinc-700 focus:outline-none py-0.5 px-1 truncate max-w-[120px]"},/*#__PURE__*/React.createElement("option",{value:"NONE:"},"No Input"),/*#__PURE__*/React.createElement("optgroup",{label:"Microphones"},audioDevices.map(d=>/*#__PURE__*/React.createElement("option",{key:d.deviceId,value:`MICROPHONE:${d.deviceId}`},d.label||`Microphone ${d.deviceId.slice(0,5)}`))),/*#__PURE__*/React.createElement("optgroup",{label:"MIDI Keyboards"},/*#__PURE__*/React.createElement("option",{value:"MIDI_KEYBOARD:ALL"},"Any MIDI Keyboard"),midiDevices.map(d=>/*#__PURE__*/React.createElement("option",{key:d.id,value:`MIDI_KEYBOARD:${d.id}`},d.name||`MIDI Input ${d.id.slice(0,5)}`)))),track.isArmed&&lastMidiNote&&(lastMidiNote.length===0||Date.now()-lastMidiNote.time<3000)&&/*#__PURE__*/React.createElement("span",{className:"text-[9px] font-mono text-emerald-400 ml-0.5 truncate max-w-[60px] shrink-0",title:"MIDI Note:velocity:length"},`${midiPitchToName(lastMidiNote.pitch)}:${lastMidiNote.velocity}:${lastMidiNote.length>0?lastMidiNote.length.toFixed(2)+'s':'...'}`)),track.isArmed&&/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1 mt-0.5"},/*#__PURE__*/React.createElement("span",{className:"w-8 text-right text-zinc-500 text-[9px]"},"VU:"),/*#__PURE__*/React.createElement("canvas",{ref:el=>{if(el)trackVuRefs.current[track.id]=el;else delete trackVuRefs.current[track.id];},width:100,height:4,className:"flex-1 bg-[#18181b] rounded h-1"}))),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5 mt-1",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("input",{type:"file",id:`upload-${track.id}`,accept:"audio/*",className:"hidden",onChange:e=>loadFileOnTrack(track.id,e.target.files[0])}),/*#__PURE__*/React.createElement("label",{htmlFor:`upload-${track.id}`,className:"px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded text-xs flex items-center gap-1 cursor-pointer border border-zinc-700 font-medium"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"upload",className:"w-3 h-3"}))," File"),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();setFxSelectorTrackId(track.id);},className:"px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-purple-400 border border-purple-900 rounded text-xs font-bold flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"wand-2",className:"w-3 h-3"}))," FX: ",/*#__PURE__*/React.createElement("span",{className:"text-zinc-500 font-normal"},track.fxType||"None")),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();openInstrumentSelector(track.id);},className:"px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-amber-400 border border-amber-800 rounded text-xs font-bold flex items-center gap-1 max-w-[120px]"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"music",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"truncate text-[10px]"},track.instrumentName||track.instrumentId||"Synth"),/*#__PURE__*/React.createElement("i",{"data-lucide":"chevron-down",className:"w-3 h-3 shrink-0"}))),/*#__PURE__*/React.createElement("div",{onMouseDown:e=>handleTrackResizeMouseDown(e,track.id),className:"absolute bottom-0 left-0 right-0 h-1 cursor-ns-resize z-30 hover:bg-cyan-500/50 transition-colors",onClick:e=>e.stopPropagation()}));})),/*#__PURE__*/React.createElement("div",{className:"h-[48px] border-r border-zinc-900 bg-[#1e1e1e]/50 shrink-0"})),/*#__PURE__*/React.createElement("div",{className:"w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10",onMouseDown:startTcpResize}),/*#__PURE__*/React.createElement("div",{ref:handleTimelineWrapperRef,onScroll:handleTimelineScroll,className:"flex-1 relative z-10 overflow-x-auto overflow-y-auto min-w-0"},/*#__PURE__*/React.createElement("div",{style:{width:`${timelineWidth}px`},className:"relative flex flex-col min-h-full"},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 z-30 bg-[#1a1a1a]"},/*#__PURE__*/React.createElement(TimelineRuler,{bpm:parseInt(bpm)||120,zoom:zoom,timelineWidth:timelineWidth,viewportWidth:viewportWidth,onPlayheadSet:handlePlayheadSet,snapValue:snapValue,onRulerMouseDown:e=>{setSelectionFollowsTempo(false);handleRulerMouseDown(e);},scrollLeft:scrollLeft,canvasRedrawCount:canvasRedrawCount}),/*#__PURE__*/React.createElement(TempoTrackLane,{bpm:parseInt(bpm)||120,zoom:zoom,timelineWidth:timelineWidth,viewportWidth:viewportWidth,onPlayheadSet:handlePlayheadSet,snapValue:snapValue,onRulerMouseDown:e=>{setSelectionFollowsTempo(true);handleRulerMouseDown(e);},scrollLeft:scrollLeft,canvasRedrawCount:canvasRedrawCount})),selectionMode==='global'&&selLeft!==null&&selRight!==null&&selRight>selLeft&&/*#__PURE__*/React.createElement("div",{className:"absolute inset-0 pointer-events-none z-20",style:{left:`${selLeft*zoom}px`,width:`${(selRight-selLeft)*zoom}px`,top:'80px'}},/*#__PURE__*/React.createElement("div",{className:"w-full h-full bg-amber-500/10",style:{borderLeft:'1px solid #f59e0b',borderRight:'1px solid #f59e0b'}})),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col divide-y divide-[#141414] relative bg-[#111111] min-h-full",onDragOver:e=>e.preventDefault(),onDrop:async e=>{e.preventDefault();// Media Explorer drag (client FS handle / server file_id / local path) if(window.__mediaExplorerDragFile){const f=await resolveMediaExplorerDropFile();window.__mediaExplorerDragFile=null;if(!f)return;if(/\.mid$|\.midi$/i.test(f.name||'')){handleDropMidiToNewTracks(f);}else if(selectedTrackId){loadFileOnTrack(selectedTrackId,f);}return;}const f=e.dataTransfer.files&&e.dataTransfer.files[0];if(!f)return;if(/\.mid$|\.midi$/i.test(f.name||'')){handleDropMidiToNewTracks(f);}else if(selectedTrackId){loadFileOnTrack(selectedTrackId,f);}},onMouseDown:e=>{if(e.ctrlKey&&!e.shiftKey&&!e.altKey&&e.button===0){const wrapper=timelineWrapperRef.current;if(wrapper){const rect=wrapper.getBoundingClientRect();const sl=wrapper.scrollLeft;const raw=Math.max(0,(e.clientX-rect.left+sl)/zoom-leadInMargin);const time=snapValue!=='free'?snapTime(raw,snapValue,bpm):raw;handleSweepSelectStart(null,time);}}}},activeTracks.map((track,idx)=>{const isSelected=selectedTrackId===track.id;const autoHeight=track.height||(track.isArmed?164:140);return/*#__PURE__*/React.createElement("div",{key:track.id,style:{height:`${autoHeight}px`},className:`shrink-0 relative border-b border-[#141414] hover:bg-zinc-850/5 transition-colors ${isSelected?'bg-zinc-800/10':''}`,onDragOver:e=>e.preventDefault(),onDrop:async e=>{e.preventDefault();e.stopPropagation();// Media Explorer drag (client FS handle / server file_id / local path) if(window.__mediaExplorerDragFile){const f=await resolveMediaExplorerDropFile();window.__mediaExplorerDragFile=null;if(f)loadFileOnTrack(track.id,f);return;}const f=e.dataTransfer.files&&e.dataTransfer.files[0];if(!f)return;loadFileOnTrack(track.id,f);},onMouseEnter:()=>{setHoveredTrackId(track.id);hoveredTrackIdRef.current=track.id;}},/*#__PURE__*/React.createElement(WaveformLane,{track:track,zoom:zoom,timelineWidth:timelineWidth,viewportWidth:viewportWidth,scrollLeft:scrollLeft,onSelectRange:handleSelectRange,onPlayheadSet:handlePlayheadSet,isSelected:isSelected,selectedItemIds:selectedItemIds,onSelectTrack:setSelectedTrackId,markers:track.markers,onTrackLaneMouseDown:handleTrackLaneMouseDown,onClearSelection:()=>{captureSelectionUndo();setSelectedItemIds(new Set());},onSweepSelectStart:handleSweepSelectStart,onDeselectItem:handleDeselectItem,onAddToSelection:handleAddToSelection,onSetPendingDrag:handleSetPendingDrag,onSetPendingDragMove:handleSetPendingDragMove,onContextMenu:handleContextMenu,onClipDragStart:handleClipDragStart,onClipStretchStart:handleClipStretchStart,onSectionItemDragStart:handleSectionItemDragStart,onSectionItemResizeStart:handleSectionItemResizeStart,onSelectionEdgeDragStart:handleSelectionEdgeDragStart,setSelectedClipId:setSelectedClipId,selectedClipId:selectedClipId,activeTool:activeTool,onSplitTrackAtTime:handleSplitTrackAtTime,onEditClipInSubTab:handleEditClipInSubTab,onEditSectionInTab:handleEditSectionInTab,onEditMidiInTab:handleEditMidiInTab,snapValue:snapValue,bpm:bpm,selectionMode:selectionMode,localSelectionTrackId:localSelectionTrackId,localSelectionStart:localSelectionStart,currentTime:currentTime,getLocalAnchor:()=>localSelectionAnchorRef.current,onClearLocalSelection:()=>{captureSelectionUndo();clearLocalSelection();},onSetSelectionMode:mode=>{captureSelectionUndo();setSelectionMode(mode);},onSetSelectionStart:val=>{captureSelectionUndo();setSelectionStart(val);},onSetSelectionEnd:val=>{captureSelectionUndo();setSelectionEnd(val);},onSetCurrentTime:setCurrentTime,onSetLocalSelectionTrackId:setLocalSelectionTrackId,onSetLocalSelectionStart:setLocalSelectionStart,onSetLocalSelectionEnd:setLocalSelectionEnd,localSelLeft:localSelectionStart!==null&&localSelectionEnd!==null?Math.min(localSelectionStart,localSelectionEnd):null,localSelRight:localSelectionStart!==null&&localSelectionEnd!==null?Math.max(localSelectionStart,localSelectionEnd):null,scrollLeft:scrollLeft,recordingState:recordingState,recTempMidiNotes:recTempMidiNotes,recTempAudioBuffer:recTempAudioBuffer,recStartTimelineTime:recStartTimelineTime,canvasRedrawCount:canvasRedrawCount}),selectionMode==='local'&&localSelectionTrackId===track.id&&localSelectionStart!==null&&localSelectionEnd!==null&&Math.abs(localSelectionEnd-localSelectionStart)>0&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 border-l border-r border-amber-400 bg-amber-500/20 z-20 pointer-events-none",style:{left:`${Math.min(localSelectionStart,localSelectionEnd)*zoom}px`,width:`${Math.abs(localSelectionEnd-localSelectionStart)*zoom}px`}},/*#__PURE__*/React.createElement("div",{className:"absolute -left-[1px] top-0 bottom-0 w-[2px] bg-amber-400 z-30 pointer-events-auto cursor-ew-resize",onMouseDown:e=>handleHandleDragStart(e,'left')}),/*#__PURE__*/React.createElement("div",{className:"absolute -right-[1px] top-0 bottom-0 w-[2px] bg-amber-400 z-30 pointer-events-auto cursor-ew-resize",onMouseDown:e=>handleHandleDragStart(e,'right')})),sweepSelect&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 border-l border-r border-amber-400 bg-amber-500/10 z-20 pointer-events-none",style:{left:`${Math.min(sweepSelect.startTime,sweepSelect.endTime)*zoom}px`,width:`${Math.abs(sweepSelect.endTime-sweepSelect.startTime)*zoom}px`}}),track.buffer&&/*#__PURE__*/React.createElement("div",{className:"absolute right-2 top-2 flex items-center gap-1 z-10 opacity-70 hover:opacity-100"},/*#__PURE__*/React.createElement("button",{onClick:()=>handleSplitTrack(track.id),className:"px-1.5 py-0.5 bg-zinc-900/95 text-zinc-300 rounded text-xs flex items-center gap-1 border border-zinc-700/50"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"scissors",className:"w-2.5 h-2.5 text-cyan-400"}))," Cắt")),/*#__PURE__*/React.createElement("div",{onMouseDown:e=>handleTrackResizeMouseDown(e,track.id),className:"absolute bottom-0 left-0 right-0 h-1 cursor-ns-resize z-30 hover:bg-cyan-500/50 transition-colors",onClick:e=>e.stopPropagation()}));}),/*#__PURE__*/React.createElement("div",{className:"h-[48px] flex items-center justify-center text-xs text-zinc-500 hover:bg-zinc-900/20 cursor-pointer select-none border-t border-dashed border-zinc-800",onMouseEnter:()=>{if(draggedClipRef.current||draggedSectionItemRef.current){setHoveredTrackId(addNewTrack());}},onClick:addNewTrack},/*#__PURE__*/React.createElement("span",{className:"flex items-center gap-1 text-zinc-400"},/*#__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"}))," Kéo clip xuống hoặc Click tạo Track")),selectionMode==='global'&&selLeft!==null&&selRight!==null&&selRight>selLeft&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 border-l border-r border-amber-500 bg-amber-500/10 z-20 selection-interactive-box cursor-grab active:cursor-grabbing",style:{left:`${selLeft*zoom}px`,width:`${(selRight-selLeft)*zoom}px`},onMouseDown:handleSelectionBodyDragStart},/*#__PURE__*/React.createElement("div",{className:"absolute -left-[1px] top-0 bottom-0 w-[1px] bg-amber-500 hover:bg-amber-400 cursor-ew-resize z-30",onMouseDown:e=>handleHandleDragStart(e,'left')}),/*#__PURE__*/React.createElement("div",{className:"absolute -right-[1px] top-0 bottom-0 w-[1px] bg-amber-500 hover:bg-amber-400 cursor-ew-resize z-30",onMouseDown:e=>handleHandleDragStart(e,'right')})),/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 w-[2px] bg-[#ef4444] z-20 pointer-events-none",style:{left:`${playheadLeftPos}px`}},/*#__PURE__*/React.createElement("div",{className:"w-3 h-3 bg-[#ef4444] rotate-45 transform -translate-x-1/2 -translate-y-1/2 absolute top-0"})))))):(()=>{const st=subTabs.find(s=>s.id===activeTab);if(!st)return null;if(st.type==='PIANO_ROLL'){return/*#__PURE__*/React.createElement(PianoRollTabEditor,{st:st,zoom:zoom,bpm:bpm,viewportWidth:viewportWidth,activeTracks:activeTracks,onClose:()=>closeSubTab(st.id),onUpdateNotes:handleUpdateMidiNotes,onSaveNotes:handleSaveMidiNotes,setSubTabs:setSubTabs,onPlayPause:handlePlayPause,onStop:stopAllPlayback,isPlaying:isPlaying,playPreviewNote:playMidiPreviewNote,showToast:showToast,midiDevices:midiDevices,recordingState:recordingState,recTempMidiNotes:recTempMidiNotes,onRecord:handleRecordClick,selectedMidiInputId:selectedMidiInputId,onMidiInputSelect:handleMidiInputSelect,activeMidiPitches:activeMidiPitches,onInstrumentSelect:trackId=>{openInstrumentSelector(trackId);},onRealtimePlay:handlePianoRollRealtimePlay,onRescheduleMidi:updatedNotes=>{const playingSub=subTabs.find(s=>s.id===activeTab&&s.type==='PIANO_ROLL'&&s.isPlaying);if(playingSub){const offset=playingSub.currentTime||0;const ctx=getAudioContext();const tNode=activeTrackNodesRef.current[playingSub.trackId];if(tNode&&tNode.gainNode){tNode.gainNode.gain.setValueAtTime(tNode.gainNode.gain.value||1,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(0.001,ctx.currentTime+0.04);}setTimeout(()=>{window.SonicSF.stopAll();if(tNode&&tNode.gainNode){const trackData=activeTracksRef.current?activeTracksRef.current.find(t=>t.id===playingSub.trackId):null;const volDb=trackData?trackData.volumeDb??0:0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);tNode.gainNode.gain.setValueAtTime(0.001,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(volLinear||0.8,ctx.currentTime+0.015);}startOffsetTimeRef.current=offset;startAudioTimeRef.current=ctx.currentTime;startBufferOffsetRef.current=offset*(playingSub.speed||1.0);schedulePianoRollMidi(playingSub,offset,updatedNotes);},50);}},onSeekPlayhead:clickTime=>{const seekSt=subTabs.find(s=>s.id===activeTab&&s.type==='PIANO_ROLL');if(!seekSt)return;if(seekSt.isPlaying){stopAllPlayback();window.SonicSF.stopAll();setSubTabs(prev=>prev.map(s=>s.id===seekSt.id?{...s,currentTime:clickTime,isPlaying:true}:s));const ctx=getAudioContext();startOffsetTimeRef.current=clickTime;startAudioTimeRef.current=ctx.currentTime;startBufferOffsetRef.current=clickTime*(seekSt.speed||1.0);schedulePianoRollMidi(seekSt,clickTime);startSubTabPlayback(seekSt,clickTime);}else{setSubTabs(prev=>prev.map(s=>s.id===seekSt.id?{...s,currentTime:clickTime}:s));}}});}const subTrack=tracks.find(t=>t.id===st.trackId);const vTrack=subTrack?{...subTrack,buffer:st.buffer,isSubTab:true}:null;const subTabDuration=st.type==='PIANO_ROLL'?(()=>{const notes=st.notes||[];const beatSec=60.0/(parseFloat(bpm)||120);let maxEnd=0;notes.forEach(n=>{const end=(n.start_beat||0)+(n.duration_beats||1);if(end>maxEnd)maxEnd=end;});return maxEnd*beatSec+1.0;})():st.buffer&&'duration'in st.buffer?st.buffer.duration:4.0;const subTabTimelineWidth=Math.max(zoom*subTabDuration,viewportWidth);return/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{className:"shrink-0 z-30 bg-[#262626] overflow-y-scroll no-scrollbar flex flex-col border-r border-zinc-900",style:{width:tcpWidth+'px',scrollbarWidth:'none',msOverflowStyle:'none'}},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 z-50 flex items-center h-10 border-b border-zinc-900 bg-[#242424] px-2 justify-between shrink-0"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-zinc-500 uppercase"},"Sub-Tab"),/*#__PURE__*/React.createElement("button",{onClick:()=>closeSubTab(st.id),className:"px-1.5 py-0.5 text-xs bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded border border-zinc-700 flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"}))," Close")),vTrack?/*#__PURE__*/React.createElement("div",{key:vTrack.id,className:"flex-1 flex flex-col p-2.5 bg-[#1e1e1e] border-r border-zinc-900 border-l-4 border-l-cyan-500"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between mb-2"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("label",{onClick:e=>{e.stopPropagation();const el=e.currentTarget.querySelector('input');if(el)el.click();},className:"cursor-pointer"},/*#__PURE__*/React.createElement("input",{type:"color",value:vTrack.color||'#0f766e',onChange:e=>{e.stopPropagation();updateTrackColor(vTrack.id,e.target.value);},className:"w-0 h-0 opacity-0 absolute pointer-events-none"}),/*#__PURE__*/React.createElement("div",{className:"w-3 h-3 rounded-full border border-zinc-600 hover:scale-110 transition-transform",style:{backgroundColor:vTrack.color}})),editingTrackName===vTrack.id?/*#__PURE__*/React.createElement("input",{type:"text",value:editNameInput,autoFocus:true,onChange:e=>setEditNameInput(e.target.value),onBlur:()=>{updateTrackName(vTrack.id,editNameInput||vTrack.name);setEditingTrackName(null);},onKeyDown:e=>{if(e.key==='Enter'){updateTrackName(vTrack.id,editNameInput||vTrack.name);setEditingTrackName(null);}if(e.key==='Escape')setEditingTrackName(null);},onClick:e=>e.stopPropagation(),className:"text-xs font-semibold bg-black text-zinc-200 border border-cyan-500 rounded px-1 py-0 w-24 outline-none"}):/*#__PURE__*/React.createElement("span",{className:"text-xs font-semibold text-zinc-300 truncate max-w-[80px] cursor-text hover:text-cyan-300 transition-colors",title:"Click to rename",onClick:e=>{e.stopPropagation();setEditingTrackName(vTrack.id);setEditNameInput(vTrack.name);}},vTrack.name)),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackMute(vTrack.id);},className:`px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${vTrack.muted?'bg-red-950 text-red-400 border-red-700':'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`},"M"),/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();toggleTrackSoloEvaluate(vTrack.id);},className:`px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${vTrack.solo?'bg-amber-950 text-amber-400 border-amber-600':'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`},"S"))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col gap-2.5 text-[14px] mb-3"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"w-10 text-right text-zinc-500 text-[14px]"},"Vol:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-50",max:"7",step:"0.5",id:`tcp-vol-${st.id}`,value:vTrack.volumeDb??0,onChange:e=>updateTrackVolumeDb(vTrack.id,parseFloat(e.target.value)),className:"flex-1 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-cyan-500",style:{height:'4px'}}),/*#__PURE__*/React.createElement("span",{className:"w-16 text-right font-mono text-zinc-300 text-[14px]",id:`tcp-vol-label-${st.id}`},vTrack.volumeDb??0,"dB")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"w-10 text-right text-zinc-500 text-[14px]"},"Pan:"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-100",max:"100",step:"1",id:`tcp-pan-${st.id}`,value:vTrack.pan??0,onChange:e=>updateTrackPan(vTrack.id,parseInt(e.target.value)),className:"flex-1 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-purple-500",style:{height:'4px'}}),/*#__PURE__*/React.createElement("span",{className:"w-16 text-right font-mono text-zinc-300 text-[14px]",id:`tcp-pan-label-detailed-${st.id}`},vTrack.pan>0?'R'+vTrack.pan:vTrack.pan<0?'L'+Math.abs(vTrack.pan):'C')),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-1.5 text-[14px]"},/*#__PURE__*/React.createElement("button",{onClick:()=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,isLooping:!s.isLooping}:s)),className:`px-2 py-0.5 text-[14px] rounded border font-bold ${st.isLooping?'bg-amber-700 text-white border-amber-500':'bg-zinc-800 text-zinc-400 border-zinc-700'}`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"repeat",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>updateSubTabEffects(st.id,{reverse:!(st.effects||{}).reverse}),className:`px-2 py-0.5 rounded border text-[14px] font-bold ${(st.effects||{}).reverse?'bg-zinc-600 text-white border-zinc-500':'bg-zinc-800 text-zinc-400 border-zinc-700'} flex items-center justify-center`,title:"Reverse"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"arrow-left-right",className:"w-3.5 h-3.5"}))),/*#__PURE__*/React.createElement("span",{className:"w-10 text-right text-zinc-500 text-[14px]"},"Loop:"),/*#__PURE__*/React.createElement("input",{type:"number",min:"0",max:"999",value:st.loopCount||0,onChange:e=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,loopCount:Math.max(0,parseInt(e.target.value)||0)}:s)),className:"w-12 bg-black text-amber-300 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono",title:"Loop count"}))),/*#__PURE__*/React.createElement("div",{className:"flex gap-1 justify-between my-2.5"},/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center bg-zinc-900/60 p-1.5 rounded border border-zinc-800 flex-1"},/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-bold text-zinc-400 uppercase tracking-tighter",title:"Normalize"},"Norm"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"0",step:"0.1",value:subTabNormVal,onChange:e=>setSubTabNormVal(parseFloat(e.target.value)),style:{writingMode:'vertical-lr',direction:'rtl',height:'120px',width:'20px',accentColor:'#a1a1aa'},className:"my-2 cursor-pointer"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-mono text-zinc-300 font-bold"},subTabNormVal,"dB"),/*#__PURE__*/React.createElement("button",{onClick:()=>applySubTabEffect(st.id,'normalize',subTabNormVal),className:"mt-1.5 px-1 py-1 bg-zinc-700 hover:bg-zinc-600 text-zinc-100 rounded text-[14px] font-bold w-full uppercase border border-zinc-650"},"Apply")),/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center bg-zinc-900/60 p-1.5 rounded border border-zinc-800 flex-1"},/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-bold text-zinc-400 uppercase tracking-tighter",title:"Pitch Shift"},"Pitch"),/*#__PURE__*/React.createElement("input",{type:"range",min:"-12",max:"12",step:"0.5",value:subTabPitchVal,onChange:e=>setSubTabPitchVal(parseFloat(e.target.value)),style:{writingMode:'vertical-lr',direction:'rtl',height:'120px',width:'20px',accentColor:'#a1a1aa'},className:"my-2 cursor-pointer"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-mono text-zinc-300 font-bold"},subTabPitchVal>0?'+':'',subTabPitchVal,"st"),/*#__PURE__*/React.createElement("button",{onClick:()=>applySubTabEffect(st.id,'pitch',subTabPitchVal),className:"mt-1.5 px-1 py-1 bg-zinc-700 hover:bg-zinc-600 text-zinc-100 rounded text-[14px] font-bold w-full uppercase border border-zinc-650"},"Apply")),/*#__PURE__*/React.createElement("div",{className:"flex flex-col items-center bg-zinc-900/60 p-1.5 rounded border border-zinc-800 flex-1"},/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-bold text-zinc-400 uppercase tracking-tighter",title:"Gain Multiplier"},"Gain"),/*#__PURE__*/React.createElement("input",{type:"range",min:"0",max:"150",step:"1",value:subTabGainVal,onChange:e=>setSubTabGainVal(parseInt(e.target.value)),style:{writingMode:'vertical-lr',direction:'rtl',height:'120px',width:'20px',accentColor:'#a1a1aa'},className:"my-2 cursor-pointer"}),/*#__PURE__*/React.createElement("span",{className:"text-[14px] font-mono text-zinc-300 font-bold"},subTabGainVal,"%"),/*#__PURE__*/React.createElement("button",{onClick:()=>applySubTabEffect(st.id,'gain',subTabGainVal),className:"mt-1.5 px-1 py-1 bg-zinc-700 hover:bg-zinc-600 text-zinc-100 rounded text-[14px] font-bold w-full uppercase border border-zinc-650"},"Apply"))),/*#__PURE__*/React.createElement("div",{className:"flex-1"}),/*#__PURE__*/React.createElement("div",{className:"mt-auto pt-2.5 border-t border-zinc-800"},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[14px] text-zinc-500 mb-1 font-semibold uppercase"},/*#__PURE__*/React.createElement("span",null,"Duration:"),/*#__PURE__*/React.createElement("span",{className:"font-mono text-zinc-300"},st.buffer?formatTime(subTabDuration):'0s')),/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between text-[14px] text-zinc-500 mb-2.5 font-semibold uppercase"},/*#__PURE__*/React.createElement("span",null,"SR:"),/*#__PURE__*/React.createElement("span",{className:"font-mono text-zinc-300"},st.buffer?st.buffer.sampleRate:0," Hz")),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-1 mb-2"},/*#__PURE__*/React.createElement("button",{onClick:async e=>{subTabAiTrackIdRef.current=st.trackId;handleAIScan();},disabled:analysisState.isRunning,className:"py-1 bg-purple-900 hover:bg-purple-800 text-purple-100 font-bold rounded text-xs border border-purple-700 flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"map-pin",className:"w-3 h-3"}))," Scan"),/*#__PURE__*/React.createElement("button",{onClick:async e=>{subTabAiTrackIdRef.current=st.trackId;handleAICutToNewTrack();},disabled:analysisState.isRunning,className:"py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-xs flex items-center justify-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"scissors",className:"w-3 h-3"}))," Cut"),/*#__PURE__*/React.createElement("button",{onClick:async e=>{subTabAiTrackIdRef.current=st.trackId;if(st.buffer){setSelectionRangeOnBuffer(st.buffer,st.selectionStart||0,st.selectionEnd||st.buffer.duration);}handleAIAnalysicLoop();},disabled:analysisState.isRunning,className:"py-1 bg-violet-800 hover:bg-violet-700 text-violet-100 font-bold rounded text-xs border border-violet-600 flex items-center justify-center gap-1 col-span-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sparkles",className:"w-3 h-3"}))," AI Analysic Loop")),/*#__PURE__*/React.createElement("button",{onClick:()=>exportSubTabBuffer(st.id),className:"w-full py-1.5 mb-1.5 bg-blue-700 hover:bg-blue-600 text-white font-bold rounded text-[14px] flex items-center justify-center gap-1 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"download",className:"w-4 h-4"}))," Export"),/*#__PURE__*/React.createElement("button",{onClick:()=>applySubTab(st.id),className:"w-full py-1.5 bg-emerald-700 hover:bg-emerald-600 text-white font-bold rounded text-[14px] flex items-center justify-center gap-1 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"save",className:"w-4 h-4"}))," Save"))):/*#__PURE__*/React.createElement("div",{className:"flex-1 flex items-center justify-center text-xs text-zinc-500"},"Track not found")),/*#__PURE__*/React.createElement("div",{className:"w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10",onMouseDown:startTcpResize}),/*#__PURE__*/React.createElement("div",{ref:handleTimelineWrapperRef,className:"flex-1 relative z-10 overflow-x-auto overflow-y-auto min-w-0"},/*#__PURE__*/React.createElement("div",{style:{width:`${subTabTimelineWidth}px`},className:"relative flex flex-col min-h-full"},/*#__PURE__*/React.createElement("div",{className:"sticky top-0 z-30 bg-[#1a1a1a]"},/*#__PURE__*/React.createElement(TimelineRuler,{bpm:parseInt(bpm)||120,zoom:zoom,timelineWidth:subTabTimelineWidth,viewportWidth:viewportWidth,onPlayheadSet:setCurrentTime,snapValue:snapValue,onRulerMouseDown:e=>{setSelectionFollowsTempo(false);const wrapper=timelineWrapperRef.current;if(!wrapper)return;const rect=wrapper.getBoundingClientRect();const sl=wrapper.scrollLeft;const raw=Math.max(0,(e.clientX-rect.left+sl)/zoom);const t=snapValue!=='free'?snapTime(raw,snapValue,bpm):raw;if(e.ctrlKey){e.preventDefault();e.stopPropagation();setSubTabs(prev=>prev.map(s=>s.id===activeTab?{...s,selectionStart:null,selectionEnd:null}:s));return;}if(e.shiftKey){e.preventDefault();e.stopPropagation();}handlePlayheadSet(t);subTabDragStartRef.current=t;isDraggingSubTabRef.current=true;},scrollLeft:scrollLeft,canvasRedrawCount:canvasRedrawCount})),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex flex-col relative bg-[#111111] min-h-full"},vTrack&&/*#__PURE__*/React.createElement("div",{style:{height:`${subTabHeight}px`},className:"relative overflow-hidden border-2 border-purple-500/40 rounded bg-[#151515] mx-1 mt-1 shrink-0"},/*#__PURE__*/React.createElement(SubTabWaveform,{buffer:st.buffer,subTabId:st.id,activeTab:activeTab,activeTool:activeTool,currentTime:st.currentTime,selectionStart:st.selectionStart,selectionEnd:st.selectionEnd,onSelectRange:(start,end)=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,selectionStart:start,selectionEnd:end}:s)),onPlayheadSet:time=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,currentTime:time}:s)),onContextMenu:(e,clickTime)=>setContextMenu({x:e.clientX,y:e.clientY,isSubTab:true,subTabId:st.id,time:clickTime}),zoom:zoom,timelineWidth:subTabTimelineWidth,color:vTrack.color,name:vTrack.name,speed:st.speed||1.0,volumeNodes:st.volumeNodes||[],panningNodes:st.panningNodes||[],fadeInLen:st.fadeInLen||0,fadeOutLen:st.fadeOutLen||0,graphMode:st.graphMode,channelInfo:st.channelInfo,selectedNodeTime:subTabSelectedNodeTime,setSelectedNodeTime:setSubTabSelectedNodeTime,onUpdateNodes:nodes=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,[s.graphMode==='pan'?'panningNodes':'volumeNodes']:nodes}:s)),onUpdateFade:fade=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,fadeInLen:fade.fadeInLen??s.fadeInLen,fadeOutLen:fade.fadeOutLen??s.fadeOutLen}:s)),onModeToggle:()=>setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,graphMode:s.graphMode==='pan'?null:'pan'}:s)),onSpeedChange:newSpeed=>{setSubTabs(prev=>prev.map(s=>{if(s.id!==st.id)return s;const oldSpeed=s.speed||1.0;const ratio=oldSpeed/newSpeed;const newVolumeNodes=(s.volumeNodes||[]).map(n=>({...n,time:n.time*ratio}));const newPanningNodes=(s.panningNodes||[]).map(n=>({...n,time:n.time*ratio}));return{...s,speed:newSpeed,volumeNodes:newVolumeNodes,panningNodes:newPanningNodes,fadeInLen:(s.fadeInLen||0)*ratio,fadeOutLen:(s.fadeOutLen||0)*ratio,currentTime:(s.currentTime||0)*ratio,label:s.label.replace(/\s\(\d+%\)$/,'')+` (${Math.round(newSpeed*100)}%)`};}));const n=activeTrackNodesRef.current[st.trackId];if(n&&n.source)n.source.playbackRate.value=newSpeed;// Reset time refs to prevent playhead jump when speed changes mid-playback -const ctx=getAudioContext();const elapsed=ctx.currentTime-startAudioTimeRef.current;const oldSpeed=activePlaybackSpeedRef.current;const ratio=oldSpeed/newSpeed;startBufferOffsetRef.current=startBufferOffsetRef.current+elapsed*oldSpeed;startOffsetTimeRef.current=(startOffsetTimeRef.current+elapsed)*ratio;startAudioTimeRef.current=ctx.currentTime;activePlaybackSpeedRef.current=newSpeed;}}),/*#__PURE__*/React.createElement("div",{onMouseDown:handleSubTabResizeMouseDown,className:"absolute bottom-0 left-0 right-0 h-1.5 cursor-ns-resize z-30 hover:bg-purple-500/50 transition-colors"})),st.selectionStart!==null&&st.selectionEnd!==null&&st.selectionEnd>st.selectionStart&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 border-l border-r border-amber-500 bg-amber-500/10 z-20 pointer-events-none",style:{left:`${Math.min(st.selectionStart,st.selectionEnd)*zoom}px`,width:`${Math.abs(st.selectionEnd-st.selectionStart)*zoom}px`}})))));})())),/*#__PURE__*/React.createElement("div",{className:"w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10",onMouseDown:startColResize}),renderDock('right','Right')),renderDock('bottom','Bottom'),showExportPanel&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[300] bg-black/70 backdrop-blur-sm flex items-center justify-center p-6",onClick:()=>setShowExportPanel(false)},/*#__PURE__*/React.createElement("div",{className:"w-full max-w-2xl bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl overflow-hidden",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"h-9 bg-slate-800/80 border-b border-slate-700 flex items-center justify-between px-3 select-none"},/*#__PURE__*/React.createElement("span",{className:"text-xs font-bold text-cyan-300 font-mono flex items-center gap-1.5"},/*#__PURE__*/React.createElement("i",{"data-lucide":"download-cloud",className:"w-3.5 h-3.5"})," EXPORT"),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowExportPanel(false),className:"text-zinc-400 hover:text-white"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-4 h-4"}))),/*#__PURE__*/React.createElement("div",{className:"p-4 max-h-[72vh] overflow-y-auto"},renderPanelContent('export')))),showMixer&&/*#__PURE__*/React.createElement("div",{className:"flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none",style:{height:mixerHeight+'px'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between px-2 py-0.5 bg-zinc-800/80 shrink-0 cursor-ns-resize",onMouseDown:e=>{e.preventDefault();var startY=e.clientY;var startH=mixerHeight;var onMove=function(ev){var newH=Math.max(80,Math.min(400,startH-(ev.clientY-startY)));setMixerHeight(newH);localStorage.setItem('studio_mixer_height',newH.toString());};var onUp=function(){document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-horizontal",className:"w-3 h-3 text-zinc-600"})),/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-zinc-400 uppercase tracking-wider"},"MIXER")),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowMixer(false),className:"p-0.5 rounded text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-x-auto p-1.5 gap-1.5 items-stretch"},/*#__PURE__*/React.createElement(MasterStripConsole,{masterVolume:masterVolume,setMasterVolume:setMasterVolume,showMasteringModal:showMasteringModal,setShowMasteringModal:setShowMasteringModal,masteringSettings:masteringSettings,setMasteringSettings:setMasteringSettings,isPlaying:isPlaying}),activeTracks.length>0&&/*#__PURE__*/React.createElement("div",{className:"w-px bg-zinc-700 shrink-0 self-stretch mx-0.5"}),activeTracks.map(function(track,idx){return/*#__PURE__*/React.createElement(TrackStripConsole,{key:track.id,track:track,index:idx,onUpdateTrack:updateTrackProp,trackVuRefs:trackVuRefs});}))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none",style:{height:mediaExplorerPanelHeight+'px',display:showMediaExplorer?'flex':'none'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between px-2 py-0.5 bg-zinc-800/80 shrink-0 cursor-ns-resize",onMouseDown:e=>{e.preventDefault();var startY=e.clientY;var startH=mediaExplorerPanelHeight;var onMove=function(ev){var newH=Math.max(120,Math.min(520,startH-(ev.clientY-startY)));setMediaExplorerPanelHeight(newH);localStorage.setItem('studio_media_explorer_height',newH.toString());};var onUp=function(){document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-horizontal",className:"w-3 h-3 text-zinc-600"})),/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-emerald-400 uppercase tracking-wider"},"Media Explorer (F6)")),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowMediaExplorer(false),className:"p-0.5 rounded text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 min-h-0 overflow-hidden bg-[#262626]"},/*#__PURE__*/React.createElement(MediaExplorerPanel,{height:mediaExplorerPanelHeight,clipboardRef:clipboardRef}))));})(),/*#__PURE__*/React.createElement("div",{className:"h-6 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-xs text-zinc-500 select-none shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-4"},/*#__PURE__*/React.createElement("span",null,"Status: ",isPlaying?'Playing':'Stopped'),activeTab!=='main'&&/*#__PURE__*/React.createElement("span",{className:"text-amber-400 font-semibold uppercase"},"Sub-Tab"),activeTab==='main'&&/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-semibold uppercase"},"Track: ID ",selectedTrackId),selectionMode==='local'&&/*#__PURE__*/React.createElement("span",{className:"text-amber-400 font-semibold uppercase text-xs"},"Local Sel"),selectionMode==='global'&&/*#__PURE__*/React.createElement("span",{className:"text-purple-400 font-semibold uppercase text-xs"},"Global Sel"),hasAnySolo&&/*#__PURE__*/React.createElement("span",{className:"text-amber-500 font-semibold"},"Solo: ",tracks.filter(t=>t.solo).length," track(s)"),isLoopingSelection&&selectionMode==='local'&&/*#__PURE__*/React.createElement("span",{className:"text-emerald-400 font-semibold uppercase text-xs"},"Solo Loop"),isLoopingSelection&&selectionMode!=='local'&&/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-semibold uppercase text-xs"},"Master Loop")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>setShowExportPanel(p=>!p),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showExportPanel?'bg-cyan-900 text-cyan-300':'text-zinc-500 hover:text-zinc-300'}`,title:`Export (floating modal)`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"save",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"text-[7px] opacity-60"},showExportPanel?panelPositions.export[0].toUpperCase():'')),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowAIPanel(p=>!p),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showAIPanel?'bg-purple-900 text-purple-300':'text-zinc-500 hover:text-zinc-300'}`,title:`AI Panel (${panelPositions.ai})`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"text-[7px] opacity-60"},showAIPanel?panelPositions.ai[0].toUpperCase():'')),/*#__PURE__*/React.createElement("button",{onClick:()=>window.__toggleMixerRef(),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMixer?'bg-indigo-900 text-indigo-300':'text-zinc-500 hover:text-zinc-300'}`,title:"Mixer Panel (F7)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders-horizontal",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>window.__toggleMediaExplorerRef(),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMediaExplorer?'bg-emerald-900 text-emerald-300':'text-zinc-500 hover:text-zinc-300'}`,title:"Media Explorer Panel (F6)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"folder-open",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("span",{className:"w-[1px] h-3 bg-zinc-800 mx-1"}),/*#__PURE__*/React.createElement("span",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"info",className:"w-3 h-3 text-zinc-600"}))," Scroll: Zoom"),/*#__PURE__*/React.createElement("span",null,"|"),/*#__PURE__*/React.createElement("span",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"keyboard",className:"w-3 h-3 text-zinc-600"}))," Ctrl+Scroll: Playhead"))),contextMenu&&(contextMenu.isSubTab?/*#__PURE__*/React.createElement("div",{className:"fixed z-[60] bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 overflow-y-auto",style:{left:Math.min(contextMenu.x,window.innerWidth-260),top:contextMenu.y+200>window.innerHeight?undefined:contextMenu.y,bottom:contextMenu.y+200>window.innerHeight?window.innerHeight-contextMenu.y:undefined,maxHeight:'60vh'},onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"px-3 py-1 text-xs text-zinc-500 font-bold uppercase border-b border-zinc-800 pb-1 mb-1"},"Selection: ",formatTime(subTabs.find(s=>s.id===contextMenu.subTabId)?.selectionStart||0)," - ",formatTime(subTabs.find(s=>s.id===contextMenu.subTabId)?.selectionEnd||0)),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabCut(contextMenu.subTabId);closeContextMenu();},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":"scissors",className:"w-3.5 h-3.5 text-rose-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Cut"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+X")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabCopy(contextMenu.subTabId);closeContextMenu();},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":"copy",className:"w-3.5 h-3.5 text-zinc-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Copy"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+C")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabPaste(contextMenu.subTabId);closeContextMenu();},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":"clipboard",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Paste"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+V")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabDelete(contextMenu.subTabId);closeContextMenu();},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":"trash-2",className:"w-3.5 h-3.5 text-red-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Delete Selected Segment"),/*#__PURE__*/React.createElement("span",{className:"text-amber-400 text-xs font-semibold font-mono ml-auto"},"Del")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabLoop(contextMenu.subTabId,4);closeContextMenu();},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":"repeat",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Loop Selection 4 times"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+L"))):/*#__PURE__*/React.createElement("div",{className:"fixed z-[60] bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 overflow-y-auto",style:{left:Math.min(contextMenu.x,window.innerWidth-260),top:contextMenu.y+260>window.innerHeight?undefined:contextMenu.y,bottom:contextMenu.y+260>window.innerHeight?window.innerHeight-contextMenu.y:undefined,maxHeight:'60vh'},onClick:e=>e.stopPropagation()},contextMenu.sectionId?/*#__PURE__*/React.createElement("button",{onClick:contextMenuEditSection,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":"file-edit",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Edit Section"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+E")):/*#__PURE__*/React.createElement("button",{onClick:contextMenuEdit,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":"file-edit",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Edit"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+E")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuSplit,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":"scissors",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Split"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"S")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuMerge,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":"combine",className:"w-3.5 h-3.5 text-purple-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Merge"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+M")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("div",{className:"px-3 py-0.5 text-[10px] text-zinc-500 font-bold uppercase tracking-wider"},"Insert"),!sessionTabs.some(s=>s.id===activeTab)&&/*#__PURE__*/React.createElement("button",{onClick:()=>{insertSectionAtPlayhead();closeContextMenu();},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":"folder-plus",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Section")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertMidiItemAtPlayhead();closeContextMenu();},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":"music",className:"w-3.5 h-3.5 text-purple-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert MIDI Item")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertSoundClipAtCursor();closeContextMenu();},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":"file-audio",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Sound Clip")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertTrackBelow();closeContextMenu();},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":"plus-square",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Track")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:contextMenuCopy,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":"copy",className:"w-3.5 h-3.5 text-zinc-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Copy"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+C")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuCut,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":"scissors",className:"w-3.5 h-3.5 text-rose-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Cut"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+X")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuPaste,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":"clipboard",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Paste"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+V")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:contextMenuDelete,className:"w-full px-3 py-1.5 text-xs text-red-300 hover:bg-red-950 text-left flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"trash-2",className:"w-3.5 h-3.5 text-red-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Delete"),/*#__PURE__*/React.createElement("span",{className:"text-amber-400 text-xs font-semibold font-mono ml-auto pl-8"},"Del")))),toastMessage&&/*#__PURE__*/React.createElement("div",{className:"absolute top-14 right-4 z-50 px-4 py-2.5 rounded shadow-lg text-xs font-semibold flex items-center gap-2 border bg-zinc-900 text-zinc-100 border-zinc-800"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"bell",className:`w-4 h-4 ${toastMessage.type==='success'?'text-emerald-400':toastMessage.type==='error'?'text-rose-400':toastMessage.type==='warning'?'text-amber-400':'text-cyan-400'}`})),toastMessage.text,toastMessage.onActionClick&&/*#__PURE__*/React.createElement("button",{onClick:()=>{toastMessage.onActionClick();setToastMessage(null);},className:"ml-2 px-2 py-0.5 bg-purple-700 hover:bg-purple-600 text-white rounded text-[10px] uppercase font-bold"},toastMessage.actionText||'Tải về'))),appWarningModal&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm"},/*#__PURE__*/React.createElement("div",{className:"bg-[#222222] border border-zinc-800 rounded-lg shadow-2xl p-6 max-w-md w-full text-slate-200"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 mb-3 text-cyan-400 font-bold"},/*#__PURE__*/React.createElement("h3",{className:"text-base font-bold"},appWarningModal.title)),/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-300 mb-5 leading-relaxed whitespace-pre-line"},appWarningModal.message),/*#__PURE__*/React.createElement("div",{className:"flex justify-end gap-2"},appWarningModal.isAlert?/*#__PURE__*/React.createElement("button",{onClick:()=>setAppWarningModal(null),className:"px-4 py-2 bg-cyan-600 hover:bg-cyan-500 active:bg-cyan-700 text-white rounded text-xs font-semibold transition"},"Đóng"):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("button",{onClick:()=>setAppWarningModal(null),className:"px-4 py-2 bg-zinc-700 hover:bg-zinc-600 active:bg-zinc-800 text-slate-300 rounded text-xs font-semibold transition"},"Hủy"),/*#__PURE__*/React.createElement("button",{onClick:()=>{const fn=appWarningModal.onConfirm;setAppWarningModal(null);if(fn)fn();},className:"px-4 py-2 bg-cyan-600 hover:bg-cyan-500 active:bg-cyan-700 text-white rounded text-xs font-semibold transition"},"Xác nhận"))))),tabContextMenu&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[190]",onClick:()=>setTabContextMenu(null),onContextMenu:e=>{e.preventDefault();setTabContextMenu(null);}}),/*#__PURE__*/React.createElement("div",{className:"fixed bg-zinc-900 border border-zinc-800 rounded-lg shadow-2xl p-2 z-[200] flex gap-1.5",style:{top:tabContextMenu.y,left:tabContextMenu.x},onClick:e=>e.stopPropagation()},['#f43f5e','#f59e0b','#10b981','#06b6d4','#8b5cf6','#64748b'].map(color=>/*#__PURE__*/React.createElement("button",{key:color,className:"w-5 h-5 rounded-full border border-zinc-700 hover:scale-110 active:scale-95 transition",style:{backgroundColor:color},onClick:()=>{handleSetTabColor(tabContextMenu.tabId,tabContextMenu.tabType,color);setTabContextMenu(null);}})))),/*#__PURE__*/React.createElement(AuthModal,{isOpen:authModalOpen,mode:authMode,forceMandatory:isMandatoryLogin,onClose:()=>setAuthModalOpen(false),onSuccess:handleAuthSuccess}),/*#__PURE__*/React.createElement(ProfileModal,{isOpen:profileModalOpen,onClose:()=>setProfileModalOpen(false),tracks:tracks,setTracks:setTracks,setSelectedTrackId:setSelectedTrackId,projectName:projectName,setProjectName:setProjectName,currentProjectId:currentProjectId,setCurrentProjectId:setCurrentProjectId,showToast:showToast,loadAudioBuffersForTracks:loadAudioBuffersForTracks,setAppWarningModal:setAppWarningModal,bpm:bpm,setBpm:setBpm,setMasteringSettings:setMasteringSettings,setSessionTabs:setSessionTabs,setSubTabs:setSubTabs,trackMidiChannelsRef:trackMidiChannelsRef}),/*#__PURE__*/React.createElement(SaveProjectModal,{isOpen:saveProjectModalOpen,onClose:()=>setSaveProjectModalOpen(false),projectName:projectName,onSaveCloud:(newName,existingId)=>{handleSaveCloudProject(newName,existingId);},onSaveLocal:newName=>{handleSaveLocalProject(newName);}}),/*#__PURE__*/React.createElement(SaveAsModal,{isOpen:saveAsModalOpen,onClose:()=>setSaveAsModalOpen(false),projectName:projectName,onSaveCloud:newName=>{handleSaveAsCloud(newName);},onSaveLocal:newName=>{handleExportSFS(newName);setProjectName(newName);localStorage.setItem('sonic_project_name',newName);}}),/*#__PURE__*/React.createElement(OpenProjectModal,{isOpen:openProjectModalOpen,onClose:()=>setOpenProjectModalOpen(false),onOpenCloud:(projId,projName)=>{setOpenProjectModalOpen(false);handleOpenProject(projId,projName);},onOpenLocal:()=>{setOpenProjectModalOpen(false);handleImportSFS();}}),/*#__PURE__*/React.createElement(AIConfigModal,{isOpen:aiConfigModalOpen,onClose:()=>setAiConfigModalOpen(false),onConfigSaved:providers=>{setAiProviders(providers);const active=providers.find(p=>p.is_active)||providers[0];if(active)setSelectedProviderId(active.id);}}),/*#__PURE__*/React.createElement(SystemManagerModal,{isOpen:systemManagerModalOpen,onClose:()=>setSystemManagerModalOpen(false)}),/*#__PURE__*/React.createElement(PluginManagerModal,{isOpen:pluginManagerModalOpen,onClose:()=>setPluginManagerModalOpen(false),pluginsData:pluginsData}),/*#__PURE__*/React.createElement(AIPresetModal,{isOpen:aiPresetModalOpen,onClose:()=>{setAiPresetModalOpen(false);setAiPresetVersion(v=>v+1);}}),/*#__PURE__*/React.createElement(MasteringModal,{isOpen:showMasteringModal,onClose:()=>setShowMasteringModal(false),masteringSettings:masteringSettings,setMasteringSettings:setMasteringSettings}),fxRackTarget&&/*#__PURE__*/React.createElement(FXRackModal,{track:tracks.find(t=>t.id===fxRackTarget.trackId)||null,onUpdateTrack:updateTrackProp,onClose:()=>setFxRackTarget(null)}),instrumentSelectorTrackId&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:closeInstrumentSelector},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-4xl p-5 text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center pb-3 border-b border-[#383838]"},/*#__PURE__*/React.createElement("h3",{className:"text-sm font-bold text-amber-400"},"Select Instrument"),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("select",{value:instrumentSelectorTrackId||'',onChange:e=>{var tid=e.target.value;if(tid){openInstrumentSelector(tid);}},className:"bg-[#1e1e1e] text-zinc-300 border border-zinc-700 rounded px-2 py-1 text-xs font-mono cursor-pointer"},activeTracks.map(function(at){return/*#__PURE__*/React.createElement("option",{key:at.id,value:at.id},at.name+' ('+(at.instrumentName||'Synth')+')');})),/*#__PURE__*/React.createElement("button",{onClick:closeInstrumentSelector,className:"text-slate-400 hover:text-slate-200"},"\u2715"))),/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"T\u00ecm nh\u1ea1c c\u1ee5...",value:sfPresetSearchQuery,onChange:e=>setSfPresetSearchQuery(e.target.value),autoFocus:true,className:"w-full bg-black border border-zinc-700 rounded px-2 py-1.5 outline-none my-3 text-sm"}),/*#__PURE__*/React.createElement("div",{className:"flex gap-4",style:{height:"420px"}},/*#__PURE__*/React.createElement("div",{className:"w-1/3 border-r border-[#383838] overflow-y-auto space-y-0.5 pr-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>{setSelectedSoundFontId(null);setSynthCategory(null);},className:"w-full text-left px-3 py-2 text-sm rounded "+(!selectedSoundFontId?"bg-amber-700 text-white":"bg-zinc-800 hover:bg-zinc-700 text-zinc-300")},"All Instruments"),(instrumentSelectorData?.soundfonts||[]).map(sf=>{const sfId=sf.id.startsWith('sf_')?sf.id:'sf_'+sf.id;const sfName=sf.display||sf.name||sf.id;return/*#__PURE__*/React.createElement("button",{key:sfId,onClick:()=>{setSelectedSoundFontId(sfId);setSfPresets(null);const baseId=sfId.replace('sf_','');window.SonicAPI.listSoundfontInstruments(baseId).then(data=>{if(data&&data.presets){const mapping=data.presets.map(p=>({...p,_sfId:sfId,_sfName:sfName,_sfDisplay:sfName.substring(0,30)}));setSfPresets(mapping);setInstrumentSelectorData(prev=>prev?{...prev,soundfonts:(prev.soundfonts||[]).map(s=>s.id===sf.id?{...s,presets:data.presets}:s)}:prev);}else{setSfPresets([]);}}).catch(()=>{setSfPresets([]);});},className:"w-full text-left px-3 py-2 text-sm rounded "+(selectedSoundFontId===sfId?"bg-amber-700 text-white":"bg-zinc-800 hover:bg-zinc-700 text-zinc-300")},sfName);})),/*#__PURE__*/React.createElement("div",{className:"flex-1 overflow-y-auto space-y-0.5"},/*#__PURE__*/React.createElement("button",{onClick:()=>setTrackInstrumentWithUndo(instrumentSelectorTrackId,null),className:"w-full text-left px-3 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None (Default Synth)"),sfPresets===null?(/*#__PURE__*/React.createElement("p",{className:"text-sm text-zinc-500 py-2"},"Loading instruments...")):sfPresets.length>0?sfPresets.filter(p=>!selectedSoundFontId||p._sfId===selectedSoundFontId).filter(p=>!sfPresetSearchQuery||(p.name||'').toLowerCase().includes(sfPresetSearchQuery.toLowerCase())||(p._sfName||'').toLowerCase().includes(sfPresetSearchQuery.toLowerCase())).map((p,i)=>/*#__PURE__*/React.createElement("button",{key:i,onClick:()=>setTrackInstrumentWithUndo(instrumentSelectorTrackId,p._sfId,p.name||'Preset '+p.program,p.bank,p.program),onDoubleClick:()=>{setTrackInstrumentWithUndo(instrumentSelectorTrackId,p._sfId,p.name||'Preset '+p.program,p.bank,p.program);closeInstrumentSelector();},className:"w-full text-left px-3 py-2 text-sm rounded bg-zinc-800/60 hover:bg-amber-800 text-zinc-300 truncate flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 shrink-0"},p._sfDisplay),p.bank===128?/*#__PURE__*/React.createElement("span",{className:"mr-1"},"🥁"):null,p.name||'Preset '+p.program)):(/*#__PURE__*/React.createElement("p",{className:"text-sm text-zinc-500 py-2"},sfPresetSearchQuery?"Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 ph\u00f9 h\u1ee3p.":"No presets found.")))))),fxSelectorTrackId&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:()=>setFxSelectorTrackId(null)},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-64 p-4 text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("h3",{className:"text-sm font-bold text-purple-400 mb-3"},"Track FX"),/*#__PURE__*/React.createElement("div",{className:"space-y-1"},/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,null),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None"),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,'chorus'),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-cyan-800 text-cyan-300"},"Chorus"),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,'reverb'),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-purple-800 text-purple-300"},"Reverb")))),instrumentDropdownTrackId&&instrumentDropdownBtnRect&&/*#__PURE__*/React.createElement("div",{"data-instr-dropdown":"",className:"fixed z-[100] bg-[#1e1e1e] border border-zinc-700 rounded shadow-xl w-[220px] max-h-[300px] flex flex-col",style:{top:instrumentDropdownBtnRect.bottom+4,left:Math.max(4,Math.min(instrumentDropdownBtnRect.left,window.innerWidth-224))}},/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"T\u00ecm nh\u1ea1c c\u1ee5...",value:instrumentSearchQuery,onChange:e=>setInstrumentSearchQuery(e.target.value),autoFocus:true,className:"w-full bg-black border-b border-zinc-700 px-2 py-1.5 text-xs outline-none"}),/*#__PURE__*/React.createElement("div",{className:"flex-1 overflow-y-auto"},/*#__PURE__*/React.createElement("button",{onClick:()=>setTrackInstrumentWithUndo(instrumentDropdownTrackId,null),className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None (Default Synth)"),filteredInstruments.soundfonts.length>0&&/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-zinc-500 px-3 py-1 mt-1 uppercase font-bold"},"SoundFonts"),filteredInstruments.soundfonts.map((sf,i)=>/*#__PURE__*/React.createElement("button",{key:"sfd_"+i,onClick:()=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setTrackInstrumentWithUndo(instrumentDropdownTrackId,"sf_"+sf.id,sf.display||sf.name||sf.id,undefined,undefined);},className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-amber-800 text-zinc-200 flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"truncate"},sf.display||sf.name||sf.id),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-amber-400 shrink-0 ml-1"},"SF"))),filteredInstruments.vst.length>0&&/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-zinc-500 px-3 py-1 mt-1 uppercase font-bold"},"VST Instruments"),filteredInstruments.vst.map((v,i)=>/*#__PURE__*/React.createElement("button",{key:"vstd_"+i,onClick:()=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setTrackInstrumentWithUndo(instrumentDropdownTrackId,v.id,v.name||v.id);},className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-violet-800 text-zinc-200 flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"truncate"},v.name||v.id),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-cyan-400 shrink-0 ml-1"},v.type||"VST"))),!filteredInstruments.soundfonts.length&&!filteredInstruments.vst.length&&/*#__PURE__*/React.createElement("p",{className:"text-xs text-zinc-500 py-4 text-center"},"Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 n\u00e0o"))));};const root=ReactDOM.createRoot(document.getElementById('root'));root.render(/*#__PURE__*/React.createElement(App,null));setTimeout(()=>lucide.createIcons(),300); +const ctx=getAudioContext();const elapsed=ctx.currentTime-startAudioTimeRef.current;const oldSpeed=activePlaybackSpeedRef.current;const ratio=oldSpeed/newSpeed;startBufferOffsetRef.current=startBufferOffsetRef.current+elapsed*oldSpeed;startOffsetTimeRef.current=(startOffsetTimeRef.current+elapsed)*ratio;startAudioTimeRef.current=ctx.currentTime;activePlaybackSpeedRef.current=newSpeed;}}),/*#__PURE__*/React.createElement("div",{onMouseDown:handleSubTabResizeMouseDown,className:"absolute bottom-0 left-0 right-0 h-1.5 cursor-ns-resize z-30 hover:bg-purple-500/50 transition-colors"})),st.selectionStart!==null&&st.selectionEnd!==null&&st.selectionEnd>st.selectionStart&&/*#__PURE__*/React.createElement("div",{className:"absolute top-0 bottom-0 border-l border-r border-amber-500 bg-amber-500/10 z-20 pointer-events-none",style:{left:`${Math.min(st.selectionStart,st.selectionEnd)*zoom}px`,width:`${Math.abs(st.selectionEnd-st.selectionStart)*zoom}px`}})))));})())),/*#__PURE__*/React.createElement("div",{className:"w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10",onMouseDown:startColResize}),renderDock('right','Right')),renderDock('bottom','Bottom'),showMixer&&/*#__PURE__*/React.createElement("div",{className:"flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none",style:{height:mixerHeight+'px'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between px-2 py-0.5 bg-zinc-800/80 shrink-0 cursor-ns-resize",onMouseDown:e=>{e.preventDefault();var startY=e.clientY;var startH=mixerHeight;var onMove=function(ev){var newH=Math.max(80,Math.min(400,startH-(ev.clientY-startY)));setMixerHeight(newH);localStorage.setItem('studio_mixer_height',newH.toString());};var onUp=function(){document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-horizontal",className:"w-3 h-3 text-zinc-600"})),/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-zinc-400 uppercase tracking-wider"},"MIXER")),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowMixer(false),className:"p-0.5 rounded text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 flex overflow-x-auto p-1.5 gap-1.5 items-stretch"},/*#__PURE__*/React.createElement(MasterStripConsole,{masterVolume:masterVolume,setMasterVolume:setMasterVolume,showMasteringModal:showMasteringModal,setShowMasteringModal:setShowMasteringModal,masteringSettings:masteringSettings,setMasteringSettings:setMasteringSettings,isPlaying:isPlaying}),activeTracks.length>0&&/*#__PURE__*/React.createElement("div",{className:"w-px bg-zinc-700 shrink-0 self-stretch mx-0.5"}),activeTracks.map(function(track,idx){return/*#__PURE__*/React.createElement(TrackStripConsole,{key:track.id,track:track,index:idx,onUpdateTrack:updateTrackProp,trackVuRefs:trackVuRefs});}))),/*#__PURE__*/React.createElement("div",{className:"flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none",style:{height:mediaExplorerPanelHeight+'px',display:showMediaExplorer?'flex':'none'}},/*#__PURE__*/React.createElement("div",{className:"flex items-center justify-between px-2 py-0.5 bg-zinc-800/80 shrink-0 cursor-ns-resize",onMouseDown:e=>{e.preventDefault();var startY=e.clientY;var startH=mediaExplorerPanelHeight;var onMove=function(ev){var newH=Math.max(120,Math.min(520,startH-(ev.clientY-startY)));setMediaExplorerPanelHeight(newH);localStorage.setItem('studio_media_explorer_height',newH.toString());};var onUp=function(){document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);}},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"grip-horizontal",className:"w-3 h-3 text-zinc-600"})),/*#__PURE__*/React.createElement("span",{className:"text-[10px] font-bold text-emerald-400 uppercase tracking-wider"},"Media Explorer (F6)")),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowMediaExplorer(false),className:"p-0.5 rounded text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700 transition"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"x",className:"w-3 h-3"})))),/*#__PURE__*/React.createElement("div",{className:"flex-1 min-h-0 overflow-hidden bg-[#262626]"},/*#__PURE__*/React.createElement(MediaExplorerPanel,{height:mediaExplorerPanelHeight,clipboardRef:clipboardRef}))));})(),/*#__PURE__*/React.createElement("div",{className:"h-6 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-xs text-zinc-500 select-none shrink-0"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-4"},/*#__PURE__*/React.createElement("span",null,"Status: ",isPlaying?'Playing':'Stopped'),activeTab!=='main'&&/*#__PURE__*/React.createElement("span",{className:"text-amber-400 font-semibold uppercase"},"Sub-Tab"),activeTab==='main'&&/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-semibold uppercase"},"Track: ID ",selectedTrackId),selectionMode==='local'&&/*#__PURE__*/React.createElement("span",{className:"text-amber-400 font-semibold uppercase text-xs"},"Local Sel"),selectionMode==='global'&&/*#__PURE__*/React.createElement("span",{className:"text-purple-400 font-semibold uppercase text-xs"},"Global Sel"),hasAnySolo&&/*#__PURE__*/React.createElement("span",{className:"text-amber-500 font-semibold"},"Solo: ",tracks.filter(t=>t.solo).length," track(s)"),isLoopingSelection&&selectionMode==='local'&&/*#__PURE__*/React.createElement("span",{className:"text-emerald-400 font-semibold uppercase text-xs"},"Solo Loop"),isLoopingSelection&&selectionMode!=='local'&&/*#__PURE__*/React.createElement("span",{className:"text-cyan-400 font-semibold uppercase text-xs"},"Master Loop")),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>setShowExportPanel(p=>!p),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showExportPanel?'bg-cyan-900 text-cyan-300':'text-zinc-500 hover:text-zinc-300'}`,title:`Export (floating modal)`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"save",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"text-[7px] opacity-60"},showExportPanel?panelPositions.export[0].toUpperCase():'')),/*#__PURE__*/React.createElement("button",{onClick:()=>setShowAIPanel(p=>!p),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showAIPanel?'bg-purple-900 text-purple-300':'text-zinc-500 hover:text-zinc-300'}`,title:`AI Panel (${panelPositions.ai})`},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"cpu",className:"w-3 h-3"})),/*#__PURE__*/React.createElement("span",{className:"text-[7px] opacity-60"},showAIPanel?panelPositions.ai[0].toUpperCase():'')),/*#__PURE__*/React.createElement("button",{onClick:()=>window.__toggleMixerRef(),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMixer?'bg-indigo-900 text-indigo-300':'text-zinc-500 hover:text-zinc-300'}`,title:"Mixer Panel (F7)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"sliders-horizontal",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("button",{onClick:()=>window.__toggleMediaExplorerRef(),className:`px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMediaExplorer?'bg-emerald-900 text-emerald-300':'text-zinc-500 hover:text-zinc-300'}`,title:"Media Explorer Panel (F6)"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"folder-open",className:"w-3 h-3"}))),/*#__PURE__*/React.createElement("span",{className:"w-[1px] h-3 bg-zinc-800 mx-1"}),/*#__PURE__*/React.createElement("span",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"info",className:"w-3 h-3 text-zinc-600"}))," Scroll: Zoom"),/*#__PURE__*/React.createElement("span",null,"|"),/*#__PURE__*/React.createElement("span",{className:"flex items-center gap-1"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"keyboard",className:"w-3 h-3 text-zinc-600"}))," Ctrl+Scroll: Playhead"))),contextMenu&&(contextMenu.isSubTab?/*#__PURE__*/React.createElement("div",{className:"fixed z-[60] bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 overflow-y-auto",style:{left:Math.min(contextMenu.x,window.innerWidth-260),top:contextMenu.y+200>window.innerHeight?undefined:contextMenu.y,bottom:contextMenu.y+200>window.innerHeight?window.innerHeight-contextMenu.y:undefined,maxHeight:'60vh'},onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"px-3 py-1 text-xs text-zinc-500 font-bold uppercase border-b border-zinc-800 pb-1 mb-1"},"Selection: ",formatTime(subTabs.find(s=>s.id===contextMenu.subTabId)?.selectionStart||0)," - ",formatTime(subTabs.find(s=>s.id===contextMenu.subTabId)?.selectionEnd||0)),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabCut(contextMenu.subTabId);closeContextMenu();},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":"scissors",className:"w-3.5 h-3.5 text-rose-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Cut"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+X")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabCopy(contextMenu.subTabId);closeContextMenu();},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":"copy",className:"w-3.5 h-3.5 text-zinc-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Copy"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+C")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabPaste(contextMenu.subTabId);closeContextMenu();},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":"clipboard",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Paste"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+V")),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabDelete(contextMenu.subTabId);closeContextMenu();},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":"trash-2",className:"w-3.5 h-3.5 text-red-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Delete Selected Segment"),/*#__PURE__*/React.createElement("span",{className:"text-amber-400 text-xs font-semibold font-mono ml-auto"},"Del")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:()=>{handleSubTabLoop(contextMenu.subTabId,4);closeContextMenu();},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":"repeat",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Loop Selection 4 times"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto"},"Ctrl+L"))):/*#__PURE__*/React.createElement("div",{className:"fixed z-[60] bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 overflow-y-auto",style:{left:Math.min(contextMenu.x,window.innerWidth-260),top:contextMenu.y+260>window.innerHeight?undefined:contextMenu.y,bottom:contextMenu.y+260>window.innerHeight?window.innerHeight-contextMenu.y:undefined,maxHeight:'60vh'},onClick:e=>e.stopPropagation()},contextMenu.sectionId?/*#__PURE__*/React.createElement("button",{onClick:contextMenuEditSection,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":"file-edit",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Edit Section"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+E")):/*#__PURE__*/React.createElement("button",{onClick:contextMenuEdit,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":"file-edit",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Edit"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+E")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuSplit,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":"scissors",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Split"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"S")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuMerge,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":"combine",className:"w-3.5 h-3.5 text-purple-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Merge"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+M")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("div",{className:"px-3 py-0.5 text-[10px] text-zinc-500 font-bold uppercase tracking-wider"},"Insert"),!sessionTabs.some(s=>s.id===activeTab)&&/*#__PURE__*/React.createElement("button",{onClick:()=>{insertSectionAtPlayhead();closeContextMenu();},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":"folder-plus",className:"w-3.5 h-3.5 text-amber-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Section")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertMidiItemAtPlayhead();closeContextMenu();},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":"music",className:"w-3.5 h-3.5 text-purple-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert MIDI Item")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertSoundClipAtCursor();closeContextMenu();},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":"file-audio",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Sound Clip")),/*#__PURE__*/React.createElement("button",{onClick:()=>{insertTrackBelow();closeContextMenu();},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":"plus-square",className:"w-3.5 h-3.5 text-cyan-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Insert Track")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:contextMenuCopy,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":"copy",className:"w-3.5 h-3.5 text-zinc-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Copy"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+C")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuCut,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":"scissors",className:"w-3.5 h-3.5 text-rose-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Cut"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+X")),/*#__PURE__*/React.createElement("button",{onClick:contextMenuPaste,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":"clipboard",className:"w-3.5 h-3.5 text-emerald-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Paste"),/*#__PURE__*/React.createElement("span",{className:"text-purple-400 text-xs font-semibold font-mono ml-auto pl-8"},"Ctrl+V")),/*#__PURE__*/React.createElement("div",{className:"h-px bg-zinc-700 my-1"}),/*#__PURE__*/React.createElement("button",{onClick:contextMenuDelete,className:"w-full px-3 py-1.5 text-xs text-red-300 hover:bg-red-950 text-left flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"trash-2",className:"w-3.5 h-3.5 text-red-400 shrink-0"})),/*#__PURE__*/React.createElement("span",{className:"flex-1"},"Delete"),/*#__PURE__*/React.createElement("span",{className:"text-amber-400 text-xs font-semibold font-mono ml-auto pl-8"},"Del")))),toastMessage&&/*#__PURE__*/React.createElement("div",{className:"absolute top-14 right-4 z-50 px-4 py-2.5 rounded shadow-lg text-xs font-semibold flex items-center gap-2 border bg-zinc-900 text-zinc-100 border-zinc-800"},/*#__PURE__*/React.createElement("span",{className:"inline-flex items-center shrink-0"},/*#__PURE__*/React.createElement("i",{"data-lucide":"bell",className:`w-4 h-4 ${toastMessage.type==='success'?'text-emerald-400':toastMessage.type==='error'?'text-rose-400':toastMessage.type==='warning'?'text-amber-400':'text-cyan-400'}`})),toastMessage.text,toastMessage.onActionClick&&/*#__PURE__*/React.createElement("button",{onClick:()=>{toastMessage.onActionClick();setToastMessage(null);},className:"ml-2 px-2 py-0.5 bg-purple-700 hover:bg-purple-600 text-white rounded text-[10px] uppercase font-bold"},toastMessage.actionText||'Tải về'))),appWarningModal&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm"},/*#__PURE__*/React.createElement("div",{className:"bg-[#222222] border border-zinc-800 rounded-lg shadow-2xl p-6 max-w-md w-full text-slate-200"},/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2 mb-3 text-cyan-400 font-bold"},/*#__PURE__*/React.createElement("h3",{className:"text-base font-bold"},appWarningModal.title)),/*#__PURE__*/React.createElement("div",{className:"text-xs text-slate-300 mb-5 leading-relaxed whitespace-pre-line"},appWarningModal.message),/*#__PURE__*/React.createElement("div",{className:"flex justify-end gap-2"},appWarningModal.isAlert?/*#__PURE__*/React.createElement("button",{onClick:()=>setAppWarningModal(null),className:"px-4 py-2 bg-cyan-600 hover:bg-cyan-500 active:bg-cyan-700 text-white rounded text-xs font-semibold transition"},"Đóng"):/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("button",{onClick:()=>setAppWarningModal(null),className:"px-4 py-2 bg-zinc-700 hover:bg-zinc-600 active:bg-zinc-800 text-slate-300 rounded text-xs font-semibold transition"},"Hủy"),/*#__PURE__*/React.createElement("button",{onClick:()=>{const fn=appWarningModal.onConfirm;setAppWarningModal(null);if(fn)fn();},className:"px-4 py-2 bg-cyan-600 hover:bg-cyan-500 active:bg-cyan-700 text-white rounded text-xs font-semibold transition"},"Xác nhận"))))),tabContextMenu&&/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-[190]",onClick:()=>setTabContextMenu(null),onContextMenu:e=>{e.preventDefault();setTabContextMenu(null);}}),/*#__PURE__*/React.createElement("div",{className:"fixed bg-zinc-900 border border-zinc-800 rounded-lg shadow-2xl p-2 z-[200] flex gap-1.5",style:{top:tabContextMenu.y,left:tabContextMenu.x},onClick:e=>e.stopPropagation()},['#f43f5e','#f59e0b','#10b981','#06b6d4','#8b5cf6','#64748b'].map(color=>/*#__PURE__*/React.createElement("button",{key:color,className:"w-5 h-5 rounded-full border border-zinc-700 hover:scale-110 active:scale-95 transition",style:{backgroundColor:color},onClick:()=>{handleSetTabColor(tabContextMenu.tabId,tabContextMenu.tabType,color);setTabContextMenu(null);}})))),/*#__PURE__*/React.createElement(AuthModal,{isOpen:authModalOpen,mode:authMode,forceMandatory:isMandatoryLogin,onClose:()=>setAuthModalOpen(false),onSuccess:handleAuthSuccess}),/*#__PURE__*/React.createElement(ProfileModal,{isOpen:profileModalOpen,onClose:()=>setProfileModalOpen(false),tracks:tracks,setTracks:setTracks,setSelectedTrackId:setSelectedTrackId,projectName:projectName,setProjectName:setProjectName,currentProjectId:currentProjectId,setCurrentProjectId:setCurrentProjectId,showToast:showToast,loadAudioBuffersForTracks:loadAudioBuffersForTracks,setAppWarningModal:setAppWarningModal,bpm:bpm,setBpm:setBpm,setMasteringSettings:setMasteringSettings,setSessionTabs:setSessionTabs,setSubTabs:setSubTabs,trackMidiChannelsRef:trackMidiChannelsRef}),/*#__PURE__*/React.createElement(SaveProjectModal,{isOpen:saveProjectModalOpen,onClose:()=>setSaveProjectModalOpen(false),projectName:projectName,onSaveCloud:(newName,existingId)=>{handleSaveCloudProject(newName,existingId);},onSaveLocal:newName=>{handleSaveLocalProject(newName);}}),/*#__PURE__*/React.createElement(SaveAsModal,{isOpen:saveAsModalOpen,onClose:()=>setSaveAsModalOpen(false),projectName:projectName,onSaveCloud:newName=>{handleSaveAsCloud(newName);},onSaveLocal:newName=>{handleExportSFS(newName);setProjectName(newName);localStorage.setItem('sonic_project_name',newName);}}),/*#__PURE__*/React.createElement(OpenProjectModal,{isOpen:openProjectModalOpen,onClose:()=>setOpenProjectModalOpen(false),onOpenCloud:(projId,projName)=>{setOpenProjectModalOpen(false);handleOpenProject(projId,projName);},onOpenLocal:()=>{setOpenProjectModalOpen(false);handleImportSFS();}}),/*#__PURE__*/React.createElement(AIConfigModal,{isOpen:aiConfigModalOpen,onClose:()=>setAiConfigModalOpen(false),onConfigSaved:providers=>{setAiProviders(providers);const active=providers.find(p=>p.is_active)||providers[0];if(active)setSelectedProviderId(active.id);}}),/*#__PURE__*/React.createElement(SystemManagerModal,{isOpen:systemManagerModalOpen,onClose:()=>setSystemManagerModalOpen(false)}),/*#__PURE__*/React.createElement(PluginManagerModal,{isOpen:pluginManagerModalOpen,onClose:()=>setPluginManagerModalOpen(false),pluginsData:pluginsData}),/*#__PURE__*/React.createElement(AIPresetModal,{isOpen:aiPresetModalOpen,onClose:()=>{setAiPresetModalOpen(false);setAiPresetVersion(v=>v+1);}}),/*#__PURE__*/React.createElement(MasteringModal,{isOpen:showMasteringModal,onClose:()=>setShowMasteringModal(false),masteringSettings:masteringSettings,setMasteringSettings:setMasteringSettings}),fxRackTarget&&/*#__PURE__*/React.createElement(FXRackModal,{track:tracks.find(t=>t.id===fxRackTarget.trackId)||null,onUpdateTrack:updateTrackProp,onClose:()=>setFxRackTarget(null)}),/*#__PURE__*/React.createElement(ExportModal,{open:showExportPanel,onClose:()=>setShowExportPanel(false),exportSettings:exportSettings,setExportSettings:setExportSettings,isExporting:isExporting,onExport:triggerWavExport,onBounce:triggerBounceExport}),instrumentSelectorTrackId&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:closeInstrumentSelector},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-4xl p-5 text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center pb-3 border-b border-[#383838]"},/*#__PURE__*/React.createElement("h3",{className:"text-sm font-bold text-amber-400"},"Select Instrument"),/*#__PURE__*/React.createElement("div",{className:"flex items-center gap-2"},/*#__PURE__*/React.createElement("select",{value:instrumentSelectorTrackId||'',onChange:e=>{var tid=e.target.value;if(tid){openInstrumentSelector(tid);}},className:"bg-[#1e1e1e] text-zinc-300 border border-zinc-700 rounded px-2 py-1 text-xs font-mono cursor-pointer"},activeTracks.map(function(at){return/*#__PURE__*/React.createElement("option",{key:at.id,value:at.id},at.name+' ('+(at.instrumentName||'Synth')+')');})),/*#__PURE__*/React.createElement("button",{onClick:closeInstrumentSelector,className:"text-slate-400 hover:text-slate-200"},"\u2715"))),/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"T\u00ecm nh\u1ea1c c\u1ee5...",value:sfPresetSearchQuery,onChange:e=>setSfPresetSearchQuery(e.target.value),autoFocus:true,className:"w-full bg-black border border-zinc-700 rounded px-2 py-1.5 outline-none my-3 text-sm"}),/*#__PURE__*/React.createElement("div",{className:"flex gap-4",style:{height:"420px"}},/*#__PURE__*/React.createElement("div",{className:"w-1/3 border-r border-[#383838] overflow-y-auto space-y-0.5 pr-2"},/*#__PURE__*/React.createElement("button",{onClick:()=>{setSelectedSoundFontId(null);setSynthCategory(null);},className:"w-full text-left px-3 py-2 text-sm rounded "+(!selectedSoundFontId?"bg-amber-700 text-white":"bg-zinc-800 hover:bg-zinc-700 text-zinc-300")},"All Instruments"),(instrumentSelectorData?.soundfonts||[]).map(sf=>{const sfId=sf.id.startsWith('sf_')?sf.id:'sf_'+sf.id;const sfName=sf.display||sf.name||sf.id;return/*#__PURE__*/React.createElement("button",{key:sfId,onClick:()=>{setSelectedSoundFontId(sfId);setSfPresets(null);const baseId=sfId.replace('sf_','');window.SonicAPI.listSoundfontInstruments(baseId).then(data=>{if(data&&data.presets){const mapping=data.presets.map(p=>({...p,_sfId:sfId,_sfName:sfName,_sfDisplay:sfName.substring(0,30)}));setSfPresets(mapping);setInstrumentSelectorData(prev=>prev?{...prev,soundfonts:(prev.soundfonts||[]).map(s=>s.id===sf.id?{...s,presets:data.presets}:s)}:prev);}else{setSfPresets([]);}}).catch(()=>{setSfPresets([]);});},className:"w-full text-left px-3 py-2 text-sm rounded "+(selectedSoundFontId===sfId?"bg-amber-700 text-white":"bg-zinc-800 hover:bg-zinc-700 text-zinc-300")},sfName);})),/*#__PURE__*/React.createElement("div",{className:"flex-1 overflow-y-auto space-y-0.5"},/*#__PURE__*/React.createElement("button",{onClick:()=>setTrackInstrumentWithUndo(instrumentSelectorTrackId,null),className:"w-full text-left px-3 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None (Default Synth)"),sfPresets===null?(/*#__PURE__*/React.createElement("p",{className:"text-sm text-zinc-500 py-2"},"Loading instruments...")):sfPresets.length>0?sfPresets.filter(p=>!selectedSoundFontId||p._sfId===selectedSoundFontId).filter(p=>!sfPresetSearchQuery||(p.name||'').toLowerCase().includes(sfPresetSearchQuery.toLowerCase())||(p._sfName||'').toLowerCase().includes(sfPresetSearchQuery.toLowerCase())).map((p,i)=>/*#__PURE__*/React.createElement("button",{key:i,onClick:()=>setTrackInstrumentWithUndo(instrumentSelectorTrackId,p._sfId,p.name||'Preset '+p.program,p.bank,p.program),onDoubleClick:()=>{setTrackInstrumentWithUndo(instrumentSelectorTrackId,p._sfId,p.name||'Preset '+p.program,p.bank,p.program);closeInstrumentSelector();},className:"w-full text-left px-3 py-2 text-sm rounded bg-zinc-800/60 hover:bg-amber-800 text-zinc-300 truncate flex items-center gap-2"},/*#__PURE__*/React.createElement("span",{className:"text-xs text-zinc-500 shrink-0"},p._sfDisplay),p.bank===128?/*#__PURE__*/React.createElement("span",{className:"mr-1"},"🥁"):null,p.name||'Preset '+p.program)):(/*#__PURE__*/React.createElement("p",{className:"text-sm text-zinc-500 py-2"},sfPresetSearchQuery?"Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 ph\u00f9 h\u1ee3p.":"No presets found.")))))),fxSelectorTrackId&&/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:()=>setFxSelectorTrackId(null)},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-64 p-4 text-slate-200",onClick:e=>e.stopPropagation()},/*#__PURE__*/React.createElement("h3",{className:"text-sm font-bold text-purple-400 mb-3"},"Track FX"),/*#__PURE__*/React.createElement("div",{className:"space-y-1"},/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,null),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None"),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,'chorus'),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-cyan-800 text-cyan-300"},"Chorus"),/*#__PURE__*/React.createElement("button",{onClick:()=>handleSetTrackFx(fxSelectorTrackId,'reverb'),className:"w-full text-left px-3 py-2 text-xs rounded bg-zinc-800 hover:bg-purple-800 text-purple-300"},"Reverb")))),instrumentDropdownTrackId&&instrumentDropdownBtnRect&&/*#__PURE__*/React.createElement("div",{"data-instr-dropdown":"",className:"fixed z-[100] bg-[#1e1e1e] border border-zinc-700 rounded shadow-xl w-[220px] max-h-[300px] flex flex-col",style:{top:instrumentDropdownBtnRect.bottom+4,left:Math.max(4,Math.min(instrumentDropdownBtnRect.left,window.innerWidth-224))}},/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"T\u00ecm nh\u1ea1c c\u1ee5...",value:instrumentSearchQuery,onChange:e=>setInstrumentSearchQuery(e.target.value),autoFocus:true,className:"w-full bg-black border-b border-zinc-700 px-2 py-1.5 text-xs outline-none"}),/*#__PURE__*/React.createElement("div",{className:"flex-1 overflow-y-auto"},/*#__PURE__*/React.createElement("button",{onClick:()=>setTrackInstrumentWithUndo(instrumentDropdownTrackId,null),className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-zinc-700 text-zinc-400"},"None (Default Synth)"),filteredInstruments.soundfonts.length>0&&/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-zinc-500 px-3 py-1 mt-1 uppercase font-bold"},"SoundFonts"),filteredInstruments.soundfonts.map((sf,i)=>/*#__PURE__*/React.createElement("button",{key:"sfd_"+i,onClick:()=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setTrackInstrumentWithUndo(instrumentDropdownTrackId,"sf_"+sf.id,sf.display||sf.name||sf.id,undefined,undefined);},className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-amber-800 text-zinc-200 flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"truncate"},sf.display||sf.name||sf.id),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-amber-400 shrink-0 ml-1"},"SF"))),filteredInstruments.vst.length>0&&/*#__PURE__*/React.createElement("div",{className:"text-[10px] text-zinc-500 px-3 py-1 mt-1 uppercase font-bold"},"VST Instruments"),filteredInstruments.vst.map((v,i)=>/*#__PURE__*/React.createElement("button",{key:"vstd_"+i,onClick:()=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setTrackInstrumentWithUndo(instrumentDropdownTrackId,v.id,v.name||v.id);},className:"w-full text-left px-3 py-1.5 text-xs bg-zinc-800 hover:bg-violet-800 text-zinc-200 flex items-center justify-between"},/*#__PURE__*/React.createElement("span",{className:"truncate"},v.name||v.id),/*#__PURE__*/React.createElement("span",{className:"text-[10px] text-cyan-400 shrink-0 ml-1"},v.type||"VST"))),!filteredInstruments.soundfonts.length&&!filteredInstruments.vst.length&&/*#__PURE__*/React.createElement("p",{className:"text-xs text-zinc-500 py-4 text-center"},"Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 n\u00e0o"))));};const root=ReactDOM.createRoot(document.getElementById('root'));root.render(/*#__PURE__*/React.createElement(App,null));setTimeout(()=>lucide.createIcons(),300); \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html index c8adf05..004a192 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -24,7 +24,7 @@ - +