fix: Plugin Manager '+ Add Directory' khong hien folder picker (ban 1.1.3)
Nguyen nhan: UI that chay tren http://127.0.0.1:8000 (engine) nen window.__TAURI__ KHONG ton tai (Tauri chi inject vao trang no serve - src-tauri/ui/index.html chi la loader redirect). Fallback window.prompt KHONG hoat dong trong WebView2 (no-op) -> khong co picker nao xuat hien. Fix: in-app folder browser qua backend (co san /media/computer + /media/browse trong media.py, auth qua apiRequest): - services/api.js: them browseComputer() + browseDir(path) - app.jsx PluginManagerModal: state pmPicker + overlay UI (roots/drives -> dirs -> 'Len' (parent) -> 'Chon thu muc nay' -> setPmDirs) - Thu tu uu tien: __TAURI__.core.invoke('plugin:dialog|open') -> in-app browser -> prompt (fallback cuoi, browser thuan khong phai WebView2) - Bump ?v= cache-bust index.html (api.js + app.precompiled.js) Verify tren engine frozen: /media/computer tra roots, /media/browse tra {path, parent, dirs} - dir picker flow OK. Skill da ghi lai: ~/.hermes/skills/software-development/sonicforge-daw-engine-packaging
This commit is contained in:
+116
-10
@@ -5299,9 +5299,39 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
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).
|
||||
// Folder picker:
|
||||
// 1) Neu page duoc Tauri serve (__TAURI__ co) -> dialog plugin invoke.
|
||||
// 2) Binh thuong UI chay tren http://127.0.0.1:8000 (engine) -> __TAURI__
|
||||
// KHONG co (Tauri chi inject vao trang no serve; window.prompt cung
|
||||
// khong hoat dong trong WebView2) -> mo TRINH DUYET THU MUC in-app
|
||||
// (backend /media/computer + /media/browse) — hoat dong moi OS.
|
||||
// 3) Cuoi cung: prompt nhap path (browser thuan).
|
||||
const [pmPicker, setPmPicker] = React.useState(null); // {path, dirs, parent, roots, loading}
|
||||
const openPluginPicker = async () => {
|
||||
setPmPicker({ path: null, dirs: null, parent: null, roots: null, loading: true });
|
||||
try {
|
||||
const data = await window.SonicAPI.browseComputer();
|
||||
setPmPicker({ path: null, dirs: null, parent: null, roots: data.roots || [], loading: false });
|
||||
} catch (e) {
|
||||
setPmPicker(null);
|
||||
showToast('Không mở được trình duyệt thư mục: ' + (e.message || e), 'error');
|
||||
}
|
||||
};
|
||||
const browsePluginDir = async (path) => {
|
||||
setPmPicker(prev => ({ ...prev, loading: true }));
|
||||
try {
|
||||
const data = await window.SonicAPI.browseDir(path);
|
||||
setPmPicker({ path: data.path, parent: data.parent, dirs: data.dirs || [], roots: null, loading: false });
|
||||
} catch (e) {
|
||||
setPmPicker(prev => ({ ...prev, loading: false }));
|
||||
showToast('Không đọc được thư mục: ' + (e.message || e), 'error');
|
||||
}
|
||||
};
|
||||
const confirmPluginDir = () => {
|
||||
const p = pmPicker && pmPicker.path;
|
||||
if (p && !pmDirs.includes(p)) setPmDirs(prev => [...prev, p]);
|
||||
setPmPicker(null);
|
||||
};
|
||||
const pickPluginFolder = async () => {
|
||||
try {
|
||||
if (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.invoke) {
|
||||
@@ -5313,13 +5343,21 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
}
|
||||
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]);
|
||||
}
|
||||
// Trinh duyet thu muc in-app (backend) — hoat dong khi UI chay tren
|
||||
// localhost:8000 (khong co __TAURI__, window.prompt vo hieu trong WebView2)
|
||||
await openPluginPicker();
|
||||
return;
|
||||
} catch (e) {
|
||||
showToast('Browse failed: ' + (e.message || e), 'error');
|
||||
// Fallback cuoi: prompt nhap tay (browser thuan, khong phai WebView2)
|
||||
try {
|
||||
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 (e2) {
|
||||
showToast('Browse failed: ' + (e.message || e), 'error');
|
||||
}
|
||||
}
|
||||
};
|
||||
const removePluginDir = (dir) => {
|
||||
@@ -5372,10 +5410,78 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
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',
|
||||
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 relative',
|
||||
style: { maxHeight: '80vh' },
|
||||
onClick: e => e.stopPropagation()
|
||||
},
|
||||
// ── In-app folder picker (Plugin Directories) ─────────────────────
|
||||
pmPicker && React.createElement('div', {
|
||||
className: 'absolute inset-0 z-10 bg-[#171717]/97 flex flex-col',
|
||||
style: { padding: 16 }
|
||||
},
|
||||
React.createElement('div', { className: 'flex items-center justify-between mb-2' },
|
||||
React.createElement('div', { className: 'text-xs font-bold text-violet-300 uppercase' }, 'Chọn thư mục plugin'),
|
||||
React.createElement('button', {
|
||||
onClick: () => setPmPicker(null),
|
||||
className: 'text-zinc-500 hover:text-zinc-200 transition'
|
||||
}, React.createElement('i', { 'data-lucide': 'x', className: 'w-4 h-4' }))
|
||||
),
|
||||
React.createElement('div', { className: 'flex items-center gap-2 mb-2' },
|
||||
React.createElement('button', {
|
||||
onClick: () => { if (pmPicker.parent) browsePluginDir(pmPicker.parent); },
|
||||
disabled: !pmPicker.parent,
|
||||
className: 'px-2 py-1 bg-zinc-800 hover:bg-zinc-700 rounded text-[11px] text-zinc-300 disabled:opacity-30 shrink-0'
|
||||
}, 'Lên'),
|
||||
React.createElement('div', {
|
||||
className: 'flex-1 text-[11px] text-zinc-400 font-mono truncate',
|
||||
title: pmPicker.path || ''
|
||||
}, pmPicker.path || (pmPicker.loading ? 'Đang tải...' : 'My Computer'))
|
||||
),
|
||||
pmPicker.loading ? React.createElement('div', { className: 'flex-1 flex items-center justify-center text-zinc-500 text-xs' }, 'Đang tải...') :
|
||||
(pmPicker.dirs ? (
|
||||
pmPicker.dirs.length === 0 ? React.createElement('div', { className: 'flex-1 flex items-center justify-center text-zinc-600 text-xs italic' }, 'Thư mục trống') :
|
||||
React.createElement('div', { className: 'flex-1 overflow-y-auto space-y-1' },
|
||||
pmPicker.dirs.map((d, i) =>
|
||||
React.createElement('div', {
|
||||
key: 'pd_' + i,
|
||||
className: 'flex items-center gap-2 px-2 py-1.5 rounded hover:bg-violet-900/30 cursor-pointer group',
|
||||
onClick: () => browsePluginDir(d.path)
|
||||
},
|
||||
React.createElement('i', { 'data-lucide': 'folder', className: 'w-3.5 h-3.5 text-amber-500/80 shrink-0' }),
|
||||
React.createElement('span', { className: 'flex-1 text-[11px] text-zinc-300 truncate' }, d.name),
|
||||
React.createElement('i', { 'data-lucide': 'chevron-right', className: 'w-3 h-3 text-zinc-600 group-hover:text-violet-400 shrink-0' })
|
||||
)
|
||||
)
|
||||
)
|
||||
) : (
|
||||
pmPicker.roots ? (
|
||||
pmPicker.roots.length === 0 ? React.createElement('div', { className: 'flex-1 flex items-center justify-center text-zinc-600 text-xs' }, 'Không tìm thấy ổ đĩa') :
|
||||
React.createElement('div', { className: 'flex-1 overflow-y-auto space-y-1' },
|
||||
pmPicker.roots.map((r, i) =>
|
||||
React.createElement('div', {
|
||||
key: 'pr_' + i,
|
||||
className: 'flex items-center gap-2 px-2 py-1.5 rounded hover:bg-violet-900/30 cursor-pointer',
|
||||
onClick: () => browsePluginDir(r.path)
|
||||
},
|
||||
React.createElement('i', { 'data-lucide': 'hard-drive', className: 'w-3.5 h-3.5 text-cyan-500/80 shrink-0' }),
|
||||
React.createElement('span', { className: 'flex-1 text-[11px] text-zinc-300' }, r.name || r.path)
|
||||
)
|
||||
)
|
||||
)
|
||||
) : null
|
||||
)),
|
||||
React.createElement('div', { className: 'flex items-center justify-end gap-2 mt-2 pt-2 border-t border-[#383838]' },
|
||||
React.createElement('button', {
|
||||
onClick: () => setPmPicker(null),
|
||||
className: 'px-3 py-1.5 bg-zinc-800 hover:bg-zinc-700 text-white text-xs rounded'
|
||||
}, 'Hủy'),
|
||||
React.createElement('button', {
|
||||
onClick: confirmPluginDir,
|
||||
disabled: !pmPicker.path,
|
||||
className: 'px-3 py-1.5 bg-violet-700 hover:bg-violet-600 text-white text-xs font-semibold rounded disabled:opacity-30'
|
||||
}, 'Chọn thư mục này')
|
||||
)
|
||||
),
|
||||
// Header
|
||||
React.createElement('div', {
|
||||
className: 'flex items-center justify-between px-5 py-3 bg-[#252525] border-b border-[#383838]'
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -67,6 +67,10 @@ window.API_BASE_URL = window.API_BASE_URL || window.location.origin;
|
||||
getPluginDirs: () => apiRequest('/api/v1/plugins/dirs', { method: 'GET' }),
|
||||
savePluginDirs: (dirs) => apiRequest('/api/v1/plugins/dirs', { method: 'POST', body: JSON.stringify(dirs) }),
|
||||
scanPluginDirs: () => apiRequest('/api/v1/plugins/scan', { method: 'POST' }),
|
||||
// Folder picker cho Plugin Manager: duyet thu muc qua backend (media)
|
||||
// — hoat dong moi OS, khong can window.__TAURI__ (UI chay tren localhost:8000)
|
||||
browseComputer: () => apiRequest('/api/v1/media/computer', { method: 'GET' }),
|
||||
browseDir: (path) => apiRequest(`/api/v1/media/browse?path=${encodeURIComponent(path)}`, { method: 'GET' }),
|
||||
getSoundfontCatalog: () => apiRequest('/api/v1/plugins/soundfonts/catalog', { method: 'GET' }),
|
||||
listDefaultSoundfonts: () => apiRequest('/api/v1/plugins/default-soundfonts', { method: 'GET' }),
|
||||
listSoundfontInstruments: (sfId) => apiRequest(`/api/v1/plugins/soundfont-instruments/${sfId}`, { method: 'GET' }),
|
||||
|
||||
Reference in New Issue
Block a user