diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index e6caccf..8ab16c1 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -7021,6 +7021,11 @@ const App = () => { if (!hasText && !hasCalls) { setAiActionLog(prev => [...prev, { type: 'error', text: ` AI không trả về lệnh hoặc text. Kiểm tra provider/model có hỗ trợ function calling.`, time: Date.now() }]); } + if (prompt) { + promptHistRef.current = [...promptHistRef.current.slice(-49), prompt]; + setPromptHistory(promptHistRef.current); + } + setPromptHistIdx(-1); setAiActionLog(prev => [...prev, { type: 'status', text: ` Hoàn tất.`, time: Date.now() }]); setAiPrompt(''); setTimeout(() => lucide.createIcons(), 200); @@ -7352,6 +7357,9 @@ const App = () => { } } detectedBPM = Math.round(Math.min(300, Math.max(30, detectedBPM))); + if (args.set_tempo !== false && detectedBPM > 0) { + setBpm(String(detectedBPM)); + } const bitDepth = 16; const bitrate = Math.round(sr * channels * bitDepth / 1000); return { @@ -8306,6 +8314,22 @@ const App = () => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleAISend(); + } else if (e.key === 'ArrowUp' && promptHistRef.current.length > 0) { + e.preventDefault(); + const idx = promptHistIdx === -1 ? promptHistRef.current.length - 1 : Math.max(0, promptHistIdx - 1); + setPromptHistIdx(idx); + setAiPrompt(promptHistRef.current[idx]); + } else if (e.key === 'ArrowDown') { + e.preventDefault(); + if (promptHistIdx === -1) return; + const idx = promptHistIdx + 1; + if (idx >= promptHistRef.current.length) { + setPromptHistIdx(-1); + setAiPrompt(''); + } else { + setPromptHistIdx(idx); + setAiPrompt(promptHistRef.current[idx]); + } } } }), /*#__PURE__*/React.createElement("div", { diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 9d7039e..fba9770 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -3056,6 +3056,9 @@ const App = () => { isRunning: false }); const [aiPrompt, setAiPrompt] = useState(''); + const [promptHistory, setPromptHistory] = useState([]); + const [promptHistIdx, setPromptHistIdx] = useState(-1); + const promptHistRef = useRef([]); const [aiProvider, setAiProvider] = useState('OpenAI'); const [aiModel, setAiModel] = useState('GPT-4o'); const [aiActionLog, setAiActionLog] = useState([]); @@ -7057,6 +7060,11 @@ const App = () => { time: Date.now() }]); } + if (prompt) { + promptHistRef.current = [...promptHistRef.current.slice(-49), prompt]; + setPromptHistory(promptHistRef.current); + } + setPromptHistIdx(-1); setAiActionLog(prev => [...prev, { type: 'status', text: ` Hoàn tất.`, @@ -7485,6 +7493,9 @@ const App = () => { } } detectedBPM = Math.round(Math.min(300, Math.max(30, detectedBPM))); + if (args.set_tempo !== false && detectedBPM > 0) { + setBpm(String(detectedBPM)); + } const bitDepth = 16; const bitrate = Math.round(sr * channels * bitDepth / 1000); return { @@ -8451,6 +8462,22 @@ const App = () => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleAISend(); + } else if (e.key === 'ArrowUp' && promptHistRef.current.length > 0) { + e.preventDefault(); + const idx = promptHistIdx === -1 ? promptHistRef.current.length - 1 : Math.max(0, promptHistIdx - 1); + setPromptHistIdx(idx); + setAiPrompt(promptHistRef.current[idx]); + } else if (e.key === 'ArrowDown') { + e.preventDefault(); + if (promptHistIdx === -1) return; + const idx = promptHistIdx + 1; + if (idx >= promptHistRef.current.length) { + setPromptHistIdx(-1); + setAiPrompt(''); + } else { + setPromptHistIdx(idx); + setAiPrompt(promptHistRef.current[idx]); + } } } }), /*#__PURE__*/React.createElement("div", { diff --git a/app/static/js/services/aiGateway.js b/app/static/js/services/aiGateway.js index 1fdae70..d007cfd 100644 --- a/app/static/js/services/aiGateway.js +++ b/app/static/js/services/aiGateway.js @@ -168,11 +168,12 @@ const AIGateway = (function() { } }, { name: 'scan_track', - description: 'Quét và phân tích track âm thanh: phát hiện BPM (tempo), sample rate, số kênh (mono/stereo), bit depth, duration', + description: 'Quét và phân tích track âm thanh: phát hiện BPM (tempo) và tự động cập nhật tempo hệ thống, sample rate, số kênh (mono/stereo), duration', parameters: { type: 'object', properties: { - track_id: { type: 'string', description: 'ID của track cần quét. Nếu không có thì quét track đang chọn.' } + track_id: { type: 'string', description: 'ID của track cần quét' }, + set_tempo: { type: 'boolean', description: 'Tự động cập nhật tempo hệ thống (mặc định: true)' } } } }, { diff --git a/app/storage/sonicforge.db b/app/storage/sonicforge.db index 9e89621..063dd24 100644 Binary files a/app/storage/sonicforge.db and b/app/storage/sonicforge.db differ