From 09bb431a2bbdc109213d0cff9086f9cf91f789ac Mon Sep 17 00:00:00 2001 From: 3dtours Date: Wed, 22 Jul 2026 07:23:36 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20=C4=91=C3=A3=20ho=C3=A0n=20th=C3=A0nh=20?= =?UTF-8?q?md=2029=20v=C3=A0=20b=E1=BA=AFt=20=C4=91=E1=BA=A7u=20fix=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/app.jsx | 426 ++++++++++++++++++++++--------- app/static/js/app.precompiled.js | 418 +++++++++++++++++++++--------- app/storage/sonicforge.db | Bin 45056 -> 45056 bytes 3 files changed, 610 insertions(+), 234 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index b1a465f..922510a 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -1955,11 +1955,11 @@ const SubTabToolbar = ({ })))), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("span", { - className: "text-[9px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Snap"), /*#__PURE__*/React.createElement("select", { value: snapValue || 'free', onChange: e => onSnapChange(e.target.value), - className: "bg-zinc-850 text-zinc-300 text-[10px] px-1 py-0.5 rounded border border-zinc-800 focus:outline-none font-mono" + className: "bg-zinc-850 text-zinc-300 text-xs px-1 py-0.5 rounded border border-zinc-800 focus:outline-none font-mono" }, /*#__PURE__*/React.createElement("option", { value: "free" }, "Free"), /*#__PURE__*/React.createElement("option", { @@ -1979,7 +1979,7 @@ const SubTabToolbar = ({ }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Normalize:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-12", @@ -1989,11 +1989,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabNormalizeWithValue(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.normalizeDb || 0, " dB")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Gain:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-40", @@ -2003,11 +2003,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabGainWithValue(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.gainDb || 0, " dB")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Pitch:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-12", @@ -2017,11 +2017,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabPitch(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.pitch || 0, " st")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Stretch:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "50", @@ -2031,15 +2031,15 @@ const SubTabToolbar = ({ onChange: e => handleSubTabStretch(st.id, parseInt(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.speedStretch || 100, "%"))), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("button", { onClick: () => handleSubTabFade(st.id, 'in'), - className: "px-2 py-1 text-[10px] rounded hover:bg-zinc-700" + className: "px-2 py-1 text-xs rounded hover:bg-zinc-700" }, "Fade In"), /*#__PURE__*/React.createElement("button", { onClick: () => handleSubTabFade(st.id, 'out'), - className: "px-2 py-1 text-[10px] rounded hover:bg-zinc-700" + className: "px-2 py-1 text-xs rounded hover:bg-zinc-700" }, "Fade Out")), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("button", { @@ -2099,11 +2099,11 @@ const SubTabToolbar = ({ })))), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Rate:"), /*#__PURE__*/React.createElement("select", { value: st.playbackRate || 1, onChange: e => onRateChange(parseFloat(e.target.value)), - className: "bg-zinc-700 text-zinc-100 text-[10px] px-1 py-0.5 rounded border border-zinc-600" + className: "bg-zinc-700 text-zinc-100 text-xs px-1 py-0.5 rounded border border-zinc-600" }, /*#__PURE__*/React.createElement("option", { value: "0.5" }, "0.5x"), /*#__PURE__*/React.createElement("option", { @@ -2585,7 +2585,7 @@ const AIConfigModal = ({ }, /*#__PURE__*/React.createElement("div", { className: "space-y-1.5 border-r border-[#383838] pr-3" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] uppercase font-bold text-slate-400 block mb-2" + className: "text-xs uppercase font-bold text-slate-400 block mb-2" }, "Providers"), providers.map(p => /*#__PURE__*/React.createElement("button", { key: p.id, onClick: () => setSelectedId(p.id), @@ -2866,7 +2866,7 @@ const SystemManagerModal = ({ }, /*#__PURE__*/React.createElement("td", { className: "p-3 font-semibold text-teal-300" }, u.username, u.must_change_password && /*#__PURE__*/React.createElement("span", { - className: "ml-2 text-[10px] bg-amber-900/60 text-amber-300 px-1.5 py-0.5 rounded" + className: "ml-2 text-xs bg-amber-900/60 text-amber-300 px-1.5 py-0.5 rounded" }, "Mật khẩu gốc")), /*#__PURE__*/React.createElement("td", { className: "p-3 text-slate-300" }, u.email), /*#__PURE__*/React.createElement("td", { @@ -2884,7 +2884,7 @@ const SystemManagerModal = ({ className: "w-16 bg-[#1e1e1e] border border-[#444] rounded px-1 py-0.5 text-xs text-slate-200" }), /*#__PURE__*/React.createElement("span", null, "MB"), /*#__PURE__*/React.createElement("button", { onClick: () => handleSaveQuota(u.id), - className: "px-2 py-0.5 bg-teal-600 rounded text-[10px]" + className: "px-2 py-0.5 bg-teal-600 rounded text-xs" }, "Lưu")) : /*#__PURE__*/React.createElement("span", { className: "font-semibold" }, u.quota_mb, " MB")), /*#__PURE__*/React.createElement("td", { @@ -2894,13 +2894,13 @@ const SystemManagerModal = ({ setEditingQuotaUser(u.id); setNewQuotaMb(u.quota_mb); }, - className: "px-2 py-1 bg-slate-700 hover:bg-slate-600 rounded text-[11px]" + className: "px-2 py-1 bg-slate-700 hover:bg-slate-600 rounded text-xs" }, "Sửa Quota"), /*#__PURE__*/React.createElement("button", { onClick: () => handleToggleRole(u), - className: "px-2 py-1 bg-amber-700/60 hover:bg-amber-600 rounded text-[11px]" + className: "px-2 py-1 bg-amber-700/60 hover:bg-amber-600 rounded text-xs" }, "Đổi Role"), u.role !== 'admin' && /*#__PURE__*/React.createElement("button", { onClick: () => handleDeleteUser(u.id), - className: "px-2 py-1 bg-red-700/60 hover:bg-red-600 rounded text-[11px]" + className: "px-2 py-1 bg-red-700/60 hover:bg-red-600 rounded text-xs" }, "Xóa"))))))))); }; const App = () => { @@ -2978,17 +2978,27 @@ const App = () => { const [showAIPanel, setShowAIPanel] = useState(true); const [showSelectionPanel, setShowSelectionPanel] = useState(true); const [showPythonToolsPanel, setShowPythonToolsPanel] = useState(true); + const [showMediaExplorer, setShowMediaExplorer] = useState(true); + const [showFxRack, setShowFxRack] = useState(false); + const [showMidiEvents, setShowMidiEvents] = useState(false); + const [rightSidebarWidth, setRightSidebarWidth] = useState(320); + const [mediaExplorerHeight, setMediaExplorerHeight] = useState(50); const [panelPositions, setPanelPositions] = useState({ export: 'bottom', - ai: 'bottom', + ai: 'right', python_tools: 'bottom', - selection: 'bottom' + selection: 'bottom', + media_explorer: 'right', + fx_rack: 'bottom', + midi_events: 'bottom' }); const [panelDropZone, setPanelDropZone] = useState(null); const [dragGhostPos, setDragGhostPos] = useState(null); const [dragGhostPanel, setDragGhostPanel] = useState(null); const panelDragRef = useRef(null); const workspaceRef = useRef(null); + const colResizerRef = useRef(null); + const rowResizerRef = useRef(null); const [aiConfig, setAiConfig] = useState({ baseUrl: localStorage.getItem('ai_base_url') || `${API_BASE_URL}`, apiKey: localStorage.getItem('ai_api_key') || '', @@ -3311,6 +3321,46 @@ const App = () => { document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); }; + + // ── Right Sidebar Column Resizer ── + const startColResize = e => { + e.preventDefault(); + const startX = e.clientX; + const startWidth = rightSidebarWidth; + const onMove = ev => { + const deltaX = startX - ev.clientX; + const newWidth = Math.max(200, Math.min(600, startWidth + deltaX)); + setRightSidebarWidth(newWidth); + }; + const onUp = () => { + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; + + // ── Right Sidebar Row Resizer (Media Explorer / AI Panel) ── + const startRowResize = e => { + e.preventDefault(); + const startY = e.clientY; + const startHeight = mediaExplorerHeight; + const sidebarEl = document.getElementById('right-sidebar'); + const sidebarHeight = sidebarEl ? sidebarEl.getBoundingClientRect().height : 400; + const onMove = ev => { + const deltaY = ev.clientY - startY; + const pct = ((startHeight / 100 * sidebarHeight + deltaY) / sidebarHeight) * 100; + const newPct = Math.max(20, Math.min(80, pct)); + setMediaExplorerHeight(newPct); + }; + const onUp = () => { + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; + const rulerRef = useRef(null); const activeSourcesRef = useRef([]); const activeTrackNodesRef = useRef({}); // { [trackId]: { gainNode, pannerNode } } @@ -7399,7 +7449,7 @@ const App = () => { className: "relative" }, /*#__PURE__*/React.createElement("button", { onClick: () => setMenuOpen(menuOpen === menu.label ? null : menu.label), - className: `px-3 py-1 text-[11px] font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}` + className: `px-3 py-1 text-xs font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}` }, menu.label), menuOpen === menu.label && /*#__PURE__*/React.createElement("div", { className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-52 z-50", onClick: () => setMenuOpen(null) @@ -7422,16 +7472,16 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, item.label), item.shortcut && /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto pl-8" }, item.shortcut)))))), /*#__PURE__*/React.createElement("div", { className: "flex-1" }), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2 px-2" }, /*#__PURE__*/React.createElement("span", { - className: `text-[9px] font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus === 'connected' ? 'bg-emerald-950 text-emerald-400' : serverStatus === 'checking' ? 'bg-amber-950 text-amber-400' : 'bg-red-950 text-red-400'}` + className: `text-xs font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus === 'connected' ? 'bg-emerald-950 text-emerald-400' : serverStatus === 'checking' ? 'bg-amber-950 text-amber-400' : 'bg-red-950 text-red-400'}` }, "Server: ", serverStatus), /*#__PURE__*/React.createElement("button", { onClick: () => setShowAIConfig(!showAIConfig), - className: `px-1.5 py-0.5 rounded text-[10px] border transition ${showAIConfig ? 'bg-purple-900 text-purple-200 border-purple-700' : 'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}` + className: `px-1.5 py-0.5 rounded text-xs border transition ${showAIConfig ? 'bg-purple-900 text-purple-200 border-purple-700' : 'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7444,7 +7494,7 @@ const App = () => { className: "h-7 bg-[#222] border-b border-zinc-800 flex items-stretch px-2 gap-0.5 shrink-0 overflow-x-auto" }, /*#__PURE__*/React.createElement("button", { onClick: () => setActiveTab('main'), - className: `px-3 text-[10px] font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab === 'main' ? 'text-cyan-400 border-cyan-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` + className: `px-3 text-xs font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab === 'main' ? 'text-cyan-400 border-cyan-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7455,7 +7505,7 @@ const App = () => { className: "flex items-stretch" }, /*#__PURE__*/React.createElement("button", { onClick: () => setActiveTab(st.id), - className: `px-2 text-[10px] font-medium border-b-2 transition flex items-center gap-1 ${activeTab === st.id ? 'text-amber-400 border-amber-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` + className: `px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${activeTab === st.id ? 'text-amber-400 border-amber-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7465,7 +7515,7 @@ const App = () => { className: "max-w-[100px] truncate" }, st.label)), /*#__PURE__*/React.createElement("button", { onClick: () => closeSubTab(st.id), - className: "px-1 text-zinc-600 hover:text-red-400 transition text-[9px]", + className: "px-1 text-zinc-600 hover:text-red-400 transition text-xs", title: "Close tab" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -7486,7 +7536,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Endpoint Base URL"), /*#__PURE__*/React.createElement("input", { type: "text", value: aiConfig.baseUrl, @@ -7494,12 +7544,12 @@ const App = () => { ...prev, baseUrl: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "https://api.openai.com/v1" })), /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "API Token Key"), /*#__PURE__*/React.createElement("input", { type: "password", value: aiConfig.apiKey, @@ -7507,12 +7557,12 @@ const App = () => { ...prev, apiKey: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "sk-..." })), /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Model Name"), /*#__PURE__*/React.createElement("input", { type: "text", value: aiConfig.model, @@ -7520,7 +7570,7 @@ const App = () => { ...prev, model: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "gpt-4o-mini" })))), /*#__PURE__*/React.createElement("div", { className: "h-9 bg-[#222] border-b border-zinc-800 flex items-center px-3 gap-2 shrink-0 select-none" @@ -7907,8 +7957,17 @@ const App = () => { addPanel('ai', panelPositions.ai, showAIPanel); addPanel('python_tools', panelPositions.python_tools || 'bottom', showPythonToolsPanel); addPanel('selection', panelPositions.selection, showSelectionPanel); + addPanel('media_explorer', panelPositions.media_explorer || 'right', showMediaExplorer); + addPanel('fx_rack', panelPositions.fx_rack || 'bottom', showFxRack); + addPanel('midi_events', panelPositions.midi_events || 'bottom', showMidiEvents); 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); + 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 === 'media_explorer') setShowMediaExplorer(false); + else if (id === 'fx_rack') setShowFxRack(false); + else if (id === 'midi_events') setShowMidiEvents(false); }; const renderPanelContent = panelId => { const h = id => e => { @@ -7920,7 +7979,7 @@ const App = () => { 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-[10px] text-zinc-200 flex items-center gap-1" + 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", { @@ -7949,7 +8008,7 @@ const App = () => { ...p, sampleRate: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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: "44100" }, "44.1k"), /*#__PURE__*/React.createElement("option", { @@ -7962,7 +8021,7 @@ const App = () => { ...p, bitDepth: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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: "16" }, "16"), /*#__PURE__*/React.createElement("option", { @@ -7975,13 +8034,13 @@ const App = () => { ...p, format: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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("button", { onClick: triggerWavExport, disabled: isExporting, - className: "w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-[10px] flex items-center justify-center gap-1" + 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", { @@ -7994,7 +8053,7 @@ const App = () => { 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-[10px] text-zinc-200 flex items-center gap-1" + 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", { @@ -8028,7 +8087,7 @@ const App = () => { "data-lucide": "x", className: "w-3 h-3" }))))), /*#__PURE__*/React.createElement("div", { - className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-[9px] font-mono min-h-[20px]" + className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-xs font-mono min-h-[20px]" }, /*#__PURE__*/React.createElement("div", { className: "text-zinc-500" }, "// ", /*#__PURE__*/React.createElement("span", { @@ -8039,7 +8098,7 @@ const App = () => { className: "grid grid-cols-3 gap-1" }, /*#__PURE__*/React.createElement("button", { onClick: handleAIScan, - className: "py-1 bg-purple-900 hover:bg-purple-800 text-purple-100 font-bold rounded text-[9px] border border-purple-700 flex items-center justify-center gap-1" + 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", { @@ -8048,7 +8107,7 @@ const App = () => { })), " Scan"), /*#__PURE__*/React.createElement("button", { onClick: handleAICutToNewTrack, disabled: analysisState.isRunning, - className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-[9px] border border-fuchsia-600 flex items-center justify-center gap-1" + className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-xs border border-fuchsia-600 flex items-center justify-center gap-1" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8057,7 +8116,7 @@ const App = () => { })), " Cut"), /*#__PURE__*/React.createElement("button", { onClick: handleAIAnalysicLoop, disabled: analysisState.isRunning, - className: "py-1 bg-violet-800 hover:bg-violet-700 text-violet-100 font-bold rounded text-[9px] border border-violet-600 flex items-center justify-center gap-1" + 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" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8066,7 +8125,7 @@ const App = () => { })), " Loop")), /*#__PURE__*/React.createElement("div", { className: "border-t border-zinc-800 pt-1.5 mt-1" }, /*#__PURE__*/React.createElement("div", { - className: "text-[9px] font-bold text-zinc-400 uppercase mb-1 flex items-center gap-1" + 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", { @@ -8076,7 +8135,7 @@ const App = () => { value: aiPrompt, onChange: e => setAiPrompt(e.target.value), placeholder: "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-[10px] resize-none", + 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-none", rows: 2, onKeyDown: e => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { @@ -8089,7 +8148,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("button", { onClick: () => window.executeAIPrompt(), disabled: aiProcessing, - className: "flex-1 py-1 bg-purple-700 hover:bg-purple-600 text-white font-bold rounded text-[10px] border border-purple-500 flex items-center justify-center gap-1" + 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", { @@ -8100,13 +8159,13 @@ const App = () => { setAiPrompt(''); setAiActionLog([]); }, - className: "px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded text-[9px] border border-zinc-700" + 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-[8px] text-zinc-600 mt-0.5" + className: "text-xs text-zinc-600 mt-0.5" }, "Ctrl+Enter để gửi nhanh")), /*#__PURE__*/React.createElement("div", { className: "border-t border-zinc-800 pt-1.5 mt-1" }, /*#__PURE__*/React.createElement("div", { - className: "text-[9px] font-bold text-zinc-400 uppercase mb-1 flex items-center justify-between" + className: "text-xs font-bold text-zinc-400 uppercase mb-1 flex items-center justify-between" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center gap-1" }, /*#__PURE__*/React.createElement("i", { @@ -8125,21 +8184,22 @@ const App = () => { setAiActionLog(prev => [...prev, { type: 'undo', text: 'Undo (Ctrl+Z)', time: Date.now() }]); } }, - className: "text-[8px] text-amber-400 hover:text-amber-300 border border-amber-800 rounded px-1 py-0.5" + className: "text-xs text-amber-400 hover:text-amber-300 border border-amber-800 rounded px-1 py-0.5" }, "Undo"))), /*#__PURE__*/React.createElement("div", { className: "flex-1 overflow-y-auto no-scrollbar bg-[#0f0f0f] rounded border border-zinc-800 p-1 max-h-[120px]" }, aiActionLog.length === 0 ? /*#__PURE__*/React.createElement("div", { - className: "text-[9px] text-zinc-600 italic" + className: "text-xs text-zinc-600 italic" }, "Chưa có hành động nào.") : aiActionLog.map((entry, i) => /*#__PURE__*/React.createElement("div", { key: i, - className: `text-[9px] 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)))); return /*#__PURE__*/React.createElement("div", { + 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)))); + 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-[10px] text-amber-300 flex items-center gap-1" + 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", { @@ -8159,9 +8219,9 @@ const App = () => { "data-lucide": "x", className: "w-3 h-3" })))), /*#__PURE__*/React.createElement("div", { - className: "p-1 bg-[#141414] rounded border border-zinc-800 text-[8px] font-mono text-zinc-400" + className: "p-1 bg-[#141414] rounded border border-zinc-800 text-xs font-mono text-zinc-400" }, "// Non-AI Audio Processing Tools"), /*#__PURE__*/React.createElement("div", { - className: "grid grid-cols-2 gap-1 text-[9px]" + 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" @@ -8181,7 +8241,7 @@ const App = () => { 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-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1" + 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", { @@ -8204,7 +8264,7 @@ const App = () => { 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-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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", { @@ -8212,13 +8272,13 @@ const App = () => { 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-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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-[9px] font-semibold mt-0.5" + 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", { @@ -8237,7 +8297,7 @@ const App = () => { setSelectionStart(t); setSelectionEnd(t + beatDuration * 4); }, - className: "w-full bg-[#242424] text-white text-center font-mono text-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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", { @@ -8252,29 +8312,136 @@ const App = () => { setSelectionEnd(t + beatDuration * 4); setNumberBar(b - beginBar + 1); }, - className: "w-full bg-[#242424] text-white text-center font-mono text-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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-[9px] font-semibold mt-0.5" + className: "text-zinc-200 font-mono text-xs font-semibold mt-0.5" }, numberBar)))); + if (panelId === 'media_explorer') 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('media_explorer', e) + }, /*#__PURE__*/React.createElement("h3", { + className: "font-bold text-xs text-emerald-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": "folder-open", + className: "w-3.5 h-3.5 text-emerald-400" + })), " Media Explorer"), /*#__PURE__*/React.createElement("button", { + onClick: () => closePanel('media_explorer'), + 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 overflow-y-auto text-xs text-zinc-400 font-mono p-2" + }, "// Placeholder: Media files browser")); + 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 sideClass = isSide ? 'w-56 shrink-0 overflow-y-auto border-zinc-800' : 'shrink-0'; 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`, + 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" + }, panels.map((p, idx) => /*#__PURE__*/React.createElement(React.Fragment, { + key: p + }, /*#__PURE__*/React.createElement("div", { + className: `${p === 'media_explorer' ? 'flex-shrink-0' : 'flex-1 min-h-0'} border border-zinc-700 rounded-lg bg-[#262626] shadow-sm`, + style: p === 'media_explorer' ? { + height: `${mediaExplorerHeight}%` + } : {} + }, renderPanelContent(p)), idx < panels.length - 1 && /*#__PURE__*/React.createElement("div", { + className: "h-1.5 cursor-ns-resize hover:bg-cyan-500/50 transition-colors rounded shrink-0", + onMouseDown: startRowResize + }))))); + const sideClass = isSide ? 'shrink-0 overflow-y-auto' : 'shrink-0'; return /*#__PURE__*/React.createElement("div", { + id: "daw-bottom", className: `${sideClass} ${borderClass} ${bgClass} flex ${isSide ? 'flex-col p-2 gap-2' : 'flex-row p-1.5 gap-3'} select-none ${highlight ? 'ring-2 ring-cyan-500 ring-inset' : ''}` }, panels.map(p => /*#__PURE__*/React.createElement("div", { key: p, - className: `${isSide ? 'w-full' : 'flex-1 min-w-0'} border border-zinc-700 rounded-lg bg-[#262626] shadow-sm p-2` + 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", { @@ -8297,14 +8464,14 @@ const App = () => { top: dragGhostPos.y } }, /*#__PURE__*/React.createElement("div", { - className: "flex items-center gap-2 text-[10px] text-zinc-200 font-bold" + 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-[8px] text-zinc-500 mt-1" + 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", { @@ -8330,7 +8497,7 @@ const App = () => { className: "w-3.5 h-3.5 text-cyan-400" })), "TRACKS (", tracks.length, ")"), /*#__PURE__*/React.createElement("button", { onClick: addNewTrack, - className: "px-2.5 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-[11px] font-semibold flex items-center gap-1 shadow transition" + 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", { @@ -8343,7 +8510,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] font-bold text-purple-400 font-mono" + 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", { @@ -8353,11 +8520,11 @@ const App = () => { value: bpm, onChange: e => setBpm(e.target.value), onBlur: () => localStorage.setItem('studio_bpm', bpm), - className: "w-12 bg-zinc-800 border border-zinc-700 rounded text-[10px] text-zinc-300 text-center font-mono focus:outline-none focus:border-purple-500", + 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-[9px] text-zinc-500" + className: "text-xs text-zinc-500" }, "BPM")))), /*#__PURE__*/React.createElement("div", { className: "flex-1 flex flex-col divide-y divide-[#141414] bg-[#262626]" }, tracks.length === 0 ? /*#__PURE__*/React.createElement("div", { @@ -8391,7 +8558,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] font-bold text-zinc-500 font-mono" + className: "text-xs font-bold text-zinc-500 font-mono" }, (idx + 1).toString().padStart(2, '0')), /*#__PURE__*/React.createElement("label", { onClick: e => { e.stopPropagation(); @@ -8445,13 +8612,13 @@ const App = () => { e.stopPropagation(); toggleTrackMute(track.id); }, - className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition ${track.muted ? 'bg-red-950 text-red-400 border-red-700' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border transition ${track.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(track.id); }, - className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition ${soloedTrackId === track.id || track.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border transition ${soloedTrackId === track.id || track.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("button", { onClick: e => { e.stopPropagation(); @@ -8464,12 +8631,12 @@ const App = () => { "data-lucide": "trash-2", className: "w-3.5 h-3.5" }))))), /*#__PURE__*/React.createElement("div", { - className: "flex flex-col gap-0.5 text-[10px]", + 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-[9px]" + className: "w-8 text-right text-zinc-500 text-xs" }, "Vol:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-50", @@ -8482,11 +8649,11 @@ const App = () => { height: '4px' } }), /*#__PURE__*/React.createElement("span", { - className: "w-12 text-right font-mono text-zinc-300 text-[9px]" + 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-[9px]" + className: "w-8 text-right text-zinc-500 text-xs" }, "Pan:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-100", @@ -8499,7 +8666,7 @@ const App = () => { height: '4px' } }), /*#__PURE__*/React.createElement("span", { - className: "w-12 text-right font-mono text-zinc-300 text-[9px]" + 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.5 mt-1", onClick: e => e.stopPropagation() @@ -8511,7 +8678,7 @@ const App = () => { 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-[10px] flex items-center gap-1 cursor-pointer border border-zinc-700 font-medium" + 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", { @@ -8522,7 +8689,7 @@ const App = () => { e.stopPropagation(); showToast('FX panel for track ' + track.id, 'info'); }, - className: "px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-purple-400 border border-purple-900 rounded text-[10px] font-bold flex items-center gap-1" + 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", { @@ -8532,7 +8699,7 @@ const App = () => { className: "text-zinc-500 font-normal" }, "None")), /*#__PURE__*/React.createElement("button", { onClick: () => generateSynthToTrack(track.id, 'synth'), - className: "px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-amber-400 border border-amber-800 rounded text-[10px] font-bold flex items-center gap-1" + 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" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8670,7 +8837,7 @@ const App = () => { 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-[9px] flex items-center gap-1 border border-zinc-700/50" + 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", { @@ -8735,10 +8902,10 @@ const App = () => { }, /*#__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-[10px] font-bold text-zinc-500 uppercase" + 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-[9px] bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded border border-zinc-700 flex items-center gap-1" + 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", { @@ -8804,13 +8971,13 @@ const App = () => { e.stopPropagation(); toggleTrackMute(vTrack.id); }, - className: `px-1.5 py-0.5 text-[10px] 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'}` + 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-[10px] rounded font-mono font-bold border ${soloedTrackId === vTrack.id || vTrack.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${soloedTrackId === vTrack.id || 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", { @@ -8985,7 +9152,7 @@ const App = () => { handleAIScan(); }, disabled: analysisState.isRunning, - className: "py-1 bg-purple-900 hover:bg-purple-800 text-purple-100 font-bold rounded text-[12px] border border-purple-700 flex items-center justify-center gap-1" + 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", { @@ -8997,7 +9164,7 @@ const App = () => { handleAICutToNewTrack(); }, disabled: analysisState.isRunning, - className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-[12px] flex items-center justify-center gap-1" + 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", { @@ -9012,7 +9179,7 @@ const App = () => { handleAIAnalysicLoop(); }, disabled: analysisState.isRunning, - className: "py-1 bg-violet-800 hover:bg-violet-700 text-violet-100 font-bold rounded text-[12px] border border-violet-600 flex items-center justify-center gap-1 col-span-2" + 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", { @@ -9035,7 +9202,7 @@ const App = () => { "data-lucide": "save", className: "w-4 h-4" })), " Save"))) : /*#__PURE__*/React.createElement("div", { - className: "flex-1 flex items-center justify-center text-[10px] text-zinc-500" + className: "flex-1 flex items-center justify-center text-xs text-zinc-500" }, "Track not found")), /*#__PURE__*/React.createElement("div", { ref: timelineWrapperRef, className: "flex-1 relative z-10 overflow-x-auto overflow-y-auto min-w-0" @@ -9174,9 +9341,12 @@ const App = () => { width: `${Math.abs(st.selectionEnd - st.selectionStart) * zoom}px` } }))))); - })())), renderDock('right', 'Right')), renderDock('bottom', 'Bottom')); + })())), /*#__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')); })(), /*#__PURE__*/React.createElement("div", { - className: "h-6 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-[10px] text-zinc-500 select-none shrink-0" + 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", { @@ -9184,20 +9354,20 @@ const App = () => { }, "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-[9px]" + 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-[9px]" + className: "text-purple-400 font-semibold uppercase text-xs" }, "Global Sel"), soloedTrackId && /*#__PURE__*/React.createElement("span", { className: "text-amber-500 font-semibold" }, "Solo: ID ", soloedTrackId), isLoopingSelection && selectionMode === 'local' && /*#__PURE__*/React.createElement("span", { - className: "text-emerald-400 font-semibold uppercase text-[9px]" + 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-[9px]" + 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-[9px] font-semibold transition flex items-center gap-1 ${showExportPanel ? 'bg-cyan-900 text-cyan-300' : 'text-zinc-500 hover:text-zinc-300'}`, + 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 Panel (${panelPositions.export})` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -9208,7 +9378,7 @@ const App = () => { className: "text-[7px] opacity-60" }, showExportPanel ? panelPositions.export[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { onClick: () => setShowSelectionPanel(p => !p), - className: `px-1.5 py-0.5 rounded text-[9px] font-semibold transition flex items-center gap-1 ${showSelectionPanel ? 'bg-amber-900 text-amber-300' : 'text-zinc-500 hover:text-zinc-300'}`, + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showSelectionPanel ? 'bg-amber-900 text-amber-300' : 'text-zinc-500 hover:text-zinc-300'}`, title: `Selection Panel (${panelPositions.selection})` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -9219,7 +9389,7 @@ const App = () => { className: "text-[7px] opacity-60" }, showSelectionPanel ? panelPositions.selection[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { onClick: () => setShowAIPanel(p => !p), - className: `px-1.5 py-0.5 rounded text-[9px] font-semibold transition flex items-center gap-1 ${showAIPanel ? 'bg-purple-900 text-purple-300' : 'text-zinc-500 hover:text-zinc-300'}`, + 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" @@ -9228,7 +9398,29 @@ const App = () => { className: "w-3 h-3" })), /*#__PURE__*/React.createElement("span", { className: "text-[7px] opacity-60" - }, showAIPanel ? panelPositions.ai[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("span", { + }, showAIPanel ? panelPositions.ai[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { + onClick: () => setShowFxRack(p => !p), + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showFxRack ? 'bg-rose-900 text-rose-300' : 'text-zinc-500 hover:text-zinc-300'}`, + title: `FX Rack Panel (${panelPositions.fx_rack || 'bottom'})` + }, /*#__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("span", { + className: "text-[7px] opacity-60" + }, showFxRack ? (panelPositions.fx_rack || 'bottom')[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { + onClick: () => setShowMidiEvents(p => !p), + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMidiEvents ? 'bg-sky-900 text-sky-300' : 'text-zinc-500 hover:text-zinc-300'}`, + title: `MIDI Events Panel (${panelPositions.midi_events || 'bottom'})` + }, /*#__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: "text-[7px] opacity-60" + }, showMidiEvents ? (panelPositions.midi_events || 'bottom')[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("span", { className: "w-[1px] h-3 bg-zinc-800 mx-1" }), /*#__PURE__*/React.createElement("span", { className: "flex items-center gap-1" @@ -9252,7 +9444,7 @@ const App = () => { }, onClick: e => e.stopPropagation() }, /*#__PURE__*/React.createElement("div", { - className: "px-3 py-1 text-[10px] text-zinc-500 font-bold uppercase border-b border-zinc-800 pb-1 mb-1" + 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", { @@ -9269,7 +9461,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Cut"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+X")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabCopy(contextMenu.subTabId); @@ -9284,7 +9476,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Copy"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+C")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabPaste(contextMenu.subTabId); @@ -9299,7 +9491,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Paste"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+V")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabDelete(contextMenu.subTabId); @@ -9314,7 +9506,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Delete Selected Segment"), /*#__PURE__*/React.createElement("span", { - className: "text-amber-400 text-[12px] font-semibold font-mono ml-auto" + 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", { @@ -9331,7 +9523,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Loop Selection 4 times"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + 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", style: { @@ -9350,7 +9542,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Edit"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9362,7 +9554,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Split"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9374,7 +9566,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Merge"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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("button", { @@ -9388,7 +9580,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Copy"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9400,7 +9592,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Cut"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9412,7 +9604,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Paste"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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", { @@ -9426,7 +9618,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Delete"), /*#__PURE__*/React.createElement("span", { - className: "text-amber-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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", { diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index e1df5ce..a3a1905 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -1955,11 +1955,11 @@ const SubTabToolbar = ({ })))), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("span", { - className: "text-[9px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Snap"), /*#__PURE__*/React.createElement("select", { value: snapValue || 'free', onChange: e => onSnapChange(e.target.value), - className: "bg-zinc-850 text-zinc-300 text-[10px] px-1 py-0.5 rounded border border-zinc-800 focus:outline-none font-mono" + className: "bg-zinc-850 text-zinc-300 text-xs px-1 py-0.5 rounded border border-zinc-800 focus:outline-none font-mono" }, /*#__PURE__*/React.createElement("option", { value: "free" }, "Free"), /*#__PURE__*/React.createElement("option", { @@ -1979,7 +1979,7 @@ const SubTabToolbar = ({ }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Normalize:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-12", @@ -1989,11 +1989,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabNormalizeWithValue(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.normalizeDb || 0, " dB")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Gain:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-40", @@ -2003,11 +2003,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabGainWithValue(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.gainDb || 0, " dB")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Pitch:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-12", @@ -2017,11 +2017,11 @@ const SubTabToolbar = ({ onChange: e => handleSubTabPitch(st.id, parseFloat(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.pitch || 0, " st")), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Stretch:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "50", @@ -2031,15 +2031,15 @@ const SubTabToolbar = ({ onChange: e => handleSubTabStretch(st.id, parseInt(e.target.value)), className: "w-20 h-1.5" }), /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 w-10 text-right" + className: "text-xs text-zinc-500 w-10 text-right" }, st.effects?.speedStretch || 100, "%"))), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("button", { onClick: () => handleSubTabFade(st.id, 'in'), - className: "px-2 py-1 text-[10px] rounded hover:bg-zinc-700" + className: "px-2 py-1 text-xs rounded hover:bg-zinc-700" }, "Fade In"), /*#__PURE__*/React.createElement("button", { onClick: () => handleSubTabFade(st.id, 'out'), - className: "px-2 py-1 text-[10px] rounded hover:bg-zinc-700" + className: "px-2 py-1 text-xs rounded hover:bg-zinc-700" }, "Fade Out")), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1 border-r border-zinc-700 pr-3" }, /*#__PURE__*/React.createElement("button", { @@ -2099,11 +2099,11 @@ const SubTabToolbar = ({ })))), /*#__PURE__*/React.createElement("div", { className: "flex items-center space-x-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-400" + className: "text-xs text-zinc-400" }, "Rate:"), /*#__PURE__*/React.createElement("select", { value: st.playbackRate || 1, onChange: e => onRateChange(parseFloat(e.target.value)), - className: "bg-zinc-700 text-zinc-100 text-[10px] px-1 py-0.5 rounded border border-zinc-600" + className: "bg-zinc-700 text-zinc-100 text-xs px-1 py-0.5 rounded border border-zinc-600" }, /*#__PURE__*/React.createElement("option", { value: "0.5" }, "0.5x"), /*#__PURE__*/React.createElement("option", { @@ -2585,7 +2585,7 @@ const AIConfigModal = ({ }, /*#__PURE__*/React.createElement("div", { className: "space-y-1.5 border-r border-[#383838] pr-3" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] uppercase font-bold text-slate-400 block mb-2" + className: "text-xs uppercase font-bold text-slate-400 block mb-2" }, "Providers"), providers.map(p => /*#__PURE__*/React.createElement("button", { key: p.id, onClick: () => setSelectedId(p.id), @@ -2866,7 +2866,7 @@ const SystemManagerModal = ({ }, /*#__PURE__*/React.createElement("td", { className: "p-3 font-semibold text-teal-300" }, u.username, u.must_change_password && /*#__PURE__*/React.createElement("span", { - className: "ml-2 text-[10px] bg-amber-900/60 text-amber-300 px-1.5 py-0.5 rounded" + className: "ml-2 text-xs bg-amber-900/60 text-amber-300 px-1.5 py-0.5 rounded" }, "Mật khẩu gốc")), /*#__PURE__*/React.createElement("td", { className: "p-3 text-slate-300" }, u.email), /*#__PURE__*/React.createElement("td", { @@ -2884,7 +2884,7 @@ const SystemManagerModal = ({ className: "w-16 bg-[#1e1e1e] border border-[#444] rounded px-1 py-0.5 text-xs text-slate-200" }), /*#__PURE__*/React.createElement("span", null, "MB"), /*#__PURE__*/React.createElement("button", { onClick: () => handleSaveQuota(u.id), - className: "px-2 py-0.5 bg-teal-600 rounded text-[10px]" + className: "px-2 py-0.5 bg-teal-600 rounded text-xs" }, "Lưu")) : /*#__PURE__*/React.createElement("span", { className: "font-semibold" }, u.quota_mb, " MB")), /*#__PURE__*/React.createElement("td", { @@ -2894,13 +2894,13 @@ const SystemManagerModal = ({ setEditingQuotaUser(u.id); setNewQuotaMb(u.quota_mb); }, - className: "px-2 py-1 bg-slate-700 hover:bg-slate-600 rounded text-[11px]" + className: "px-2 py-1 bg-slate-700 hover:bg-slate-600 rounded text-xs" }, "Sửa Quota"), /*#__PURE__*/React.createElement("button", { onClick: () => handleToggleRole(u), - className: "px-2 py-1 bg-amber-700/60 hover:bg-amber-600 rounded text-[11px]" + className: "px-2 py-1 bg-amber-700/60 hover:bg-amber-600 rounded text-xs" }, "Đổi Role"), u.role !== 'admin' && /*#__PURE__*/React.createElement("button", { onClick: () => handleDeleteUser(u.id), - className: "px-2 py-1 bg-red-700/60 hover:bg-red-600 rounded text-[11px]" + className: "px-2 py-1 bg-red-700/60 hover:bg-red-600 rounded text-xs" }, "Xóa"))))))))); }; const App = () => { @@ -2978,17 +2978,27 @@ const App = () => { const [showAIPanel, setShowAIPanel] = useState(true); const [showSelectionPanel, setShowSelectionPanel] = useState(true); const [showPythonToolsPanel, setShowPythonToolsPanel] = useState(true); + const [showMediaExplorer, setShowMediaExplorer] = useState(true); + const [showFxRack, setShowFxRack] = useState(false); + const [showMidiEvents, setShowMidiEvents] = useState(false); + const [rightSidebarWidth, setRightSidebarWidth] = useState(320); + const [mediaExplorerHeight, setMediaExplorerHeight] = useState(50); const [panelPositions, setPanelPositions] = useState({ export: 'bottom', - ai: 'bottom', + ai: 'right', python_tools: 'bottom', - selection: 'bottom' + selection: 'bottom', + media_explorer: 'right', + fx_rack: 'bottom', + midi_events: 'bottom' }); const [panelDropZone, setPanelDropZone] = useState(null); const [dragGhostPos, setDragGhostPos] = useState(null); const [dragGhostPanel, setDragGhostPanel] = useState(null); const panelDragRef = useRef(null); const workspaceRef = useRef(null); + const colResizerRef = useRef(null); + const rowResizerRef = useRef(null); const [aiConfig, setAiConfig] = useState({ baseUrl: localStorage.getItem('ai_base_url') || `${API_BASE_URL}`, apiKey: localStorage.getItem('ai_api_key') || '', @@ -3311,6 +3321,45 @@ const App = () => { document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); }; + + // ── Right Sidebar Column Resizer ── + const startColResize = e => { + e.preventDefault(); + const startX = e.clientX; + const startWidth = rightSidebarWidth; + const onMove = ev => { + const deltaX = startX - ev.clientX; + const newWidth = Math.max(200, Math.min(600, startWidth + deltaX)); + setRightSidebarWidth(newWidth); + }; + const onUp = () => { + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; + + // ── Right Sidebar Row Resizer (Media Explorer / AI Panel) ── + const startRowResize = e => { + e.preventDefault(); + const startY = e.clientY; + const startHeight = mediaExplorerHeight; + const sidebarEl = document.getElementById('right-sidebar'); + const sidebarHeight = sidebarEl ? sidebarEl.getBoundingClientRect().height : 400; + const onMove = ev => { + const deltaY = ev.clientY - startY; + const pct = (startHeight / 100 * sidebarHeight + deltaY) / sidebarHeight * 100; + const newPct = Math.max(20, Math.min(80, pct)); + setMediaExplorerHeight(newPct); + }; + const onUp = () => { + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; const rulerRef = useRef(null); const activeSourcesRef = useRef([]); const activeTrackNodesRef = useRef({}); // { [trackId]: { gainNode, pannerNode } } @@ -7493,7 +7542,7 @@ const App = () => { className: "relative" }, /*#__PURE__*/React.createElement("button", { onClick: () => setMenuOpen(menuOpen === menu.label ? null : menu.label), - className: `px-3 py-1 text-[11px] font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}` + className: `px-3 py-1 text-xs font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}` }, menu.label), menuOpen === menu.label && /*#__PURE__*/React.createElement("div", { className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-52 z-50", onClick: () => setMenuOpen(null) @@ -7516,16 +7565,16 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, item.label), item.shortcut && /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto pl-8" }, item.shortcut)))))), /*#__PURE__*/React.createElement("div", { className: "flex-1" }), /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2 px-2" }, /*#__PURE__*/React.createElement("span", { - className: `text-[9px] font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus === 'connected' ? 'bg-emerald-950 text-emerald-400' : serverStatus === 'checking' ? 'bg-amber-950 text-amber-400' : 'bg-red-950 text-red-400'}` + className: `text-xs font-bold uppercase px-1.5 py-0.5 rounded ${serverStatus === 'connected' ? 'bg-emerald-950 text-emerald-400' : serverStatus === 'checking' ? 'bg-amber-950 text-amber-400' : 'bg-red-950 text-red-400'}` }, "Server: ", serverStatus), /*#__PURE__*/React.createElement("button", { onClick: () => setShowAIConfig(!showAIConfig), - className: `px-1.5 py-0.5 rounded text-[10px] border transition ${showAIConfig ? 'bg-purple-900 text-purple-200 border-purple-700' : 'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}` + className: `px-1.5 py-0.5 rounded text-xs border transition ${showAIConfig ? 'bg-purple-900 text-purple-200 border-purple-700' : 'bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7538,7 +7587,7 @@ const App = () => { className: "h-7 bg-[#222] border-b border-zinc-800 flex items-stretch px-2 gap-0.5 shrink-0 overflow-x-auto" }, /*#__PURE__*/React.createElement("button", { onClick: () => setActiveTab('main'), - className: `px-3 text-[10px] font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab === 'main' ? 'text-cyan-400 border-cyan-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` + className: `px-3 text-xs font-bold uppercase tracking-wider border-b-2 transition flex items-center gap-1 ${activeTab === 'main' ? 'text-cyan-400 border-cyan-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7549,7 +7598,7 @@ const App = () => { className: "flex items-stretch" }, /*#__PURE__*/React.createElement("button", { onClick: () => setActiveTab(st.id), - className: `px-2 text-[10px] font-medium border-b-2 transition flex items-center gap-1 ${activeTab === st.id ? 'text-amber-400 border-amber-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` + className: `px-2 text-xs font-medium border-b-2 transition flex items-center gap-1 ${activeTab === st.id ? 'text-amber-400 border-amber-500 bg-zinc-800/50' : 'text-zinc-500 border-transparent hover:text-zinc-300 hover:bg-zinc-800/30'}` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -7559,7 +7608,7 @@ const App = () => { className: "max-w-[100px] truncate" }, st.label)), /*#__PURE__*/React.createElement("button", { onClick: () => closeSubTab(st.id), - className: "px-1 text-zinc-600 hover:text-red-400 transition text-[9px]", + className: "px-1 text-zinc-600 hover:text-red-400 transition text-xs", title: "Close tab" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -7580,7 +7629,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Endpoint Base URL"), /*#__PURE__*/React.createElement("input", { type: "text", value: aiConfig.baseUrl, @@ -7588,12 +7637,12 @@ const App = () => { ...prev, baseUrl: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "https://api.openai.com/v1" })), /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "API Token Key"), /*#__PURE__*/React.createElement("input", { type: "password", value: aiConfig.apiKey, @@ -7601,12 +7650,12 @@ const App = () => { ...prev, apiKey: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "sk-..." })), /*#__PURE__*/React.createElement("div", { className: "flex flex-col gap-1" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] text-zinc-500 font-bold uppercase" + className: "text-xs text-zinc-500 font-bold uppercase" }, "Model Name"), /*#__PURE__*/React.createElement("input", { type: "text", value: aiConfig.model, @@ -7614,7 +7663,7 @@ const App = () => { ...prev, model: e.target.value })), - className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-[11px]", + className: "bg-zinc-850 text-zinc-200 p-1.5 rounded border border-zinc-800 focus:outline-none focus:border-purple-600 font-mono text-xs", placeholder: "gpt-4o-mini" })))), /*#__PURE__*/React.createElement("div", { className: "h-9 bg-[#222] border-b border-zinc-800 flex items-center px-3 gap-2 shrink-0 select-none" @@ -8001,8 +8050,11 @@ const App = () => { addPanel('ai', panelPositions.ai, showAIPanel); addPanel('python_tools', panelPositions.python_tools || 'bottom', showPythonToolsPanel); addPanel('selection', panelPositions.selection, showSelectionPanel); + addPanel('media_explorer', panelPositions.media_explorer || 'right', showMediaExplorer); + addPanel('fx_rack', panelPositions.fx_rack || 'bottom', showFxRack); + addPanel('midi_events', panelPositions.midi_events || 'bottom', showMidiEvents); 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); + 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 === 'media_explorer') setShowMediaExplorer(false);else if (id === 'fx_rack') setShowFxRack(false);else if (id === 'midi_events') setShowMidiEvents(false); }; const renderPanelContent = panelId => { const h = id => e => { @@ -8014,7 +8066,7 @@ const App = () => { 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-[10px] text-zinc-200 flex items-center gap-1" + 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", { @@ -8043,7 +8095,7 @@ const App = () => { ...p, sampleRate: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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: "44100" }, "44.1k"), /*#__PURE__*/React.createElement("option", { @@ -8056,7 +8108,7 @@ const App = () => { ...p, bitDepth: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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: "16" }, "16"), /*#__PURE__*/React.createElement("option", { @@ -8069,13 +8121,13 @@ const App = () => { ...p, format: e.target.value })), - className: "w-full bg-[#141414] border border-zinc-800 rounded px-1 py-0.5 text-[9px] text-zinc-300 focus:outline-none" + 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("button", { onClick: triggerWavExport, disabled: isExporting, - className: "w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-[10px] flex items-center justify-center gap-1" + 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", { @@ -8088,7 +8140,7 @@ const App = () => { 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-[10px] text-zinc-200 flex items-center gap-1" + 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", { @@ -8122,7 +8174,7 @@ const App = () => { "data-lucide": "x", className: "w-3 h-3" }))))), /*#__PURE__*/React.createElement("div", { - className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-[9px] font-mono min-h-[20px]" + className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-xs font-mono min-h-[20px]" }, /*#__PURE__*/React.createElement("div", { className: "text-zinc-500" }, "// ", /*#__PURE__*/React.createElement("span", { @@ -8133,7 +8185,7 @@ const App = () => { className: "grid grid-cols-3 gap-1" }, /*#__PURE__*/React.createElement("button", { onClick: handleAIScan, - className: "py-1 bg-purple-900 hover:bg-purple-800 text-purple-100 font-bold rounded text-[9px] border border-purple-700 flex items-center justify-center gap-1" + 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", { @@ -8142,7 +8194,7 @@ const App = () => { })), " Scan"), /*#__PURE__*/React.createElement("button", { onClick: handleAICutToNewTrack, disabled: analysisState.isRunning, - className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-[9px] border border-fuchsia-600 flex items-center justify-center gap-1" + className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-xs border border-fuchsia-600 flex items-center justify-center gap-1" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8151,7 +8203,7 @@ const App = () => { })), " Cut"), /*#__PURE__*/React.createElement("button", { onClick: handleAIAnalysicLoop, disabled: analysisState.isRunning, - className: "py-1 bg-violet-800 hover:bg-violet-700 text-violet-100 font-bold rounded text-[9px] border border-violet-600 flex items-center justify-center gap-1" + 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" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8160,7 +8212,7 @@ const App = () => { })), " Loop")), /*#__PURE__*/React.createElement("div", { className: "border-t border-zinc-800 pt-1.5 mt-1" }, /*#__PURE__*/React.createElement("div", { - className: "text-[9px] font-bold text-zinc-400 uppercase mb-1 flex items-center gap-1" + 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", { @@ -8170,7 +8222,7 @@ const App = () => { value: aiPrompt, onChange: e => setAiPrompt(e.target.value), placeholder: "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-[10px] resize-none", + 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-none", rows: 2, onKeyDown: e => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { @@ -8183,7 +8235,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("button", { onClick: () => window.executeAIPrompt(), disabled: aiProcessing, - className: "flex-1 py-1 bg-purple-700 hover:bg-purple-600 text-white font-bold rounded text-[10px] border border-purple-500 flex items-center justify-center gap-1" + 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", { @@ -8194,13 +8246,13 @@ const App = () => { setAiPrompt(''); setAiActionLog([]); }, - className: "px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded text-[9px] border border-zinc-700" + 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-[8px] text-zinc-600 mt-0.5" + className: "text-xs text-zinc-600 mt-0.5" }, "Ctrl+Enter để gửi nhanh")), /*#__PURE__*/React.createElement("div", { className: "border-t border-zinc-800 pt-1.5 mt-1" }, /*#__PURE__*/React.createElement("div", { - className: "text-[9px] font-bold text-zinc-400 uppercase mb-1 flex items-center justify-between" + className: "text-xs font-bold text-zinc-400 uppercase mb-1 flex items-center justify-between" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center gap-1" }, /*#__PURE__*/React.createElement("i", { @@ -8227,22 +8279,22 @@ const App = () => { }]); } }, - className: "text-[8px] text-amber-400 hover:text-amber-300 border border-amber-800 rounded px-1 py-0.5" + className: "text-xs text-amber-400 hover:text-amber-300 border border-amber-800 rounded px-1 py-0.5" }, "Undo"))), /*#__PURE__*/React.createElement("div", { className: "flex-1 overflow-y-auto no-scrollbar bg-[#0f0f0f] rounded border border-zinc-800 p-1 max-h-[120px]" }, aiActionLog.length === 0 ? /*#__PURE__*/React.createElement("div", { - className: "text-[9px] text-zinc-600 italic" + className: "text-xs text-zinc-600 italic" }, "Chưa có hành động nào.") : aiActionLog.map((entry, i) => /*#__PURE__*/React.createElement("div", { key: i, - className: `text-[9px] 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'}` + 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)))); - return /*#__PURE__*/React.createElement("div", { + 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-[10px] text-amber-300 flex items-center gap-1" + 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", { @@ -8262,9 +8314,9 @@ const App = () => { "data-lucide": "x", className: "w-3 h-3" })))), /*#__PURE__*/React.createElement("div", { - className: "p-1 bg-[#141414] rounded border border-zinc-800 text-[8px] font-mono text-zinc-400" + className: "p-1 bg-[#141414] rounded border border-zinc-800 text-xs font-mono text-zinc-400" }, "// Non-AI Audio Processing Tools"), /*#__PURE__*/React.createElement("div", { - className: "grid grid-cols-2 gap-1 text-[9px]" + 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" @@ -8284,7 +8336,7 @@ const App = () => { 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-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1" + 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", { @@ -8307,7 +8359,7 @@ const App = () => { 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-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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", { @@ -8315,13 +8367,13 @@ const App = () => { 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-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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-[9px] font-semibold mt-0.5" + 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", { @@ -8340,7 +8392,7 @@ const App = () => { setSelectionStart(t); setSelectionEnd(t + beatDuration * 4); }, - className: "w-full bg-[#242424] text-white text-center font-mono text-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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", { @@ -8355,29 +8407,136 @@ const App = () => { setSelectionEnd(t + beatDuration * 4); setNumberBar(b - beginBar + 1); }, - className: "w-full bg-[#242424] text-white text-center font-mono text-[9px] rounded py-0.5 border border-zinc-700 focus:outline-none" + 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-[9px] font-semibold mt-0.5" + className: "text-zinc-200 font-mono text-xs font-semibold mt-0.5" }, numberBar)))); + if (panelId === 'media_explorer') 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('media_explorer', e) + }, /*#__PURE__*/React.createElement("h3", { + className: "font-bold text-xs text-emerald-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": "folder-open", + className: "w-3.5 h-3.5 text-emerald-400" + })), " Media Explorer"), /*#__PURE__*/React.createElement("button", { + onClick: () => closePanel('media_explorer'), + 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 overflow-y-auto text-xs text-zinc-400 font-mono p-2" + }, "// Placeholder: Media files browser")); + 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 sideClass = isSide ? 'w-56 shrink-0 overflow-y-auto border-zinc-800' : 'shrink-0'; 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`, + 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" + }, panels.map((p, idx) => /*#__PURE__*/React.createElement(React.Fragment, { + key: p + }, /*#__PURE__*/React.createElement("div", { + className: `${p === 'media_explorer' ? 'flex-shrink-0' : 'flex-1 min-h-0'} border border-zinc-700 rounded-lg bg-[#262626] shadow-sm`, + style: p === 'media_explorer' ? { + height: `${mediaExplorerHeight}%` + } : {} + }, renderPanelContent(p)), idx < panels.length - 1 && /*#__PURE__*/React.createElement("div", { + className: "h-1.5 cursor-ns-resize hover:bg-cyan-500/50 transition-colors rounded shrink-0", + onMouseDown: startRowResize + }))))); + const sideClass = isSide ? 'shrink-0 overflow-y-auto' : 'shrink-0'; return /*#__PURE__*/React.createElement("div", { + id: "daw-bottom", className: `${sideClass} ${borderClass} ${bgClass} flex ${isSide ? 'flex-col p-2 gap-2' : 'flex-row p-1.5 gap-3'} select-none ${highlight ? 'ring-2 ring-cyan-500 ring-inset' : ''}` }, panels.map(p => /*#__PURE__*/React.createElement("div", { key: p, - className: `${isSide ? 'w-full' : 'flex-1 min-w-0'} border border-zinc-700 rounded-lg bg-[#262626] shadow-sm p-2` + 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", { @@ -8400,14 +8559,14 @@ const App = () => { top: dragGhostPos.y } }, /*#__PURE__*/React.createElement("div", { - className: "flex items-center gap-2 text-[10px] text-zinc-200 font-bold" + 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-[8px] text-zinc-500 mt-1" + 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", { @@ -8433,7 +8592,7 @@ const App = () => { className: "w-3.5 h-3.5 text-cyan-400" })), "TRACKS (", tracks.length, ")"), /*#__PURE__*/React.createElement("button", { onClick: addNewTrack, - className: "px-2.5 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-[11px] font-semibold flex items-center gap-1 shadow transition" + 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", { @@ -8446,7 +8605,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] font-bold text-purple-400 font-mono" + 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", { @@ -8456,11 +8615,11 @@ const App = () => { value: bpm, onChange: e => setBpm(e.target.value), onBlur: () => localStorage.setItem('studio_bpm', bpm), - className: "w-12 bg-zinc-800 border border-zinc-700 rounded text-[10px] text-zinc-300 text-center font-mono focus:outline-none focus:border-purple-500", + 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-[9px] text-zinc-500" + className: "text-xs text-zinc-500" }, "BPM")))), /*#__PURE__*/React.createElement("div", { className: "flex-1 flex flex-col divide-y divide-[#141414] bg-[#262626]" }, tracks.length === 0 ? /*#__PURE__*/React.createElement("div", { @@ -8494,7 +8653,7 @@ const App = () => { }, /*#__PURE__*/React.createElement("div", { className: "flex items-center gap-2" }, /*#__PURE__*/React.createElement("span", { - className: "text-[10px] font-bold text-zinc-500 font-mono" + className: "text-xs font-bold text-zinc-500 font-mono" }, (idx + 1).toString().padStart(2, '0')), /*#__PURE__*/React.createElement("label", { onClick: e => { e.stopPropagation(); @@ -8548,13 +8707,13 @@ const App = () => { e.stopPropagation(); toggleTrackMute(track.id); }, - className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition ${track.muted ? 'bg-red-950 text-red-400 border-red-700' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border transition ${track.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(track.id); }, - className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition ${soloedTrackId === track.id || track.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border transition ${soloedTrackId === track.id || track.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("button", { onClick: e => { e.stopPropagation(); @@ -8567,12 +8726,12 @@ const App = () => { "data-lucide": "trash-2", className: "w-3.5 h-3.5" }))))), /*#__PURE__*/React.createElement("div", { - className: "flex flex-col gap-0.5 text-[10px]", + 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-[9px]" + className: "w-8 text-right text-zinc-500 text-xs" }, "Vol:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-50", @@ -8585,11 +8744,11 @@ const App = () => { height: '4px' } }), /*#__PURE__*/React.createElement("span", { - className: "w-12 text-right font-mono text-zinc-300 text-[9px]" + 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-[9px]" + className: "w-8 text-right text-zinc-500 text-xs" }, "Pan:"), /*#__PURE__*/React.createElement("input", { type: "range", min: "-100", @@ -8602,7 +8761,7 @@ const App = () => { height: '4px' } }), /*#__PURE__*/React.createElement("span", { - className: "w-12 text-right font-mono text-zinc-300 text-[9px]" + 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.5 mt-1", onClick: e => e.stopPropagation() @@ -8614,7 +8773,7 @@ const App = () => { 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-[10px] flex items-center gap-1 cursor-pointer border border-zinc-700 font-medium" + 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", { @@ -8625,7 +8784,7 @@ const App = () => { e.stopPropagation(); showToast('FX panel for track ' + track.id, 'info'); }, - className: "px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-purple-400 border border-purple-900 rounded text-[10px] font-bold flex items-center gap-1" + 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", { @@ -8635,7 +8794,7 @@ const App = () => { className: "text-zinc-500 font-normal" }, "None")), /*#__PURE__*/React.createElement("button", { onClick: () => generateSynthToTrack(track.id, 'synth'), - className: "px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-amber-400 border border-amber-800 rounded text-[10px] font-bold flex items-center gap-1" + 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" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { @@ -8773,7 +8932,7 @@ const App = () => { 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-[9px] flex items-center gap-1 border border-zinc-700/50" + 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", { @@ -8838,10 +8997,10 @@ const App = () => { }, /*#__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-[10px] font-bold text-zinc-500 uppercase" + 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-[9px] bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded border border-zinc-700 flex items-center gap-1" + 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", { @@ -8907,13 +9066,13 @@ const App = () => { e.stopPropagation(); toggleTrackMute(vTrack.id); }, - className: `px-1.5 py-0.5 text-[10px] 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'}` + 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-[10px] rounded font-mono font-bold border ${soloedTrackId === vTrack.id || vTrack.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}` + className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${soloedTrackId === vTrack.id || 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", { @@ -9088,7 +9247,7 @@ const App = () => { handleAIScan(); }, disabled: analysisState.isRunning, - className: "py-1 bg-purple-900 hover:bg-purple-800 text-purple-100 font-bold rounded text-[12px] border border-purple-700 flex items-center justify-center gap-1" + 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", { @@ -9100,7 +9259,7 @@ const App = () => { handleAICutToNewTrack(); }, disabled: analysisState.isRunning, - className: "py-1 bg-fuchsia-800 hover:bg-fuchsia-700 text-white font-bold rounded text-[12px] flex items-center justify-center gap-1" + 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", { @@ -9115,7 +9274,7 @@ const App = () => { handleAIAnalysicLoop(); }, disabled: analysisState.isRunning, - className: "py-1 bg-violet-800 hover:bg-violet-700 text-violet-100 font-bold rounded text-[12px] border border-violet-600 flex items-center justify-center gap-1 col-span-2" + 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", { @@ -9138,7 +9297,7 @@ const App = () => { "data-lucide": "save", className: "w-4 h-4" })), " Save"))) : /*#__PURE__*/React.createElement("div", { - className: "flex-1 flex items-center justify-center text-[10px] text-zinc-500" + className: "flex-1 flex items-center justify-center text-xs text-zinc-500" }, "Track not found")), /*#__PURE__*/React.createElement("div", { ref: timelineWrapperRef, className: "flex-1 relative z-10 overflow-x-auto overflow-y-auto min-w-0" @@ -9277,9 +9436,12 @@ const App = () => { width: `${Math.abs(st.selectionEnd - st.selectionStart) * zoom}px` } }))))); - })())), renderDock('right', 'Right')), renderDock('bottom', 'Bottom')); + })())), /*#__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')); })(), /*#__PURE__*/React.createElement("div", { - className: "h-6 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-[10px] text-zinc-500 select-none shrink-0" + 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", { @@ -9287,20 +9449,20 @@ const App = () => { }, "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-[9px]" + 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-[9px]" + className: "text-purple-400 font-semibold uppercase text-xs" }, "Global Sel"), soloedTrackId && /*#__PURE__*/React.createElement("span", { className: "text-amber-500 font-semibold" }, "Solo: ID ", soloedTrackId), isLoopingSelection && selectionMode === 'local' && /*#__PURE__*/React.createElement("span", { - className: "text-emerald-400 font-semibold uppercase text-[9px]" + 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-[9px]" + 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-[9px] font-semibold transition flex items-center gap-1 ${showExportPanel ? 'bg-cyan-900 text-cyan-300' : 'text-zinc-500 hover:text-zinc-300'}`, + 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 Panel (${panelPositions.export})` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -9311,7 +9473,7 @@ const App = () => { className: "text-[7px] opacity-60" }, showExportPanel ? panelPositions.export[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { onClick: () => setShowSelectionPanel(p => !p), - className: `px-1.5 py-0.5 rounded text-[9px] font-semibold transition flex items-center gap-1 ${showSelectionPanel ? 'bg-amber-900 text-amber-300' : 'text-zinc-500 hover:text-zinc-300'}`, + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showSelectionPanel ? 'bg-amber-900 text-amber-300' : 'text-zinc-500 hover:text-zinc-300'}`, title: `Selection Panel (${panelPositions.selection})` }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" @@ -9322,7 +9484,7 @@ const App = () => { className: "text-[7px] opacity-60" }, showSelectionPanel ? panelPositions.selection[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { onClick: () => setShowAIPanel(p => !p), - className: `px-1.5 py-0.5 rounded text-[9px] font-semibold transition flex items-center gap-1 ${showAIPanel ? 'bg-purple-900 text-purple-300' : 'text-zinc-500 hover:text-zinc-300'}`, + 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" @@ -9331,7 +9493,29 @@ const App = () => { className: "w-3 h-3" })), /*#__PURE__*/React.createElement("span", { className: "text-[7px] opacity-60" - }, showAIPanel ? panelPositions.ai[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("span", { + }, showAIPanel ? panelPositions.ai[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { + onClick: () => setShowFxRack(p => !p), + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showFxRack ? 'bg-rose-900 text-rose-300' : 'text-zinc-500 hover:text-zinc-300'}`, + title: `FX Rack Panel (${panelPositions.fx_rack || 'bottom'})` + }, /*#__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("span", { + className: "text-[7px] opacity-60" + }, showFxRack ? (panelPositions.fx_rack || 'bottom')[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("button", { + onClick: () => setShowMidiEvents(p => !p), + className: `px-1.5 py-0.5 rounded text-xs font-semibold transition flex items-center gap-1 ${showMidiEvents ? 'bg-sky-900 text-sky-300' : 'text-zinc-500 hover:text-zinc-300'}`, + title: `MIDI Events Panel (${panelPositions.midi_events || 'bottom'})` + }, /*#__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: "text-[7px] opacity-60" + }, showMidiEvents ? (panelPositions.midi_events || 'bottom')[0].toUpperCase() : '')), /*#__PURE__*/React.createElement("span", { className: "w-[1px] h-3 bg-zinc-800 mx-1" }), /*#__PURE__*/React.createElement("span", { className: "flex items-center gap-1" @@ -9355,7 +9539,7 @@ const App = () => { }, onClick: e => e.stopPropagation() }, /*#__PURE__*/React.createElement("div", { - className: "px-3 py-1 text-[10px] text-zinc-500 font-bold uppercase border-b border-zinc-800 pb-1 mb-1" + 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", { @@ -9372,7 +9556,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Cut"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+X")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabCopy(contextMenu.subTabId); @@ -9387,7 +9571,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Copy"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+C")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabPaste(contextMenu.subTabId); @@ -9402,7 +9586,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Paste"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + className: "text-purple-400 text-xs font-semibold font-mono ml-auto" }, "Ctrl+V")), /*#__PURE__*/React.createElement("button", { onClick: () => { handleSubTabDelete(contextMenu.subTabId); @@ -9417,7 +9601,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Delete Selected Segment"), /*#__PURE__*/React.createElement("span", { - className: "text-amber-400 text-[12px] font-semibold font-mono ml-auto" + 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", { @@ -9434,7 +9618,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Loop Selection 4 times"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto" + 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", style: { @@ -9453,7 +9637,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Edit"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9465,7 +9649,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Split"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9477,7 +9661,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Merge"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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("button", { @@ -9491,7 +9675,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Copy"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9503,7 +9687,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Cut"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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" @@ -9515,7 +9699,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Paste"), /*#__PURE__*/React.createElement("span", { - className: "text-purple-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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", { @@ -9529,7 +9713,7 @@ const App = () => { })), /*#__PURE__*/React.createElement("span", { className: "flex-1" }, "Delete"), /*#__PURE__*/React.createElement("span", { - className: "text-amber-400 text-[12px] font-semibold font-mono ml-auto pl-8" + 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", { diff --git a/app/storage/sonicforge.db b/app/storage/sonicforge.db index d7c66d890ff35b07f6d0a8d7ea388e9059b8babc..0e82cf38a9793277941bb2c9dd9db223f9f9f62f 100644 GIT binary patch delta 31 ncmZp8z|`=7X@WGP)kGO*Myrhpp7m@qn2u*p{l0m9eV_pVyHyNt delta 31 ncmZp8z|`=7X@WGP@kAMCM&peMp7m_gpVW%nl-az#KF|OFx^)Z+