From ef6fe0e718d4fbd89d0b57f5caf11e9bfdc9e2bb Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sun, 26 Jul 2026 17:15:50 +0700 Subject: [PATCH] fix(ui): search instrument names inside SoundFont, not just SF names Add sfPresetSearchQuery state + search input in instrument selector modal. Filters sfPresets by name (case-insensitive) when user types. Clear search on SoundFont switch / modal close. --- app/static/js/app.jsx | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index d19c042..335fe66 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -6424,6 +6424,7 @@ const App = () => { const [instrumentSelectorData, setInstrumentSelectorData] = useState(null); const openInstrumentSelector = trackId => { setInstrumentSelectorTrackId(trackId); + setSfPresetSearchQuery(''); const track = activeTracks.find(t => t.id === trackId); if (track && track.instrumentId && track.instrumentId.startsWith('sf_')) { // Track already has a SoundFont assigned → open instrument selection directly @@ -6437,7 +6438,8 @@ const App = () => { } else { // Reset synth state and always reload plugin data setSynthCategory(null); - setSelectedSoundFontId(null); + setSelectedSoundFontId(null); + setSfPresetSearchQuery(''); window.SonicAPI.listPlugins().then(data => setInstrumentSelectorData(data)).catch(e => console.error('listPlugins failed:', e)); } }; @@ -6445,6 +6447,7 @@ const App = () => { setInstrumentSelectorTrackId(null); setSynthCategory(null); setSelectedSoundFontId(null); + setSfPresetSearchQuery(''); }; const [synthCategory, setSynthCategory] = useState(null); // 'vst' | 'soundfont' const [selectedSoundFontId, setSelectedSoundFontId] = useState(null); @@ -6452,6 +6455,7 @@ const App = () => { const [instrumentDropdownTrackId, setInstrumentDropdownTrackId] = useState(null); const [instrumentDropdownBtnRect, setInstrumentDropdownBtnRect] = useState(null); const [instrumentSearchQuery, setInstrumentSearchQuery] = useState(''); + const [sfPresetSearchQuery, setSfPresetSearchQuery] = useState(''); const filteredInstruments = useMemo(() => { if (!instrumentSelectorData || !instrumentSearchQuery) return { soundfonts: instrumentSelectorData?.soundfonts || [], vst: instrumentSelectorData?.vst_instruments || [] }; const q = instrumentSearchQuery.toLowerCase(); @@ -6568,6 +6572,7 @@ const App = () => { setSynthCategory('soundfont'); setInstrumentSelectorTrackId(trackId); setSfPresets(null); + setSfPresetSearchQuery(''); // Fetch actual presets from the SoundFont const sfIdParam = instrumentId.replace('sf_', ''); window.SonicAPI.listSoundfontInstruments(sfIdParam) @@ -16866,12 +16871,20 @@ const App = () => { /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: "flex justify-between items-center pb-3 border-b border-[#383838]" }, /*#__PURE__*/React.createElement("div", null, - /*#__PURE__*/React.createElement("button", { onClick: () => { setSynthCategory(null); setSelectedSoundFontId(null); }, className: "text-[10px] text-cyan-400 hover:text-cyan-300 mr-2" }, "\u2190 Back"), + /*#__PURE__*/React.createElement("button", { onClick: () => { setSynthCategory(null); setSelectedSoundFontId(null); setSfPresetSearchQuery(''); }, className: "text-[10px] text-cyan-400 hover:text-cyan-300 mr-2" }, "\u2190 Back"), /*#__PURE__*/React.createElement("h3", { className: "text-sm font-bold inline text-amber-400" }, "Select Instrument") ), /*#__PURE__*/React.createElement("button", { onClick: closeInstrumentSelector, className: "text-slate-400 hover:text-slate-200" }, "\u2715") ), /*#__PURE__*/React.createElement("p", { className: "text-[10px] text-zinc-500 mt-2 mb-2" }, "SoundFont: ", selectedSoundFontId), + /*#__PURE__*/React.createElement("input", { + type: "text", + placeholder: "T\u00ecm nh\u1ea1c c\u1ee5 trong SoundFont...", + value: sfPresetSearchQuery, + onChange: e => setSfPresetSearchQuery(e.target.value), + autoFocus: true, + className: "w-full bg-black border border-zinc-700 rounded px-2 py-1.5 text-xs outline-none mb-2" + }), /*#__PURE__*/React.createElement("div", { className: "mt-2 max-h-72 overflow-y-auto space-y-0.5" }, /*#__PURE__*/React.createElement("button", { onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, selectedSoundFontId), @@ -16881,14 +16894,16 @@ const App = () => { React.createElement("p", { className: "text-[10px] text-zinc-500 py-2" }, "Loading instruments...") ) : sfPresets.length > 0 ? ( React.createElement("div", { className: "grid grid-cols-2 gap-0.5" }, - sfPresets.map((p, i) => React.createElement("button", { + sfPresets + .filter(p => !sfPresetSearchQuery || (p.name || '').toLowerCase().includes(sfPresetSearchQuery.toLowerCase())) + .map((p, i) => React.createElement("button", { key: i, onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, selectedSoundFontId, p.program, p.name || 'Preset ' + p.program, p.bank), className: "text-left px-2 py-1 text-[10px] rounded bg-zinc-800/60 hover:bg-amber-800 text-zinc-300 truncate" }, p.bank === 128 ? /*#__PURE__*/React.createElement("span", { className: "mr-1" }, "🥁") : null, p.name || 'Preset ' + p.program) )) ) : ( - React.createElement("p", { className: "text-[10px] text-zinc-500 py-2" }, "No presets found.") + React.createElement("p", { className: "text-[10px] text-zinc-500 py-2" }, sfPresetSearchQuery ? "Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 ph\u00f9 h\u1ee3p." : "No presets found.") ) ) )