diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index bcf1dff..0637d37 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -5299,11 +5299,15 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => { setTimeout(() => { try { window.lucide.createIcons(); } catch(e) {} }, 50); } }, [isOpen]); - // Folder picker: Tauri dialog (desktop) nếu có; fallback: paste path. + // Folder picker: Tauri v2 dialog qua invoke (window.__TAURI__.dialog KHONG + // ton tai trong v2 — plugin dialog chi co JS binding qua npm; goi truc tiep + // command 'plugin:dialog|open'). Fallback: prompt nhap path (browser). const pickPluginFolder = async () => { try { - if (window.__TAURI__ && window.__TAURI__.dialog) { - const sel = await window.__TAURI__.dialog.open({ directory: true, multiple: false }); + if (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.invoke) { + const sel = await window.__TAURI__.core.invoke('plugin:dialog|open', { + options: { directory: true, multiple: false } + }); if (typeof sel === 'string' && sel) { if (!pmDirs.includes(sel)) setPmDirs(prev => [...prev, sel]); } diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 8d2be4f..4d9290a 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -363,8 +363,10 @@ const GraphEditorCanvas=({buffer,zoom,timelineWidth,volumeNodes,panningNodes,fad useEffect(()=>{if(!isOpen)return;if(window.SonicAPI&&window.SonicAPI.apiRequest){window.SonicAPI.apiRequest('/api/v1/auth/first-time',{method:'GET'}).then(function(d){setFirstTime(!!(d&&d.first_time));})// Fail-closed: endpoint lỗi/404 (backend chưa restart) → ẨN gợi ý // (không hiện — user đã yêu cầu bỏ gợi ý sau lần đầu). .catch(function(){setFirstTime(false);});}else{setFirstTime(false);}},[isOpen]);const handleSubmit=async e=>{e.preventDefault();setError('');setLoading(true);try{if(activeTab==='login'){const targetUsername=username.trim()||'admin';const targetPwd=password.trim()||'admin123';const res=await window.SonicAPI.login(targetUsername,targetPwd);localStorage.setItem('sonic_token',res.access_token);localStorage.setItem('sonic_user',JSON.stringify(res.user));if(res.user&&res.user.must_change_password){setOldPassword(targetPwd);}onSuccess(res.user,res.access_token);}else if(activeTab==='register'){const res=await window.SonicAPI.register(username.trim(),email.trim(),password.trim());localStorage.setItem('sonic_token',res.access_token);localStorage.setItem('sonic_user',JSON.stringify(res.user));onSuccess(res.user,res.access_token);}else if(activeTab==='force_change'){const res=await window.SonicAPI.changePassword(oldPassword.trim(),newPassword.trim());localStorage.setItem('sonic_token',res.access_token);const user=JSON.parse(localStorage.getItem('sonic_user')||'{}');user.must_change_password=false;localStorage.setItem('sonic_user',JSON.stringify(user));onSuccess(user,res.access_token);}}catch(err){setError(err.message||(activeTab==='login'?'Tài khoản hoặc mật khẩu không chính xác. (Nếu bạn đã đổi mật khẩu trước đó, vui lòng nhập mật khẩu mới mà bạn đã tạo)':'Thao tác không thành công'));}finally{setLoading(false);}};const isForceMode=activeTab==='force_change';const canClose=!forceMandatory&&!isForceMode;return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-md"},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-md p-6 text-slate-200"},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center pb-4 border-b border-[#383838]"},/*#__PURE__*/React.createElement("h3",{className:"text-lg font-bold text-teal-400"},isForceMode?'⚠️ Bắt Buộc Đổi Mật Khẩu Khởi Tạo':activeTab==='login'?'🔐 Đăng Nhập Hệ Thống':'📝 Đăng Ký Tài Khoản'),canClose&&/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-400 hover:text-slate-200"},"✕")),error&&/*#__PURE__*/React.createElement("div",{className:"mt-4 p-3 bg-red-900/40 border border-red-700 rounded-lg text-red-200 text-sm"},error),/*#__PURE__*/React.createElement("form",{onSubmit:handleSubmit,className:"mt-4 space-y-4"},isForceMode?/*#__PURE__*/React.createElement(React.Fragment,null,/*#__PURE__*/React.createElement("p",{className:"text-xs text-amber-400 bg-amber-950/60 p-2.5 border border-amber-800/80 rounded leading-relaxed"},"🔒 Tài khoản của bạn đang dùng mật khẩu khởi tạo mặc định. Để bảo mật hệ thống, bạn phải đổi mật khẩu mới trước khi tiếp tục."),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Mật khẩu hiện tại (Mặc định: admin123)"),/*#__PURE__*/React.createElement("input",{type:"password",autoComplete:"current-password",required:true,value:oldPassword,onChange:e=>setOldPassword(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Mật khẩu mới"),/*#__PURE__*/React.createElement("input",{type:"password",autoComplete:"new-password",required:true,value:newPassword,onChange:e=>setNewPassword(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"}))):/*#__PURE__*/React.createElement(React.Fragment,null,activeTab==='login'?/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Tên đăng nhập ",firstTime&&/*#__PURE__*/React.createElement("span",{className:"text-teal-400 font-normal"},"(Tùy chọn - Admin có thể bỏ trống)")),/*#__PURE__*/React.createElement("input",{type:"text",placeholder:"Mặc định: admin",value:username,onChange:e=>setUsername(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"})):/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Tên đăng nhập"),/*#__PURE__*/React.createElement("input",{type:"text",required:true,value:username,onChange:e=>setUsername(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"})),activeTab==='register'&&/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Email"),/*#__PURE__*/React.createElement("input",{type:"email",required:true,value:email,onChange:e=>setEmail(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold mb-1 text-slate-400"},"Mật khẩu ",activeTab==='login'&&firstTime&&/*#__PURE__*/React.createElement("span",{className:"text-amber-400 font-normal"},"(Lần đầu: admin123)")),/*#__PURE__*/React.createElement("input",{type:"password",autoComplete:activeTab==='login'?'current-password':'new-password',required:true,value:password,onChange:e=>setPassword(e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"}))),/*#__PURE__*/React.createElement("button",{type:"submit",disabled:loading,className:"w-full py-2 bg-teal-600 hover:bg-teal-500 text-white font-semibold rounded-lg shadow transition duration-150"},loading?'Đang xác thực...':isForceMode?'Đổi Mật Khẩu Ngay':activeTab==='login'?'Đăng Nhập System':'Tạo Tài Khoản Mới'),activeTab==='login'&&!isForceMode&&firstTime&&/*#__PURE__*/React.createElement("button",{type:"button",onClick:()=>{setUsername('admin');setPassword('admin123');setError('');},className:"w-full mt-2 py-1.5 bg-amber-950/60 hover:bg-amber-900/80 text-amber-300 border border-amber-800/60 text-xs font-semibold rounded-lg transition flex items-center justify-center gap-1.5"},"🔑 Điền Nhanh Tài Khoản Admin (admin / admin123)")),!isForceMode&&/*#__PURE__*/React.createElement("div",{className:"mt-4 pt-4 border-t border-[#383838] text-center text-xs text-slate-400"},activeTab==='login'?/*#__PURE__*/React.createElement("span",null,"Chưa có tài khoản? ",/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab('register'),className:"text-teal-400 hover:underline"},"Đăng ký ngay")):/*#__PURE__*/React.createElement("span",null,"Đã có tài khoản? ",/*#__PURE__*/React.createElement("button",{onClick:()=>setActiveTab('login'),className:"text-teal-400 hover:underline"},"Đăng nhập")))));};const AIConfigModal=({isOpen,onClose,onConfigSaved})=>{if(!isOpen)return null;const defaultProvidersList=[{id:'openai_default',name:'OpenAI Official',provider_type:'openai',api_base_url:'https://api.openai.com/v1',api_key:'',model_name:'gpt-4o',temperature:0.7,is_active:true},{id:'openai_compat_default',name:'OpenAI Compatible (Ollama/LocalAI/DeepSeek)',provider_type:'openai_compatible',api_base_url:'http://localhost:11434/v1',api_key:'ollama',model_name:'deepseek-r1',temperature:0.7,is_active:false},{id:'anthropic_default',name:'Anthropic Claude',provider_type:'anthropic',api_base_url:'https://api.anthropic.com/v1',api_key:'',model_name:'claude-3-5-sonnet',temperature:0.7,is_active:false},{id:'gemini_default',name:'Google Gemini',provider_type:'gemini',api_base_url:'https://generativelanguage.googleapis.com',api_key:'',model_name:'gemini-1.5-pro',temperature:0.7,is_active:false}];const[providers,setProviders]=useState(defaultProvidersList);const[selectedId,setSelectedId]=useState('openai_default');const[msg,setMsg]=useState('');const[error,setError]=useState('');const[loading,setLoading]=useState(false);useEffect(()=>{if(isOpen)loadConfigs();},[isOpen]);const loadConfigs=async()=>{setLoading(true);setError('');try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setProviders(data.providers);if(data.providers.length>0)setSelectedId(data.providers[0].id);}}catch(err){setError(err.message||'Lỗi nạp cấu hình AI');}finally{setLoading(false);}};const handleSave=async e=>{e.preventDefault();setMsg('');setError('');setLoading(true);try{const res=await window.SonicAPI.saveAIConfigs(providers);setMsg(res.message||'Đã lưu cấu hình AI Providers thành công!');if(onConfigSaved)onConfigSaved(providers);}catch(err){setError(err.message||'Lỗi khi lưu cấu hình AI');}finally{setLoading(false);}};const updateProviderField=(id,field,value)=>{setProviders(prev=>prev.map(p=>p.id===id?{...p,[field]:value}:p));};const activeProvider=providers.find(p=>p.id===selectedId)||providers[0];return/*#__PURE__*/React.createElement("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm select-none"},/*#__PURE__*/React.createElement("div",{className:"bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200"},/*#__PURE__*/React.createElement("div",{className:"flex justify-between items-center pb-4 border-b border-[#383838]"},/*#__PURE__*/React.createElement("h3",{className:"text-lg font-bold text-cyan-400 flex items-center gap-2"},"🤖 Quản Lý & Cấu Hình AI Providers"),/*#__PURE__*/React.createElement("button",{onClick:onClose,className:"text-slate-400 hover:text-slate-200"},"✕")),msg&&/*#__PURE__*/React.createElement("div",{className:"mt-3 p-2 bg-emerald-950/60 border border-emerald-700 text-emerald-300 rounded text-xs"},msg),error&&/*#__PURE__*/React.createElement("div",{className:"mt-3 p-2 bg-red-950/60 border border-red-700 text-red-300 rounded text-xs"},error),/*#__PURE__*/React.createElement("div",{className:"mt-4 grid grid-cols-3 gap-4"},/*#__PURE__*/React.createElement("div",{className:"space-y-1.5 border-r border-[#383838] pr-3"},/*#__PURE__*/React.createElement("span",{className:"text-xs uppercase font-bold text-slate-400 block mb-2"},"Providers"),providers.map(p=>/*#__PURE__*/React.createElement("button",{key:p.id,tabIndex:0,onClick:()=>setSelectedId(p.id),onKeyDown:e=>{if(e.key===' '){e.preventDefault();updateProviderField(p.id,'is_active',!p.is_active);}},className:`w-full text-left px-3 py-2 rounded-lg text-xs font-semibold flex items-center justify-between transition ${selectedId===p.id?'bg-cyan-600 text-white shadow':'bg-[#1e1e1e] text-slate-300 hover:bg-[#2e2e2e]'}`},/*#__PURE__*/React.createElement("span",{className:"truncate"},p.name),p.is_active&&/*#__PURE__*/React.createElement("span",{className:"w-2 h-2 rounded-full bg-emerald-400"}))),/*#__PURE__*/React.createElement("div",{className:"flex gap-1 mt-2"},/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();const rearrangeNewId='provider_'+Date.now();setProviders(prev=>[...prev,{id:rearrangeNewId,name:'New Provider',provider_type:'openai_compatible',api_base_url:'https://api.openai.com/v1',api_key:'',model_name:'gpt-4o-mini',temperature:0.7,is_active:false}]);setSelectedId(rearrangeNewId);},className:"flex-1 px-2 py-1 bg-emerald-700 hover:bg-emerald-600 text-white text-xs font-bold rounded"},"+ Thêm"),providers.length>1&&/*#__PURE__*/React.createElement("button",{onClick:e=>{e.stopPropagation();if(confirm(`Xóa provider "${providers.find(p=>p.id===selectedId)?.name}"?`)){setProviders(prev=>{const filtered=prev.filter(p=>p.id!==selectedId);if(filtered.length>0)setSelectedId(filtered[0].id);return filtered;});}},className:"px-2 py-1 bg-red-800 hover:bg-red-700 text-white text-xs font-bold rounded"},"Xóa"),providers.length>1&&/*#__PURE__*/React.createElement("button",{onClick:function(e){e.stopPropagation();var provs=providers.slice();var pos=-1;for(var pi=0;pi0){var tmp=provs[pos];provs[pos]=provs[pos-1];provs[pos-1]=tmp;setProviders(provs);}},className:"px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-white text-xs font-bold rounded",title:"Di chuyển lên"},"▲"),providers.length>1&&/*#__PURE__*/React.createElement("button",{onClick:function(e){e.stopPropagation();var provs=providers.slice();var pos=-1;for(var pi=0;piupdateProviderField(activeProvider.id,'name',e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-1.5 text-xs text-slate-200 focus:outline-none focus:border-cyan-500"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold text-slate-400 mb-1"},"API Base URL (Endpoint)"),/*#__PURE__*/React.createElement("input",{type:"text",value:activeProvider.api_base_url||'',onChange:e=>updateProviderField(activeProvider.id,'api_base_url',e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-1.5 text-xs font-mono text-cyan-300 focus:outline-none focus:border-cyan-500",placeholder:"https://api.openai.com/v1"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold text-slate-400 mb-1"},"API Key Cá Nhân"),/*#__PURE__*/React.createElement("input",{type:"password",value:activeProvider.api_key||'',onChange:e=>updateProviderField(activeProvider.id,'api_key',e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-1.5 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500",placeholder:"sk-..."})),/*#__PURE__*/React.createElement("div",{className:"grid grid-cols-2 gap-3"},/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold text-slate-400 mb-1"},"Model Name"),/*#__PURE__*/React.createElement("input",{type:"text",value:activeProvider.model_name||'',onChange:e=>updateProviderField(activeProvider.id,'model_name',e.target.value),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-1.5 text-xs font-mono text-amber-300 focus:outline-none focus:border-cyan-500"})),/*#__PURE__*/React.createElement("div",null,/*#__PURE__*/React.createElement("label",{className:"block text-xs font-semibold text-slate-400 mb-1"},"Temperature"),/*#__PURE__*/React.createElement("input",{type:"number",step:"0.1",min:"0",max:"2",value:activeProvider.temperature??0.7,onChange:e=>updateProviderField(activeProvider.id,'temperature',parseFloat(e.target.value)),className:"w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-1.5 text-xs text-slate-200 focus:outline-none focus:border-cyan-500"}))),/*#__PURE__*/React.createElement("div",{className:"pt-2 flex items-center justify-between"},/*#__PURE__*/React.createElement("label",{className:"flex items-center gap-2 cursor-pointer text-xs text-slate-300"},/*#__PURE__*/React.createElement("input",{type:"checkbox",checked:activeProvider.is_active,onChange:e=>updateProviderField(activeProvider.id,'is_active',e.target.checked),className:"rounded accent-cyan-500"}),"Kích hoạt Provider này"),/*#__PURE__*/React.createElement("button",{type:"submit",disabled:loading,className:"px-4 py-1.5 bg-cyan-600 hover:bg-cyan-500 text-white font-semibold text-xs rounded-lg shadow transition"},loading?'Đang lưu...':'Lưu Cấu Hình AI'))))));};const PluginManagerModal=({isOpen,onClose,pluginsData})=>{if(!isOpen)return null;const[localData,setLocalData]=React.useState(pluginsData);const[sfUploadStatus,setSfUploadStatus]=React.useState('');const[sfToDelete,setSfToDelete]=React.useState(null);const[pmDirs,setPmDirs]=React.useState([]);const[pmScanning,setPmScanning]=React.useState(false);const[pmScanResult,setPmScanResult]=React.useState('');const[pmScanData,setPmScanData]=React.useState(null);// { vst_found, soundfonts } -React.useEffect(()=>{if(isOpen){window.SonicAPI.listPlugins().then(data=>setLocalData(data)).catch(()=>setLocalData({vst_instruments:[],soundfonts:[]}));window.SonicAPI.getPluginDirs().then(d=>setPmDirs(d.plugin_dirs||[])).catch(()=>{});setTimeout(()=>{try{window.lucide.createIcons();}catch(e){}},50);}},[isOpen]);// Folder picker: Tauri dialog (desktop) nếu có; fallback: paste path. -const pickPluginFolder=async()=>{try{if(window.__TAURI__&&window.__TAURI__.dialog){const sel=await window.__TAURI__.dialog.open({directory:true,multiple:false});if(typeof sel==='string'&&sel){if(!pmDirs.includes(sel))setPmDirs(prev=>[...prev,sel]);}return;}const manual=window.prompt('Nhập đường dẫn thư mục plugin (VST / SoundFont):');if(manual&&manual.trim()){const p=manual.trim();if(!pmDirs.includes(p))setPmDirs(prev=>[...prev,p]);}}catch(e){showToast('Browse failed: '+(e.message||e),'error');}};const removePluginDir=dir=>{setPmDirs(prev=>prev.filter(d=>d!==dir));};// Lưu dirs (user override) + scan cả 2 thư mục → refresh list + catalog. +React.useEffect(()=>{if(isOpen){window.SonicAPI.listPlugins().then(data=>setLocalData(data)).catch(()=>setLocalData({vst_instruments:[],soundfonts:[]}));window.SonicAPI.getPluginDirs().then(d=>setPmDirs(d.plugin_dirs||[])).catch(()=>{});setTimeout(()=>{try{window.lucide.createIcons();}catch(e){}},50);}},[isOpen]);// Folder picker: Tauri v2 dialog qua invoke (window.__TAURI__.dialog KHONG +// ton tai trong v2 — plugin dialog chi co JS binding qua npm; goi truc tiep +// command 'plugin:dialog|open'). Fallback: prompt nhap path (browser). +const pickPluginFolder=async()=>{try{if(window.__TAURI__&&window.__TAURI__.core&&window.__TAURI__.core.invoke){const sel=await window.__TAURI__.core.invoke('plugin:dialog|open',{options:{directory:true,multiple:false}});if(typeof sel==='string'&&sel){if(!pmDirs.includes(sel))setPmDirs(prev=>[...prev,sel]);}return;}const manual=window.prompt('Nhập đường dẫn thư mục plugin (VST / SoundFont):');if(manual&&manual.trim()){const p=manual.trim();if(!pmDirs.includes(p))setPmDirs(prev=>[...prev,p]);}}catch(e){showToast('Browse failed: '+(e.message||e),'error');}};const removePluginDir=dir=>{setPmDirs(prev=>prev.filter(d=>d!==dir));};// Lưu dirs (user override) + scan cả 2 thư mục → refresh list + catalog. const saveAndScanDirs=async()=>{setPmScanning(true);setPmScanResult('');setPmScanData(null);try{await window.SonicAPI.savePluginDirs({plugin_dirs:pmDirs});const scan=await window.SonicAPI.scanPluginDirs();setPmScanData({vst_found:scan.vst_found||[],soundfonts:scan.soundfonts||[]});const data=await window.SonicAPI.listPlugins();setLocalData(data);try{const cat=await window.SonicAPI.getSoundfontCatalog();window.__soundfontCatalog=cat;}catch(_){}setPmScanResult(`VST: ${scan.vst_count||0} | SoundFonts: ${scan.soundfont_count||0}`);showToast(`Scan xong: ${scan.vst_count||0} VST, ${scan.soundfont_count||0} SoundFonts.`,'success');}catch(err){setPmScanResult('Scan failed: '+(err.message||err));showToast('Scan failed: '+(err.message||err),'error');}finally{setPmScanning(false);}};const handleUploadSF=async e=>{const file=e.target.files?.[0];if(!file)return;setSfUploadStatus('Uploading...');try{const result=await window.SonicAPI.uploadSoundFont(file);setSfUploadStatus('Uploaded: '+result.name);// Refresh plugin list and catalog const data=await window.SonicAPI.listPlugins();setLocalData(data);try{const cat=await window.SonicAPI.getSoundfontCatalog();window.__soundfontCatalog=cat;}catch(_){}}catch(err){setSfUploadStatus('Error: '+err.message);}};const[pmTab,setPmTab]=React.useState('soundfont');return React.createElement('div',{className:'fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm',onClick:onClose},React.createElement('div',{className:'bg-[#1e1e1e] border border-[#383838] rounded-xl shadow-2xl w-full max-w-3xl p-0 text-slate-200 overflow-hidden flex flex-col',style:{maxHeight:'80vh'},onClick:e=>e.stopPropagation()},// Header React.createElement('div',{className:'flex items-center justify-between px-5 py-3 bg-[#252525] border-b border-[#383838]'},React.createElement('h3',{className:'text-base font-bold text-cyan-400 flex items-center gap-2'},React.createElement('i',{'data-lucide':'zap',className:'w-4 h-4'}),'Plugin Manager (SoundFont / VSTi)'),React.createElement('button',{onClick:onClose,className:'text-zinc-500 hover:text-zinc-200 transition'},React.createElement('i',{'data-lucide':'x',className:'w-4 h-4'}))),// Left-right body diff --git a/build_windows.ps1 b/build_windows.ps1 index 1783d5b..b43ba0b 100644 --- a/build_windows.ps1 +++ b/build_windows.ps1 @@ -50,9 +50,19 @@ if ($LASTEXITCODE -ne 0) { exit 1 } -Write-Host "== [4/6] Sidecar binary -> src-tauri/binaries (tauri triple naming) ==" -New-Item -ItemType Directory -Force src-tauri\binaries | Out-Null -Copy-Item dist\daw_engine.exe src-tauri\binaries\daw_engine-x86_64-pc-windows-msvc.exe -Force +Write-Host "== [4/6] ONEDIR engine -> src-tauri/resources/daw_engine (Tauri resources) ==" +# ONEDIR: copy ca thu muc dist\daw_engine\ (exe + _internal) vao resources. +# Tauri bundle resources -> resource_dir()/daw_engine/daw_engine.exe luc runtime. +if (-not (Test-Path "dist\daw_engine\daw_engine.exe")) { + Write-Host "ERROR: dist\daw_engine\daw_engine.exe khong ton tai (onedir build loi?)" -ForegroundColor Red + exit 1 +} +if (Test-Path "src-tauri\resources\daw_engine") { + Remove-Item -Recurse -Force "src-tauri\resources\daw_engine" +} +New-Item -ItemType Directory -Force "src-tauri\resources\daw_engine" | Out-Null +Copy-Item "dist\daw_engine\*" "src-tauri\resources\daw_engine\" -Recurse -Force +Write-Host "Copied onedir engine -> src-tauri\resources\daw_engine" Write-Host "== [5/6] VC++ Redistributable cho hooks.nsh ==" if (-not (Test-Path src-tauri\vc_redist.x64.exe)) { diff --git a/engine.spec b/engine.spec index aff2a8d..fe1f417 100644 --- a/engine.spec +++ b/engine.spec @@ -112,10 +112,8 @@ pyz = PYZ(a.pure, a.zipped_data, cipher=None) exe = EXE( pyz, a.scripts, - a.binaries, - a.zipfiles, - a.datas, [], + exclude_binaries=True, name='daw_engine', debug=False, bootloader_ignore_signals=False, @@ -128,3 +126,14 @@ exe = EXE( # sinh boi tools/gen_favicon_ico.py). Cung nguon voi icon hien thi trong app. icon='src-tauri/icons/favicon.ico', ) + +# ONEDIR (khong phai onefile): exe 700MB+ onefile phai giai nen toan bo vao +# %TEMP% moi lan chay -> load RAT CHAM tren Windows. Onedir chay truc tiep tu +# thu muc (bundle qua Tauri resources), khoi dong gan nhu tuc thi. +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + name='daw_engine', +) diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index 9847717..0000046 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png index 3272f57..031d8e3 100644 Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index 6617788..c407c9c 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fada17b..77bf16f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -12,15 +12,23 @@ pub fn run() { .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) .setup(|app| { - // 1. Spawn sidecar daw_engine.exe (PyInstaller bundle) - let sidecar_command = app - .shell() - .sidecar("daw_engine") - .expect("sidecar daw_engine not found — run build_windows.ps1 first"); - let (_rx, child) = sidecar_command + // 1. Spawn daw_engine (PyInstaller ONEDIR bundle — khong giai nen + // moi lan chay nhu onefile, khoi dong nhanh). Bundle qua Tauri + // resources: resource_dir()/daw_engine/daw_engine(.exe) + let res_dir = app + .path() + .resource_dir() + .expect("resource dir not found"); + let engine_dir = res_dir.join("daw_engine"); + let engine_exe = if cfg!(windows) { + engine_dir.join("daw_engine.exe") + } else { + engine_dir.join("daw_engine") + }; + let (_rx, child) = tauri_plugin_shell::process::Command::new(engine_exe) .env("SF_PARENT_PID", std::process::id().to_string()) .spawn() - .expect("Failed to spawn daw_engine sidecar"); + .expect("Failed to spawn daw_engine (onedir resource) — run build_windows.ps1 first"); app.manage(EngineProcess(Mutex::new(Some(child)))); println!("Python Background Engine started (localhost:8000, auto-fallback 8000-8010)"); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 94fecda..c329e0e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -30,11 +30,12 @@ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", - "icons/icon.ico" + "icons/favicon.ico" ], - "externalBin": [ - "binaries/daw_engine" + "resources": [ + "resources/daw_engine/**" ], + "externalBin": [], "windows": { "nsis": { "installerHooks": "hooks.nsh"