feat: thêm soundfont và VSTi cho MIDI
This commit is contained in:
@@ -3132,6 +3132,87 @@ const AIConfigModal = ({
|
||||
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('');
|
||||
React.useEffect(() => {
|
||||
if (isOpen && !localData) {
|
||||
window.SonicAPI.listPlugins()
|
||||
.then(data => setLocalData(data))
|
||||
.catch(() => setLocalData({ vst_instruments: [], soundfonts: [] }));
|
||||
}
|
||||
}, [isOpen]);
|
||||
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
|
||||
const data = await window.SonicAPI.listPlugins();
|
||||
setLocalData(data);
|
||||
} catch (err) {
|
||||
setSfUploadStatus('Error: ' + err.message);
|
||||
}
|
||||
};
|
||||
const vsts = localData?.vst_instruments || [];
|
||||
const sfs = localData?.soundfonts || [];
|
||||
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-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200',
|
||||
onClick: e => e.stopPropagation()
|
||||
}, React.createElement('div', {
|
||||
className: 'flex justify-between items-center pb-4 border-b border-[#383838]'
|
||||
}, React.createElement('h3', {
|
||||
className: 'text-lg font-bold text-cyan-400'
|
||||
}, 'Plugin Manager (SoundFont / VSTi)'), React.createElement('button', {
|
||||
onClick: onClose,
|
||||
className: 'text-slate-400 hover:text-slate-200'
|
||||
}, '✕')), React.createElement('div', {
|
||||
className: 'mt-4 space-y-4'
|
||||
}, React.createElement('div', null, React.createElement('h4', {
|
||||
className: 'text-sm font-bold text-purple-400 mb-2 uppercase'
|
||||
}, 'VST Instruments'), vsts.length === 0 ? React.createElement('p', {
|
||||
className: 'text-xs text-zinc-500'
|
||||
}, 'No VST instruments available on server.') : React.createElement('div', {
|
||||
className: 'grid grid-cols-1 gap-1'
|
||||
}, vsts.map((v, i) => React.createElement('div', {
|
||||
key: i,
|
||||
className: 'flex items-center justify-between bg-[#1e1e1e] px-3 py-2 rounded border border-[#333]'
|
||||
}, React.createElement('span', {
|
||||
className: 'text-xs font-semibold text-slate-200'
|
||||
}, v.id), React.createElement('span', {
|
||||
className: 'text-[10px] text-cyan-400 bg-cyan-950/40 px-1.5 py-0.5 rounded'
|
||||
}, v.type))))), React.createElement('div', null, React.createElement('h4', {
|
||||
className: 'text-sm font-bold text-amber-400 mb-2 uppercase'
|
||||
}, 'SoundFonts'), sfs.length === 0 ? React.createElement('p', {
|
||||
className: 'text-xs text-zinc-500'
|
||||
}, 'No SoundFonts available.') : React.createElement('div', {
|
||||
className: 'space-y-1'
|
||||
}, sfs.map((sf, i) => React.createElement('div', {
|
||||
key: i,
|
||||
className: 'flex items-center justify-between bg-[#1e1e1e] px-3 py-2 rounded border border-[#333]'
|
||||
}, React.createElement('span', {
|
||||
className: 'text-xs text-slate-200'
|
||||
}, sf.name))))), React.createElement('div', {
|
||||
className: 'pt-4 border-t border-[#383838]'
|
||||
}, React.createElement('h4', {
|
||||
className: 'text-sm font-bold text-emerald-400 mb-2 uppercase'
|
||||
}, 'Upload SoundFont'), React.createElement('input', {
|
||||
type: 'file',
|
||||
accept: '.sf2,.sf3',
|
||||
onChange: handleUploadSF,
|
||||
className: 'w-full text-xs text-zinc-400 file:mr-2 file:py-1 file:px-3 file:rounded file:border-0 file:text-xs file:font-semibold file:bg-emerald-700 file:text-white hover:file:bg-emerald-600'
|
||||
}), sfUploadStatus && React.createElement('p', {
|
||||
className: 'text-xs mt-1 text-zinc-400'
|
||||
}, sfUploadStatus))))));
|
||||
};
|
||||
|
||||
const ProfileModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
@@ -4998,6 +5079,8 @@ const App = () => {
|
||||
const [profileModalOpen, setProfileModalOpen] = useState(false);
|
||||
const [systemManagerModalOpen, setSystemManagerModalOpen] = useState(false);
|
||||
const [aiConfigModalOpen, setAiConfigModalOpen] = useState(false);
|
||||
const [pluginManagerModalOpen, setPluginManagerModalOpen] = useState(false);
|
||||
const [pluginsData, setPluginsData] = useState(null);
|
||||
useEffect(() => {
|
||||
const checkAuthStatus = async () => {
|
||||
const savedToken = localStorage.getItem('sonic_token');
|
||||
@@ -11216,6 +11299,15 @@ const App = () => {
|
||||
label: 'DSP Tools Panel',
|
||||
icon: 'wrench',
|
||||
action: () => openPanel('python_tools')
|
||||
}, {
|
||||
sep: true
|
||||
}, {
|
||||
label: 'Plugin Manager (SoundFont/VSTi)',
|
||||
icon: 'zap',
|
||||
action: () => {
|
||||
setPluginManagerModalOpen(true);
|
||||
window.SonicAPI.listPlugins().then(data => setPluginsData(data)).catch(() => {});
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
label: 'Help',
|
||||
@@ -13676,6 +13768,10 @@ const App = () => {
|
||||
}), /*#__PURE__*/React.createElement(SystemManagerModal, {
|
||||
isOpen: systemManagerModalOpen,
|
||||
onClose: () => setSystemManagerModalOpen(false)
|
||||
}), /*#__PURE__*/React.createElement(PluginManagerModal, {
|
||||
isOpen: pluginManagerModalOpen,
|
||||
onClose: () => setPluginManagerModalOpen(false),
|
||||
pluginsData: pluginsData
|
||||
}));
|
||||
};
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
|
||||
Reference in New Issue
Block a user