From 04403b0af7b5625c3488fd879000665b42aca51b Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 21 Jul 2026 21:38:37 +0700 Subject: [PATCH] =?UTF-8?q?fix=20t=E1=BA=A1m=20l=E1=BB=97i=20c=C3=A0i=20?= =?UTF-8?q?=C4=91=E1=BA=B7t=20AI=20prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/app.jsx | 190 +------------- app/static/js/app.precompiled.js | 358 +++++++++++++++++++++++++- app/static/js/services/aiGateway.js | 5 +- app/storage/sonicforge.db | Bin 45056 -> 45056 bytes app/templates/index.html | 129 ++++++++++ SUB_EDITOR.md => md/4_SUB_EDITOR.md | 0 USER_MANAGER.md => md/USER_MANAGER.md | 0 7 files changed, 484 insertions(+), 198 deletions(-) rename SUB_EDITOR.md => md/4_SUB_EDITOR.md (100%) rename USER_MANAGER.md => md/USER_MANAGER.md (100%) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 4d4fcb0..ea49814 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -6872,192 +6872,6 @@ const App = () => { }, 800); }; - // ── AI Copilot: Natural Language Prompt → Function Calls (28_AI_PANEL.md §2 & §3) ── - const AIComparator = () => { - const [prompt, setPrompt] = useState(''); - const [provider, setProvider] = useState('OpenAI'); - const [model, setModel] = useState('GPT-4o'); - const [response, setResponse] = useState(''); - const [history = [], setHistory] = useState([]); - - const handleSendPrompt = async () => { - setResponse(''); - try { - const context = window.AIGateway.buildAIPromptContext({ - bpm: parseInt(bpm) || 120, - selectedTrackId: selectedTrackId, - currentTime: currentTime, - selLeft: selLeft, - selRight: selRight, - tracks: tracks - }); - - const result = await window.AIGateway.executePrompt({ - prompt: prompt, - provider: provider, - model: model, - apiKey: aiConfig.apiKey, - baseUrl: aiConfig.baseUrl - }); - - setResponse(result.textResponse); - - result.functionCalls.forEach(call => { - DAWCommandDispatcher.execute(call.name, call.arguments); - setHistory([...history, { name: call.name, args: call.arguments }]); - }); - } - catch (e) { - setResponse(`Lỗi: ${e.message}`); - } - }; - const prompt = aiPrompt.trim(); - if (!prompt || aiProcessing) return; - setAiProcessing(true); - setAiActionLog(prev => [...prev, { type: 'status', text: 'AI đang phân tích lệnh...', time: Date.now() }]); - const dawContext = window.AIGateway.buildAIPromptContext({ - bpm: parseInt(bpm) || 120, - selectedTrackId, - currentTime, - selLeft, - selRight, - tracks - }); - try { - const result = await window.AIGateway.executePrompt({ - prompt, - provider: aiProvider.toLowerCase(), - model: aiModel, - apiKey: aiConfig.apiKey, - baseUrl: aiConfig.baseUrl, - dawContext - }); - const calls = result.functionCalls || []; - if (calls.length === 0 && result.textResponse) { - setAiActionLog(prev => [...prev, { type: 'text', text: result.textResponse, time: Date.now() }]); - } - for (const call of calls) { - const logEntry = { type: 'action', text: `[${call.name}] Đang thực thi...`, time: Date.now(), callName: call.name, args: call.arguments }; - setAiActionLog(prev => [...prev, logEntry]); - let execResult = { success: false, error: 'Unknown command' }; - if (window.DAWCommandDispatcher && window.DAWCommandDispatcher.execute) { - execResult = window.DAWCommandDispatcher.execute(call.name, call.arguments); - } else { - const api = { - createTrack: (args) => { - const name = args.name || `AI_Track_${Date.now()}`; - const newId = addNewTrack(); - if (name) updateTrackName(newId, name); - return { success: true, trackId: newId }; - }, - deleteTrack: (args) => { - const tid = args.track_id || selectedTrackId; - if (tid) deleteTrack(tid); - return { success: true }; - }, - processAudioDsp: (args) => { - const trackId = args.track_id || selectedTrackId; - const action = args.action; - const params = args.params || {}; - const track = tracks.find(t => t.id === trackId); - if (!track || !track.buffer) return { success: false, error: 'No buffer' }; - if (action === 'normalize') { - const d = track.buffer.getChannelData(0); - let mx = 0; for (let i = 0; i < d.length; i++) mx = Math.max(mx, Math.abs(d[i])); - if (mx > 0) { const g = 1.0 / mx; for (let i = 0; i < d.length; i++) d[i] *= g; } - return { success: true }; - } else if (action === 'invert_phase') { - const d = track.buffer.getChannelData(0); - for (let i = 0; i < d.length; i++) d[i] *= -1; - return { success: true }; - } else if (action === 'gain') { - const g = Math.pow(10, (params.gain_db ?? 0) / 20); - const d = track.buffer.getChannelData(0); - for (let i = 0; i < d.length; i++) d[i] = Math.max(-1, Math.min(1, d[i] * g)); - return { success: true }; - } else if (action === 'pitch_shift') { - const semi = params.semitones ?? 0; - const ratio = Math.pow(2, semi / 12); - const resample = (data, r) => { - const nl = Math.round(data.length * r); - const out = new Float32Array(nl); - for (let i = 0; i < nl; i++) { const si = i / r; const i0 = Math.floor(si); const i1 = Math.min(i0 + 1, data.length - 1); const f = si - i0; out[i] = data[i0] * (1 - f) + data[i1] * f; } - return out; - }; - const d = track.buffer.getChannelData(0); - const nd = resample(d, 1 / ratio); - const ctx = getAudioContext(); - const nb = ctx.createBuffer(1, nd.length, track.buffer.sampleRate); - nb.copyToChannel(nd, 0); - setTracks(p => p.map(t => t.id === trackId ? { ...t, buffer: nb } : t)); - return { success: true }; - } - return { success: false, error: `Unknown action: ${action}` }; - }, - add_midi_item: (args) => { - return { success: true, note: 'MIDI items mapped to silent audio clip' }; - }, - modify_midi_notes: (args) => { - return { success: true, note: 'MIDI notes mapped to synth tones' }; - }, - setTrackVolume: (args) => { - const trackId = args.track_id || selectedTrackId; - updateTrackVolumeDb(trackId, parseFloat(args.volume_db ?? args.volume ?? 0)); - return { success: true }; - }, - setTrackPan: (args) => { - const trackId = args.track_id || selectedTrackId; - updateTrackPan(trackId, parseInt(args.pan ?? 0)); - return { success: true }; - }, - toggleMute: (args) => { - const trackId = args.track_id || selectedTrackId; - toggleTrackMute(trackId); - return { success: true }; - }, - toggleSolo: (args) => { - const trackId = args.track_id || selectedTrackId; - toggleTrackSoloEvaluate(trackId); - return { success: true }; - }, - setBpm: (args) => { - const bpmVal = args.bpm || args.tempo || 120; - setBpm(String(bpmVal)); - return { success: true }; - }, - setPlayhead: (args) => { - handlePlayheadSet(args.time ?? args.position ?? 0); - return { success: true }; - }, - addMarker: (args) => { - const trackId = args.track_id || selectedTrackId; - const time = args.time ?? currentTime; - setTracks(prev => prev.map(t => { - if (t.id !== trackId) return t; - return { ...t, markers: [...(t.markers || []), { id: 'ai_marker_' + Date.now(), time, label: args.label || 'AI Marker' }] }; - })); - return { success: true }; - } - }; - if (api[call.name]) { - execResult = api[call.name](call.arguments); - } - } - setAiActionLog(prev => prev.map(entry => { - if (entry === logEntry) { - return { ...entry, text: `[${call.name}] ${execResult.success ? 'Thành công' : 'Thất bại: ' + (execResult.error || 'unknown')}`, result: execResult }; - } - return entry; - })); - } - setAiActionLog(prev => [...prev, { type: 'status', text: `Hoàn thành. Đã xử lý ${calls.length} lệnh.`, time: Date.now() }]); - } catch (err) { - setAiActionLog(prev => [...prev, { type: 'error', text: 'Lỗi: ' + err.message, time: Date.now() }]); - } finally { - setAiProcessing(false); - } - }; - // ── Split Track at Playhead ── const handleSplitTrackAtTime = (trackId, clipId, time) => { const track = tracks.find(t => t.id === trackId); @@ -8319,9 +8133,7 @@ const App = () => { }, "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'}` - }, /*#__PURE__*/React.createElement("span", { - className: "text-zinc-600 mr-1" - }, new Date(entry.time).toLocaleTimeString()), entry.text))))); + }, 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", { diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 12b71ff..0ae0981 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -2999,6 +2999,11 @@ const App = () => { data: null, isRunning: false }); + const [aiPrompt, setAiPrompt] = useState(''); + const [aiProvider, setAiProvider] = useState('OpenAI'); + const [aiModel, setAiModel] = useState('GPT-4o'); + const [aiActionLog, setAiActionLog] = useState([]); + const [aiProcessing, setAiProcessing] = useState(false); const [exportSettings, setExportSettings] = useState({ sampleRate: '44100', bitDepth: '16', @@ -7004,6 +7009,253 @@ const App = () => { showToast(`Đã gộp ${clips.length} clips thành công.`, 'success'); }; + // ── DAW Command Registration for AI (28_AI_PANEL.md §1 & §2) ── + useEffect(() => { + if (typeof window.DAWCommandDispatcher === 'undefined') return; + const api = { + createTrack: args => { + const name = args.name || `AI_Track_${Date.now()}`; + const type = args.type || 'audio'; + const newId = addNewTrack(); + if (name && name !== `AI_Track_${Date.now()}`) { + updateTrackName(newId, name); + } + return { + success: true, + trackId: newId, + name + }; + }, + deleteTrack: args => { + const tid = args.track_id || selectedTrackId; + if (!tid) return { + success: false, + error: 'No track_id provided' + }; + deleteTrack(tid); + return { + success: true, + trackId: tid + }; + }, + addClip: args => { + const trackId = args.track_id || selectedTrackId; + const startTime = args.start_time || args.start_bar ? args.start_bar * (60 / parseInt(bpm || 120)) * 4 : currentTime; + const track = tracks.find(t => t.id === trackId); + if (!track) return { + success: false, + error: 'Track not found' + }; + const ctx = getAudioContext(); + const sr = 44100; + const duration = args.duration_seconds || args.length_bars ? args.length_bars * (60 / parseInt(bpm || 120)) * 4 : 2; + const buffer = ctx.createBuffer(1, Math.floor(sr * duration), sr); + const data = buffer.getChannelData(0); + for (let i = 0; i < data.length; i++) data[i] = 0; + const clipId = 'clip_' + Date.now(); + setTracks(prev => prev.map(t => { + if (t.id !== trackId) return t; + const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{ + id: 'default_' + t.id, + buffer: t.buffer, + startTime: t.startTime || 0, + name: t.name + }] : []; + return { + ...t, + clips: [...clips, { + id: clipId, + buffer, + startTime, + name: args.name || 'AI Clip' + }], + buffer: clips.length > 0 ? clips[0].buffer : buffer, + startTime: clips.length > 0 ? clips[0].startTime : startTime, + name: clips.length > 0 ? clips[0].name : args.name || t.name + }; + })); + return { + success: true, + clipId, + trackId + }; + }, + removeClip: args => { + const trackId = args.track_id || selectedTrackId; + const clipId = args.clip_id; + setTracks(prev => prev.map(t => { + if (t.id !== trackId) return t; + const updatedClips = (t.clips || []).filter(c => c.id !== clipId); + return { + ...t, + clips: updatedClips, + buffer: updatedClips[0]?.buffer || null, + startTime: updatedClips[0]?.startTime || 0, + name: updatedClips[0]?.name || t.name + }; + })); + return { + success: true + }; + }, + setTrackVolume: args => { + const trackId = args.track_id || selectedTrackId; + const vol = args.volume_db ?? args.volume ?? 0; + updateTrackVolumeDb(trackId, parseFloat(vol)); + return { + success: true, + trackId, + volumeDb: vol + }; + }, + setTrackPan: args => { + const trackId = args.track_id || selectedTrackId; + const pan = args.pan ?? 0; + updateTrackPan(trackId, parseInt(pan)); + return { + success: true, + trackId, + pan + }; + }, + toggleMute: args => { + const trackId = args.track_id || selectedTrackId; + toggleTrackMute(trackId); + const track = tracks.find(t => t.id === trackId); + return { + success: true, + trackId, + muted: track ? track.muted : null + }; + }, + toggleSolo: args => { + const trackId = args.track_id || selectedTrackId; + toggleTrackSoloEvaluate(trackId); + const track = tracks.find(t => t.id === trackId); + return { + success: true, + trackId, + solo: track ? track.solo : null + }; + }, + processAudioDsp: args => { + const trackId = args.track_id || selectedTrackId; + const action = args.action; + const params = args.params || {}; + const track = tracks.find(t => t.id === trackId); + if (!track || !track.buffer) return { + success: false, + error: 'Track has no audio buffer' + }; + if (action === 'normalize') { + const channelData = track.buffer.getChannelData(0); + let maxVal = 0; + for (let i = 0; i < channelData.length; i++) maxVal = Math.max(maxVal, Math.abs(channelData[i])); + if (maxVal > 0) { + const gain = 1.0 / maxVal; + for (let i = 0; i < channelData.length; i++) channelData[i] *= gain; + } + return { + success: true, + action: 'normalize' + }; + } else if (action === 'invert_phase') { + const channelData = track.buffer.getChannelData(0); + for (let i = 0; i < channelData.length; i++) channelData[i] *= -1; + return { + success: true, + action: 'invert_phase' + }; + } else if (action === 'gain') { + const gainDb = params.gain_db ?? 0; + const scale = Math.pow(10, gainDb / 20); + const channelData = track.buffer.getChannelData(0); + for (let i = 0; i < channelData.length; i++) channelData[i] = Math.max(-1, Math.min(1, channelData[i] * scale)); + return { + success: true, + action: 'gain', + gainDb + }; + } else if (action === 'pitch_shift') { + const semitones = params.semitones ?? 0; + const ratio = Math.pow(2, semitones / 12); + const applyResample = (data, r) => { + const newLen = Math.round(data.length * r); + const out = new Float32Array(newLen); + for (let i = 0; i < newLen; i++) { + const srcIdx = i / r; + const idx0 = Math.floor(srcIdx); + const idx1 = Math.min(idx0 + 1, data.length - 1); + const frac = srcIdx - idx0; + out[i] = data[idx0] * (1 - frac) + data[idx1] * frac; + } + return out; + }; + const channelData = track.buffer.getChannelData(0); + const newData = applyResample(channelData, 1 / ratio); + const ctx = getAudioContext(); + const newBuffer = ctx.createBuffer(1, newData.length, track.buffer.sampleRate); + newBuffer.copyToChannel(newData, 0); + setTracks(prev => prev.map(t => t.id === trackId ? { + ...t, + buffer: newBuffer + } : t)); + return { + success: true, + action: 'pitch_shift', + semitones + }; + } + return { + success: false, + error: `Unknown action: ${action}` + }; + }, + setBpm: args => { + const bpmVal = args.bpm || args.tempo || 120; + setBpm(String(bpmVal)); + return { + success: true, + bpm: bpmVal + }; + }, + setPlayhead: args => { + const time = args.time ?? args.position ?? 0; + handlePlayheadSet(time); + return { + success: true, + time + }; + }, + addMarker: args => { + const trackId = args.track_id || selectedTrackId; + const time = args.time ?? currentTime; + const track = tracks.find(t => t.id === trackId); + if (!track) return { + success: false, + error: 'Track not found' + }; + setTracks(prev => prev.map(t => { + if (t.id !== trackId) return t; + return { + ...t, + markers: [...(t.markers || []), { + id: 'ai_marker_' + Date.now(), + time, + label: args.label || 'AI Marker' + }] + }; + })); + return { + success: true, + trackId, + time + }; + } + }; + window.DAWCommandDispatcher.registerDAWCommands(api); + }, [tracks, selectedTrackId, currentTime, bpm]); + // ── Save AI config to localStorage ── useEffect(() => { localStorage.setItem('ai_base_url', aiConfig.baseUrl); @@ -7847,7 +8099,21 @@ const App = () => { }, /*#__PURE__*/React.createElement("i", { "data-lucide": "cpu", className: "w-3.5 h-3.5 text-purple-400" - })), " AI"), /*#__PURE__*/React.createElement("button", { + })), " AI Copilot"), /*#__PURE__*/React.createElement("div", { + className: "flex items-center gap-1" + }, /*#__PURE__*/React.createElement("button", { + onClick: () => { + setAiActionLog([]); + showToast('Đã xoá nhật ký AI.', 'info'); + }, + className: "text-zinc-600 hover:text-zinc-300", + title: "Clear log" + }, /*#__PURE__*/React.createElement("span", { + className: "inline-flex items-center shrink-0" + }, /*#__PURE__*/React.createElement("i", { + "data-lucide": "trash-2", + className: "w-3 h-3" + }))), /*#__PURE__*/React.createElement("button", { onClick: () => closePanel('ai'), className: "text-zinc-600 hover:text-zinc-300" }, /*#__PURE__*/React.createElement("span", { @@ -7855,8 +8121,8 @@ const App = () => { }, /*#__PURE__*/React.createElement("i", { "data-lucide": "x", className: "w-3 h-3" - })))), /*#__PURE__*/React.createElement("div", { - className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-[9px] font-mono min-h-[24px]" + }))))), /*#__PURE__*/React.createElement("div", { + className: "p-1.5 bg-[#141414] rounded border border-zinc-800 text-[9px] font-mono min-h-[20px]" }, /*#__PURE__*/React.createElement("div", { className: "text-zinc-500" }, "// ", /*#__PURE__*/React.createElement("span", { @@ -7873,16 +8139,16 @@ const App = () => { }, /*#__PURE__*/React.createElement("i", { "data-lucide": "map-pin", className: "w-3 h-3" - })), " AI Scan"), /*#__PURE__*/React.createElement("button", { + })), " 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] flex items-center justify-center gap-1" + 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" }, /*#__PURE__*/React.createElement("span", { className: "inline-flex items-center shrink-0" }, /*#__PURE__*/React.createElement("i", { "data-lucide": "scissors", className: "w-3 h-3" - })), " AI Cut"), /*#__PURE__*/React.createElement("button", { + })), " 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" @@ -7891,7 +8157,85 @@ const App = () => { }, /*#__PURE__*/React.createElement("i", { "data-lucide": "sparkles", className: "w-3 h-3" - })), " AI Analysic Loop"))); + })), " 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" + }, /*#__PURE__*/React.createElement("span", { + className: "inline-flex items-center shrink-0" + }, /*#__PURE__*/React.createElement("i", { + "data-lucide": "message-square", + className: "w-3 h-3" + })), " Copilot Prompt"), /*#__PURE__*/React.createElement("textarea", { + 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", + rows: 2, + onKeyDown: e => { + if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + executeAIPrompt(); + } + } + }), /*#__PURE__*/React.createElement("div", { + className: "flex items-center gap-1 mt-1" + }, /*#__PURE__*/React.createElement("button", { + onClick: 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" + }, aiProcessing ? 'Đang suy luận...' : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", { + className: "inline-flex items-center shrink-0" + }, /*#__PURE__*/React.createElement("i", { + "data-lucide": "send", + className: "w-3 h-3" + })), " Gửi")), /*#__PURE__*/React.createElement("button", { + onClick: () => { + setAiPrompt(''); + setAiActionLog([]); + }, + className: "px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 rounded text-[9px] border border-zinc-700" + }, "Clear")), /*#__PURE__*/React.createElement("div", { + className: "text-[8px] 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" + }, /*#__PURE__*/React.createElement("span", { + className: "inline-flex items-center gap-1" + }, /*#__PURE__*/React.createElement("i", { + "data-lucide": "list", + className: "w-3 h-3" + }), " Action Log"), aiActionLog.length > 0 && /*#__PURE__*/React.createElement("button", { + onClick: () => { + if (window.DAWCommandDispatcher && window.DAWCommandDispatcher.undo) { + const entry = window.DAWCommandDispatcher.undo(); + if (entry) { + setAiActionLog(prev => [...prev, { + type: 'undo', + text: `Undo: ${entry.name}`, + time: Date.now() + }]); + showToast(`Undo AI: ${entry.name}`, 'info'); + } + } else { + handleUndo(); + setAiActionLog(prev => [...prev, { + type: 'undo', + text: 'Undo (Ctrl+Z)', + time: Date.now() + }]); + } + }, + className: "text-[8px] 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" + }, "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))); if (panelId === 'python_tools') return /*#__PURE__*/React.createElement("div", { className: "flex flex-col h-full gap-1.5" }, /*#__PURE__*/React.createElement("div", { diff --git a/app/static/js/services/aiGateway.js b/app/static/js/services/aiGateway.js index 728b647..4308f2c 100644 --- a/app/static/js/services/aiGateway.js +++ b/app/static/js/services/aiGateway.js @@ -155,7 +155,7 @@ const AIGateway = (function() { }; } - async function executePrompt({ prompt, provider, model, apiKey, baseUrl, dawContext, tools }) { + async function executeAIPrompt({ prompt, provider, model, apiKey, baseUrl, dawContext, tools }) { const messages = buildUserMessage(prompt, dawContext); const toolList = tools || DEFAULT_TOOLS; @@ -211,11 +211,12 @@ const AIGateway = (function() { extractFunctionCalls, buildUserMessage, buildAIPromptContext, - executePrompt, + executeAIPrompt, createMidiItem, modifyMidiNotes, processAIDSP }; })(); +window.executeAIPrompt = AIGateway.executeAIPrompt; window.AIGateway = AIGateway; diff --git a/app/storage/sonicforge.db b/app/storage/sonicforge.db index da3822403a2b38691fe8ee15732748741d4be34b..0ef2a79a6851c0a209567d6a7e517a0f67ad6d15 100644 GIT binary patch delta 30 mcmZp8z|`=7X@WGP_Cy(HM(vFWUiGXGnUtNrY+hd_i!7M%j%CUiGZETGy{Szj=LqpaB4}$_!)x diff --git a/app/templates/index.html b/app/templates/index.html index f67c219..a0c7adc 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -12,8 +12,17 @@ + diff --git a/SUB_EDITOR.md b/md/4_SUB_EDITOR.md similarity index 100% rename from SUB_EDITOR.md rename to md/4_SUB_EDITOR.md diff --git a/USER_MANAGER.md b/md/USER_MANAGER.md similarity index 100% rename from USER_MANAGER.md rename to md/USER_MANAGER.md