feat: instrument selector modal 2-column layout

Modal max-w-md -> max-w-4xl. Font 14px (text-sm).
Left column: soundfont list. Right column: instruments
of selected SF. All Instruments view when none selected.
Search filters both columns.
This commit is contained in:
2026-07-28 07:20:01 +07:00
parent d0def4bf1e
commit 14e3a561a3
3 changed files with 50 additions and 26 deletions
+43 -25
View File
@@ -17683,7 +17683,7 @@ const App = () => {
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",
onClick: closeInstrumentSelector
}, /*#__PURE__*/React.createElement("div", {
className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-md p-5 text-slate-200",
className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-4xl p-5 text-slate-200",
onClick: e => e.stopPropagation()
},
/*#__PURE__*/React.createElement("div", { className: "flex justify-between items-center pb-3 border-b border-[#383838]" },
@@ -17696,34 +17696,52 @@ const App = () => {
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 my-3"
className: "w-full bg-black border border-zinc-700 rounded px-2 py-1.5 outline-none my-3 text-sm"
}),
/*#__PURE__*/React.createElement("div", { className: "max-h-72 overflow-y-auto space-y-0.5" },
/*#__PURE__*/React.createElement("button", {
onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, null),
className: "w-full text-left px-3 py-1.5 text-xs rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400 mb-1"
}, "None (Default Synth)"),(
sfPresets === null ? (
/*#__PURE__*/React.createElement("p", { className: "text-[10px] text-zinc-500 py-2" }, "Loading instruments...")
) : sfPresets.length > 0 ? (
/*#__PURE__*/React.createElement("div", { className: "space-y-0.5" },
sfPresets
.filter(p => !sfPresetSearchQuery || (p.name || '').toLowerCase().includes(sfPresetSearchQuery.toLowerCase()) || (p._sfName || '').toLowerCase().includes(sfPresetSearchQuery.toLowerCase()))
.map((p, i) => /*#__PURE__*/React.createElement("button", {
key: i,
onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, p._sfId, p.program, p.name || 'Preset ' + p.program, p.bank),
className: "w-full text-left px-2 py-1 text-[10px] rounded bg-zinc-800/60 hover:bg-amber-800 text-zinc-300 truncate flex items-center gap-2"
},
/*#__PURE__*/React.createElement("span", { className: "text-[9px] text-zinc-500 shrink-0" }, p._sfDisplay),
p.bank === 128 ? /*#__PURE__*/React.createElement("span", { className: "mr-1" }, "🥁") : null,
p.name || 'Preset ' + p.program
))
/*#__PURE__*/React.createElement("div", { className: "flex gap-4", style: { height: "420px" } },
/*#__PURE__*/React.createElement("div", { className: "w-1/3 border-r border-[#383838] overflow-y-auto space-y-0.5 pr-2" },
/*#__PURE__*/React.createElement("button", {
onClick: () => { setSelectedSoundFontId(null); setSynthCategory(null); },
className: "w-full text-left px-3 py-2 text-sm rounded " + (!selectedSoundFontId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
}, "All Instruments"),
sfPresets === null ? (
/*#__PURE__*/React.createElement("p", { className: "text-sm text-zinc-500 py-2" }, "Loading...")
) : (
[...new Map(sfPresets.map(p => [p._sfId, p])).values()].map(sf => /*#__PURE__*/React.createElement("button", {
key: sf._sfId,
onClick: () => setSelectedSoundFontId(sf._sfId),
className: "w-full text-left px-3 py-2 text-sm rounded " + (selectedSoundFontId === sf._sfId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
}, sf._sfName))
)
),
/*#__PURE__*/React.createElement("div", { className: "flex-1 overflow-y-auto space-y-0.5" },
/*#__PURE__*/React.createElement("button", {
onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, null),
className: "w-full text-left px-3 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-400"
}, "None (Default Synth)"),
sfPresets === null ? (
/*#__PURE__*/React.createElement("p", { className: "text-sm text-zinc-500 py-2" }, "Loading instruments...")
) : (
sfPresets.length > 0 ? (
sfPresets
.filter(p => !selectedSoundFontId || p._sfId === selectedSoundFontId)
.filter(p => !sfPresetSearchQuery || (p.name || '').toLowerCase().includes(sfPresetSearchQuery.toLowerCase()) || (p._sfName || '').toLowerCase().includes(sfPresetSearchQuery.toLowerCase()))
.map((p, i) => /*#__PURE__*/React.createElement("button", {
key: i,
onClick: () => setTrackInstrumentWithProgram(instrumentSelectorTrackId, p._sfId, p.program, p.name || 'Preset ' + p.program, p.bank),
className: "w-full text-left px-3 py-2 text-sm rounded bg-zinc-800/60 hover:bg-amber-800 text-zinc-300 truncate flex items-center gap-2"
},
/*#__PURE__*/React.createElement("span", { className: "text-xs text-zinc-500 shrink-0" }, p._sfDisplay),
p.bank === 128 ? /*#__PURE__*/React.createElement("span", { className: "mr-1" }, "🥁") : null,
p.name || 'Preset ' + p.program
))
) : (
/*#__PURE__*/React.createElement("p", { className: "text-sm text-zinc-500 py-2" }, sfPresetSearchQuery ? "Kh\u00f4ng t\u00ecm th\u1ea5y nh\u1ea1c c\u1ee5 ph\u00f9 h\u1ee3p." : "No presets found.")
)
)
) : (
/*#__PURE__*/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.")
)
)
))), fxSelectorTrackId && /*#__PURE__*/React.createElement("div", {
)), fxSelectorTrackId && /*#__PURE__*/React.createElement("div", {
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",
onClick: () => setFxSelectorTrackId(null)
}, /*#__PURE__*/React.createElement("div", {
File diff suppressed because one or more lines are too long
+6
View File
@@ -1,3 +1,9 @@
### [2026-07-28 07:13] Task: Instrument selector modal — 2 cột + font 14px
- **Tóm tắt thay đổi:** Mở rộng modal `max-w-md``max-w-4xl`. Font size từ `text-[10px]`/`text-xs``text-sm` (14px). Bố cục 2 cột: trái danh sách soundfont, phải instruments của SF đang chọn. Click SF trái → lọc instrument phải. Nút "All Instruments" để xem tất cả. Search filter cả 2 cột.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`
- **Ghi chú/Test (nếu có):** `npm run build` — build passes. Synth button → modal 2 cột → chọn SF trái → instrument phải.
---
### [2026-07-26 18:05] Task: Fix SpessaSynth CDN 404 + AudioWorklet init
- **Tóm tắt thay đổi:** Sửa CDN URL từ `@latest/dist/spessasynth_lib.js` (404) → `@4.3.1/dist/index.js` (200). Dùng `WorkletSynthesizer` class + `audioWorklet.addModule(processor.min.js)`, `soundBankManager.addSoundBank()`, `await isReady`. Fallback WorkerSynthesizer nếu AudioWorklet không khả dụng.
- **Các file ảnh hưởng:** `app/templates/index.html`, `app/static/js/services/soundfontPlayer.js`, `app/static/js/app.precompiled.js`