PageSourceSearch

https://www.hamienet.com/assets/SoundfontManager-DfrQSYkU.js

js hamienet.com collected 2026-10-02 05:02:09 UTC 125,276 bytes, 1,948 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, R as React } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-w5lXls15.js";
3import { j as formatFileSize, l as GM_INSTRUMENTS } from "./utils-CEVb7nN1.js";
4import { SoundfontQualityLab } from "./SoundfontQualityLab-BxeAHII3.js";
5import { u as useSoundfontKeyboard, a as useKeyboardInput, d as getSoundfontName, r as renderSharedKeyboard, g as getMidiFromKeyboardClick } from "./SharedKeyboard-B8ktwnUd.js";
6import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js";
7import { bg as Music, cM as RefreshCw, cC as ChevronDown, bF as ChevronRight, bs as Zap, b1 as CircleAlert, b5 as Trash2, bM as Clock, bv as CircleCheckBig, cG as Keyboard, du as Folder, bi as Sparkles, cg as Activity, bA as Eye, bz as EyeOff, b3 as FileHeadphone, c9 as ArrowUpDown, aW as X, cd as Save, c_ as ChevronLeft } from "./vendor-D0P4XgNj.js";
8import "./vendor-react-three-BCww3P1x.js";
9import "./useMidiKeyboard-DlyEIN8T.js";
10import "./wasm-synth-adapter-DpNGIIyM.js";
11import "./vendor-spessasynth-DH8n6o_V.js";
12const getBaseName = (name) => {
13  return name.replace(/(_JS|_HSF3Bank|_SF3Bank)(?:_.*)?$/i, "").replace(/(?:_test_\d*)?\.sf[23]$/i, "");
14};
15const getSoundfontFormatType = (lib) => {
16  const nameLower = lib.name.toLowerCase();
17  if (lib.isHsf3Bank || nameLower.endsWith("_hsf3bank") || nameLower.endsWith("_sf3bank") || nameLower.endsWith(".hsf3")) {
18    return "HSF3";
19  }
20  if (nameLower.endsWith(".sf3")) {
21    return "SF3";
22  }
23  if (nameLower.endsWith(".sf2")) {
24    return "SF2";
25  }
26  if (nameLower.endsWith(".sfark")) {
27    return "SFARK";
28  }
29  return "JS";
30};
31const SoundfontKeyboardDock = ({
32  selectedSf,
33  program,
34  setProgram,
35  bank,
36  setBank,
37  onClose,
38  gain
39}) => {
40  const processedInstruments = reactExports.useMemo(() => [{ program, midiChannel: bank === 128 ? 9 : 0 }], [program, bank]);
41  const { playNote, stopNote, stopAllNotes, audioContextRef, isLoading, loadingProgress } = useSoundfontKeyboard(0, processedInstruments, void 0, selectedSf, gain);
42  const canvasRef = reactExports.useRef(null);
43  const renderActiveNotesRef = reactExports.useRef(/* @__PURE__ */ new Set());
44  const [baseOctave, setBaseOctave] = reactExports.useState(4);
45  const baseOctaveRef = reactExports.useRef(4);
46  const activeKeysRef = reactExports.useRef(/* @__PURE__ */ new Map());
47  const mouseActiveKeyRef = reactExports.useRef(null);
48  const [labelMode, setLabelMode] = reactExports.useState("pc");
49  const [velocity, setVelocity] = reactExports.useState(100);
50  const changeOctave = reactExports.useCallback((newOctave) => {
51    const bounded = Math.max(0, Math.min(8, newOctave));
52    setBaseOctave(bounded);
53    baseOctaveRef.current = bounded;
54  }, []);
55  const handleKeyOn = reactExports.useCallback((midi) => {
56    if (audioContextRef.current && audioContextRef.current.state === "suspended") {
57      audioContextRef.current.resume().catch(() => {
58      });
59    }
60    playNote(midi, void 0, velocity);
61    renderActiveNotesRef.current.add(midi);
62  }, [playNote, audioContextRef, velocity]);
63  const handleKeyOff = reactExports.useCallback((midi) => {
64    stopNote(midi);
65    renderActiveNotesRef.current.delete(midi);
66  }, [stopNote]);
67  useKeyboardInput({
68    playNote: handleKeyOn,
69    stopNote: handleKeyOff,
70    baseOctaveRef,
71    activeKeysRef,
72    enabled: true,
73    onOctaveChange: setBaseOctave
74  });
75  reactExports.useEffect(() => {
76    let animationFrameId;
77    const render = () => {
78      if (canvasRef.current) {
79        const canvas = canvasRef.current;
80        const ctx = canvas.getContext("2d");
81        if (ctx) {
82          const width = canvas.width;
83          const height = canvas.height;
84          ctx.clearRect(0, 0, width, height);
85          renderSharedKeyboard(
86            ctx,
87            width,
88            height,
89            height,
90            renderActiveNotesRef.current,
91            labelMode,
92            baseOctaveRef.current
93          );
94        }
95      }
96      animationFrameId = requestAnimationFrame(render);
97    };
98    render();
99    return () => cancelAnimationFrame(animationFrameId);
100  }, [labelMode]);
101  const handlePointerDown = (e) => {
102    e.preventDefault();
103    const canvas = canvasRef.current;
104    if (!canvas) return;
105    const rect = canvas.getBoundingClientRect();
106    const x = e.clientX - rect.left;
107    const y = e.clientY - rect.top;
108    const midi = getMidiFromKeyboardClick(x, y, rect.width, rect.height);
109    if (midi !== null) {
110      mouseActiveKeyRef.current = midi;
111      handleKeyOn(midi);
112    }
113  };
114  const handlePointerMove = (e) => {
115    e.preventDefault();
116    if (e.buttons !== 1) return;
117    const canvas = canvasRef.current;
118    if (!canvas) return;
119    const rect = canvas.getBoundingClientRect();
120    const x = e.clientX - rect.left;
121    const y = e.clientY - rect.top;
122    const midi = getMidiFromKeyboardClick(x, y, rect.width, rect.height);
123    if (midi !== null && midi !== mouseActiveKeyRef.current) {
124      if (mouseActiveKeyRef.current !== null) {
125        handleKeyOff(mouseActiveKeyRef.current);
126      }
127      mouseActiveKeyRef.current = midi;
128      handleKeyOn(midi);
129    }
130  };
131  const handlePointerUp = (e) => {
132    e.preventDefault();
133    if (mouseActiveKeyRef.current !== null) {
134      handleKeyOff(mouseActiveKeyRef.current);
135      mouseActiveKeyRef.current = null;
136    }
137  };
138  const isSF2 = selectedSf.toLowerCase().endsWith(".sf2");
139  const isSF3 = selectedSf.toLowerCase().endsWith(".sf3");
140  const isHSF3 = selectedSf.toLowerCase().endsWith("_hsf3bank") || selectedSf.toLowerCase().endsWith("_sf3bank");
141  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed bottom-0 left-0 right-0 z-50 bg-zinc-900/95 backdrop-blur-md border-t border-zinc-700 text-white shadow-2xl p-3 flex flex-col gap-2", children: [
142    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 text-xs", children: [
143      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
144        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
145          /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { className: "w-4 h-4 text-indigo-400" }),
146          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-sm text-indigo-300", children: "Shared Keyboard" })
147        ] }),
148        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-black/20 px-2 py-1 rounded", children: [
149          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-400 font-mono flex items-center gap-1", title: "Note Velocity (Loudness)", children: [
150            /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "w-3 h-3 text-amber-500" }),
151            "VEL:"
152          ] }),
153          /* @__PURE__ */ jsxRuntimeExports.jsx(
154            "input",
155            {
156              type: "range",
157              min: "1",
158              max: "127",
159              value: velocity,
160              onChange: (e) => setVelocity(parseInt(e.target.value) || 100),
161              onMouseUp: (e) => e.target.blur(),
162              onTouchEnd: (e) => e.target.blur(),
163              className: "w-20 accent-amber-500",
164              title: `Velocity: ${velocity}`
165            }
166          ),
167          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400 font-mono font-bold w-6 text-right", children: velocity })
168        ] }),
169        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-zinc-800/80 px-2.5 py-1 rounded-lg border border-zinc-700", children: [
170          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-bold uppercase", children: "Soundfont:" }),
171          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-bold text-white max-w-[180px] truncate", title: selectedSf, children: selectedSf }),
172          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] px-1.5 py-0.5 font-black rounded uppercase ${isHSF3 ? "bg-emerald-500/20 text-emerald-400" : isSF3 ? "bg-cyan-500/20 text-cyan-400" : isSF2 ? "bg-indigo-500/20 text-indigo-400" : "bg-green-500/20 text-green-400"}`, children: isHSF3 ? "HSF3" : isSF3 ? "SF3" : isSF2 ? "SF2" : "JS" })
173        ] }),
174        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-zinc-800/80 px-2.5 py-1 rounded-lg border border-zinc-700", children: [
175          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-bold uppercase", children: "Gain:" }),
176          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono font-bold text-white max-w-[80px]", children: [
177            gain ?? 1,
178            "x"
179          ] })
180        ] }),
181        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-zinc-800/80 px-2.5 py-1 rounded-lg border border-zinc-700", children: [
182          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-bold uppercase", children: "Bank:" }),
183          /* @__PURE__ */ jsxRuntimeExports.jsxs(
184            "select",
185            {
186              value: bank,
187              onChange: (e) => setBank(parseInt(e.target.value)),
188              className: "bg-zinc-900 text-zinc-200 border border-zinc-700 rounded px-2 py-0.5 text-xs outline-none focus:ring-1 focus:ring-indigo-500 font-mono",
189              children: [
190                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: 0, children: "0 (Melodic)" }),
191                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: 128, children: "128 (Percussion)" })
192              ]
193            }
194          )
195        ] }),
196        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-zinc-800/80 px-2.5 py-1 rounded-lg border border-zinc-700", children: [
197          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400 font-bold uppercase", children: [
198            "Program (",
199            program,
200            "):"
201          ] }),
202          /* @__PURE__ */ jsxRuntimeExports.jsx(
203            "select",
204            {
205              value: program,
206              onChange: (e) => setProgram(parseInt(e.target.value)),
207              className: "bg-zinc-900 text-zinc-200 border border-zinc-700 rounded px-2 py-0.5 text-xs outline-none focus:ring-1 focus:ring-indigo-500 font-mono max-w-[160px]",
208              children: Array.from({ length: 128 }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: i, className: "text-zinc-200 bg-zinc-900", children: [
209                i,
210                ": ",
211                getSoundfontName(i).replace(/_/g, " ")
212              ] }, i))
213            }
214          )
215        ] })
216      ] }),
217      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
218        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-800/80 px-2 py-0.5 rounded-lg border border-zinc-700", children: [
219          /* @__PURE__ */ jsxRuntimeExports.jsx(
220            "button",
221            {
222              onClick: () => changeOctave(baseOctave - 1),
223              disabled: baseOctave <= 0,
224              className: "p-1 hover:bg-zinc-700 rounded text-zinc-300 disabled:opacity-30",
225              title: "Previous Octave (L-Shift)",
226              children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "w-3.5 h-3.5" })
227            }
228          ),
229          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-[11px] font-bold text-zinc-200 px-1", children: [
230            "Oct ",
231            baseOctave,
232            " (C",
233            baseOctave,
234            "-B",
235            baseOctave,
236            ")"
237          ] }),
238          /* @__PURE__ */ jsxRuntimeExports.jsx(
239            "button",
240            {
241              onClick: () => changeOctave(baseOctave + 1),
242              disabled: baseOctave >= 8,
243              className: "p-1 hover:bg-zinc-700 rounded text-zinc-300 disabled:opacity-30",
244              title: "Next Octave (R-Shift)",
245              children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-3.5 h-3.5" })
246            }
247          )
248        ] }),
249        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-800/80 p-0.5 rounded-lg border border-zinc-700 text-[10px]", children: [
250          /* @__PURE__ */ jsxRuntimeExports.jsx(
251            "button",
252            {
253              onClick: () => setLabelMode("pc"),
254              className: `px-2 py-0.5 rounded font-bold transition-colors ${labelMode === "pc" ? "bg-indigo-600 text-white" : "text-zinc-400 hover:text-white"}`,
255              children: "PC Keys"
256            }
257          ),
258          /* @__PURE__ */ jsxRuntimeExports.jsx(
259            "button",
260            {
261              onClick: () => setLabelMode("notes"),
262              className: `px-2 py-0.5 rounded font-bold transition-colors ${labelMode === "notes" ? "bg-indigo-600 text-white" : "text-zinc-400 hover:text-white"}`,
263              children: "Notes"
264            }
265          ),
266          /* @__PURE__ */ jsxRuntimeExports.jsx(
267            "button",
268            {
269              onClick: () => setLabelMode("off"),
270              className: `px-2 py-0.5 rounded font-bold transition-colors ${labelMode === "off" ? "bg-indigo-600 text-white" : "text-zinc-400 hover:text-white"}`,
271              children: "Off"
272            }
273          )
274        ] }),
275        /* @__PURE__ */ jsxRuntimeExports.jsx(
276          "button",
277          {
278            onClick: stopAllNotes,
279            className: "px-2.5 py-1 bg-zinc-800 hover:bg-red-900/50 text-zinc-300 hover:text-red-300 border border-zinc-700 rounded-lg text-[10px] font-bold transition-colors",
280            title: "Stop all playing notes",
281            children: "Panic"
282          }
283        ),
284        /* @__PURE__ */ jsxRuntimeExports.jsx(
285          "button",
286          {
287            onClick: onClose,
288            className: "p-1 text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-lg transition-colors",
289            title: "Close Keyboard",
290            children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "w-5 h-5" })
291          }
292        )
293      ] })
294    ] }),
295    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-28 w-full border border-zinc-700 bg-zinc-950 rounded-lg overflow-hidden flex select-none touch-none shadow-inner", children: [
296      isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center p-8 bg-zinc-800 rounded-lg animate-pulse absolute inset-0 z-10", children: [
297        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
298          /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-6 h-6 animate-spin text-zinc-400" }),
299          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-mono text-sm", children: "Loading soundfont engine..." })
300        ] }),
301        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-64 bg-zinc-900 rounded-full h-2 overflow-hidden border border-zinc-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
302          "div",
303          {
304            className: "bg-indigo-500 h-2 transition-all duration-300",
305            style: { width: `${Math.round(loadingProgress * 100)}%` }
306          }
307        ) }),
308        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-500 text-xs mt-2 font-mono", children: [
309          Math.round(loadingProgress * 100),
310          "%"
311        ] })
312      ] }),
313      /* @__PURE__ */ jsxRuntimeExports.jsx(
314        "canvas",
315        {
316          ref: canvasRef,
317          width: 1400,
318          height: 112,
319          className: "w-full h-full cursor-pointer",
320          onPointerDown: handlePointerDown,
321          onPointerMove: handlePointerMove,
322          onPointerUp: handlePointerUp,
323          onPointerLeave: handlePointerUp
324        }
325      )
326    ] }),
327    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 text-center font-medium", children: "Play chords using PC Keyboard (Z X C V...) or click/drag keys. Press L-Shift / R-Shift to shift octaves." })
328  ] });
329};
330const SoundfontManager = () => {
331  var _a;
332  const { t } = useTranslation();
333  const [soundfonts, setSoundfonts] = reactExports.useState([]);
334  const [downloadedLibraries, setDownloadedLibraries] = reactExports.useState([]);
335  const [jobs, setJobs] = reactExports.useState([]);
336  const [loading, setLoading] = reactExports.useState(true);
337  const [activeSf, setActiveSf] = reactExports.useState(null);
338  const [expandedLibraries, setExpandedLibraries] = reactExports.useState(/* @__PURE__ */ new Set());
339  const [expandedFolders, setExpandedFolders] = reactExports.useState(/* @__PURE__ */ new Set());
340  const [customUrl, setCustomUrl] = reactExports.useState("");
341  const [preRenderedSf, setPreRenderedSf] = reactExports.useState("GeneralUser-GS");
342  const [metadata, setMetadata] = reactExports.useState({});
343  const [defaultLiveSf, setDefaultLiveSf] = reactExports.useState("MusyngKite");
344  const [defaultConverterSf, setDefaultConverterSf] = reactExports.useState("GeneralUser-GS.sf2");
345  const [defaultWasmSf, setDefaultWasmSf] = reactExports.useState("SoundBlasterOld.sf2");
346  const [defaultKeyboardSf, setDefaultKeyboardSf] = reactExports.useState("MusyngKite");
347  const [defaultJsSf, setDefaultJsSf] = reactExports.useState("MusyngKite");
348  const [showEngine, setShowEngine] = reactExports.useState(false);
349  const [showEngineOrder, setShowEngineOrder] = reactExports.useState(false);
350  const [showExternal, setShowExternal] = reactExports.useState(false);
351  const [showJobs, setShowJobs] = reactExports.useState(false);
352  const [showAssets, setShowAssets] = reactExports.useState(true);
353  const [showKeyboard, setShowKeyboard] = reactExports.useState(false);
354  const [selectedSfForKeyboard, setSelectedSfForKeyboard] = reactExports.useState("");
355  const [keyboardProgram, setKeyboardProgram] = reactExports.useState(0);
356  const [keyboardBank, setKeyboardBank] = reactExports.useState(0);
357  const [engineOrder, setEngineOrder] = reactExports.useState({
358    watchDesktop: ["opus", "singleopus", "wasmsynth", "synth", "fmsynth", "chiptune", "webmidi"],
359    watchMobile: ["singleopus", "opus", "wasmsynth", "chiptune", "fmsynth", "synth", "webmidi"],
360    slowMobile: ["singleopus", "chiptune", "wasmsynth", "fmsynth", "synth", "webmidi"],
361    miniPlayer: ["singleopus", "wasmsynth", "synth", "fmsynth", "chiptune", "webmidi"]
362  });
363  const [profiles, setProfiles] = reactExports.useState({});
364  const [autoLevelingLibs, setAutoLevelingLibs] = reactExports.useState({});
365  const handleSaveGain = async (libName, newGain) => {
366    const baseName = getBaseName(libName);
367    const existing = profiles[baseName] || {
368      skipStep: 1,
369      noteDuration: 2,
370      opusBitrate: "64k",
371      gain: 1.8,
372      fadeOutDuration: 0.3,
373      reverbLevel: 0.5,
374      chorusLevel: 0.3,
375      notes: "60-72",
376      program: 0
377    };
378    const nextProfiles = {
379      ...profiles,
380      [baseName]: {
381        ...existing,
382        gain: newGain,
383        savedAt: (/* @__PURE__ */ new Date()).toISOString()
384      }
385    };
386    try {
387      await api.saveSystemSetting("SOUNDFONT_QUALITY_PROFILES", nextProfiles);
388      setProfiles(nextProfiles);
389    } catch (e) {
390      console.error("Failed to save soundfont gain profile", e);
391    }
392  };
393  const handleAutoLevelForRow = async (libName) => {
394    const baseName = getBaseName(libName);
395    let targetSf2Path = libName.endsWith(".sf2") ? libName : null;
396    if (!targetSf2Path) {
397      const found = soundfonts.find((f) => {
398        var _a2;
399        return ((_a2 = f.path) == null ? void 0 : _a2.endsWith(".sf2")) && f.path.includes(baseName);
400      });
401      if (found) targetSf2Path = found.path;
402      else targetSf2Path = `${baseName}.sf2`;
403    }
404    setAutoLevelingLibs((prev) => ({ ...prev, [libName]: true }));
405    try {
406      const res = await api.autoLevelSoundfont(targetSf2Path, 0, -18);
407      if (res.recommendedGain) {
408        const newGain = parseFloat(Math.min(10, Math.max(0.1, res.recommendedGain)).toFixed(2));
409        await handleSaveGain(baseName, newGain);
410      }
411    } catch (e) {
412      alert(`Auto level failed: ${e.message || "Make sure the source .sf2 file exists in storage."}`);
413    } finally {
414      setAutoLevelingLibs((prev) => ({ ...prev, [libName]: false }));
415    }
416  };
417  const [draggedItem, setDraggedItem] = reactExports.useState(null);
418  const handleDragStart = (context, index) => {
419    setDraggedItem({ type: "list", context, index });
420  };
421  const handleMatrixDragStart = (id) => {
422    setDraggedItem({ type: "matrix", id });
423  };
424  const handleRemoveListMode = (context, index) => {
425    const currentList = [...engineOrder[context]];
426    currentList.splice(index, 1);
427    setEngineOrder((prev) => ({
428      ...prev,
429      [context]: currentList
430    }));
431  };
432  const handleDragOver = (e) => {
433    e.preventDefault();
434  };
435  const handleDrop = (context, targetIndex) => {
436    if (!draggedItem) return;
437    const currentList = [...engineOrder[context]];
438    if (draggedItem.type === "matrix" && draggedItem.id) {
439      if (currentList.includes(draggedItem.id)) return;
440      currentList.splice(targetIndex, 0, draggedItem.id);
441    } else if (draggedItem.type === "list" && draggedItem.context === context && draggedItem.index !== void 0) {
442      const [movedItem] = currentList.splice(draggedItem.index, 1);
443      currentList.splice(targetIndex, 0, movedItem);
444    } else {
445      return;
446    }
447    setEngineOrder((prev) => ({
448      ...prev,
449      [context]: currentList
450    }));
451    setDraggedItem(null);
452  };
453  const saveEngineOrder = async (nextOrder) => {
454    try {
455      await api.saveSystemSetting("PLAYBACK_ENGINE_ORDER", nextOrder);
456      setEngineOrder(nextOrder);
457      alert("Playback engine priority order saved successfully!");
458    } catch (e) {
459      alert("Failed to save engine order");
460    }
461  };
462  const ENGINE_DETAILS = {
463    opus: { label: "STEMS", color: "#ff5500", desc: "Instrument Stems (Opus)", condition: "Watch Page + Processed Stems" },
464    singleopus: { label: "OPUS", color: "#3b82f6", desc: "Single-file Opus", condition: "Master Audio + Pitch = 0" },
465    synth: { label: "JS", color: "#10b981", desc: "Web Audio JS SoundFont", condition: "JS Opus notes (Web Audio)" },
466    wasmsynth: { label: "SF", color: "#06b6d4", desc: "Direct SF2/SF3 SoundFont (WASM)", condition: "WASM & AudioWorklet (HTTPS)" },
467    fmsynth: { label: "FM", color: "#8b5cf6", desc: "16-bit FM Synth", condition: "Real-time FM synth (Low CPU)" },
468    chiptune: { label: "8BIT", color: "#f59e0b", desc: "Chiptune 8-bit", condition: "Retro 8-bit waves (Ultra Low CPU)" },
469    webmidi: { label: "MIDI", color: "#6366f1", desc: "Web MIDI Output", condition: "Web MIDI API + Connected Device" }
470  };
471  const renderEngineOrderEditor = () => {
472    const contexts = [
473      { id: "watchDesktop", name: "Watch Page (Desktop)", desc: "Preferred playback engine layout on desktop watch page." },
474      { id: "watchMobile", name: "Watch Page (Mobile)", desc: "Preferred playback engine layout on mobile watch page." },
475      { id: "slowMobile", name: "Slow / Low-End Devices", desc: "Fallback priority on low-end CPUs or slow network connections." },
476      { id: "miniPlayer", name: "Mini Player (Previews)", desc: "Preferred playback engine layout for home/search previews." }
477    ];
478    const resetToDefault = (context) => {
479      const defaults = {
480        watchDesktop: ["opus", "singleopus", "synth", "wasmsynth", "fmsynth", "chiptune", "webmidi"],
481        watchMobile: ["singleopus", "opus", "chiptune", "wasmsynth", "fmsynth", "synth", "webmidi"],
482        slowMobile: ["singleopus", "chiptune", "wasmsynth", "fmsynth", "synth", "webmidi"],
483        miniPlayer: ["singleopus", "synth", "wasmsynth", "fmsynth", "chiptune", "webmidi"]
484      };
485      setEngineOrder((prev) => ({
486        ...prev,
487        [context]: defaults[context]
488      }));
489    };
490    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden shadow-sm mb-6", children: [
491      /* @__PURE__ */ jsxRuntimeExports.jsx(
492        "div",
493        {
494          onClick: () => setShowEngineOrder(!showEngineOrder),
495          className: `p-4 ${showEngineOrder ? "border-b border-zinc-200 dark:border-zinc-800" : ""} bg-zinc-50/50 dark:bg-zinc-800/50 flex items-center justify-between cursor-pointer select-none`,
496          children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
497            showEngineOrder ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-indigo-500 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-indigo-500 shrink-0" }),
498            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
499              /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm uppercase tracking-wider text-zinc-700 dark:text-zinc-300 flex items-center gap-2", children: [
500                /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { className: "w-4 h-4 text-indigo-500" }),
501                "Playback Engine Priority Order & Rules"
502              ] }),
503              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-zinc-500 mt-0.5", children: "Drag and drop cards within each context column to rearrange selection priority. The engine at the top will be selected first if its prerequisite condition is satisfied." })
504            ] })
505          ] })
506        }
507      ),
508      showEngineOrder && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-5 space-y-6", children: [
509        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4", children: contexts.map((ctx) => {
510          const engines = engineOrder[ctx.id] || [];
511          return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50/50 dark:bg-zinc-950/20 border border-zinc-200 dark:border-zinc-800 rounded-xl p-4 flex flex-col h-[520px]", children: [
512            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex justify-between items-center", children: [
513              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
514                /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-xs text-zinc-800 dark:text-zinc-200 uppercase tracking-tight", children: ctx.name }),
515                /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[9px] text-zinc-400 mt-0.5 leading-tight", children: ctx.desc })
516              ] }),
517              /* @__PURE__ */ jsxRuntimeExports.jsx(
518                "button",
519                {
520                  onClick: () => resetToDefault(ctx.id),
521                  className: "text-[9px] font-bold text-zinc-400 hover:text-indigo-500 transition-colors uppercase shrink-0",
522                  children: "Reset"
523                }
524              )
525            ] }),
526            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto space-y-2 custom-scrollbar pr-1", children: engines.map((engineId, idx) => {
527              const details = ENGINE_DETAILS[engineId] || { label: engineId.toUpperCase(), color: "#71717a", desc: "Unknown Engine", condition: "None" };
528              return /* @__PURE__ */ jsxRuntimeExports.jsxs(
529                "div",
530                {
531                  draggable: true,
532                  onDragStart: () => handleDragStart(ctx.id, idx),
533                  onDragOver: handleDragOver,
534                  onDrop: () => handleDrop(ctx.id, idx),
535                  className: "flex flex-col gap-1.5 p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 hover:border-indigo-500/50 rounded-lg cursor-grab active:cursor-grabbing transition-all select-none shadow-sm group hover:scale-[1.01] relative",
536                  children: [
537                    /* @__PURE__ */ jsxRuntimeExports.jsx(
538                      "button",
539                      {
540                        onClick: (e) => {
541                          e.stopPropagation();
542                          handleRemoveListMode(ctx.id, idx);
543                        },
544                        className: "absolute -top-2 -right-2 bg-zinc-200 dark:bg-zinc-700 hover:bg-red-500 hover:text-white dark:hover:bg-red-500 rounded-full p-1 shadow-sm opacity-0 group-hover:opacity-100 transition-opacity",
545                        title: "Remove Engine",
546                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "w-2.5 h-2.5" })
547                      }
548                    ),
549                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
550                      /* @__PURE__ */ jsxRuntimeExports.jsx(
551                        "div",
552                        {
553                          className: "font-mono text-[8px] font-black px-1.5 py-0.5 rounded border tracking-widest shrink-0 text-center min-w-[38px] bg-zinc-950",
554                          style: {
555                            color: details.color,
556                            textShadow: `0 0 3px ${details.color}`
557                          },
558                          children: details.label
559                        }
560                      ),
561                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-zinc-700 dark:text-zinc-300 truncate", children: details.desc }) }),
562                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[8px] text-zinc-400 font-mono shrink-0", children: [
563                        "#",
564                        idx + 1
565                      ] })
566                    ] }),
567                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-[8px] text-zinc-500 dark:text-zinc-400 bg-zinc-100 dark:bg-zinc-950 px-1.5 py-0.5 rounded font-mono", children: [
568                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
569                        "⚡ ",
570                        details.condition
571                      ] }),
572                      /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { className: "w-2.5 h-2.5 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity" })
573                    ] })
574                  ]
575                },
576                engineId
577              );
578            }) })
579          ] }, ctx.id);
580        }) }),
581        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-950/40 border border-zinc-200 dark:border-zinc-800 rounded-xl p-4 space-y-3 mt-6", children: [
582          /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "font-bold text-xs uppercase tracking-wider text-zinc-700 dark:text-zinc-300 flex items-center gap-2", children: [
583            /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "w-3.5 h-3.5 text-indigo-500" }),
584            "Playback Engine Prerequisite & Fallback Matrix"
585          ] }),
586          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-500 mb-2", children: "Drag an item from below into a column above to add it to the priority list." }),
587          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 text-[10px]", children: [
588            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("opus"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
589              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-amber-500 block", children: "🎵 STEMS (Opus Multi-Track)" }),
590              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Requires track to be fully processed with individual stem files available. Only supported on Watch Page." })
591            ] }),
592            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("singleopus"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
593              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-blue-400 block", children: "🎧 OPUS (Single Pre-mixed)" }),
594              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Requires pre-rendered master audio. Automatically bypassed if user applies Pitch Shift ≠ 0." })
595            ] }),
596            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("wasmsynth"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
597              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-cyan-400 block", children: "🎹 SF (Direct WASM SoundFont)" }),
598              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Synthesizes raw .sf2 or .sf3 files in browser via WASM AudioWorklet. Requires HTTPS/localhost context." })
599            ] }),
600            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("synth"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
601              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-emerald-400 block", children: "⚡ JS (Web Audio JS SoundFont)" }),
602              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Decodes pre-rendered Opus note samples per instrument. Works on plain HTTP. Heavy CPU on mobile." })
603            ] }),
604            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("fmsynth"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
605              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-purple-400 block", children: "📻 FM (16-bit Arcade FM)" }),
606              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Generates retro FM synthesis in real time. Works everywhere with minimal CPU overhead." })
607            ] }),
608            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("chiptune"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
609              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-yellow-400 block", children: "🕹️ 8BIT (Chiptune Waves)" }),
610              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Ultra-light 8-bit square/triangle waves. Recommended for slow devices or weak connections." })
611            ] }),
612            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { draggable: true, onDragStart: () => handleMatrixDragStart("webmidi"), className: "p-2.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg space-y-1 cursor-grab active:cursor-grabbing hover:border-indigo-500/50 transition-colors", children: [
613              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-indigo-400 block", children: "🔌 MIDI (Web MIDI Output)" }),
614              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Sends raw MIDI signals to external hardware or loopback MIDI devices. Requires browser support." })
615            ] })
616          ] })
617        ] }),
618        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end pt-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
619          "button",
620          {
621            onClick: () => saveEngineOrder(engineOrder),
622            className: "px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-xs font-bold rounded-lg transition-colors flex items-center gap-1.5 shadow-sm",
623            children: [
624              /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "w-3.5 h-3.5" }),
625              "Save Priority Order"
626            ]
627          }
628        ) }),
629        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-950/40 border border-zinc-200 dark:border-zinc-800 rounded-xl p-4 space-y-3 mt-6", children: [
630          /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "font-bold text-xs uppercase tracking-wider text-zinc-700 dark:text-zinc-300 flex items-center gap-2", children: [
631            /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "w-3.5 h-3.5 text-emerald-500" }),
632            "Automatic JS Engine Fallback Configuration"
633          ] }),
634          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-500 mb-2", children: "When a track falls back to the JS (Web Audio) engine, but the active soundfont does not have JS files available, the player will automatically try to load the fallback soundfont selected below." }),
635          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-w-md", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
636            "select",
637            {
638              value: defaultJsSf || "MusyngKite",
639              onChange: (e) => saveGlobalSetting("DEFAULT_JS_SOUNDFONT", e.target.value),
640              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
641              children: downloadedLibraries.filter((lib) => lib.name !== "Root").sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base", numeric: true })).map((lib) => {
642                const typeSuffix = getSoundfontFormatType(lib);
643                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
644                  "option",
645                  {
646                    value: lib.name,
647                    className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
648                    children: [
649                      lib.name,
650                      " (",
651                      typeSuffix,
652                      ") (",
653                      formatFileSize(lib.size),
654                      ")"
655                    ]
656                  },
657                  lib.name
658                );
659              })
660            }
661          ) })
662        ] })
663      ] })
664    ] });
665  };
666  const FALLBACK_METADATA = {
667    "MusyngKite": { name: "MusyngKite", desc: "High Fidelity" },
668    "FluidR3_GM": { name: "FluidR3 GM", desc: "Orchestral" },
669    "FatBoy": { name: "FatBoy", desc: "Punchy/Modern" },
670    "Chaos": { name: "Chaos", desc: "Aggressive/Raw" },
671    "GeneralUser-GS": { name: "GeneralUser GS", desc: "Balanced" },
672    "GravisUltraSound": { name: "Gravis UltraSound", desc: "Retro/Classic" },
673    "MuseScore_General": { name: "MuseScore General", desc: "Balanced/Classical" },
674    "TimGM6mb": { name: "TimGM6mb", desc: "Ultralight" },
675    "Aspirin": { name: "Aspirin DX", desc: "Warm/Balanced" }
676  };
677  const presets = [
678    { name: "Chaos (SF2)", url: "https://github.com/surikov/webaudiofontdata/raw/master/sf2/chaos.sf2" },
679    { name: "GeneralUser GS (SF2)", url: "https://github.com/surikov/webaudiofontdata/raw/master/sf2/GeneralUserGS.sf2" },
680    { name: "MuseScore General (SF2)", url: "https://ftp.osuosl.org/pub/musescore/soundfont/MuseScore_General/MuseScore_General.sf2" },
681    { name: "FluidR3 GM (SF2)", url: "https://github.com/fhunleth/midi_synth/releases/download/v0.1.0/FluidR3_GM.sf2" },
682    { name: "TimGM6mb (SF2)", url: "https://github.com/craffel/pretty-midi/raw/main/pretty_midi/TimGM6mb.sf2" },
683    { name: "Aspirin (SF2)", url: "https://github.com/surikov/webaudiofontdata/raw/master/sf2/Aspirin.sf2" }
684  ];
685  const toggleLibrary = (baseName) => {
686    setExpandedLibraries((prev) => {
687      const next = new Set(prev);
688      if (next.has(baseName)) next.delete(baseName);
689      else next.add(baseName);
690      return next;
691    });
692  };
693  const toggleFolder = (folderName, e) => {
694    e.stopPropagation();
695    setExpandedFolders((prev) => {
696      const next = new Set(prev);
697      if (next.has(folderName)) next.delete(folderName);
698      else next.add(folderName);
699      return next;
700    });
701  };
702  const fetchData = async (force = false) => {
703    setLoading(true);
704    try {
705      const [sfData, jobData, preSet, metaSet, defaultLiveSet, defaultConverterSet, defaultWasmSet, defaultKeyboardSet, defaultJsSet, profileSet, engineOrderSet] = await Promise.all([
706        api.getSoundfonts({ scan: force }),
707        api.getSoundfontJobs(),
708        api.getSystemSetting("PRE_RENDERED_SOUNDFONT"),
709        api.getSystemSetting("SOUNDFONT_METADATA"),
710        api.getSystemSetting("DEFAULT_LIVE_SOUNDFONT"),
711        api.getSystemSetting("DEFAULT_CONVERTER_SOUNDFONT"),
712        api.getSystemSetting("DEFAULT_WASM_SOUNDFONT"),
713        api.getSystemSetting("DEFAULT_KEYBOARD_SOUNDFONT"),
714        api.getSystemSetting("DEFAULT_JS_SOUNDFONT"),
715        api.getSystemSetting("SOUNDFONT_QUALITY_PROFILES"),
716        api.getSystemSetting("PLAYBACK_ENGINE_ORDER")
717      ]);
718      setSoundfonts(sfData.files || []);
719      setDownloadedLibraries(sfData.libraries || []);
720      if (sfData.libraries && sfData.libraries.length > 0) {
721        setSelectedSfForKeyboard((prev) => prev || sfData.libraries[0].name);
722        const allBaseNames = new Set(sfData.libraries.map((lib) => getBaseName(lib.name)));
723        setExpandedLibraries((prev) => prev.size === 0 ? allBaseNames : prev);
724      }
725      setActiveSf(sfData.active);
726      setJobs(jobData);
727      if (preSet) setPreRenderedSf(preSet.value);
728      if (defaultLiveSet) setDefaultLiveSf(defaultLiveSet.value || "MusyngKite");
729      if (defaultConverterSet) setDefaultConverterSf(defaultConverterSet.value || "GeneralUser-GS.sf2");
730      if (defaultWasmSet) setDefaultWasmSf(defaultWasmSet.value || "SoundBlasterOld.sf2");
731      if (defaultKeyboardSet) setDefaultKeyboardSf(defaultKeyboardSet.value || "MusyngKite");
732      if (defaultJsSet) setDefaultJsSf(defaultJsSet.value || "MusyngKite");
733      if (profileSet && profileSet.value) {
734        try {
735          setProfiles(typeof profileSet.value === "string" ? JSON.parse(profileSet.value) : profileSet.value);
736        } catch {
737        }
738      }
739      if (engineOrderSet && engineOrderSet.value) {
740        try {
741          const parsed = typeof engineOrderSet.value === "string" ? JSON.parse(engineOrderSet.value) : engineOrderSet.value;
742          const defaults = {
743            watchDesktop: ["opus", "singleopus", "wasmsynth", "synth", "fmsynth", "chiptune", "webmidi"],
744            watchMobile: ["singleopus", "opus", "wasmsynth", "chiptune", "fmsynth", "synth", "webmidi"],
745            slowMobile: ["singleopus", "chiptune", "wasmsynth", "fmsynth", "synth", "webmidi"],
746            miniPlayer: ["singleopus", "wasmsynth", "synth", "fmsynth", "chiptune", "webmidi"]
747          };
748          setEngineOrder({ ...defaults, ...parsed || {} });
749        } catch {
750        }
751      }
752      if (metaSet && metaSet.value) {
753        try {
754          const parsed = typeof metaSet.value === "string" ? JSON.parse(metaSet.value) : metaSet.value;
755          const nextMeta = { ...parsed || {} };
756          Object.keys(FALLBACK_METADATA).forEach((key) => {
757            if (!nextMeta[key]) {
758              nextMeta[key] = { ...FALLBACK_METADATA[key], visible: true, enabledWasm: true, access: "free" };
759            }
760          });
761          setMetadata(nextMeta);
762        } catch (e) {
763          console.error("Metadata JSON corrupt:", e);
764          const fallback = { ...FALLBACK_METADATA };
765          setMetadata(fallback);
766        }
767      } else if (metaSet) {
768        const fallback = { ...FALLBACK_METADATA };
769        setMetadata(fallback);
770      } else {
771        console.warn("Failed to fetch SOUNDFONT_METADATA. Keeping current local state.");
772      }
773    } catch (e) {
774      console.error("Failed to fetch soundfonts:", e);
775    } finally {
776      setLoading(false);
777    }
778  };
779  const fetchJobsOnly = async () => {
780    try {
781      const jobData = await api.getSoundfontJobs();
782      const hadActiveJobs = jobs.some((j) => j.status === "pending" || j.status === "processing");
783      const hasActiveJobsNow = jobData.some((j) => j.status === "pending" || j.status === "processing" || j.status === "running");
784      setJobs(jobData);
785      if (hadActiveJobs && !hasActiveJobsNow) {
786        fetchData();
787      }
788    } catch (e) {
789      console.error("Failed to fetch jobs:", e);
790    }
791  };
792  const saveGlobalSetting = async (key, value) => {
793    try {
794      await api.saveSystemSetting(key, value);
795      fetchData();
796    } catch (e) {
797      alert("Failed to save setting");
798    }
799  };
800  reactExports.useEffect(() => {
801    fetchData();
802  }, []);
803  reactExports.useEffect(() => {
804    const hasActiveJobs = jobs.some((j) => j.status === "pending" || j.status === "processing");
805    if (!hasActiveJobs) return;
806    const interval = setInterval(fetchJobsOnly, 5e3);
807    return () => clearInterval(interval);
808  }, [jobs]);
809  const clearFinishedJobs = async () => {
810    const finished = jobs.filter((j) => j.status === "completed");
811    if (finished.length === 0) return;
812    try {
813      await Promise.all(finished.map((j) => api.deleteSoundfontJob(j.id)));
814      fetchData();
815    } catch (e) {
816      alert("Failed to clear jobs");
817    }
818  };
819  const startSf2Conversion = async (sfPath) => {
820    if (!confirm("Generating a JS library from an SF2 takes 5-15 minutes and can use significant storage (up to 400MB+). Continue?")) return;
821    try {
822      await api.startSoundfontJob(`Sample SF2 to JS: ${sfPath.split("/").pop()}`, sfPath, "SF2_TO_JS");
823      fetchData();
824    } catch (e) {
825      alert("Failed to start SF2 sampling");
826    }
827  };
828  const startHsf3BankConversion = async (sfPath) => {
829    if (!confirm("Convert this SF2 to HSF3 Bank (Split instrument files + DRM obfuscated)?")) return;
830    try {
831      await api.startSoundfontJob(`Convert SF2 → HSF3 Bank: ${sfPath.split("/").pop()}`, sfPath, "SF2_TO_HSF3_BANK");
832      fetchData();
833    } catch (e) {
834      alert("Failed to start HSF3 Bank conversion");
835    }
836  };
837  const startSf3Conversion = async (sfPath) => {
838    if (!confirm("Convert this SF2 to SF3 (OGG Vorbis compressed)? The output will be ~90% smaller than the original SF2.")) return;
839    try {
840      await api.startSoundfontJob(`Convert SF2 → SF3: ${sfPath.split("/").pop()}`, sfPath, "SF2_TO_SF3");
841      fetchData();
842    } catch (e) {
843      alert("Failed to start SF3 conversion");
844    }
845  };
846  const startBulkSf2Conversion = async () => {
847    const sf2Libs = downloadedLibraries.filter((lib) => lib.name.toLowerCase().endsWith(".sf2"));
848    if (sf2Libs.length === 0) {
849      alert("No .sf2 soundfonts found to convert.");
850      return;
851    }
852    const existingJsFolders = new Set(
853      downloadedLibraries.filter((lib) => !lib.name.toLowerCase().endsWith(".sf2") && !lib.name.toLowerCase().endsWith(".sf3") && !lib.name.toLowerCase().endsWith(".sfark")).map((lib) => lib.name.toLowerCase())
854    );
855    const unconvertedLibs = sf2Libs.filter((lib) => !existingJsFolders.has(lib.name.toLowerCase().replace(/\.sf2$/, "")));
856    const alreadyConvertedCount = sf2Libs.length - unconvertedLibs.length;
857    let targetsToQueue = unconvertedLibs;
858    if (alreadyConvertedCount > 0) {
859      if (unconvertedLibs.length === 0) {
860        const reConvert = confirm(`All ${sf2Libs.length} .sf2 soundfont(s) have already been sampled to JS.
861
862Do you want to FORCE re-sample all ${sf2Libs.length} soundfonts?`);
863        if (!reConvert) return;
864        targetsToQueue = sf2Libs;
865      } else {
866        const msg = `Found ${sf2Libs.length} .sf2 soundfont(s):
867• ${unconvertedLibs.length} pending JS sampling
868• ${alreadyConvertedCount} already sampled to JS
869
870Click OK to sample ONLY the ${unconvertedLibs.length} pending soundfonts.
871(Skipping the ${alreadyConvertedCount} already-sampled files)`;
872        if (!confirm(msg)) return;
873        targetsToQueue = unconvertedLibs;
874      }
875    } else {
876      if (!confirm(`Are you sure you want to queue ${targetsToQueue.length} .sf2 soundfont(s) for JS conversion? This may take time.`)) return;
877    }
878    let successCount = 0;
879    let failCount = 0;
880    for (const lib of targetsToQueue) {
881      try {
882        await api.startSoundfontJob(`Sample SF2 to JS: ${lib.name.split("/").pop()}`, lib.name, "SF2_TO_JS");
883        successCount++;
884      } catch (e) {
885        console.error(`Failed to queue ${lib.name}`, e);
886        failCount++;
887      }
888    }
889    fetchData();
890    if (failCount > 0) {
891      alert(`Queued ${successCount} jobs, but failed to queue ${failCount}.`);
892    } else {
893      alert(`Successfully queued ${successCount} conversion jobs.`);
894    }
895  };
896  const startBulkSf3Conversion = async () => {
897    const sf2Libs = downloadedLibraries.filter((lib) => lib.name.toLowerCase().endsWith(".sf2"));
898    if (sf2Libs.length === 0) {
899      alert("No .sf2 soundfonts found to convert.");
900      return;
901    }
902    const existingSf3s = new Set(
903      downloadedLibraries.filter((lib) => lib.name.toLowerCase().endsWith(".sf3")).map((lib) => lib.name.toLowerCase().replace(/\.sf3$/, ""))
904    );
905    const unconvertedLibs = sf2Libs.filter((lib) => !existingSf3s.has(lib.name.toLowerCase().replace(/\.sf2$/, "")));
906    const alreadyConvertedCount = sf2Libs.length - unconvertedLibs.length;
907    let targetsToQueue = unconvertedLibs;
908    if (alreadyConvertedCount > 0) {
909      if (unconvertedLibs.length === 0) {
910        const reConvert = confirm(`All ${sf2Libs.length} .sf2 soundfont(s) have already been converted to SF3.
911
912Do you want to FORCE re-convert all ${sf2Libs.length} soundfonts?`);
913        if (!reConvert) return;
914        targetsToQueue = sf2Libs;
915      } else {
916        const msg = `Found ${sf2Libs.length} .sf2 soundfont(s):
917• ${unconvertedLibs.length} pending conversion
918• ${alreadyConvertedCount} already converted to SF3
919
920Click OK to convert ONLY the ${unconvertedLibs.length} pending soundfonts.
921(Skipping the ${alreadyConvertedCount} already-converted files)`;
922        if (!confirm(msg)) return;
923        targetsToQueue = unconvertedLibs;
924      }
925    } else {
926      if (!confirm(`Convert ${targetsToQueue.length} .sf2 soundfont(s) to SF3? This compresses samples with OGG Vorbis (~90% smaller).`)) return;
927    }
928    let successCount = 0;
929    let failCount = 0;
930    for (const lib of targetsToQueue) {
931      try {
932        await api.startSoundfontJob(`Convert SF2 → SF3: ${lib.name.split("/").pop()}`, lib.name, "SF2_TO_SF3");
933        successCount++;
934      } catch (e) {
935        console.error(`Failed to queue ${lib.name}`, e);
936        failCount++;
937      }
938    }
939    fetchData();
940    if (failCount > 0) {
941      alert(`Queued ${successCount} jobs, but failed to queue ${failCount}.`);
942    } else {
943      alert(`Successfully queued ${successCount} SF3 conversion jobs.`);
944    }
945  };
946  const startBulkHsf3BankConversion = async () => {
947    const sf2Libs = downloadedLibraries.filter((lib) => lib.name.toLowerCase().endsWith(".sf2"));
948    if (sf2Libs.length === 0) {
949      alert("No .sf2 soundfonts found to convert.");
950      return;
951    }
952    const existingHsf3s = new Set(
953      downloadedLibraries.filter((lib) => lib.isHsf3Bank || lib.name.toLowerCase().endsWith("_hsf3bank")).map((lib) => lib.name.toLowerCase().replace(/_hsf3bank$/, ""))
954    );
955    const unconvertedLibs = sf2Libs.filter((lib) => !existingHsf3s.has(lib.name.toLowerCase().replace(/\.sf2$/, "")));
956    const alreadyConvertedCount = sf2Libs.length - unconvertedLibs.length;
957    let targetsToQueue = unconvertedLibs;
958    if (alreadyConvertedCount > 0) {
959      if (unconvertedLibs.length === 0) {
960        const reConvert = confirm(`All ${sf2Libs.length} .sf2 soundfont(s) have already been converted to HSF3 Banks.
961
962Do you want to FORCE re-convert all ${sf2Libs.length} soundfonts?`);
963        if (!reConvert) return;
964        targetsToQueue = sf2Libs;
965      } else {
966        const overwrite = confirm(`Found ${sf2Libs.length} .sf2 soundfont(s):
967• ${unconvertedLibs.length} pending conversion
968• ${alreadyConvertedCount} already converted to HSF3 Banks
969
970Do you want to FORCE RE-CONVERT ALL ${sf2Libs.length} soundfonts (including existing)?
971
972Click OK to overwrite all.
973Click Cancel to proceed with ONLY the ${unconvertedLibs.length} pending ones.`);
974        if (overwrite) {
975          targetsToQueue = sf2Libs;
976        } else {
977          const proceed = confirm(`Proceeding with ONLY the ${unconvertedLibs.length} pending soundfonts. Are you sure?`);
978          if (!proceed) return;
979          targetsToQueue = unconvertedLibs;
980        }
981      }
982    } else {
983      if (!confirm(`Convert ${targetsToQueue.length} .sf2 soundfont(s) to HSF3 Banks? This creates split DRM obfuscated instrument files.`)) return;
984    }
985    let successCount = 0;
986    let failCount = 0;
987    for (const lib of targetsToQueue) {
988      try {
989        await api.startSoundfontJob(`Convert SF2 → HSF3 Bank: ${lib.name.split("/").pop()}`, lib.name, "SF2_TO_HSF3_BANK");
990        successCount++;
991      } catch (e) {
992        console.error(`Failed to queue ${lib.name}`, e);
993        failCount++;
994      }
995    }
996    fetchData();
997    if (failCount > 0) {
998      alert(`Queued ${successCount} jobs, but failed to queue ${failCount}.`);
999    } else {
1000      alert(`Successfully queued ${successCount} HSF3 conversion jobs.`);
1001    }
1002  };
1003  const saveTimeoutRef = reactExports.useRef(null);
1004  const pendingSaveRef = reactExports.useRef(null);
1005  const saveMetadataBulk = (ids, updates) => {
1006    setMetadata((prev) => {
1007      const nextMeta = { ...prev || {} };
1008      for (const id of ids) {
1009        const current = nextMeta[id] || { name: id, desc: "" };
1010        nextMeta[id] = { ...current, ...updates };
1011      }
1012      pendingSaveRef.current = nextMeta;
1013      return nextMeta;
1014    });
1015    if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
1016    saveTimeoutRef.current = setTimeout(() => {
1017      if (pendingSaveRef.current) {
1018        api.saveSystemSetting("SOUNDFONT_METADATA", pendingSaveRef.current).catch((e) => {
1019          console.error("Failed to save metadata bulk:", e);
1020        });
1021      }
1022    }, 500);
1023  };
1024  const saveMetadata = (id, updates) => {
1025    console.log(`[SoundfontManager] saveMetadata called for id: ${id}`, updates);
1026    setMetadata((prev) => {
1027      const current = prev[id] || { name: id, desc: "" };
1028      const updated = { ...current, ...updates };
1029      console.log(`[SoundfontManager] Previous state for ${id}:`, current);
1030      console.log(`[SoundfontManager] Updated state for ${id}:`, updated);
1031      const nextMeta = { ...prev || {}, [id]: updated };
1032      pendingSaveRef.current = nextMeta;
1033      return nextMeta;
1034    });
1035    console.log(`[SoundfontManager] Scheduling save for latest pending state.`);
1036    if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
1037    saveTimeoutRef.current = setTimeout(() => {
1038      if (pendingSaveRef.current) {
1039        console.log(`[SoundfontManager] Executing save timeout. Payload being sent:`, pendingSaveRef.current);
1040        api.saveSystemSetting("SOUNDFONT_METADATA", pendingSaveRef.current).then(() => {
1041          console.log(`[SoundfontManager] Save successful.`);
1042        }).catch((e) => {
1043          console.error("Failed to save metadata:", e);
1044          alert(`Failed to save metadata: ${(e == null ? void 0 : e.message) || e}`);
1045        });
1046      }
1047    }, 500);
1048  };
1049  const handleDeleteSoundfont = async (filename) => {
1050    if (!confirm(`Are you sure you want to delete ${filename}? This cannot be undone.`)) return;
1051    try {
1052      await api.deleteSoundfont(filename);
1053      fetchData();
1054    } catch (e) {
1055      alert("Failed to delete soundfont");
1056    }
1057  };
1058  const startDownload = async (libName) => {
1059    const isUrl = libName == null ? void 0 : libName.startsWith("http");
1060    const displayName = isUrl ? libName.split("/").pop() : libName;
1061    try {
1062      await api.startSoundfontJob(`Download ${displayName}`, libName, "DOWNLOAD");
1063      fetchData();
1064    } catch (e) {
1065      alert("Failed to start download");
1066    }
1067  };
1068  const deleteJob = async (id) => {
1069    try {
1070      await api.deleteSoundfontJob(id);
1071      fetchData();
1072    } catch (e) {
1073      alert("Failed to delete job");
1074    }
1075  };
1076  const groupedLibraries = reactExports.useMemo(() => {
1077    const groups = {};
1078    for (const lib of downloadedLibraries) {
1079      if (lib.name === "Root") continue;
1080      const baseName = getBaseName(lib.name);
1081      if (!groups[baseName]) {
1082        groups[baseName] = { baseName, formats: [], totalSize: 0 };
1083      }
1084      groups[baseName].formats.push(lib);
1085      groups[baseName].totalSize += lib.size || 0;
1086      if (!groups[baseName].createdAt || lib.createdAt && new Date(lib.createdAt) > new Date(groups[baseName].createdAt)) {
1087        groups[baseName].createdAt = lib.createdAt;
1088      }
1089      if (lib.name.toLowerCase().endsWith(".sf2")) groups[baseName].sf2 = lib;
1090      else if (lib.name.toLowerCase().endsWith(".sf3")) groups[baseName].sf3 = lib;
1091      else if (lib.name.endsWith("_JS")) groups[baseName].js = lib;
1092      else if (lib.name.endsWith("_HSF3Bank") || lib.name.endsWith("_SF3Bank")) groups[baseName].hsf3 = lib;
1093      else {
1094        const filesInFolder = soundfonts.filter((f) =>
1094 f.path.startsWith(lib.name + "/"));
1095        if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".hsf3"))) {
1096          groups[baseName].hsf3 = lib;
1097        } else if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".sf3"))) {
1098          groups[baseName].sf3 = lib;
1099        } else if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".sf2"))) {
1100          groups[baseName].sf2 = lib;
1101        } else {
1102          if (!groups[baseName].sf2) groups[baseName].sf2 = lib;
1103        }
1104      }
1105    }
1106    return Object.values(groups).sort((a, b) => a.baseName.localeCompare(b.baseName));
1107  }, [downloadedLibraries, soundfonts]);
1108  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 md:p-6 lg:p-8 w-full max-w-[1700px] mx-auto space-y-6", children: [
1109    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1110      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1111        /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-bold flex items-center gap-2", children: [
1112          /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { className: "w-6 h-6 text-indigo-500" }),
1113          "Soundfont Management"
1114        ] }),
1115        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500", children: "Manage SF2 files and convert JS soundfonts to Opus." })
1116      ] }),
1117      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1118        "button",
1119        {
1120          onClick: () => fetchData(true),
1121          className: "flex items-center gap-2 px-3 py-2 bg-white dark:bg-zinc-800 hover:bg-zinc-100 dark:hover:bg-zinc-700 border border-zinc-200 dark:border-zinc-700 rounded-lg transition-all text-xs font-bold text-zinc-600 dark:text-zinc-300 shadow-sm",
1122          title: "Scan disk for new soundfonts",
1123          children: [
1124            /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: `w-4 h-4 ${loading ? "animate-spin text-indigo-500" : "text-zinc-400"}` }),
1125            "Scan Disk"
1126          ]
1127        }
1128      )
1129    ] }),
1130    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-50 dark:bg-indigo-900/10 border border-indigo-100 dark:border-indigo-900/30 rounded-xl overflow-hidden mb-6", children: [
1131      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1132        "div",
1133        {
1134          onClick: () => setShowEngine(!showEngine),
1135          className: `p-4 ${showEngine ? "border-b border-indigo-100 dark:border-indigo-900/30" : ""} flex items-center justify-between cursor-pointer select-none`,
1136          children: [
1137            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1138              showEngine ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-indigo-500 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-indigo-500 shrink-0" }),
1139              /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm uppercase tracking-wider text-indigo-600 dark:text-indigo-400 flex items-center gap-2", children: [
1140                /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "w-4 h-4" }),
1141                "Soundfont Engine"
1142              ] })
1143            ] }),
1144            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 font-bold bg-white dark:bg-zinc-800 px-2 py-1 rounded-md border border-zinc-200 dark:border-zinc-700", children: "Global Defaults & Engine Configs" })
1145          ]
1146        }
1147      ),
1148      showEngine && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: [
1149        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1150          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold uppercase text-zinc-500", children: "Pre-rendered Default (Worker)" }),
1151          /* @__PURE__ */ jsxRuntimeExports.jsx(
1152            "select",
1153            {
1154              value: preRenderedSf || "",
1155              onChange: (e) => saveGlobalSetting("PRE_RENDERED_SOUNDFONT", e.target.value),
1156              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
1157              children: soundfonts.filter((f) => {
1158                var _a2, _b, _c;
1159                return ((_a2 = f.path) == null ? void 0 : _a2.endsWith(".sf2")) || ((_b = f.path) == null ? void 0 : _b.endsWith(".sf3")) || ((_c = f.path) == null ? void 0 : _c.endsWith(".hsf3"));
1160              }).sort((a, b) => a.path.localeCompare(b.path, void 0, { sensitivity: "base", numeric: true })).map((sf) => {
1161                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1162                  "option",
1163                  {
1164                    value: sf.path,
1165                    className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
1166                    children: [
1167                      sf.path,
1168                      " (",
1169                      formatFileSize(sf.size),
1170                      ")"
1171                    ]
1172                  },
1173                  sf.path
1174                );
1175              })
1176            }
1177          ),
1178          /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[9px] text-zinc-500 italic mt-1", children: [
1179            "MIDI worker uses this ",
1180            /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: ".sf2" }),
1181            " to render stems."
1182          ] })
1183        ] }),
1184        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1185          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold uppercase text-zinc-500", children: "Default Live (Fallback)" }),
1186          /* @__PURE__ */ jsxRuntimeExports.jsxs(
1187            "select",
1188            {
1189              value: defaultLiveSf || "",
1190              onChange: (e) => saveGlobalSetting("DEFAULT_LIVE_SOUNDFONT", e.target.value),
1191              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
1192              children: [
1193                /* @__P
1193URE__ */ jsxRuntimeExports.jsx("option", { value: "", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: "-- Select Live Soundfont --" }),
1194                downloadedLibraries.filter((lib) => lib.name !== "Root").sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base", numeric: true })).map((lib) => {
1195                  const typeSuffix = getSoundfontFormatType(lib);
1196                  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1197                    "option",
1198                    {
1199                      value: lib.name,
1200                      className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
1201                      children: [
1202                        lib.name,
1203                        " (",
1204                        typeSuffix,
1205                        ") (",
1206                        formatFileSize(lib.size),
1207                        ")"
1208                      ]
1209                    },
1210                    lib.name
1211                  );
1212                })
1213              ]
1214            }
1215          ),
1216          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[9px] text-zinc-500 italic mt-1", children: "Interactive keyboard source when live." })
1217        ] }),
1218        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1219          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold uppercase text-zinc-500", children: "Default Converter SF2" }),
1220          /* @__PURE__ */ jsxRuntimeExports.jsx(
1221            "select",
1222            {
1223              value: defaultConverterSf || "",
1224              onChange: (e) => saveGlobalSetting("DEFAULT_CONVERTER_SOUNDFONT", e.target.value),
1225              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
1226              children: soundfonts.filter((f) => {
1227                var _a2, _b, _c;
1228                return ((_a2 = f.path) == null ? void 0 : _a2.endsWith(".sf2")) || ((_b = f.path) == null ? void 0 : _b.endsWith(".sf3")) || ((_c = f.path) == null ? void 0 : _c.endsWith(".hsf3"));
1229              }).sort((a, b) => a.path.localeCompare(b.path, void 0, { sensitivity: "base", numeric: true })).map((sf) => {
1230                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1231                  "option",
1232                  {
1233                    value: sf.path,
1234                    className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
1235                    children: [
1236                      sf.path,
1237                      " (",
1238                      formatFileSize(sf.size),
1239                      ")"
1240                    ]
1241                  },
1242                  sf.path
1243                );
1244              })
1245            }
1246          ),
1247          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[9px] text-zinc-500 italic mt-1", children: "Default selected on MIDI Converter page." })
1248        ] }),
1249        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1250          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold uppercase text-zinc-500", children: "Default Keyboard SF" }),
1251          /* @__PURE__ */ jsxRuntimeExports.jsx(
1252            "select",
1253            {
1254              value: defaultKeyboardSf || "MusyngKite",
1255              onChange: (e) => saveGlobalSetting("DEFAULT_KEYBOARD_SOUNDFONT", e.target.value),
1256              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
1257              children: downloadedLibraries.filter((lib) => lib.name !== "Root").sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base", numeric: true })).map((lib) => {
1258                const typeSuffix = getSoundfontFormatType(lib);
1259                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1260                  "option",
1261                  {
1262                    value: lib.name,
1263                    className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
1264                    children: [
1265                      lib.name,
1266                      " (",
1267                      typeSuffix,
1268                      ") (",
1269                      formatFileSize(lib.size),
1270                      ")"
1271                    ]
1272                  },
1273                  lib.name
1274                );
1275              })
1276            }
1277          ),
1278          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[9px] text-zinc-500 italic mt-1", children: "Initial soundfont for the Keyboard emulator." })
1279        ] }),
1280        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1281          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold uppercase text-zinc-500", children: "Default WASM SF2" }),
1282          /* @__PURE__ */ jsxRuntimeExports.jsx(
1283            "select",
1284            {
1285              value: defaultWasmSf || "SoundBlasterOld.sf2",
1286              onChange: (e) => saveGlobalSetting("DEFAULT_WASM_SOUNDFONT", e.target.value),
1287              className: "w-full text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-indigo-500 mb-1",
1288              children: soundfonts.filter((f) => {
1289                var _a2, _b, _c;
1290                return ((_a2 = f.path) == null ? void 0 : _a2.endsWith(".sf2")) || ((_b = f.path) == null ? void 0 : _b.endsWith(".sf3")) || ((_c = f.path) == null ? void 0 : _c.endsWith(".hsf3"));
1291              }).sort((a, b) => a.path.localeCompare(b.path, void 0, { sensitivity: "base", numeric: true })).map((sf) => {
1292                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1293                  "option",
1294                  {
1295                    value: sf.path,
1296                    className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900",
1297                    children: [
1298                      sf.path,
1299                      " (",
1300                      formatFileSize(sf.size),
1301                      ")"
1302                    ]
1303                  },
1304                  sf.path
1305                );
1306              })
1307            }
1308          ),
1309          /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[9px] text-zinc-500 italic mt-1", children: [
1310            "Default ",
1311            /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: ".sf2" }),
1312            " loaded in WASM mode."
1313          ] })
1314        ] })
1315      ] }) })
1316    ] }),
1317    /* @__PURE__ */ jsxRuntimeExports.jsx(
1318      SoundfontQualityLab,
1319      {
1320        sf2Files: soundfonts.filter((f) => {
1321          var _a2;
1322          return (_a2 = f.path) == null ? void 0 : _a2.endsWith(".sf2");
1323        }).sort((a, b) => a.path.localeCompare(b.path, void 0, { sensitivity: "base", numeric: true })),
1324        onRefreshProfiles: fetchData
1325      }
1326    ),
1327    renderEngineOrderEditor(),
1328    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [
1329      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden", children: [
1330        /* @__PURE__ */ jsxRuntimeExports.jsx(
1331          "div",
1332          {
1333            onClick: () => setShowExternal(!showExternal),
1334            className: `p-4 ${showExternal ? "border-b border-zinc-200 dark:border-zinc-800" : ""} bg-zinc-50/50 dark:bg-zinc-800/50 flex items-center justify-between cursor-pointer select-none`,
1335            children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1336              showExternal ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-zinc-400 shrink-0" }),
1337              /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-sm uppercase tracking-wider text-zinc-500", children: "External Soundfont Sources" })
1338            ] })
1339          }
1340        ),
1341        showExternal && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 space-y-4", children: [
1342          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: presets.map((preset) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1343            "button",
1344            {
1345              onClick: () => startDownload(preset.url),
1346              className: "p-3 bg-zinc-100 dark:bg-zinc-800 hover:bg-indigo-500 hover:text-white rounded-lg text-left transition-all group",
1347              children: [
1348                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold uppercase tracking-tight", children: preset.name }),
1349                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] opacity-60 truncate", children: preset.url })
1350              ]
1351            },
1352            preset.name
1353          )) }),
1354          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1355            /* @__PURE__ */ jsxRuntimeExports.jsx(
1356              "input",
1357              {
1358                type: "text",
1359                value: customUrl,
1360                onChange: (e) => setCustomUrl(e.target.value),
1361                placeholder: "https://example.com/soundfont.sf2",
1362                className: "flex-1 bg-zinc-100 dark:bg-zinc-800 border-none rounded-lg px-3 py-2 text-xs focus:ring-2 focus:ring-indigo-500 outline-none"
1363              }
1364            ),
1365            /* @__PURE__ */ jsxRuntimeExports.jsxs(
1366              "button",
1367              {
1368                onClick: () => {
1369                  if (customUrl) {
1370                    startDownload(customUrl);
1371                    setCustomUrl("");
1372                  }
1373                },
1374                className: "px-4 py-2 bg-zinc-800 dark:bg-zinc-700 hover:bg-indigo-500 text-white text-xs font-bold rounded-lg transition-colors flex items-center gap-2",
1375                children: [
1376                  /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }),
1377                  "Download URL"
1378                ]
1379              }
1380            )
1381          ] }),
1382          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-400 italic", children: "* Supports direct links to .sf2 or .js files. GitHub links are automatically converted to raw." })
1383        ] })
1384      ] }),
1385      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden", children: [
1386        /* @__PURE__ */ jsxRuntimeExports.jsxs(
1387          "div",
1388          {
1389            onClick: () => setShowJobs(!showJobs),
1390            className: `p-4 ${showJobs ? "border-b border-zinc-200 dark:border-zinc-800" : ""} bg-zinc-50/50 dark:bg-zinc-800/50 flex items-center justify-between cursor-pointer select-none`,
1391            children: [
1392              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1393                showJobs ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-zinc-400 shrink-0" }),
1394                /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-sm uppercase tracking-wider text-zinc-500", children: "Active Jobs" })
1395              ] }),
1396              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", onClick: (e) => e.stopPropagation(), children: [
1397                jobs.some((j) => j.status === "error") && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1398                  "button",
1399                  {
1400                    onClick: () => {
1401                      if (confirm("Dismiss all errored jobs? You will lose their error details.")) {
1402                        const errorJobs = jobs.filter((j) => j.status === "error");
1403                        Promise.all(errorJobs.map((j) => api.deleteSoundfontJob(j.id))).then(() => fetchData());
1404                      }
1405                    },
1406                    className: "text-[10px] font-bold text-red-500 hover:text-red-600 uppercase flex items-center gap-1 transition-colors",
1407                    children: [
1408                      /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 12 }),
1409                      "Dismiss Errors (",
1410                      jobs.filter((j) => j.status === "error").length,
1411                      ")"
1412                    ]
1413                  }
1414                ),
1415                jobs.some((j) => j.status === "completed") && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1416                  "button",
1417                  {
1418                    onClick: () => clearFinishedJobs(),
1419                    className: "text-[10px] font-bold text-emerald-500 hover:text-emerald-600 uppercase flex items-center gap-1 transition-colors",
1420                    children: [
1421                      /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 12 }),
1422                      "Clear Finished"
1423                    ]
1424                  }
1425                )
1426              ] })
1427            ]
1428          }
1429        ),
1430        showJobs && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "divide-y divide-zinc-200 dark:divide-zinc-800 max-h-[400px] overflow-y-auto", children: jobs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-8 text-center text-zinc-500 text-sm", children: "No active or recent jobs." }) : jobs.map((job) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 space-y-2", children: [
1431          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1432            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1433              job.status === "pending" && /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "w-3 h-3 text-zinc-500" }),
1434              job.status === "processing" && /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3 h-3 text-indigo-500 animate-spin" }),
1435              job.status === "completed" && /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "w-3 h-3 text-green-500" }),
1436              job.status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "w-3 h-3 text-red-500" }),
1437              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium", children: job.name })
1438            ] }),
1439            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => deleteJob(job.id), className: "text-zinc-400 hover:text-red-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "w-4 h-4" }) })
1440          ] }),
1441          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-100 dark:bg-zinc-800 h-1 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `h-full transition-all duration-500 ${job.status === "error" ? "bg-red-500" : "bg-indigo-500"}`, style: { width: `${job.progress}%` } }) }),
1442          job.details && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 dark:text-zinc-400 italic", children: job.details })
1443        ] }, job.id)) })
1444      ] }),
1445      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl lg:col-span-2", children: [
1446        /* @__PURE__ */ jsxRuntimeExports.jsxs(
1447          "div",
1448          {
1449            onClick: () => setShowAssets(!showAssets),
1450            className: `p-4 ${showAssets ? "border-b border-zinc-200 dark:border-zinc-800" : ""} bg-zinc-50/50 dark:bg-zinc-800/50 flex items-center justify-between flex-wrap gap-2 cursor-pointer select-none`,
1451            children: [
1452              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1453                showAssets ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-zinc-400 shrink-0" }),
1454                /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-sm uppercase tracking-wider text-zinc-500", children: "Available Soundfont Assets" })
1455              ] }),
1456              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap", onClick: (e) => e.stopPropagation(), children: [
1457                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1458                  "button",
1459                  {
1460                    onClick: () => {
1461                      if (!selectedSfForKeyboard && downloadedLibraries.length > 0) {
1462                        setSelectedSfForKeyboard(downloadedLibraries[0].name);
1463                      }
1464                      setShowKeyboard(!showKeyboard);
1465                    },
1466                    className: `px-2.5 py-1 text-[10px] font-bold rounded uppercase flex items-center gap-1.5 transition-all shadow-sm ${showKeyboard ? "bg-indigo-600 text-white ring-2 ring-indigo-400" : "bg-zinc-200 dark:bg-zinc-700 hover:bg-indigo-500 hover:text-white text-zinc-700 dark:text-zinc-200"}`,
1467                    title: "Toggle full 88-key interactive keyboard at bottom of screen",
1468                    children: [
1469                      /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { className: "w-3.5 h-3.5" }),
1470                      showKeyboard ? "Keyboard Active" : "Toggle Keyboard"
1471                    ]
1472                  }
1473                ),
1474                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1475                  "button",
1476                  {
1477                    onClick: startBulkSf2Conversion,
1478                    className: "px-2 py-1 bg-indigo-500 hover:bg-indigo-600 text-white text-[10px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1479                    title: "Sample all .sf2 files to JS",
1480                    children: [
1481                      /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3 h-3" }),
1482                      "Resample All .sf2"
1483                    ]
1484                  }
1485                ),
1486                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1487                  "button",
1488                  {
1489                    onClick: startBulkSf3Conversion,
1490                    className: "px-2 py-1 bg-teal-500 hover:bg-teal-600 text-white text-[10px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1491                    title: "Convert all .sf2 files to SF3 (OGG compressed)",
1492                    children: [
1493                      /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3 h-3" }),
1494                      "Convert All → SF3"
1495                    ]
1496                  }
1497                ),
1498                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1499                  "button",
1500                  {
1501                    onClick: startBulkHsf3BankConversion,
1502                    className: "px-2 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-[10px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1503                    title: "Convert all .sf2 files to HSF3 Bank (Split instrument files + DRM obfuscated)",
1504                    children: [
1505                      /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3 h-3" }),
1506                      "Convert All → HSF3"
1507                    ]
1508                  }
1509                ),
1510                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] bg-zinc-200 dark:bg-zinc-700 px-2 py-0.5 rounded-full font-bold", children: [
1511                  soundfonts.length,
1512                  " Files"
1513                ] })
1514              ] })
1515            ]
1516          }
1517        ),
1518        showAssets && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full transition-all", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse min-w-[1250px]", children: [
1519          /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 shadow-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-100 dark:bg-zinc-800 text-[10px] uppercase tracking-wider text-zinc-500 font-bold border-b border-zinc-200 dark:border-zinc-800", children: [
1520            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-8" }),
1521            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 min-w-[160px]", children: "Soundfont / Format" }),
1522            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 min-w-[150px]", children: "Friendly Name" }),
1523            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 min-w-[150px]", children: "Description" }),
1524            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-28 text-center", children: "Type & Size" }),
1525            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-36 text-center", children: "Target Gain" }),
1526            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-32 text-center", title: "Smart Auto-Routing Threshold", children: "Auto-Route" }),
1527            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-24 text-center", children: "Playback" }),
1528            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-24 text-center", children: "Converter" }),
1529            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-24 text-center", children: "Keyboard" }),
1530            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-28 text-center", children: "WASM Engine" }),
1531            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 w-24 text-center", children: "Tier Access" }),
1532            /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "sticky top-0 z-20 bg-zinc-100 dark:bg-zinc-800 px-3 py-3 min-w-[180px] text-right", children: "Actions" })
1533          ] }) }),
1534          /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800", children: groupedLibraries.map((group) => {
1535            var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
1536            const baseName = group.baseName;
1537            const isExpanded = expandedLibraries.has(baseName);
1538            const bestSfForKeyboard = ((_a2 = group.sf2) == null ? void 0 : _a2.name) || ((_b = group.sf3) == null ? void 0 : _b.name) || ((_c = group.js) == null ? void 0 : _c.name) || baseName;
1539            const isGroupSelectedForKb = selectedSfForKeyboard === bestSfForKeyboard;
1540            const currentGain = ((_d = profiles[baseName]) == null ? void 0 : _d.gain) ?? 1.8;
1541            const isLeveling = !!autoLevelingLibs[baseName];
1542            group.formats.length > 0 ? group.formats.some((f) => {
1543              var _a3;
1544              return ((_a3 = metadata[f.name]) == null ? void 0 : _a3.visible) !== false;
1545            }) : ((_e = metadata[baseName]) == null ? void 0 : _e.visible) !== false;
1546            group.formats.length > 0 ? group.formats.some((f) => {
1547              var _a3;
1548              return ((_a3 = metadata[f.name]) == null ? void 0 : _a3.visibleConverter) !== false;
1549            }) : ((_f = metadata[baseName]) == null ? void 0 : _f.visibleConverter) !== false;
1550            group.formats.length > 0 ? group.formats.some((f) => {
1551              var _a3;
1552              return ((_a3 = metadata[f.name]) == null ? void 0 : _a3.visibleKeyboard) !== false;
1553            }) : ((_g = metadata[baseName]) == null ? void 0 : _g.visibleKeyboard) !== false;
1554            const isFolderPro = group.formats.length > 0 ? group.formats.some((f) => {
1555              var _a3;
1556              return ((_a3 = metadata[f.name]) == null ? void 0 : _a3.access) === "pro";
1557            }) : ((_h = metadata[baseName]) == null ? void 0 : _h.access) === "pro";
1558            group.formats.length > 0 && group.formats.every((f) => f.name.endsWith("_JS"));
1559            const sf3Size = ((_i = group.sf3) == null ? void 0 : _i.size) || 0;
1560            const hsf3TotalSize = ((_j = group.hsf3) == null ? void 0 : _j.size) || 0;
1561            const calcThreshold = sf3Size > 0 && hsf3TotalSize > 0 ? Math.floor(sf3Size / (hsf3TotalSize / 128)) : 15;
1562            const isAutoRoute = ((_k = metadata[baseName]) == null ? void 0 : _k.autoRoute) === true;
1563            const overrideThreshold = (_l = metadata[baseName]) == null ? void 0 : _l.autoRouteThresholdOverride;
1564            return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
1565              /* @__PURE__ */ jsxRuntimeExports.jsxs(
1566                "tr",
1567                {
1568                  onClick: () => toggleLibrary(baseName),
1569                  className: `bg-zinc-100/60 dark:bg-zinc-800/60 hover:bg-zinc-200/60 dark:hover:bg-zinc-750 transition-colors group cursor-pointer border-b border-zinc-200/80 dark:border-zinc-800 ${isGroupSelectedForKb ? "border-l-4 border-l-indigo-500" : ""}`,
1570                  children: [
1571                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => {
1572                      e.stopPropagation();
1573                      toggleLibrary(baseName);
1574                    }, className: "text-zinc-400 hover:text-indigo-500 transition-colors", children: isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-indigo-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-zinc-400" }) }) }),
1575                    /* @__P
1575URE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
1576                      /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "w-4 h-4 text-indigo-500 shrink-0" }),
1577                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs font-bold text-zinc-900 dark:text-zinc-100 truncate max-w-[180px]", title: baseName, children: baseName }),
1578                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] bg-indigo-500/10 text-indigo-500 border border-indigo-500/20 font-bold px-1.5 py-0.5 rounded-full shrink-0", children: [
1579                        group.formats.length,
1580                        " ",
1581                        group.formats.length === 1 ? "Format" : "Formats"
1582                      ] })
1583                    ] }) }),
1584                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1585                      "input",
1586                      {
1587                        type: "text",
1588                        value: ((_m = metadata[baseName]) == null ? void 0 : _m.name) || baseName.replace(/_/g, " "),
1589                        onChange: (e) => saveMetadata(baseName, { name: e.target.value }),
1590                        placeholder: "Group Friendly Name",
1591                        className: "w-full bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded px-2 py-1 text-xs focus:ring-1 focus:ring-indigo-500 outline-none font-semibold text-zinc-800 dark:text-zinc-200"
1592                      }
1593                    ) }),
1594                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1595                      "input",
1596                      {
1597                        type: "text",
1598                        value: ((_n = metadata[baseName]) == null ? void 0 : _n.desc) || "",
1599                        onChange: (e) => saveMetadata(baseName, { desc: e.target.value }),
1600                        placeholder: "Group Description...",
1601                        className: "w-full bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded px-2 py-1 text-xs focus:ring-1 focus:ring-indigo-500 outline-none text-zinc-500 dark:text-zinc-400"
1602                      }
1603                    ) }),
1604                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center whitespace-nowrap", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-bold text-zinc-700 dark:text-zinc-300", children: [
1605                      "Total: ",
1606                      formatFileSize(group.totalSize)
1607                    ] }) }),
1608                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-1.5", children: [
1609                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1610                        "input",
1611                        {
1612                          type: "number",
1613                          step: 0.1,
1614                          min: 0.1,
1615                          max: 10,
1616                          defaultValue: currentGain,
1617                          onBlur: (e) => {
1618                            const val = parseFloat(e.target.value);
1619                            if (!isNaN(val) && val !== currentGain) {
1620                              handleSaveGain(baseName, val);
1621                            }
1622                          },
1623                          className: "w-14 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded px-1.5 py-0.5 text-xs text-right font-mono font-bold outline-none focus:ring-1 focus:ring-indigo-500 text-zinc-800 dark:text-zinc-200 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
1624                          title: "Target Gain Multiplier (Shared by all formats of this soundfont)"
1625                        },
1626                        `gain-${baseName}-${currentGain}`
1627                      ),
1628                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-bold", children: "x" }),
1629                      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1630                        "button",
1631                        {
1632                          onClick: () => handleAutoLevelForRow(baseName),
1633                          disabled: isLeveling,
1634                          className: "p-1.5 rounded bg-amber-50 text-amber-600 hover:bg-amber-100 dark:bg-amber-950/40 dark:text-amber-400 dark:hover:bg-amber-950/60 border border-amber-200 dark:border-amber-900/40 font-bold transition-all disabled:opacity-50 flex items-center gap-1 text-[10px]",
1635                          title: "Auto Level Volume (-18 dB Target baseline)",
1636                          children: [
1637                            isLeveling ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "w-3 h-3" }),
1638                            /* @__P
1638URE__ */ jsxRuntimeExports.jsx("span", { children: "Auto" })
1639                          ]
1640                        }
1641                      )
1642                    ] }) }),
1643                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center gap-1", children: [
1644                      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1645                        "button",
1646                        {
1647                          onClick: () => saveMetadata(baseName, { autoRoute: !isAutoRoute }),
1648                          className: `px-1.5 py-0.5 rounded transition-colors flex items-center gap-1 text-[9px] font-bold uppercase tracking-wider border ${isAutoRoute ? "text-amber-500 bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800/50" : "text-zinc-400 bg-zinc-100 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
1649                          title: isAutoRoute ? "Smart Auto-Routing Enabled" : "Enable Smart Auto-Routing",
1650                          children: [
1651                            /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "w-3 h-3" }),
1652                            isAutoRoute ? "Auto" : "Manual"
1653                          ]
1654                        }
1655                      ),
1656                      isAutoRoute && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1", title: `Calculated Optimal Break-Even Threshold: ${calcThreshold} instruments
1657(Leave empty to use auto)`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1658                        "input",
1659                        {
1660                          type: "number",
1661                          placeholder: calcThreshold.toString(),
1662                          value: overrideThreshold || "",
1663                          onChange: (e) => saveMetadata(baseName, { autoRouteThresholdOverride: e.target.value ? parseInt(e.target.value) : null }),
1664                          className: "w-10 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded px-1 py-0.5 text-[9px] text-center font-mono font-bold outline-none focus:ring-1 focus:ring-amber-500 text-amber-600 dark:text-amber-500 placeholder:text-zinc-300 dark:placeholder:text-zinc-600 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
1665                        }
1666                      ) })
1667                    ] }) }),
1668                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1669                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1670                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1671                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1672                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1673                      "select",
1674                      {
1675                        value: isFolderPro ? "pro" : "free",
1676                        onChange: (e) => saveMetadataBulk([...group.formats.map((f) => f.name), baseName], { access: e.target.value }),
1677                        className: `text-[10px] font-bold px-2 py-1 rounded outline-none border transition-colors ${isFolderPro ? "bg-purple-50 text-purple-600 border-purple-200 dark:bg-purple-900/20 dark:text-purple-400 dark:border-purple-800/50" : "bg-emerald-50 text-emerald-600 border-emerald-200 dark:bg-emerald-900/20 dark:text-emerald-400 dark:border-emerald-800/50"}`,
1678                        children: [
1679                          /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "free", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850", children: "FREE" }),
1680                          /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "pro", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850", children: "PRO" })
1681                        ]
1682                      }
1683                    ) }),
1684                    /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-3 text-right", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-end gap-1.5 min-w-[170px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1685                      "button",
1686                      {
1687                        onClick: () => confirm(`Permanently delete all formats of ${baseName}?`) && group.formats.forEach((sf) => handleDeleteSoundfont(sf.name)),
1688                        className: "p-1.5 text-red-500 hover:bg-red-50 dark:hover:bg-red-950/40 rounded transition-colors border border-red-200 dark:border-red-900/50",
1689                        title: "Delete All Formats of this Soundfont",
1690                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "w-3.5 h-3.5" })
1691                      }
1692                    ) }) })
1693                  ]
1694                }
1695              ),
1696              isExpanded && group.formats.map((lib) => {
1697                var _a3, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j2, _k2, _l2;
1698                const formatName = lib.name;
1699                let isJS = formatName.endsWith("_JS");
1700                let isSf2 = formatName.toLowerCase().endsWith(".sf2");
1701                let isSf3 = formatName.toLowerCase().endsWith(".sf3");
1702                let isHsf3 = formatName.endsWith("_HSF3Bank") || formatName.endsWith("_SF3Bank") || lib.isHsf3Bank;
1703                if (!isJS && !isSf2 && !isSf3 && !isHsf3) {
1704                  const filesInFolder = soundfonts.filter((f) => f.path.startsWith(formatName + "/"));
1705                  if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".hsf3"))) {
1706                    isHsf3 = true;
1707                  } else if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".sf3"))) {
1708                    isSf3 = true;
1709                  } else if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".sf2"))) {
1710                    isSf2 = true;
1711                  } else if (filesInFolder.some((f) => f.path.toLowerCase().endsWith(".js"))) {
1712                    isJS = true;
1713                  } else {
1714                    isSf2 = true;
1715                  }
1716                }
1717                const isSfBinary = isSf2 || isSf3;
1718                const isSelectedForKb = selectedSfForKeyboard === formatName;
1719                return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
1720                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
1721                    "tr",
1722                    {
1723                      onClick: () => setSelectedSfForKeyboard(formatName),
1724                      className: `hover:bg-indigo-50/50 dark:hover:bg-indigo-950/20 transition-colors group cursor-pointer bg-white dark:bg-zinc-900/90 border-b border-zinc-100 dark:border-zinc-800/50 ${isSelectedForKb ? "bg-indigo-50 dark:bg-indigo-950/40 border-l-4 border-l-indigo-500 font-medium" : ""}`,
1725                      children: [
1726                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-right", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-indigo-400 font-bold text-xs select-none", children: "↳" }) }),
1727                        /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "px-3 py-2 pl-4", children: [
1728                          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
1729                            (isJS || isHsf3) && lib.children && lib.children.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => toggleFolder(formatName, e), className: "p-0.5 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded transition-colors text-zinc-500", children: expandedFolders.has(formatName) ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-3.5 h-3.5" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 shrink-0" }),
1730                            isJS || isHsf3 ? /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: `w-3.5 h-3.5 shrink-0 ${isHsf3 ? "text-purple-400" : "text-indigo-400"}` }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { className: "w-3.5 h-3.5 text-zinc-400 shrink-0" }),
1731                            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[11px] text-zinc-700 dark:text-zinc-300 font-medium truncate max-w-[170px]", title: formatName, children: formatName }),
1732                            isJS && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9.5px] text-zinc-400 dark:text-zinc-500 font-normal italic shrink-0 hidden sm:inline-block", title: "Used only by the Persistent Player as a fallback for older devices or mobile.", children: "(Web Player Fallback)" }),
1733                            isHsf3 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9.5px] text-purple-400/80 font-normal italic shrink-0 hidden sm:inline-block", title: "Primary high-performance WASM synthesizer format.", children: "(Primary Synth Engine)" }),
1734                            isSelectedForKb && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[8px] bg-indigo-600 text-white font-bold px-1.5 py-0.5 rounded shadow-sm shrink-0", children: "Active Target" })
1735                          ] }),
1736                          lib.createdAt && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[9px] text-zinc-400 dark:text-zinc-500 font-mono mt-0.5 flex items-center gap-1 select-none", children: [
1737                            /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "w-2.5 h-2.5" }),
1738                            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: new Date(lib.createdAt).toLocaleString(void 0, {
1739                              year: "numeric",
1740                              month: "short",
1741                              day: "numeric",
1742                              hour: "2-digit",
1743                              minute: "2-digit"
1744                            }) })
1745                          ] })
1746                        ] }),
1747                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1748                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1749                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center whitespace-nowrap", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
1750                          isSf2 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold text-zinc-600 dark:text-zinc-300 bg-zinc-200 dark:bg-zinc-700 px-1.5 py-0.5 rounded", children: "SF2" }),
1751                          isSf3 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold text-cyan-400 bg-cyan-500/10 px-1.5 py-0.5 rounded border border-cyan-500/20", children: "SF3" }),
1752                          isJS && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold text-green-500 bg-green-500/10 px-1.5 py-0.5 rounded border border-green-500/20", children: "JS" }),
1753                          isHsf3 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold text-purple-500 bg-purple-500/10 px-1.5 py-0.5 rounded border border-purple-500/20", children: "HSF3" }),
1754                          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-zinc-500", children: formatFileSize(lib.size) })
1755                        ] }) }),
1756                        /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "px-3 py-2 text-center text-[10px] font-mono font-semibold text-zinc-400", children: [
1757                          currentGain,
1758                          "x"
1759                        ] }),
1760                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1761                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1762                          "button",
1763                          {
1764                            onClick: () => {
1765                              var _a4;
1766                              return saveMetadata(formatName, { visible: ((_a4 = metadata[formatName]) == null ? void 0 : _a4.visible) === false ? true : false });
1767                            },
1768                            className: `p-1.5 rounded transition-colors ${((_a3 = metadata[formatName]) == null ? void 0 : _a3.visible) !== false ? "text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20" : "text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800"}`,
1769                            title: ((_b2 = metadata[formatName]) == null ? void 0 : _b2.visible) !== false ? "Visible on Main Site" : "Hidden from Main Site",
1770                            children: ((_c2 = metadata[formatName]) == null ? void 0 : _c2.visible) !== false ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "w-4 h-4" })
1771                          }
1772                        ) }),
1773                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center", onClick: (e) => e.stopPropagation(), children: !isJS ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1774                          "button",
1775                          {
1776                            onClick: () => {
1777                              var _a4;
1778                              return saveMetadata(formatName, { visibleConverter: ((_a4 = metadata[formatName]) == null ? void 0 : _a4.visibleConverter) === false ? true : false });
1779                            },
1780                            className: `p-1.5 rounded transition-colors ${((_d2 = metadata[formatName]) == null ? void 0 : _d2.visibleConverter) !== false ? "text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20" : "text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800"}`,
1781                            title: ((_e2 = metadata[formatName]) == null ? void 0 : _e2.visibleConverter) !== false ? "Visible in Converter page" : "Hidden from Converter page",
1782                            children: ((_f2 = metadata[formatName]) == null ? void 0 : _f2.visibleConverter) !== false ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "w-4 h-4" })
1783                          }
1784                        ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-medium italic select-none", title: "JS is not a valid output format for the server-side MIDI Converter.", children: "N/A" }) }),
1785                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1786                          "button",
1787                          {
1788                            onClick: () => {
1789                              var _a4;
1790                              return saveMetadata(formatName, { visibleKeyboard: ((_a4 = metadata[formatName]) == null ? void 0 : _a4.visibleKeyboard) === false ? true : false });
1791                            },
1792                            className: `p-1.5 rounded transition-colors ${((_g2 = metadata[formatName]) == null ? void 0 : _g2.visibleKeyboard) !== false ? "text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20" : "text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800"}`,
1793                            title: ((_h2 = metadata[formatName]) == null ? void 0 : _h2.visibleKeyboard) !== false ? "Visible in Keyboard Emulator dropdown" : "Hidden from Keyboard Emulator",
1794                            children: ((_i2 = metadata[formatName]) == null ? void 0 : _i2.visibleKeyboard) !== false ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "w-4 h-4" })
1795                          }
1796                        ) }),
1797                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center", onClick: (e) => e.stopPropagation(), children: isSfBinary ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
1798                          "button",
1799                          {
1800                            onClick: () => {
1801                              var _a4;
1802                              return saveMetadata(formatName, { enabledWasm: ((_a4 = metadata[formatName]) == null ? void 0 : _a4.enabledWasm) === false ? true : false });
1803                            },
1804                            className: `px-2 py-0.5 rounded text-[10px] font-bold flex items-center justify-center gap-1 mx-auto transition-all ${((_j2 = metadata[formatName]) == null ? void 0 : _j2.enabledWasm) !== false ? "text-cyan-600 bg-cyan-50 dark:bg-cyan-950/40 border border-cyan-200 dark:border-cyan-800" : "text-zinc-400 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 opacity-60"}`,
1805                            title: ((_k2 = metadata[formatName]) == null ? void 0 : _k2.enabledWasm) !== false ? "Enabled for WebAssembly (WASM) engine" : "Disabled for WASM engine",
1806                            children: [
1807                              /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "w-3 h-3 text-cyan-500" }),
1808                              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: ((_l2 = metadata[formatName]) == null ? void 0 : _l2.enabledWasm) !== false ? "Enabled" : "Disabled" })
1809                            ]
1810                          }
1811                        ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 italic", title: "JS Soundfonts do not use WASM", children: "—" }) }),
1812                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-center text-zinc-300 dark:text-zinc-700 select-none", children: "-" }),
1813                        /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-right", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-1.5 flex-wrap min-w-[170px]", children: [
1814                          /* @__P
1814URE__ */ jsxRuntimeExports.jsx(
1815                            "button",
1816                            {
1817                              onClick: () => {
1818                                setSelectedSfForKeyboard(formatName);
1819                                if (!showKeyboard) setShowKeyboard(true);
1820                              },
1821                              className: `p-1.5 rounded transition-colors border ${isSelectedForKb ? "text-indigo-600 bg-indigo-100 dark:bg-indigo-950/80 border-indigo-500" : "text-zinc-400 hover:text-indigo-500 hover:bg-zinc-100 dark:hover:bg-zinc-800 border-zinc-200 dark:border-zinc-700"}`,
1822                              title: "Play specifically this format on Keyboard",
1823                              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { className: "w-3.5 h-3.5" })
1824                            }
1825                          ),
1826                          isJS && /* @__PURE__ */ jsxRuntimeExports.jsx(
1827                            "button",
1828                            {
1829                              onClick: () => confirm(`Rebuild JS Library for ${formatName}?`) && handleDeleteSoundfont(formatName),
1830                              className: "p-1.5 text-amber-500 hover:bg-amber-50 dark:hover:bg-amber-900/20 rounded transition-colors border border-amber-200 dark:border-amber-800/50",
1831                              title: "Rebuild JS Library",
1832                              children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3.5 h-3.5" })
1833                            }
1834                          ),
1835                          isHsf3 && /* @__PURE__ */ jsxRuntimeExports.jsx(
1836                            "button",
1837                            {
1838                              onClick: () => {
1839                                if (confirm(`Rebuild HSF3 Bank for ${formatName}?`)) {
1840                                  const sf2Path = formatName.replace(/_HSF3Bank$/i, ".sf2");
1841                                  startHsf3BankConversion(sf2Path);
1842                                }
1843                              },
1844                              className: "p-1.5 text-purple-500 hover:bg-purple-50 dark:hover:bg-purple-900/20 rounded transition-colors border border-purple-200 dark:border-purple-800/50",
1845                              title: "Rebuild HSF3 Bank",
1846                              children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3.5 h-3.5" })
1847                            }
1848                          ),
1849                          isSf2 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1850                            /* @__PURE__ */ jsxRuntimeExports.jsxs(
1851                              "button",
1852                              {
1853                                onClick: () => startSf2Conversion(formatName),
1854                                className: "px-2 py-1 bg-indigo-500 hover:bg-indigo-600 text-white text-[9px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1855                                title: "Sample to JS",
1856                                children: [
1857                                  /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3.5 h-3.5" }),
1858                                  "Sample"
1859                                ]
1860                              }
1861                            ),
1862                            /* @__PURE__ */ jsxRuntimeExports.jsxs(
1863                              "button",
1864                              {
1865                                onClick: () => startSf3Conversion(formatName),
1866                                className: "px-2 py-1 bg-teal-500 hover:bg-teal-600 text-white text-[9px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1867                                title: "Convert to SF3 (OGG compressed)",
1868                                children: [
1869                                  /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3.5 h-3.5" }),
1870                                  "SF3"
1871                                ]
1872                              }
1873                            ),
1874                            /* @__PURE__ */ jsxRuntimeExports.jsxs(
1875                              "button",
1876                              {
1877                                onClick: () => startHsf3BankConversion(formatName),
1878                                className: "px-2 py-1 bg-purple-500 hover:bg-purple-600 text-white text-[9px] font-bold rounded uppercase flex items-center gap-1 transition-all shadow-sm",
1879                                title: "Convert to HSF3 Bank (Split instrument files + DRM obfuscated)",
1880                                children: [
1881                                  /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "w-3.5 h-3.5" }),
1882                                  "HSF3 Bank"
1883                                ]
1884                              }
1885                            )
1886                          ] }),
1887                          /* @__PURE__ */ jsxRuntimeExports.jsx(
1888                            "button",
1889                            {
1890                              onClick: () => confirm(`Delete format ${formatName}?`) && handleDeleteSoundfont(formatName),
1891                              className: "p-1.5 text-zinc-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors",
1892                              title: `Delete ${formatName}`,
1893                              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "w-3.5 h-3.5" })
1894                            }
1895                          )
1896                        ] }) })
1897                      ]
1898                    }
1899                  ),
1900                  expandedFolders.has(formatName) && lib.children && lib.children.map((child) => {
1901                    var _a4;
1902                    return /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-50/50 dark:bg-zinc-900/50 border-b border-zinc-100 dark:border-zinc-800/50 text-[10px]", children: [
1903                      /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-right", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-300 dark:text-zinc-700 font-mono", children: "↳" }) }),
1904                      /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 pl-10", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 text-zinc-500 dark:text-zinc-400 font-mono", children: [
1905                        /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { className: "w-3 h-3 mt-1 shrink-0" }),
1906                        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
1907                          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: child.name || ((_a4 = child.path) == null ? void 0 : _a4.split("/").pop()) || "Unknown" }),
1908                          (() => {
1909                            const name = child.name || child.path || "";
1910                            const match = name.match(/_(\d+)\.hsf3$/i) || name.match(/_(\d+)_.*\.hsf3$/i);
1911                            if (match) {
1912                              const prog = parseInt(match[1], 10);
1913                              return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-400 truncate", children: GM_INSTRUMENTS[prog] });
1914                            }
1915                            return null;
1916                          })()
1917                        ] })
1918                      ] }) }),
1919                      /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 2 }),
1920                      /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-center text-zinc-400 font-mono font-medium", children: formatFileSize(child.size) }),
1921                      /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 6 })
1922                    ] }, `${formatName}/${child.name}`);
1923                  })
1924                ] }, formatName);
1925              })
1926            ] }, baseName);
1927          }) })
1928        ] }) })
1929      ] })
1930    ] }),
1931    showKeyboard && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { height: 350 } }),
1932    showKeyboard && selectedSfForKeyboard && /* @__PURE__ */ jsxRuntimeExports.jsx(
1933      SoundfontKeyboardDock,
1934      {
1935        selectedSf: selectedSfForKeyboard,
1936        program: keyboardProgram,
1937        setProgram: setKeyboardProgram,
1938        bank: keyboardBank,
1939        setBank: setKeyboardBank,
1940        onClose: () => setShowKeyboard(false),
1941        gain: ((_a = profiles[selectedSfForKeyboard.replace(/_JS$/, "").replace(/_HSF3Bank$/i, "").replace(/_SF3Bank$/i, "").replace(/\.h?sf[23]$/i, "")]) == null ? void 0 : _a.gain) ?? 1.8
1942      }
1943    )
1944  ] });
1945};
1946export {
1947  SoundfontManager as default
1948};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.