UNPKG

wave-roll

Version:

JavaScript Library for Comparative MIDI Piano-Roll Visualization

531 lines (530 loc) 28.5 kB
import { D as q, t as B, O as U, r as _, P as O, a as K, b as Y } from "./index-C4TFo-hd.js"; function V(t = "multi-midi-settings-modal") { const r = document.getElementById(t); if (r) return { overlay: r, modal: r.firstElementChild }; const p = document.createElement("div"); p.id = t, p.style.cssText = ` position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 2000; `; const o = document.createElement("div"); return o.style.cssText = ` width: 600px; max-width: 95%; max-height: 80vh; overflow-y: auto; background: var(--panel-bg); border-radius: 12px; padding: 24px; display: flex; flex-direction: column; gap: 24px; `, p.appendChild(o), { overlay: p, modal: o }; } function J(t, r) { const p = document.createElement("div"); p.style.cssText = "display:flex;justify-content:space-between;align-items:center;"; const o = document.createElement("h2"); o.textContent = t, o.style.cssText = "margin:0;font-size:20px;font-weight:700;color:var(--text-primary);"; const C = document.createElement("button"); return C.textContent = "✕", C.style.cssText = "border:none;background:transparent;font-size:24px;cursor:pointer;color:var(--text-muted);", C.onclick = r, p.appendChild(o), p.appendChild(C), p; } function Q(t, r, p, o) { const C = document.getElementById("wr-onset-picker-overlay"); C && C.remove(); const f = document.createElement("div"); f.id = "wr-onset-picker-overlay", f.style.cssText = ` position: fixed; inset: 0; z-index: 3000; background: transparent; `; const l = document.createElement("div"); l.setAttribute("role", "dialog"), l.setAttribute("aria-label", "Onset marker picker"), l.style.cssText = ` position: absolute; min-width: 240px; max-width: 420px; background: var(--surface); border: 1px solid var(--ui-border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.18); padding: 10px; display: flex; flex-direction: column; gap: 10px; `; function M() { const E = p.getBoundingClientRect(), S = l.offsetHeight, A = l.offsetWidth, w = window.innerHeight - E.bottom - 8, N = E.top - 8; let R; S <= w || w >= N ? R = Math.min(E.bottom + 6, window.innerHeight - S - 8) : R = Math.max(8, E.top - S - 6); let j = Math.max(8, Math.min(window.innerWidth - A - 8, E.left)); l.style.top = `${Math.round(R)}px`, l.style.left = `${Math.round(j)}px`; } const P = document.createElement("div"); P.textContent = "Choose color & marker", P.style.cssText = "font-size:12px;color:var(--text-muted);margin-bottom:2px;"; const { activePaletteId: D, customPalettes: h } = t.midiManager.getState(), a = [...q, ...h].find((d) => d.id === D) || q[0], m = t.midiManager.getState().files.find((d) => d.id === r), e = B(m?.color ?? 0), n = t.stateManager.getOnsetMarkerForFile(r) || t.stateManager.ensureOnsetMarkerForFile(r), u = document.createElement("div"); u.setAttribute("role", "listbox"), u.style.cssText = "display:flex;gap:6px;flex-wrap:wrap;"; const b = []; let i = e; const c = () => { b.forEach((d) => { const x = (d.dataset.hex || "").toLowerCase() === i.toLowerCase(); d.style.outline = x ? "2px solid var(--focus-ring)" : "none", d.setAttribute("aria-selected", String(x)), d.tabIndex = x ? 0 : -1; }); }; a.colors.forEach((d, x) => { const E = B(d), S = document.createElement("button"); S.type = "button", S.dataset.hex = E, S.setAttribute("aria-label", `Select color ${E}`), S.style.cssText = `width:22px;height:22px;border-radius:4px;border:1px solid var(--ui-border);background:${E};cursor:pointer;`, S.onclick = () => { t.midiManager.updateColor(r, d), i = E, c(), o && o(n, E); }, x === 0 && (S.tabIndex = 0), b.push(S), u.appendChild(S); }), c(); const s = document.createElement("div"); s.style.cssText = "height:1px;background:var(--ui-border);margin:2px 0;"; const g = document.createElement("div"); g.style.cssText = "display:flex;flex-direction:column;gap:6px;"; const L = ["filled", "outlined"], y = []; let T = { ...n }; const F = () => { y.forEach((d) => { const x = d.dataset.shape === T.shape && d.dataset.variant === T.variant; d.style.outline = x ? "2px solid var(--focus-ring)" : "none", d.setAttribute("aria-pressed", String(x)), x && (d.tabIndex = 0); }); }; L.forEach((d) => { const x = document.createElement("div"); x.textContent = d === "filled" ? "Filled" : "Outlined", x.style.cssText = "font-size:11px;color:var(--text-muted);"; const E = document.createElement("div"); E.style.cssText = "display:grid;grid-template-columns:repeat(7,28px);gap:6px;", U.forEach((S) => { const A = { shape: S, variant: d, size: 12, strokeWidth: 2 }, w = document.createElement("button"); w.type = "button", w.setAttribute("aria-label", `${S} ${d}`), w.dataset.shape = String(S), w.dataset.variant = String(d), w.dataset.index = String(y.length), w.style.cssText = "width:28px;height:28px;border:1px solid var(--ui-border);border-radius:6px;background:var(--surface);display:flex;align-items:center;justify-content:center;cursor:pointer;", w.innerHTML = _(A, e, 16), w.onclick = () => { t.stateManager.setOnsetMarkerForFile(r, A); const N = t.midiManager.getState().files.find((j) => j.id === r), R = B(N?.color ?? 0); T = A, F(), o && o(A, R); }, E.appendChild(w), y.push(w); }), g.appendChild(x), g.appendChild(E); }), F(); const z = document.createElement("div"); z.style.cssText = "display:flex;gap:8px;justify-content:flex-end;"; const v = document.createElement("button"); v.type = "button", v.textContent = "Auto assign", v.style.cssText = "padding:4px 8px;border:1px solid var(--ui-border);border-radius:4px;background:var(--surface);cursor:pointer;", v.onclick = () => { const d = t.stateManager.assignNextUniqueOnsetMarker ? t.stateManager.assignNextUniqueOnsetMarker(r) : t.stateManager.ensureOnsetMarkerForFile(r), x = t.midiManager.getState().files.find((S) => S.id === r), E = B(x?.color ?? 0); T = d, F(), o && o(d, E); }; const I = document.createElement("button"); I.type = "button", I.textContent = "Close", I.style.cssText = "padding:4px 8px;border:1px solid var(--ui-border);border-radius:4px;background:var(--surface);cursor:pointer;", I.onclick = () => f.remove(), z.appendChild(v), z.appendChild(I), l.appendChild(P), l.appendChild(u), l.appendChild(s), l.appendChild(g), l.appendChild(z), f.appendChild(l), f.addEventListener("click", (d) => { d.target === f && f.remove(); }), f.addEventListener("keydown", (d) => { d.key === "Escape" && f.remove(); }), f.addEventListener("keydown", (d) => { const x = d, E = x.target; if (E && E.tagName.toLowerCase() === "button") { if (E.hasAttribute("data-index")) { const A = Number(E.getAttribute("data-index") || "0"); let w = A; if (x.key === "ArrowRight") w = Math.min(y.length - 1, A + 1); else if (x.key === "ArrowLeft") w = Math.max(0, A - 1); else if (x.key === "ArrowDown") w = Math.min(y.length - 1, A + 7); else if (x.key === "ArrowUp") w = Math.max(0, A - 7); else if (x.key === "Enter" || x.key === " ") { E.click(), x.preventDefault(); return; } w !== A && (x.preventDefault(), y[w]?.focus()); } else if (u.contains(E)) { const A = b.indexOf(E); if (A >= 0) { let w = A; if (x.key === "ArrowRight") w = Math.min(b.length - 1, A + 1); else if (x.key === "ArrowLeft") w = Math.max(0, A - 1); else if (x.key === "Enter" || x.key === " ") { E.click(), x.preventDefault(); return; } w !== A && (x.preventDefault(), b[w]?.focus()); } } } }), document.body.appendChild(f), l.style.visibility = "hidden", requestAnimationFrame(() => { l.style.visibility = "visible", M(); }); const H = () => M(); window.addEventListener("resize", H), window.addEventListener("scroll", H, { passive: !0 }); const G = () => { window.removeEventListener("resize", H), window.removeEventListener("scroll", H); }; f.addEventListener("remove", G), setTimeout(() => { l.querySelector("button")?.focus?.(); }, 0); } function X(t) { const r = document.createElement("div"), p = document.createElement("h3"); p.textContent = "MIDI Files", p.style.cssText = "margin:0 0 12px;font-size:16px;font-weight:600;", r.appendChild(p); const o = document.createElement("div"); o.style.cssText = "display:flex;flex-direction:column;gap:8px;", r.appendChild(o); const C = () => { o.innerHTML = ""; const f = t.midiManager.getState().files, l = t.permissions?.canRemoveFiles !== !1, M = (a) => { a.preventDefault(); }, P = (a) => { a.preventDefault(); const m = parseInt( a.dataTransfer.getData("text/plain"), 10 ); if (Number.isNaN(m)) return; const e = f.length - 1; m !== e && (t.midiManager.reorderFiles(m, e), C()); }; o.removeEventListener("dragover", M), o.removeEventListener("drop", P), o.addEventListener("dragover", M), o.addEventListener("drop", P), f.forEach((a, m) => { const e = document.createElement("div"); e.style.cssText = "display:flex;align-items:center;gap:8px;background:var(--surface-alt);padding:8px;border-radius:6px;"; const n = document.createElement("span"); n.id = `file-list-handle-${a.id}`, n.draggable = !0, n.innerHTML = O.menu, n.style.cssText = "cursor:grab;color:var(--text-muted);display:flex;align-items:center;justify-content:center;width:18px;user-select:none;"; const u = (v) => { v.stopPropagation(); }, b = B(a.color), i = document.createElement("div"); i.style.cssText = "position:relative;display:flex;align-items:center;"; const c = document.createElement("button"); c.type = "button", c.title = "Click to change color", c.style.cssText = "width:24px;height:24px;border-radius:4px;border:1px solid var(--ui-border);cursor:pointer;background:transparent;position:relative;padding:0;display:flex;align-items:center;justify-content:center;"; const s = document.createElement("div"); s.style.cssText = "width:18px;height:18px;display:flex;align-items:center;justify-content:center;"; const g = (v, I) => _(v, I, 16), L = t.stateManager.ensureOnsetMarkerForFile(a.id); s.innerHTML = g(L, b), c.appendChild(s); const y = document.createElement("input"); y.type = "color", y.value = b, y.style.cssText = "position:absolute;opacity:0;width:0;height:0;border:0;padding:0;", c.onclick = (v) => { Q( t, a.id, c, (I, H) => { s.innerHTML = g(I, H); } ); }, y.onchange = (v) => { const I = v.target.value; t.midiManager.updateColor( a.id, parseInt(I.substring(1), 16) ); const H = t.stateManager.getOnsetMarkerForFile(a.id) || L; s.innerHTML = g(H, I); }, c.appendChild(y), i.appendChild(c); const T = document.createElement("input"); T.type = "text", T.value = a.name, T.onchange = (v) => { t.midiManager.updateName( a.id, v.target.value ); }, T.style.cssText = "flex:1;padding:4px 6px;border:1px solid var(--ui-border);border-radius:4px;background:var(--surface);color:var(--text-primary);"; const F = document.createElement("button"); F.setAttribute("aria-label", "Delete MIDI file"), F.innerHTML = O.trash, F.style.cssText = "border:none;background:transparent;cursor:pointer;width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);", F.onclick = () => { l && confirm(`Delete ${a.name}?`) && (t.midiManager.removeMidiFile(a.id), C()); }, e.dataset.index = m.toString(), n.addEventListener("dragstart", (v) => { v.dataTransfer.effectAllowed = "move", v.dataTransfer.setData( "text/plain", (e.dataset.index || "0").toString() ), e.style.opacity = "0.6", n.style.cursor = "grabbing"; }), n.addEventListener("dragend", () => { e.style.opacity = "1", n.style.cursor = "grab", e.style.outline = "none"; }), e.draggable = !1, [c, T, F].forEach((v) => { v.addEventListener("mousedown", u), v.addEventListener("touchstart", u); }), e.addEventListener("dragover", (v) => { v.preventDefault(), v.stopPropagation(), e.style.outline = "2px dashed var(--focus-ring)"; }), e.addEventListener("dragleave", () => { e.style.outline = "none"; }), n.addEventListener("dragover", (v) => { v.preventDefault(), v.stopPropagation(), e.style.outline = "2px dashed var(--focus-ring)"; }); const z = (v) => (I) => { I.preventDefault(), I.stopPropagation(); const H = parseInt( I.dataTransfer.getData("text/plain"), 10 ); !Number.isNaN(H) && H !== v && (t.midiManager.reorderFiles(H, v), C()), e.style.outline = "none"; }; e.addEventListener("drop", z(m)), n.addEventListener("drop", z(m)), e.appendChild(n), e.appendChild(i), e.appendChild(T), l && e.appendChild(F), o.appendChild(e); }); const D = t.permissions?.canAddFiles !== !1, h = document.createElement("button"); h.type = "button", h.textContent = "Add MIDI Files", h.style.cssText = "margin-top:12px;padding:8px;border:1px solid var(--ui-border);border-radius:6px;background:var(--surface);cursor:pointer;font-size:14px;color:var(--text-primary);"; const k = document.createElement("input"); k.type = "file", k.accept = ".mid,.midi", k.multiple = !0, k.style.display = "none", h.onclick = () => { D && k.click(); }, k.onchange = async (a) => { if (!D) return; const m = Array.from(a.target.files || []); if (m.length !== 0) { for (const e of m) try { const n = t.stateManager?.getState(), u = n?.visual.pedalElongate ?? !0, b = n?.visual.pedalThreshold ?? 64, i = await K(e, { applyPedalElongate: u, pedalThreshold: b }); t.midiManager.addMidiFile(e.name, i, void 0, e); } catch (n) { console.error("Failed to parse MIDI", n); } C(), k.value = ""; } }, D && o.appendChild(h); }; return C(), typeof t.midiManager.subscribe == "function" && t.midiManager.subscribe(C), r; } function W(t, r, p, o = r ? "edit" : "create") { const { overlay: C, modal: f } = V( "palette-editor-modal" ); for (; f.firstChild; ) f.removeChild(f.firstChild); const l = o === "edit", M = l && r ? r.id : Date.now().toString(), P = o === "clone" && r ? `${r.name} Copy` : r?.name ?? ""; function D() { if (r) return r.colors; const c = t.midiManager.getState().files.map((s) => s.color); return c.filter((s, g) => c.indexOf(s) === g); } const h = D().length > 0 ? D().map((c) => B(c)) : [B(0)], k = document.createElement("label"); k.textContent = "Palette name", k.style.cssText = "font-weight:600;font-size:14px;display:block;margin-bottom:4px;"; const a = document.createElement("input"); a.type = "text", a.value = P, a.placeholder = "My palette", a.style.cssText = "width:100%;padding:6px 8px;border:1px solid var(--ui-border);border-radius:6px;margin-bottom:12px;background:var(--surface);color:var(--text-primary);"; const m = document.createElement("div"); m.style.cssText = "display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;"; const e = () => { for (; m.firstChild; ) m.removeChild(m.firstChild); h.forEach((c, s) => { const g = document.createElement("div"); g.style.cssText = "display:flex;flex-direction:column;align-items:center;gap:4px;"; const L = document.createElement("button"); L.type = "button", L.title = "Click to change color, right-click to remove", L.style.cssText = `width:32px;height:32px;border-radius:4px;border:1px solid var(--ui-border);background:${c};cursor:pointer;position:relative;`; const y = document.createElement("input"); y.type = "text", y.maxLength = 7, y.placeholder = "#000000", y.value = c, y.style.cssText = "width:70px;padding:2px 4px;font-size:10px;font-family:monospace;text-align:center;border:1px solid var(--ui-border);border-radius:4px;background:var(--surface);color:var(--text-primary);"; const T = document.createElement("input"); T.type = "color", T.value = c, T.style.cssText = "position:absolute;opacity:0;width:0;height:0;border:0;padding:0;", T.onchange = () => { h[s] = T.value, L.style.background = T.value, y.value = T.value.replace("#", ""); }, L.onclick = () => T.click(), L.oncontextmenu = (F) => { F.preventDefault(), h.length > 1 && (h.splice(s, 1), e()); }, L.appendChild(T), y.oninput = () => { const F = y.value.trim(); /^#[0-9a-fA-F]{0,6}$/.test(F) && F.length === 7 && (h[s] = F, L.style.background = F, T.value = F); }, g.appendChild(L), g.appendChild(y), m.appendChild(g); }); }; e(); const n = document.createElement("button"); n.type = "button", n.textContent = "+ Add color", n.style.cssText = "padding:6px 8px;border:1px dashed var(--ui-border);border-radius:6px;background:var(--surface);font-size:12px;cursor:pointer;margin-bottom:16px;color:var(--text-primary);", n.onclick = () => { h.push("#000000"), e(); }; const u = document.createElement("div"); u.style.cssText = "display:flex;justify-content:flex-end;gap:8px;"; const b = document.createElement("button"); b.type = "button", b.textContent = "Cancel", b.style.cssText = "padding:6px 12px;border:1px solid var(--ui-border);border-radius:6px;background:var(--surface);cursor:pointer;color:var(--text-primary);", b.onclick = () => C.remove(); const i = document.createElement("button"); i.type = "button", i.textContent = l ? "Update" : "Create", i.style.cssText = "padding:6px 12px;border:1px solid var(--accent-strong);border-radius:6px;background:var(--accent-strong);color:var(--on-accent);cursor:pointer;", i.onclick = () => { const c = a.value.trim(); if (!c) { alert("Palette name is required"); return; } const s = h.map((g) => g.replace("#", "")).filter((g) => /^([0-9a-fA-F]{6})$/.test(g)).map((g) => parseInt(g, 16)); if (s.length === 0) { alert("At least one valid color is required"); return; } l ? t.midiManager.updateCustomPalette(M, { name: c, colors: s }) : t.midiManager.addCustomPalette({ id: M, name: c, colors: s }), C.remove(), p(); }, u.append(b, i), f.append(k, a, m, n, u), document.body.appendChild(C); } function $(t) { const r = document.createElement("div"); r.setAttribute("data-palette-selector", "true"); const p = document.createElement("h3"); p.id = "palette-title", p.textContent = "Color Palette", p.style.cssText = "margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text-primary);"; const o = document.createElement("div"); o.id = "palette-grid", o.style.cssText = "display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:12px;"; const { customPalettes: C, activePaletteId: f } = t.midiManager.getState(), l = [...q, ...C], M = document.createElement("div"); M.style.cssText = "margin-top:12px;padding:8px;border:1px solid var(--ui-border);border-radius:6px;background:var(--surface-alt);display:none;flex-wrap:wrap;gap:12px;align-items:center;"; let P = ""; const D = (e) => { if (P === e.id) { M.style.display = "none", M.innerHTML = "", P = ""; return; } P = e.id, M.innerHTML = ""; const n = document.createElement("div"); n.style.cssText = "display:flex;gap:4px;flex-wrap:wrap;", e.colors.forEach((s) => { const g = document.createElement("div"); g.style.cssText = `width:20px;height:20px;border-radius:3px;background:${B( s )}`, n.appendChild(g); }); const u = document.createElement("span"); u.textContent = e.name, u.style.cssText = "font-size:14px;font-weight:600;color:var(--text-primary);"; const b = document.createElement("div"); b.style.cssText = "display:flex;gap:8px;margin-left:auto;"; const i = (s, g, L) => { const y = document.createElement("button"); return y.type = "button", y.title = g, y.innerHTML = s, y.style.cssText = "width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:none;background:none;cursor:pointer;color:var(--text-muted);", y.onclick = (T) => { T.stopPropagation(), L(); }, y; }; b.appendChild( i(O.duplicate, "Duplicate", () => { W( t, e, () => { const s = $(t); r.replaceWith(s); }, "clone" ); }) ), C.some((s) => s.id === e.id) && (b.appendChild( i(O.edit, "Edit", () => { W( t, e, () => { const s = $(t); r.replaceWith(s); }, "edit" ); }) ), b.appendChild( i(O.trash, "Delete", () => { if (confirm( `Delete palette "${e.name}"? This action cannot be undone.` )) { t.midiManager.removeCustomPalette(e.id); const s = $(t); r.replaceWith(s); } }) )), M.append(n, u, b), M.style.display = "flex"; }; l.forEach((e) => { const n = document.createElement("button"); n.type = "button", n.style.cssText = `display:flex;flex-direction:column;align-items:center;padding:6px 4px;border:1px solid var(--ui-border);border-radius:6px;cursor:pointer;background:${e.id === f ? "var(--surface-alt)" : "var(--surface)"};transition:background 0.2s;`; const u = document.createElement("div"); u.style.cssText = "display:flex;gap:2px;margin-bottom:4px;", e.colors.slice(0, 8).forEach((g) => { const L = document.createElement("div"); L.style.cssText = `width:12px;height:12px;border-radius:2px;background:${B( g )}`, u.appendChild(L); }); const b = document.createElement("span"); b.textContent = e.name, b.style.cssText = "font-size:12px;color:var(--text-muted);"; const i = document.createElement("div"); i.style.cssText = "display:flex;gap:4px;position:absolute;top:4px;right:4px;opacity:0;transition:opacity 0.15s;"; const c = () => i.style.opacity = "1", s = () => i.style.opacity = "0"; n.addEventListener("mouseenter", c), n.addEventListener("mouseleave", s), n.addEventListener("focus", c), n.addEventListener("blur", s), n.onclick = () => { t.midiManager.getState().activePaletteId !== e.id && t.midiManager.setActivePalette(e.id), [...o.children].forEach( (g) => g instanceof HTMLElement && (g.style.background = "var(--surface)") ), n.style.background = "var(--surface-alt)", D(e); }, n.style.position = "relative", n.append(u, b), o.appendChild(n); }); const h = document.createElement("button"); h.type = "button", h.style.cssText = "display:flex;flex-direction:column;align-items:center;justify-content:center;padding:6px 4px;border:1px dashed var(--ui-border);border-radius:6px;cursor:pointer;background:var(--surface);gap:4px;transition:background 0.2s;"; const k = document.createElement("span"); k.textContent = "+", k.style.cssText = "font-size:20px;line-height:1;color:var(--text-muted);"; const a = document.createElement("span"); a.textContent = "New Palette", a.style.cssText = "font-size:12px;color:var(--text-muted);", h.append(k, a), h.onclick = () => { W(t, null, () => { const e = $(t); r.replaceWith(e); }); }, o.appendChild(h); const m = l.find((e) => e.id === f) ?? l[0]; return D(m), r.append(p, o, M), r; } function Z(t) { const r = document.createElement("div"), p = document.createElement("h3"); p.textContent = "WAV File", p.style.cssText = "margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text-primary);", r.appendChild(p); const o = document.createElement("div"); o.style.cssText = "display:flex;flex-direction:column;gap:8px;", r.appendChild(o); const C = () => globalThis._waveRollAudio, f = () => { o.innerHTML = ""; const l = C(), M = l?.getFiles?.() ?? []; M.forEach((k) => { const a = document.createElement("div"); a.style.cssText = "display:flex;align-items:center;gap:8px;background:var(--surface-alt);padding:8px;border-radius:6px;border:1px solid var(--ui-border);"; const m = document.createElement("button"); m.type = "button"; const e = `#${(k.color >>> 0).toString(16).padStart(6, "0")}`; m.style.cssText = `width:20px;height:20px;border-radius:3px;border:1px solid var(--ui-border);background:${e};cursor:pointer;position:relative;padding:0;`; const n = document.createElement("input"); n.type = "color", n.value = e, n.style.cssText = "position:absolute;opacity:0;width:0;height:0;border:0;padding:0;", n.addEventListener("change", () => { const i = n.value, c = parseInt(i.replace("#", ""), 16); l?.updateColor?.(k.id, c), m.style.background = i; }), m.addEventListener("click", () => n.click()), m.appendChild(n); const u = document.createElement("input"); if (u.type = "text", u.value = k.name, u.style.cssText = "flex:1;padding:4px 6px;border:1px solid var(--ui-border);border-radius:4px;background:var(--surface);color:var(--text-primary);", u.addEventListener("change", () => { l?.updateName?.(k.id, u.value.trim()); }), a.appendChild(m), a.appendChild(u), t.permissions?.canRemoveFiles !== !1) { const i = document.createElement("button"); i.type = "button", i.innerHTML = O.trash, i.style.cssText = "width:24px;height:24px;padding:0;border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-muted);opacity:0.7;", i.title = "Remove audio file", i.addEventListener("mouseenter", () => { i.style.opacity = "1", i.style.color = "var(--danger, #dc3545)"; }), i.addEventListener("mouseleave", () => { i.style.opacity = "0.7", i.style.color = "var(--text-muted)"; }), i.addEventListener("click", () => { l?.remove?.(k.id); try { t.audioPlayer?.pause?.(); } catch { } f(); const c = document.querySelector('[data-role="file-toggle"]'); if (c) { const s = window.FileToggleManager; s && s.updateFileToggleSection(c, t); } }), a.appendChild(i); } o.appendChild(a); }); const P = t.permissions?.canAddFiles !== !1, D = document.createElement("button"); D.type = "button", D.textContent = M.length > 0 ? "Change WAV File" : "Add WAV File", D.style.cssText = "margin-top:12px;padding:8px;border:1px solid var(--ui-border);border-radius:6px;background:var(--surface);cursor:pointer;font-size:14px;color:var(--text-primary);"; const h = document.createElement("input"); h.type = "file", h.accept = ".wav,.mp3,.m4a,.ogg", h.style.display = "none", D.onclick = () => { P && h.click(); }, h.onchange = async (k) => { if (!P) return; const a = k.target.files; if (!a || a.length === 0) return; const m = a[0]; try { const e = URL.createObjectURL(m); await Y(null, e, m.name), f(); const n = document.querySelector('[data-role="file-toggle"]'); if (n) { const u = window.FileToggleManager; u && u.updateFileToggleSection(n, t); } } catch (e) { console.error("Failed to load audio file", e); } h.value = ""; }, P && (o.appendChild(D), o.appendChild(h)); }; return f(), r; } function te(t) { const { overlay: r, modal: p } = V(); if (p.childElementCount > 0) { r.parentElement || document.body.appendChild(r); return; } const o = J("Tracks & Appearance", () => r.remove()), C = $(t), f = Z(t), l = X(t); p.appendChild(o), p.appendChild(C), p.appendChild(f), p.appendChild(l), r.addEventListener("click", (M) => { M.target === r && r.remove(); }), document.body.appendChild(r); } export { te as openSettingsModal };