PageSourceSearch

https://www.hamienet.com/assets/main-CznOH3wZ.js

js hamienet.com collected 2026-09-24 18:58:51 UTC 500,938 bytes, 9,130 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/HomePage-q4aW8A7v.js","assets/vendor-react-Bay9Y7Lb.js","assets/SectionPreviewModal-Dtgnulvj.js","assets/utils-BYCVoSTN.js","assets/index-D0Bmh-UX.js","assets/vendor-i18n-R-bMoYp_.js","assets/vendor-dVNaLJx_.js","assets/vendor-DqKRdbjz.css","assets/index-WEfYDNmt.css","assets/bilingual-B8ftIVs5.js","assets/SectionContent-TfBXVZBi.js","assets/TrackRow-DUmbuow9.js","assets/GlobeFlag-V1uaxVEK.js","assets/UserAvatar-BQN4jlY-.js","assets/AdSlot-CPW4Sb2o.js","assets/labTools-BATZiaPB.js","assets/vendor-react-three-MDLA5jog.js","assets/synth-BE1TSTD4.js","assets/wasm-synth-adapter-BrqgBX1y.js","assets/vendor-spessasynth-DH8n6o_V.js","assets/vendor-audio-DH3w0BzW.js","assets/Waveform-MxtdejK7.js","assets/vendor-framer-C8eEgkdL.js","assets/AdvancedSearchPage-tGa2266t.js","assets/AdvancedSearchPage-DWZp9cn1.css","assets/MelodySearchPage-C8DfrA33.js","assets/WatchPage-B8_07PT2.js","assets/SharedKeyboard-B3r3Jaho.js","assets/useMidiKeyboard-DlyEIN8T.js","assets/ExplorePage-BWHkev4d.js","assets/AccountPage-mZQDQlod.js","assets/PlaylistsPage-Br-5J847.js","assets/ProjectList-DNE9O-rf.js","assets/ConverterPage-kt7PUb_r.js","assets/credit-rules-8PgauDOD.js","assets/CreditsExhaustedModal-CRFvqXo7.js","assets/EditorPage-Dsed0DcB.js","assets/ScoreEditorCanvas-hnha0PBU.js","assets/MidiEngine-BqW6hqRc.js","assets/HistoryPage-BYHoeFoZ.js","assets/ListPage-gXg1XIZF.js","assets/LikedPage-SwAUDqZq.js","assets/WatchLaterPage-32xxryAT.js","assets/SubscriptionsPage-D5-uiNqs.js","assets/ChannelPage-DAFNmqL_.js","assets/TagPage-DI5S2d93.js","assets/HelpPage-DsiYZpvH.js","assets/MessengerPage-
1D0_m_QNf.js","assets/LyricsPage-BWsBdPvc.js","assets/LyricsSearchPage-CzRGUKfj.js","assets/AdminApp-D1oXn0it.js","assets/PersonalSpacePage-BQt1uXtk.js","assets/NewlyUploadedPage-CswaPEfP.js","assets/BillingPage-B2Hb0PMo.js","assets/CollectionIndexPage-B76qFKH2.js","assets/CollectionPage-DTEimYXF.js","assets/CollectionSectionPage-B6Zl3jqi.js","assets/EmailVerification-D-JaNSfM.js","assets/ResetPassword-CpmlaEYr.js","assets/ScoreConverterPage-BvnaneVe.js","assets/VocalProcessorPage-BWIy7sro.js","assets/AlgorithmStudio-BAKVJ64e.js","assets/AlgorithmEditor-BuTd-7oq.js","assets/ScoreMusicXmlViewer-2usqegaw.js","assets/StagingArena-C9ym5iYy.js","assets/TribunalPage-yYCBAk5S.js","assets/HamieLabPage-KXwalnzA.js","assets/MidiVisualizerPage-B4L5oo98.js","assets/DMCAForm-DoA_m2iR.js","assets/SheetMusicDigitizerPage-CGO_x0iu.js","assets/AudioDigitizerPage-DjRFGvnM.js","assets/ClientAudioDigitizerPage-wnww40sz.js","assets/vendor-basicpitch-Bd1IWE9_.js","assets/index--Kd7xwwe.js","assets/KeyboardEmulatorPage-3bHJ91O7.js","assets/SoundfontsPage-Kim5OuPr.js","assets/SoundfontDetailPage-R4ZkN9OB.js","assets/SoundfontUploadPage-C9Pu1znK.js"])))=>i.map(i=>d[i]);
2import { a as api, u as useSettings, L as LANGUAGE_REGIONS, N as NATIVE_LANGUAGE_NAMES, S as SettingsProvider, b as uxTracker } from "./index-D0Bmh-UX.js";
3import { r as reactExports, j as jsxRuntimeExports, R as React, h as ReactDOM, f as createRoot } from "./vendor-react-Bay9Y7Lb.js";
4import { _ as __vitePreload } from "./vendor-react-three-MDLA5jog.js";
5import { i as isPro, g as getColorsFromHash, a as generateTrackUrl, t as timeAgo, b as getInstrumentName, c as generatePseudoWaveform, d as isContentBlocked, e as isSlowMobile, f as getBestAudioMode } from "./utils-BYCVoSTN.js";
6import { s as synth, p as parseMidiInstruments } from "./synth-BE1TSTD4.js";
7import { u as useTranslation } from "./vendor-i18n-R-bMoYp_.js";
8import { aW as X, aX as ArrowLeft, aY as TriangleAlert, aZ as Mail, a_ as CircleCheck, a$ as Shield, b0 as FileText, b1 as CircleAlert, b2 as Upload, b3 as FileHeadphone, b4 as FolderOpen, b5 as Trash2, b6 as LoaderCircle, b7 as Check, b8 as CheckCheck, b9 as Settings, ba as Info, bb as User, bc as MessageSquare, bd as Heart, be as Menu, bf as Search, bg as Music, bh as Bell, bi as Sparkles, bj as CreditCard, bk as LogOut, bl as UserPlus, bm as ArrowUp, bn as Globe, bo as Monitor, bp as CirclePlay, bq as Coins, br as Moon, bs as Zap, bt as Tv, bu as Sun, bv as CircleCheckBig, bw as Bug, bx as Send, by as MessageCircle, bz as EyeOff, bA as Eye, bB as ListMusic, bC as FlaskConical, bD as Link, bE as ChevronRight, bF as CircleQuestionMark, bG as Box, bH as Hash, bI as Star, bJ as PanelsTopLeft, bK as ThumbsUp, bL as Clock, bM as History, bN as Library, bO as Compass, bP as House, bQ as ExternalLink, bR as CircleStop, bS as Mic, bT as Volume2, bU as Play, bV as RefreshCcw, bW as VolumeX, bX as ListPlus, bY as FolderPlus, bZ as Pause, b_ as EllipsisVertical, b$ as Repeat, c0 as Share2, c1 as Copy, c2 as Loader, c3 as RotateCcw, c4 as SkipBack, c5 as SkipForward, c6 as Shuffle, c7 as Timer, c8 as ArrowUpDown, c9 as Cast, ca as PinOff, 
8cb as Pin, cc as Save, cd as Ellipsis, ce as Plus, cf as Activity, cg as Gauge, ch as Database, ci as Cpu, cj as Layers, ck as HardDrive, cl as OctagonAlert } from "./vendor-dVNaLJx_.js";
9import { T as TagAutocomplete, u as useWaveform, W as Waveform } from "./Waveform-MxtdejK7.js";
10import { H as HamieLogo, D as DEFAULT_TOOLS } from "./labTools-BATZiaPB.js";
11import { U as UserAvatar } from "./UserAvatar-BQN4jlY-.js";
12import "./wasm-synth-adapter-BrqgBX1y.js";
13import "./vendor-spessasynth-DH8n6o_V.js";
14import "./vendor-audio-DH3w0BzW.js";
15import "./vendor-framer-C8eEgkdL.js";
16const stats = {
17  navStart: performance.now(),
18  dbQueryTime: 0,
19  dbQueryCount: 0,
20  serverNode: "Unknown",
21  dbNode: "Unknown",
22  activeFetches: 0,
23  isInitialLoadComplete: false
24};
25function updateMetaTag() {
26  let meta = document.querySelector('meta[name="x-srv"]');
27  if (!meta) {
28    meta = document.createElement("meta");
29    meta.name = "x-srv";
30    document.head.appendChild(meta);
31  }
32  const pageLoadTime = performance.now() - stats.navStart;
33  const loadSec = (pageLoadTime / 1e3).toFixed(4);
34  const dbSec = (stats.dbQueryTime / 1e3).toFixed(4);
35  const srv = stats.serverNode || "Unknown";
36  const db = stats.dbNode || "Unknown";
37  const count = stats.dbQueryCount;
38  meta.content = `${srv}-${loadSec}-${db}-${dbSec}-${count}`;
39}
40function onNavigation() {
41  stats.navStart = performance.now();
42  stats.dbQueryTime = 0;
43  stats.dbQueryCount = 0;
44  stats.activeFetches = 0;
45  stats.isInitialLoadComplete = false;
46}
47const originalPushState = history.pushState;
48history.pushState = function(...args) {
49  originalPushState.apply(this, args);
50  onNavigation();
51};
52const originalReplaceState = history.replaceState;
53history.replaceState = function(...args) {
54  originalReplaceState.apply(this, args);
55  onNavigation();
56};
57window.addEventListener("popstate", onNavigation);
58const originalFetch = window.fetch;
59window.fetch = async function(...args) {
60  var _a;
61  const url = typeof args[0] === "string" ? args[0] : ((_a = args[0]) == null ? void 0 : _a.url) || "";
62  const isBackground = url.includes("/performance") || url.includes("/monitoring-status") || url.includes("/system/") || url.includes("/stats");
63  if (!isBackground && !stats.isInitialLoadComplete) {
64    stats.activeFetches++;
65    if (window.__telemetryIdleTimer) {
66      clearTimeout(window.__telemetryIdleTimer);
67    }
68  }
69  try {
70    const response = await originalFetch.apply(this, args);
71    const qTime = parseFloat(response.headers.get("X-Query-Time") || "0");
72    const qCount = parseInt(response.headers.get("X-Query-Count") || "0", 10);
73    const srvNode = response.headers.get("X-Server-Node");
74    const dbNode = response.headers.get("X-DB-Node");
75    if (srvNode) stats.serverNode = srvNode;
76    if (dbNode) stats.dbNode = dbNode;
77    if (!isBackground && !stats.isInitialLoadComplete) {
78      if (!isNaN(qTime)) stats.dbQueryTime += qTime;
79      if (!isNaN(qCount)) stats.dbQueryCount += qCount;
80    }
81    return response;
82  } finally {
83    if (!isBackground && !stats.isInitialLoadComplete) {
84      stats.activeFetches--;
85      if (window.__telemetryIdleTimer) {
86        clearTimeout(window.__telemetryIdleTimer);
87      }
88      if (stats.activeFetches === 0) {
89        window.__telemetryIdleTimer = setTimeout(() => {
90          if (stats.activeFetches === 0 && !stats.isInitialLoadComplete) {
91            stats.isInitialLoadComplete = true;
92            updateMetaTag();
93          }
94        }, 500);
95      }
96    }
97  }
98};
99const AdminContext = reactExports.createContext(void 0);
100const AdminProvider = ({ children, currentUser }) => {
101  const [staffModeEnabled, setStaffModeEnabled] = reactExports.useState(false);
102  const [isSuperAdmin, setIsSuperAdmin] = reactExports.useState((currentUser == null ? void 0 : currentUser.isSuperAdmin) || false);
103  const refreshStaffStatus = async () => {
104    if (!currentUser || currentUser.role !== "ADMIN") {
105      setStaffModeEnabled(false);
106      setIsSuperAdmin(false);
107      return;
108    }
109    try {
110      const status = await api.getStaffModeStatus();
111      setStaffModeEnabled(status.enabled);
112      const finalIsSuper = !!(status.isSuperAdmin || (currentUser == null ? void 0 : currentUser.isSuperAdmin));
113      setIsSuperAdmin(finalIsSuper);
114    } catch (e) {
115      console.error("[AdminContext] Failed to fetch staff status", e);
116      if ((currentUser == null ? void 0 : currentUser.role) === "ADMIN") {
117        setIsSuperAdmin(currentUser.isSuperAdmin || false);
118      }
119    }
120  };
121  reactExports.useEffect(() => {
122    refreshStaffStatus();
123  }, [currentUser == null ? void 0 : currentUser.id]);
124  const toggleStaffMode = async () => {
125    var _a, _b;
126    try {
127      const result = await api.toggleStaffMode();
128      setStaffModeEnabled(result.enabled);
129      window.dispatchEvent(new CustomEvent("staff-mode-change", { detail: { enabled: result.enabled } }));
130    } catch (error) {
131      console.error("[AdminContext] Toggle failed:", error);
132      const msg = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Failed to toggle Staff Mode.";
133      alert(`${msg} Ensure you have Super-admin privileges.`);
134    }
135  };
136  return /* @__PURE__ */ jsxRuntimeExports.jsx(AdminContext.Provider, { value: { staffModeEnabled, isSuperAdmin, toggleStaffMode, refreshStaffStatus }, children });
137};
138const useAdmin = () => {
139  const context = reactExports.useContext(AdminContext);
140  if (context === void 0) {
141    throw new Error("useAdmin must be used within an AdminProvider");
142  }
143  return context;
144};
145const AuthModal = ({ isOpen, onClose, onAuthSuccess }) => {
146  const { t, i18n } = useTranslation();
147  const [flowState, setFlowState] = reactExports.useState("initial");
148  const [email, setEmail] = reactExports.useState("");
149  const [lookupValue, setLookupValue] = reactExports.useState("");
150  const [lookupResult, setLookupResult] = reactExports.useState(null);
151  const [newEmail, setNewEmail] = reactExports.useState("");
152  const [isEditingEmail, setIsEditingEmail] = reactExports.useState(false);
153  const [password, setPassword] = reactExports.useState("");
154  const [name, setName] = reactExports.useState("");
155  const [loading, setLoading] = reactExports.useState(false);
156  const [error, setError] = reactExports.useState(null);
157  const [successMsg, setSuccessMsg] = reactExports.useState(null);
158  const [resendCooldown, setResendCooldown] = reactExports.useState(0);
159  const [capsLockOn, setCapsLockOn] = reactExports.useState(false);
160  const [selectedAvatar, setSelectedAvatar] = reactExports.useState(null);
161  const [defaultAvatars, setDefaultAvatars] = reactExports.useState([]);
162  const [enabledProviders, setEnabledProviders] = reactExports.useState({
163    google: false,
164    facebook: false,
165    apple: false
166  });
167  const emailInputRef = reactExports.useRef(null);
168  const nameInputRef = reactExports.useRef(null);
169  const passwordInputRef = reactExports.useRef(null);
170  reactExports.useEffect(() => {
171    api.getAuthProviders().then(setEnabledProviders);
172  }, []);
173  reactExports.useEffect(() => {
174    if (!isOpen) {
175      const timer = setTimeout(() => {
176        setFlowState("initial");
177        setEmail("");
178        setNewEmail("");
179        setIsEditingEmail(false);
180        setPassword("");
181        setName("");
182        setError(null);
183        setSuccessMsg(null);
184        setCapsLockOn(false);
185        setSelectedAvatar(null);
186      }, 300);
187      return () => clearTimeout(timer);
188    } else {
189      if (flowState === "initial") {
190        setTimeout(() => {
191          var _a;
192          return (_a = emailInputRef.current) == null ? void 0 : _a.focus();
193        }, 100);
194      }
195    }
196  }, [isOpen, flowState]);
197  reactExports.useEffect(() => {
198    let timer;
199    if (resendCooldown > 0) {
200      timer = setInterval(() => {
201        setResendCooldown((prev) => Math.max(0, prev - 1));
202      }, 1e3);
203    }
204    return () => clearInterval(timer);
205  }, [resendCooldown]);
206  reactExports.useEffect(() => {
207    if (flowState === "login") {
208      setTimeout(() => {
209        var _a;
210        return (_a = passwordInputRef.current) == null ? void 0 : _a.focus();
211      }, 100);
212    } else if (flowState === "verify-pending") {
213      setResendCooldown(60);
214    } else if (flowState === "register") {
215      setTimeout(() => {
216        var _a;
217        return (_a = nameInputRef.current) == null ? void 0 : _a.focus();
218      }, 100);
219      api.getDefaultAvatars().then((avatars) => {
220        setDefaultAvatars(avatars);
221        if (avatars.length > 0 && !selectedAvatar) {
222          setSelectedAvatar(avatars[0]);
223        }
224      });
225    }
226  }, [flowState]);
227  const checkCapsLock = (e) => {
228    if (e.getModifierState) {
229      setCapsLockOn(e.getModifierState("CapsLock"));
230    }
231  };
232  if (!isOpen) return null;
233  const handleContinue = async (e) => {
234    e.preventDefault();
235    setLoading(true);
236    setError(null);
237    try {
238      const inputStr = email.trim();
239      const exists = await api.checkUserExists(inputStr);
240      setTimeout(() => {
241        if (exists) {
242          setFlowState("login");
243        } else {
244          if (!inputStr.includes("@")) {
245            setError(t("auth.usernameNotFound", "Username not found. Please use an email address to create a new account."));
246            setFlowState("initial");
247          } else {
248            setFlowState("register");
249          }
250        }
251        setLoading(false);
252      }, 600);
253    } catch (e2) {
254      setLoading(false);
255      setFlowState("initial");
256      setError(t("common.error", "An error occurred"));
257    }
258  };
259  const validatePassword = (pass) => {
260    if (pass.length < 8) return t("auth.errorPasswordShort", "Password must be at least 8 characters long.");
261    if (!/[a-zA-Z]/.test(pass) || !/[0-9]/.test(pass)) {
262      return t("auth.errorPasswordSimple", "Password must contain at least one letter and one number.");
263    }
264    return null;
265  };
266  const handleSubmit = async (e) => {
267    e.preventDefault();
268    if (flowState === "register") {
269      const passError = validatePassword(password);
270      if (passError) {
271        setError(passError);
272        return;
273      }
274    }
275    setLoading(true);
276    setError(null);
277    try {
278      let authedUser = null;
279      const cleanEmail = email.trim();
280      if (flowState === "login") {
281        authedUser = await api.login(cleanEmail, password);
282      } else {
283        authedUser = await api.register(name, cleanEmail, password, selectedAvatar || void 0, i18n.language);
284      }
285      if (authedUser) {
286        if (authedUser.needsEmailConfirmation) {
287          setNewEmail(authedUser.email);
288          setFlowState("confirm-email");
289          setLoading(false);
290          return;
291        }
292        if (authedUser.pendingVerification) {
293          setFlowState("verify-pending");
294          setLoading(false);
295          return;
296        }
297        onAuthSuccess(authedUser);
298        localStorage.setItem("hamie_session", JSON.stringify(authedUser));
299        setLoading(false);
300        onClose();
301      }
302    } catch (err) {
303      if (err.message === "Email verification required" || err.unverified) {
304        setFlowState("verify-pending");
305      } else {
306        setError(err.message || t("common.error", "An error occurred"));
307      }
308      setLoading(false);
309    }
310  };
311  const handleResendVerification = async () => {
312    if (resendCooldown > 0) return;
313    setLoading(true);
314    setError(null);
315    try {
316      await api.resendVerification(email.trim(), i18n.language);
317      setSuccessMsg("Verification link resent! Please check your inbox.");
318      setResendCooldown(60);
319    } catch (err) {
320      setError(err.message || "Failed to resend link.");
321    } finally {
322      setLoading(false);
323    }
324  };
325  const handleSocialLogin = async (provider) => {
326    setLoading(true);
327    setError(null);
328    try {
329      const user = await api.socialLogin(provider);
330      onAuthSuccess(user);
331      localStorage.setItem("hamie_session", JSON.stringify(user));
332      onClose();
333    } catch (e) {
334      setError(e.message || "Social login failed.");
335    } finally {
336      setLoading(false);
337    }
338  };
339  const handleForgotPassword = async (e) => {
340    e.preventDefault();
341    setLoading(true);
342    setError(null);
343    try {
344      const result = await api.forgotPassword(email.trim(), i18n.language);
345      if (result.success) {
346        setSuccessMsg(result.message);
347      } else {
348        setError(result.message || "Failed to send reset link.");
349      }
350    } catch (err) {
351      setError(err.message || "Failed to send reset link.");
352    } finally {
353      setLoading(false);
354    }
355  };
356  const handleEmailUpdate = async (e) => {
357    e.preventDefault();
358    setLoading(true);
359    setError(null);
360    try {
361      const result = await api.updateEmail(newEmail.trim());
362      if (result.success) {
363        setEmail(newEmail.trim());
364        setFlowState("verify-pending");
365      }
366    } catch (err) {
367      setError(err.message || "Failed to update email.");
368    } finally {
369      setLoading(false);
370    }
371  };
372  const handleBack = () => {
373    setFlowState("initial");
374    setPassword("");
375    setName("");
376    setError(null);
377    setSuccessMsg(null);
378    setCapsLockOn(false);
379    setSelectedAvatar(null);
380  };
381  const GoogleIcon = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { role: "img", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", children: [
382    /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: "Google" }),
383    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12.48 10.92v3.28h7.84c-.24 1.84-.85 3.18-1.73 4.1-1.02 1.02-2.3 1.84-4.32 1.84-5.18 0-9.4-4.22-9.4-9.4s4.22-9.4 9.4-9.4c2.86 0 5.21 1.09 6.84 2.63l2.5-2.5C20.06 2.5 16.73 1 12.48 1 5.83 1 1 5.83 1 12.48s4.83 11.48 11.48 11.48c6.48 0 11.23-4.49 11.23-11.58 0-.79-.07-1.55-.19-2.28z", fill: "currentColor" })
384  ] });
385  const AppleIcon = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { role: "img", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", children: [
386    /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: "Apple" }),
387    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12.15,2.44A5.32,5.32,0,0,0,8.73,4.62a5.45,5.45,0,0,0-2.3,4.24,5.83,5.83,0,0,0,3.52,5.55,4.7,4.7,0,0,0,2.68.85,1.24,1.24,0,0,1,.87,0,4.7,4.7,0,0,0,2.69-.85,5.85,5.85,0,0,0,3.52-5.55,5.4,5.4,0,0,0-2.3-4.24A5.25,5.25,0,0,0,12.15,2.44Zm-1.2,16.5A4.5,4.5,0,0,0,12,18.31a4.34,4.34,0,0,0,1.05.63,2.42,2.42,0,0,1-2.1,0Z", fill: "currentColor" }),
388    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12.15,0A10.83,10.83,0,0,0,5.2,3.71,11.23,11.23,0,0,0,1.21,12a10.45,10.45,0,0,0,4.64,8.87,10,10,0,0,0,6.3,2.83,10.15,10.15,0,0,0,10.63-10.4,10.47,10.47,0,0,0-2.73-7.14A10.81,10.81,0,0,0,12.15,0Z", fill: "currentColor" })
389  ] });
390  const FacebookIcon = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { role: "img", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", children: [
391    /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: "Facebook" }),
392    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z", fill: "currentColor" })
393  ] });
394  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[1000] flex items-center justify-center p-4", children: [
395    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: onClose }),
396    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-sm bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-8 shadow-2xl animate-in zoom-in-95 text-zinc-900 dark:text-white max-h-[90vh] flex flex-col", children: [
397      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "absolute top-4 right-4 p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors text-zinc-500 hover:text-black dark:hover:text-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20 }) }),
398      flowState !== "initial" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleBack, className: "absolute top-4 left-4 p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors text-zinc-500 hover:text-black dark:hover:text-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 20 }) }),
399      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-8 shrink-0", children: [
400        /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-2xl font-bold mb-1", children: [
401          flowState === "initial" && t("auth.signInOrCreate", "Sign in or create account"),
402          flowState === "login" && t("auth.welcome", "Welcome back"),
403          flowState === "register" && t("auth.createAccount", "Create your account"),
404          flowState === "forgot-password" && t("auth.resetPassword", "Reset Password"),
405          flowState === "tos" && t("auth.termsOfService", "Terms of Service"),
406          flowState === "privacy" && t("auth.privacyPolicy", "Privacy Policy"),
407          flowState === "confirm-email" && t("auth.confirmEmail", "Confirm Your Email")
408        ] }),
409        (flowState === "login" || flowState === "register" || flowState === "confirm-email") && /* @__P
409URE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400 break-all", children: email })
410      ] }),
411      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto custom-scrollbar -mx-4 px-4 pt-1", children: [
412        flowState === "initial" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
413          enabledProviders.google && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => handleSocialLogin("google"), disabled: loading, className: "w-full flex items-center justify-center gap-3 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700/80 border border-zinc-200 dark:border-zinc-700 py-3 rounded-full font-medium text-sm transition-all text-zinc-900 dark:text-white disabled:opacity-50", children: [
414            /* @__PURE__ */ jsxRuntimeExports.jsx(GoogleIcon, {}),
415            " ",
416            t("auth.continueGoogle", "Continue with Google")
417          ] }),
418          enabledProviders.facebook && /* @__P
418URE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => handleSocialLogin("facebook"), disabled: loading, className: "w-full flex items-center justify-center gap-3 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700/80 border border-zinc-200 dark:border-zinc-700 py-3 rounded-full font-medium text-sm transition-all text-zinc-900 dark:text-white disabled:opacity-50", children: [
419            /* @__PURE__ */ jsxRuntimeExports.jsx(FacebookIcon, {}),
420            " ",
421            t("auth.continueFacebook", "Continue with Facebook")
422          ] }),
423          enabledProviders.apple && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => handleSocialLogin("apple"), disabled: loading, className: "w-full flex items-center justify-center gap-3 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700/80 border border-zinc-200 dark:border-zinc-700 py-3 rounded-full font-medium text-sm transition-all text-zinc-900 dark:text-white disabled:opacity-50", children: [
424            /* @__PURE__ */ jsxRuntimeExports.jsx(AppleIcon, {}),
425            " ",
426            t("auth.continueApple", "Continue with Apple")
427          ] }),
428          (enabledProviders.google || enabledProviders.facebook || enabledProviders.apple) && /* @__P
428URE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center text-xs text-zinc-500 before:h-px before:flex-1 before:bg-zinc-200 dark:before:bg-zinc-700 before:mr-4 after:h-px after:flex-1 after:bg-zinc-200 dark:after:bg-zinc-700 after:ml-4", children: t("auth.or", "or") }),
429          /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleContinue, children: [
430            error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 dark:text-red-400 text-xs text-center bg-red-100 dark:bg-red-900/20 border border-red-200 dark:border-red-500/20 rounded-md p-2 mb-3", children: error }),
431            /* @__PURE__ */ jsxRuntimeExports.jsx(
432              "input",
433              {
434                ref: emailInputRef,
435                type: "text",
436                required: true,
437                className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-4 py-3 outline-none transition-all text-sm focus:ring-1 focus:ring-indigo-600 mb-3 text-zinc-900 dark:text-white",
438                placeholder: t("auth.emailOnly", "Email Address"),
439                value: email,
440                onChange: (e) => setEmail(e.target.value),
441                autoComplete: "username"
442              }
443            ),
444            /* @__PURE__ */ jsxRuntimeExports.jsx(
445              "button",
446              {
447                type: "submit",
448                disabled: loading,
449                className: "w-full bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all disabled:opacity-50 flex items-center justify-center gap-2 text-white",
450                children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }) : t("auth.continue", "Continue")
451              }
452            ),
453            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center mt-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
454              "button",
455              {
456                type: "button",
457                onClick: () => setFlowState("trouble"),
458                className: "text-xs text-indigo-600 dark:text-indigo-400 hover:underline font-medium",
459                children: t("auth.havingTrouble", "Having trouble logging in?")
460              }
461            ) })
462          ] }),
463          /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 text-center px-4 pt-2", children: [
464            t("auth.termsPrefix", "By continuing, you agree to the"),
465            " ",
466            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setFlowState("tos"), className: "underline hover:text-zinc-800 dark:hover:text-zinc-300", children: t("auth.termsOfService", "Terms of Service") }),
467            " ",
468            t("auth.and", "and"),
469            " ",
470            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setFlowState("privacy"), className: "underline hover:text-zinc-800 dark:hover:text-zinc-300", children: t("auth.privacyPolicy", "Privacy Policy") }),
471            "."
472          ] })
473        ] }),
474        (flowState === "login" || flowState === "register") && /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
475          error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 dark:text-red-400 text-xs text-center bg-red-100 dark:bg-red-900/20 border border-red-200 dark:border-red-500/20 rounded-md p-2", children: error }),
476          flowState === "register" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
477            /* @__PURE__ */ jsxRuntimeExports.jsx(
478              "input",
479              {
480                ref: nameInputRef,
481                type: "text",
482                required: true,
483                maxLength: 50,
484                className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-4 py-3 outline-none transition-all text-sm focus:ring-1 focus:ring-indigo-600 text-zinc-900 dark:text-white",
485                placeholder: t("auth.fullName", "Full Name"),
486                value: name,
487                onChange: (e) => setName(e.target.value),
488                autoComplete: "name"
489              }
490            ),
491            defaultAvatars.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
492              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: t("auth.chooseAvatar", "Choose Avatar") }),
493              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-5 gap-2 max-h-[120px] overflow-y-auto custom-scrollbar p-1", children: defaultAvatars.map((filename) => /* @__PURE__ */ jsxRuntimeExports.jsx(
494                "button",
495                {
496                  type: "button",
497                  onClick: () => setSelectedAvatar(filename),
498                  className: `w-full aspect-square rounded-lg overflow-hidden border-2 transition-all hover:scale-105 ${selectedAvatar === filename ? "border-indigo-500 ring-2 ring-indigo-500/30 scale-105" : "border-zinc-200 dark:border-zinc-700 opacity-70 hover:opacity-100"}`,
499                  children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: `/storage/avatars/defaults/${filename}`, alt: filename, className: "w-full h-full object-cover" })
500                },
501                filename
502              )) })
503            ] })
504          ] }),
505          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
506            /* @__PURE__ */ jsxRuntimeExports.jsx(
507              "input",
508              {
509                ref: passwordInputRef,
510                type: "password",
511                required: true,
512                className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-4 py-3 outline-none transition-all text-sm focus:ring-1 focus:ring-indigo-600 text-zinc-900 dark:text-white",
513                placeholder: t("auth.password", "Password"),
514                value: password,
515                onChange: (e) => setPassword(e.target.value),
516                onKeyUp: checkCapsLock,
517                onKeyDown: checkCapsLock,
518                onClick: checkCapsLock,
519                autoComplete: "off"
520              }
521            ),
522            capsLockOn && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-amber-500 flex items-center gap-1 text-xs font-bold animate-pulse", children: [
523              /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 14 }),
524              " ",
525              t("auth.capsLockOn", "CAPS LOCK IS ON")
526            ] })
527          ] }),
528          /* @__PURE__ */ jsxRuntimeExports.jsx(
529            "button",
530            {
531              type: "submit",
532              disabled: loading,
533              className: "w-full bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all disabled:opacity-50 flex items-center justify-center gap-2 mt-4 text-white",
534              children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }) : flowState === "login" ? t("auth.signInButton", "Sign In") : t("auth.createAccountButton", "Create Account")
535            }
536          ),
537          flowState === "login" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setFlowState("forgot-password"), className: "block w-full text-center text-xs text-indigo-500 dark:text-indigo-400 hover:underline pt-2", children: t("auth.forgotPassword", "Forgot your password?") })
538        ] }),
539        flowState === "forgot-password" && /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleForgotPassword, className: "space-y-6", children: [
540          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
541            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-indigo-50 dark:bg-indigo-900/20 rounded-full flex items-center justify-center mx-auto mb-4 text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { size: 32 }) }),
542            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-600 dark:text-zinc-400 mb-6", children: t("auth.resetPasswordDesc", "Enter your email address and we'll send you a link to reset your password.") })
543          ] }),
544          successMsg ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800 p-4 rounded-lg flex gap-3", children: [
545            /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "text-emerald-500 shrink-0", size: 20 }),
546            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-emerald-800 dark:text-emerald-200", children: successMsg })
547          ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
548            /* @__PURE__ */ jsxRuntimeExports.jsx(
549              "input",
550              {
551                type: "email",
552                required: true,
553                className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-4 py-3 outline-none transition-all text-sm focus:ring-1 focus:ring-indigo-600 text-zinc-900 dark:text-white",
554                placeholder: t("auth.emailPlaceholder", "Email Address"),
555                value: email,
556                onChange: (e) => setEmail(e.target.value)
557              }
558            ),
559            /* @__PURE__ */ jsxRuntimeExports.jsx(
560              "button",
561              {
562                type: "submit",
563                disabled: loading || !email,
564                className: "w-full bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all disabled:opacity-50 flex items-center justify-center gap-2 text-white",
565                children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }) : t("auth.sendResetLink", "Send Reset Link")
566              }
567            )
568          ] })
569        ] }),
570        flowState === "confirm-email" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
571          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
572            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-indigo-50 dark:bg-indigo-900/20 rounded-full flex items-center justify-center mx-auto mb-4 text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { size: 32 }) }),
573            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-600 dark:text-zinc-400 mb-4", children: t("auth.migrationNotice", "To keep your account secure, please confirm your email address. Once verified, you can log in with either your email or username.") })
574          ] }),
575          error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 dark:text-red-400 text-xs text-center bg-red-100 dark:bg-red-900/20 border border-red-200 dark:border-red-500/20 rounded-md p-2 mb-3", children: error }),
576          !isEditingEmail ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
577            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-4", children: [
578              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 uppercase font-bold mb-1", children: t("auth.currentEmail", "Current Email") }),
579              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: email })
580            ] }),
581            /* @__PURE__ */ jsxRuntimeExports.jsx(
582              "button",
583              {
584                onClick: () => setIsEditingEmail(true),
585                className: "w-full text-xs text-indigo-500 hover:underline text-center",
586                children: t("auth.changeEmail", "Use a different email address")
587              }
588            ),
589            /* @__PURE__ */ jsxRuntimeExports.jsx(
590              "button",
591              {
592                onClick: handleEmailUpdate,
593                disabled: loading,
594                className: "w-full bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all text-white disabled:opacity-50",
595                children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto" }) : t("auth.confirmAndSend", "Confirm & Send Verification")
596              }
597            )
598          ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleEmailUpdate, className: "space-y-4", children: [
599            /* @__PURE__ */ jsxRuntimeExports.jsx(
600              "input",
601              {
602                type: "email",
603                required: true,
604                className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-4 py-3 outline-none transition-all text-sm focus:ring-1 focus:ring-indigo-600 text-zinc-900 dark:text-white",
605                placeholder: t("auth.newEmailPlaceholder", "Enter new email address"),
606                value: newEmail,
607                onChange: (e) => setNewEmail(e.target.value)
608              }
609            ),
610            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
611              /* @__PURE__ */ jsxRuntimeExports.jsx(
612                "button",
613                {
614                  type: "button",
615                  onClick: () => {
616                    setIsEditingEmail(false);
617                    setNewEmail(email);
618                  },
619                  className: "flex-1 bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 py-3 rounded-full font-bold text-sm transition-all",
620                  children: t("common.cancel", "Cancel")
621                }
622              ),
623              /* @__PURE__ */ jsxRuntimeExports.jsx(
624                "button",
625                {
626                  type: "submit",
627                  disabled: loading || !newEmail || newEmail === email,
628                  className: "flex-[2] bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all text-white disabled:opacity-50",
629                  children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto" }) : t("auth.updateAndSend", "Update & Send")
630                }
631              )
632            ] })
633          ] })
634        ] }),
635        flowState === "verify-pending" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 text-center animate-in fade-in slide-in-from-bottom-4", children: [
636          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-amber-50 dark:bg-amber-900/20 rounded-full flex items-center justify-center mx-auto mb-4 text-amber-600 dark:text-amber-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { size: 32 }) }),
637          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
638            /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold mb-2", children: "Verify your email" }),
639            /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-zinc-600 dark:text-zinc-400 mb-6", children: [
640              "We've sent a verification link to ",
641              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-zinc-900 dark:text-white", children: email }),
642              ". Please click the link in the email to activate your account."
643            ] })
644          ] }),
645          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
646            successMsg && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800 p-4 rounded-lg flex gap-3 text-left mb-4", children: [
647              /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "text-emerald-500 shrink-0", size: 20 }),
648              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-emerald-800 dark:text-emerald-200", children: successMsg })
649            ] }),
650            /* @__PURE__ */ jsxRuntimeExports.jsx(
651              "button",
652              {
653                onClick: handleResendVerification,
654                disabled: loading || resendCooldown > 0,
655                className: "w-full bg-indigo-600 hover:bg-indigo-700 py-3 rounded-full font-bold text-sm transition-all disabled:opacity-50 flex items-center justify-center gap-2 text-white shadow-lg shadow-indigo-500/20",
656                children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }) : resendCooldown > 0 ? `Resend in ${resendCooldown}s` : "Resend Verification Link"
657              }
658            ),
659            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: "Can't find it? Check your spam folder or try resending." })
660          ] }),
661          /* @__PURE__ */ jsxRuntimeExports.jsx(
662            "button",
663            {
664              onClick: () => setFlowState("initial"),
665              className: "text-sm font-bold text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200 transition-colors pt-4",
666              children: "Back to Sign In"
667            }
668          )
669        ] }),
670        (flowState === "tos" || flowState === "privacy") && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-zinc-600 dark:text-zinc-400 space-y-4", children: flowState === "tos" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
671          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-indigo-600 dark:text-indigo-400 font-bold mb-2", children: [
672            /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 18 }),
673            " ",
674            t("auth.tosTitle", "HamieNET Terms of Service")
675          ] }),
676          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.tosIntro", "Welcome to HamieNET. By using our website, you agree to these Terms. Please read them carefully.") }),
677          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-zinc-900 dark:text-white mt-4", children: t("auth.tosSection1", "1. Use of Service") }),
678          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.tosSection1Desc", "You must follow any policies made available to you within the Service. You may not misuse our Service.") }),
679          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-zinc-900 dark:text-white mt-4", children: t("auth.tosSection2", "2. Your Content") }),
680          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.tosSection2Desc", "Some of our Services allow you to upload, submit, store, send or receive content. You retain ownership of any intellectual property rights that you hold in that content.") }),
681          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-zinc-900 dark:text-white mt-4", children: t("auth.tosSection3", "3. Copyright") }),
682          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.tosSection3Desc", "We respond to notices of alleged copyright infringement and terminate accounts of repeat infringers according to the process set out in the U.S. Digital Millennium Copyright Act.") })
683        ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
684          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-indigo-600 dark:text-indigo-400 font-bold mb-2", children: [
685            /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { size: 18 }),
686            " ",
687            t("auth.privacyPolicy", "Privacy Policy")
688          ] }),
689          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.privacyIntro", "Your privacy is important to us. This policy explains how we collect, use, and protect your information.") }),
690          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-zinc-900 dark:text-white mt-4", children: t("auth.privacySection1", "1. Information We Collect") }),
691          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.privacySection1Desc", "We collect information to provide better services to all our users. This includes basic stuff like which language you speak, to more complex things like which ads you’ll find most useful.") }),
692          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-zinc-900 dark:text-white mt-4", children: t("auth.privacySection2", "2. How We Use Information") }),
693          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("auth.privacySection2Desc", "We use the information we collect from all our services to provide, maintain, protect and improve them, to develop new ones, and to protect HamieNET and our users.") })
694        ] }) }),
695        flowState === "trouble" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
696          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
697            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setFlowState("initial"), className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 18, className: "text-zinc-500" }) }),
698            /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: t("auth.havingTroubleTitle", "Having trouble?") })
699          ] }),
700          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-amber-50 dark:bg-amber-900/20 border border-amber-100 dark:border-amber-800 rounded-xl p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-amber-800 dark:text-amber-200 leading-relaxed", children: [
701            /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Legacy User?" }),
702            " If you had an account on the old HamieNET, we've transitioned to a secure email-login system. You can still use your old username to find your account."
703          ] }) }),
704          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
705            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-xl", children: [
706              /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-2 flex items-center gap-2", children: [
707                /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { size: 16, className: "text-indigo-500" }),
708                t("auth.forgotPassword", "Forgot Password")
709              ] }),
710              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mb-3", children: "If you know your registered email, we can send you a reset link." }),
711              /* @__PURE__ */ jsxRuntimeExports.jsx(
712                "button",
713                {
714                  onClick: () => setFlowState("forgot-password"),
715                  className: "w-full py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold transition-all",
716                  children: t("auth.resetByEmail", "Reset by Email")
717                }
718              )
719            ] }),
720            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-xl", children: [
721              /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-2 flex items-center gap-2", children: [
722                /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 16, className: "text-emerald-500" }),
723                t("auth.lookupHandle", "Account Lookup")
724              ] }),
725              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mb-3", children: "Don't remember your email? Find your account by your old username." }),
726              !lookupResult ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
727                /* @__PURE__ */ jsxRuntimeExports.jsx(
728                  "input",
729                  {
730                    type: "text",
731                    placeholder: t("auth.oldUsername", "Old Username"),
732                    className: "w-full 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-1 focus:ring-indigo-600 text-zinc-900 dark:text-white",
733                    value: lookupValue,
734                    onChange: (e) => setLookupValue(e.target.value)
735                  }
736                ),
737                /* @__PURE__ */ jsxRuntimeExports.jsx(
738                  "button",
739                  {
740                    onClick: async () => {
741                      setLoading(true);
742                      setError(null);
743                      try {
744                        const res = await api.lookupHandle(lookupValue);
745                        if (res.error) setError(res.error);
746                        else setLookupResult(res);
747                      } catch (e) {
748                        setError(e.message);
749                      } finally {
750                        setLoading(false);
751                      }
752                    },
753                    disabled: loading || !lookupValue,
754                    className: "w-full py-2 bg-zinc-900 dark:bg-white text-white dark:text-zinc-900 rounded-lg text-xs font-bold transition-all disabled:opacity-50",
755                    children: loading ? "..." : t("auth.findAccount", "Find Account")
756                  }
757                )
758              ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-100 dark:border-emerald-800 rounded-lg p-3 text-center", children: [
759                /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { size: 24, className: "text-emerald-500 mx-auto mb-2" }),
760                /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-emerald-900 dark:text-emerald-100 font-bold mb-1", children: "Account Found!" }),
761                /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-emerald-700 dark:text-emerald-300", children: [
762                  "Your new handle is ",
763                  /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { children: [
764                    "@",
765                    lookupResult.handle
766                  ] }),
767                  ".",
768                  /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
769                  "Registered email: ",
770                  /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: lookupResult.email })
771                ] }),
772                /* @__PURE__ */ jsxRuntimeExports.jsx(
773                  "button",
774                  {
775                    onClick: () => {
776                      setFlowState("forgot-password");
777                    },
778                    className: "mt-3 text-[10px] text-emerald-600 dark:text-emerald-400 underline font-bold",
779                    children: "Go to password reset"
780                  }
781                )
782              ] }),
783              error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-red-500 mt-2", children: error })
784            ] })
785          ] }),
786          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
787            "button",
788            {
789              onClick: () => setFlowState("tos"),
790              className: "text-[10px] text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline",
791              children: "Still need help? Contact support"
792            }
793          ) })
794        ] })
795      ] })
796    ] })
797  ] });
798};
799function UploadModal({ isOpen, onClose, currentUser, initialFile, initialMetadata }) {
800  var _a, _b, _c;
801  const { t } = useTranslation();
802  const [step, setStep] = reactExports.useState(1);
803  const [file, setFile] = reactExports.useState(null);
804  const [metadata, setMetadata] = reactExports.useState({ title: "", artist: "", description: "", tags: "", genre: "" });
805  const [artistInput, setArtistInput] = reactExports.useState("");
806  const [tagsInput, setTagsInput] = reactExports.useState("");
807  const [batchFiles, setBatchFiles] = reactExports.useState([]);
808  const [globalSettings, setGlobalSettings] = reactExports.useState({ artist: "", genre: "", tags: "" });
809  const [availableGenres, setAvailableGenres] = reactExports.useState([]);
810  const [uploading, setUploading] = reactExports.useState(false);
811  const [validationErrors, setValidationErrors] = reactExports.useState({});
812  const [isValidating, setIsValidating] = reactExports.useState(false);
813  const [filterConfig, setFilterConfig] = reactExports.useState(null);
814  const fileInputRef = reactExports.useRef(null);
815  const folderInputRef = reactExports.useRef(null);
816  const validationTimeoutRef = reactExports.useRef(null);
817  const lastAutofilledFor = reactExports.useRef({ artist: "", title: "" });
818  const [fairUseAgreed, setFairUseAgreed] = reactExports.useState(false);
819  const [isDragging, setIsDragging] = reactExports.useState(false);
820  const [duplicateConflict, setDuplicateConflict] = reactExports.useState(null);
821  const [suggestedArtists, setSuggestedArtists] = reactExports.useState([]);
822  const [canonicalWorks, setCanonicalWorks] = reactExports.useState([]);
823  const [selectedPathParts, setSelectedPathParts] = reactExports.useState([]);
824  const [selectedWorkId, setSelectedWorkId] = reactExports.useState("");
825  const handleDragOver = (e) => {
826    e.preventDefault();
827    setIsDragging(true);
828  };
829  const handleDragLeave = (e) => {
830    e.preventDefault();
831    setIsDragging(false);
832  };
833  const processSelectedFiles = (filesList) => {
834    const validFiles = Array.from(filesList).filter((f) => {
835      var _a2;
836      const ext = (_a2 = f.name.split(".").pop()) == null ? void 0 : _a2.toLowerCase();
837      return ext === "mid" || ext === "midi" || ext === "kar";
838    });
839    if (validFiles.length === 0) {
840      alert(t("upload.invalidType", "Please select a valid MIDI file (.mid, .midi, .kar)"));
841      return;
842    }
843    if (validFiles.length > 10) {
844      alert(t("upload.maxFilesLimit", "Max 10 files allowed"));
845      validFiles.length = 10;
846    }
847    if (validFiles.length === 1) {
848      const f = validFiles[0];
849      setFile(f);
850      setBatchFiles([]);
851      if (f.name) {
852        setMetadata((prev) => ({
853          ...prev,
854          title: prev.title || f.name.replace(".mid", "").replace(".midi", "").replace(".kar", "").replace(/_/g, " ")
855        }));
856      }
857    } else {
858      setFile(null);
859      const batchData = validFiles.map((f) => ({
860        id: Math.random().toString(36).substring(2, 9),
861        file: f,
862        title: f.name.replace(".mid", "").replace(".midi", "").replace(".kar", "").replace(/_/g, " "),
863        artist: "",
864        genre: "",
865        description: "",
866        tags: "",
867        status: "pending"
868      }));
869      setBatchFiles(batchData);
870    }
871    setStep(2);
872  };
873  const handleDrop = (e) => {
874    e.preventDefault();
875    setIsDragging(false);
876    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
877      processSelectedFiles(e.dataTransfer.files);
878    }
879  };
880  reactExports.useEffect(() => {
881    if (isOpen) {
882      api.getTags({ type: "GENRE" }).then((tags) => {
883        const sorted = [...tags].sort((a, b) => a.name.localeCompare(b.name));
884        setAvailableGenres(sorted);
885      });
886      api.getSystemSetting("content_filter_config").then((setting) => {
887        if (setting) setFilterConfig(setting.value);
888      });
889      if (initialFile) {
890        processSelectedFiles([initialFile]);
891        setArtistInput("");
892        setTagsInput("");
893      } else {
894        setStep(1);
895        setFile(null);
896        setBatchFiles([]);
897        setGlobalSettings({ artist: "", genre: "", tags: "" });
898        if (initialMetadata) {
899          setMetadata({
900            title: initialMetadata.title || "",
901            artist: initialMetadata.artist || "",
902            description: "",
903            tags: initialMetadata.tags || "",
904            genre: ""
905          });
906          setArtistInput(initialMetadata.artist || "");
907          setTagsInput(initialMetadata.tags || "");
908          if (initialMetadata.canonicalWorkId) {
909            setSelectedWorkId(initialMetadata.canonicalWorkId);
910          } else {
911            setSelectedWorkId("");
912          }
913        } else {
914          setMetadata({ title: "", artist: "", description: "", tags: "", genre: "" });
915          setArtistInput("");
916          setTagsInput("");
917          setSelectedWorkId("");
918        }
919      }
920      setDuplicateConflict(null);
921      setSuggestedArtists([]);
922    }
923  }, [isOpen, initialFile, initialMetadata]);
924  reactExports.useEffect(() => {
925    if (step === 2 && file && !batchFiles.length) {
926      if (validationTimeoutRef.current) clearTimeout(validationTimeoutRef.current);
927      setIsValidating(true);
928      validationTimeoutRef.current = setTimeout(async () => {
929        try {
930          const res = await api.validateContent({
931            title: metadata.title,
932            artist: metadata.artist,
933            description: metadata.description
934          });
935          const errors = {};
936          Object.entries(res.results).forEach(([key, val]) => {
937            if (!val.valid) errors[key] = val.error || "Invalid content";
938          });
939          setValidationErrors(errors);
940        } catch (e) {
941          console.error("Validation failed", e);
942        } finally {
943          setIsValidating(false);
944        }
945      }, 500);
946    }
947    return () => {
948      if (validationTimeoutRef.current) clearTimeout(validationTimeoutRef.current);
949    };
950  }, [metadata.title, metadata.artist, metadata.description, step, file, batchFiles.length]);
951  reactExports.useEffect(() => {
952    if (step !== 2 || !file || batchFiles.length) return;
953    const timeout = setTimeout(async () => {
954      const currentArtist = (metadata.artist || "").trim();
955      const currentTitle = (metadata.title || "").trim();
956      if (!currentArtist && !currentTitle) return;
957      if (lastAutofilledFor.current.artist === currentArtist && lastAutofilledFor.current.title === currentTitle) return;
958      try {
959        const autoTags = await api.autofillTags(currentArtist, currentTitle);
960        if (autoTags && autoTags.length > 0) {
961          setMetadata((prev) => {
962            const currentTagsList = prev.tags ? prev.tags.split(",").map((t2) => t2.trim()).filter(Boolean) : [];
963            let addedAny = false;
964            for (const t2 of autoTags) {
965              if (!currentTagsList.some((existing) => existing.toLowerCase() === t2.toLowerCase())) {
966                currentTagsList.push(t2);
967                addedAny = true;
968              }
969            }
970            if (addedAny) return { ...prev, tags: currentTagsList.join(", ") };
971            return prev;
972          });
973        }
974      } catch (err) {
975        console.error("Autofill failed", err);
976      } finally {
977        lastAutofilledFor.current = { artist: currentArtist, title: currentTitle };
978      }
979    }, 1e3);
980    return () => clearTimeout(timeout);
981  }, [metadata.artist, metadata.title, step, file, batchFiles.length]);
982  reactExports.useEffect(() => {
983    if (step !== 2 || !file || batchFiles.length) return;
984    const title = (metadata.title || "").trim();
985    if (title.length >= 3) {
986      const timeout = setTimeout(async () => {
987        try {
988          const artists = await api.suggestArtistsByTitle(title);
989          setSuggestedArtists(artists);
990        } catch (err) {
991          console.error("Failed to fetch suggested artists for title", err);
992        }
993      }, 600);
994      return () => clearTimeout(timeout);
995    } else {
996      setSuggestedArtists([]);
997    }
998  }, [metadata.title, step, file, batchFiles.length]);
999  reactExports.useEffect(() => {
1000    if (step !== 2 || !file || batchFiles.length) return;
1001    const artist = (metadata.artist || "").trim();
1002    if (artist.length >= 3) {
1003      const timeout = setTimeout(async () => {
1004        try {
1005          const works = await api.getCanonicalWorksByComposer(artist);
1006          setCanonicalWorks(works);
1007          if (works.length === 0) {
1008            setSelectedPathParts([]);
1009            setSelectedWorkId("");
1010          }
1011        } catch (err) {
1012          console.error("Failed to fetch canonical works for composer", err);
1013          setCanonicalWorks([]);
1014        }
1015      }, 600);
1016      return () => clearTimeout(timeout);
1017    } else {
1018      setCanonicalWorks([]);
1019      setSelectedPathParts([]);
1020      setSelectedWorkId("");
1021    }
1022  }, [metadata.artist, step, file, batchFiles.length]);
1023  if (!isOpen) return null;
1024  const handleFileSelect = (e) => {
1025    if (e.target.files && e.target.files.length > 0) {
1026      processSelectedFiles(e.target.files);
1027    }
1028  };
1029  const handleUploadSingle = async (force = false) => {
1030    if (!file) return;
1031    setUploading(true);
1032    const formData = new FormData();
1033    formData.append("midiFile", file);
1034    formData.append("title", metadata.title);
1035    const finalArtist = metadata.artist.trim() || artistInput.trim();
1036    formData.append("artist", finalArtist);
1037    formData.append("description", metadata.description);
1038    let finalTags = metadata.tags;
1039    if (tagsInput.trim()) {
1040      finalTags = finalTags ? finalTags + "," + tagsInput.trim() : tagsInput.trim();
1041    }
1042    if (metadata.genre) {
1043      finalTags = metadata.genre + (finalTags ? "," + finalTags : "");
1044    }
1045    formData.append("tags", finalTags);
1046    formData.append("userId", currentUser ? currentUser.id.toString() : "-1");
1047    if (force) {
1048      formData.append("overrideDuplicate", "true");
1049    }
1050    if (selectedWorkId) {
1051      formData.append("canonicalWorkId", selectedWorkId);
1052    }
1053    try {
1054      await api.uploadTrack(formData);
1055      setStep(3);
1056      setDuplicateConflict(null);
1057      if (typeof window !== "undefined") {
1058        window.dispatchEvent(new CustomEvent("hamienet:midi_uploaded", {
1059          detail: { canonicalWorkId: selectedWorkId }
1060        }));
1061      }
1062    } catch (e) {
1063      if (e.duplicate || e.message && e.message.includes("similar MIDI file")) {
1064        setDuplicateConflict({
1065          message: e.message || "A highly similar MIDI file already exists.",
1066          existingTracks: e.existingTracks || []
1067        });
1068      } else {
1069        alert(e.message || t("upload.failed", "Upload failed. Please try again."));
1070      }
1071    } finally {
1072      setUploading(false);
1073    }
1074  };
1075  const handleUploadBatch = async () => {
1076    if (batchFiles.length === 0) return;
1077    setUploading(true);
1078    let successCount = 0;
1079    for (let i = 0; i < batchFiles.length; i++) {
1080      const bf = batchFiles[i];
1081      if (bf.status === "success") {
1082        successCount++;
1083        continue;
1084      }
1085      setBatchFiles((prev) => prev.map((p) => p.id === bf.id ? { ...p, status: "uploading" } : p));
1086      const formData = new FormData();
1087      formData.append("midiFile", bf.file);
1088      formData.append("title", bf.title);
1089      formData.append("artist", bf.artist.trim());
1090      formData.append("description", bf.description);
1091      let finalTags = bf.tags;
1092      if (bf.genre) {
1093        finalTags = bf.genre + (finalTags ? "," + finalTags : "");
1094      }
1095      formData.append("tags", finalTags);
1096      formData.append("userId", currentUser ? currentUser.id.toString() : "-1");
1097      if (selectedWorkId) {
1098        formData.append("canonicalWorkId", selectedWorkId);
1099      }
1100      try {
1101        await api.uploadTrack(formData);
1102        setBatchFiles((prev) => prev.map((p) => p.id === bf.id ? { ...p, status: "success" } : p));
1103        successCount++;
1104      } catch (e) {
1105        const errorMsg = e.duplicate ? t("upload.duplicateFound", "Duplicate") : e.message || t("upload.failed", "Error");
1106        setBatchFiles((prev) => prev.map((p) => p.id === bf.id ? { ...p, status: "error", error: errorMsg } : p));
1107      }
1108    }
1109    setUploading(false);
1110    if (successCount > 0 && typeof window !== "undefined") {
1111      window.dispatchEvent(new CustomEvent("hamienet:midi_uploaded", {
1112        detail: { canonicalWorkId: selectedWorkId }
1113      }));
1114    }
1115    if (successCount === batchFiles.length) {
1116      setStep(3);
1117    }
1118  };
1119  const handleApplyGlobal = () => {
1120    setBatchFiles((prev) => prev.map((bf) => ({
1121      ...bf,
1122      artist: globalSettings.artist || bf.artist,
1123      genre: globalSettings.genre || bf.genre,
1124      tags: globalSettings.tags || bf.tags
1125    })));
1126  };
1127  const handleUpdateBatchFile = (id, field, value) => {
1128    setBatchFiles((prev) => prev.map((bf) => bf.id === id ? { ...bf, [field]: value } : bf));
1129  };
1130  const handleRemoveBatchFile = (id) => {
1131    const remaining = batchFiles.filter((bf) => bf.id !== id);
1132    setBatchFiles(remaining);
1133    if (remaining.length === 1) {
1134      const f = remaining[0];
1135      setFile(f.file);
1136      setMetadata((prev) => ({
1137        ...prev,
1138        title: f.title,
1139        artist: f.artist,
1140        genre: f.genre,
1141        description: f.description,
1142        tags: f.tags
1143      }));
1144      setArtistInput(f.artist);
1145      setTagsInput(f.tags);
1146      setBatchFiles([]);
1147    } else if (remaining.length === 0) {
1148      setStep(1);
1149    }
1150  };
1151  const handleClose = () => {
1152    setStep(1);
1153    setFile(null);
1154    setBatchFiles([]);
1155    setMetadata({ title: "", artist: "", description: "", tags: "", genre: "" });
1156    setDuplicateConflict(null);
1157    setArtistInput("");
1158    setTagsInput("");
1159    onClose();
1160  };
1161  const isBatchMode = batchFiles.length > 0;
1162  const modalMaxWidthClass = step === 2 && isBatchMode ? "max-w-6xl" : "max-w-2xl";
1163  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[1000] flex items-center justify-center p-4", children: [
1164    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: handleClose }),
1165    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `relative w-full ${modalMaxWidthClass} bg-white dark:bg-zinc-900 rounded-xl shadow-2xl flex flex-col max-h-[90vh] overflow-hidden animate-in zoom-in-95 transition-all duration-300`, children: [
1166      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center p-4 border-b border-zinc-200 dark:border-zinc-800", children: [
1167        /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: t("upload.modalTitle", "Upload MIDI") }),
1168        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleClose, children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200" }) })
1169      ] }),
1170      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-8 flex-1 overflow-y-auto", children: [
1171        !currentUser && step !== 3 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 flex gap-3 text-amber-800 dark:text-amber-200 text-sm", children: [
1172          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "shrink-0 mt-0.5", size: 18 }),
1173          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1174            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-bold", children: "You are uploading as a Community Contributor (Guest)." }),
1175            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 space-y-1 opacity-90", children: [
1176              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
1177                "• Max file size: ",
1178                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-amber-900 dark:text-amber-100", children: [
1179                  ((_a = filterConfig == null ? void 0 : filterConfig.guestLimits) == null ? void 0 : _a.maxFileSizeKB) || 500,
1180                  "KB"
1181                ] })
1182              ] }),
1183              ((_b = filterConfig == null ? void 0 : filterConfig.guestLimits) == null ? void 0 : _b.blockUrls) !== false && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
1184                "• Links/URLs are ",
1185                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-amber-900 dark:text-amber-100", children: "strictly prohibited" }),
1186                " in metadata."
1187              ] }),
1188              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
1189                "• Daily limit: ",
1190                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-amber-900 dark:text-amber-100", children: [
1191                  ((_c = filterConfig == null ? void 0 : filterConfig.guestLimits) == null ? void 0 : _c.maxUploadsPerDay) || 3,
1192                  " uploads"
1193                ] }),
1194                " per IP."
1195              ] }),
1196              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "pt-2 text-[11px] leading-relaxed italic", children: [
1197                "Guest uploads are wiki-style and can be improved by the community. ",
1198                /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "#", onClick: (e) => {
1199                  e.preventDefault();
1200                  onClose();
1201                }, className: "underline font-bold not-italic", children: "Log in" }),
1202                " to remove these limits and claim ownership."
1203              ] })
1204            ] })
1205          ] })
1206        ] }),
1207        step === 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1208          "div",
1209          {
1210            onDragOver: handleDragOver,
1211            onDragLeave: handleDragLeave,
1212            onDrop: handleDrop,
1213            onClick: () => {
1214              var _a2;
1215              return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1216            },
1217            className: `h-64 border-2 border-dashed rounded-2xl flex flex-col items-center justify-center cursor-pointer transition-colors ${isDragging ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950/20" : "border-zinc-300 dark:border-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800/50"}`,
1218            children: [
1219              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-zinc-100 dark:bg-zinc-800 rounded-full flex items-center justify-center mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 32, className: isDragging ? "text-indigo-500" : "text-zinc-500" }) }),
1220              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-medium text-zinc-900 dark:text-white", children: t("upload.selectFiles", "Select MIDI files to upload") }),
1221              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 mt-2 mb-4", children: t("upload.dragDropSub", "or drag and drop them here") }),
1222              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-amber-600 dark:text-amber-400 font-bold bg-amber-50 dark:bg-amber-900/30 px-3 py-1 rounded-full mb-4", children: t("upload.maxFilesLimit", "Max 10 files allowed") }),
1223              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4 mt-2", onClick: (e) => e.stopPropagation(), children: [
1224                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1225                  "button",
1226                  {
1227                    onClick: () => {
1228                      var _a2;
1229                      return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1230                    },
1231                    className: "px-4 py-2 bg-indigo-600 text-white rounded-lg font-bold text-sm hover:bg-indigo-700 flex items-center gap-2",
1232                    children: [
1233                      /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 16 }),
1234                      " Select Files"
1235                    ]
1236                  }
1237                ),
1238                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1239                  "button",
1240                  {
1241                    onClick: () => {
1242                      var _a2;
1243                      return (_a2 = folderInputRef.current) == null ? void 0 : _a2.click();
1244                    },
1245                    className: "px-4 py-2 bg-zinc-200 dark:bg-zinc-800 text-zinc-900 dark:text-white rounded-lg font-bold text-sm hover:bg-zinc-300 dark:hover:bg-zinc-700 flex items-center gap-2",
1246                    children: [
1247                      /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 16 }),
1248                      " ",
1249                      t("upload.selectFolder", "Select Folder")
1250                    ]
1251                  }
1252                )
1253              ] }),
1254              /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "file", ref: fileInputRef, className: "hidden", accept: ".mid,.midi,.kar", multiple: true, onChange: handleFileSelect }),
1255              /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "file", ref: folderInputRef, className: "hidden", accept: ".mid,.midi,.kar", webkitdirectory: "", directory: "", multiple: true, onChange: handleFileSelect })
1256            ]
1257          }
1258        ),
1259        step === 2 && !isBatchMode && !duplicateConflict && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
1260          /* @__PURE__ */ jsxRuntimeExports.jsxs(
1261            "div",
1262            {
1263              onDragOver: handleDragOver,
1264              onDragLeave: handleDragLeave,
1265              onDrop: handleDrop,
1266              onClick: () => {
1267                var _a2;
1268                return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1269              },
1270              className: `flex items-center gap-3 p-4 rounded-lg border-2 border-dashed transition-all cursor-pointer ${isDragging ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950/20" : "border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 hover:bg-zinc-100 dark:hover:bg-zinc-700"}`,
1271              children: [
1272                /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { className: file ? "text-indigo-500" : "text-zinc-400" }),
1273                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-sm flex-1 truncate text-zinc-900 dark:text-white", children: file ? file.name : t("common.selectFile", "Select a file") }),
1274                /* @__PURE__ */ jsxRuntimeExports.jsx(
1275                  "button",
1276                  {
1277                    type: "button",
1278                    onClick: (e) => {
1279                      var _a2;
1280                      e.stopPropagation();
1281                      if (file) {
1282                        setStep(1);
1283                      } else {
1284                        (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1285                      }
1286                    },
1287                    className: `text-xs font-bold hover:underline ${file ? "text-indigo-500" : "text-indigo-500"}`,
1288                    children: file ? t("common.change", "Change") : t("common.selectFile", "Select a file")
1289                  }
1290                )
1291              ]
1292            }
1293          ),
1294          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
1295            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1296              /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium mb-1 text-zinc-900 dark:text-white", children: [
1297                t("upload.fields.title", "Title"),
1298                " ",
1299                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
1300              ] }),
1301              /* @__PURE__ */ jsxRuntimeExports.jsx(
1302                "input",
1303                {
1304                  type: "text",
1305                  maxLength: 200,
1306                  value: metadata.title,
1307                  onChange: (e) => setMetadata({ ...metadata, title: e.target.value }),
1308                  className: `w-full bg-zinc-50 dark:bg-zinc-800 border ${validationErrors.title ? "border-red-500" : "border-zinc-300 dark:border-zinc-700"} rounded p-2 text-zinc-900 dark:text-white`
1309                }
1310              ),
1311              validationErrors.title && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-red-500 mt-0.5", children: validationErrors.title })
1312            ] }),
1313            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1314              /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium mb-1 text-zinc-900 dark:text-white", children: [
1315                t("upload.fields.genre", "Primary Genre"),
1316                " ",
1317                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
1318              ] }),
1319              /* @__PURE__ */ jsxRuntimeExports.jsxs(
1320                "select",
1321                {
1322                  value: metadata.genre,
1323                  onChange: (e) => setMetadata({ ...metadata, genre: e.target.value }),
1324                  className: "w-full bg-zinc-50 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded p-2 text-zinc-900 dark:text-white",
1325                  children: [
1326                    /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: t("upload.fields.selectGenre", "-- Select Genre --") }),
1327                    availableGenres.map((g) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: g.name, children: g.name }, g.id))
1328                  ]
1329                }
1330              )
1331            ] })
1332          ] }),
1333          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1334            /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium mb-1 text-zinc-900 dark:text-white", children: [
1335              t("upload.fields.artist", "Composer/Artist"),
1336              " ",
1337              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
1338            ] }),
1339            /* @__PURE__ */ jsxRuntimeExports.jsx(
1340              TagAutocomplete,
1341              {
1342                value: metadata.artist,
1343                onChange: (val) => setMetadata({ ...metadata, artist: val }),
1344                inputValue: artistInput,
1345                onInputChange: setArtistInput,
1346                suggestedArtists,
1347                onSelectSuggestion: (s) => {
1348                  if (s.source === "MusicBrainz" || s.type === "GENRE") {
1349                    const match = availableGenres.find((g) => g.name.toLowerCase() === s.name.toLowerCase());
1350                    if (match) {
1351                      if (!metadata.genre) {
1352                        setMetadata((prev) => ({ ...prev, genre: match.name }));
1353                      }
1354                    } else {
1355                      setMetadata((prev) => {
1356                        const currentTags = prev.tags ? prev.tags.split(",").map((t2) => t2.trim()).filter(Boolean) : [];
1357                        if (!currentTags.some((t2) => t2.toLowerCase() === s.name.toLowerCase())) {
1358                          currentTags.push(s.name);
1359                          return { ...prev, tags: currentTags.join(", ") };
1360                        }
1361                        return prev;
1362                      });
1363                    }
1364                  }
1365                },
1366                placeholder: "Andrew Lloyd Webber, Joe Hisaishi...",
1367                maxLength: 100,
1368                className: validationErrors.artist ? "border-red-500" : ""
1369              }
1370            ),
1371            validationErrors.artist && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-red-500 mt-0.5", children: validationErrors.artist })
1372          ] }),
1373          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1374            /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium mb-1 text-zinc-900 dark:text-white", children: t("upload.fields.description", "Description") }),
1375            /* @__PURE__ */ jsxRuntimeExports.jsx(
1376              "textarea",
1377              {
1378                value: metadata.description,
1379                onChange: (e) => setMetadata({ ...metadata, description: e.target.value }),
1380                className: `w-full bg-zinc-50 dark:bg-zinc-800 border ${validationErrors.description ? "border-red-500" : "border-zinc-300 dark:border-zinc-700"} rounded p-2 h-24 text-zinc-900 dark:text-white`
1381              }
1382            ),
1383            validationErrors.description && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-red-500 mt-0.5", children: validationErrors.description })
1384          ] }),
1385          canonicalWorks.length > 0 && (() => {
1386            const levelDropdowns = [];
1387            for (let i = 0; i <= selectedPathParts.length; i++) {
1388              const currentPrefix = selectedPathParts.slice(0, i).join(" > ");
1389              const parts = /* @__PURE__ */ new Set();
1390              canonicalWorks.forEach((w) => {
1391                const group = (w.groupName || "Other").replace(/ > /g, ">").replace(/>/g, " > ");
1392                const groupParts = group.split(" > ").map((s) => s.trim());
1393                if (groupParts.length > i) {
1394                  if (i === 0 || groupParts.slice(0, i).join(" > ") === currentPrefix) {
1395                    parts.add(groupParts[i]);
1396                  }
1397                }
1398              });
1399              const options = Array.from(parts).sort();
1400              if (options.length > 0) {
1401                levelDropdowns.push({
1402                  level: i,
1403                  options,
1404                  selectedValue: selectedPathParts[i] || ""
1405                });
1406              } else {
1407                break;
1408              }
1409            }
1410            const currentFullGroup = selectedPathParts.join(" > ");
1411            const availableWorks = canonicalWorks.filter((w) => {
1412              const group = (w.groupName || "Other").replace(/ > /g, ">").replace(/>/g, " > ");
1413              return group === currentFullGroup;
1414            });
1415            return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-50/50 dark:bg-indigo-950/20 border border-indigo-100 dark:border-indigo-900/50 rounded-lg p-4 space-y-4", children: [
1416              /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-indigo-900 dark:text-indigo-200 text-sm flex items-center gap-2", children: [
1417                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "🏛️" }),
1418                " Classical Work Binding (Optional)"
1419              ] }),
1420              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [
1421                levelDropdowns.map((dropdown, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1422                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold mb-1 text-indigo-800 dark:text-indigo-300", children: idx === 0 ? "Type of Work" : "Sub-Category" }),
1423                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
1424                    "select",
1425                    {
1426                      value: dropdown.selectedValue,
1427                      onChange: (e) => {
1428                        const newParts = selectedPathParts.slice(0, idx);
1429                        if (e.target.value) {
1430                          newParts.push(e.target.value);
1431                        }
1432                        setSelectedPathParts(newParts);
1433                        setSelectedWorkId("");
1434                        setMetadata((m) => ({ ...m, sectionSongId: void 0 }));
1435                      },
1436                      className: "w-full bg-white dark:bg-zinc-800 border border-indigo-200 dark:border-indigo-800 rounded p-2 text-sm text-zinc-900 dark:text-white",
1437                      children: [
1438                        /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "-- Select --" }),
1439                        dropdown.options.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: opt, children: opt }, opt))
1440                      ]
1441                    }
1442                  )
1443                ] }, idx)),
1444                availableWorks.length > 0 && selectedPathParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-indigo-100 dark:border-indigo-900/50", children: [
1445                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold mb-1 text-indigo-800 dark:text-indigo-300", children: "Work" }),
1446                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
1447                    "select",
1448                    {
1449                      value: selectedWorkId,
1450                      onChange: (e) => setSelectedWorkId(e.target.value),
1451                      className: "w-full bg-white dark:bg-zinc-800 border border-indigo-200 dark:border-indigo-800 rounded p-2 text-sm text-zinc-900 dark:text-white",
1452                      children: [
1453                        /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "-- Select Work --" }),
1454                        availableWorks.map((w) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: w.id, children: w.title }, w.id))
1455                      ]
1456                    }
1457                  )
1458                ] })
1459              ] })
1460            ] });
1461          })(),
1462          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1463            /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium mb-1 text-zinc-900 dark:text-white", children: t("upload.fields.tags", "Additional Tags") }),
1464            /* @__PURE__ */ jsxRuntimeExports.jsx(
1465              TagAutocomplete,
1466              {
1467                value: metadata.tags,
1468                onChange: (val) => setMetadata({ ...metadata, tags: val }),
1469                inputValue: tagsInput,
1470                onInputChange: setTagsInput,
1471                placeholder: "piano, virtuoso, 8-bit"
1472              }
1473            )
1474          ] }),
1475          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 p-3 bg-zinc-100 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-700 flex items-start gap-3", children: [
1476            /* @__PURE__ */ jsxRuntimeExports.jsx(
1477              "input",
1478              {
1479                type: "checkbox",
1480                id: "fairUseAgree",
1481                checked: fairUseAgreed,
1482                onChange: (e) => setFairUseAgreed(e.target.checked),
1483                className: "mt-1 w-4 h-4 rounded text-indigo-600 focus:ring-indigo-500 bg-white dark:bg-zinc-900 border-zinc-300 dark:border-zinc-600 cursor-pointer"
1484              }
1485            ),
1486            /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "fairUseAgree", className: "text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed cursor-pointer select-none", children: [
1487              /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-zinc-200", children: "Fair Use & Educational Notice:" }),
1488              " I claim this upload is for non-profit musicological research, study, or educational purposes. I automatically authorize HamieNET to issue a DMCA Counter-Notice on my behalf against any takedown attempts."
1489            ] })
1490          ] })
1491        ] }),
1492        step === 2 && isBatchMode && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1493          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-50 dark:bg-indigo-950/30 border border-indigo-100 dark:border-indigo-900/50 rounded-lg p-4", children: [
1494            /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-indigo-900 dark:text-indigo-200 mb-3 text-sm flex items-center gap-2", children: [
1495              /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 16 }),
1496              t("upload.globalSettings", "Global Settings (Apply to All)")
1497            ] }),
1498            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [
1499              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1500                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold mb-1 text-indigo-800 dark:text-indigo-300", children: t("upload.fields.genre", "Primary Genre") }),
1501                /* @__PURE__ */ jsxRuntimeExports.jsxs(
1502                  "select",
1503                  {
1504                    value: globalSettings.genre,
1505                    onChange: (e) => setGlobalSettings({ ...globalSettings, genre: e.target.value }),
1506                    className: "w-full bg-white dark:bg-zinc-800 border border-indigo-200 dark:border-indigo-800 rounded p-1.5 text-sm",
1507                    children: [
1508                      /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: "", children: [
1509                        "-- ",
1510                        t("common.optional", "Optional"),
1511                        " --"
1512                      ] }),
1513                      availableGenres.map((g) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: g.name, children: g.name }, g.id))
1514                    ]
1515                  }
1516                )
1517              ] }),
1518              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1519                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold mb-1 text-indigo-800 dark:text-indigo-300", children: t("upload.fields.artist", "Composer/Artist") }),
1520                /* @__PURE__ */ jsxRuntimeExports.jsx(
1521                  "input",
1522                  {
1523                    type: "text",
1524                    value: globalSettings.artist,
1525                    onChange: (e) => setGlobalSettings({ ...globalSettings, artist: e.target.value }),
1526                    className: "w-full bg-white dark:bg-zinc-800 border border-indigo-200 dark:border-indigo-800 rounded p-1.5 text-sm",
1527                    placeholder: "Set artist for all..."
1528                  }
1529                )
1530              ] }),
1531              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1532                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold mb-1 text-indigo-800 dark:text-indigo-300", children: t("upload.fields.tags", "Tags") }),
1533                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1534                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1535                    "input",
1536                    {
1537                      type: "text",
1538                      value: globalSettings.tags,
1539                      onChange: (e) => setGlobalSettings({ ...globalSettings, tags: e.target.value }),
1540                      className: "w-full bg-white dark:bg-zinc-800 border border-indigo-200 dark:border-indigo-800 rounded p-1.5 text-sm",
1541                      placeholder: "Set tags for all..."
1542                    }
1543                  ),
1544                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1545                    "button",
1546                    {
1547                      onClick: handleApplyGlobal,
1548                      className: "px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded font-bold text-xs whitespace-nowrap",
1549                      children: t("upload.applyToAll", "Apply to All")
1550                    }
1551                  )
1552                ] })
1553              ] })
1554            ] })
1555          ] }),
1556          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-zinc-200 dark:border-zinc-800 rounded-lg overflow-x-auto shadow-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left text-sm", children: [
1557            /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-zinc-100 dark:bg-zinc-800/80 text-zinc-600 dark:text-zinc-400 font-medium", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
1558              /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-48", children: "File" }),
1559              /* @__PURE__ */ jsxRuntimeExports.jsxs("th", { className: "p-3 w-48", children: [
1560                t("upload.fields.title", "Title"),
1561                "*"
1562              ] }),
1563              /* @__PURE__ */ jsxRuntimeExports.jsxs("th", { className: "p-3 w-48", children: [
1564                t("upload.fields.artist", "Artist"),
1565                "*"
1566              ] }),
1567              /* @__PURE__ */ jsxRuntimeExports.jsxs("th", { className: "p-3 w-32", children: [
1568                t("upload.fields.genre", "Genre"),
1569                "*"
1570              ] }),
1571              /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-32", children: t("upload.fields.tags", "Tags") }),
1572              /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-16 text-center", children: "Status" })
1573            ] }) }),
1574            /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800", children: batchFiles.map((bf) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: bf.status === "success" ? "bg-emerald-50 dark:bg-emerald-900/10" : bf.status === "error" ? "bg-red-50 dark:bg-red-900/10" : "bg-white dark:bg-zinc-900", children: [
1575              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between group", children: [
1576                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs truncate max-w-[140px]", title: bf.file.name, children: bf.file.name }),
1577                bf.status !== "uploading" && bf.status !== "success" && /* @__PURE__ */ jsxRuntimeExports.jsx(
1578                  "button",
1579                  {
1580                    onClick: () => handleRemoveBatchFile(bf.id),
1581                    className: "text-zinc-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity p-1",
1582                    title: t("upload.remove", "Remove"),
1583                    children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 })
1584                  }
1585                )
1586              ] }) }),
1587              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1588                "input",
1589                {
1590                  type: "text",
1591                  value: bf.title,
1592                  onChange: (e) => handleUpdateBatchFile(bf.id, "title", e.target.value),
1593                  className: `w-full bg-zinc-50 dark:bg-zinc-800 border ${!bf.title.trim() ? "border-red-400" : "border-zinc-300 dark:border-zinc-700"} rounded p-1.5 text-xs`,
1594                  disabled: bf.status === "uploading" || bf.status === "success"
1595                }
1596              ) }),
1597              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1598                "input",
1599                {
1600                  type: "text",
1601                  value: bf.artist,
1602                  onChange: (e) => handleUpdateBatchFile(bf.id, "artist", e.target.value),
1603                  className: `w-full bg-zinc-50 dark:bg-zinc-800 border ${!bf.artist.trim() ? "border-red-400" : "border-zinc-300 dark:border-zinc-700"} rounded p-1.5 text-xs`,
1604                  disabled: bf.status === "uploading" || bf.status === "success",
1605                  placeholder: "Composer..."
1606                }
1607              ) }),
1608              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1609                "select",
1610                {
1611                  value: bf.genre,
1612                  onChange: (e) => handleUpdateBatchFile(bf.id, "genre", e.target.value),
1613                  className: `w-full bg-zinc-50 dark:bg-zinc-800 border ${!bf.genre ? "border-red-400" : "border-zinc-300 dark:border-zinc-700"} rounded p-1.5 text-xs`,
1614                  disabled: bf.status === "uploading" || bf.status === "success",
1615                  children: [
1616                    /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "-" }),
1617                    availableGenres.map((g) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: g.name, children: g.name }, g.id))
1618                  ]
1619                }
1620              ) }),
1621              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1622                "input",
1623                {
1624                  type: "text",
1625                  value: bf.tags,
1626                  onChange: (e) => handleUpdateBatchFile(bf.id, "tags", e.target.value),
1627                  className: "w-full bg-zinc-50 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded p-1.5 text-xs",
1628                  disabled: bf.status === "uploading" || bf.status === "success"
1629                }
1630              ) }),
1631              /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-2 align-top text-center", children: [
1632                bf.status === "pending" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-500", children: t("upload.statusPending", "Pending") }),
1633                bf.status === "uploading" && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "animate-spin text-indigo-500 mx-auto", size: 16 }),
1634                bf.status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "text-emerald-500 mx-auto", size: 16 }),
1635                bf.status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-red-500 block truncate", title: bf.error, children: bf.error })
1636              ] })
1637            ] }, bf.id)) })
1638          ] }) }),
1639          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 p-3 bg-zinc-100 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-700 flex items-start gap-3", children: [
1640            /* @__PURE__ */ jsxRuntimeExports.jsx(
1641              "input",
1642              {
1643                type: "checkbox",
1644                id: "fairUseAgreeBatch",
1645                checked: fairUseAgreed,
1646                onChange: (e) => setFairUseAgreed(e.target.checked),
1647                className: "mt-1 w-4 h-4 rounded text-indigo-600 focus:ring-indigo-500 bg-white dark:bg-zinc-900 border-zinc-300 dark:border-zinc-600 cursor-pointer"
1648              }
1649            ),
1650            /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "fairUseAgreeBatch", className: "text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed cursor-pointer select-none", children: [
1651              /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-zinc-200", children: "Fair Use & Educational Notice:" }),
1652              " I claim these uploads are for non-profit musicological research, study, or educational purposes. I automatically authorize HamieNET to issue a DMCA Counter-Notice on my behalf against any takedown attempts."
1653            ] })
1654          ] })
1655        ] }),
1656        step === 2 && !isBatchMode && duplicateConflict && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 text-center py-6", children: [
1657          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-amber-50 dark:bg-amber-950/20 rounded-full flex items-center justify-center mx-auto mb-2 text-amber-500 animate-bounce", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 32 }) }),
1658          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: "Duplicate MIDI Detected" }),
1659          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-505 dark:text-zinc-400 text-sm max-w-md mx-auto", children: duplicateConflict.message }),
1660          duplicateConflict.existingTracks && duplicateConflict.existingTracks.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800 p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 text-left max-w-md mx-auto space-y-2", children: [
1661            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-bold block uppercase tracking-wider", children: "Existing Match" }),
1662            duplicateConflict.existingTracks.map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-semibold text-zinc-900 dark:text-white flex items-center justify-between", children: [
1663              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1664                t2.title,
1665                " ",
1666                t2.artist ? `by ${t2.artist}` : ""
1667              ] }),
1668              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-400 font-mono", children: [
1669                "ID: ",
1670                t2.id
1671              ] })
1672            ] }, t2.id))
1673          ] }),
1674          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 dark:text-zinc-500 max-w-sm mx-auto leading-relaxed", children: "You can choose to publish this upload as an educational/variation group variant of the existing file." }),
1675          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 justify-center pt-4", children: [
1676            /* @__PURE__ */ jsxRuntimeExports.jsx(
1677              "button",
1678              {
1679                onClick: () => setDuplicateConflict(null),
1680                className: "px-5 py-2 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded-lg font-bold text-zinc-900 dark:text-white text-sm",
1681                children: "Cancel & Edit"
1682              }
1683            ),
1684            /* @__PURE__ */ jsxRuntimeExports.jsx(
1685              "button",
1686              {
1687                onClick: () => handleUploadSingle(true),
1688                disabled: uploading,
1689                className: "px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold text-sm disabled:opacity-50",
1690                children: uploading ? "Uploading..." : "Publish as Variation"
1691              }
1692            )
1693          ] })
1694        ] }),
1695        step === 3 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-10", children: [
1696          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { size: 64, className: "mx-auto text-emerald-500 mb-4" }),
1697          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-2xl font-bold text-zinc-900 dark:text-white", children: t("upload.complete", "Upload Complete!") }),
1698          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 mt-2", children: isBatchMode ? `Successfully uploaded ${batchFiles.filter((b) => b.status === "success").length} out of ${batchFiles.length} files.` : t("upload.successMsg", "Your MIDI is now processing. It will appear on your channel shortly.") })
1699        ] })
1700      ] }),
1701      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900 flex justify-end gap-3", children: [
1702        step === 2 && !duplicateConflict && (isBatchMode ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1703          "button",
1704          {
1705            disabled: uploading || !fairUseAgreed || batchFiles.some((bf) => !bf.title.trim() || !bf.artist.trim() || !bf.genre),
1706            onClick: handleUploadBatch,
1707            className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold disabled:opacity-50 flex items-center gap-2 transition-opacity",
1708            children: uploading ? t("upload.uploading", "Uploading...") : t("upload.uploadAll", "Upload All")
1709          }
1710        ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
1711          "button",
1712          {
1713            disabled: !file || uploading || isValidating || Object.keys(validationErrors).length > 0 || !fairUseAgreed || !metadata.title.trim() || !(metadata.artist.trim() || artistInput.trim()) || !metadata.genre,
1714            onClick: handleUploadSingle,
1715            className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold disabled:opacity-50 flex items-center gap-2 transition-opacity",
1716            children: uploading ? t("upload.uploading", "Uploading...") : isValidating ? t("common.validating", "Validating...") : t("upload.publish", "Publish")
1717          }
1718        )),
1719        (step === 3 || step === 1 || step === 2 && duplicateConflict) && /* @__P
1719URE__ */ jsxRuntimeExports.jsx("button", { onClick: handleClose, className: "px-6 py-2 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded-lg font-bold text-zinc-900 dark:text-white", children: t("common.close", "Close") })
1720      ] })
1721    ] })
1722  ] });
1723}
1724const PlayerContext = reactExports.createContext(void 0);
1725const PlayerProvider = ({ children }) => {
1726  const [activeTrack, setActiveTrack] = reactExports.useState(null);
1727  const [isPlaying, setIsPlaying] = reactExports.useState(false);
1728  const [currentTime, setCurrentTime] = reactExports.useState(0);
1729  const [duration, setDuration] = reactExports.useState(0);
1730  const [queue, setQueue] = reactExports.useState([]);
1731  const [activePlaylist, setActivePlaylist] = reactExports.useState(null);
1732  const [playlistLabel, setPlaylistLabel] = reactExports.useState(null);
1733  const [seekRequest, setSeekRequest] = reactExports.useState(null);
1734  const [audioMode, setAudioMode] = reactExports.useState("wasmsynth");
1735  const [engineOrder, setEngineOrder] = reactExports.useState(null);
1736  const [isBuffering, setIsBuffering] = reactExports.useState(false);
1737  const [trackStates, setTrackStates] = reactExports.useState({});
1738  const [isLiveSynthFallback, setIsLiveSynthFallback] = reactExports.useState(false);
1739  const [soundfont, setSoundfont] = reactExports.useState("MusyngKite");
1740  const [hasStartedLoading, setHasStartedLoading] = reactExports.useState(false);
1741  reactExports.useEffect(() => {
1742    const fetchEngineOrder = async () => {
1743      try {
1744        const res = await api.getSoundfonts();
1745        if (res && res.engineOrder) {
1746          setEngineOrder(res.engineOrder);
1747        }
1748      } catch (err) {
1749        console.warn("Failed to fetch engine order in PlayerContext:", err);
1750      }
1751    };
1752    fetchEngineOrder();
1753  }, []);
1754  const isSynthReady = reactExports.useMemo(() => {
1755    if (!hasStartedLoading) return false;
1756    const isLiveMode = isLiveSynthFallback || ["synth", "webmidi", "chiptune", "wasmsynth", "fmsynth"].includes(audioMode);
1757    if (isLiveMode) {
1758      const keys = Object.keys(trackStates);
1759      if (keys.length === 0) return false;
1760      return keys.every((key) => {
1761        const status = trackStates[parseInt(key)];
1762        return status && (status.ready || status.error);
1763      });
1764    }
1765    if (!(activeTrack == null ? void 0 : activeTrack.instruments) || activeTrack.instruments.length === 0) return true;
1766    const allLoaded = activeTrack.instruments.every((inst, idx) => {
1767      if (!inst.stemPath) return true;
1768      const status = trackStates[idx];
1769      return status && (status.ready || status.error);
1770    });
1771    return allLoaded;
1772  }, [trackStates, activeTrack, hasStartedLoading, isLiveSynthFallback, audioMode]);
1773  const togglePlay = reactExports.useCallback(() => {
1774    setIsPlaying((prev) => !prev);
1775  }, [setIsPlaying]);
1776  const playTrack = reactExports.useCallback((track, playlist) => {
1777    if (playlist !== void 0) {
1778      setActivePlaylist(playlist);
1779    }
1780    setActiveTrack((prevActiveTrack) => {
1781      if ((prevActiveTrack == null ? void 0 : prevActiveTrack.id) === track.id) {
1782        setIsPlaying((prevIsPlaying) => !prevIsPlaying);
1783        return prevActiveTrack;
1784      } else {
1785        setIsPlaying(true);
1786        return track;
1787      }
1788    });
1789  }, [setActiveTrack, setIsPlaying, setActivePlaylist]);
1790  const seekTo = reactExports.useCallback((time) => {
1791    setSeekRequest({ time, timestamp: Date.now() });
1792    setCurrentTime(time);
1793  }, [setSeekRequest, setCurrentTime]);
1794  const addToQueue = reactExports.useCallback((track) => {
1795    setQueue((prev) => {
1796      if (!prev.find((t) => t.id === track.id)) {
1797        return [...prev, track];
1798      }
1799      return prev;
1800    });
1801  }, [setQueue]);
1802  const value = reactExports.useMemo(() => ({
1803    activeTrack,
1804    setActiveTrack,
1805    isPlaying,
1806    setIsPlaying,
1807    currentTime,
1808    setCurrentTime,
1809    duration,
1810    setDuration,
1811    queue,
1812    setQueue,
1813    activePlaylist,
1814    setActivePlaylist,
1815    playlistLabel,
1816    setPlaylistLabel,
1817    seekRequest,
1818    setSeekRequest,
1819    togglePlay,
1820    playTrack,
1821    seekTo,
1822    addToQueue,
1823    audioMode,
1824    setAudioMode,
1825    engineOrder,
1826    setEngineOrder,
1827    isBuffering,
1828    setIsBuffering,
1829    trackStates,
1830    setTrackStates,
1831    isLiveSynthFallback,
1832    setIsLiveSynthFallback,
1833    isSynthReady,
1834    soundfont,
1835    setSoundfont,
1836    hasStartedLoading,
1837    setHasStartedLoading
1838  }), [
1839    activeTrack,
1840    setActiveTrack,
1841    isPlaying,
1842    setIsPlaying,
1843    currentTime,
1844    setCurrentTime,
1845    duration,
1846    setDuration,
1847    queue,
1848    setQueue,
1849    activePlaylist,
1850    setActivePlaylist,
1851    seekRequest,
1852    setSeekRequest,
1853    togglePlay,
1854    playTrack,
1855    seekTo,
1856    addToQueue,
1857    audioMode,
1858    engineOrder,
1859    isBuffering,
1860    trackStates,
1861    isLiveSynthFallback,
1862    isSynthReady,
1863    soundfont,
1864    hasStartedLoading
1865  ]);
1866  return /* @__PURE__ */ jsxRuntimeExports.jsx(PlayerContext.Provider, { value, children });
1867};
1868const usePlayer = () => {
1869  const context = reactExports.useContext(PlayerContext);
1870  if (context === void 0) {
1871    throw new Error("usePlayer must be used within a PlayerProvider");
1872  }
1873  return context;
1874};
1875function formatRelativeTime$1(date) {
1876  const now = Date.now();
1877  const then = new Date(date).getTime();
1878  const diff = Math.floor((now - then) / 1e3);
1879  if (diff < 60) return "just now";
1880  if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
1881  if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
1882  if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`;
1883  return new Date(date).toLocaleDateString();
1884}
1885const getIcon$1 = (type) => {
1886  switch (type) {
1887    case "upload":
1888      return /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 14, className: "text-zinc-600 dark:text-white" });
1889    case "like":
1890      return /* @__PURE__ */ jsxRuntimeExports.jsx(Heart, { size: 14, className: "text-red-500 fill-red-500" });
1891    case "reply":
1892      return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { size: 14, className: "text-blue-500 dark:text-blue-400 fill-blue-500 dark:fill-blue-400" });
1893    case "subscriber":
1894      return /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 14, className: "text-emerald-500 dark:text-emerald-400" });
1895    default:
1896      return /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 14, className: "text-zinc-400" });
1897  }
1898};
1899const NotificationsDropdown = ({ notifications, onMarkAsRead, onMarkAllRead, onSeeAll, onClose, onNavigate }) => {
1900  const { t } = useTranslation();
1901  const unreadCount = notifications.filter((n) => !n.isRead).length;
1902  if (notifications.length === 0) {
1903    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-12 right-0 w-96 bg-white dark:bg-[#212121] border border-zinc-200 dark:border-zinc-700 rounded-xl shadow-2xl z-50 p-6 text-center animate-in fade-in slide-in-from-top-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm", children: t("notifications.empty", "No notifications here.") }) });
1904  }
1905  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-12 right-0 w-96 bg-white dark:bg-[#212121] border border-zinc-200 dark:border-zinc-700 rounded-xl shadow-2xl z-50 flex flex-col animate-in fade-in slide-in-from-top-2 custom-scrollbar", children: [
1906    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-3 px-4 border-b border-zinc-200 dark:border-zinc-700 sticky top-0 bg-white dark:bg-[#212121] z-10", children: [
1907      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-medium text-base text-zinc-900 dark:text-white", children: t("notifications.title", "Notifications") }),
1908      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
1909        unreadCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1910          "button",
1911          {
1912            onClick: onMarkAllRead,
1913            className: "flex items-center gap-1 text-xs text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 px-2 py-1 rounded-md hover:bg-indigo-50 dark:hover:bg-indigo-900/20 transition-colors",
1914            title: t("notifications.markAllRead", "Mark all as read"),
1915            children: [
1916              /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCheck, { size: 13 }),
1917              t("notifications.markAllShort", "All read")
1918            ]
1919          }
1920        ),
1921        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-700 rounded-full text-zinc-500 hover:text-black dark:text-zinc-400 dark:hover:text-white transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 18 }) })
1922      ] })
1923    ] }),
1924    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col py-2", children: notifications.map((note) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1925      "div",
1926      {
1927        onClick: () => {
1928          if (!note.isRead) onMarkAsRead(note.id);
1929          if (note.link && onNavigate) {
1930            onNavigate(note.link);
1931            onClose();
1932          }
1933        },
1934        className: `flex gap-3 px-4 py-3 hover:bg-zinc-100 dark:hover:bg-zinc-700/50 cursor-pointer transition-colors relative group ${!note.isRead ? "bg-indigo-50 dark:bg-indigo-900/10" : ""}`,
1935        children: [
1936          !note.isRead && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-1 top-1/2 -translate-y-1/2 w-1.5 h-1.5 bg-blue-500 rounded-full" }),
1937          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", children: [
1938            /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: note.actor.avatar, name: note.actor.name, size: 40 }),
1939            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -bottom-1 -right-1 p-1 bg-white dark:bg-[#212121] rounded-full ring-2 ring-white dark:ring-[#212121]", children: getIcon$1(note.type) })
1940          ] }),
1941          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
1942            /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm leading-snug text-zinc-900 dark:text-white", children: [
1943              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold mr-1", children: note.actor.name }),
1944              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600 dark:text-zinc-300", dangerouslySetInnerHTML: { __html: note.message } })
1945            ] }),
1946            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-500 mt-1 block", children: formatRelativeTime$1(note.timestamp) })
1947          ] }),
1948          note.thumbnail ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 w-16 h-10 bg-zinc-200 dark:bg-zinc-800 rounded overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: note.thumbnail, className: "w-full h-full object-cover opacity-80", alt: "" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-10" })
1949        ]
1950      },
1951      note.id
1952    )) }),
1953    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-200 dark:border-zinc-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1954      "button",
1955      {
1956        onClick: () => {
1957          onSeeAll();
1958          onClose();
1959        },
1960        className: "w-full py-3 text-sm font-medium text-indigo-600 dark:text-indigo-400 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors rounded-b-xl",
1961        children: t("notifications.seeAll", "See all notifications")
1962      }
1963    ) })
1964  ] });
1965};
1966function useDebounce(value, delay) {
1967  const [debouncedValue, setDebouncedValue] = reactExports.useState(value);
1968  reactExports.useEffect(() => {
1969    const handler = setTimeout(() => {
1970      setDebouncedValue(value);
1971    }, delay);
1972    return () => clearTimeout(handler);
1973  }, [value, delay]);
1974  return debouncedValue;
1975}
1976function Header({
1977  isSidebarOpen,
1978  setIsSidebarOpen,
1979  handleGoHome,
1980  searchQuery,
1981  setSearchQuery,
1982  handleSearch,
1983  currentUser,
1984  setIsAuthModalOpen,
1985  onAccountClick,
1986  onPersonalSpaceClick,
1987  onLogout,
1988  notifications,
1989  onMarkNotificationRead,
1990  onMarkAllNotificationsRead,
1991  onSeeAllNotifications,
1992  onUploadClick,
1993  onMelodySearchClick,
1994  onNavigateNotification,
1995  onNavigate,
1996  disableNearHeaderExpand = false
1997}) {
1998  var _a, _b;
1999  const { t } = useTranslation();
2000  const { graphics } = useSettings();
2001  const [showNotifications, setShowNotifications] = reactExports.useState(false);
2002  const [activeLogo, setActiveLogo] = reactExports.useState(null);
2003  const notificationRef = reactExports.useRef(null);
2004  const [isScrolled, setIsScrolled] = reactExports.useState(false);
2005  const [isHovered, setIsHovered] = reactExports.useState(false);
2006  const [isSearchFocused, setIsSearchFocused] = reactExports.useState(false);
2007  const searchInputRef = reactExports.useRef(null);
2008  const [suggestions, setSuggestions] = reactExports.useState([]);
2009  const [showDropdown, setShowDropdown] = reactExports.useState(false);
2010  const [isMobileSearchOpen, setIsMobileSearchOpen] = reactExports.useState(false);
2011  const [activeIndex, setActiveIndex] = reactExports.useState(-1);
2012  const dropdownRef = reactExports.useRef(null);
2013  const [showUserDropdown, setShowUserDropdown] = reactExports.useState(false);
2014  const userDropdownRef = reactExports.useRef(null);
2015  reactExports.useEffect(() => {
2016    const handleScroll = (e) => {
2017      const target = e.target;
2018      if (target && target.tagName === "MAIN" && typeof target.scrollTop === "number") {
2019        setIsScrolled(target.scrollTop > 50);
2020      }
2021    };
2022    window.addEventListener("scroll", handleScroll, { passive: true, capture: true });
2023    return () => {
2024      window.removeEventListener("scroll", handleScroll, true);
2025    };
2026  }, []);
2027  const isCollapsed = isScrolled && !(isSearchFocused || showDropdown || showNotifications || showUserDropdown);
2028  const isHoverExpanded = isCollapsed && isHovered;
2029  const debouncedQuery = useDebounce(searchQuery, 250);
2030  const unreadCount = notifications.filter((n) => !n.isRead).length;
2031  reactExports.useEffect(() => {
2032    const handleClickOutside = (event) => {
2033      if (notificationRef.current && !notificationRef.current.contains(event.target)) {
2034        setShowNotifications(false);
2035      }
2036      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
2037        setShowDropdown(false);
2038      }
2039      if (userDropdownRef.current && !userDropdownRef.current.contains(event.target)) {
2040        setShowUserDropdown(false);
2041      }
2042    };
2043    document.addEventListener("mousedown", handleClickOutside);
2044    return () => document.removeEventListener("mousedown", handleClickOutside);
2045  }, []);
2046  reactExports.useEffect(() => {
2047    if (!debouncedQuery || debouncedQuery.trim().length < 2) {
2048      setSuggestions([]);
2049      setShowDropdown(false);
2050      return;
2051    }
2052    const fetchSuggestions = async () => {
2053      try {
2054        const results = await api.getSearchSuggestions(debouncedQuery);
2055        setSuggestions(results);
2056        if (document.activeElement === searchInputRef.current) {
2057          setShowDropdown(results.length > 0);
2058        }
2059        setActiveIndex(-1);
2060      } catch (err) {
2061        console.error("Failed to load search suggestions", err);
2062      }
2063    };
2064    fetchSuggestions();
2065  }, [debouncedQuery]);
2066  const handleSelectSuggestion = (item) => {
2067    var _a2;
2068    setSearchQuery(item.title);
2069    setShowDropdown(false);
2070    (_a2 = searchInputRef.current) == null ? void 0 : _a2.blur();
2071    if (item.type === "TRACK") {
2072      const trackId = item.id.replace("track-", "");
2073      if (onNavigate) {
2074        onNavigate("watch", trackId);
2075      } else {
2076        window.location.href = `/?v=${trackId}`;
2077      }
2078    } else if (item.type === "LYRIC") {
2079      const lyricId = item.id.replace("lyric-", "");
2080      if (onNavigate) {
2081        onNavigate("lyrics", lyricId);
2082      } else {
2083        window.location.href = `/lyrics/${lyricId}`;
2084      }
2085    } else {
2086      if (onNavigate) {
2087        try {
2088          const url = new URL(item.target, window.location.origin);
2089          const view = url.pathname.replace(/^\/+/, "") || "home";
2090          const search = url.search;
2091          onNavigate(view + search);
2092        } catch {
2093          window.location.href = item.target;
2094        }
2095      } else {
2096        window.location.href = item.target;
2097      }
2098    }
2099  };
2100  const handleKeyDown = (e) => {
2101    if (!showDropdown || suggestions.length === 0) return;
2102    if (e.key === "ArrowDown") {
2103      e.preventDefault();
2104      setActiveIndex((prev) => prev + 1 < suggestions.length ? prev + 1 : 0);
2105    } else if (e.key === "ArrowUp") {
2106      e.preventDefault();
2107      setActiveIndex((prev) => prev - 1 >= 0 ? prev - 1 : suggestions.length - 1);
2108    } else if (e.key === "Enter") {
2109      if (activeIndex >= 0 && activeIndex < suggestions.length) {
2110        e.preventDefault();
2111        handleSelectSuggestion(suggestions[activeIndex]);
2112      }
2113    } else if (e.key === "Escape") {
2114      setShowDropdown(false);
2115    }
2116  };
2117  reactExports.useEffect(() => {
2118    const fetchDynamicLogo = async () => {
2119      try {
2120        const res = await fetch("/api/logo/current");
2121        if (res.ok) {
2122          const data = await res.json();
2123          if (data && data.imageUrl) {
2124            setActiveLogo(data.imageUrl);
2125            return;
2126          }
2127        }
2128      } catch (err) {
2129        console.error("Failed to fetch dynamic logo", err);
2130      }
2131      if (graphics.mainLogo) {
2132        setActiveLogo(graphics.mainLogo);
2133      }
2134    };
2135    fetchDynamicLogo();
2136  }, [graphics]);
2137  const highlightMatch = (text, query) => {
2138    if (!query) return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: text });
2139    const parts = text.split(new RegExp(`(${query.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")})`, "gi"));
2140    return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: parts.map(
2141      (part, i) => part.toLowerCase() === query.toLowerCase() ? /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-white font-extrabold", children: part }, i) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: part }, i)
2142    ) });
2143  };
2144  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2145    "header",
2146    {
2147      className: `absolute top-0 left-0 right-0 md:right-[14px] z-50 bg-white dark:bg-[#1a1a1a] border-b border-zinc-200 dark:border-zinc-800 h-16 transition-all duration-200 banner-collapsible ${isCollapsed ? "banner-collapsed" : ""} ${isHoverExpanded ? "banner-expanded-hover" : ""}`,
2148      onMouseEnter: () => setIsHovered(true),
2149      onMouseLeave: () => setIsHovered(false),
2150      children: [
2151        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "banner-inner-container px-4", children: [
2152          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-4 ${isMobileSearchOpen ? "hidden md:flex" : "flex"}`, children: [
2153            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setIsSidebarOpen(!isSidebarOpen), className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors text-zinc-700 dark:text-zinc-200", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Menu, { size: 24 }) }),
2154            /* @__PURE__ */ jsxRuntimeExports.jsx(HamieLogo, { onClick: handleGoHome, overrideSrc: activeLogo, href: "/" })
2155          ] }),
2156          isMobileSearchOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
2157            "button",
2158            {
2159              className: "md:hidden p-2 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors mr-2",
2160              onClick: () => setIsMobileSearchOpen(false),
2161              children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 20 })
2162            }
2163          ),
2164          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex-1 max-w-2xl md:mx-8 ${isMobileSearchOpen ? "block" : "hidden md:block"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: (e) => {
2165            var _a2;
2166            setShowDropdown(false);
2167            if (isMobileSearchOpen) setIsMobileSearchOpen(false);
2168            (_a2 = searchInputRef.current) == null ? void 0 : _a2.blur();
2169            handleSearch(e);
2170          }, className: "flex gap-2 w-full", children: [
2171            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 relative", ref: dropdownRef, children: [
2172              /* @__PURE__ */ jsxRuntimeExports.jsx(
2173                "input",
2174                {
2175                  ref: searchInputRef,
2176                  type: "text",
2177                  placeholder: t("common.search", "Search archives..."),
2178                  className: "w-full bg-zinc-100 dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-md h-10 pl-10 pr-4 text-sm focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 outline-none transition-all",
2179                  value: searchQuery,
2180                  onChange: (e) => setSearchQuery(e.target.value),
2181                  onKeyDown: handleKeyDown,
2182                  onFocus: () => {
2183                    setIsSearchFocused(true);
2184                    if (suggestions.length > 0) setShowDropdown(true);
2185                  },
2186                  onBlur: () => {
2187                    setIsSearchFocused(false);
2188                  }
2189                }
2190              ),
2191              /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "submit", className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-indigo-500 transition-colors", title: t("common.search", "Search"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 18 }) }),
2192              searchQuery && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
2193                setSearchQuery("");
2194                setSuggestions([]);
2195                setShowDropdown(false);
2196              }, className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16 }) }),
2197              showDropdown && suggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-full left-0 right-0 mt-1 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-md shadow-xl overflow-hidden z-50 animate-in fade-in slide-in-from-top-1 duration-150", children: /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { children: suggestions.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2198                "li",
2199                {
2200                  onClick: () => handleSelectSuggestion(item),
2201                  className: `flex items-center justify-between px-4 py-2.5 cursor-pointer text-sm transition-all ${index === activeIndex ? "bg-zinc-100 dark:bg-zinc-800 text-indigo-600 dark:text-white" : "hover:bg-zinc-50 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300"}`,
2202                  children: [
2203                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
2204                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: highlightMatch(item.title, searchQuery) }),
2205                      item.subtitle && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-normal", children: item.subtitle })
2206                    ] }),
2207                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1.5 items-center shrink-0", children: item.types && item.types.length > 0 ? item.types.map((tLabel) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2208                      "span",
2209                      {
2210                        className: `text-[9px] font-extrabold uppercase tracking-wider px-1.5 py-0.5 rounded border ${tLabel === "MIDI" ? "bg-blue-50 dark:bg-blue-950/20 text-blue-600 dark:text-blue-400 border-blue-100 dark:border-blue-900/30" : "bg-purple-50 dark:bg-purple-950/20 text-purple-600 dark:text-purple-400 border-purple-100 dark:border-purple-900/30"}`,
2211                        children: tLabel
2212                      },
2213                      tLabel
2214                    )) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase font-bold tracking-wider px-2 py-0.5 rounded text-zinc-400 dark:text-zinc-500 bg-zinc-50 dark:bg-zinc-950 border border-zinc-100 dark:border-zinc-800/30", children: item.type }) })
2215                  ]
2216                },
2217                item.id
2218              )) }) })
2219            ] }),
2220            /* @__PURE__ */ jsxRuntimeExports.jsx(
2221              "button",
2222              {
2223                type: "button",
2224                onClick: onMelodySearchClick,
2225                className: "h-10 w-10 bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 border border-zinc-300 dark:border-zinc-700 rounded-md flex items-center justify-center transition-colors text-zinc-600 dark:text-zinc-300",
2226                title: t("header.melodySearch", "Search by Melody"),
2227                children: /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 18 })
2228              }
2229            )
2230          ] }) }),
2231          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `items-center gap-2 ${isMobileSearchOpen ? "hidden md:flex" : "flex"}`, children: [
2232            /* @__PURE__ */ jsxRuntimeExports.jsx(
2233              "button",
2234              {
2235                className: "md:hidden p-2 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors flex items-center gap-2",
2236                onClick: () => {
2237                  setIsMobileSearchOpen(true);
2238                  setTimeout(() => {
2239                    var _a2;
2240                    return (_a2 = searchInputRef.current) == null ? void 0 : _a2.focus();
2241                  }, 10);
2242                },
2243                title: t("common.search", "Search"),
2244                children: /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 20 })
2245              }
2246            ),
2247            /* @__PURE__ */ jsxRuntimeExports.jsxs(
2248              "button",
2249              {
2250                className: "p-2 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors flex items-center gap-2",
2251                onClick: onUploadClick,
2252                title: t("common.upload", "Upload"),
2253                children: [
2254                  /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 20 }),
2255                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden lg:inline text-sm font-medium", children: t("common.upload", "Upload") })
2256                ]
2257              }
2258            ),
2259            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", ref: notificationRef, children: [
2260              /* @__PURE__ */ jsxRuntimeExports.jsxs(
2261                "button",
2262                {
2263                  className: `p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md relative transition-colors text-zinc-600 dark:text-zinc-300 ${showNotifications ? "bg-zinc-100 dark:bg-zinc-800" : ""}`,
2264                  onClick: () => {
2265                    if (!currentUser) {
2266                      setIsAuthModalOpen(true);
2267                    } else {
2268                      setShowNotifications(!showNotifications);
2269                    }
2270                  },
2271                  children: [
2272                    /* @__PURE__ */ jsxRuntimeExports.jsx(Bell, { size: 20 }),
2273                    currentUser && unreadCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0.5 right-0.5 min-w-[18px] h-[18px] px-1 bg-red-500 text-white text-[10px] font-bold rounded-full border-2 border-white dark:border-[#1a1a1a] flex items-center justify-center leading-none", children: unreadCount > 9 ? "9+" : unreadCount })
2274                  ]
2275                }
2276              ),
2277              showNotifications && /* @__PURE__ */ jsxRuntimeExports.jsx(
2278                NotificationsDropdown,
2279                {
2280                  notifications,
2281                  onMarkAsRead: onMarkNotificationRead,
2282                  onMarkAllRead: onMarkAllNotificationsRead,
2283                  onSeeAll: onSeeAllNotifications,
2284                  onClose: () => setShowNotifications(false),
2285                  onNavigate: onNavigateNotification
2286                }
2287              )
2288            ] }),
2289            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-6 w-px bg-zinc-300 dark:bg-zinc-700 mx-2" }),
2290            currentUser ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", ref: userDropdownRef, children: [
2291              /* @__PURE__ */ jsxRuntimeExports.jsxs(
2292                "button",
2293                {
2294                  onClick: () => setShowUserDropdown(!showUserDropdown),
2295                  className: "flex items-center gap-3 cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-800/50 p-1.5 rounded-lg transition-colors border-0 bg-transparent text-left",
2296                  children: [
2297                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", children: [
2298                      /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: currentUser.avatar, name: currentUser.username, size: 32, className: "rounded-md" }),
2299                      isPro(currentUser) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute -bottom-1 -right-1 text-white text-[7px] font-bold px-1 rounded-full border border-white dark:border-zinc-900 uppercase tracking-tighter ${((_a = currentUser.subscriptionTier) == null ? void 0 : _a.toLowerCase()) === "premium" ? "bg-indigo-600" : ((_b = currentUser.subscriptionTier) == null ? void 0 : _b.toLowerCase()) === "gold" ? "bg-amber-500" : "bg-zinc-500"}`, children: currentUser.role === "ADMIN" ? "ADMIN" : currentUser.subscriptionTier || "PRO" })
2300                    ] }),
2301                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "hidden lg:block text-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-900 dark:text-white leading-none", children: currentUser.username }) })
2302                  ]
2303                }
2304              ),
2305              showUserDropdown && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute right-0 mt-2 w-56 bg-white dark:bg-zinc-900 rounded-xl shadow-xl border border-zinc-200 dark:border-zinc-800 py-1.5 z-50 animate-in fade-in slide-in-from-top-2 duration-150", children: [
2306                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-2 border-b border-zinc-100 dark:border-zinc-800", children: [
2307                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-900 dark:text-white truncate", children: currentUser.username }),
2308                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: currentUser.email })
2309                ] }),
2310                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-b border-zinc-100 dark:border-zinc-800 bg-amber-50/30 dark:bg-amber-950/10", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
2311                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-amber-600 dark:text-amber-500", children: [
2312                    /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16 }),
2313                    /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-sm", children: [
2314                      currentUser.credits || 0,
2315                      " Credits"
2316                    ] })
2317                  ] }),
2318                  /* @__PURE__ */ jsxRuntimeExports.jsx(
2319                    "a",
2320                    {
2321                      href: "/billing",
2322                      className: "text-xs font-semibold text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300 transition-colors",
2323                      onClick: (e) => {
2324                        if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
2325                          e.preventDefault();
2326                          setShowUserDropdown(false);
2327                          onNavigate == null ? void 0 : onNavigate("billing");
2328                        }
2329                      },
2330                      children: "Details"
2331                    }
2332                  )
2333                ] }) }),
2334                /* @__PURE__ */ jsxRuntimeExports.jsxs(
2335                  "a",
2336                  {
2337                    href: "/personal-space",
2338                    onClick: (e) => {
2339                      if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
2340                        e.preventDefault();
2341                        setShowUserDropdown(false);
2342                        onPersonalSpaceClick();
2343                      }
2344                    },
2345                    className: "flex items-center gap-3 px-4 py-2.5 text-sm text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors",
2346                    children: [
2347                      /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16, className: "text-indigo-500" }),
2348                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "My Personal Space" })
2349                    ]
2350                  }
2351                ),
2352                /* @__PURE__ */ jsxRuntimeExports.jsxs(
2353                  "a",
2354                  {
2355                    href: "/account",
2356                    onClick: (e) => {
2357                      if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
2358                        e.preventDefault();
2359                        setShowUserDropdown(false);
2360                        onAccountClick();
2361                      }
2362                    },
2363                    className: "flex items-center gap-3 px-4 py-2.5 text-sm text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors",
2364                    children: [
2365                      /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: "text-zinc-400 dark:text-zinc-500" }),
2366                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Account Settings" })
2367                    ]
2368                  }
2369                ),
2370                /* @__PURE__ */ jsxRuntimeExports.jsxs(
2371                  "a",
2372                  {
2373                    href: "/billing",
2374                    onClick: (e) => {
2375                      if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
2376                        e.preventDefault();
2377                        setShowUserDropdown(false);
2378                        onNavigate == null ? void 0 : onNavigate("billing");
2379                      }
2380                    },
2381                    className: "flex items-center gap-3 px-4 py-2.5 text-sm text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors",
2382                    children: [
2383                      /* @__PURE__ */ jsxRuntimeExports.jsx(CreditCard, { size: 16, className: "text-indigo-500" }),
2384                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Support Us / Billing" })
2385                    ]
2386                  }
2387                ),
2388                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-100 dark:border-zinc-800 my-1" }),
2389                /* @__PURE__ */ jsxRuntimeExports.jsxs(
2390                  "button",
2391                  {
2392                    onClick: () => {
2393                      setShowUserDropdown(false);
2394                      onLogout == null ? void 0 : onLogout();
2395                    },
2396                    className: "w-full flex items-center gap-3 px-4 py-2.5 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-950/20 transition-colors text-left border-0 bg-transparent",
2397                    children: [
2398                      /* @__PURE__ */ jsxRuntimeExports.jsx(LogOut, { size: 16 }),
2399                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Sign Out" })
2400                    ]
2401                  }
2402                )
2403              ] })
2404            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
2405              "button",
2406              {
2407                onClick: () => setIsAuthModalOpen(true),
2408                className: "flex items-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md text-sm font-bold transition-all shadow-sm",
2409                children: [
2410                  /* @__PURE__ */ jsxRuntimeExports.jsx(UserPlus, { size: 18 }),
2411                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("common.signIn", "Sign In / Join") })
2412                ]
2413              }
2414            )
2415          ] })
2416        ] }),
2417        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "banner-trigger-tab", title: "Show header", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6 h-1 bg-zinc-400 dark:bg-zinc-600 rounded-full" }) })
2418      ]
2419    }
2420  );
2421}
2422function BackToTop() {
2423  const [isVisible, setIsVisible] = reactExports.useState(false);
2424  reactExports.useEffect(() => {
2425    const handleScroll = (e) => {
2426      const target = e.target;
2427      if (target && target.tagName === "MAIN" && typeof target.scrollTop === "number") {
2428        setIsVisible(target.scrollTop > 300);
2429      }
2430    };
2431    window.addEventListener("scroll", handleScroll, true);
2432    return () => window.removeEventListener("scroll", handleScroll, true);
2433  }, []);
2434  const scrollToTop = () => {
2435    const main = document.getElementById("main-scroll-container");
2436    if (main) {
2437      main.scrollTo({ top: 0, behavior: "smooth" });
2438    } else {
2439      window.scrollTo({ top: 0, behavior: "smooth" });
2440    }
2441  };
2442  if (!isVisible) return null;
2443  return /* @__PURE__ */ jsxRuntimeExports.jsx(
2444    "button",
2445    {
2446      onClick: scrollToTop,
2447      className: "fixed bottom-[100px] md:bottom-10 right-6 z-[60] p-3 rounded-full bg-indigo-600 text-white shadow-lg shadow-indigo-600/30 hover:bg-indigo-700 hover:scale-110 transition-all duration-300 animate-in fade-in slide-in-from-bottom-4 group",
2448      title: "Back to top",
2449      children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { size: 24, className: "group-hover:-translate-y-0.5 transition-transform" })
2450    }
2451  );
2452}
2453const LanguagePickerModal = ({ isOpen, onClose, availableCodes }) => {
2454  const { t } = useTranslation();
2455  const { language, setLanguage } = useSettings();
2456  const [search, setSearch] = reactExports.useState("");
2457  const handleSelect = (code) => {
2458    setLanguage(code);
2459    onClose();
2460  };
2461  const filteredRegions = reactExports.useMemo(() => {
2462    const query = search.toLowerCase().trim();
2463    if (!query) return LANGUAGE_REGIONS;
2464    const result = {};
2465    for (const [region, codes] of Object.entries(LANGUAGE_REGIONS)) {
2466      const matchedCodes = codes.filter((c) => {
2467        if (!availableCodes.includes(c)) return false;
2468        const nativeName = NATIVE_LANGUAGE_NAMES[c] || c;
2469        return nativeName.toLowerCase().includes(query) || c.toLowerCase().includes(query);
2470      });
2471      if (matchedCodes.length > 0) {
2472        result[region] = matchedCodes;
2473      }
2474    }
2475    return result;
2476  }, [search, availableCodes]);
2477  if (!isOpen) return null;
2478  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4 animate-in fade-in duration-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 w-full max-w-4xl max-h-[85vh] rounded-2xl shadow-2xl flex flex-col border border-zinc-200 dark:border-zinc-800", children: [
2479    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 border-b border-zinc-100 dark:border-zinc-800", children: [
2480      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
2481        /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "text-zinc-500", size: 24 }),
2482        /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-800 dark:text-zinc-100", children: t("language.selectLanguage", "Select Language") })
2483      ] }),
2484      /* @__PURE__ */ jsxRuntimeExports.jsx(
2485        "button",
2486        {
2487          onClick: onClose,
2488          className: "p-2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors",
2489          children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20 })
2490        }
2491      )
2492    ] }),
2493    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 border-b border-zinc-100 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
2494      /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400", size: 18 }),
2495      /* @__PURE__ */ jsxRuntimeExports.jsx(
2496        "input",
2497        {
2498          type: "text",
2499          placeholder: t("language.search", "Search languages..."),
2500          value: search,
2501          onChange: (e) => setSearch(e.target.value),
2502          className: "w-full pl-10 pr-4 py-3 bg-zinc-100 dark:bg-zinc-800 border-none rounded-xl text-zinc-800 dark:text-zinc-200 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 outline-none",
2503          autoFocus: true
2504        }
2505      )
2506    ] }) }),
2507    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-4 custom-scrollbar", children: [
2508      Object.entries(filteredRegions).map(([region, codes]) => {
2509        const validCodes = codes.filter((c) => availableCodes.includes(c));
2510        if (validCodes.length === 0) return null;
2511        return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-8 last:mb-0", children: [
2512          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold text-zinc-400 dark:text-zinc-500 uppercase tracking-wider mb-3 px-2", children: region }),
2513          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2", children: validCodes.map((code) => {
2514            const isSelected = language === code;
2515            const nativeName = NATIVE_LANGUAGE_NAMES[code] || code;
2516            return /* @__PURE__ */ jsxRuntimeExports.jsx(
2517              "button",
2518              {
2519                onClick: () => handleSelect(code),
2520                className: `flex flex-col items-start p-3 rounded-xl text-left transition-colors
2521                          ${isSelected ? "bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 border border-blue-200 dark:border-blue-800" : "hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300 border border-transparent"}`,
2522                children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-[15px]", children: nativeName })
2523              },
2524              code
2525            );
2526          }) })
2527        ] }, region);
2528      }),
2529      Object.keys(filteredRegions).length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-12 text-center text-zinc-500", children: t("language.noResults", "No languages found") })
2530    ] })
2531  ] }) });
2532};
2533const LanguageRegionSelector = () => {
2534  const { i18n, t } = useTranslation();
2535  const { language, setLanguage } = useSettings();
2536  const [locales, setLocales] = reactExports.useState([]);
2537  const [isModalOpen, setIsModalOpen] = reactExports.useState(false);
2538  reactExports.useEffect(() => {
2539    const fetchData = async () => {
2540      try {
2541        const localesData = await api.getLocales();
2542        setLocales(localesData.filter((l) => l.isActive));
2543      } catch (error) {
2544        console.error("Failed to fetch locales", error);
2545      }
2546    };
2547    fetchData();
2548  }, []);
2549  const handleLanguageChange = (val) => {
2550    if (val === "MORE") {
2551      setIsModalOpen(true);
2552      return;
2553    }
2554    setLanguage(val);
2555  };
2556  const activeLocales = locales.length > 0 ? locales.map((l) => l.code) : Object.keys(NATIVE_LANGUAGE_NAMES);
2557  const topLanguages = ["en", "zh-CN", "zh-TW", "es", "fr", "de", "ja", "pt", "ru", "ko"];
2558  const popularOptions = topLanguages.filter((code) => activeLocales.includes(code));
2559  if (!popularOptions.includes(language) && activeLocales.includes(language)) {
2560    popularOptions.unshift(language);
2561  }
2562  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2563    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col md:flex-row gap-4 items-center text-sm text-zinc-600 dark:text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-100 dark:bg-zinc-800 px-3 py-1.5 rounded-full border border-zinc-200 dark:border-zinc-700", children: [
2564      /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 14, className: "text-zinc-400" }),
2565      /* @__PURE__ */ jsxRuntimeExports.jsxs(
2566        "select",
2567        {
2568          value: language,
2569          onChange: (e) => handleLanguageChange(e.target.value),
2570          className: "bg-transparent border-none focus:ring-0 cursor-pointer outline-none font-medium text-zinc-800 dark:text-zinc-200",
2571          children: [
2572            popularOptions.map((code) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: code, className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850", children: NATIVE_LANGUAGE_NAMES[code] || code }, code)),
2573            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "MORE", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850 font-bold", children: t("language.more", "More languages...") })
2574          ]
2575        }
2576      )
2577    ] }) }),
2578    /* @__PURE__ */ jsxRuntimeExports.jsx(
2579      LanguagePickerModal,
2580      {
2581        isOpen: isModalOpen,
2582        onClose: () => setIsModalOpen(false),
2583        availableCodes: activeLocales
2584      }
2585    )
2586  ] });
2587};
2588const Footer = () => {
2589  const { t } = useTranslation();
2590  const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
2591  const [versionDate, setVersionDate] = reactExports.useState(null);
2592  reactExports.useEffect(() => {
2593    api.getAppVersionDate().then((data) => {
2594      if (data && data.versionDate) {
2595        const d = new Date(data.versionDate);
2596        if (!isNaN(d.getTime())) {
2597          const pad = (n) => String(n).padStart(2, "0");
2598          const formatted = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
2599          setVersionDate(formatted);
2600        } else {
2601          setVersionDate(data.versionDate);
2602        }
2603      }
2604    }).catch((err) => {
2605      console.warn("Failed to fetch version date:", err);
2606    });
2607  }, []);
2608  return /* @__PURE__ */ jsxRuntimeExports.jsx("footer", { className: "bg-zinc-50 dark:bg-[#0a0a0a] border-t border-zinc-200 dark:border-zinc-800 py-8 px-6 mt-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center gap-6", children: [
2609    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 text-center md:text-left max-w-md", children: [
2610      /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-indigo-600 dark:text-indigo-400", children: "HamieNET" }),
2611      /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] leading-relaxed text-zinc-500 dark:text-zinc-600 uppercase tracking-tight font-medium", children: [
2612        "Copyright © 1999-",
2613        currentYear,
2614        " HamieNET.com. Certain Data Copyright © 2002-",
2615        currentYear,
2616        " Open Educational Music Library. For Educational Use Only. All Rights Reserved. Works, Contents, And Titles Are Property Of Their Respective Owners. Access or Use of This Site Signifies Your Acceptance of the ",
2617        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/terms", className: "underline hover:text-indigo-500", children: "Terms and Conditions" }),
2618        " and ",
2619        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/privacy", className: "underline hover:text-indigo-500", children: "Privacy Information" }),
2620        ". For DMCA or copyright concerns, please ",
2621        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/dmca", className: "underline hover:text-indigo-500", children: "file an official takedown request" }),
2622        "."
2623      ] })
2624    ] }),
2625    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center md:items-end gap-4", children: [
2626      /* @__PURE__ */ jsxRuntimeExports.jsx(LanguageRegionSelector, {}),
2627      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-6 text-xs font-medium text-zinc-500 dark:text-zinc-500", children: [
2628        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/help", className: "hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors", children: t("footer.help", "Help") }),
2629        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/terms", className: "hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors", children: t("footer.terms", "Terms") }),
2630        /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/privacy", className: "hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors", children: t("footer.privacy", "Privacy") })
2631      ] }),
2632      versionDate && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-400 dark:text-zinc-600 font-mono tracking-tight text-center md:text-right mt-1", children: [
2633        "Latest Edit: ",
2634        versionDate
2635      ] })
2636    ] })
2637  ] }) });
2638};
2639const GlobalSettingsModal = ({ isOpen, onClose, theme, toggleTheme, isAdmin }) => {
2640  const { t } = useTranslation();
2641  const {
2642    language,
2643    setLanguage,
2644    currency,
2645    setCurrency,
2646    availableLanguages,
2647    uiConfig,
2648    autoplay,
2649    setAutoplay
2650  } = useSettings();
2651  if (!isOpen) return null;
2652  const getThemeIcon = (t2) => {
2653    if (t2 === "dark") return /* @__PURE__ */ jsxRuntimeExports.jsx(Moon, { size: 18 });
2654    if (t2 === "fun") return /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { size: 18 });
2655    if (t2 === "retro") return /* @__PURE__ */ jsxRuntimeExports.jsx(Tv, { size: 18 });
2656    return /* @__PURE__ */ jsxRuntimeExports.jsx(Sun, { size: 18 });
2657  };
2658  const activeModes = ["light", "dark", "fun"];
2659  if (isAdmin) {
2660    activeModes.push("retro");
2661  }
2662  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[200] flex items-center justify-center p-4", children: [
2663    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/60 backdrop-blur-sm", onClick: onClose }),
2664    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-md bg-white dark:bg-zinc-900 rounded-2xl shadow-2xl border border-zinc-200 dark:border-zinc-800 flex flex-col overflow-hidden animate-in zoom-in-95", children: [
2665      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 flex justify-between items-center bg-zinc-50 dark:bg-zinc-950", children: [
2666        /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-lg font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
2667          /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "text-indigo-500", size: 20 }),
2668          t("nav.settings", "Settings")
2669        ] }),
2670        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "p-2 hover:bg-zinc-200 dark:hover:bg-zinc-800 rounded-full text-zinc-500 dark:text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20 }) })
2671      ] }),
2672      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 space-y-6 max-h-[70vh] overflow-y-auto custom-scrollbar", children: [
2673        uiConfig.enableAppearanceSelector && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { children: [
2674          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: t("header.appearance", "Appearance") }),
2675          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `grid gap-2 ${activeModes.length === 4 ? "grid-cols-4" : "grid-cols-3"}`, children: activeModes.map((mode) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2676            "button",
2677            {
2678              onClick: () => {
2679                if (theme !== mode) toggleTheme(mode);
2680              },
2681              className: `flex flex-col items-center justify-center p-3 rounded-xl border-2 transition-all ${theme === mode ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-700 dark:text-indigo-300" : "border-transparent bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
2682              children: [
2683                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2", children: getThemeIcon(mode) }),
2684                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold capitalize", children: mode })
2685              ]
2686            },
2687            mode
2688          )) })
2689        ] }),
2690        /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { children: [
2691          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: t("player.settings", "Playback") }),
2692          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-3 bg-zinc-50 dark:bg-zinc-800/50 rounded-xl border border-zinc-100 dark:border-zinc-800", children: [
2693            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
2694              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-2 bg-zinc-200 dark:bg-zinc-700 rounded-lg text-zinc-700 dark:text-zinc-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlay, { size: 18 }) }),
2695              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2696                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-900 dark:text-white", children: t("player.autoplay", "Autoplay") }),
2697                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-zinc-500", children: "Auto-play videos on load" })
2698              ] })
2699            ] }),
2700            /* @__PURE__ */ jsxRuntimeExports.jsx(
2701              "button",
2702              {
2703                onClick: () => setAutoplay(!autoplay),
2704                className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${autoplay ? "bg-indigo-600" : "bg-zinc-300 dark:bg-zinc-600"}`,
2705                children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-block h-4 w-4 transform rounded-full bg-white transition duration-200 ease-in-out ${autoplay ? "translate-x-6" : "translate-x-1"}` })
2706              }
2707            )
2708          ] })
2709        ] }),
2710        uiConfig.enableLanguageSelector && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { children: [
2711          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: [
2712            /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 14 }),
2713            " ",
2714            t("header.language", "Language")
2715          ] }),
2716          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-2", children: availableLanguages.map((lang) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2717            "button",
2718            {
2719              onClick: () => setLanguage(lang.code),
2720              className: `flex items-center justify-between px-3 py-2 rounded-lg text-sm font-medium transition-colors ${language === lang.code ? "bg-indigo-600 text-white shadow-md" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
2721              children: [
2722                lang.name,
2723                language === lang.code && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 14 })
2724              ]
2725            },
2726            lang.code
2727          )) })
2728        ] }),
2729        uiConfig.enableCurrencySelector && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { children: [
2730          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: [
2731            /* @__PURE__ */ jsxRuntimeExports.jsx(Coins, { size: 14 }),
2732            " ",
2733            t("header.currency", "Currency")
2734          ] }),
2735          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-2", children: ["USD", "JPY", "EUR", "GBP"].map((curr) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2736            "button",
2737            {
2738              onClick: () => setCurrency(curr),
2739              className: `px-2 py-2 rounded-lg text-sm font-bold text-center transition-colors ${currency === curr ? "bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 border border-amber-200 dark:border-amber-700" : "bg-zinc-50 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700"}`,
2740              children: curr
2741            },
2742            curr
2743          )) })
2744        ] })
2745      ] })
2746    ] })
2747  ] });
2748};
2749function BugReportModal({ isOpen, onClose, currentUser }) {
2750  const { t } = useTranslation();
2751  const [subject, setSubject] = reactExports.useState("");
2752  const [description, setDescription] = reactExports.useState("");
2753  const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
2754  const [isSuccess, setIsSuccess] = reactExports.useState(false);
2755  const [error, setError] = reactExports.useState("");
2756  const [honeypot, setHoneypot] = reactExports.useState("");
2757  const [startTime] = reactExports.useState(Date.now());
2758  const [hasInteracted, setHasInteracted] = reactExports.useState(false);
2759  if (!isOpen) return null;
2760  const handleSubmit = async (e) => {
2761    e.preventDefault();
2762    setIsSubmitting(true);
2763    setError("");
2764    if (honeypot) {
2765      console.warn("Spam detected via honeypot");
2766      onClose();
2767      return;
2768    }
2769    const duration = (Date.now() - startTime) / 1e3;
2770    if (duration < 4) {
2771      setError(t("bugReport.errorTooFast", "Please take a moment to fill out the form (Too fast)."));
2772      setIsSubmitting(false);
2773      return;
2774    }
2775    if (!hasInteracted) {
2776      setError(t("bugReport.errorInteract", "Please interact with the form before submitting."));
2777      setIsSubmitting(false);
2778      return;
2779    }
2780    try {
2781      await api.createTicket(
2782        currentUser.id,
2783        `[Bug] ${subject}`,
2784        "bug_report",
2785        "high",
2786        description,
2787        honeypot
2788      );
2789      setIsSuccess(true);
2790      setTimeout(() => {
2791        setIsSuccess(false);
2792        setSubject("");
2793        setDescription("");
2794        onClose();
2795      }, 3e3);
2796    } catch (err) {
2797      setError(err.message || t("common.error", "An error occurred"));
2798    } finally {
2799      setIsSubmitting(false);
2800    }
2801  };
2802  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[1000] flex items-center justify-center p-4", children: [
2803    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/60 dark:bg-black/80 backdrop-blur-md transition-opacity animate-in fade-in duration-300", onClick: onClose }),
2804    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-lg bg-white/95 dark:bg-zinc-900/90 rounded-2xl shadow-2xl p-6 border border-zinc-200/50 dark:border-zinc-800/50 backdrop-blur-xl animate-in zoom-in-95 duration-200 overflow-hidden", children: [
2805      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -top-12 -left-12 w-32 h-32 bg-rose-500/10 dark:bg-rose-500/5 rounded-full blur-2xl pointer-events-none" }),
2806      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -bottom-12 -right-12 w-32 h-32 bg-orange-500/10 dark:bg-orange-500/5 rounded-full blur-2xl pointer-events-none" }),
2807      /* @__PURE__ */ jsxRuntimeExports.jsx(
2808        "button",
2809        {
2810          onClick: onClose,
2811          className: "absolute top-4 right-4 p-2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-all duration-200 hover:rotate-90",
2812          "aria-label": t("common.close", "Close"),
2813          children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 18 })
2814        }
2815      ),
2816      isSuccess ? (
2817        /* Beautiful Success Screen with Animation */
2818        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-8 flex flex-col items-center text-center animate-in fade-in zoom-in-95 duration-300", children: [
2819          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800 rounded-full flex items-center justify-center text-emerald-500 dark:text-emerald-400 mb-6 shadow-lg shadow-emerald-500/10 animate-bounce", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { size: 32 }) }),
2820          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-2xl font-black text-zinc-900 dark:text-white mb-2", children: t("bugReport.successTitle", "Securely Transmitted") }),
2821          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-600 dark:text-zinc-400 max-w-sm text-sm", children: t("bugReport.successMessage", "Thank you for helping us improve HamieNET! Your bug report has been securely registered in our system.") })
2822        ] })
2823      ) : (
2824        /* Form Screen */
2825        /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2826          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3.5 mb-5", children: [
2827            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-rose-50 dark:bg-rose-950/30 rounded-xl text-rose-500 dark:text-rose-400 border border-rose-100 dark:border-rose-900/20 shadow-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Bug, { size: 24, className: "animate-pulse" }) }),
2828            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2829              /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-black text-zinc-900 dark:text-white tracking-tight", children: t("bugReport.title", "Report a Bug") }),
2830              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: t("bugReport.subtitle", "Submit system issue notification") })
2831            ] })
2832          ] }),
2833          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-5 p-3.5 bg-rose-500/10 dark:bg-rose-500/5 border border-rose-500/20 rounded-xl text-xs text-rose-700 dark:text-rose-300 leading-relaxed", children: [
2834            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-bold mb-0.5 flex items-center gap-1.5 text-rose-800 dark:text-rose-400", children: [
2835              /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 14 }),
2836              t("bugReport.disclaimerTitle", "Silent Notification Only")
2837            ] }),
2838            t("bugReport.disclaimer", "Bug reports are dispatched directly to the system administrators. Because these are recorded as background notifications to safeguard your dashboard, you will not receive confirmation emails or replies.")
2839          ] }),
2840          error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg flex items-center gap-2 text-xs text-red-700 dark:text-red-300 animate-shake", children: [
2841            /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 15 }),
2842            " ",
2843            error
2844          ] }),
2845          /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
2846            /* @__PURE__ */ jsxRuntimeExports.jsx(
2847              "input",
2848              {
2849                type: "text",
2850                name: "form_honeypot_field",
2851                value: honeypot,
2852                onChange: (e) => setHoneypot(e.target.value),
2853                className: "hidden",
2854                autoComplete: "off",
2855                tabIndex: -1
2856              }
2857            ),
2858            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2859              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-black text-zinc-600 dark:text-zinc-400 uppercase tracking-wider mb-1.5", children: t("bugReport.subjectLabel", "What went wrong?") }),
2860              /* @__PURE__ */ jsxRuntimeExports.jsx(
2861                "input",
2862                {
2863                  required: true,
2864                  type: "text",
2865                  value: subject,
2866                  onChange: (e) => {
2867                    setSubject(e.target.value);
2868                    setHasInteracted(true);
2869                  },
2870                  onFocus: () => setHasInteracted(true),
2871                  className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-rose-500/40 focus:border-rose-500 text-zinc-900 dark:text-white transition-all text-sm shadow-inner",
2872                  placeholder: t("bugReport.subjectPlaceholder", "E.g., soundfont playback cuts off on track 3")
2873                }
2874              )
2875            ] }),
2876            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2877              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-black text-zinc-600 dark:text-zinc-400 uppercase tracking-wider mb-1.5", children: t("bugReport.descriptionLabel", "Steps to Reproduce / Description") }),
2878              /* @__PURE__ */ jsxRuntimeExports.jsx(
2879                "textarea",
2880                {
2881                  required: true,
2882                  value: description,
2883                  onChange: (e) => {
2884                    setDescription(e.target.value);
2885                    setHasInteracted(true);
2886                  },
2887                  onFocus: () => setHasInteracted(true),
2888                  className: "w-full h-36 bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-rose-500/40 focus:border-rose-500 text-zinc-900 dark:text-white resize-none transition-all text-sm shadow-inner",
2889                  placeholder: t("bugReport.descriptionPlaceholder", "Please describe the bug, how to reproduce it, or any details about your browser/device...")
2890                }
2891              )
2892            ] }),
2893            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3 pt-4 border-t border-zinc-200/50 dark:border-zinc-800/50 mt-2", children: [
2894              /* @__PURE__ */ jsxRuntimeExports.jsx(
2895                "button",
2896                {
2897                  type: "button",
2898                  onClick: onClose,
2899                  className: "px-5 py-2.5 rounded-xl font-bold text-sm text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors",
2900                  children: t("common.cancel", "Cancel")
2901                }
2902              ),
2903              /* @__PURE__ */ jsxRuntimeExports.jsx(
2904                "button",
2905                {
2906                  type: "submit",
2907                  disabled: isSubmitting,
2908                  className: "px-5 py-2.5 bg-gradient-to-r from-rose-500 to-rose-600 hover:from-rose-600 hover:to-rose-700 text-white rounded-xl font-bold text-sm flex items-center gap-2 transition-all shadow-lg shadow-rose-500/25 active:scale-95 disabled:opacity-50",
2909                  children: isSubmitting ? t("bugReport.submitting", "Transmitting...") : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2910                    /* @__PURE__ */ jsxRuntimeExports.jsx(Send, { size: 15 }),
2911                    t("bugReport.submit", "Transmit Bug Report")
2912                  ] })
2913                }
2914              )
2915            ] })
2916          ] })
2917        ] })
2918      )
2919    ] })
2920  ] });
2921}
2922const ICON_MAP = {
2923  Home: House,
2924  Compass,
2925  Library,
2926  History,
2927  ListMusic,
2928  Clock,
2929  ThumbsUp,
2930  Shield,
2931  FileAudio: FileHeadphone,
2932  Layout: PanelsTopLeft,
2933  Star,
2934  Heart,
2935  Mail,
2936  Info,
2937  Tv,
2938  Hash,
2939  Monitor,
2940  Box,
2941  Settings,
2942  HelpCircle: CircleQuestionMark,
2943  MessageCircle,
2944  ChevronRight,
2945  Link,
2946  Music,
2947  Flask: FlaskConical
2948};
2949const SidebarItem = ({ icon: Icon, label, active = false, onClick, badge, href, target, collapsed }) => {
2950  const content = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2951    /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { size: 20, className: `shrink-0 ${active ? "fill-current" : ""}` }),
2952    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-sm font-medium truncate transition-all duration-300 ease-in-out ${collapsed ? "w-0 opacity-0 hidden" : "w-auto opacity-100 flex-1 ml-3"}`, children: label }),
2953    !collapsed && badge && badge > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full animate-in fade-in zoom-in duration-300", children: badge }),
2954    !collapsed && target === "_blank" && /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 12, className: "opacity-50 ml-2" })
2955  ] });
2956  const className = `flex items-center px-4 py-3 cursor-pointer transition-all duration-200 border-l-4 group
2957    ${active ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-700 dark:text-indigo-300" : "border-transparent text-zinc-600 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-zinc-800 hover:text-zinc-900 dark:hover:text-zinc-200"}
2958    ${collapsed ? "justify-center px-2" : ""}
2959  `;
2960  if (href) {
2961    return /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href, target, onClick: (e) => {
2962      if (target !== "_blank" && !e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
2963        e.preventDefault();
2964        onClick && onClick();
2965      }
2966    }, className, title: collapsed ? label : void 0, children: content });
2967  }
2968  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick, className, title: collapsed ? label : void 0, children: content });
2969};
2970const DEFAULT_SECTIONS = [
2971  {
2972    id: 1,
2973    title: "Main",
2974    order: 0,
2975    roles: null,
2976    isPublic: true,
2977    links: [
2978      { id: 1, sectionId: 1, label: "Home", path: "/", icon: "Home", order: 0, roles: null, isPublic: true },
2979      { id: 2, sectionId: 1, label: "Explore", path: "/explore", icon: "Compass", order: 1, roles: null, isPublic: true },
2980      { id: 3, sectionId: 1, label: "Melody Search", path: "/melody-search", icon: "Music", order: 2, roles: null, isPublic: true },
2981      { id: 4, sectionId: 1, label: "Subscriptions", path: "/subscriptions", icon: "Library", order: 3, roles: null, isPublic: true }
2982    ]
2983  },
2984  {
2985    id: 2,
2986    title: "Library",
2987    order: 1,
2988    roles: null,
2989    isPublic: true,
2990    links: [
2991      { id: 5, sectionId: 2, label: "History", path: "/history", icon: "History", order: 0, roles: null, isPublic: true },
2992      { id: 6, sectionId: 2, label: "Playlists", path: "/playlists", icon: "ListMusic", order: 1, roles: null, isPublic: true },
2993      { id: 7, sectionId: 2, label: "Watch Later", path: "/watchLater", icon: "Clock", order: 2, roles: null, isPublic: true },
2994      { id: 8, sectionId: 2, label: "Liked Tracks", path: "/liked", icon: "ThumbsUp", order: 3, roles: null, isPublic: true },
2995      { id: 9, sectionId: 2, label: "Lyrics", path: "/lyrics/search", icon: "Music", order: 4, roles: null, isPublic: true }
2996    ]
2997  },
2998  {
2999    id: 3,
3000    title: "Tools",
3001    order: 2,
3002    roles: null,
3003    isPublic: true,
3004    links: [
3005      { id: 10, sectionId: 3, label: "Hamie Lab", path: "/lab", icon: "Flask", order: 0, roles: null, isPublic: true },
3006      { id: 11, sectionId: 3, label: "Help & Support", path: "/help", icon: "HelpCircle", order: 1, roles: null, isPublic: true }
3007    ]
3008  }
3009];
3010const Sidebar = ({
3011  isSidebarOpen,
3012  setIsSidebarOpen,
3013  activeView,
3014  setActiveView,
3015  activeTrack,
3016  setActiveTrack,
3017  currentUser,
3018  setIsAuthModalOpen,
3019  subscriptions,
3020  handleChannelSelect,
3021  handleGoHome,
3022  onAdminClick,
3023  siteModules = [],
3024  unreadMessageCount = 0,
3025  theme,
3026  toggleTheme,
3027  isMediaPage = false
3028}) => {
3029  var _a;
3030  const { t } = useTranslation();
3031  const [isSettingsOpen, setIsSettingsOpen] = reactExports.useState(false);
3032  const [isBugReportOpen, setIsBugReportOpen] = reactExports.useState(false);
3033  const [navSections, setNavSections] = reactExports.useState(DEFAULT_SECTIONS);
3034  const { staffModeEnabled, isSuperAdmin, toggleStaffMode } = useAdmin();
3035  const [isHovered, setIsHovered] = reactExports.useState(false);
3036  reactExports.useEffect(() => {
3037    api.getNavigation().then((sections) => {
3038      if (sections && sections.length > 0) {
3039        setNavSections(sections);
3040      }
3041    }).catch(() => {
3042    });
3043  }, []);
3044  const isDesktopExpanded = isMediaPage ? true : isSidebarOpen || isHovered;
3045  const isCollapsed = !isDesktopExpanded;
3046  const handleMouseEnter = () => {
3047    if (!isMediaPage && !isSidebarOpen) {
3048      setIsHovered(true);
3049    }
3050  };
3051  const handleMouseLeave = () => {
3052    if (!isMediaPage && !isSidebarOpen) {
3053      setIsHovered(false);
3054    }
3055  };
3056  const canShow = (roles) => {
3057    if (!roles || roles.length === 0) return true;
3058    if (!currentUser) return roles.includes("GUEST");
3059    return roles.includes(currentUser.role);
3060  };
3061  const renderLink = (link) => {
3062    if (!canShow(link.roles)) return null;
3063    const Icon = link.icon && ICON_MAP[link.icon] ? ICON_MAP[link.icon] : Link;
3064    let isActive = false;
3065    if (link.path === "/") isActive = activeView === "home";
3066    else isActive = activeView === link.path.substring(1);
3067    return /* @__PURE__ */ jsxRuntimeExports.jsx(
3068      SidebarItem,
3069      {
3070        icon: Icon,
3071        label: t(`nav.link.${link.label}`, link.label),
3072        active: isActive,
3073        onClick: () => {
3074          if (link.path === "/") {
3075            handleGoHome();
3076          } else if (link.path.startsWith("/")) {
3077            const view = link.path.substring(1);
3078            setActiveView(view);
3079          }
3080          if (window.innerWidth < 1024 || isMediaPage) {
3081            setIsSidebarOpen(false);
3082          }
3083        },
3084        href: link.path,
3085        target: link.target,
3086        collapsed: isCollapsed
3087      },
3088      link.id
3089    );
3090  };
3091  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3092    /* @__PURE__ */ jsxRuntimeExports.jsx(
3093      GlobalSettingsModal,
3094      {
3095        isOpen: isSettingsOpen,
3096        onClose: () => setIsSettingsOpen(false),
3097        theme,
3098        toggleTheme,
3099        isAdmin: (currentUser == null ? void 0 : currentUser.role) === "ADMIN"
3100      }
3101    ),
3102    currentUser && /* @__PURE__ */ jsxRuntimeExports.jsx(
3103      BugReportModal,
3104      {
3105        isOpen: isBugReportOpen,
3106        onClose: () => setIsBugReportOpen(false),
3107        currentUser
3108      }
3109    ),
3110    isMediaPage && isSidebarOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
3111      "div",
3112      {
3113        className: "fixed inset-0 bg-black/50 z-30 lg:z-30 transition-opacity duration-300",
3114        onClick: () => setIsSidebarOpen(false)
3115      }
3116    ),
3117    /* @__PURE__ */ jsxRuntimeExports.jsx(
3118      "aside",
3119      {
3120        className: `
3121            fixed inset-y-0 left-0 z-40 bg-white dark:bg-[#1a1a1a] border-r border-zinc-200 dark:border-zinc-800
3122            flex flex-col pt-16 overflow-hidden
3123            transition-all duration-300 ease-in-out
3124            ${isMediaPage ? (
3125          // Media Page Styles (Overlay Drawer)
3126          (isSidebarOpen ? "translate-x-0 shadow-2xl" : "-translate-x-full") + " w-64"
3127        ) : (
3128          // General Page Styles (Collapsible Sticky)
3129          `lg:sticky lg:top-0 lg:h-full lg:translate-x-0 
3130                   ${isDesktopExpanded ? "lg:w-64 lg:shadow-xl" : "lg:w-[72px] lg:shadow-none"} 
3131                   ${isSidebarOpen ? "translate-x-0" : "-translate-x-full"} 
3132                   w-64`
3133        )}
3134        `,
3135        onMouseEnter: handleMouseEnter,
3136        onMouseLeave: handleMouseLeave,
3137        children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3138          "div",
3139          {
3140            className: "flex-1 overflow-y-auto custom-scrollbar py-4 overflow-x-hidden",
3141            style: { paddingBottom: activeTrack ? "96px" : "8px" },
3142            children: [
3143              navSections.map((section) => {
3144                if (!canShow(section.roles)) return null;
3145                return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [
3146                  section.title && section.title !== "Main" && /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: `px-6 mb-2 text-xs font-bold text-zinc-500 uppercase tracking-wider transition-opacity duration-300 ${isCollapsed ? "opacity-0 h-0 overflow-hidden mb-0" : "opacity-100"}`, children: t(`nav.section.${section.title}`, section.title) }),
3147                  isCollapsed && section.title && section.title !== "Main" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-4 my-2 h-px bg-zinc-100 dark:bg-zinc-800" }),
3148                  section.links.map(renderLink)
3149                ] }, section.id);
3150              }),
3151              navSections.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `px-6 py-4 text-sm text-zinc-500 ${isCollapsed ? "hidden" : ""}`, children: "Loading navigation..." }),
3152              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [
3153                /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: `px-6 mb-2 text-xs font-bold text-zinc-500 uppercase tracking-wider transition-opacity duration-300 ${isCollapsed ? "opacity-0 h-0 overflow-hidden mb-0" : "opacity-100"}`, children: t("nav.subscriptions", "Subscriptions") }),
3154                isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-4 my-2 h-px bg-zinc-100 dark:bg-zinc-800" }),
3155                currentUser ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: Array.from(subscriptions).map((artist) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3156                  "a",
3157                  {
3158                    href: `/channel?c=${artist}`,
3159                    onClick: (e) => {
3160                      if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
3161                        e.preventDefault();
3162                        handleChannelSelect(artist);
3163                      }
3164                    },
3165                    className: `flex items-center gap-3 px-4 py-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 cursor-pointer transition-colors ${isCollapsed ? "justify-center px-2" : "px-6"}`,
3166                    title: isCollapsed ? artist : void 0,
3167                    children: [
3168                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6 h-6 rounded-full bg-indigo-500 flex items-center justify-center text-[10px] font-bold text-white shrink-0", children: artist.substring(0, 1) }),
3169                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-sm text-zinc-700 dark:text-zinc-300 truncate transition-all duration-300 ${isCollapsed ? "w-0 opacity-0 hidden" : "w-auto opacity-100 flex-1"}`, children: artist })
3170                    ]
3171                  },
3172                  artist
3173                )) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `px-6 text-sm text-zinc-500 italic ${isCollapsed ? "hidden" : ""}`, children: t("sidebar.signInSubs", "Sign in to view") })
3174              ] }),
3175              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 mx-2 rounded-lg bg-zinc-100/70 dark:bg-zinc-800/50 py-2", children: [
3176                currentUser && (currentUser.role === "ADMIN" || currentUser.canMessage) && /* @__PURE__ */ jsxRuntimeExports.jsx(
3177                  SidebarItem,
3178                  {
3179                    icon: MessageCircle,
3180                    label: t("nav.internalComms", "Internal Comms"),
3181                    active: activeView === "messages",
3182                    onClick: () => setActiveView("messages"),
3183                    badge: unreadMessageCount,
3184                    href: "/messages",
3185                    collapsed: isCollapsed
3186                  }
3187                ),
3188                (currentUser == null ? void 0 : currentUser.role) === "ADMIN" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3189                  /* @__PURE__ */ jsxRuntimeExports.jsx(
3190                    SidebarItem,
3191                    {
3192                      icon: Shield,
3193                      label: t("nav.admin", "Admin Studio"),
3194                      active: activeView === "admin",
3195                      onClick: onAdminClick,
3196                      href: ((_a = window.HAMIE_ENV) == null ? void 0 : _a.ADMIN_URL) ? `${window.HAMIE_ENV.ADMIN_URL}/admin` : "/admin",
3197                      collapsed: isCollapsed
3198                    }
3199                  ),
3200                  isSuperAdmin && /* @__PURE__ */ jsxRuntimeExports.jsx(
3201                    SidebarItem,
3202                    {
3203                      icon: staffModeEnabled ? EyeOff : Eye,
3204                      label: staffModeEnabled ? t("nav.disableStaffMode", "Disable Staff Mode") : t("nav.enableStaffMode", "Enable Staff Mode"),
3205                      active: staffModeEnabled,
3206                      onClick: toggleStaffMode,
3207                      collapsed: isCollapsed
3208                    }
3209                  ),
3210                  /* @__PURE__ */ jsxRuntimeExports.jsx(
3211                    SidebarItem,
3212                    {
3213                      icon: ListMusic,
3214                      label: "Soundfonts Beta",
3215                      active: activeView === "soundfonts",
3216                      onClick: () => setActiveView("soundfonts"),
3217                      href: "/soundfonts",
3218                      collapsed: isCollapsed
3219                    }
3220                  )
3221                ] }),
3222                /* @__PURE__ */ jsxRuntimeExports.jsx(
3223                  SidebarItem,
3224                  {
3225                    icon: Settings,
3226                    label: t("nav.settings", "Settings"),
3227                    active: false,
3228                    onClick: () => setIsSettingsOpen(true),
3229                    collapsed: isCollapsed
3230                  }
3231                ),
3232                /* @__PURE__ */ jsxRuntimeExports.jsx(
3233                  SidebarItem,
3234                  {
3235                    icon: Bug,
3236                    label: t("nav.bugReport", "Report a Bug"),
3237                    active: false,
3238                    onClick: () => {
3239                      if (!currentUser) {
3240                        setIsAuthModalOpen(true);
3241                      } else {
3242                        setIsBugReportOpen(true);
3243                      }
3244                    },
3245                    collapsed: isCollapsed
3246                  }
3247                )
3248              ] })
3249            ]
3250          }
3251        )
3252      }
3253    )
3254  ] });
3255};
3256const MelodyInput = ({ onSearch, onClose }) => {
3257  const { t } = useTranslation();
3258  const [notes, setNotes] = reactExports.useState([]);
3259  const [isListening, setIsListening] = reactExports.useState(false);
3260  const [isPlayingSequence, setIsPlayingSequence] = reactExports.useState(false);
3261  const [clef, setClef] = reactExports.useState("treble");
3262  const audioContextRef = reactExports.useRef(null);
3263  const analyserRef = reactExports.useRef(null);
3264  const micStreamRef = reactExports.useRef(null);
3265  const requestRef = reactExports.useRef(null);
3266  const c4KeyRef = reactExports.useRef(null);
3267  reactExports.useEffect(() => {
3268    const timer = setTimeout(() => {
3269      if (c4KeyRef.current) {
3270        c4KeyRef.current.scrollIntoView({ behavior: "auto", block: "nearest", inline: "center" });
3271      }
3272    }, 150);
3273    return () => clearTimeout(timer);
3274  }, []);
3275  const startListening = async () => {
3276    try {
3277      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
3278      micStreamRef.current = stream;
3279      audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
3280      const source = audioContextRef.current.createMediaStreamSource(stream);
3281      analyserRef.current = audioContextRef.current.createAnalyser();
3282      analyserRef.current.fftSize = 2048;
3283      source.connect(analyserRef.current);
3284      setIsListening(true);
3285      detectPitch();
3286    } catch (e) {
3287      console.error("Mic Error", e);
3288      alert("Could not access microphone.");
3289    }
3290  };
3291  const stopListening = () => {
3292    setIsListening(false);
3293    if (micStreamRef.current) {
3294      micStreamRef.current.getTracks().forEach((track) => track.stop());
3295    }
3296    if (requestRef.current) cancelAnimationFrame(requestRef.current);
3297  };
3298  const detectPitch = () => {
3299    var _a;
3300    if (!analyserRef.current) return;
3301    const buffer = new Float32Array(analyserRef.current.fftSize);
3302    analyserRef.current.getFloatTimeDomainData(buffer);
3303    let bestOffset = -1;
3304    let bestCorrelation = 0;
3305    let rms = 0;
3306    let foundGoodCorrelation = false;
3307    for (let i = 0; i < buffer.length; i++) {
3308      rms += buffer[i] * buffer[i];
3309    }
3310    rms = Math.sqrt(rms / buffer.length);
3311    if (rms > 0.05) {
3312      for (let offset = 0; offset < buffer.length / 2; offset++) {
3313        let correlation = 0;
3314        for (let i = 0; i < buffer.length / 2; i++) {
3315          correlation += Math.abs(buffer[i] - buffer[i + offset]);
3316        }
3317        correlation = 1 - correlation / (buffer.length / 2);
3318        if (correlation > 0.9 && correlation > bestCorrelation) {
3319          foundGoodCorrelation = true;
3320          if (correlation > bestCorrelation) {
3321            bestCorrelation = correlation;
3322            bestOffset = offset;
3323          }
3324        } else if (foundGoodCorrelation) {
3325          const sampleRate = ((_a = audioContextRef.current) == null ? void 0 : _a.sampleRate) || 44100;
3326          const frequency = sampleRate / bestOffset;
3327          const midi = Math.round(69 + 12 * Math.log2(frequency / 440));
3328          setNotes((prev) => {
3329            const last = prev[prev.length - 1];
3330            if (last !== midi) {
3331              playNote(midi);
3332              return [...prev, midi];
3333            }
3334            return prev;
3335          });
3336          break;
3337        }
3338      }
3339    }
3340    requestRef.current = requestAnimationFrame(detectPitch);
3341  };
3342  const playNote = (midi) => {
3343    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
3344    if (!AudioContextClass) return;
3345    const ctx = new AudioContextClass();
3346    if (ctx.state === "suspended") {
3347      ctx.resume().catch(() => {
3348      });
3349    }
3350    const t2 = ctx.currentTime;
3351    const freq = 440 * Math.pow(2, (midi - 69) / 12);
3352    const masterGain = ctx.createGain();
3353    masterGain.gain.setValueAtTime(0, t2);
3354    masterGain.gain.linearRampToValueAtTime(0.5, t2 + 0.015);
3355    masterGain.gain.exponentialRampToValueAtTime(0.01, t2 + 2);
3356    masterGain.connect(ctx.destination);
3357    const filter = ctx.createBiquadFilter();
3358    filter.type = "lowpass";
3359    filter.Q.value = 0.5;
3360    filter.frequency.setValueAtTime(freq * 6, t2);
3361    filter.frequency.exponentialRampToValueAtTime(freq * 1.5, t2 + 0.5);
3362    filter.connect(masterGain);
3363    const detunes = [0, -4, 4];
3364    detunes.forEach((detune) => {
3365      const osc = ctx.createOscillator();
3366      osc.type = "sawtooth";
3367      osc.frequency.value = freq;
3368      osc.detune.value = detune;
3369      osc.connect(filter);
3370      osc.start(t2);
3371      osc.stop(t2 + 2.5);
3372    });
3373    const noiseOsc = ctx.createOscillator();
3374    noiseOsc.type = "square";
3375    noiseOsc.frequency.value = 80;
3376    const noiseGain = ctx.createGain();
3377    noiseGain.gain.setValueAtTime(0.05, t2);
3378    noiseGain.gain.exponentialRampToValueAtTime(1e-3, t2 + 0.08);
3379    const noiseFilter = ctx.createBiquadFilter();
3380    noiseFilter.type = "lowpass";
3381    noiseFilter.frequency.value = 500;
3382    noiseOsc.connect(noiseFilter);
3383    noiseFilter.connect(noiseGain);
3384    noiseGain.connect(ctx.destination);
3385    noiseOsc.start(t2);
3386    noiseOsc.stop(t2 + 0.08);
3387  };
3388  const playSequence = async () => {
3389    if (isPlayingSequence) return;
3390    setIsPlayingSequence(true);
3391    for (const note of notes) {
3392      playNote(note);
3393      await new Promise((r) => setTimeout(r, 400));
3394    }
3395    setIsPlayingSequence(false);
3396  };
3397  const handleKeyClick = (midi) => {
3398    playNote(midi);
3399    setNotes((prev) => [...prev, midi]);
3400  };
3401  const handleDeleteNote = (index) => {
3402    setNotes((prev) => prev.filter((_, i) => i !== index));
3403  };
3404  const getDiatonicStep = (midi) => {
3405    const pc = midi % 12;
3406    const octave = Math.floor(midi / 12) - 1;
3407    const stepMap = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6];
3408    return octave * 7 + stepMap[pc];
3409  };
3410  const getMidiFromDiatonicStep = (step) => {
3411    const octave = Math.floor(step / 7) + 1;
3412    const degree = step % 7;
3413    const scaleMap = [0, 2, 4, 5, 7, 9, 11];
3414    return octave * 12 + scaleMap[degree];
3415  };
3416  const renderPiano = () => {
3417    const getNoteLabel = (midi) => {
3418      const pc = midi % 12;
3419      const octave = Math.floor(midi / 12) - 1;
3420      const names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
3421      return `${names[pc]}${octave}`;
3422    };
3423    const startKey = 36;
3424    const endKey = 83;
3425    const allKeys = [];
3426    for (let i = startKey; i <= endKey; i++) {
3427      allKeys.push({ midi: i, isBlack: [1, 3, 6, 8, 10].includes(i % 12) });
3428    }
3429    const whiteKeys = allKeys.filter((k) => !k.isBlack);
3430    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-start md:justify-center relative h-40 bg-zinc-100 dark:bg-zinc-800 rounded-b-xl overflow-x-auto overflow-y-hidden select-none border-t border-zinc-200 dark:border-zinc-700 custom-scrollbar", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex relative h-full px-4 md:px-0", children: whiteKeys.map((k) => {
3431      const isC4 = k.midi === 60;
3432      const hasBlackNoteAfter = allKeys.find((bk) => bk.midi === k.midi + 1 && bk.isBlack);
3433      return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", children: [
3434        /* @__PURE__ */ jsxRuntimeExports.jsx(
3435          "div",
3436          {
3437            ref: isC4 ? c4KeyRef : null,
3438            onMouseDown: () => handleKeyClick(k.midi),
3439            className: `w-10 h-full border-r border-b border-zinc-300 dark:border-zinc-600 rounded-b-[4px] hover:bg-zinc-100 active:bg-zinc-200 cursor-pointer shadow-sm ${isC4 ? "bg-indigo-50/80 dark:bg-indigo-900/20" : "bg-white"} flex flex-col justify-end pb-2`,
3440            children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] text-center w-full pointer-events-none ${isC4 ? "text-indigo-600 font-bold" : "text-zinc-400 dark:text-zinc-500"}`, children: getNoteLabel(k.midi) })
3441          }
3442        ),
3443        hasBlackNoteAfter && /* @__PURE__ */ jsxRuntimeExports.jsx(
3444          "div",
3445          {
3446            onMouseDown: (e) => {
3447              e.stopPropagation();
3448              handleKeyClick(k.midi + 1);
3449            },
3450            className: "absolute z-10 w-6 h-24 bg-black rounded-b-[3px] top-0 -right-3 cursor-pointer shadow-md hover:bg-zinc-800 active:bg-zinc-700 border-x border-b border-zinc-800 flex flex-col justify-end pb-1",
3451            style: { boxShadow: "2px 2px 4px rgba(0,0,0,0.4)" },
3452            children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[7px] text-center w-full text-zinc-400 pointer-events-none font-medium", children: getNoteLabel(k.midi + 1) })
3453          }
3454        )
3455      ] }, k.midi);
3456    }) }) });
3457  };
3458  const renderSheet = () => {
3459    const width = 600;
3460    const height = 240;
3461    const noteRadius = 6;
3462    const bottomLineY = 140;
3463    const stepSpacing = 5;
3464    let refStep = 30;
3465    if (clef === "bass") refStep = 18;
3466    if (clef === "alto") refStep = 24;
3467    const getYForNote = (midi) => {
3468      const step = getDiatonicStep(midi);
3469      return bottomLineY - (step - refStep) * stepSpacing;
3470    };
3471    const getLedgerLines = (midi) => {
3472      const step = getDiatonicStep(midi);
3473      const lines = [];
3474      const relStep = step - refStep;
3475      if (relStep <= -2) {
3476        for (let s = -2; s >= relStep; s -= 2) {
3477          lines.push(bottomLineY - s * stepSpacing);
3478        }
3479      }
3480      if (relStep >= 10) {
3481        for (let s = 10; s <= relStep; s += 2) {
3482          lines.push(bottomLineY - s * stepSpacing);
3483        }
3484      }
3485      return lines;
3486    };
3487    const getClefSymbol = () => {
3488      switch (clef) {
3489        case "treble":
3490          return { symbol: "𝄞", y: 130, size: 55 };
3491        case "bass":
3492          return { symbol: "𝄢", y: 110, size: 45 };
3493        case "alto":
3494          return { symbol: "𝄡", y: 120, size: 45 };
3495      }
3496    };
3497    const clefConfig = getClefSymbol();
3498    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative bg-white rounded-t-lg border border-zinc-200 dark:border-zinc-700 overflow-hidden select-none", children: [
3499      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-2 left-4 z-10 flex gap-1 bg-zinc-100 rounded-lg p-1 border border-zinc-200 shadow-sm", children: [
3500        /* @__PURE__ */ jsxRuntimeExports.jsx(
3501          "button",
3502          {
3503            onClick: () => setClef("treble"),
3504            className: `px-3 py-1 rounded-md text-xs font-bold transition-colors ${clef === "treble" ? "bg-white shadow text-black" : "text-zinc-500 hover:text-zinc-700"}`,
3505            children: t("melody.treble", "Treble")
3506          }
3507        ),
3508        /* @__PURE__ */ jsxRuntimeExports.jsx(
3509          "button",
3510          {
3511            onClick: () => setClef("alto"),
3512            className: `px-3 py-1 rounded-md text-xs font-bold transition-colors ${clef === "alto" ? "bg-white shadow text-black" : "text-zinc-500 hover:text-zinc-700"}`,
3513            children: t("melody.alto", "Alto")
3514          }
3515        ),
3516        /* @__PURE__ */ jsxRuntimeExports.jsx(
3517          "button",
3518          {
3519            onClick: () => setClef("bass"),
3520            className: `px-3 py-1 rounded-md text-xs font-bold transition-colors ${clef === "bass" ? "bg-white shadow text-black" : "text-zinc-500 hover:text-zinc-700"}`,
3521            children: t("melody.bass", "Bass")
3522          }
3523        )
3524      ] }),
3525      /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, className: "cursor-crosshair bg-[#fffaf5]", onClick: (e) => {
3526        const rect = e.currentTarget.getBoundingClientRect();
3527        const clickY = e.clientY - rect.top;
3528        const diffSteps = Math.round((bottomLineY - clickY) / stepSpacing);
3529        const targetStep = refStep + diffSteps;
3530        const pitch = getMidiFromDiatonicStep(targetStep);
3531        handleKeyClick(pitch);
3532      }, children: [
3533        [0, 2, 4, 6, 8].map((stepOffset) => {
3534          const y = bottomLineY - stepOffset * stepSpacing;
3535          return /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "0", y1: y, x2: width, y2: y, stroke: "#999", strokeWidth: "1" }, y);
3536        }),
3537        /* @__PURE__ */ jsxRuntimeExports.jsx(
3538          "text",
3539          {
3540            x: "20",
3541            y: clefConfig.y,
3542            fontSize: clefConfig.size,
3543            fontFamily: "serif",
3544            fill: "#333",
3545            pointerEvents: "none",
3546            dominantBaseline: "central",
3547            className: "opacity-90",
3548            children: clefConfig.symbol
3549          }
3550        ),
3551        notes.map((n, i) => {
3552          const x = 80 + i * 35;
3553          const y = getYForNote(n);
3554          const ledgerLines = getLedgerLines(n);
3555          const isBlack = [1, 3, 6, 8, 10].includes(n % 12);
3556          return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3557            "g",
3558            {
3559              className: "hover:opacity-60 cursor-pointer group",
3560              onClick: (e) => {
3561                e.stopPropagation();
3562                handleDeleteNote(i);
3563              },
3564              children: [
3565                ledgerLines.map((ly) => /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: x - 12, y1: ly, x2: x + 12, y2: ly, stroke: "black", strokeWidth: "1" }, ly)),
3566                /* @__PURE__ */ jsxRuntimeExports.jsx(
3567                  "line",
3568                  {
3569                    x1: x + (y > 120 ? 6 : -6),
3570                    y1: y,
3571                    x2: x + (y > 120 ? 6 : -6),
3572                    y2: y > 120 ? y - 35 : y + 35,
3573                    stroke: "black",
3574                    strokeWidth: "1.5",
3575                    className: "group-hover:stroke-red-500"
3576                  }
3577                ),
3578                /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: x, cy: y, r: noteRadius, fill: "black", className: "group-hover:fill-red-500" }),
3579                isBlack && /* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: x - 18, y: y + 5, fontSize: "16", fontWeight: "bold", children: "♯" }),
3580                /* @__PURE__ */ jsxRuntimeExports.jsx(
3581                  "text",
3582                  {
3583                    x,
3584                    y: y > 120 ? y + 16 : y - 16,
3585                    textAnchor: "middle",
3586                    fontSize: "10",
3587                    fontWeight: "bold",
3588                    fill: "#4b5563",
3589                    className: "pointer-events-none select-none",
3590                    children: (() => {
3591                      const pc = n % 12;
3592                      const octave = Math.floor(n / 12) - 1;
3593                      const names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
3594                      return `${names[pc]}${octave}`;
3595                    })()
3596                  }
3597                ),
3598                /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: t("melody.deleteNote", { midi: n, defaultValue: `MIDI ${n} (Click to delete)` }) })
3599              ]
3600            },
3601            i
3602          );
3603        })
3604      ] }),
3605      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 text-[10px] text-zinc-400 font-mono bg-white/50 px-2 rounded", children: t("melody.instructions", "Click staff to add note • Click note to remove") })
3606    ] });
3607  };
3608  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[2000] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 w-full max-w-2xl rounded-2xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95", children: [
3609    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 flex justify-between items-center bg-zinc-50 dark:bg-zinc-950", children: [
3610      /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-lg font-bold flex items-center gap-2 text-zinc-900 dark:text-white", children: [
3611        /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { className: "text-indigo-500" }),
3612        " ",
3613        t("melody.title", "Melody Search")
3614      ] }),
3615      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "p-2 hover:bg-zinc-200 dark:hover:bg-zinc-800 rounded-full text-zinc-500 dark:text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20 }) })
3616    ] }),
3617    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6", children: [
3618      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col rounded-xl shadow-lg border border-zinc-200 dark:border-zinc-700", children: [
3619        renderSheet(),
3620        renderPiano()
3621      ] }),
3622      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mt-6", children: [
3623        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
3624          /* @__PURE__ */ jsxRuntimeExports.jsxs(
3625            "button",
3626            {
3627              onClick: isListening ? stopListening : startListening,
3628              className: `px-4 py-2 rounded-full flex items-center gap-2 transition-all font-medium text-sm ${isListening ? "bg-red-500 text-white animate-pulse" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
3629              children: [
3630                isListening ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleStop, { size: 18 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 18 }),
3631                isListening ? t("melody.listening", "Listening...") : t("melody.hum", "Hum Melody")
3632              ]
3633            }
3634          ),
3635          /* @__PURE__ */ jsxRuntimeExports.jsxs(
3636            "button",
3637            {
3638              onClick: playSequence,
3639              disabled: notes.length === 0 || isPlayingSequence,
3640              className: `px-4 py-2 rounded-full flex items-center gap-2 transition-all font-medium text-sm ${isPlayingSequence ? "bg-emerald-500 text-white" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 disabled:opacity-50"}`,
3641              children: [
3642                isPlayingSequence ? /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 18, className: "animate-pulse" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 18 }),
3643                t("melody.playback", "Playback")
3644              ]
3645            }
3646          )
3647        ] }),
3648        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3649          /* @__PURE__ */ jsxRuntimeExports.jsx(
3650            "button",
3651            {
3652              onClick: () => setNotes([]),
3653              className: "p-2 text-zinc-400 hover:text-red-500 transition-colors",
3654              title: t("melody.clearAll", "Clear All"),
3655              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 20 })
3656            }
3657          ),
3658          /* @__PURE__ */ jsxRuntimeExports.jsx(
3659            "button",
3660            {
3661              onClick: () => {
3662                setNotes((prev) => prev.slice(0, -1));
3663              },
3664              className: "p-2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 transition-colors",
3665              title: t("melody.undo", "Undo Last"),
3666              children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCcw, { size: 20 })
3667            }
3668          )
3669        ] })
3670      ] })
3671    ] }),
3672    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 border-t border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3673      "button",
3674      {
3675        onClick: () => onSearch(notes),
3676        disabled: notes.length < 3,
3677        className: "px-6 py-2.5 bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed text-white rounded-lg font-bold flex items-center gap-2 transition-all shadow-lg shadow-indigo-500/20",
3678        children: [
3679          /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 18 }),
3680          " ",
3681          t("melody.findMatches", "Find Matching Tracks")
3682        ]
3683      }
3684    ) })
3685  ] }) });
3686};
3687const Metronome = ({ bpm, isPlaying, isBuffering, currentTime, timeSignature = { numerator: 4, denominator: 4 } }) => {
3688  const { t } = useTranslation();
3689  const [soundEnabled, setSoundEnabled] = reactExports.useState(false);
3690  const audioCtxRef = reactExports.useRef(null);
3691  const lastBeatRef = reactExports.useRef(-1);
3692  const [displayAngle, setDisplayAngle] = reactExports.useState(0);
3693  const requestRef = reactExports.useRef();
3694  const startTimeRef = reactExports.useRef(0);
3695  const lastSyncTimeRef = reactExports.useRef(0);
3696  const initAudio = () => {
3697    if (!audioCtxRef.current) {
3698      audioCtxRef.current = new (window.AudioContext || window.webkitAudioContext)();
3699    }
3700    if (audioCtxRef.current.state === "suspended") {
3701      audioCtxRef.current.resume().catch(() => {
3702      });
3703    }
3704  };
3705  const playTick = (isDownbeat) => {
3706    if (!audioCtxRef.current || !soundEnabled) return;
3707    const now = audioCtxRef.current.currentTime;
3708    const osc = audioCtxRef.current.createOscillator();
3709    const gain = audioCtxRef.current.createGain();
3710    const filter = audioCtxRef.current.createBiquadFilter();
3711    osc.type = isDownbeat ? "sine" : "triangle";
3712    osc.frequency.setValueAtTime(isDownbeat ? 2e3 : 800, now);
3713    filter.type = "lowpass";
3714    filter.frequency.setValueAtTime(isDownbeat ? 4e3 : 2e3, now);
3715    gain.gain.setValueAtTime(0, now);
3716    gain.gain.linearRampToValueAtTime(isDownbeat ? 0.8 : 0.3, now + 2e-3);
3717    gain.gain.exponentialRampToValueAtTime(1e-3, now + (isDownbeat ? 0.15 : 0.06));
3718    osc.connect(filter);
3719    filter.connect(gain);
3720    gain.connect(audioCtxRef.current.destination);
3721    osc.start(now);
3722    osc.stop(now + (isDownbeat ? 0.12 : 0.08));
3723    setTimeout(() => {
3724      osc.disconnect();
3725      gain.disconnect();
3726      filter.disconnect();
3727    }, 200);
3728  };
3729  const animate = (time) => {
3730    if (isPlaying && !isBuffering) {
3731      const elapsedSinceSync = (time - lastSyncTimeRef.current) / 1e3;
3732      const estimatedTime = startTimeRef.current + elapsedSinceSync;
3733      const phase = Math.PI * bpm * estimatedTime / 60;
3734      const angle = 25 * Math.cos(phase);
3735      setDisplayAngle(angle);
3736      const beatDuration = 60 / bpm;
3737      const currentBeat = Math.floor(estimatedTime / beatDuration);
3738      if (currentBeat !== lastBeatRef.current) {
3739        if (lastBeatRef.current !== -1 && Math.abs(currentBeat - lastBeatRef.current) === 1) {
3740          const isDownbeat = currentBeat % timeSignature.numerator === 0;
3741          playTick(isDownbeat);
3742        }
3743        lastBeatRef.current = currentBeat;
3744      }
3745    } else {
3746      setDisplayAngle(0);
3747      lastBeatRef.current = -1;
3748    }
3749    requestRef.current = requestAnimationFrame(animate);
3750  };
3751  reactExports.useEffect(() => {
3752    requestRef.current = requestAnimationFrame(animate);
3753    return () => {
3754      if (requestRef.current) cancelAnimationFrame(requestRef.current);
3755    };
3756  }, [isPlaying, isBuffering, bpm, soundEnabled]);
3757  reactExports.useEffect(() => {
3758    lastSyncTimeRef.current = performance.now();
3759    startTimeRef.current = currentTime;
3760    const beatDuration = 60 / bpm;
3761    lastBeatRef.current = Math.floor(currentTime / beatDuration);
3762  }, [currentTime, bpm]);
3763  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center group relative h-full py-1", children: [
3764    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
3765      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-8 h-9 shrink-0 flex items-end justify-center", children: [
3766        /* @__PURE__ */ jsxRuntimeExports.jsxs(
3767          "div",
3768          {
3769            className: "absolute inset-0 bg-[#5b3f2e] dark:bg-[#3d2b1f] shadow-inner",
3770            style: { clipPath: "polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%)" },
3771            children: [
3772              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-b from-black/20 to-transparent pointer-events-none" }),
3773              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-2 left-1/2 -translate-x-1/2 w-14 h-14 border border-zinc-200/20 rounded-full opacity-40 pointer-events-none" })
3774            ]
3775          }
3776        ),
3777        /* @__PURE__ */ jsxRuntimeExports.jsx(
3778          "div",
3779          {
3780            className: "absolute bottom-1.5 w-0.5 h-6 bg-zinc-300 dark:bg-zinc-300 origin-bottom will-change-transform z-10",
3781            style: { transform: `rotate(${displayAngle}deg)` },
3782            children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 left-1/2 -translate-x-1/2 w-2.5 h-2 bg-gradient-to-br from-amber-200 to-amber-600 rounded-[1px] shadow-sm border border-amber-700/50" })
3783          }
3784        ),
3785        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-0.5 w-1.5 h-1.5 bg-zinc-800 dark:bg-zinc-200 rounded-full z-20 border border-zinc-600 shadow-md translate-y-0.5" })
3786      ] }),
3787      /* @__PURE__ */ jsxRuntimeExports.jsx(
3788        "button",
3789        {
3790          onClick: (e) => {
3791            e.stopPropagation();
3792            initAudio();
3793            setSoundEnabled(!soundEnabled);
3794          },
3795          className: `absolute -bottom-2 -right-3 p-1 rounded-full transition-all border shadow-sm z-30 scale-90 ${soundEnabled ? "text-[#f50] bg-white dark:bg-[#1a1a1a] border-[#f50]/30" : "text-zinc-500 bg-white dark:bg-[#1a1a1a] border-zinc-200 dark:border-zinc-700 hover:text-zinc-700 dark:hover:text-zinc-300"}`,
3796          title: soundEnabled ? t("player.muteMetronome", "Mute Metronome") : t("player.unmuteMetronome", "Unmute Metronome"),
3797          children: soundEnabled ? /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(VolumeX, { size: 12 })
3798        }
3799      )
3800    ] }),
3801    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute -top-8 left-1/2 -translate-x-1/2 bg-zinc-800 text-white text-[10px] px-1.5 py-0.5 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap font-mono", children: [
3802      Math.round(bpm),
3803      " BPM"
3804    ] })
3805  ] });
3806};
3807const AutoScrollingText = ({
3808  text,
3809  className = "",
3810  containerClassName = "",
3811  scrollDuration = 8,
3812  style,
3813  shrinkOnOverflow = false
3814}) => {
3815  const containerRef = reactExports.useRef(null);
3816  const textRef = reactExports.useRef(null);
3817  const [overflows, setOverflows] = reactExports.useState(false);
3818  const [shrunkFontSize, setShrunkFontSize] = reactExports.useState(null);
3819  reactExports.useEffect(() => {
3820    const checkOverflow = () => {
3821      if (containerRef.current && textRef.current) {
3822        const isOverflowing = textRef.current.offsetWidth > containerRef.current.offsetWidth;
3823        setOverflows(isOverflowing);
3824        if (shrinkOnOverflow && isOverflowing && containerRef.current) {
3825          const containerWidth = containerRef.current.offsetWidth;
3826          const textWidth = textRef.current.scrollWidth;
3827          const currentSize = parseFloat(getComputedStyle(textRef.current).fontSize);
3828          const ratio = containerWidth / textWidth;
3829          const newSize = Math.max(9, Math.floor(currentSize * ratio));
3830          setShrunkFontSize(newSize);
3831        } else {
3832          setShrunkFontSize(null);
3833        }
3834      }
3835    };
3836    checkOverflow();
3837    const timer = setTimeout(checkOverflow, 150);
3838    window.addEventListener("resize", checkOverflow);
3839    return () => {
3840      clearTimeout(timer);
3841      window.removeEventListener("resize", checkOverflow);
3842    };
3843  }, [text, shrinkOnOverflow]);
3844  if (shrinkOnOverflow && shrunkFontSize !== null) {
3845    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: containerRef, className: `w-full truncate flex items-center ${containerClassName}`, style, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: textRef, className: `${className}`, style: { fontSize: `${shrunkFontSize}px` }, children: text }) });
3846  }
3847  if (overflows && !shrinkOnOverflow) {
3848    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: containerRef, className: `overflow-hidden relative w-full flex items-center ${containerClassName}`, style, children: [
3849      /* @__PURE__ */ jsxRuntimeExports.jsxs(
3850        "div",
3851        {
3852          className: "animate-marquee-infinite flex w-max absolute inset-y-0 left-0 items-center",
3853          style: { "--scroll-dist": "-50%", "--animation-duration": `${scrollDuration}s` },
3854          children: [
3855            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-6 pr-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `whitespace-nowrap ${className}`, children: text }) }),
3856            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-6 pr-6", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `whitespace-nowrap ${className}`, children: text }) })
3857          ]
3858        }
3859      ),
3860      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `invisible whitespace-nowrap ${className}`, children: text })
3861    ] });
3862  }
3863  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: containerRef, className: `w-full truncate flex items-center ${containerClassName}`, style, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: textRef, className: `${className}`, children: text }) });
3864};
3865const generateVerticalScrollCSS = (trackId, itemCount) => {
3866  const animationName = `slide-up-instruments-${trackId}`;
3867  const itemHeight = 20;
3868  let keyframes = `@keyframes ${animationName} {
3869`;
3870  const stepPercent = 100 / itemCount;
3871  const transitionPercent = 3.5;
3872  for (let i = 0; i <= itemCount; i++) {
3873    const yOffset = -(i * itemHeight);
3874    const startPct = i * stepPercent;
3875    const endPct = (i + 1) * stepPercent - transitionPercent;
3876    if (i === itemCount) {
3877      keyframes += `  100% { transform: translateY(${yOffset}px); }
3878`;
3879    } else {
3880      keyframes += `  ${startPct.toFixed(1)}%, ${endPct.toFixed(1)}% { transform: translateY(${yOffset}px); }
3881`;
3882    }
3883  }
3884  keyframes += `}`;
3885  return { animationName, css: keyframes };
3886};
3887const MidiThumbnail = ({
3888  track,
3889  isHovered,
3890  isActive,
3891  currentProgress,
3892  waveform,
3893  variant,
3894  isTitleOverflowing = false,
3895  titleContainerRef,
3896  titleRef,
3897  className = "",
3898  onClick
3899}) => {
3900  const displayArtist = track.artist && !["unknown", "unknown artist"].includes(track.artist.toLowerCase().trim()) ? track.artist : null;
3901  const hashColors = getColorsFromHash(track.title + (displayArtist || ""));
3902  const isCompact = variant === "player-thumbnail" || variant === "play-button" || variant === "small";
3903  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3904    "div",
3905    {
3906      onClick,
3907      style: { background: hashColors.gradient },
3908      className: `relative overflow-hidden flex items-center justify-center select-none ${className}`,
3909      children: [
3910        !isCompact && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/55 via-black/15 to-black/35 z-10 pointer-events-none" }),
3911        variant === "standard" && displayArtist && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-3 left-3 z-20 pointer-events-none max-w-[65%] h-6 flex items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3912          AutoScrollingText,
3913          {
3914            text: displayArtist,
3915            className: "text-[10px] font-bold text-zinc-300 drop-shadow-[0_1px_3px_rgba(0,0,0,0.85)] uppercase tracking-widest leading-none",
3916            containerClassName: "h-3.5 flex items-center"
3917          }
3918        ) }),
3919        variant === "standard" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-0 bottom-0 flex items-center justify-center z-11 opacity-65 group-hover:opacity-95 transition-opacity pointer-events-none select-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3920          Waveform,
3921          {
3922            data: waveform.length > 0 ? waveform : generatePseudoWaveform(String(track.id), 1e3),
3923            className: "w-full h-full",
3924            active: isActive,
3925            progress: currentProgress,
3926            colorBg: hashColors.waveformBg,
3927            colorFg: hashColors.waveformFg,
3928            colorBgActive: hashColors.waveformBgActive,
3929            colorFgActive: hashColors.waveformFgActive
3930          }
3931        ) }),
3932        variant === "standard" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute z-12 left-4 right-4 top-1/2 -translate-y-1/2 text-center pointer-events-none flex flex-col items-center justify-center w-[calc(100%-2rem)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3933          AutoScrollingText,
3934          {
3935            text: track.title,
3936            className: "font-black text-sm sm:text-base text-white drop-shadow-[0_2px_10px_rgba(0,0,0,0.7)] leading-tight",
3937            containerClassName: "h-6 flex items-center justify-center"
3938          }
3939        ) }),
3940        (variant === "small" || isCompact) && /* @__PURE__ */ jsxRuntimeExports.jsx(
3941          Music,
3942          {
3943            size: variant === "small" ? 16 : 14,
3944            className: "midi-thumbnail-icon text-white/70 drop-shadow-[0_0_4px_rgba(255,255,255,0.2)] z-10 pointer-events-none"
3945          }
3946        ),
3947        variant === "standard" && track.instruments && track.instruments.length > 0 && (() => {
3948          const validInsts = track.instruments.filter((inst) => inst.notesCount > 0 && inst.midiChannel !== -1);
3949          const uniqueNames = Array.from(new Set(validInsts.map((inst) => getInstrumentName(inst.program, inst.midiChannel))));
3950          if (uniqueNames.length === 0 && track.instruments.length > 0) {
3951            uniqueNames.push(getInstrumentName(track.instruments[0].program, track.instruments[0].midiChannel));
3952          }
3953          if (uniqueNames.length === 0) return null;
3954          const shouldAnimate = uniqueNames.length > 1;
3955          const { animationName, css } = generateVerticalScrollCSS(String(track.id), uniqueNames.length);
3956          return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3957            shouldAnimate && /* @__PURE__ */ jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: css } }),
3958            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-5 overflow-hidden z-12 bg-[#18181b] border-t border-indigo-500/20", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3959              "div",
3960              {
3961                className: "flex flex-col m-0 p-0 list-none",
3962                style: shouldAnimate ? {
3963                  animation: `${animationName} ${uniqueNames.length * 3.5}s cubic-bezier(0.85, 0, 0.15, 1) infinite`
3964                } : void 0,
3965                children: [
3966                  uniqueNames.map((name, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5 flex items-center justify-center shrink-0 select-none", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[8.5px] font-black text-zinc-100 dark:text-zinc-50 whitespace-nowrap uppercase tracking-widest flex items-center gap-1 drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]", children: [
3967                    "🎹 ",
3968                    name
3969                  ] }) }, i)),
3970                  shouldAnimate && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5 flex items-center justify-center shrink-0 select-none", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[8.5px] font-black text-zinc-100 dark:text-zinc-50 whitespace-nowrap uppercase tracking-widest flex items-center gap-1 drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]", children: [
3971                    "🎹 ",
3972                    uniqueNames[0]
3973                  ] }) })
3974                ]
3975              }
3976            ) })
3977          ] });
3978        })()
3979      ]
3980    }
3981  );
3982};
3983const MidiCard = ({
3984  track,
3985  onSelect,
3986  onToggleWatchLater,
3987  isWatchLater = false,
3988  onAddToQueue,
3989  onAddToPlaylist,
3990  onArtistSelect,
3991  variant = "standard",
3992  layout = "grid",
3993  className = "",
3994  sizeClass = "",
3995  playlist
3996}) => {
3997  var _a, _b, _c;
3998  const [isMenuOpen, setIsMenuOpen] = reactExports.useState(false);
3999  const menuRef = reactExports.useRef(null);
4000  const { t } = useTranslation();
4001  const { activeTrack, isPlaying, playTrack, togglePlay, currentTime, duration, seekTo, isBuffering } = usePlayer();
4002  const { staffModeEnabled } = useAdmin();
4003  const [isHovered, setIsHovered] = reactExports.useState(false);
4004  const [isTitleOverflowing, setIsTitleOverflowing] = reactExports.useState(false);
4005  const titleContainerRef = reactExports.useRef(null);
4006  const titleRef = reactExports.useRef(null);
4007  const handleMouseEnter = () => {
4008    setIsHovered(true);
4009    if (titleContainerRef.current && titleRef.current) {
4010      setIsTitleOverflowing(titleRef.current.scrollWidth > titleContainerRef.current.clientWidth || titleRef.current.scrollHeight > titleContainerRef.current.clientHeight);
4011    }
4012  };
4013  const [currentStatus, setCurrentStatus] = reactExports.useState(track.visibility);
4014  const [commentText, setCommentText] = reactExports.useState("");
4015  const isActive = (activeTrack == null ? void 0 : activeTrack.id) === track.id;
4016  const isCommunityContributor = !track.uploader || track.uploader.id === -1 || track.uploader.name === "Community Contributor";
4017  const currentProgress = isActive && duration > 0 ? currentTime / duration : 0;
4018  const displayArtist = track.artist && !["unknown", "unknown artist"].includes(track.artist.toLowerCase().trim()) ? track.artist : null;
4019  const hashColors = getColorsFromHash(track.title + (displayArtist || ""));
4020  const cardRef = reactExports.useRef(null);
4021  const [isInView, setIsInView] = reactExports.useState(false);
4022  reactExports.useEffect(() => {
4023    const observer = new IntersectionObserver((entries) => {
4024      if (entries[0].isIntersecting) {
4025        setIsInView(true);
4026        observer.disconnect();
4027      }
4028    }, { rootMargin: "300px" });
4029    if (cardRef.current) {
4030      observer.observe(cardRef.current);
4031    }
4032    return () => observer.disconnect();
4033  }, []);
4034  const { waveform } = useWaveform(String(track.id), isInView ? track.waveformJsonPath : void 0, track.waveform);
4035  reactExports.useEffect(() => {
4036    const handleClickOutside = (event) => {
4037      if (menuRef.current && !menuRef.current.contains(event.target)) {
4038        setIsMenuOpen(false);
4039      }
4040    };
4041    document.addEventListener("mousedown", handleClickOutside);
4042    return () => document.removeEventListener("mousedown", handleClickOutside);
4043  }, []);
4044  reactExports.useEffect(() => {
4045    const checkOverflow = () => {
4046      if (titleContainerRef.current && titleRef.current) {
4047        setIsTitleOverflowing(
4048          titleRef.current.scrollWidth > titleContainerRef.current.clientWidth
4049        );
4050      }
4051    };
4052    checkOverflow();
4053    const timer = setTimeout(checkOverflow, 150);
4054    window.addEventListener("resize", checkOverflow);
4055    return () => {
4056      clearTimeout(timer);
4057      window.removeEventListener("resize", checkOverflow);
4058    };
4059  }, [track.title, variant, layout]);
4060  const handleArtistClick = (e) => {
4061    e.preventDefault();
4062    e.stopPropagation();
4063    if (!displayArtist) return;
4064    const targetUrl = `/?q=${encodeURIComponent(displayArtist)}`;
4065    if (e.ctrlKey || e.metaKey || e.shiftKey || e.button === 1) {
4066      window.open(targetUrl, "_blank");
4067    } else if (onArtistSelect) {
4068      onArtistSelect(displayArtist);
4069    } else {
4070      window.location.href = targetUrl;
4071    }
4072  };
4073  const handleMenuClick = (e) => {
4074    e.stopPropagation();
4075    e.preventDefault();
4076    setIsMenuOpen(!isMenuOpen);
4077  };
4078  const handleToggleVisibility = async (e) => {
4079    e.stopPropagation();
4080    e.preventDefault();
4081    try {
4082      const result = await api.toggleTrackVisibility(track.id);
4083      setCurrentStatus(result.status);
4084    } catch (e2) {
4085      console.error(e2);
4086    }
4087  };
4088  const handleClick = (e) => {
4089    var _a2;
4090    if ((_a2 = window.getSelection()) == null ? void 0 : _a2.toString()) {
4091      return;
4092    }
4093    const targetElement = e.target;
4094    if (targetElement == null ? void 0 : targetElement.closest("button")) {
4095      e.preventDefault();
4096      e.stopPropagation();
4097      return;
4098    }
4099    if (e.ctrlKey || e.metaKey || e.shiftKey || e.button === 1) {
4100      return;
4101    }
4102    e.preventDefault();
4103    e.stopPropagation();
4104    onSelect(track);
4105  };
4106  const renderStatusBadge = () => {
4107    if (!track.status || track.status === "completed") return null;
4108    let currentStatus2 = track.status;
4109    if (currentStatus2 === "pending") {
4110      currentStatus2 = "processing";
4111    } else if (currentStatus2 !== "rejected" && currentStatus2 !== "suspended" && currentStatus2 !== "processing") {
4112      currentStatus2 = "processing";
4113    }
4114    let config = {
4115      label: currentStatus2.toUpperCase(),
4116      className: "bg-zinc-500/90",
4117      icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 10 })
4118    };
4119    switch (currentStatus2) {
4120      case "processing":
4121        config = {
4122          label: t("status.processing", "Processing..."),
4123          className: "bg-amber-500/90",
4124          icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Loader, { size: 10, className: "animate-spin" })
4125        };
4126        break;
4127      case "pending":
4128      case "paused":
4129        config = {
4130          label: t("status.pending", "Pending"),
4131          className: "bg-orange-500/90",
4132          icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 10 })
4133        };
4134        break;
4135      case "rejected":
4136      case "suspended":
4137        config = {
4138          label: currentStatus2.toUpperCase(),
4139          className: "bg-red-600/90",
4140          icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 10 })
4141        };
4142        break;
4143    }
4144    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `absolute top-1.5 right-1.5 ${config.className} text-white text-[9px] font-bold px-1.5 py-0.5 rounded flex items-center gap-1 shadow-lg backdrop-blur-[2px] z-25`, children: [
4145      config.icon,
4146      " ",
4147      config.label
4148    ] });
4149  };
4150  const renderStaffOverlay = () => {
4151    if (!staffModeEnabled) return null;
4152    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-1.5 left-1.5 z-30 flex items-center gap-1", children: [
4153      /* @__PURE__ */ jsxRuntimeExports.jsxs(
4154        "button",
4155        {
4156          onClick: handleToggleVisibility,
4157          className: `p-1.5 rounded-lg shadow-lg backdrop-blur-md flex items-center gap-1.5 text-[10px] font-bold transition-all ${currentStatus === "suspended" ? "bg-red-600 text-white" : "bg-black/60 text-white hover:bg-black/80"}`,
4158          title: currentStatus === "suspended" ? "Suspended (Click to Unhide)" : "Visible (Click to Hide)",
4159          children: [
4160            currentStatus === "suspended" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 12 }),
4161            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline", children: currentStatus === "suspended" ? "UNHIDE" : "HIDE" })
4162          ]
4163        }
4164      ),
4165      /* @__PURE__ */ jsxRuntimeExports.jsxs(
4166        "button",
4167        {
4168          onClick: (e) => {
4169            e.stopPropagation();
4170            e.preventDefault();
4171            window.open(`/admin/database/table/Midi?search=${track.id}`, "_blank");
4172          },
4173          className: "p-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg shadow-lg backdrop-blur-md flex items-center gap-1.5 text-[10px] font-bold transition-all",
4174          children: [
4175            /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { size: 12 }),
4176            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline", children: "EDIT" })
4177          ]
4178        }
4179      )
4180    ] });
4181  };
4182  const menuDropdownContent = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute right-0 top-6 w-48 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded-lg shadow-xl z-50 overflow-hidden py-1", onClick: (e) => e.stopPropagation(), children: [
4183    onAddToQueue && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: (e) => {
4184      e.preventDefault();
4185      onAddToQueue(track);
4186      setIsMenuOpen(false);
4187    }, className: "w-full text-left px-4 py-2 text-xs text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 flex items-center gap-2", children: [
4188      /* @__PURE__ */ jsxRuntimeExports.jsx(ListPlus, { size: 14 }),
4189      " ",
4190      t("player.addToQueue", "Add to queue")
4191    ] }),
4192    onToggleWatchLater && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: (e) => {
4193      e.preventDefault();
4194      onToggleWatchLater(track.id, e);
4195      setIsMenuOpen(false);
4196    }, className: "w-full text-left px-4 py-2 text-xs text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 flex items-center gap-2", children: [
4197      /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 14 }),
4198      " ",
4199      isWatchLater ? t("list.removeWatchLater", "Remove from Later") : t("nav.watchLater", "Watch Later")
4200    ] }),
4201    onAddToPlaylist && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: (e) => {
4202      e.preventDefault();
4203      onAddToPlaylist(track);
4204      setIsMenuOpen(false);
4205    }, className: "w-full text-left px-4 py-2 text-xs text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 flex items-center gap-2", children: [
4206      /* @__PURE__ */ jsxRuntimeExports.jsx(FolderPlus, { size: 14 }),
4207      " ",
4208      t("playlists.saveTo", "Save to playlist")
4209    ] })
4210  ] });
4211  if (variant === "player-thumbnail") {
4212    return /* @__PURE__ */ jsxRuntimeExports.jsx(
4213      "a",
4214      {
4215        ref: cardRef,
4216        href: generateTrackUrl(track),
4217        onClick: handleClick,
4218        className: "block",
4219        children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4220          MidiThumbnail,
4221          {
4222            track,
4223            isHovered,
4224            isActive,
4225            currentProgress,
4226            waveform,
4227            variant: "player-thumbnail",
4228            className: `${sizeClass || "w-10 h-10 rounded"} cursor-pointer hover:opacity-85 transition-opacity ${className}`
4229          }
4230        )
4231      }
4232    );
4233  }
4234  if (variant === "play-button") {
4235    return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4236      "a",
4237      {
4238        ref: cardRef,
4239        href: generateTrackUrl(track),
4240        onClick: handleClick,
4241        onMouseEnter: handleMouseEnter,
4242        onMouseLeave: () => setIsHovered(false),
4243        className: `flex items-center justify-between gap-3 bg-zinc-100 dark:bg-zinc-900/60 p-1 rounded-lg border border-zinc-200/50 dark:border-zinc-800/40 hover:border-zinc-300 dark:hover:border-zinc-700 hover:shadow-sm cursor-pointer group no-underline text-inherit ${className}`,
4244        children: [
4245          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
4246            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-10 h-10 rounded bg-zinc-800 overflow-hidden flex items-center justify-center shrink-0", children: [
4247              /* @__PURE__ */ jsxRuntimeExports.jsx(
4248                MidiThumbnail,
4249                {
4250                  track,
4251                  isHovered,
4252                  isActive,
4253                  currentProgress,
4254                  waveform,
4255                  variant: "play-button",
4256                  className: "w-full h-full"
4257                }
4258              ),
4259              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/40 flex items-center justify-center transition-all z-10 select-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4260                "button",
4261                {
4262                  onTouchStart: (e) => e.stopPropagation(),
4263                  onTouchEnd: (e) => e.stopPropagation(),
4264                  onClick: (e) => {
4265                    e.preventDefault();
4266                    e.stopPropagation();
4267                    playTrack(track, playlist);
4268                  },
4269                  className: "w-6 h-6 bg-indigo-600 text-white rounded-full flex items-center justify-center hover:scale-105",
4270                  children: isActive && isPlaying ? isBuffering ? /* @__P
4270URE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 12, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12, fill: "currentColor", className: "ml-0.5" })
4271                }
4272              ) })
4273            ] }),
4274            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-center", children: [
4275              /* @__PURE__ */ jsxRuntimeExports.jsx(
4276                AutoScrollingText,
4277                {
4278                  text: track.title,
4279                  className: "font-bold text-xs text-zinc-900 dark:text-white leading-tight",
4280                  containerClassName: "h-5 flex items-center"
4281                }
4282              ),
4283              displayArtist && /* @__PURE__ */ jsxRuntimeExports.jsx(
4284                AutoScrollingText,
4285                {
4286                  text: displayArtist,
4287                  className: "text-[10px] text-zinc-500 leading-tight",
4288                  containerClassName: "h-4 mt-0.5 flex items-center"
4289                }
4290              )
4291            ] })
4292          ] }),
4293          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 text-[10px] text-zinc-400 dark:text-zinc-500 font-medium shrink-0 pr-1", children: [
4294            !isCommunityContributor && ((_a = track.uploader) == null ? void 0 : _a.name) ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "hidden sm:inline-flex items-center gap-1.5 w-28 shrink-0 min-w-0", children: [
4295              /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: track.uploader.avatar, name: track.uploader.name, size: 14 }),
4296              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 dark:text-zinc-400 font-semibold truncate", children: track.uploader.name })
4297            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline-flex w-28 shrink-0" }),
4298            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden md:inline w-20 shrink-0", children: timeAgo(track.uploadedAt) }),
4299            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden lg:inline w-10 shrink-0 text-right", children: track.duration })
4300          ] })
4301        ]
4302      }
4303    );
4304  }
4305  if (variant === "small") {
4306    return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4307      "a",
4308      {
4309        ref: cardRef,
4310        href: generateTrackUrl(track),
4311        onClick: handleClick,
4312        className: `flex flex-col group w-full bg-white dark:bg-zinc-900 p-1.5 rounded-xl transition-all relative border border-zinc-200/50 dark:border-zinc-800/40 hover:border-zinc-300 dark:hover:border-zinc-700 hover:shadow-sm cursor-pointer no-underline text-inherit ${className} midi-small-card-custom midi-card-container`,
4313        onMouseEnter: handleMouseEnter,
4314        onMouseLeave: () => setIsHovered(false),
4315        style: isHovered ? { boxShadow: hashColors.glow } : void 0,
4316        children: [
4317          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
4318            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-10 h-10 shrink-0 bg-zinc-100 dark:bg-zinc-800 rounded-lg overflow-hidden flex items-center justify-center", children: [
4319              /* @__PURE__ */ jsxRuntimeExports.jsx(
4320                MidiThumbnail,
4321                {
4322                  track,
4323                  isHovered,
4324                  isActive,
4325                  currentProgress,
4326                  waveform,
4327                  variant: "small",
4328                  className: "w-full h-full"
4329                }
4330              ),
4331              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute inset-0 bg-black/40 flex items-center justify-center transition-opacity z-10 select-none ${isHovered || isActive ? "opacity-100" : "opacity-0"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4332                "button",
4333                {
4334                  onTouchStart: (e) => e.stopPropagation(),
4335                  onTouchEnd: (e) => e.stopPropagation(),
4336                  onClick: (e) => {
4337                    e.preventDefault();
4338                    e.stopPropagation();
4339                    playTrack(track, playlist);
4340                  },
4341                  className: "w-6 h-6 bg-indigo-600 hover:bg-indigo-500 text-white rounded-full flex items-center justify-center transition-all transform scale-90 hover:scale-100",
4342                  children: isActive && isPlaying ? isBuffering ? /* @__P
4342URE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 10, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { fill: "currentColor", size: 10 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { fill: "currentColor", size: 10, className: "ml-0.5" })
4343                }
4344              ) })
4345            ] }),
4346            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 pr-6 flex flex-col justify-center", children: [
4347              /* @__PURE__ */ jsxRuntimeExports.jsx(
4348                AutoScrollingText,
4349                {
4350                  text: track.title,
4351                  className: "font-bold text-xs text-zinc-900 dark:text-white group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors leading-tight",
4352                  containerClassName: "h-5 flex items-center",
4353                  shrinkOnOverflow: true
4354                }
4355              ),
4356              (() => {
4357                const validInstruments = (track.instruments || []).filter(
4358                  (inst) => inst.notesCount === void 0 || inst.notesCount > 0
4359                );
4360                const uniqueNames = Array.from(new Set(validInstruments.map((inst) => getInstrumentName(inst.program, inst.midiChannel))));
4361                if (isHovered && uniqueNames.length > 0) {
4362                  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-1 w-full overflow-hidden mt-0.5 pointer-events-none", children: [
4363                    uniqueNames.slice(0, 2).map((name, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[7.5px] font-bold text-indigo-500 dark:text-indigo-400 whitespace-nowrap bg-indigo-500/10 dark:bg-indigo-500/20 px-1 py-0.5 rounded", children: [
4364                      "🎹 ",
4365                      name
4366                    ] }, i)),
4367                    uniqueNames.length > 2 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[7.5px] font-bold text-indigo-500 dark:text-indigo-400 whitespace-nowrap bg-indigo-500/10 dark:bg-indigo-500/20 px-1 py-0.5 rounded", children: [
4368                      "+",
4369                      uniqueNames.length - 2
4370                    ] })
4371                  ] });
4372                }
4373                return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 dark:text-zinc-400 leading-normal mt-0.5 w-full flex items-center gap-1.5 select-none font-medium truncate", children: [
4374                  displayArtist ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4375                    "button",
4376                    {
4377                      type: "button",
4378                      onClick: handleArtistClick,
4379                      onAuxClick: handleArtistClick,
4380                      className: "truncate max-w-[120px] hover:text-indigo-600 dark:hover:text-indigo-400 hover:underline cursor-pointer transition-colors text-left font-medium",
4381                      title: `Explore all tracks by ${displayArtist}`,
4382                      children: displayArtist
4383                    }
4384                  ) : null,
4385                  displayArtist ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-350 dark:text-zinc-700 select-none", children: "•" }) : null,
4386                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: track.duration }),
4387                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-350 dark:text-zinc-700 select-none", children: "•" }),
4388                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-0.5 shrink-0", children: [
4389                    /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 8 }),
4390                    " ",
4391                    track.plays
4392                  ] })
4393                ] });
4394              })()
4395            ] })
4396          ] }),
4397          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute right-2 top-2.5 shrink-0 z-20", ref: menuRef, children: [
4398            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleMenuClick, className: "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 p-1 rounded-full opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { size: 14 }) }),
4399            isMenuOpen && menuDropdownContent
4400          ] })
4401        ]
4402      }
4403    );
4404  }
4405  if (layout === "list" || variant === "list") {
4406    return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4407      "a",
4408      {
4409        href: generateTrackUrl(track),
4410        onClick: handleClick,
4411        className: `flex gap-4 group w-full bg-white dark:bg-[#18181b] p-2 rounded-xl transition-all relative border border-zinc-200/50 dark:border-zinc-800/40 hover:border-zinc-300 dark:hover:border-zinc-700 hover:shadow-lg no-underline text-inherit ${className}`,
4412        onMouseEnter: handleMouseEnter,
4413        onMouseLeave: () => setIsHovered(false),
4414        style: isHovered ? { boxShadow: hashColors.glow } : void 0,
4415        children: [
4416          /* @__PURE__ */ jsxRuntimeExports.jsxs(
4417            "div",
4418            {
4419              className: "relative w-48 h-28 sm:w-56 sm:h-32 shrink-0 bg-zinc-950 rounded-lg overflow-hidden shadow-sm flex items-center justify-center cursor-pointer",
4420              onClick: (e) => {
4421                e.preventDefault();
4422                e.stopPropagation();
4423                playTrack(track, playlist);
4424              },
4425              children: [
4426                /* @__PURE__ */ jsxRuntimeExports.jsx(
4427                  MidiThumbnail,
4428                  {
4429                    track,
4430                    isHovered,
4431                    isActive,
4432                    currentProgress,
4433                    waveform,
4434                    variant: "standard",
4435                    isTitleOverflowing: false,
4436                    className: "w-full h-full"
4437                  }
4438                ),
4439                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-1.5 right-1.5 bg-black/80 text-white text-[10px] font-bold px-1.5 py-0.5 rounded shadow-lg backdrop-blur-[2px] z-20", children: track.duration }),
4440                ((_b = track.midiMetadata) == null ? void 0 : _b.lyrics) && track.midiMetadata.lyrics.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-1.5 left-1.5 bg-indigo-600/90 text-white text-[9px] font-bold px-1.5 py-0.5 rounded flex items-center gap-1 shadow-lg z-20", children: [
4441                  /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 10 }),
4442                  " ",
4443                  t("midi.karaoke", "Karaoke")
4444                ] }),
4445                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute inset-0 bg-black/40 flex items-center justify-center transition-opacity z-10 pointer-events-none ${isHovered || isActive ? "opacity-100" : "opacity-0"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-10 h-10 bg-indigo-600/90 text-white rounded-full flex items-center justify-center transition-transform transform scale-90 group-hover:scale-100 shadow-lg backdrop-blur-sm", children: isActive && isPlaying ? isBuffering ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 20, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { fill: "currentColor", size: 20 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { fill: "currentColor", size: 20, className: "ml-1" }) }) }),
4446                renderStatusBadge(),
4447                renderStaffOverlay()
4448              ]
4449            }
4450          ),
4451          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-between py-1", children: [
4452            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start gap-4", children: [
4453              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
4454                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "block font-bold text-base text-zinc-900 dark:text-white hover:text-indigo-650 dark:hover:text-indigo-400 transition-colors w-full truncate", title: track.title, children: track.title }),
4455                displayArtist && /* @__PURE__ */ jsxRuntimeExports.jsx(
4456                  "button",
4457                  {
4458                    type: "button",
4459                    onClick: handleArtistClick,
4460                    onAuxClick: handleArtistClick,
4461                    className: "text-sm text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:underline transition-colors font-medium truncate mt-0.5 w-full text-left cursor-pointer",
4462                    title: `Explore all tracks by ${displayArtist}`,
4463                    children: displayArtist
4464                  }
4465                ),
4466                ((_c = track.uploader) == null ? void 0 : _c.name) && /* @__P
4466URE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mt-1.5", children: [
4467                  /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: track.uploader.avatar, name: track.uploader.name, size: 18 }),
4468                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-indigo-600 dark:text-indigo-400 font-bold truncate", children: track.uploader.name })
4469                ] })
4470              ] }),
4471              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", ref: menuRef, children: [
4472                /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleMenuClick, className: "text-zinc-500 p-1.5 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded-full opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { size: 16 }) }),
4473                isMenuOpen && menuDropdownContent
4474              ] })
4475            ] }),
4476            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center gap-3 mt-2 w-full h-12", children: [
4477              /* @__PURE__ */ jsxRuntimeExports.jsx(
4478                "button",
4479                {
4480                  onTouchStart: (e) => e.stopPropagation(),
4481                  onTouchEnd: (e) => e.stopPropagation(),
4482                  onClick: (e) => {
4483                    e.preventDefault();
4484                    e.stopPropagation();
4485                    playTrack(track, playlist);
4486                  },
4487                  className: `flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center transition-all ${isActive ? "bg-indigo-600 text-white shadow-md shadow-indigo-500/20" : "bg-indigo-50 hover:bg-indigo-400 text-white shadow-lg shadow-indigo-500/30"}`,
4488                  children: isActive && isPlaying ? isBuffering ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 20, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { fill: "currentColor", size: 20 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { fill: "currentColor", size: 20, className: isActive && isPlaying ? "" : "ml-0.5" })
4489                }
4490              ),
4491              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 relative h-full", children: [
4492                /* @__PURE__ */ jsxRuntimeExports.jsx(
4493                  Waveform,
4494                  {
4495                    data: waveform.length > 0 ? waveform : generatePseudoWaveform(String(track.id), 1e3),
4496                    className: "h-full",
4497                    active: isActive,
4498                    progress: currentProgress,
4499                    onSeek: (p) => {
4500                      if (isActive && duration > 0) seekTo(p * duration);
4501                      else {
4502                        playTrack(track, playlist);
4503                        setTimeout(() => seekTo(p * duration), 300);
4504                      }
4505                    },
4506                    colorBg: hashColors.waveformBg,
4507                    colorFg: hashColors.waveformFg,
4508                    colorBgActive: hashColors.waveformBgActive,
4509                    colorFgActive: hashColors.waveformFgActive
4510                  }
4511                ),
4512                isActive && isPlaying && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-indigo-500/5 mix-blend-overlay pointer-events-none pulse-animation" })
4513              ] })
4514            ] }),
4515            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mt-2 pt-2 border-t border-zinc-100 dark:border-zinc-800/60", children: [
4516              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
4517                /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "flex items-center gap-1.5 px-2 py-1 text-[10px] font-medium text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors", children: [
4518                  /* @__PURE__ */ jsxRuntimeExports.jsx(Heart, { size: 12 }),
4519                  " ",
4520                  track.likesCount || 0
4521                ] }),
4522                /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "flex items-center gap-1.5 px-2 py-1 text-[10px] font-medium text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors", children: [
4523                  /* @__PURE__ */ jsxRuntimeExports.jsx(Repeat, { size: 12 }),
4524                  " ",
4525                  track.repostsCount || 0
4526                ] }),
4527                /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "flex items-center gap-1.5 px-2 py-1 text-[10px] font-medium text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors", children: [
4528                  /* @__PURE__ */ jsxRuntimeExports.jsx(Share2, { size: 12 }),
4529                  " ",
4530                  t("actions.share", "Share")
4531                ] }),
4532                /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: (e) => {
4533                  navigator.clipboard.writeText(window.location.origin + generateTrackUrl(track));
4534                }, className: "flex items-center gap-1.5 px-2 py-1 text-[10px] font-medium text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors", children: [
4535                  /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { size: 12 }),
4536                  " ",
4537                  t("actions.copyLink", "Copy")
4538                ] }),
4539                onAddToQueue && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: (e) => {
4540                  e.preventDefault();
4541                  onAddToQueue(track);
4542                }, className: "flex items-center gap-1.5 px-2 py-1 text-[10px] font-medium text-zinc-600 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors", children: [
4543                  /* @__PURE__ */ jsxRuntimeExports.jsx(ListPlus, { size: 12 }),
4544                  " ",
4545                  t("player.addToQueue", "Add to queue")
4546                ] })
4547              ] }),
4548              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-[10px] text-zinc-500", children: [
4549                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
4550                  /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 10, className: "fill-current" }),
4551                  " ",
4552                  track.plays,
4553                  " views"
4554                ] }),
4555                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
4556                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeAgo(track.uploadedAt) })
4557              ] })
4558            ] }),
4559            isActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex items-center gap-2 animate-in slide-in-from-top-2 fade-in", children: [
4560              /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: void 0, name: "Current User", size: 24 }),
4561              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 relative", children: [
4562                /* @__PURE__ */ jsxRuntimeExports.jsx(
4563                  "input",
4564                  {
4565                    type: "text",
4566                    placeholder: "Write a comment...",
4567                    value: commentText,
4568                    onChange: (e) => setCommentText(e.target.value),
4569                    className: "w-full bg-zinc-100 dark:bg-zinc-800 border border-transparent focus:border-indigo-500 dark:focus:border-indigo-500 rounded-full px-4 py-1 text-[11px] text-zinc-900 dark:text-white outline-none transition-colors"
4570                  }
4571                ),
4572                commentText.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setCommentText(""), className: "absolute right-2 top-1/2 -translate-y-1/2 text-indigo-600 dark:text-indigo-400 text-[10px] font-bold hover:text-indigo-700", children: "Post" })
4573              ] })
4574            ] })
4575          ] })
4576        ]
4577      }
4578    );
4579  }
4580  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4581    "a",
4582    {
4583      ref: cardRef,
4584      href: generateTrackUrl(track),
4585      className: `flex flex-col w-full h-full bg-white dark:bg-zinc-900 rounded-2xl border border-zinc-200 dark:border-zinc-800 overflow-hidden hover:shadow-xl hover:-translate-y-1 hover:border-zinc-300 dark:hover:border-zinc-700 transition-all duration-300 cursor-pointer group block relative no-underline text-inherit ${className} midi-card-custom midi-card-container`,
4586      onClick: handleClick,
4587      onMouseEnter: handleMouseEnter,
4588      onMouseLeave: () => setIsHovered(false),
4589      style: isHovered ? { boxShadow: hashColors.glow } : void 0,
4590      children: [
4591        /* @__PURE__ */ jsxRuntimeExports.jsxs(
4592          "div",
4593          {
4594            className: "relative aspect-video w-full overflow-hidden bg-zinc-950 flex items-center justify-center shrink-0 cursor-pointer",
4595            onClick: (e) => {
4596              e.preventDefault();
4597              e.stopPropagation();
4598              playTrack(track, playlist);
4599            },
4600            children: [
4601              /* @__PURE__ */ jsxRuntimeExports.jsx(
4602                MidiThumbnail,
4603                {
4604                  track,
4605                  isHovered,
4606                  isActive,
4607                  currentProgress,
4608                  waveform,
4609                  variant: "standard",
4610                  isTitleOverflowing,
4611                  titleContainerRef,
4612                  titleRef,
4613                  className: "w-full h-full transition-transform duration-500 group-hover:scale-105"
4614                }
4615              ),
4616              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute inset-0 bg-black/40 flex items-center justify-center transition-opacity z-10 pointer-events-none ${isHovered || isActive ? "opacity-100" : "opacity-0"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 bg-indigo-600/90 text-white rounded-full flex items-center justify-center transition-transform transform scale-90 group-hover:scale-100 shadow-lg backdrop-blur-sm", children: isActive && isPlaying ? isBuffering ? /* @__P
4616URE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 24, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { fill: "currentColor", size: 24 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { fill: "currentColor", size: 24, className: "ml-1" }) }) }),
4617              renderStatusBadge(),
4618              renderStaffOverlay()
4619            ]
4620          }
4621        ),
4622        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2.5 pb-2 flex flex-col gap-1 bg-white dark:bg-zinc-900 z-20 shrink-0", children: [
4623          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start gap-2", children: [
4624            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
4625              /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-black text-sm text-zinc-900 dark:text-white leading-tight hover:text-indigo-650 dark:hover:text-indigo-400 transition-colors w-full truncate", title: track.title, children: track.title }),
4626              displayArtist ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4627                "button",
4628                {
4629                  type: "button",
4630                  onClick: handleArtistClick,
4631                  onAuxClick: handleArtistClick,
4632                  className: "text-[11px] text-zinc-500 dark:text-zinc-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:underline transition-colors font-semibold leading-normal w-full truncate mt-0.5 text-left cursor-pointer",
4633                  title: `Explore all tracks by ${displayArtist}`,
4634                  children: displayArtist
4635                }
4636              ) : null
4637            ] }),
4638            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", ref: menuRef, children: [
4639              onToggleWatchLater && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleMenuClick, className: "text-zinc-500 hover:text-zinc-300 p-1 rounded-full hover:bg-zinc-800 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { size: 14 }) }),
4640              isMenuOpen && menuDropdownContent
4641            ] })
4642          ] }),
4643          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-px bg-zinc-150 dark:bg-zinc-800/80 my-0.5" }),
4644          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 midi-card-bottom-row", children: [
4645            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 min-w-0 flex-1 uploader-info", children: !isCommunityContributor ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4646              /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: track.uploader.avatar, name: track.uploader.name, size: 18 }),
4647              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] font-bold text-zinc-600 dark:text-zinc-400 truncate", title: track.uploader.name, children: track.uploader.name })
4648            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] font-bold text-zinc-500", children: t("midi.community", "Community") }) }),
4649            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0 stats-controls", children: [
4650              /* @__PURE__ */ jsxRuntimeExports.jsx(
4651                "button",
4652                {
4653                  onTouchStart: (e) => e.stopPropagation(),
4654                  onTouchEnd: (e) => e.stopPropagation(),
4655                  onClick: (e) => {
4656                    e.preventDefault();
4657                    e.stopPropagation();
4658                    playTrack(track, playlist);
4659                  },
4660                  className: `flex items-center justify-center p-1.5 rounded-full transition-all shadow-sm ${isActive ? "bg-indigo-600 text-white" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
4661                  title: isActive && isPlaying ? t("player.pause", "Pause") : t("player.play", "Play"),
4662                  children: isActive && isPlaying ? isBuffering ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 10, className: "animate-spin text-white" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 10, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 10, className: "ml-0.5", fill: "currentColor" })
4663                }
4664              ),
4665              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 font-medium font-mono", children: track.duration }),
4666              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-500 font-medium flex items-center gap-0.5", children: [
4667                /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 9 }),
4668                " ",
4669                track.plays
4670              ] })
4671            ] })
4672          ] })
4673        ] })
4674      ]
4675    }
4676  );
4677};
4678const VideoCard = React.memo(MidiCard);
4679const parseDurationString = (str) => {
4680  if (!str) return 0;
4681  const parts = str.split(":").map(Number);
4682  if (parts.some(isNaN)) return 0;
4683  if (parts.length === 3) {
4684    return parts[0] * 3600 + parts[1] * 60 + parts[2];
4685  } else if (parts.length === 2) {
4686    return parts[0] * 60 + parts[1];
4687  }
4688  return parts[0] || 0;
4689};
4690function PersistentPlayer({
4691  track,
4692  isPlaying,
4693  setIsPlaying,
4694  onTimeUpdate,
4695  onNext,
4696  onPrev,
4697  toggleLike,
4698  isLiked,
4699  onTrackClick,
4700  onAddToPlaylist,
4701  queue = [],
4702  activePlaylist = null,
4703  playlistLabel = null,
4704  onClearPlaylist,
4705  onRemoveFromQueue,
4706  onPlayQueueTrack,
4707  onPlaybackRateChange,
4708  audioMode,
4709  setAudioMode,
4710  isBuffering = false,
4711  seekTo,
4712  isGameMode = false,
4713  currentUser,
4714  audioQuality,
4715  pitchSemitones,
4716  setPitchSemitones,
4717  activeView
4718}) {
4719  var _a, _b, _c, _d, _e, _f, _g, _h;
4720  const { trackStates, setTrackStates, isLiveSynthFallback, setIsLiveSynthFallback, isSynthReady, setIsBuffering, soundfont, setSoundfont, hasStartedLoading, setHasStartedLoading, engineOrder, setEngineOrder } = usePlayer();
4721  const { t } = useTranslation();
4722  const [sfMetadata, setSfMetadata] = reactExports.useState({});
4723  const [unavailableModes, setUnavailableModes] = reactExports.useState({});
4724  reactExports.useEffect(() => {
4725    var _a2;
4726    if (!track) return;
4727    setUnavailableModes({});
4728    if (track.opusPath) {
4729      fetch(track.opusPath, { method: "HEAD" }).then((res) => {
4730        if (!res.ok) setUnavailableModes((prev) => ({ ...prev, singleopus: true }));
4731      }).catch(() => setUnavailableModes((prev) => ({ ...prev, singleopus: true })));
4732    } else {
4733      setUnavailableModes((prev) => ({ ...prev, singleopus: true }));
4734    }
4735    const firstInst = (_a2 = track.instruments) == null ? void 0 : _a2.find((i) => !!(i.stemPathHigh || i.stemPath));
4736    const firstStem = (firstInst == null ? void 0 : firstInst.stemPathHigh) || (firstInst == null ? void 0 : firstInst.stemPath);
4737    if (firstStem) {
4738      fetch(firstStem, { method: "HEAD" }).then((res) => {
4739        if (!res.ok) setUnavailableModes((prev) => ({ ...prev, opus: true }));
4740      }).catch(() => setUnavailableModes((prev) => ({ ...prev, opus: true })));
4741    } else {
4742      setUnavailableModes((prev) => ({ ...prev, opus: true }));
4743    }
4744    const eventsSources = [
4745      track.cleanMidiPath,
4746      track.midiPath
4747    ].filter(Boolean);
4748    if (eventsSources.length > 0) {
4749      Promise.all(eventsSources.map((src) => fetch(src, { method: "HEAD" }))).then((responses) => {
4750        if (!responses.some((res) => res.ok)) {
4751          setUnavailableModes((prev) => ({
4752            ...prev,
4753            synth: true,
4754            webmidi: true,
4755            chiptune: true,
4756            wasmsynth: true,
4757            fmsynth: true
4758          }));
4759        }
4760      }).catch(() => {
4761        setUnavailableModes((prev) => ({
4762          ...prev,
4763          synth: true,
4764          webmidi: true,
4765          chiptune: true,
4766          wasmsynth: true,
4767          fmsynth: true
4768        }));
4769      });
4770    } else {
4771      setUnavailableModes((prev) => ({
4772        ...prev,
4773        synth: true,
4774        webmidi: true,
4775        chiptune: true,
4776        wasmsynth: true,
4777        fmsynth: true
4778      }));
4779    }
4780  }, [track == null ? void 0 : track.id, track == null ? void 0 : track.opusPath, track == null ? void 0 : track.instruments, track == null ? void 0 : track.pianoRollPath, track == null ? void 0 : track.cleanMidiPath, track == null ? void 0 : track.midiPath]);
4781  reactExports.useEffect(() => {
4782    const loadInitData = async () => {
4783      try {
4784        const res = await fetch("/api/soundfonts");
4785        if (res.ok) {
4786          const data = await res.json();
4787          if (activeView !== "watch") {
4788            const activeSf = data.active || data.defaultLiveSf || "MusyngKite";
4789            setSoundfont(activeSf);
4790          }
4791          if (data.engineOrder) {
4792            setEngineOrder(data.engineOrder);
4793          }
4794          if (data.metadata) {
4795            setSfMetadata(data.metadata);
4796          }
4797          if (data.defaultWasmSf) {
4798            synth.defaultWasmSoundfont = data.defaultWasmSf;
4799          }
4800          if (data.defaultJsSf) {
4801            synth.defaultJsSoundfont = data.defaultJsSf;
4802          }
4803        }
4804      } catch (e) {
4805        console.warn("[PersistentPlayer] Failed to load soundfont init data", e);
4806      }
4807    };
4808    loadInitData();
4809  }, [activeView, setSoundfont]);
4810  const [singleOpusProgress, setSingleOpusProgress] = reactExports.useState(0);
4811  const downloadProgress = reactExports.useMemo(() => {
4812    var _a2, _b2;
4813    if (audioMode === "singleopus") return singleOpusProgress;
4814    if (isSynthReady) return 100;
4815    if (!hasStartedLoading) return 0;
4816    if (isLiveSynthFallback) {
4817      const keys = Object.keys(trackStates);
4818      if (keys.length === 0) return 0;
4819      const total = keys.reduce((acc, key) => {
4820        var _a3;
4821        return acc + (((_a3 = trackStates[parseInt(key)]) == null ? void 0 : _a3.percent) || 0);
4822      }, 0);
4823      return total / keys.length;
4824    }
4825    const tracksToLoad = ((_a2 = track.instruments) == null ? void 0 : _a2.filter((inst) => !!inst.stemPath)) || [];
4826    if (tracksToLoad.length === 0) {
4827      if (trackStates[0]) return trackStates[0].percent;
4828      return 0;
4829    }
4830    let totalPercent = 0;
4831    let count = 0;
4832    (_b2 = track.instruments) == null ? void 0 : _b2.forEach((inst, idx) => {
4833      var _a3;
4834      if (inst.stemPath) {
4835        totalPercent += ((_a3 = trackStates[idx]) == null ? void 0 : _a3.percent) || 0;
4836        count++;
4837      }
4838    });
4839    return count > 0 ? totalPercent / count : 0;
4840  }, [trackStates, track.instruments, isSynthReady, hasStartedLoading, isLiveSynthFallback, audioMode, singleOpusProgress]);
4841  const { country, autoplay, setAutoplay, setAudioQuality, soundfontVolume, setSoundfontVolume } = useSettings();
4842  const getThumbnailElement = (targetTrack, sizeClass, onClick) => {
4843    return /* @__PURE__ */ jsxRuntimeExports.jsx(
4844      VideoCard,
4845      {
4846        track: targetTrack,
4847        variant: "player-thumbnail",
4848        sizeClass,
4849        onSelect: onClick ? () => onClick() : () => {
4850        }
4851      }
4852    );
4853  };
4854  const audioRef = reactExports.useRef(null);
4855  const progressContainerRef = reactExports.useRef(null);
4856  const volumeRef = reactExports.useRef(null);
4857  const volumeTimeoutRef = reactExports.useRef(null);
4858  const [volume, setVolume] = reactExports.useState(1);
4859  const [progress, setProgress] = reactExports.useState(0);
4860  const [duration, setDuration] = reactExports.useState(
4861    ((_a = track.midiMetadata) == null ? void 0 : _a.duration) || parseDurationString(track.duration) || 0
4862  );
4863  const [currentTime, setCurrentTime] = reactExports.useState(0);
4864  const [isMuted, setIsMuted] = reactExports.useState(false);
4865  const [showQueue, setShowQueue] = reactExports.useState(false);
4866  const [showSettings, setShowSettings] = reactExports.useState(false);
4867  const [showEngineMenu, setShowEngineMenu] = reactExports.useState(false);
4868  const [showVolumeSlider, setShowVolumeSlider] = reactExports.useState(false);
4869  const [playbackError, setPlaybackError] = reactExports.useState(false);
4870  const [showTempoMenu, setShowTempoMenu] = reactExports.useState(false);
4871  const [showMoreMenu, setShowMoreMenu] = reactExports.useState(false);
4872  const [containerWidth, setContainerWidth] = reactExports.useState(0);
4873  const [midiOutputs, setMidiOutputs] = reactExports.useState([]);
4874  const [selectedMidiId, setSelectedMidiId] = reactExports.useState("");
4875  const [lowPerfToggle, setLowPerfToggle] = reactExports.useState(() => {
4876    if (typeof window !== "undefined") {
4877      return localStorage.getItem("low_performance") === "true";
4878    }
4879    return false;
4880  });
4881  const [isDragging, setIsDragging] = reactExports.useState(false);
4882  const [fullscreenElement, setFullscreenElement] = reactExports.useState(null);
4883  const [showFullscreenControls, setShowFullscreenControls] = reactExports.useState(true);
4884  const [isPinned, setIsPinned] = reactExports.useState(false);
4885  const hideControlsTimer = reactExports.useRef(null);
4886  const isHoveringControls = reactExports.useRef(false);
4887  const getRetroLcdConfig = (mode) => {
4888    switch (mode) {
4889      case "opus":
4890        return { label: "STEMS", color: "#ff5500", bg: "bg-zinc-950", border: "border-[#ff5500]/50", text: "text-[#ff5500]", shadow: "shadow-[#ff5500]/10" };
4891      case "singleopus":
4892        return { label: "OPUS", color: "#3b82f6", bg: "bg-zinc-950", border: "border-[#3b82f6]/50", text: "text-[#3b82f6]", shadow: "shadow-[#3b82f6]/10" };
4893      case "synth":
4894        return { label: "JS", color: "#10b981", bg: "bg-zinc-950", border: "border-[#10b981]/50", text: "text-[#10b981]", shadow: "shadow-[#10b981]/10" };
4895      case "wasmsynth":
4896        return { label: "SF", color: "#06b6d4", bg: "bg-zinc-950", border: "border-[#06b6d4]/50", text: "text-[#06b6d4]", shadow: "shadow-[#06b6d4]/10" };
4897      case "fmsynth":
4898        return { label: "FM", color: "#8b5cf6", bg: "bg-zinc-950", border: "border-[#8b5cf6]/50", text: "text-[#8b5cf6]", shadow: "shadow-[#8b5cf6]/10" };
4899      case "chiptune":
4900        return { label: "8BIT", color: "#f59e0b", bg: "bg-zinc-950", border: "border-[#f59e0b]/50", text: "text-[#f59e0b]", shadow: "shadow-[#f59e0b]/10" };
4901      case "webmidi":
4902        return { label: "MIDI", color: "#6366f1", bg: "bg-zinc-950", border: "border-[#6366f1]/50", text: "text-[#6366f1]", shadow: "shadow-[#6366f1]/10" };
4903      default:
4904        return { label: "SYNTH", color: "#10b981", bg: "bg-zinc-950", border: "border-[#10b981]/50", text: "text-[#10b981]", shadow: "shadow-[#10b981]/10" };
4905    }
4906  };
4907  const renderEngineOption = (mode, badge, color, title, desc, disabled, beforeSwitch) => {
4908    const isSelected = audioMode === mode;
4909    return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4910      "button",
4911      {
4912        disabled,
4913        type: "button",
4914        onClick: async () => {
4915          if (mode === "webmidi" && !synth.midiAccess) {
4916            await synth.initMidiOutput();
4917            const outputs = synth.getMidiOutputs();
4918            setMidiOutputs(outputs);
4919            setSelectedMidiId(synth.getActiveMidiOutputId() || "");
4920            if (synth.midiAccess) {
4921              synth.midiAccess.onstatechange = () => {
4922                setMidiOutputs(synth.getMidiOutputs());
4923                setSelectedMidiId(synth.getActiveMidiOutputId() || "");
4924              };
4925            }
4926            if (!synth.midiAccess) {
4927              setTrackStates((prev) => ({ ...prev }));
4928              return;
4929            }
4930          }
4931          setAudioMode(mode);
4932          if (typeof window !== "undefined") {
4933            localStorage.setItem("preferredAudioMode", mode);
4934          }
4935          setShowEngineMenu(false);
4936        },
4937        className: `w-full flex items-center justify-between p-2 rounded-lg text-left transition-colors duration-150 relative ${isSelected ? "bg-zinc-100 dark:bg-zinc-800/80 border border-zinc-200 dark:border-zinc-700/50" : "hover:bg-zinc-50 dark:hover:bg-zinc-800/40 border border-transparent"} ${disabled ? "opacity-30 cursor-not-allowed hover:bg-transparent" : "hover:bg-zinc-100/50 dark:hover:bg-zinc-800/50"}`,
4938        children: [
4939          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
4940            /* @__PURE__ */ jsxRuntimeExports.jsx(
4941              "div",
4942              {
4943                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 ${disabled ? "opacity-40 grayscale" : ""}`,
4944                style: {
4945                  borderColor: disabled ? "rgba(113, 113, 122, 0.4)" : `${color}50`,
4946                  color: disabled ? "#a1a1aa" : color,
4947                  textShadow: disabled ? "none" : `0 0 3px ${color}`
4948                },
4949                children: badge
4950              }
4951            ),
4952            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col min-w-0", children: [
4953              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs font-semibold truncate ${disabled ? "text-zinc-400 dark:text-zinc-500" : "text-zinc-800 dark:text-zinc-200"}`, children: title }),
4954              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-500 dark:text-zinc-400 truncate", children: desc || (disabled ? "Not available for this track" : "") })
4955            ] })
4956          ] }),
4957          isSelected && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 12, className: "shrink-0", style: { color } })
4958        ]
4959      }
4960    );
4961  };
4962  const renderRetroLcdIndicator = () => {
4963    var _a2, _b2;
4964    const config = getRetroLcdConfig(audioMode);
4965    const isMetadataOnly = track.status === "metadata_only";
4966    const isCompleted = track.status === "completed";
4967    const hasAudio = isCompleted && !isMetadataOnly && !!track.opusPath && ((_a2 = track.midiMetadata) == null ? void 0 : _a2.hasAudio) !== false;
4968    const hasStems = isCompleted && !isMetadataOnly && !!(track.instruments && track.instruments.some((i) => !!i.stemPath));
4969    const isJsHidden = ((_b2 = sfMetadata[`${soundfont}_JS`]) == null ? void 0 : _b2.visible) === false;
4970    const isSynthDisabled = unavailableModes["synth"] || isJsHidden;
4971    const engineOptions = [
4972      { id: "opus", label: "STEMS", color: "#ff5500", title: "Instrument Stems (Opus)", desc: "Multi-file high-fidelity audio", disabled: unavailableModes["opus"] || !hasStems },
4973      { id: "singleopus", label: "OPUS", color: "#3b82f6", title: "Single-file Opus", desc: "Pre-mixed single file (low resource)", disabled: unavailableModes["singleopus"] || !hasAudio },
4974      { id: "synth", label: "JS", color: "rgb(16, 185, 129)", title: "Web Audio JS SoundFont", desc: typeof navigator !== "undefined" && (/Mobi|Android|iPhone|iPad/i.test(navigator.userAgent) || "ontouchstart" in window) ? "Sampled Opus notes per instrument (heavy on mobile)" : "Sampled Opus notes per instrument (Web Audio)", disabled: isSynthDisabled },
4975      { id: "wasmsynth", label: "SF", color: "#06b6d4", title: "Direct SoundFont Engine", desc: "Direct SF2/SF3 SoundFont rendering via WASM (High quality, low bandwidth)", disabled: unavailableModes["wasmsynth"] },
4976      { id: "fmsynth", label: "FM", color: "#8b5cf6", title: "16-bit FM Synth", desc: "Retro arcade FM sound (Low CPU)", disabled: unavailableModes["fmsynth"] },
4977      { id: "chiptune", label: "8BIT", color: "#f59e0b", title: "Chiptune Synth (8-bit)", desc: "Retro 8-bit sound (Ultra Low CPU)", disabled: unavailableModes["chiptune"] },
4978      { id: "webmidi", label: "MIDI", color: "#6366f1", title: "Web MIDI Output", desc: getWebMidiStatusLabel() || "External hardware MIDI", disabled: unavailableModes["webmidi"] || !(typeof navigator !== "undefined" && !!navigator.requestMIDIAccess) || synth.midiAccessStatus === "denied" }
4979    ];
4980    let list = null;
4981    if (engineOrder) {
4982      if (activeView === "watch") {
4983        list = isSlowMobile() ? engineOrder.watchMobile : engineOrder.watchDesktop;
4984      } else {
4985        list = engineOrder.miniPlayer;
4986      }
4987    }
4988    let sortedOptions = engineOptions;
4989    if (list && Array.isArray(list) && list.length > 0) {
4990      const sorted = [];
4991      for (const key of list) {
4992        const opt = engineOptions.find((o) => o.id === key);
4993        if (opt) sorted.push(opt);
4994      }
4995      sortedOptions = sorted;
4996    }
4997    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center", children: [
4998      /* @__PURE__ */ jsxRuntimeExports.jsx(
4999        "button",
5000        {
5001          onClick: () => setShowEngineMenu(!showEngineMenu),
5002          type: "button",
5003          className: `flex items-center font-mono text-[9px] font-black tracking-widest px-1.5 py-0.5 rounded border select-none transition-all duration-200 cursor-pointer shadow-sm hover:scale-105 active:scale-95 ${config.bg} ${config.border} ${config.text} ${config.shadow}`,
5004          style: { textShadow: `0 0 4px ${config.color}` },
5005          children: config.label
5006        }
5007      ),
5008      showEngineMenu && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-8 right-0 w-64 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-2xl p-3 z-[110] backdrop-blur-md animate-in slide-in-from-bottom-3 text-zinc-900 dark:text-white", children: [
5009        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 pb-2 mb-1 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-[#111] flex items-center justify-between rounded-t-xl -mx-3 -mt-3 p-3", children: [
5010          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-zinc-500 dark:text-zinc-400 uppercase tracking-wider", children: "Playback Engine" }),
5011          /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowEngineMenu(false), className: "text-zinc-500 hover:text-zinc-900 dark:hover:text-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 12 }) })
5012        ] }),
5013        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 max-h-[300px] overflow-y-auto pr-1 mt-2", children: sortedOptions.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(React.Fragment, { children: renderEngineOption(opt.id, opt.label, opt.color, opt.title, opt.desc, opt.disabled) }, opt.id)) })
5014      ] })
5015    ] });
5016  };
5017  const { waveform } = useWaveform(String(track.id), track.waveformJsonPath, track.waveform);
5018  const titleContainerRef = reactExports.useRef(null);
5019  const titleContentRef = reactExports.useRef(null);
5020  const [shouldScroll, setShouldScroll] = reactExports.useState(false);
5021  const [marqueeScrollDist, setMarqueeScrollDist] = reactExports.useState(0);
5022  const [marqueeDuration, setMarqueeDuration] = reactExports.useState("0s");
5023  const activeAudioSrc = reactExports.useMemo(() => {
5024    if (audioMode === "singleopus" && track.opusPath) return track.opusPath;
5025    if (track.mp3PathHigh) return track.mp3PathHigh;
5026    return track.mp3Path || "";
5027  }, [track.mp3Path, track.mp3PathHigh, track.opusPath, audioMode]);
5028  const [castAvailable, setCastAvailable] = reactExports.useState(false);
5029  const [castSession, setCastSession] = reactExports.useState(null);
5030  const originalTempo = ((_b = track.midiMetadata) == null ? void 0 : _b.tempo) || 120;
5031  const [currentTempo, setCurrentTempo] = reactExports.useState(originalTempo);
5032  const isBlocked = isContentBlocked(country, (_c = track.midiMetadata) == null ? void 0 : _c.blockedRegions);
5033  const visualWaveform = reactExports.useMemo(() => {
5034    return waveform.length > 0 ? [...waveform] : generatePseudoWaveform(track.id, 1e3);
5035  }, [track.id, waveform, containerWidth]);
5036  reactExports.useEffect(() => {
5037    if (!progressContainerRef.current) return;
5038    const target = progressContainerRef.current;
5039    const ro = new ResizeObserver((entries) => {
5040      for (const entry of entries) {
5041        setContainerWidth(entry.contentRect.width);
5042      }
5043    });
5044    ro.observe(target);
5045    return () => ro.disconnect();
5046  }, [fullscreenElement]);
5047  reactExports.useEffect(() => {
5048    if (!titleContainerRef.current || !titleContentRef.current) return;
5049    const container2 = titleContainerRef.current;
5050    const content = titleContentRef.current;
5051    const updateMarquee = () => {
5052      const isOverflowing = content.scrollWidth > container2.clientWidth;
5053      setShouldScroll(isOverflowing);
5054      if (isOverflowing) {
5055        const gap = 48;
5056        const dist = content.scrollWidth + gap;
5057        setMarqueeScrollDist(-dist);
5058        const minSpeed = 30;
5059        const scrollTime = dist / minSpeed;
5060        const totalTime = Math.max(15, scrollTime + 3);
5061        setMarqueeDuration(`${totalTime}s`);
5062      }
5063    };
5064    const timer = setTimeout(updateMarquee, 100);
5065    const ro = new ResizeObserver(updateMarquee);
5066    ro.observe(container2);
5067    return () => {
5068      clearTimeout(timer);
5069      ro.disconnect();
5070    };
5071  }, [track.id, track.title, track.artist, containerWidth]);
5072  reactExports.useEffect(() => {
5073    const handleKeyDown = (e) => {
5074      var _a2;
5075      if (e.code === "Space") {
5076        const activeTag = (_a2 = document.activeElement) == null ? void 0 : _a2.tagName.toLowerCase();
5077        if (activeTag === "input" || activeTag === "textarea") return;
5078        e.preventDefault();
5079        setPlaybackError(false);
5080        if (!isPlaying && audioRef.current && duration > 0 && currentTime >= duration - 0.1) {
5081          audioRef.current.currentTime = 0;
5082          setCurrentTime(0);
5083          setProgress(0);
5084          onTimeUpdate(0, duration);
5085          if (["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode)) synth.seek(0);
5086        }
5087        const actx = synth.getAudioContext();
5088        if (actx.state === "suspended") {
5089          actx.resume().catch(() => {
5090          });
5091        }
5092        setIsPlaying(!isPlaying);
5093      }
5094    };
5095    window.addEventListener("keydown", handleKeyDown);
5096    return () => window.removeEventListener("keydown", handleKeyDown);
5097  }, [isPlaying, setIsPlaying, currentTime, duration, audioMode, onTimeUpdate]);
5098  const loadMediaToCast = reactExports.useCallback((session) => {
5099    if (!session || !activeAudioSrc) return;
5100    const cast = window.cast;
5101    const mediaInfo = new cast.framework.messages.MediaInfo(activeAudioSrc, "audio/opus");
5102    mediaInfo.metadata = new cast.framework.messages.MusicTrackMediaMetadata();
5103    mediaInfo.metadata.title = track.title;
5104    mediaInfo.metadata.artist = track.artist && !["unknown", "unknown artist"].includes(track.artist.toLowerCase().trim()) ? track.artist : void 0;
5105    mediaInfo.metadata.images = [{ url: track.thumbnail }];
5106    const request = new cast.framework.messages.LoadRequest(mediaInfo);
5107    session.loadMedia(request).then(
5108      () => {
5109        console.log("Cast media loaded");
5110      },
5111      // eslint-disable-next-line @typescript-eslint/no-explicit-any
5112      (errorCode) => {
5113        console.error("Cast load error", errorCode);
5114      }
5115    );
5116  }, [activeAudioSrc, track.title, track.artist, track.thumbnail]);
5117  reactExports.useEffect(() => {
5118    window["__onGCastApiAvailable"] = (isAvailable) => {
5119      if (isAvailable) {
5120        setCastAvailable(true);
5121        const cast = window.cast;
5122        cast.framework.CastContext.getInstance().setOptions({
5123          receiverApplicationId: cast.framework.CastContext.DEFAULT_MEDIA_RECEIVER_APP_ID,
5124          autoJoinPolicy: cast.framework.AutoJoinPolicy.ORIGIN_SCOPED
5125        });
5126        const context = cast.framework.CastContext.getInstance();
5127        context.addEventListener(
5128          cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
5129          // eslint-disable-next-line @typescript-eslint/no-explicit-any
5130          (event) => {
5131            if (event.sessionState === cast.framework.SessionState.SESSION_STARTED) {
5132              setCastSession(event.session);
5133              loadMediaToCast(event.session);
5134            } else if (event.sessionState === cast.framework.SessionState.SESSION_ENDED) {
5135              setCastSession(null);
5136            }
5137          }
5138        );
5139      }
5140    };
5141  }, [loadMediaToCast]);
5142  reactExports.useEffect(() => {
5143    if (synth.midiAccess) {
5144      setMidiOutputs(synth.getMidiOutputs());
5145      setSelectedMidiId(synth.getActiveMidiOutputId() || "");
5146      synth.midiAccess.onstatechange = () => {
5147        setMidiOutputs(synth.getMidiOutputs());
5148        setSelectedMidiId(synth.getActiveMidiOutputId() || "");
5149      };
5150    }
5151  }, []);
5152  reactExports.useEffect(() => {
5153    const handleFullscreenChange = () => {
5154      const el = document.fullscreenElement;
5155      setFullscreenElement(el);
5156      if (el) setShowFullscreenControls(true);
5157    };
5158    document.addEventListener("fullscreenchange", handleFullscreenChange);
5159    return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
5160  }, []);
5161  reactExports.useEffect(() => {
5162    if (!fullscreenElement) return;
5163    const handleMouseMove = () => {
5164      if (isPinned) {
5165        setShowFullscreenControls(true);
5166        if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
5167        return;
5168      }
5169      setShowFullscreenControls(true);
5170      if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
5171      hideControlsTimer.current = setTimeout(() => {
5172        if (!isHoveringControls.current) {
5173          setShowFullscreenControls(false);
5174        }
5175      }, 2500);
5176    };
5177    fullscreenElement.addEventListener("mousemove", handleMouseMove);
5178    handleMouseMove();
5179    return () => {
5180      fullscreenElement.removeEventListener("mousemove", handleMouseMove);
5181      if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
5182    };
5183  }, [fullscreenElement, isPinned]);
5184  reactExports.useEffect(() => {
5185    if (castSession) {
5186      loadMediaToCast(castSession);
5187    }
5188  }, [castSession, loadMediaToCast]);
5189  const handleCastClick = () => {
5190    if (castAvailable) {
5191      window.cast.framework.CastContext.getInstance().requestSession();
5192    } else {
5193      console.warn("Cast API not available");
5194    }
5195  };
5196  const getWebMidiStatusLabel = () => {
5197    if (typeof navigator === "undefined") return "";
5198    const hasSupport = !!navigator.requestMIDIAccess;
5199    if (!hasSupport) {
5200      if (window.location.protocol !== "https:" && window.location.hostname !== "localhost" && window.location.hostname !== "127.0.0.1") {
5201        return "Web MIDI requires HTTPS (secure context).";
5202      }
5203      const ua = navigator.userAgent.toLowerCase();
5204      if (ua.includes("firefox")) {
5205        return "Ensure dom.webmidi.enabled is true in Firefox about:config.";
5206      }
5207      if (ua.includes("ipad") || ua.includes("iphone") || ua.includes("macintosh") && navigator.maxTouchPoints > 0) {
5208        return "Not supported on iOS/iPadOS browsers.";
5209      }
5210      if (ua.includes("safari") && !ua.includes("chrome") && !ua.includes("chromium")) {
5211        return "Not supported by Safari. Try Chrome/Edge.";
5212      }
5213      return "Not supported by browser. Try Chrome/Edge.";
5214    }
5215    if (synth.midiAccessStatus === "denied") {
5216      return "MIDI permission denied. Check browser settings.";
5217    }
5218    if (!synth.midiAccess) {
5219      return "Click to enable Web MIDI";
5220    }
5221    if (midiOutputs.length === 0) {
5222      return "No MIDI outputs found. Connect a MIDI device.";
5223    }
5224    return null;
5225  };
5226  reactExports.useEffect(() => {
5227    if (audioRef.current && activeAudioSrc) {
5228      if (isPlaying && !isBlocked && (audioMode === "opus" || audioMode === "singleopus")) {
5229        const playPromise = audioRef.current.play();
5230        if (playPromise !== void 0) {
5231          playPromise.catch((e) => {
5232            console.warn("Playback prevented:", e.message);
5233          });
5234        }
5235      } else {
5236        audioRef.current.pause();
5237      }
5238    }
5239    if (castSession) {
5240      const playerController = new window.cast.framework.RemotePlayerController(new window.cast.framework.RemotePlayer());
5241      if (isPlaying && !isBuffering) playerController.playOrPause();
5242    }
5243  }, [isPlaying, track.id, isBlocked, activeAudioSrc, isBuffering, castSession, audioMode]);
5244  reactExports.useEffect(() => {
5245    if (audioRef.current && (audioMode === "opus" || audioMode === "singleopus") && Math.abs(audioRef.current.currentTime - currentTime) > 0.5) {
5246      audioRef.current.currentTime = currentTime;
5247    }
5248  }, [audioMode]);
5249  reactExports.useEffect(() => {
5250    synth.audioMode = audioMode;
5251  }, [audioMode]);
5252  reactExports.useEffect(() => {
5253    if (!["synth", "webmidi", "chiptune", "wasmsynth", "fmsynth", "opus"].includes(audioMode)) {
5254      synth.stop();
5255    }
5256  }, [audioMode]);
5257  reactExports.useEffect(() => {
5258    if (audioRef.current) {
5259      if (["synth", "webmidi", "chiptune", "wasmsynth", "fmsynth", "opus"].includes(audioMode)) {
5260        audioRef.current.volume = 0;
5261        audioRef.current.muted = true;
5262        if (["synth", "wasmsynth", "fmsynth", "chiptune", "opus"].includes(audioMode)) {
5263          synth.setMasterVolume(isMuted ? 0 : volume);
5264        }
5265      } else {
5266        audioRef.current.volume = volume;
5267        audioRef.current.muted = isMuted;
5268      }
5269      const rate = currentTempo / originalTempo;
5270      audioRef.current.playbackRate = Math.max(0.25, Math.min(4, rate));
5271      if (["synth", "webmidi", "chiptune", "wasmsynth", "fmsynth", "opus"].includes(audioMode)) {
5272        synth.setPlaybackRate(Math.max(0.25, Math.min(4, rate)));
5273        if (["synth", "wasmsynth", "fmsynth", "chiptune", "opus"].includes(audioMode)) {
5274          synth.setPitchSemitones(pitchSemitones);
5275        }
5276      }
5277      onPlaybackRateChange == null ? void 0 : onPlaybackRateChange(rate);
5278    }
5279  }, [volume, isMuted, currentTempo, pitchSemitones, originalTempo, audioMode, onPlaybackRateChange, soundfontVolume]);
5280  reactExports.useEffect(() => {
5281    const handleSetPitch = (e) => {
5282      const customEvent = e;
5283      setPitchSemitones(customEvent.detail);
5284    };
5285    window.addEventListener("setPitchSemitones", handleSetPitch);
5286    return () => window.removeEventListener("setPitchSemitones", handleSetPitch);
5287  }, [setPitchSemitones]);
5288  reactExports.useEffect(() => {
5289    const handleSetPlaybackRate = (e) => {
5290      const customEvent = e;
5291      setCurrentTempo(originalTempo * customEvent.detail);
5292    };
5293    window.addEventListener("setPlaybackRate", handleSetPlaybackRate);
5294    return () => window.removeEventListener("setPlaybackRate", handleSetPlaybackRate);
5295  }, [originalTempo]);
5296  reactExports.useEffect(() => {
5297    const handleSetTrackDuration = (e) => {
5298      const customEvent = e;
5299      const newDuration = customEvent.detail;
5300      setDuration(newDuration);
5301    };
5302    window.addEventListener("setTrackDuration", handleSetTrackDuration);
5303    return () => window.removeEventListener("setTrackDuration", handleSetTrackDuration);
5304  }, []);
5305  reactExports.useEffect(() => {
5306    var _a2;
5307    const metaDur = Number((_a2 = track == null ? void 0 : track.midiMetadata) == null ? void 0 : _a2.duration);
5308    const strDur = parseDurationString(track == null ? void 0 : track.duration);
5309    const initialDur = !isNaN(metaDur) && metaDur > 0 ? metaDur : strDur > 0 ? strDur : 0;
5310    if (initialDur > 0) {
5311      setDuration(initialDur);
5312    }
5313  }, [track == null ? void 0 : track.id, track == null ? void 0 : track.duration, (_d = track == null ? void 0 : track.midiMetadata) == null ? void 0 : _d.duration]);
5314  const lastSeekTimestamp = reactExports.useRef(0);
5315  reactExports.useEffect(() => {
5316    if (seekTo && audioRef.current && seekTo.timestamp !== lastSeekTimestamp.current) {
5317      lastSeekTimestamp.current = seekTo.timestamp;
5318      const newTime = seekTo.time;
5319      audioRef.current.currentTime = newTime;
5320      setCurrentTime(newTime);
5321      const dur = audioRef.current.duration || duration || 1;
5322      setProgress(newTime / dur * 100);
5323      onTimeUpdate(newTime, dur);
5324      if (["synth", "wasmsynth", "fmsynth", "chiptune", "webmidi", "opus"].includes(audioMode)) {
5325        synth.seek(newTime);
5326      }
5327    }
5328  }, [seekTo, audioMode, isPlaying, isBuffering, onTimeUpdate, duration]);
5329  const lastTrackChangeRef = reactExports.useRef(0);
5330  const [prevTrackId, setPrevTrackId] = reactExports.useState(track.id);
5331  if (track.id !== prevTrackId) {
5332    setPrevTrackId(track.id);
5333    lastTrackChangeRef.current = Date.now();
5334    setProgress(0);
5335    setCurrentTime(0);
5336    setPlaybackError(false);
5337    setCurrentTempo(((_e = track.midiMetadata) == null ? void 0 : _e.tempo) || 120);
5338    setPitchSemitones(0);
5339    setHasStartedLoading(false);
5340    setTrackStates({});
5341    setDuration(((_f = track.midiMetadata) == null ? void 0 : _f.duration) || parseDurationString(track.duration) || 0);
5342  }
5343  reactExports.useEffect(() => {
5344    if (audioRef.current) {
5345      audioRef.current.currentTime = 0;
5346      audioRef.current.playbackRate = 1;
5347    }
5348  }, [track.id]);
5349  const loadedAudioModeRef = reactExports.useRef(null);
5350  reactExports.useEffect(() => {
5351    var _a2, _b2;
5352    const isLiveSynthMode = audioMode === "synth" || audioMode === "webmidi" || audioMode === "chiptune" || audioMode === "wasmsynth" || audioMode === "fmsynth";
5353    const isLiveOrOpus = isLiveSynthMode || audioMode === "opus";
5354    if (activeView !== "watch" && isPlaying && isLiveOrOpus && (!hasStartedLoading || loadedAudioModeRef.current !== audioMode)) {
5355      console.log(`[PersistentPlayer] Non-watch page requested Play. Starting Download Sequence for mode: ${audioMode}...`);
5356      setHasStartedLoading(true);
5357      loadedAudioModeRef.current = audioMode;
5358      if (isLiveSynthMode) {
5359        setIsLiveSynthFallback(true);
5360      }
5361      const eventsSources = [
5362        track.cleanMidiPath,
5363        track.midiPath
5364      ].filter(Boolean);
5365      if (eventsSources.length === 0) {
5366        console.error("[PersistentPlayer] Live synthesis error: No MIDI source path available.");
5367        setTrackStates({ 0: { ready: false, error: true, percent: 0 } });
5368        setIsPlaying(false);
5369        return;
5370      }
5371      if (audioMode === "opus") {
5372        const tracksToLoad = track.instruments && track.instruments.length > 0 ? track.instruments : [{
5373          name: "Master",
5374          program: 0,
5375          midiChannel: 0,
5376          notesCount: 0,
5377          stemPath: track.opusPath,
5378          trackId: 0
5379        }];
5380        synth.audioMode = audioMode;
5381        synth.loadTracks(
5382          tracksToLoad,
5383          ((_a2 = track.midiMetadata) == null ? void 0 : _a2.tickDiv) || 480,
5384          ((_b2 = track.midiMetadata) == null ? void 0 : _b2.tempo) || 120,
5385          audioQuality,
5386          soundfont || "MusyngKite",
5387          track.pianoRollPath || track.cleanMidiPath || track.midiPath,
5388          (idx, status) => {
5389            setTrackStates((prev) => ({ ...prev, [idx]: status }));
5390          },
5391          false,
5392          // forceLive
5393          "SoundBlasterOld.sf2",
5394          // defaultLiveSf
5395          false
5396          // isAdmin
5397        ).catch((err) => {
5398          console.error("[PersistentPlayer] Opus synthesis fallback error:", err);
5399          setTrackStates({ 0: { ready: false, error: true, percent: 0 } });
5400          setIsPlaying(false);
5401        });
5402      } else {
5403        parseMidiInstruments(eventsSources).then(({ tracks: liveFallbackTracks, duration: fallbackDuration, tempo: fallbackTempo, tickDiv: fallbackTickDiv }) => {
5404          var _a3, _b3, _c2;
5405          if (fallbackDuration > 0 && (!((_a3 = track.midiMetadata) == null ? void 0 : _a3.duration) || track.midiMetadata.duration === 0)) {
5406            window.dispatchEvent(new CustomEvent("setTrackDuration", { detail: fallbackDuration }));
5407          }
5408          window.dispatchEvent(new CustomEvent("liveFallbackTracksLoaded", { detail: liveFallbackTracks }));
5409          const tickDivToUse = ((_b3 = track.midiMetadata) == null ? void 0 : _b3.tickDiv) || fallbackTickDiv;
5410          const tempoToUse = ((_c2 = track.midiMetadata) == null ? void 0 : _c2.tempo) || fallbackTempo;
5411          let sfToLoad = soundfont || "MusyngKite";
5412          if (lowPerfToggle) {
5413            const heavySfs = ["MusyngKite", "GeneralUser-GS", "FatBoy", "FluidR3_GM", "MuseScore_General", "Aspirin"];
5414            if (heavySfs.includes(sfToLoad.replace(/\.h?sf[23]$/i, "").replace(/_JS$/i, ""))) {
5415              sfToLoad = "SoundBlasterOld.sf2";
5416            }
5417          }
5418          synth.audioMode = audioMode;
5419          synth.loadTracks(
5420            liveFallbackTracks,
5421            tickDivToUse,
5422            tempoToUse,
5423            audioQuality,
5424            sfToLoad,
5425            eventsSources,
5426            (idx, status) => {
5427              setTrackStates((prev) => ({ ...prev, [idx]: status }));
5428            },
5429            true,
5430            // forceLive
5431            "SoundBlasterOld.sf2",
5432            // defaultLiveSf
5433            false
5434            // isAdmin
5435          );
5436        }).catch((err) => {
5437          console.error("[PersistentPlayer] Live synthesis fallback error:", err);
5438          setTrackStates({ 0: { ready: false, error: true, percent: 0 } });
5439          setIsPlaying(false);
5440        });
5441      }
5442    }
5443  }, [activeView, isPlaying, hasStartedLoading, audioMode, track, audioQuality, soundfont, setHasStartedLoading, setTrackStates, setIsPlaying, setIsLiveSynthFallback]);
5444  reactExports.useEffect(() => {
5445    if (activeView !== "watch" && (audioMode === "synth" || audioMode === "webmidi" || audioMode === "chiptune" || audioMode === "wasmsynth" || audioMode === "fmsynth" || audioMode === "opus")) {
5446      if (isPlaying && isSynthReady) {
5447        synth.play(currentTimeRef.current);
5448      } else if (!isPlaying) {
5449        synth.stop();
5450      }
5451    }
5452  }, [isPlaying, audioMode, isSynthReady, activeView]);
5453  reactExports.useEffect(() => {
5454    if (activeView !== "watch" && (audioMode === "synth" || audioMode === "webmidi" || audioMode === "chiptune" || audioMode === "wasmsynth" || audioMode === "fmsynth" || audioMode === "opus")) {
5455      if (isPlaying && !isSynthReady) {
5456        setIsBuffering(true);
5457      } else {
5458        setIsBuffering(false);
5459      }
5460    } else if (activeView !== "watch" && audioMode !== "singleopus") {
5461      setIsBuffering(false);
5462    }
5463  }, [isPlaying, isSynthReady, audioMode, activeView, setIsBuffering]);
5464  const rafRef = reactExports.useRef(null);
5465  const onTimeUpdateRef = reactExports.useRef(onTimeUpdate);
5466  reactExports.useEffect(() => {
5467    onTimeUpdateRef.current = onTimeUpdate;
5468  }, [onTimeUpdate]);
5469  reactExports.useRef(0);
5470  reactExports.useRef(null);
5471  const isPlayingRef = reactExports.useRef(isPlaying);
5472  const isDraggingRef = reactExports.useRef(isDragging);
5473  const currentTimeRef = reactExports.useRef(currentTime);
5474  const durationRef = reactExports.useRef(duration);
5475  const audioModeRef = reactExports.useRef(audioMode);
5476  const previousModeRef = reactExports.useRef(audioMode);
5477  reactExports.useEffect(() => {
5478    audioModeRef.current = audioMode;
5479    if (previousModeRef.current !== audioMode) {
5480      setPlaybackError(false);
5481      setTrackStates({});
5482      previousModeRef.current = audioMode;
5483    }
5484  }, [audioMode, setTrackStates]);
5485  reactExports.useEffect(() => {
5486    isPlayingRef.current = isPlaying;
5487  }, [isPlaying]);
5488  reactExports.useEffect(() => {
5489    isDraggingRef.current = isDragging;
5490  }, [isDragging]);
5491  reactExports.useEffect(() => {
5492    currentTimeRef.current = currentTime;
5493  }, [currentTime]);
5494  reactExports.useEffect(() => {
5495    durationRef.current = duration;
5496  }, [duration]);
5497  const updateTime = reactExports.useCallback(() => {
5498    if (audioRef.current && !isDraggingRef.current) {
5499      let curr = audioRef.current.currentTime;
5500      const currentMode = audioModeRef.current;
5501      const isSynthMode = ["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(currentMode);
5502      const dur = isSynthMode ? durationRef.current || audioRef.current.duration || 1 : audioRef.current.duration || durationRef.current || 1;
5503      if (isSynthMode) {
5504        const synthTime = synth.getCurrentTime();
5505        if (synthTime === 0 && currentTimeRef.current > 0) {
5506          curr = currentTimeRef.current;
5507        } else {
5508          curr = synthTime;
5509        }
5510        if (dur > 1) {
5511          curr = Math.min(curr, dur);
5512          if (curr >= dur && isPlayingRef.current && Date.now() - lastTrackChangeRef.current > 1e3) {
5513            onNext(false);
5514          }
5515        }
5516      }
5517      setCurrentTime(curr);
5518      setProgress(curr / dur * 100);
5519      if (!isSynthMode) {
5520        setDuration(dur);
5521      }
5522      onTimeUpdateRef.current(curr, dur);
5523    }
5524    if (isPlayingRef.current) {
5525      rafRef.current = requestAnimationFrame(updateTime);
5526    }
5527  }, [setIsPlaying, onNext]);
5528  reactExports.useEffect(() => {
5529    if (isPlaying) {
5530      rafRef.current = requestAnimationFrame(updateTime);
5531    } else if (rafRef.current) {
5532      cancelAnimationFrame(rafRef.current);
5533    }
5534    return () => {
5535      if (rafRef.current) cancelAnimationFrame(rafRef.current);
5536    };
5537  }, [isPlaying, updateTime]);
5538  const calculateProgressFromEvent = reactExports.useCallback((clientX) => {
5539    if (!progressContainerRef.current) return 0;
5540    const rect = progressContainerRef.current.getBoundingClientRect();
5541    const x = clientX - rect.left;
5542    const width = rect.width;
5543    return Math.max(0, Math.min(100, x / width * 100));
5544  }, []);
5545  const updateScrubPosition = reactExports.useCallback((newProgress) => {
5546    if (!audioRef.current) return;
5547    const trackDuration = audioRef.current.duration || duration || 0;
5548    const newTime = newProgress / 100 * trackDuration;
5549    if (isFinite(newTime) && trackDuration > 0) {
5550      audioRef.current.currentTime = newTime;
5551      setCurrentTime(newTime);
5552      setProgress(newProgress);
5553      onTimeUpdate(newTime, trackDuration);
5554      if (["synth", "wasmsynth", "fmsynth", "chiptune", "webmidi", "opus"].includes(audioMode)) {
5555        synth.seek(newTime);
5556      }
5557    }
5558  }, [onTimeUpdate, audioMode, duration]);
5559  const handleMouseDown = (e) => {
5560    setIsDragging(true);
5561    const newProgress = calculateProgressFromEvent(e.clientX);
5562    updateScrubPosition(newProgress);
5563  };
5564  reactExports.useEffect(() => {
5565    const handleMouseMove = (e) => {
5566      if (isDragging) {
5567        e.preventDefault();
5568        const newProgress = calculateProgressFromEvent(e.clientX);
5569        updateScrubPosition(newProgress);
5570      }
5571    };
5572    const handleMouseUp = () => {
5573      if (isDragging) {
5574        setIsDragging(false);
5575      }
5576    };
5577    if (isDragging) {
5578      window.addEventListener("mousemove", handleMouseMove);
5579      window.addEventListener("mouseup", handleMouseUp);
5580    }
5581    return () => {
5582      window.removeEventListener("mousemove", handleMouseMove);
5583      window.removeEventListener("mouseup", handleMouseUp);
5584    };
5585  }, [isDragging, calculateProgressFromEvent, updateScrubPosition]);
5586  const handleVolumeChange = (e) => {
5587    if (!volumeRef.current) return;
5588    const rect = volumeRef.current.getBoundingClientRect();
5589    const height = rect.height;
5590    const y = e.clientY - rect.top;
5591    let newVolume = (height - y) / height;
5592    newVolume = Math.max(0, Math.min(1, newVolume));
5593    setVolume(newVolume);
5594    setIsMuted(newVolume === 0);
5595  };
5596  const formatTime = (seconds) => {
5597    if (!isFinite(seconds)) return "0:00";
5598    const m = Math.floor(seconds / 60);
5599    const s = Math.floor(seconds % 60);
5600    return `${m}:${s.toString().padStart(2, "0")}`;
5601  };
5602  const playbackRate = currentTempo / originalTempo;
5603  const canvasRefBg = reactExports.useRef(null);
5604  const canvasRefFg = reactExports.useRef(null);
5605  const drawWaveform = reactExports.useCallback((canvas, isForeground) => {
5606    if (!canvas || containerWidth === 0) return;
5607    const ctx = canvas.getContext("2d");
5608    if (!ctx) return;
5609    const dpr = window.devicePixelRatio || 1;
5610    const rect = canvas.getBoundingClientRect();
5611    canvas.width = rect.width * dpr;
5612    canvas.height = rect.height * dpr;
5613    ctx.scale(dpr, dpr);
5614    ctx.clearRect(0, 0, rect.width, rect.height);
5615    const data = visualWaveform;
5616    const width = rect.width;
5617    const height = rect.height;
5618    const centerY = height / 2;
5619    const step = width / data.length;
5620    ctx.beginPath();
5621    for (let i = 0; i < data.length; i++) {
5622      const val = data[i];
5623      const x = i * step;
5624      const yAmplitude = val * height / 2;
5625      ctx.moveTo(x, centerY - yAmplitude);
5626      ctx.lineTo(x, centerY + yAmplitude);
5627    }
5628    ctx.lineWidth = 1;
5629    let fgColor = "#ffffff";
5630    let bgColor = "#ffffff40";
5631    if (audioMode === "synth") {
5632      fgColor = "#10b981";
5633      bgColor = "#10b98140";
5634    } else if (audioMode === "wasmsynth") {
5635      fgColor = "#06b6d4";
5636      bgColor = "#06b6d440";
5637    } else if (audioMode === "fmsynth") {
5638      fgColor = "#8b5cf6";
5639      bgColor = "#8b5cf640";
5640    } else if (audioMode === "webmidi") {
5641      fgColor = "#6366f1";
5642      bgColor = "#6366f140";
5643    } else if (audioMode === "opus") {
5644      fgColor = "#ff5500";
5645      bgColor = "#ff550040";
5646    } else if (audioMode === "chiptune") {
5647      fgColor = "#f59e0b";
5648      bgColor = "#f59e0b40";
5649    } else {
5650      fgColor = "#3b82f6";
5651      bgColor = "#3b82f640";
5652    }
5653    if (isForeground) {
5654      ctx.strokeStyle = fgColor;
5655    } else {
5656      ctx.strokeStyle = bgColor;
5657    }
5658    ctx.stroke();
5659  }, [visualWaveform, containerWidth, fullscreenElement, audioMode]);
5660  reactExports.useEffect(() => {
5661    const timer = setTimeout(() => {
5662      drawWaveform(canvasRefBg.current, false);
5663      drawWaveform(canvasRefFg.current, true);
5664    }, 50);
5665    return () => clearTimeout(timer);
5666  }, [drawWaveform, fullscreenElement]);
5667  if (isBlocked) return null;
5668  const PlayerUI = /* @__PURE__ */ jsxRuntimeExports.jsx(
5669    "div",
5670    {
5671      className: `fixed bottom-0 left-0 right-0 border-t z-[100] shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] flex flex-col transition-all duration-300 ${fullscreenElement ? "bg-[var(--visualizer-bg,#050505)] border-white/10 text-white w-full" : "bg-white dark:bg-[#111] border-zinc-200 dark:border-zinc-800"}
5671 ${fullscreenElement && !showFullscreenControls ? "translate-y-full opacity-0" : "translate-y-0 opacity-100"}`,
5672      onMouseEnter: () => {
5673        isHoveringControls.current = true;
5674        if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
5675        setShowFullscreenControls(true);
5676      },
5677      onMouseLeave: () => {
5678        isHoveringControls.current = false;
5679      },
5680      children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex items-center justify-between px-2 sm:px-4 md:px-8 max-w-[1800px] mx-auto w-full gap-2 sm:gap-4 h-20", children: [
5681        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 sm:gap-2 md:gap-4 shrink-0", children: [
5682          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-1 sm:gap-2 md:gap-4 ${isGameMode ? "opacity-50 pointer-events-none" : ""}`, children: [
5683            /* @__PURE__ */ jsxRuntimeExports.jsx(
5684              "button",
5685              {
5686                className: `hidden lg:block transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-400 hover:text-black dark:hover:text-white"}`,
5687                title: t("player.startOver", "Start from Beginning"),
5688                onClick: () => {
5689                  if (audioRef.current) {
5690                    audioRef.current.currentTime = 0;
5691                    setCurrentTime(0);
5692                    setProgress(0);
5693                    onTimeUpdate(0, duration);
5694                    if (["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode)) synth.seek(0);
5695                  }
5696                },
5697                children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 16 })
5698              }
5699            ),
5700            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: `transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-400 hover:text-black dark:hover:text-white"}`, title: t("player.prev", "Previous"), onClick: onPrev, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SkipBack, { size: 18, fill: "currentColor" }) }),
5701            /* @__PURE__ */ jsxRuntimeExports.jsx(
5702              "button",
5703              {
5704                className: `hidden sm:block transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-400 hover:text-black dark:hover:text-white"}`,
5705                title: t("player.rewind", "Fast backward"),
5706                onClick: () => {
5707                  if (audioRef.current) {
5708                    const newTime = Math.max(0, audioRef.current.currentTime - 10);
5709                    audioRef.current.currentTime = newTime;
5710                    setCurrentTime(newTime);
5711                    setProgress(newTime / duration * 100);
5712                    onTimeUpdate(newTime, duration);
5713                    if (audioMode === "synth" || audioMode === "webmidi") synth.seek(newTime);
5714                  }
5715                },
5716                children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
5717                  /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "11 19 2 12 11 5 11 19" }),
5718                  /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "22 19 13 12 22 5 22 19" })
5719                ] })
5720              }
5721            )
5722          ] }),
5723          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
5724            "button",
5725            {
5726              onClick: () => {
5727                setPlaybackError(false);
5728                if (!isPlaying && audioRef.current && duration > 0 && currentTime >= duration - 0.1) {
5729                  audioRef.current.currentTime = 0;
5730                  setCurrentTime(0);
5731                  setProgress(0);
5732                  onTimeUpdate(0, duration);
5733                  if (["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode)) synth.seek(0);
5734                }
5735                const actx = synth.getAudioContext();
5736                if (actx.state === "suspended") {
5737                  actx.resume().catch(() => {
5738                  });
5739                }
5740                setIsPlaying(!isPlaying);
5741              },
5742              className: `hover:scale-105 transition-transform relative ${fullscreenElement ? "text-white" : "text-black dark:text-white"}`,
5743              title: isPlaying ? t("player.pause", "Pause") : t("player.play", "Play"),
5744              children: [
5745                isBuffering ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "animate-spin text-indigo-500", size: 30 }) : isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 30, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 30, fill: "currentColor" }),
5746                playbackError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border border-white dark:border-[#111]", title: t("player.audioError", "Audio Error") })
5747              ]
5748            }
5749          ) }),
5750          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-1 sm:gap-2 md:gap-4 ${isGameMode ? "opacity-50 pointer-events-none" : ""}`, children: [
5751            /* @__PURE__ */ jsxRuntimeExports.jsx(
5752              "button",
5753              {
5754                className: `hidden sm:block transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-400 hover:text-black dark:hover:text-white"}`,
5755                title: t("player.fastForward", "Fast forward"),
5756                onClick: () => {
5757                  if (audioRef.current) {
5758                    const newTime = Math.min(duration, audioRef.current.currentTime + 10);
5759                    audioRef.current.currentTime = newTime;
5760                    setCurrentTime(newTime);
5761                    setProgress(newTime / duration * 100);
5762                    onTimeUpdate(newTime, duration);
5763                    if (audioMode === "synth" || audioMode === "webmidi") synth.seek(newTime);
5764                  }
5765                },
5766                children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
5767                  /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "13 19 22 12 13 5 13 19" }),
5768                  /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "2 19 11 12 2 5 2 19" })
5769                ] })
5770              }
5771            ),
5772            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: `transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-400 hover:text-black dark:hover:text-white"}`, title: t("player.next", "Next"), onClick: () => onNext(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(SkipForward, { size: 18, fill: "currentColor" }) }),
5773            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "text-zinc-400 hover:text-[#f50] transition-colors hidden lg:block", title: t("player.shuffle", "Shuffle"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Shuffle, { size: 16 }) })
5774          ] })
5775        ] }),
5776        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex items-center min-w-0 gap-2", children: [
5777          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden sm:flex items-center gap-2 sm:gap-3 shrink-0 max-w-[120px] sm:max-w-[200px] lg:max-w-[300px] min-w-0", children: [
5778            track.status === "completed" ? getThumbnailElement(track, `h-10 w-[60px] aspect-[1.5/1] bg-zinc-200 dark:bg-zinc-800 rounded shadow-sm shrink-0 ${onTrackClick ? "cursor-pointer" : "cursor-default"}`, onTrackClick) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-10 w-[60px] shrink-0" }),
5779            /* @__PURE__ */ jsxRuntimeExports.jsx(
5780              "div",
5781              {
5782                ref: titleContainerRef,
5783                className: "flex-1 flex flex-col justify-center min-w-0 hidden sm:flex h-full relative overflow-hidden",
5784                children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
5785                  "div",
5786                  {
5787                    className: `flex min-w-max gap-12 ${shouldScroll ? "animate-marquee-infinite" : ""}`,
5788                    style: {
5789                      "--scroll-dist": `${marqueeScrollDist}px`,
5790                      "--animation-duration": marqueeDuration
5791                    },
5792                    children: [
5793                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: titleContentRef, className: "flex flex-col min-w-0", children: [
5794                        track.artist && !["unknown", "unknown artist"].includes(track.artist.toLowerCase().trim()) && /* @__PURE__ */ jsxRuntimeExports.jsx(
5795                          "a",
5796                          {
5797                            href: `/?q=${encodeURIComponent(track.artist)}`,
5798                            onClick: (e) => {
5799                              if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
5800                                e.preventDefault();
5801                                e.stopPropagation();
5802                                window.location.href = `/?q=${encodeURIComponent(track.artist)}`;
5803                              } else {
5804                                e.stopPropagation();
5805                              }
5806                            },
5807                            className: `text-xs hover:underline cursor-pointer hover:text-indigo-400 transition-colors ${fullscreenElement ? "text-zinc-400" : "text-zinc-500"}`,
5808                            title: `Explore all tracks by ${track.artist}`,
5809                            children: track.artist
5810                          }
5811                        ),
5812                        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick: onTrackClick, className: `text-sm font-medium flex items-center gap-2 whitespace-nowrap ${onTrackClick ? "cursor-pointer hover:underline" : "cursor-default"} ${fullscreenElement ? "text-white" : "text-zinc-900 dark:text-white"}`, children: [
5813                          track.title,
5814                          playbackError && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: t("player.audioError", "Audio Unavailable"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 12, className: "text-red-500" }) })
5815                        ] })
5816                      ] }),
5817                      shouldScroll && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col min-w-0", children: [
5818                        track.artist && !["unknown", "unknown artist"].includes(track.artist.toLowerCase().trim()) && /* @__PURE__ */ jsxRuntimeExports.jsx(
5819                          "a",
5820                          {
5821                            href: `/?q=${encodeURIComponent(track.artist)}`,
5822                            onClick: (e) => {
5823                              if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button !== 1) {
5824                                e.preventDefault();
5825                                e.stopPropagation();
5826                                window.location.href = `/?q=${encodeURIComponent(track.artist)}`;
5827                              } else {
5828                                e.stopPropagation();
5829                              }
5830                            },
5831                            className: `text-xs hover:underline cursor-pointer hover:text-indigo-400 transition-colors ${fullscreenElement ? "text-zinc-400" : "text-zinc-500"}`,
5832                            title: `Explore all tracks by ${track.artist}`,
5833                            children: track.artist
5834                          }
5835                        ),
5836                        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick: onTrackClick, className: `text-sm font-medium flex items-center gap-2 whitespace-nowrap ${onTrackClick ? "cursor-pointer hover:underline" : "cursor-default"} ${fullscreenElement ? "text-white" : "text-zinc-900 dark:text-white"}`, children: [
5837                          track.title,
5838                          playbackError && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: t("player.audioError", "Audio Unavailable"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 12, className: "text-red-500" }) })
5839                        ] })
5840                      ] })
5841                    ]
5842                  }
5843                )
5844              }
5845            ),
5846            /* @__PURE__ */ jsxRuntimeExports.jsx(
5847              "button",
5848              {
5849                onClick: () => toggleLike(track.id),
5850                className: `hidden sm:flex p-1.5 rounded-full border transition-all ${isLiked ? "border-[#f50] text-[#f50]" : "border-zinc-300 dark:border-zinc-700 text-zinc-400 hover:border-zinc-500"}`,
5851                children: /* @__PURE__ */ jsxRuntimeExports.jsx(Heart, { size: 14, fill: isLiked ? "currentColor" : "none" })
5852              }
5853            )
5854          ] }),
5855          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 h-10 flex items-center gap-1 sm:gap-2 px-1 sm:px-4 min-w-0", children: [
5856            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-xs font-mono shrink-0 min-w-[30px] sm:min-w-[35px] text-right ${fullscreenElement ? "text-zinc-400" : "text-zinc-500 dark:text-zinc-400"}`, children: formatTime(currentTime) }),
5857            /* @__PURE__ */ jsxRuntimeExports.jsxs(
5858              "div",
5859              {
5860                ref: progressContainerRef,
5861                className: `flex-1 h-8 relative group select-none bg-black rounded overflow-hidden ${isGameMode ? "pointer-events-none" : "cursor-pointer"}`,
5862                onMouseDown: handleMouseDown,
5863                children: [
5864                  /* @__PURE__ */ jsxRuntimeExports.jsx(
5865                    "canvas",
5866                    {
5867                      ref: canvasRefBg,
5868                      className: "absolute inset-0 w-full h-full pointer-events-none"
5869                    }
5870                  ),
5871                  /* @__PURE__ */ jsxRuntimeExports.jsx(
5872                    "div",
5873                    {
5874                      className: "absolute inset-0 overflow-hidden border-r-2",
5875                      style: {
5876                        width: `${progress}%`,
5877                        borderColor: audioMode === "synth" ? "#10b981" : audioMode === "wasmsynth" ? "#06b6d4" : audioMode === "fmsynth" ? "#8b5cf6" : audioMode === "webmidi" ? "#6366f1" : audioMode === "opus" ? "#ff5500" : audioMode === "chiptune" ? "#f59e0b" : "#3b82f6"
5878                      },
5879                      children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5880                        "canvas",
5881                        {
5882                          ref: canvasRefFg,
5883                          className: "absolute inset-0 h-full pointer-events-none",
5884                          style: { width: containerWidth ? `${containerWidth}px` : "100%" }
5885                        }
5886                      )
5887                    }
5888                  ),
5889                  /* @__PURE__ */ jsxRuntimeExports.jsx(
5890                    "div",
5891                    {
5892                      className: `absolute top-0 bottom-0 w-2 bg-transparent -ml-1`,
5893                      style: { left: `${progress}%` },
5894                      children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-4 h-4 bg-white shadow-md border border-zinc-300 rounded-full cursor-grab active:cursor-grabbing" })
5895                    }
5896                  ),
5897                  isBuffering && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/85 z-10 flex items-center justify-center backdrop-blur-[1px] transition-opacity duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-11/12 max-w-[280px] flex flex-col gap-1.5 px-3", children: [
5898                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[11px] font-semibold tracking-wide text-zinc-300 uppercase", children: [
5899                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
5900                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500 animate-ping" }),
5901                        "Downloading audio..."
5902                      ] }),
5903                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-xs", children: [
5904                        Math.round(downloadProgress),
5905                        "%"
5906                      ] })
5907                    ] }),
5908                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-1 bg-zinc-800 rounded-full overflow-hidden border border-zinc-700/30", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5909                      "div",
5910                      {
5911                        className: "h-full bg-gradient-to-r from-indigo-500 to-purple-500 rounded-full transition-all duration-300 shadow-[0_0_8px_rgba(99,102,241,0.5)] animate-pulse",
5912                        style: { width: `${downloadProgress}%` }
5913                      }
5914                    ) })
5915                  ] }) })
5916                ]
5917              }
5918            ),
5919            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 shrink-0 select-none", children: [
5920              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-xs font-mono min-w-[30px] sm:min-w-[35px] ${fullscreenElement ? "text-zinc-400" : "text-zinc-500 dark:text-zinc-400"}`, children: formatTime(duration) }),
5921              renderRetroLcdIndicator()
5922            ] })
5923          ] })
5924        ] }),
5925        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `hidden md:flex items-center gap-3 shrink-0 justify-end border-l border-zinc-200 dark:border-zinc-800 pl-4 ${isGameMode ? "opacity-50 pointer-events-none" : ""}`, children: [
5926          /* @__PURE__ */ jsxRuntimeExports.jsx(
5927            Metronome,
5928            {
5929              bpm: currentTempo,
5930              isPlaying,
5931              isBuffering,
5932              currentTime,
5933              timeSignature: typeof ((_g = track.midiMetadata) == null ? void 0 : _g.timeSignature) === "string" ? { numerator: parseInt(track.midiMetadata.timeSignature.split("/")[0] || "4"), denominator: parseInt(track.midiMetadata.timeSignature.split("/")[1] || "4") } : (_h = track.midiMetadata) == null ? void 0 : _h.timeSignature
5934            }
5935          ),
5936          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
5937            /* @__PURE__ */ jsxRuntimeExports.jsxs(
5938              "button",
5939              {
5940                onClick: () => setShowTempoMenu(!showTempoMenu),
5941                className: `text-[10px] sm:text-xs font-bold font-mono px-2 py-1 rounded transition-colors flex flex-col items-start pl-3 justify-center min-w-[70px] ${fullscreenElement ? "hover:bg-zinc-800" : "hover:bg-zinc-100 dark:hover:bg-zinc-800"} ${playbackRate !== 1 || pitchSemitones !== 0 ? "text-[#f50]" : fullscreenElement ? "text-zinc-400" : "text-zinc-500"}`,
5942                title: t("player.tempoControl", "Playback Settings"),
5943                children: [
5944                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
5945                    /* @__PURE__ */ jsxRuntimeExports.jsx(Timer, { size: 14 }),
5946                    " ",
5947                    playbackRate.toFixed(2),
5948                    "x"
5949                  ] }),
5950                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-1 leading-none mt-1`, children: [
5951                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[12px] font-bold font-sans", children: "♭♯" }),
5952                    " ",
5953                    pitchSemitones > 0 ? "+" : pitchSemitones < 0 ? "" : "±",
5954                    pitchSemitones
5955                  ] })
5956                ]
5957              }
5958            ),
5959            showTempoMenu && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-full mb-1 right-0 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-xl p-4 z-50 flex flex-col min-w-[200px] animate-in slide-in-from-bottom-2", children: [
5960              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-2", children: [
5961                /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xs font-bold text-zinc-500 uppercase", children: t("player.playbackSpeed", "Playback Speed") }),
5962                /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setCurrentTempo(originalTempo), className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full text-zinc-400 hover:text-zinc-900 dark:hover:text-white", title: "Reset Tempo", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 }) })
5963              ] }),
5964              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-3", children: [
5965                /* @__PURE__ */ jsxRuntimeExports.jsx(
5966                  "input",
5967                  {
5968                    type: "number",
5969                    min: "10",
5970                    max: "999",
5971                    value: Math.round(currentTempo),
5972                    onChange: (e) => setCurrentTempo(Math.max(10, Math.min(999, parseInt(e.target.value) || originalTempo))),
5973                    className: "w-16 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded p-1 text-center font-mono text-sm text-zinc-900 dark:white outline-none focus:border-indigo-500"
5974                  }
5975                ),
5976                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400", children: "BPM" })
5977              ] }),
5978              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1 mb-4", children: [
5979                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[10px] text-zinc-400", children: [
5980                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "0.25x" }),
5981                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "1.0x" }),
5982                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "1.75x" })
5983                ] }),
5984                /* @__PURE__ */ jsxRuntimeExports.jsx(
5985                  "input",
5986                  {
5987                    type: "range",
5988                    min: "0.25",
5989                    max: "1.75",
5990                    step: "0.05",
5991                    value: playbackRate,
5992                    onChange: (e) => setCurrentTempo(originalTempo * parseFloat(e.target.value)),
5993                    className: "w-full accent-[#f50] h-1 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer"
5994                  }
5995                )
5996              ] }),
5997              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
5998                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-200 dark:border-zinc-800 my-2" }),
5999                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-2", children: [
6000                  /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-bold text-zinc-500 uppercase flex items-center gap-1", children: [
6001                    /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { size: 12 }),
6002                    " ",
6003                    t("player.keyChange", "Key Change")
6004                  ] }),
6005                  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setPitchSemitones(0), className: "p-1 hover:bg-zinc-100 dark:bg-zinc-800 rounded-full text-zinc-400 hover:text-zinc-900 dark:hover:text-white", title: t("player.resetPitch", "Reset Pitch"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 }) })
6006                ] }),
6007                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-1 mb-1", children: [
6008                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
6009                    /* @__PURE__ */ jsxRuntimeExports.jsx(
6010                      "button",
6011                      {
6012                        onClick: () => setPitchSemitones((p) => Math.max(-24, p - 1)),
6013                        className: "w-8 h-8 flex justify-center items-center rounded bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-zinc-600 dark:text-zinc-300 transition-colors",
6014                        children: "-"
6015                      }
6016                    ),
6017                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-400", children: "♭" })
6018                  ] }),
6019                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6020                    "input",
6021                    {
6022                      type: "number",
6023                      min: "-24",
6024                      max: "24",
6025                      value: pitchSemitones,
6026                      onChange: (e) => setPitchSemitones(Math.max(-24, Math.min(24, parseInt(e.target.value) || 0))),
6027                      className: "w-16 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded p-1 text-center font-mono text-sm text-zinc-900 dark:text-white outline-none focus:border-indigo-500"
6028                    }
6029                  ) }),
6030                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
6031                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-400", children: "♯" }),
6032                    /* @__PURE__ */ jsxRuntimeExports.jsx(
6033                      "button",
6034                      {
6035                        onClick: () => setPitchSemitones((p) => Math.min(24, p + 1)),
6036                        className: "w-8 h-8 flex justify-center items-center rounded bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-zinc-600 dark:text-zinc-300 transition-colors",
6037                        children: "+"
6038                      }
6039                    )
6040                  ] })
6041                ] })
6042              ] })
6043            ] })
6044          ] }),
6045          castAvailable && /* @__PURE__ */ jsxRuntimeExports.jsx(
6046            "button",
6047            {
6048              onClick: handleCastClick,
6049              className: `hidden lg:block transition-colors ${castSession ? "text-blue-500" : fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6050              title: t("player.cast", "Cast to Device"),
6051              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Cast, { size: 18 })
6052            }
6053          ),
6054          fullscreenElement && /* @__PURE__ */ jsxRuntimeExports.jsx(
6055            "button",
6056            {
6057              onClick: () => setIsPinned(!isPinned),
6058              className: `hidden lg:block transition-colors ${isPinned ? "text-[#f50] dark:text-[#f50]" : fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6059              title: isPinned ? t("player.unpin", "Unpin Player") : t("player.pin", "Pin Player"),
6060              children: isPinned ? /* @__PURE__ */ jsxRuntimeExports.jsx(PinOff, { size: 18 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pin, { size: 18 })
6061            }
6062          )
6063        ] }),
6064        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "hidden sm:flex items-center gap-3 shrink-0 justify-end pl-2 sm:pl-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6065          "div",
6066          {
6067            className: "relative flex items-center h-full",
6068            onMouseEnter: () => {
6069              if (volumeTimeoutRef.current) clearTimeout(volumeTimeoutRef.current);
6070              setShowVolumeSlider(true);
6071            },
6072            onMouseLeave: () => {
6073              volumeTimeoutRef.current = setTimeout(() => {
6074                setShowVolumeSlider(false);
6075              }, 300);
6076            },
6077            children: [
6078              /* @__PURE__ */ jsxRuntimeExports.jsx(
6079                "button",
6080                {
6081                  className: `transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6082                  onClick: () => {
6083                    setIsMuted(!isMuted);
6084                  },
6085                  children: isMuted || volume === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(VolumeX, { size: 18 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 18 })
6086                }
6087              ),
6088              showVolumeSlider && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 w-14 h-40 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-lg shadow-xl flex items-center justify-center z-50 animate-in fade-in slide-in-from-bottom-2 duration-200 mb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6089                "div",
6090                {
6091                  ref: volumeRef,
6092                  className: "w-8 h-32 bg-zinc-200 dark:bg-zinc-850 rounded-full relative border border-zinc-300 dark:border-zinc-700 shadow-inner flex justify-center cursor-pointer",
6093                  onClick: handleVolumeChange,
6094                  onMouseDown: (e) => {
6095                    if (e.buttons === 1) handleVolumeChange(e);
6096                    const moveHandler = (ev) => handleVolumeChange(ev);
6097                    const upHandler = () => {
6098                      window.removeEventListener("mousemove", moveHandler);
6099                      window.removeEventListener("mouseup", upHandler);
6100                    };
6101                    window.addEventListener("mousemove", moveHandler);
6102                    window.addEventListener("mouseup", upHandler);
6103                  },
6104                  children: [
6105                    /* @__PURE__ */ jsxRuntimeExports.jsx(
6106                      "div",
6107                      {
6108                        className: "absolute bottom-0 left-0 w-full bg-[#f50] rounded-full pointer-events-none",
6109                        style: { height: `${isMuted ? 0 : volume * 100}%` }
6110                      }
6111                    ),
6112                    /* @__PURE__ */ jsxRuntimeExports.jsx(
6113                      "div",
6114                      {
6115                        className: "absolute left-0 w-full h-4 bg-white dark:bg-zinc-300 rounded shadow border border-zinc-400 dark:border-zinc-600 pointer-events-none z-10",
6116                        style: { bottom: `calc(${isMuted ? 0 : volume * 100}% - 8px)` },
6117                        children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-px bg-zinc-300 dark:bg-zinc-500 mt-[7px]" })
6118                      }
6119                    )
6120                  ]
6121                }
6122              ) })
6123            ]
6124          }
6125        ) }),
6126        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `hidden lg:flex items-center gap-3 shrink-0 justify-end pl-4 ${isGameMode ? "opacity-50 pointer-events-none" : ""}`, children: [
6127          onAddToPlaylist && /* @__PURE__ */ jsxRuntimeExports.jsx(
6128            "button",
6129            {
6130              onClick: onAddToPlaylist,
6131              className: `transition-colors ${fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6132              title: t("playlists.saveTo", "Save to Playlist"),
6133              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 18 })
6134            }
6135          ),
6136          /* @__PURE__ */ jsxRuntimeExports.jsx(
6137            "button",
6138            {
6139              onClick: () => setShowQueue(!showQueue),
6140              className: `transition-colors ${showQueue ? "text-[#f50] dark:text-[#f50]" : fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6141              title: t("player.nextUp", "Next Up"),
6142              children: /* @__PURE__ */ jsxRuntimeExports.jsx(ListMusic, { size: 18 })
6143            }
6144          ),
6145          /* @__PURE__ */ jsxRuntimeExports.jsx(
6146            "button",
6147            {
6148              onClick: () => setShowSettings(!showSettings),
6149              className: `transition-colors ${showSettings ? "text-[#f50] dark:text-[#f50]" : fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white"}`,
6150              title: t("player.settings", "Player Settings"),
6151              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 18 })
6152            }
6153          )
6154        ] }),
6155        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "lg:hidden flex items-center shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
6156          /* @__PURE__ */ jsxRuntimeExports.jsx(
6157            "button",
6158            {
6159              onClick: () => setShowMoreMenu(!showMoreMenu),
6160              className: `p-1.5 rounded-lg transition-colors ${showMoreMenu ? "text-[#f50] bg-zinc-100 dark:bg-zinc-800" : fullscreenElement ? "text-zinc-400 hover:text-white" : "text-zinc-500 hover:text-black dark:hover:text-white hover:bg-zinc-100 dark:hover:bg-zinc-800"}`,
6161              title: t("player.more", "More"),
6162              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Ellipsis, { size: 20 })
6163            }
6164          ),
6165          showMoreMenu && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-full mb-1 right-0 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-2xl z-50 animate-in slide-in-from-bottom-2 min-w-[220px] overflow-hidden", children: [
6166            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-[#111] flex justify-between items-center px-3", children: [
6167              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-zinc-500 uppercase", children: t("player.more", "More") }),
6168              /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowMoreMenu(false), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 14, className: "text-zinc-500 hover:text-zinc-900 dark:hover:text-white" }) })
6169            ] }),
6170            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-1", children: [
6171              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6172                "button",
6173                {
6174                  onClick: () => {
6175                    toggleLike(track.id);
6176                    setShowMoreMenu(false);
6177                  },
6178                  className: "sm:hidden w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6179                  children: [
6180                    /* @__PURE__ */ jsxRuntimeExports.jsx(Heart, { size: 16, fill: isLiked ? "currentColor" : "none", className: isLiked ? "text-[#f50]" : "text-zinc-500" }),
6181                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: isLiked ? t("player.unlike", "Unlike") : t("player.like", "Like") })
6182                  ]
6183                }
6184              ),
6185              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6186                "button",
6187                {
6188                  onClick: () => {
6189                    setIsMuted(!isMuted);
6190                  },
6191                  className: "sm:hidden w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6192                  children: [
6193                    isMuted || volume === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(VolumeX, { size: 16, className: "text-zinc-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 16, className: "text-zinc-500" }),
6194                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: isMuted ? t("player.unmute", "Unmute") : t("player.mute", "Mute") })
6195                  ]
6196                }
6197              ),
6198              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "md:hidden", children: [
6199                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex items-center gap-3 px-3 py-2.5", children: [
6200                  /* @__PURE__ */ jsxRuntimeExports.jsx(Timer, { size: 16, className: playbackRate !== 1 ? "text-[#f50]" : "text-zinc-500" }),
6201                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300 flex-1", children: "Tempo" }),
6202                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-mono text-zinc-500", children: [
6203                    playbackRate.toFixed(2),
6204                    "x"
6205                  ] })
6206                ] }),
6207                /* @__P
6207URE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6208                  "input",
6209                  {
6210                    type: "range",
6211                    min: "0.25",
6212                    max: "1.75",
6213                    step: "0.05",
6214                    value: playbackRate,
6215                    onChange: (e) => setCurrentTempo(originalTempo * parseFloat(e.target.value)),
6216                    className: "w-full accent-[#f50] h-1 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer"
6217                  }
6218                ) })
6219              ] }),
6220              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "md:hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex items-center gap-3 px-3 py-2.5", children: [
6221                /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { size: 16, className: pitchSemitones !== 0 ? "text-[#f50]" : "text-zinc-500" }),
6222                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300 flex-1", children: t("player.keyChange", "Key Change") }),
6223                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
6224                  /* @__PURE__ */ jsxRuntimeExports.jsx(
6225                    "button",
6226                    {
6227                      onClick: () => setPitchSemitones(pitchSemitones - 1),
6228                      className: "w-6 h-6 flex items-center justify-center rounded bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-[#f50] hover:text-white transition-colors",
6229                      children: "-"
6230                    }
6231                  ),
6232                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-mono text-zinc-500 w-10 text-center", children: [
6233                    pitchSemitones > 0 ? "+" : "",
6234                    pitchSemitones
6235                  ] }),
6236                  /* @__PURE__ */ jsxRuntimeExports.jsx(
6237                    "button",
6238                    {
6239                      onClick: () => setPitchSemitones(pitchSemitones + 1),
6240                      className: "w-6 h-6 flex items-center justify-center rounded bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-[#f50] hover:text-white transition-colors",
6241                      children: "+"
6242                    }
6243                  )
6244                ] })
6245              ] }) }),
6246              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6247                "button",
6248                {
6249                  onClick: () => {
6250                    if (audioRef.current) {
6251                      audioRef.current.currentTime = 0;
6252                      setCurrentTime(0);
6253                      setProgress(0);
6254                      onTimeUpdate(0, duration);
6255                      if (["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode)) synth.seek(0);
6256                    }
6257                    setShowMoreMenu(false);
6258                  },
6259                  className: "lg:hidden w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6260                  children: [
6261                    /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 16, className: "text-zinc-500" }),
6262                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.startOver", "Start Over") })
6263                  ]
6264                }
6265              ),
6266              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6267                "button",
6268                {
6269                  className: "lg:hidden w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6270                  children: [
6271                    /* @__PURE__ */ jsxRuntimeExports.jsx(Shuffle, { size: 16, className: "text-zinc-500" }),
6272                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.shuffle", "Shuffle") })
6273                  ]
6274                }
6275              ),
6276              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-200 dark:border-zinc-800 my-1" }),
6277              onAddToPlaylist && /* @__PURE__ */ jsxRuntimeExports.jsxs(
6278                "button",
6279                {
6280                  onClick: () => {
6281                    onAddToPlaylist();
6282                    setShowMoreMenu(false);
6283                  },
6284                  className: "w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6285                  children: [
6286                    /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16, className: "text-zinc-500" }),
6287                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("playlists.saveTo", "Save to Playlist") })
6288                  ]
6289                }
6290              ),
6291              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6292                "button",
6293                {
6294                  onClick: () => {
6295                    setShowQueue(!showQueue);
6296                    setShowMoreMenu(false);
6297                  },
6298                  className: "w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6299                  children: [
6300                    /* @__PURE__ */ jsxRuntimeExports.jsx(ListMusic, { size: 16, className: showQueue ? "text-[#f50]" : "text-zinc-500" }),
6301                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.nextUp", "Next Up") })
6302                  ]
6303                }
6304              ),
6305              /* @__PURE__ */ jsxRuntimeExports.jsxs(
6306                "button",
6307                {
6308                  onClick: () => {
6309                    setShowSettings(!showSettings);
6310                    setShowMoreMenu(false);
6311                  },
6312                  className: "w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6313                  children: [
6314                    /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: showSettings ? "text-[#f50]" : "text-zinc-500" }),
6315                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.settings", "Player Settings") })
6316                  ]
6317                }
6318              ),
6319              castAvailable && /* @__PURE__ */ jsxRuntimeExports.jsxs(
6320                "button",
6321                {
6322                  onClick: () => {
6323                    handleCastClick();
6324                    setShowMoreMenu(false);
6325                  },
6326                  className: "w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors text-left",
6327                  children: [
6328                    /* @__PURE__ */ jsxRuntimeExports.jsx(Cast, { size: 16, className: castSession ? "text-blue-500" : "text-zinc-500" }),
6329                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.cast", "Cast") })
6330                  ]
6331                }
6332              )
6333            ] })
6334          ] })
6335        ] }) })
6336      ] })
6337    }
6338  );
6339  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6340    showSettings && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `fixed bottom-[81px] right-4 w-60 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-2xl z-[101] animate-in slide-in-from-bottom-5 ${fullscreenElement ? "right-auto left-1/2 -translate-x-1/2" : ""}`, children: [
6341      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-[#111] rounded-t-xl flex justify-between items-center", children: [
6342        /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-xs text-zinc-900 dark:text-white", children: t("player.settings", "Player Settings") }),
6343        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowSettings(false), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 14, className: "text-zinc-500 hover:text-zinc-900 dark:hover:text-white" }) })
6344      ] }),
6345      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3", children: [
6346        /* @__PURE__ */ jsxRuntimeExports.jsxs(
6347          "button",
6348          {
6349            onClick: () => setAutoplay(!autoplay),
6350            className: "w-full flex items-center justify-between p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-colors",
6351            children: [
6352              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: t("player.autoplay", "Autoplay on Load") }),
6353              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-4 h-4 rounded border flex items-center justify-center ${autoplay ? "bg-indigo-600 border-indigo-600" : "border-zinc-400 dark:border-zinc-600"}
6353`, children: autoplay && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 10, className: "text-white" }) })
6354            ]
6355          }
6356        ),
6357        /* @__PURE__ */ jsxRuntimeExports.jsxs(
6358          "button",
6359          {
6360            onClick: () => {
6361              const current = localStorage.getItem("low_performance") === "true";
6362              const newVal = !current;
6363              localStorage.setItem("low_performance", newVal ? "true" : "false");
6364              window.dispatchEvent(new Event("low_performance_change"));
6365              if (newVal) {
6366                const hasWebMidiSupport = typeof navigator !== "undefined" && !!navigator.requestMIDIAccess;
6367                if (hasWebMidiSupport) {
6368                  setAudioMode("webmidi");
6369                } else if (track.opusPath) {
6370                  setAudioMode("singleopus");
6371                } else if (activeAudioSrc) {
6372                  setAudioMode("opus");
6373                }
6374              }
6375              setLowPerfToggle(newVal);
6376            },
6377            className: "w-full flex items-center justify-between p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-colors text-left",
6378            children: [
6379              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
6380                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: "Low-End Device Mode" }),
6381                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: "Simplify UI & use fast audio engines" })
6382              ] }),
6383              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-4 h-4 rounded border flex items-center justify-center ${lowPerfToggle ? "bg-indigo-600 border-indigo-600" : "border-zinc-400 dark:border-zinc-600"}`, children: lowPerfToggle && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 10, className: "text-white" }) })
6384            ]
6385          }
6386        ),
6387        audioMode === "synth" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6388          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-200 dark:border-zinc-800 my-2" }),
6389          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 pb-1 flex justify-between items-center", children: [
6390            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-zinc-500 uppercase tracking-wider", children: t("player.soundfontVolume", "Soundfont Volume") }),
6391            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-mono text-zinc-500 font-bold", children: [
6392              soundfontVolume.toFixed(1),
6393              "x"
6394            ] })
6395          ] }),
6396          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 py-1", children: [
6397            /* @__PURE__ */ jsxRuntimeExports.jsx(
6398              "input",
6399              {
6400                type: "range",
6401                min: "1.0",
6402                max: "10.0",
6403                step: "0.5",
6404                value: soundfontVolume,
6405                onChange: (e) => setSoundfontVolume(parseFloat(e.target.value)),
6406                className: "w-full accent-indigo-600 h-1 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer"
6407              }
6408            ),
6409            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[9px] text-zinc-400 mt-1", children: [
6410              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "1.0x (Quiet)" }),
6411              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "10.0x (Loud)" })
6412            ] })
6413          ] })
6414        ] }),
6415        audioMode === "webmidi" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6416          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-zinc-200 dark:border-zinc-800 my-2" }),
6417          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 pb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold text-zinc-500 uppercase tracking-wider", children: "MIDI Device" }) }),
6418          midiOutputs.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6419            "select",
6420            {
6421              value: selectedMidiId,
6422              onChange: (e) => {
6423                const id = e.target.value;
6424                synth.setMidiOutputById(id);
6425                setSelectedMidiId(id);
6426              },
6427              className: "w-full bg-zinc-100 dark:bg-zinc-800 text-xs text-zinc-800 dark:text-zinc-200 border border-zinc-200 dark:border-zinc-700 rounded p-1.5 outline-none cursor-pointer",
6428              children: midiOutputs.map((out) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: out.id, children: out.name || `MIDI Port ${out.id}` }, out.id))
6429            }
6430          ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1 text-[10px] text-red-500 leading-snug", children: "No MIDI outputs found. Connect a MIDI device or switch to Soundfont Synth." })
6431        ] })
6432      ] })
6433    ] }),
6434    showQueue && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `fixed bottom-[81px] w-80 bg-white dark:bg-[#1a1a1a] border border-zinc-200 dark:border-zinc-800 rounded-t-xl shadow-2xl z-[101] animate-in slide-in-from-bottom-10 max-h-[60vh] flex flex-col ${fullscreenElement ? "right-4" : "right-16"}`, children: [
6435      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 flex justify-between items-center bg-zinc-50 dark:bg-[#111] rounded-t-xl", children: [
6436        /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white", children: t("player.nextUp", "Next Up") }),
6437        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowQueue(false), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16, className: "text-zinc-500 hover:text-zinc-900 dark:hover:text-white" }) })
6438      ] }),
6439      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-2 custom-scrollbar", children: [
6440        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg flex items-center gap-3 mb-2 border border-indigo-100 dark:border-indigo-800", children: [
6441          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded bg-zinc-200 dark:bg-zinc-800 shrink-0 overflow-hidden", children: track.status === "completed" ? getThumbnailElement(track, "w-full h-full") : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full" }) }),
6442          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
6443            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-indigo-600 dark:text-indigo-400 font-bold mb-0.5", children: t("player.nowPlaying", "Now Playing") }),
6444            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-zinc-900 dark:text-white truncate", children: track.title })
6445          ] }),
6446          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-3 h-3 bg-indigo-500 rounded-full animate-pulse" }) })
6447        ] }),
6448        activePlaylist && activePlaylist.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1 mt-3 mb-4", children: [
6449          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-2 mb-1", children: [
6450            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 uppercase truncate", children: [
6451              "🎶 ",
6452              playlistLabel || t("player.activePlaylist", "Active Playlist"),
6453              " (",
6454              activePlaylist.length,
6455              ")"
6456            ] }),
6457            /* @__PURE__ */ jsxRuntimeExports.jsxs(
6458              "button",
6459              {
6460                onClick: onClearPlaylist,
6461                className: "text-[10px] text-zinc-500 hover:text-red-500 transition-colors flex items-center gap-1 bg-zinc-100 dark:bg-zinc-800 px-2 py-0.5 rounded",
6462                children: [
6463                  /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10 }),
6464                  " Clear"
6465                ]
6466              }
6467            )
6468          ] }),
6469          activePlaylist.map((pTrack, i) => {
6470            const isCurrent = pTrack._playlistItemId && track._playlistItemId ? pTrack._playlistItemId === track._playlistItemId : track.id === pTrack.id;
6471            return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `group flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors ${isCurrent ? "bg-indigo-50 dark:bg-indigo-900/20 border border-indigo-100 dark:border-indigo-800" : "hover:bg-zinc-100 dark:hover:bg-zinc-800"}`, onClick: () => onPlayQueueTrack && onPlayQueueTrack(pTrack), children: [
6472              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs font-mono w-4 ${isCurrent ? "text-indigo-500 font-bold" : "text-zinc-400"}`, children: i + 1 }),
6473              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded bg-zinc-200 dark:bg-zinc-800 shrink-0 overflow-hidden", children: pTrack.status === "completed" ? getThumbnailElement(pTrack, "w-full h-full") : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full" }) }),
6474              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
6475                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-xs font-bold truncate ${isCurrent ? "text-indigo-700 dark:text-indigo-300" : "text-zinc-900 dark:text-white"}`, children: pTrack.title }),
6476                pTrack.artist && !["unknown", "unknown artist"].includes(pTrack.artist.toLowerCase().trim()) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 truncate", children: pTrack.artist })
6477              ] }),
6478              isCurrent && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 bg-indigo-500 rounded-full animate-pulse" }) })
6479            ] }, `pl-${i}`);
6480          })
6481        ] }),
6482        queue.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1 mt-3 border-t border-zinc-100 dark:border-zinc-800/50 pt-3", children: [
6483          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-zinc-500 uppercase px-2 mb-1", children: t("player.nextUp", "Next Up") }),
6484          queue.map((qTrack, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "group flex items-center gap-3 p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg cursor-pointer", onClick: () => onPlayQueueTrack && onPlayQueueTrack(qTrack), children: [
6485            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-mono w-4", children: i + 1 }),
6486            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded bg-zinc-200 dark:bg-zinc-800 shrink-0 overflow-hidden", children: qTrack.status === "completed" ? getThumbnailElement(qTrack, "w-full h-full") : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full" }) }),
6487            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
6488              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-zinc-900 dark:text-white truncate", children: qTrack.title }),
6489              qTrack.artist && !["unknown", "unknown artist"].includes(qTrack.artist.toLowerCase().trim()) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 truncate", children: qTrack.artist })
6490            ] }),
6491            /* @__PURE__ */ jsxRuntimeExports.jsx(
6492              "button",
6493              {
6494                onClick: (e) => {
6495                  e.stopPropagation();
6496                  onRemoveFromQueue == null ? void 0 : onRemoveFromQueue(i);
6497                },
6498                className: "opacity-0 group-hover:opacity-100 p-1 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded text-zinc-500 hover:text-red-500 transition-all",
6499                children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 })
6500              }
6501            )
6502          ] }, `q-${i}`))
6503        ] }) : (!activePlaylist || activePlaylist.length === 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-8 text-zinc-500 text-xs", children: [
6504          /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 24, className: "mx-auto mb-2 opacity-50" }),
6505          t("player.queueEmpty", "Queue is empty")
6506        ] })
6507      ] })
6508    ] }),
6509    fullscreenElement ? ReactDOM.createPortal(PlayerUI, fullscreenElement) : PlayerUI,
6510    /* @__PURE__ */ jsxRuntimeExports.jsx(
6511      "audio",
6512      {
6513        id: "global-audio-element",
6514        ref: audioRef,
6515        ...activeAudioSrc ? { src: activeAudioSrc } : {},
6516        preload: activeAudioSrc ? "metadata" : "none",
6517        crossOrigin: "anonymous",
6518        onLoadedMetadata: (e) => {
6519          if (duration === 0 || !["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode)) {
6520            setDuration(e.currentTarget.duration);
6521          }
6522        },
6523        onWaiting: () => {
6524          if (audioMode === "singleopus") {
6525            setIsBuffering(true);
6526          }
6527        },
6528        onPlaying: () => {
6529          if (audioMode === "singleopus") {
6530            setIsBuffering(false);
6531          }
6532        },
6533        onSeeking: () => {
6534          if (audioMode === "singleopus") {
6535            setIsBuffering(true);
6536          }
6537        },
6538        onSeeked: () => {
6539          if (audioMode === "singleopus") {
6540            setIsBuffering(false);
6541          }
6542        },
6543        onProgress: (e) => {
6544          if (audioMode === "singleopus") {
6545            const audio = e.currentTarget;
6546            if (audio.duration > 0 && audio.buffered.length > 0) {
6547              const end = audio.buffered.end(audio.buffered.length - 1);
6548              setSingleOpusProgress(end / audio.duration * 100);
6549            }
6550          }
6551        },
6552        onEnded: () => {
6553          const isSynthMode = ["synth", "webmidi", "opus", "wasmsynth", "chiptune", "fmsynth"].includes(audioMode);
6554          if (!isSynthMode) {
6555            onNext(false);
6556          }
6557        },
6558        onError: (e) => {
6559          const errorMessage = e.currentTarget.error ? e.currentTarget.error.message : "Unknown Error";
6560          const errorCode = e.currentTarget.error ? e.currentTarget.error.code : 0;
6561          console.warn(`Audio Playback Error [Code ${errorCode}]:`, errorMessage);
6562          if (audioMode === "opus" || audioMode === "singleopus") {
6563            setPlaybackError(true);
6564            setIsPlaying(false);
6565          } else {
6566            console.log("[PersistentPlayer] Suppressing audio error as we are in Synth mode");
6567          }
6568        }
6569      }
6570    )
6571  ] });
6572}
6573const AddToPlaylistModal = ({ isOpen, onClose, track, currentUser }) => {
6574  const { t } = useTranslation();
6575  const [playlists, setPlaylists] = reactExports.useState([]);
6576  const [newPlaylistName, setNewPlaylistName] = reactExports.useState("");
6577  const [isPublic, setIsPublic] = reactExports.useState(false);
6578  const [isCreating, setIsCreating] = reactExports.useState(false);
6579  const [addedIds, setAddedIds] = reactExports.useState(/* @__PURE__ */ new Set());
6580  reactExports.useEffect(() => {
6581    if (isOpen && currentUser) {
6582      api.getPlaylists(currentUser.id).then((pl) => {
6583        setPlaylists(pl);
6584        const existing = new Set(pl.filter((p) => p.trackIds.includes(track.id)).map((p) => p.id));
6585        setAddedIds(existing);
6586      });
6587    }
6588  }, [isOpen, currentUser, track.id]);
6589  const handleCreatePlaylist = async () => {
6590    if (!newPlaylistName.trim()) return;
6591    setIsCreating(true);
6592    const newPlaylist = await api.createPlaylist(currentUser.id, newPlaylistName, isPublic);
6593    setPlaylists((prev) => [...prev, newPlaylist]);
6594    setNewPlaylistName("");
6595    setIsPublic(false);
6596    setIsCreating(false);
6597    handleAddTrack(newPlaylist.id);
6598  };
6599  const handleAddTrack = async (playlistId) => {
6600    await api.addTrackToPlaylist(playlistId, track.id);
6601    setAddedIds((prev) => new Set(prev).add(playlistId));
6602  };
6603  if (!isOpen) return null;
6604  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[1000] flex items-center justify-center p-4", children: [
6605    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: onClose }),
6606    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-sm bg-white dark:bg-zinc-900 rounded-xl shadow-2xl overflow-hidden animate-in zoom-in-95 flex flex-col max-h-[80vh]", children: [
6607      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 flex justify-between items-center", children: [
6608        /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
6609          /* @__PURE__ */ jsxRuntimeExports.jsx(ListMusic, { size: 18, className: "text-indigo-500" }),
6610          t("playlists.addTo", "Add to Playlist")
6611        ] }),
6612        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20, className: "text-zinc-500 hover:text-zinc-900 dark:hover:text-white" }) })
6613      ] }),
6614      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-2", children: [
6615        playlists.map((pl) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
6616          "button",
6617          {
6618            onClick: () => !addedIds.has(pl.id) && handleAddTrack(pl.id),
6619            className: "w-full flex items-center justify-between p-3 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors text-left",
6620            children: [
6621              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-zinc-900 dark:text-white truncate", children: pl.title }),
6622              addedIds.has(pl.id) ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 18, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-zinc-400 rounded-sm" })
6623            ]
6624          },
6625          pl.id
6626        )),
6627        playlists.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center text-zinc-500 py-4 text-sm", children: t("playlists.noPlaylists", "No playlists found.") })
6628      ] }),
6629      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 border-t border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
6630        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
6631          /* @__PURE__ */ jsxRuntimeExports.jsx(
6632            "input",
6633            {
6634              value: newPlaylistName,
6635              onChange: (e) => setNewPlaylistName(e.target.value),
6636              placeholder: t("playlists.newPlaylistPlaceholder", "New Playlist Name"),
6637              className: "flex-1 bg-white dark:bg-black border border-zinc-300 dark:border-zinc-700 rounded-lg px-3 py-2 text-sm outline-none focus:border-indigo-500 text-zinc-900 dark:text-white",
6638              onKeyDown: (e) => e.key === "Enter" && handleCreatePlaylist()
6639            }
6640          ),
6641          /* @__PURE__ */ jsxRuntimeExports.jsx(
6642            "button",
6643            {
6644              onClick: handleCreatePlaylist,
6645              disabled: !newPlaylistName || isCreating,
6646              className: "p-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg disabled:opacity-50",
6647              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 20 })
6648            }
6649          )
6650        ] }),
6651        /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-sm text-zinc-600 dark:text-zinc-400 cursor-pointer", children: [
6652          /* @__PURE__ */ jsxRuntimeExports.jsx(
6653            "input",
6654            {
6655              type: "checkbox",
6656              checked: isPublic,
6657              onChange: (e) => setIsPublic(e.target.checked),
6658              className: "rounded border-zinc-300 text-indigo-600 focus:ring-indigo-500"
6659            }
6660          ),
6661          t("playlists.makePublic", "Make this playlist public")
6662        ] })
6663      ] }) })
6664    ] })
6665  ] });
6666};
6667const StaffAdminBar = () => {
6668  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
6669  const { staffModeEnabled, toggleStaffMode, isSuperAdmin } = useAdmin();
6670  const { t } = useTranslation();
6671  const [stats2, setStats] = reactExports.useState({ pendingReports: 0, activeUserCount: 0, system: { cpu: 0, memory: 0 } });
6672  const [dbQueryTime, setDbQueryTime] = reactExports.useState(0);
6673  const [dbQueryCount, setDbQueryCount] = reactExports.useState(0);
6674  const [clientApiTime, setClientApiTime] = reactExports.useState(0);
6675  const [initialLoadTime, setInitialLoadTime] = reactExports.useState(null);
6676  const [showTelemetryDetails, setShowTelemetryDetails] = reactExports.useState(false);
6677  reactExports.useEffect(() => {
6678    if (!staffModeEnabled) return;
6679    const fetchStats = async () => {
6680      try {
6681        const data = await api.getActiveStaffStats();
6682        setStats(data);
6683      } catch (e) {
6684        console.error("Failed to fetch staff stats", e);
6685      }
6686    };
6687    fetchStats();
6688    const interval = setInterval(fetchStats, 3e4);
6689    return () => clearInterval(interval);
6690  }, [staffModeEnabled]);
6691  reactExports.useEffect(() => {
6692    if (!staffModeEnabled) return;
6693    const measureLoadTime = () => {
6694      const [navigationEntry] = performance.getEntriesByType("navigation");
6695      if (navigationEntry) {
6696        setInitialLoadTime(Math.round(navigationEntry.duration));
6697      } else {
6698        const t2 = performance.timing;
6699        if (t2) {
6700          const loadTime = t2.loadEventEnd - t2.navigationStart;
6701          if (loadTime > 0) {
6702            setInitialLoadTime(loadTime);
6703          }
6704        }
6705      }
6706    };
6707    if (document.readyState === "complete") {
6708      measureLoadTime();
6709    } else {
6710      window.addEventListener("load", measureLoadTime);
6711      return () => window.removeEventListener("load", measureLoadTime);
6712    }
6713  }, [staffModeEnabled]);
6714  reactExports.useEffect(() => {
6715    if (!staffModeEnabled) return;
6716    const handleQueryTiming = (e) => {
6717      if (e.detail) {
6718        setDbQueryTime(Math.round(e.detail.queryTime));
6719        setDbQueryCount(e.detail.queryCount);
6720        setClientApiTime(Math.round(e.detail.apiTime));
6721      }
6722    };
6723    window.addEventListener("page-query-timing", handleQueryTiming);
6724    return () => window.removeEventListener("page-query-timing", handleQueryTiming);
6725  }, [staffModeEnabled]);
6726  reactExports.useEffect(() => {
6727    if (!staffModeEnabled) return;
6728    setDbQueryTime(0);
6729    setDbQueryCount(0);
6730    setClientApiTime(0);
6731  }, [staffModeEnabled, window.location.pathname]);
6732  if (!staffModeEnabled) return null;
6733  const getLoadTimeColor = (ms) => {
6734    if (!ms) return "text-slate-300";
6735    if (ms < 600) return "text-emerald-400";
6736    if (ms < 1500) return "text-amber-400";
6737    return "text-rose-400";
6738  };
6739  const getApiTimeColor = (ms) => {
6740    if (ms === 0) return "text-slate-300";
6741    if (ms < 250) return "text-emerald-400";
6742    if (ms < 800) return "text-amber-400";
6743    return "text-rose-400";
6744  };
6745  const getDbTimeColor = (ms) => {
6746    if (ms === 0) return "text-slate-300";
6747    if (ms < 50) return "text-emerald-400";
6748    if (ms < 150) return "text-amber-400";
6749    return "text-rose-400";
6750  };
6751  const getCpuColor = (percent) => {
6752    if (percent === void 0) return "text-slate-300";
6753    if (percent < 30) return "text-emerald-400";
6754    if (percent < 70) return "text-amber-400";
6755    return "text-rose-400";
6756  };
6757  const getMemoryColor = (percent) => {
6758    if (percent === void 0) return "text-slate-300";
6759    if (percent < 50) return "text-emerald-400";
6760    if (percent < 85) return "text-amber-400";
6761    return "text-rose-400";
6762  };
6763  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "staff-admin-bar bg-indigo-600 text-white py-1 px-4 flex items-center justify-between text-xs font-bold animate-in slide-in-from-top duration-300 sticky top-0 z-[60] shadow-lg", children: [
6764    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
6765      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 px-2 py-0.5 bg-white/20 rounded-full select-none", children: [
6766        /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 14, className: "fill-current" }),
6767        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "STAFF MODE ACTIVE" })
6768      ] }),
6769      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden md:flex items-center gap-4 opacity-90 select-none", children: [
6770        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
6771          /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 14 }),
6772          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6773            "Users: ",
6774            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-300", children: [
6775              stats2.activeUserCount,
6776              " Active"
6777            ] })
6778          ] })
6779        ] }),
6780        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 border-l border-white/20 pl-4", children: [
6781          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 14 }),
6782          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6783            "Reports: ",
6784            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: stats2.pendingReports > 0 ? "text-rose-300" : "text-emerald-300", children: [
6785              stats2.pendingReports,
6786              " ",
6787              stats2.pendingReports === 1 ? "Pending" : "Pending"
6788            ] })
6789          ] })
6790        ] })
6791      ] })
6792    ] }),
6793    /* @__PURE__ */ jsxRuntimeExports.jsxs(
6794      "div",
6795      {
6796        className: "relative hidden lg:flex items-center gap-6 text-white/90 font-medium select-none cursor-pointer group py-1",
6797        onMouseEnter: () => setShowTelemetryDetails(true),
6798        onMouseLeave: () => setShowTelemetryDetails(false),
6799        children: [
6800          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 hover:text-white transition-colors", children: [
6801            /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { size: 13, className: "text-indigo-200 animate-pulse" }),
6802            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6803              "Page Load: ",
6804              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: getLoadTimeColor(initialLoadTime), children: initialLoadTime ? `${initialLoadTime}ms` : "Measuring..." })
6805            ] })
6806          ] }),
6807          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 border-l border-white/20 pl-4 hover:text-white transition-colors", children: [
6808            /* @__PURE__ */ jsxRuntimeExports.jsx(Database, { size: 13, className: "text-indigo-200" }),
6809            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6810              "DB Queries: ",
6811              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: getDbTimeColor(dbQueryTime), children: dbQueryTime > 0 ? `${dbQueryTime}ms` : "0ms" })
6812            ] })
6813          ] }),
6814          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 border-l border-white/20 pl-4 hover:text-white transition-colors", children: [
6815            /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 13, className: "text-indigo-200" }),
6816            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6817              "CPU: ",
6818              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: getCpuColor((_a = stats2.system) == null ? void 0 : _a.cpu), children: ((_b = stats2.system) == null ? void 0 : _b.cpu) !== void 0 ? `${stats2.system.cpu}%` : "0%" })
6819            ] })
6820          ] }),
6821          showTelemetryDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-full left-1/2 -translate-x-1/2 mt-1 w-80 bg-slate-900/95 backdrop-blur-md border border-slate-700 text-slate-100 p-4 rounded-xl shadow-2xl z-[70] flex flex-col gap-3 font-normal text-xs animate-in fade-in slide-in-from-top-1 duration-200", children: [
6822            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-slate-800 pb-2", children: [
6823              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-indigo-400 flex items-center gap-1", children: [
6824                /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { size: 14 }),
6825                " SYSTEM PERFORMANCE TELEMETRY"
6826              ] }),
6827              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-indigo-500/20 text-indigo-300 px-1.5 py-0.5 rounded-full font-bold", children: "STAFF MODE" })
6828            ] }),
6829            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
6830              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 font-bold tracking-wider uppercase", children: "Latency Breakdowns" }),
6831              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
6832                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-slate-300", children: [
6833                  /* @__PURE__ */ jsxRuntimeExports.jsx(Timer, { size: 13, className: "text-slate-400" }),
6834                  " Initial Hard Load"
6835                ] }),
6836                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-bold ${getLoadTimeColor(initialLoadTime)}`, children: initialLoadTime ? `${initialLoadTime}ms` : "Loading..." })
6837              ] }),
6838              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
6839                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-slate-300", children: [
6840                  /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 13, className: "text-slate-400" }),
6841                  " Current Page APIs"
6842                ] }),
6843                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-bold ${getApiTimeColor(clientApiTime)}`, children: clientApiTime > 0 ? `${clientApiTime}ms` : "0ms" })
6844              ] }),
6845              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
6846                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-slate-300", children: [
6847                  /* @__PURE__ */ jsxRuntimeExports.jsx(Database, { size: 13, className: "text-slate-400" }),
6848                  " DB Execution (",
6849                  dbQueryCount,
6850                  " queries)"
6851                ] }),
6852                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-bold ${getDbTimeColor(dbQueryTime)}`, children: dbQueryTime > 0 ? `${dbQueryTime}ms` : "0ms" })
6853              ] }),
6854              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pl-4 text-[11px] text-slate-400", children: [
6855                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Express App Overhead" }),
6856                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-slate-300", children: clientApiTime > dbQueryTime ? `${clientApiTime - dbQueryTime}ms` : "0ms" })
6857              ] })
6858            ] }),
6859            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 border-t border-slate-800 pt-2", children: [
6860              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 font-bold tracking-wider uppercase", children: "Server Resources" }),
6861              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
6862                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between", children: [
6863                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-slate-300", children: [
6864                    /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 13, className: "text-slate-400" }),
6865                    " Server CPU"
6866                  ] }),
6867                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `font-bold ${getCpuColor((_c = stats2.system) == null ? void 0 : _c.cpu)}`, children: [
6868                    ((_d = stats2.system) == null ? void 0 : _d.cpu) ?? 0,
6869                    "%"
6870                  ] })
6871                ] }),
6872                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-1.5 bg-slate-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6873                  "div",
6874                  {
6875                    className: `h-full rounded-full transition-all duration-500 ${((_e = stats2.system) == null ? void 0 : _e.cpu) > 70 ? "bg-rose-500" : ((_f = stats2.system) == null ? void 0 : _f.cpu) > 30 ? "bg-amber-500" : "bg-emerald-500"}`,
6876                    style: { width: `${((_g = stats2.system) == null ? void 0 : _g.cpu) ?? 0}%` }
6877                  }
6878                ) })
6879              ] }),
6880              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
6881                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between", children: [
6882                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-slate-300", children: [
6883                    /* @__PURE__ */ jsxRuntimeExports.jsx(HardDrive, { size: 13, className: "text-slate-400" }),
6884                    " Server Memory"
6885                  ] }),
6886                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `font-bold ${getMemoryColor((_h = stats2.system) == null ? void 0 : _h.memory)}`, children: [
6887                    ((_i = stats2.system) == null ? void 0 : _i.memory) ?? 0,
6888                    "%"
6889                  ] })
6890                ] }),
6891                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-1.5 bg-slate-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6892                  "div",
6893                  {
6894                    className: `h-full rounded-full transition-all duration-500 ${((_j = stats2.system) == null ? void 0 : _j.memory) > 70 ? "bg-rose-500" : ((_k = stats2.system) == null ? void 0 : _k.memory) > 30 ? "bg-amber-500" : "bg-emerald-500"}`,
6895                    style: { width: `${((_l = stats2.system) == null ? void 0 : _l.memory) ?? 0}%` }
6896                  }
6897                ) })
6898              ] })
6899            ] }),
6900            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-slate-800 pt-2 text-[10px] text-slate-500 text-center italic", children: "Timings accumulate over the active page's lifetime and reset on route changes." })
6901          ] })
6902        ]
6903      }
6904    ),
6905    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
6906      isSuperAdmin && /* @__PURE__ */ jsxRuntimeExports.jsx(
6907        "button",
6908        {
6909          onClick: toggleStaffMode,
6910          className: "bg-white/10 hover:bg-white/20 px-3 py-1 rounded transition-colors flex items-center gap-1",
6911          children: "Disable Staff Mode"
6912        }
6913      ),
6914      /* @__PURE__ */ jsxRuntimeExports.jsxs(
6915        "a",
6916        {
6917          href: ((_m = window.HAMIE_ENV) == null ? void 0 : _m.ADMIN_URL) ? `${window.HAMIE_ENV.ADMIN_URL}/admin` : "/admin",
6918          target: "_blank",
6919          className: "flex items-center gap-1 hover:underline",
6920          children: [
6921            "Admin Studio ",
6922            /* @__P
6922URE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { size: 12 })
6923          ]
6924        }
6925      )
6926    ] })
6927  ] });
6928};
6929function formatRelativeTime(date) {
6930  const now = Date.now();
6931  const then = new Date(date).getTime();
6932  const diff = Math.floor((now - then) / 1e3);
6933  if (diff < 60) return "just now";
6934  if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
6935  if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
6936  if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`;
6937  return new Date(date).toLocaleDateString();
6938}
6939function getIcon(type) {
6940  switch (type) {
6941    case "upload":
6942      return /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 14, className: "text-zinc-600 dark:text-white" });
6943    case "like":
6944      return /* @__PURE__ */ jsxRuntimeExports.jsx(Heart, { size: 14, className: "text-red-500 fill-red-500" });
6945    case "reply":
6946      return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { size: 14, className: "text-blue-500 dark:text-blue-400 fill-blue-500 dark:fill-blue-400" });
6947    case "subscriber":
6948      return /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 14, className: "text-emerald-500 dark:text-emerald-400" });
6949    default:
6950      return /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 14, className: "text-zinc-400" });
6951  }
6952}
6953const NotificationsModal = ({
6954  isOpen,
6955  onClose,
6956  previewNotifications,
6957  onMarkAsRead,
6958  onMarkAllRead,
6959  onNavigate
6960}) => {
6961  const { t } = useTranslation();
6962  const [notifications, setNotifications] = reactExports.useState([]);
6963  const [hasMore, setHasMore] = reactExports.useState(false);
6964  const [nextCursor, setNextCursor] = reactExports.useState(null);
6965  const [isLoading, setIsLoading] = reactExports.useState(false);
6966  const [isInitialLoad, setIsInitialLoad] = reactExports.useState(true);
6967  const sentinelRef = reactExports.useRef(null);
6968  const scrollContainerRef = reactExports.useRef(null);
6969  reactExports.useEffect(() => {
6970    if (!isOpen) return;
6971    setNotifications(previewNotifications);
6972    setIsInitialLoad(true);
6973    setNextCursor(null);
6974    setHasMore(false);
6975    loadPage(void 0, true);
6976  }, [isOpen]);
6977  const loadPage = reactExports.useCallback(async (cursor, isFirst) => {
6978    setIsLoading(true);
6979    try {
6980      const result = await api.getNotificationPage(cursor);
6981      setNotifications((prev) => {
6982        if (isFirst) return result.items;
6983        const existingIds = new Set(prev.map((n) => n.id));
6984        const newItems = result.items.filter((n) => !existingIds.has(n.id));
6985        return [...prev, ...newItems];
6986      });
6987      setHasMore(result.hasMore);
6988      setNextCursor(result.nextCursor);
6989    } finally {
6990      setIsLoading(false);
6991      setIsInitialLoad(false);
6992    }
6993  }, []);
6994  reactExports.useEffect(() => {
6995    if (!sentinelRef.current || !hasMore || isLoading) return;
6996    const observer = new IntersectionObserver(
6997      (entries) => {
6998        if (entries[0].isIntersecting && hasMore && !isLoading) {
6999          loadPage(nextCursor ?? void 0);
7000        }
7001      },
7002      { root: scrollContainerRef.current, rootMargin: "100px" }
7003    );
7004    observer.observe(sentinelRef.current);
7005    return () => observer.disconnect();
7006  }, [hasMore, isLoading, nextCursor, loadPage]);
7007  const handleClick = (note) => {
7008    if (!note.isRead) onMarkAsRead(note.id);
7009    setNotifications((prev) => prev.map((n) => n.id === note.id ? { ...n, isRead: true } : n));
7010    if (note.link) {
7011      onNavigate(note.link);
7012      onClose();
7013    }
7014  };
7015  const handleMarkAllRead = () => {
7016    onMarkAllRead();
7017    setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true })));
7018  };
7019  const unreadCount = notifications.filter((n) => !n.isRead).length;
7020  if (!isOpen) return null;
7021  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
7022    /* @__PURE__ */ jsxRuntimeExports.jsx(
7023      "div",
7024      {
7025        className: "fixed inset-0 z-40 bg-black/30 backdrop-blur-sm",
7026        onClick: onClose
7027      }
7028    ),
7029    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed top-16 right-4 z-50 w-[420px] max-h-[calc(100vh-5rem)] flex flex-col bg-white dark:bg-[#1e1e1e] border border-zinc-200 dark:border-zinc-700 rounded-2xl shadow-2xl animate-in fade-in slide-in-from-top-3 duration-200", children: [
7030      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-zinc-100 dark:border-zinc-700/60 shrink-0", children: [
7031        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
7032          /* @__PURE__ */ jsxRuntimeExports.jsx(Bell, { size: 18, className: "text-indigo-500" }),
7033          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "font-semibold text-base text-zinc-900 dark:text-white", children: t("notifications.title", "Notifications") }),
7034          unreadCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 text-xs font-bold px-2 py-0.5 rounded-full", children: unreadCount })
7035        ] }),
7036        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
7037          unreadCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
7038            "button",
7039            {
7040              onClick: handleMarkAllRead,
7041              className: "flex items-center gap-1.5 text-xs text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-200 px-2.5 py-1.5 rounded-lg hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-colors font-medium",
7042              title: t("notifications.markAllRead", "Mark all as read"),
7043              children: [
7044                /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCheck, { size: 14 }),
7045                t("notifications.markAllRead", "Mark all read")
7046              ]
7047            }
7048          ),
7049          /* @__PURE__ */ jsxRuntimeExports.jsx(
7050            "button",
7051            {
7052              onClick: onClose,
7053              className: "p-1.5 rounded-lg text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-700 transition-colors",
7054              children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 18 })
7055            }
7056          )
7057        ] })
7058      ] }),
7059      /* @__PURE__ */ jsxRuntimeExports.jsx(
7060        "div",
7061        {
7062          ref: scrollContainerRef,
7063          className: "flex-1 overflow-y-auto custom-scrollbar",
7064          children: isInitialLoad ? (
7065            // Skeleton loading
7066            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 px-5 py-3.5 border-b border-zinc-100 dark:border-zinc-800", children: [
7067              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-10 h-10 rounded-full bg-zinc-200 dark:bg-zinc-700 animate-pulse shrink-0" }),
7068              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 space-y-2 py-1", children: [
7069                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-3 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse w-4/5" }),
7070                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-2.5 bg-zinc-100 dark:bg-zinc-800 rounded animate-pulse w-2/5" })
7071              ] })
7072            ] }, i)) })
7073          ) : notifications.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-16 gap-3 text-center px-6", children: [
7074            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-14 h-14 rounded-full bg-zinc-100 dark:bg-zinc-800 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Bell, { size: 24, className: "text-zinc-400" }) }),
7075            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm leading-relaxed", children: t("notifications.empty", "No notifications here. When someone likes, comments, or subscribes, you'll see it here.") })
7076          ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
7077            notifications.map((note) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
7078              "div",
7079              {
7080                onClick: () => handleClick(note),
7081                className: `flex gap-3 px-5 py-3.5 border-b border-zinc-100 dark:border-zinc-800/60 cursor-pointer transition-colors relative group
7082                                        ${!note.isRead ? "bg-indigo-50/60 dark:bg-indigo-950/20 hover:bg-indigo-50 dark:hover:bg-indigo-950/30" : "hover:bg-zinc-50 dark:hover:bg-zinc-800/40"}
7083                                        ${note.link ? "cursor-pointer" : "cursor-default"}
7084                                    `,
7085                children: [
7086                  !note.isRead && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-2 top-1/2 -translate-y-1/2 w-1.5 h-1.5 bg-indigo-500 rounded-full" }),
7087                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative shrink-0", children: [
7088                    /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: note.actor.avatar, name: note.actor.name, size: 40 }),
7089                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -bottom-1 -right-1 p-1 bg-white dark:bg-[#1e1e1e] rounded-full ring-2 ring-white dark:ring-[#1e1e1e]", children: getIcon(note.type) })
7090                  ] }),
7091                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
7092                    /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm leading-snug text-zinc-900 dark:text-white", children: [
7093                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold mr-1", children: note.actor.name }),
7094                      /* @__PURE__ */ jsxRuntimeExports.jsx(
7095                        "span",
7096                        {
7097                          className: "text-zinc-600 dark:text-zinc-300",
7098                          dangerouslySetInnerHTML: { __html: note.message }
7099                        }
7100                      )
7101                    ] }),
7102                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 mt-1 block", children: formatRelativeTime(note.timestamp) })
7103                  ] }),
7104                  note.thumbnail ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 w-16 h-10 bg-zinc-200 dark:bg-zinc-800 rounded-md overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: note.thumbnail, className: "w-full h-full object-cover opacity-80", alt: "" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-10 shrink-0" }),
7105                  !note.isRead && /* @__PURE__ */ jsxRuntimeExports.jsx(
7106                    "button",
7107                    {
7108                      onClick: (e) => {
7109                        e.stopPropagation();
7110                        handleClick({ ...note, link: void 0 });
7111                      },
7112                      className: "absolute right-3 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 p-1 rounded-full bg-zinc-100 dark:bg-zinc-700 text-zinc-500 hover:text-indigo-600 dark:hover:text-indigo-400 transition-all",
7113                      title: t("notifications.markRead", "Mark as read"),
7114                      children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 12 })
7115                    }
7116                  )
7117                ]
7118              },
7119              note.id
7120            )),
7121            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: sentinelRef, className: "py-2 flex justify-center", children: [
7122              isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5 items-center text-zinc-400 text-xs py-2", children: [
7123                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce [animation-delay:-0.3s]" }),
7124                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce [animation-delay:-0.15s]" }),
7125                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce" })
7126              ] }),
7127              !hasMore && !isLoading && notifications.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 dark:text-zinc-600 py-3", children: t("notifications.endOfList", "You're all caught up!") })
7128            ] })
7129          ] })
7130        }
7131      )
7132    ] })
7133  ] });
7134};
7135function lazyWithRetry(componentImport) {
7136  return React.lazy(async () => {
7137    try {
7138      return await componentImport();
7139    } catch (error) {
7140      console.error("Failed to fetch dynamically imported module. Attempting auto-reload...", error);
7141      const hasAutoReloaded = sessionStorage.getItem("has_auto_reloaded_from_error");
7142      if (!hasAutoReloaded) {
7143        sessionStorage.setItem("has_auto_reloaded_from_error", "true");
7144        window.location.reload();
7145        return new Promise(() => {
7146        });
7147      }
7148      throw error;
7149    }
7150  });
7151}
7152class ErrorBoundary extends reactExports.Component {
7153  constructor() {
7154    super(...arguments);
7155    this.state = {
7156      hasError: false,
7157      error: null
7158    };
7159  }
7160  static getDerivedStateFromError(error) {
7161    return { hasError: true, error };
7162  }
7163  componentDidCatch(error, errorInfo) {
7164    console.error("Uncaught error:", error, errorInfo);
7165    fetch("/api/log-client-error", {
7166      method: "POST",
7167      headers: { "Content-Type": "application/json" },
7168      body: JSON.stringify({
7169        message: error.message,
7170        source: "React ErrorBoundary",
7171        lineno: 0,
7172        colno: 0,
7173        stack: error.stack + "\n" + errorInfo.componentStack,
7174        url: window.location.href
7175      })
7176    }).catch((e) => console.error("Failed to send error log:", e));
7177    try {
7178      const hasAutoReloaded = sessionStorage.getItem("has_auto_reloaded_from_error");
7179      if (!hasAutoReloaded) {
7180        sessionStorage.setItem("has_auto_reloaded_from_error", "true");
7181        window.location.reload();
7182      }
7183    } catch (e) {
7184      console.error("Failed to handle auto-reload in ErrorBoundary:", e);
7185    }
7186  }
7187  render() {
7188    var _a, _b;
7189    if (this.state.hasError) {
7190      if (this.props.fallback) {
7191        return this.props.fallback;
7192      }
7193      const isAdmin = window.location.pathname.startsWith("/admin");
7194      return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-screen bg-zinc-50 dark:bg-zinc-900 text-zinc-900 dark:text-white p-6 text-center", children: [
7195        /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-2xl font-bold mb-4", children: isAdmin ? "Module Crash Detected" : "Something went wrong." }),
7196        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-600 dark:text-zinc-400 mb-6 max-w-md", children: isAdmin ? "An error occurred while rendering this module. Check the stack trace below." : "We've logged this error and will look into it. Please try refreshing the page." }),
7197        /* @__PURE__ */ jsxRuntimeExports.jsx(
7198          "button",
7199          {
7200            onClick: () => window.location.reload(),
7201            className: "px-6 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition-colors",
7202            children: "Refresh Page"
7203          }
7204        ),
7205        isAdmin && /* @__PURE__ */ jsxRuntimeExports.jsxs("pre", { className: "mt-8 p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-left text-xs overflow-auto max-w-2xl whitespace-pre-wrap break-words", children: [
7206          (_a = this.state.error) == null ? void 0 : _a.toString(),
7207          "\n\n",
7208          (_b = this.state.error) == null ? void 0 : _b.stack
7209        ] }),
7210        false
7211      ] });
7212    }
7213    return this.props.children;
7214  }
7215}
7216const HomePage = lazyWithRetry(() => __vitePreload(() => import("./HomePage-q4aW8A7v.js"), true ? __vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]) : void 0));
7217const AdvancedSearchPage = lazyWithRetry(() => __vitePreload(() => import("./AdvancedSearchPage-tGa2266t.js"), true ? __vite__mapDeps([23,1,4,5,6,7,8,14,16,3,17,18,19,20,21,22,15,13,24]) : void 0));
7218const MelodySearchPage = lazyWithRetry(() => __vitePreload(() => import("./MelodySearchPage-C8DfrA33.js"), true ? __vite__mapDeps([25,1,4,5,6,7,8,14,16,3,17,18,19,20,21,22,15,13]) : void 0));
7219const WatchPage = lazyWithRetry(() => __vitePreload(() => import("./WatchPage-B8_07PT2.js"), true ? __vite__mapDeps([26,1,4,5,6,7,8,22,14,21,3,17,16,18,19,20,27,28,12,13,15]) : void 0));
7220const ExplorePage = lazyWithRetry(() => __vitePreload(() => import("./ExplorePage-BWHkev4d.js"), true ? __vite__mapDeps([29,1,11,5,6,7,4,8,14,16,3,17,18,19,20,21,22,15,13]) : void 0));
7221const AccountPage = lazyWithRetry(() => __vitePreload(() => import("./AccountPage-mZQDQlod.js"), true ? __vite__mapDeps([30,1,14,4,5,6,7,8,31,13,32,16,3,17,18,19,20,21,22,15]) : void 0));
7222const ConverterPage = lazyWithRetry(() => __vitePreload(() => import("./ConverterPage-kt7PUb_r.js"), true ? __vite__mapDeps([33,16,1,6,5,7,4,8,34,21,22,14,35]) : void 0));
7223const EditorPage = lazyWithRetry(() => __vitePreload(() => import("./EditorPage-Dsed0DcB.js"), true ? __vite__mapDeps([36,1,37,20,6,5,7,28,38,19,3,4,8,16,32]) : void 0));
7224const HistoryPage = lazyWithRetry(() => __vitePreload(() => import("./HistoryPage-BYHoeFoZ.js"), true ? __vite__mapDeps([39,1,40,14,4,5,6,7,8,16,3,17,18,19,20,21,22,15,13]) : void 0));
7225const LikedPage = lazyWithRetry(() => __vitePreload(() => import("./LikedPage-SwAUDqZq.js"), true ? __vite__mapDeps([41,1,40,14,4,5,6,7,8,16,3,17,18,19,20,21,22,15,13]) : void 0));
7226const WatchLaterPage = lazyWithRetry(() => __vitePreload(() => import("./WatchLaterPage-32xxryAT.js"), true ? __vite__mapDeps([42,1,40,14,4,5,6,7,8,16,3,17,18,19,20,21,22,15,13]) : void 0));
7227const SubscriptionsPage = lazyWithRetry(() => __vitePreload(() => import("./SubscriptionsPage-D5-uiNqs.js"), true ? __vite__mapDeps([43,1,40,14,4,5,6,7,8,16,3,17,18,19,20,21,22,15,13]) : void 0));
7228const ChannelPage = lazyWithRetry(() => __vitePreload(() => import("./ChannelPage-DAFNmqL_.js"), true ? __vite__mapDeps([44,1,4,5,6,7,8,14,13,3,16,17,18,19,20,21,22,15]) : void 0));
7229const TagPage = lazyWithRetry(() => __vitePreload(() => import("./TagPage-DI5S2d93.js"), true ? __vite__mapDeps([45,1,4,5,6,7,8,40,14,16,3,17,18,19,20,21,22,15,13]) : void 0));
7230const PlaylistsPage = lazyWithRetry(() => __vitePreload(() => import("./PlaylistsPage-Br-5J847.js"), true ? __vite__mapDeps([31,1,4,5,6,7,8]) : void 0));
7231const HelpPage = lazyWithRetry(() => __vitePreload(() => import("./HelpPage-DsiYZpvH.js"), true ? __vite__mapDeps([46,1,4,5,6,7,8,3,34]) : void 0));
7232const MessengerPage = lazyWithRetry(() => __vitePreload(() =>
7232 import("./MessengerPage-D0_m_QNf.js"), true ? __vite__mapDeps([47,1,4,5,6,7,8,13]) : void 0));
7233const LyricsPage = lazyWithRetry(() => __vitePreload(() => import("./LyricsPage-BWsBdPvc.js"), true ? __vite__mapDeps([48,1,4,5,6,7,8,14]) : void 0));
7234const LyricsSearchPage = lazyWithRetry(() => __vitePreload(() => import("./LyricsSearchPage-CzRGUKfj.js"), true ? __vite__mapDeps([49,1,4,5,6,7,8]) : void 0));
7235const AdminApp = lazyWithRetry(() => __vitePreload(() => import("./AdminApp-D1oXn0it.js"), true ? __vite__mapDeps([50,1,16,6,5,7,4,8]) : void 0));
7236const PersonalSpacePage = lazyWithRetry(() => __vitePreload(() => import("./PersonalSpacePage-BQt1uXtk.js"), true ? __vite__mapDeps([51,1,4,5,6,7,8,3]) : void 0));
7237const NewlyUploadedPage = lazyWithRetry(() => __vitePreload(() => import("./NewlyUploadedPage-CswaPEfP.js"), true ? __vite__mapDeps([52,1,14,4,5,6,7,8,16,3,17,18,19,20,21,22,15,13]) : void 0));
7238const BillingPage = lazyWithRetry(() => __vitePreload(() => import("./BillingPage-B2Hb0PMo.js"), true ? __vite__mapDeps([53,1,4,5,6,7,8]) : void 0));
7239const CollectionIndexPage = lazyWithRetry(() => __vitePreload(() => import("./CollectionIndexPage-B76qFKH2.js"), true ? __vite__mapDeps([54,1,4,5,6,7,8,3,9]) : void 0));
7240const CollectionPage = lazyWithRetry(() => __vitePreload(() => import("./CollectionPage-DTEimYXF.js"), true ? __vite__mapDeps([55,1,4,5,6,7,8,2,3,9,10,16,17,18,19,20,21,22,15,13]) : void 0));
7241const CollectionSectionPage = lazyWithRetry(() => __vitePreload(() => import("./CollectionSectionPage-B6Zl3jqi.js"), true ? __vite__mapDeps([56,1,4,5,6,7,8,10,3,9,16,17,18,19,20,21,22,15,13]) : void 0));
7242const EmailVerification = lazyWithRetry(() => __vitePreload(() => import("./EmailVerification-D-JaNSfM.js"), true ? __vite__mapDeps([57,1,4,5,6,7,8]) : void 0));
7243const ResetPassword = lazyWithRetry(() => __vitePreload(() => import("./ResetPassword-CpmlaEYr.js"), true ? __vite__mapDeps([58,1,4,5,6,7,8]) : void 0));
7244const ScoreConverterPage = lazyWithRetry(() => __vitePreload(() => import("./ScoreConverterPage-BvnaneVe.js"), true ? __vite__mapDeps([59,1,35,5,6,7]) : void 0));
7245const VocalProcessorPage = lazyWithRetry(() => __vitePreload(() => import("./VocalProcessorPage-BWIy7sro.js"), true ? __vite__mapDeps([60,1,6,5,7,17,16,18,19,20]) : void 0));
7246const AlgorithmStudio = lazyWithRetry(() => __vitePreload(() => import("./AlgorithmStudio-BAKVJ64e.js"), true ? __vite__mapDeps([61,1,6,5,7]) : void 0));
7247const AlgorithmEditor = lazyWithRetry(() => __vitePreload(() => import("./AlgorithmEditor-BuTd-7oq.js"), true ? __vite__mapDeps([62,1,63,6,5,7]) : void 0));
7248const StagingArena = lazyWithRetry(() => __vitePreload(() => import("./StagingArena-C9ym5iYy.js"), true ? __vite__mapDeps([64,1,63,6,5,7]) : void 0));
7249const TribunalPage = lazyWithRetry(() => __vitePreload(() => import("./TribunalPage-yYCBAk5S.js"), true ? __vite__mapDeps([65,1,4,5,6,7,8,13,16,3,17,18,19,20,21,22,15]) : void 0));
7250const HamieLabPage = lazyWithRetry(() => __vitePreload(() => import("./HamieLabPage-KXwalnzA.js"), true ? __vite__mapDeps([66,1,4,5,6,7,8,15]) : void 0));
7251const MidiVisualizerPage = lazyWithRetry(() => __vitePreload(() => import("./MidiVisualizerPage-B4L5oo98.js"), true ? __vite__mapDeps([67,16,1,6,5,7,20,4,8,3]) : void 0));
7252const DMCAForm = lazyWithRetry(() => __vitePreload(() => import("./DMCAForm-DoA_m2iR.js"), true ? __vite__mapDeps([68,1,6,5,7]) : void 0));
7253const SheetMusicDigitizerPage = lazyWithRetry(() => __vitePreload(() => import("./SheetMusicDigitizerPage-CGO_x0iu.js"), true ? __vite__mapDeps([69,1,4,5,6,7,8,34,37,20,28,38,19,3,35]) : void 0));
7254const AudioDigitizerPage = lazyWithRetry(() => __vitePreload(() => import("./AudioDigitizerPage-DjRFGvnM.js"), true ? __vite__mapDeps([70,1,4,5,6,7,8,34]) : void 0));
7255const ClientAudioDigitizerPage = lazyWithRetry(() => __vitePreload(() => import("./ClientAudioDigitizerPage-wnww40sz.js"), true ? __vite__mapDeps([71,16,1,6,5,7,72,20]) : void 0));
7256const NotationEditor = lazyWithRetry(() => __vitePreload(() => import("./index--Kd7xwwe.js").then((n) => n.i), true ? __vite__mapDeps([73,16,1,6,5,7,20,4,8]) : void 0));
7257const KeyboardEmulatorPage = lazyWithRetry(() => __vitePreload(() => import("./KeyboardEmulatorPage-3bHJ91O7.js"), true ? __vite__mapDeps([74,1,6,5,7,4,8,18,19,22,28]) : void 0));
7258const SoundfontsPage = lazyWithRetry(() => __vitePreload(() => import("./SoundfontsPage-Kim5OuPr.js"), true ? __vite__mapDeps([75,1,4,5,6,7,8,14]) : void 0));
7259const SoundfontDetailPage = lazyWithRetry(() => __vitePreload(() => import("./SoundfontDetailPage-R4ZkN9OB.js"), true ? __vite__mapDeps([76,1,4,5,6,7,8,27,16,28,18,19]) : void 0));
7260const SoundfontUploadPage = lazyWithRetry(() => __vitePreload(() => import("./SoundfontUploadPage-C9Pu1znK.js"), true ? __vite__mapDeps([77,1,4,5,6,7,8]) : void 0));
7261const DynamicPageRenderer = ({ module }) => {
7262  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-10 max-w-4xl mx-auto animate-in fade-in", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-yellow-50 dark:bg-yellow-900/10 border border-yellow-200 dark:border-yellow-800 rounded-xl p-6 mb-6", children: [
7263    /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-yellow-800 dark:text-yellow-200 mb-2", children: "Dynamic Preview" }),
7264    /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-yellow-700 dark:text-yellow-300 mb-4", children: "This page was created via Site Architect. In a production environment, this would be a server-rendered page or a dynamically compiled component." }),
7265    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white dark:bg-black p-4 rounded-lg border border-zinc-200 dark:border-zinc-800 font-mono text-xs overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { children: module.content }) })
7266  ] }) });
7267};
7268const LoadingFallback = () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center min-h-[50vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" }) });
7269const KNOWN_VIEWS = /* @__PURE__ */ new Set([
7270  "home",
7271  "explore",
7272  "history",
7273  "melody-search",
7274  "liked",
7275  "watchlater",
7276  "subscriptions",
7277  "channel",
7278  "tag",
7279  "playlists",
7280  "help",
7281  "terms",
7282  "privacy",
7283  "messages",
7284  "account",
7285  "converter",
7286  "editor",
7287  "algorithm-studio",
7288  "algorithm-editor",
7289  "algorithm-arena",
7290  "tribunal",
7291  "lab",
7292  "visualizer",
7293  "dmca",
7294  "verify-email",
7295  "reset-password",
7296  "not-found",
7297  "admin",
7298  "lyrics",
7299  "lyrics-search",
7300  "sheet-digitizer",
7301  "audio-digitizer",
7302  "client-transcriber",
7303  "score-format-converter",
7304  "vocal-processor",
7305  "lab",
7306  "notation-editor",
7307  "personal-space",
7308  "newly-uploaded",
7309  "billing",
7310  "search",
7311  "collections",
7312  "collection",
7313  "collection-section",
7314  "keyboard-emulator",
7315  "soundfonts",
7316  "soundfont",
7317  "soundfonts-upload"
7318]);
7319function parseUrl(pathname, search) {
7320  const params = new URLSearchParams(search);
7321  const tagName = params.get("name") || null;
7322  const channelName = params.get("c") || null;
7323  const query = params.get("q") || "";
7324  let view = "home";
7325  let id = null;
7326  let category = params.get("cat") || "all";
7327  const path = pathname;
7328  if (path === "/admin" || path.startsWith("/admin/")) {
7329    view = "admin";
7330  } else {
7331    const lyricMatch = path.match(/^\/lyrics(\d+)\.html$/);
7332    const cleanLyricMatch = path.match(/^\/lyrics\/(\d+)$/);
7333    const scoreMatch = path.match(/^\/score(b)?(\d+)-(\d+)(?:-(\d+))?\.html$/);
7334    const sheetmusicMatch = path.match(/^\/(?:sheetmusic|gtab)(\d+)\.html$/);
7335    if (lyricMatch || cleanLyricMatch) {
7336      id = lyricMatch ? lyricMatch[1] : cleanLyricMatch[1];
7337      view = "lyrics";
7338    } else if (path === "/lyrics/search") {
7339      view = "lyrics-search";
7340    } else if (scoreMatch || sheetmusicMatch) {
7341      id = scoreMatch ? scoreMatch[2] : sheetmusicMatch[1];
7342      view = "watch";
7343    } else {
7344      const match = path.match(/^\/midi(?:[a-zA-Z0-9_\-]+\/)?(\d+)_(.+)\.html$/);
7345      const trackMatch = path.match(/^\/track\/(\d+)$/);
7346      if (match) {
7347        id = match[1];
7348        view = "watch";
7349      } else if (trackMatch) {
7350        id = trackMatch[1];
7351        view = "watch";
7352      } else if (path === "/watch") {
7353        const vid = params.get("v");
7354        if (vid) {
7355          id = vid;
7356          view = "watch";
7357        }
7358      } else if (path.startsWith("/algorithm-editor/")) {
7359        id = path.split("/").pop() || null;
7360        view = "algorithm-editor";
7361      } else if (path.startsWith("/algorithm-arena/")) {
7362        id = path.split("/").pop() || null;
7363        view = "algorithm-arena";
7364      } else if (path.startsWith("/editor/")) {
7365        id = path.split("/").pop() || null;
7366        view = "editor";
7367      } else if (path.startsWith("/help/")) {
7368        id = path.split("/").pop() || null;
7369        view = "help";
7370      } else if (path.startsWith("/collections/")) {
7371        const parts = path.split("/");
7372        if (parts.length >= 4) {
7373          const secIdWithSlug = parts.pop() || "";
7374          const slug = parts.pop() || null;
7375          id = slug && secIdWithSlug ? `${slug}:${secIdWithSlug}` : null;
7376          view = "collection-section";
7377        } else {
7378          id = parts.pop() || null;
7379          view = "collection";
7380        }
7381      } else if (path.startsWith("/collection/")) {
7382        let slug = path.split("/").pop() || null;
7383        if (slug === "top-100-classical-composers") {
7384          slug = "classical-composers";
7385          if (typeof window !== "undefined" && window.history) {
7386            window.history.replaceState(null, "", "/collection/classical-composers");
7387          }
7388        }
7389        id = slug;
7390        view = "collection";
7391      } else if (path.startsWith("/collection-section/")) {
7392        id = path.split("/").pop() || null;
7393        view = "collection-section";
7394      } else if (path === "/soundfonts") {
7395        view = "soundfonts";
7396      } else if (path === "/soundfonts/upload") {
7397        view = "soundfonts-upload";
7398      } else if (path.startsWith("/soundfont/")) {
7399        id = path.split("/").pop() || null;
7400        view = "soundfont";
7401      } else if (path === "/editor") {
7402        view = "editor";
7403        id = null;
7404      } else if (path === "/" || path === "") {
7405        const vid = params.get("v");
7406        if (vid) {
7407          id = vid;
7408          view = "watch";
7409        } else {
7410          view = "home";
7411          category = params.get("cat") || "all";
7412        }
7413      } else if (path !== "/" && path !== "") {
7414        view = path.substring(1);
7415        const baseViewName = view.split("?")[0].split("#")[0].toLowerCase();
7416        if (!KNOWN_VIEWS.has(baseViewName)) {
7417          category = baseViewName;
7418        }
7419      }
7420    }
7421  }
7422  return { view, id, tag: tagName, channel: channelName, query, category };
7423}
7424const AppContent = () => {
7425  const { t, i18n } = useTranslation();
7426  const { autoplay, audioQuality, adSettings } = useSettings();
7427  const [currentUser, setCurrentUser] = reactExports.useState(() => {
7428    const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
7429    const keyParam = urlParams ? urlParams.get("key") : null;
7430    const isBotKey = keyParam === "digitizer-test-2026-key" || typeof window !== "undefined" && sessionStorage.getItem("digitizer_test_key") === "digitizer-test-2026-key";
7431    if (isBotKey) {
7432      if (typeof window !== "undefined" && keyParam) {
7433        sessionStorage.setItem("digitizer_test_key", keyParam);
7434      }
7435      return {
7436        id: 1,
7437        username: "Antigravity",
7438        email: "[email protected]",
7439        role: "USER",
7440        subscriptionTier: "premium",
7441        credits: 99999,
7442        createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7443        updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7444        isBanned: false,
7445        showCountryBadge: false,
7446        uploadsCount: 0
7447      };
7448    }
7449    const saved = localStorage.getItem("hamie_session");
7450    if (saved) {
7451      try {
7452        return JSON.parse(saved);
7453      } catch {
7454        return null;
7455      }
7456    }
7457    return null;
7458  });
7459  reactExports.useEffect(() => {
7460    const timer = setTimeout(() => {
7461      try {
7462        sessionStorage.removeItem("has_auto_reloaded_from_error");
7463      } catch (e) {
7464      }
7465    }, 3e3);
7466    return () => clearTimeout(timer);
7467  }, []);
7468  reactExports.useEffect(() => {
7469    var _a;
7470    if (!adSettings || !adSettings.isEnabled || !adSettings.publisherId) {
7471      const existingScript2 = document.getElementById("adsense-global-script");
7472      if (existingScript2) existingScript2.remove();
7473      return;
7474    }
7475    const userRole = (currentUser == null ? void 0 : currentUser.role) || "GUEST";
7476    const isExcluded = (_a = adSettings.excludeRoles) == null ? void 0 : _a.includes(userRole);
7477    if (isExcluded) {
7478      const existingScript2 = document.getElementById("adsense-global-script");
7479      if (existingScript2) existingScript2.remove();
7480      return;
7481    }
7482    const client = adSettings.publisherId.startsWith("ca-") ? adSettings.publisherId : `ca-${adSettings.publisherId}`;
7483    const isNonIntrusive = !!adSettings.enableNonIntrusiveOnly;
7484    isNonIntrusive ? false : adSettings.enableAutoAds;
7485    isNonIntrusive ? false : adSettings.enableVignettes;
7486    isNonIntrusive ? false : adSettings.enableAnchors;
7487    const existingScript = document.getElementById("adsense-global-script");
7488    if (existingScript) existingScript.remove();
7489    const script = document.createElement("script");
7490    script.id = "adsense-global-script";
7491    script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${client}`;
7492    script.async = true;
7493    script.crossOrigin = "anonymous";
7494    script.onerror = () => {
7495      console.warn("Google AdSense script blocked or failed to load.");
7496    };
7497    document.head.appendChild(script);
7498    try {
7499      window.adsbygoogle = window.adsbygoogle || [];
7500      window.__adsbygoogle_pushed = true;
7501    } catch (err) {
7502      console.warn("Failed to set adsense settings programmatically:", err);
7503    }
7504  }, [adSettings, currentUser]);
7505  reactExports.useEffect(() => {
7506    if (typeof window === "undefined" || typeof document === "undefined") return;
7507    const html = document.documentElement;
7508    const body = document.body;
7509    const checkAndFixGap = () => {
7510      const hasHtmlMargin = html.style.marginTop && html.style.marginTop !== "0px";
7511      const hasHtmlPadding = html.style.paddingTop && html.style.paddingTop !== "0px";
7512      const hasBodyMargin = body.style.marginTop && body.style.marginTop !== "0px";
7513      const hasBodyPadding = body.style.paddingTop && body.style.paddingTop !== "0px";
7514      if (hasHtmlMargin || hasHtmlPadding || hasBodyMargin || hasBodyPadding) {
7515        const fixedElements = Array.from(document.querySelectorAll("*")).filter((el) => {
7516          try {
7517            const style = window.getComputedStyle(el);
7518            return (style.position === "fixed" || style.position === "sticky") && (style.top === "0px" || style.top === "0") && el !== html && el !== body && el.id !== "root" && !el.closest("header") && el.tagName !== "HEADER";
7519          } catch (e) {
7520            return false;
7521          }
7522        });
7523        let topAdVisible = false;
7524        for (const el of fixedElements) {
7525          const hasGoogleAd = el.querySelector('ins.adsbygoogle, iframe[id*="google_ads"], iframe[src*="googleads"]') || el.classList.contains("adsbygoogle") || el.id.includes("google") || Array.from(el.attributes).some((attr) => attr.name.includes("google") || attr.value.includes("google"));
7526          if (hasGoogleAd) {
7527            const rect = el.getBoundingClientRect();
7528            if (rect.height > 0 && window.getComputedStyle(el).display !== "none") {
7529              topAdVisible = true;
7530              break;
7531            }
7532          }
7533        }
7534        if (!topAdVisible) {
7535          if (hasHtmlMargin && html.style.getPropertyValue("margin-top") !== "0px") html.style.setProperty("margin-top", "0px", "important");
7536          if (hasHtmlPadding && html.style.getPropertyValue("padding-top") !== "0px") html.style.setProperty("padding-top", "0px", "important");
7537          if (hasBodyMargin && body.style.getPropertyValue("margin-top") !== "0px") body.style.setProperty("margin-top", "0px", "important");
7538          if (hasBodyPadding && body.style.getPropertyValue("padding-top") !== "0px") body.style.setProperty("padding-top", "0px", "important");
7539        }
7540      }
7541    };
7542    const observer = new MutationObserver(() => {
7543      observer.disconnect();
7544      checkAndFixGap();
7545      observer.observe(html, { attributes: true, attributeFilter: ["style", "class"] });
7546      observer.observe(body, { attributes: true, attributeFilter: ["style", "class"] });
7547    });
7548    observer.observe(html, { attributes: true, attributeFilter: ["style", "class"] });
7549    observer.observe(body, { attributes: true, attributeFilter: ["style", "class"] });
7550    const intervalId = setInterval(checkAndFixGap, 1e3);
7551    window.addEventListener("message", checkAndFixGap);
7552    window.addEventListener("resize", checkAndFixGap);
7553    checkAndFixGap();
7554    return () => {
7555      observer.disconnect();
7556      clearInterval(intervalId);
7557      window.removeEventListener("message", checkAndFixGap);
7558      window.removeEventListener("resize", checkAndFixGap);
7559    };
7560  }, []);
7561  const [isSessionLoaded, setIsSessionLoaded] = reactExports.useState(true);
7562  const [isAuthModalOpen, setIsAuthModalOpen] = reactExports.useState(false);
7563  const [isUploadModalOpen, setIsUploadModalOpen] = reactExports.useState(false);
7564  const [uploadInitialFile, setUploadInitialFile] = reactExports.useState(null);
7565  const [uploadInitialMetadata, setUploadInitialMetadata] = reactExports.useState(null);
7566  const [isAddToPlaylistModalOpen, setIsAddToPlaylistModalOpen] = reactExports.useState(false);
7567  const [playlistTrack, setPlaylistTrack] = reactExports.useState(null);
7568  const [isNotificationsModalOpen, setIsNotificationsModalOpen] = reactExports.useState(false);
7569  const [currentLocation, setCurrentLocation] = reactExports.useState(() => ({
7570    pathname: typeof window !== "undefined" ? window.location.pathname : "/",
7571    search: typeof window !== "undefined" ? window.location.search : ""
7572  }));
7573  const navigate = React.useCallback((url, options) => {
7574    try {
7575      const cleanUrl = url.startsWith("/") ? url : "/" + url;
7576      const currentUrl = window.location.pathname + window.location.search;
7577      if (cleanUrl === currentUrl && !(options == null ? void 0 : options.replace)) {
7578        return;
7579      }
7580      if (options == null ? void 0 : options.replace) {
7581        window.history.replaceState(null, "", cleanUrl);
7582      } else {
7583        window.history.pushState(null, "", cleanUrl);
7584      }
7585      setCurrentLocation({
7586        pathname: window.location.pathname,
7587        search: window.location.search
7588      });
7589    } catch (e) {
7590      console.warn("History navigation blocked or rate limited:", e);
7591    }
7592  }, []);
7593  const route = React.useMemo(() => parseUrl(currentLocation.pathname, currentLocation.search), [currentLocation]);
7594  const activeView = route.view;
7595  const activeViewId = route.id;
7596  const submittedQuery = route.query;
7597  const activeCategory = route.category;
7598  const setActiveView = React.useCallback((v) => {
7599    let target = v.startsWith("/") ? v : "/" + v;
7600    if (v === "home") target = "/";
7601    navigate(target);
7602  }, [navigate]);
7603  const setActiveCategory = React.useCallback((cat) => {
7604    if (cat === "all") {
7605      navigate("/");
7606    } else {
7607      navigate("/" + cat);
7608    }
7609  }, [navigate]);
7610  const [searchQuery, setSearchQuery] = reactExports.useState(() => {
7611    if (typeof window !== "undefined") {
7612      const params = new URLSearchParams(window.location.search);
7613      return params.get("q") || "";
7614    }
7615    return "";
7616  });
7617  reactExports.useEffect(() => {
7618    setSearchQuery(submittedQuery || "");
7619  }, [submittedQuery]);
7620  const { activeTrack, setActiveTrack, isPlaying, setIsPlaying, queue, setQueue, activePlaylist, setActivePlaylist, playlistLabel, setPlaylistLabel, seekRequest, setSeekRequest, currentTime, setCurrentTime, duration, setDuration, seekTo, audioMode, setAudioMo
7620de, isBuffering, setIsBuffering, setIsLiveSynthFallback, engineOrder } = usePlayer();
7621  const [isSidebarOpen, setIsSidebarOpen] = reactExports.useState(() => {
7622    if (typeof window !== "undefined") {
7623      if (window.innerWidth < 1024) {
7624        return false;
7625      }
7626      const path = window.location.pathname;
7627      return path === "/" || path === "";
7628    }
7629    return true;
7630  });
7631  const [mutedChannels, setMutedChannels] = reactExports.useState([]);
7632  const [likedMidis, setLikedMidis] = reactExports.useState(/* @__PURE__ */ new Set());
7633  const [playbackRate, setPlaybackRate] = reactExports.useState(1);
7634  const [pitchSemitones, setPitchSemitones] = reactExports.useState(0);
7635  const [tracks, setTracks] = reactExports.useState([]);
7636  const contextTracksRef = reactExports.useRef([]);
7637  const [siteModules, setSiteModules] = reactExports.useState([]);
7638  const [labConfig, setLabConfig] = reactExports.useState(null);
7639  const [isLoading, setIsLoading] = reactExports.useState(true);
7640  const [isTracksLoading, setIsTracksLoading] = reactExports.useState(false);
7641  const [lyricsResults, setLyricsResults] = reactExports.useState([]);
7642  const [isLyricsLoading, setIsLyricsLoading] = reactExports.useState(false);
7643  const [isMelodySearchOpen, setIsMelodySearchOpen] = reactExports.useState(false);
7644  const [melodyResults, setMelodyResults] = reactExports.useState(null);
7645  const [isSearchingMelody, setIsSearchingMelody] = reactExports.useState(false);
7646  const [isFullscreen, setIsFullscreen] = reactExports.useState(false);
7647  reactExports.useEffect(() => {
7648    const handleFullscreenChange = () => {
7649      setIsFullscreen(!!document.fullscreenElement);
7650    };
7651    document.addEventListener("fullscreenchange", handleFullscreenChange);
7652    return () => {
7653      document.removeEventListener("fullscreenchange", handleFullscreenChange);
7654    };
7655  }, []);
7656  const [notFoundError, setNotFoundError] = reactExports.useState(null);
7657  const [isGameMode, setIsGameMode] = reactExports.useState(false);
7658  reactExports.useEffect(() => {
7659    if (activeView !== "home") {
7660      setIsSidebarOpen(false);
7661    } else {
7662      if (window.innerWidth < 1024) {
7663        setIsSidebarOpen(false);
7664      } else {
7665        setIsSidebarOpen(true);
7666      }
7667    }
7668  }, [activeView]);
7669  reactExports.useEffect(() => {
7670    if (activeView === "lab") {
7671      document.title = "Hamie Lab | HamieNET";
7672    } else if (activeView === "search") {
7673      if (submittedQuery) {
7674        document.title = `${submittedQuery} - ${t("search.title", "Search")} | HamieNET`;
7675      } else {
7676        document.title = `${t("search.advancedSearch", "Advanced Search")} | HamieNET`;
7677      }
7678    } else {
7679      const tool = DEFAULT_TOOLS.find((t2) => t2.route === activeView);
7680      if (tool) {
7681        document.title = `${tool.name} | HamieNET`;
7682      } else {
7683        const standardTitles = {
7684          "home": "HamieNET - MIDI Archive",
7685          "explore": "Explore | HamieNET",
7686          "history": "History | HamieNET",
7687          "liked": "Liked MIDIs | HamieNET",
7688          "watchLater": "Watch Later | HamieNET",
7689          "subscriptions": "Subscriptions | HamieNET",
7690          "playlists": "Playlists | HamieNET",
7691          "messages": "Messages | HamieNET",
7692          "account": "My Account | HamieNET",
7693          "personal-space": "Personal Space | HamieNET",
7694          "help": "Help Center | HamieNET",
7695          "terms": "Terms of Service | HamieNET",
7696          "privacy": "Privacy Policy | HamieNET",
7697          "billing": "Billing | HamieNET"
7698        };
7699        if (standardTitles[activeView]) {
7700          document.title = standardTitles[activeView];
7701        }
7702      }
7703    }
7704  }, [activeView, submittedQuery, t, i18n.language]);
7705  const [history2, setHistory] = reactExports.useState([]);
7706  const [watchLater, setWatchLater] = reactExports.useState(/* @__PURE__ */ new Set());
7707  const [likedTracks, setLikedTracks] = reactExports.useState([]);
7708  const [watchLaterTracks, setWatchLaterTracks] = reactExports.useState([]);
7709  const [subscriptionTracks, setSubscriptionTracks] = reactExports.useState([]);
7710  const [subscriptions, setSubscriptions] = reactExports.useState(/* @__PURE__ */ new Set());
7711  const selectedTag = route.tag;
7712  const selectedChannel = route.channel;
7713  const [notifications, setNotifications] = reactExports.useState([]);
7714  const [unreadMessageCount, setUnreadMessageCount] = reactExports.useState(0);
7715  const [theme, setTheme] = reactExports.useState(() => {
7716    const saved = localStorage.getItem("hamie_theme");
7717    return saved || "light";
7718  });
7719  reactExports.useEffect(() => {
7720    const checkI18n = async () => {
7721      const getCookie = (name) => {
7722        var _a;
7723        const value = `; ${document.cookie}`;
7724        const parts = value.split(`; ${name}=`);
7725        if (parts.length === 2) return (_a = parts.pop()) == null ? void 0 : _a.split(";").shift();
7726        return null;
7727      };
7728      const savedSettings = localStorage.getItem("hamie_settings");
7729      const hasManualLang = savedSettings && savedSettings.includes('"language"');
7730      const hasLang = getCookie("i18nextLng") || localStorage.getItem("i18nextLng") || hasManualLang;
7731      const hasRegion = getCookie("hamie_region");
7732      if (!hasLang || !hasRegion) {
7733        try {
7734          const { country } = await api.geolocate();
7735          const expiry = /* @__PURE__ */ new Date();
7736          expiry.setFullYear(expiry.getFullYear() + 1);
7737          if (!hasRegion) {
7738            document.cookie = `hamie_region=${country}; expires=${expiry.toUTCString()}; path=/; SameSite=Lax`;
7739          }
7740          if (!hasLang) {
7741            const countryToLang = {
7742              "JP": "ja",
7743              "FR": "fr",
7744              "DE": "de",
7745              "ES": "es",
7746              "IT": "it",
7747              "RU": "ru",
7748              "CN": "zh-CN",
7749              "TW": "zh-TW",
7750              "KR": "ko",
7751              "BR": "pt",
7752              "PT": "pt",
7753              "SA": "ar",
7754              "EG": "ar",
7755              "ID": "id"
7756            };
7757            const targetLang = countryToLang[country] || "en";
7758            __vitePreload(() => import("./index-D0Bmh-UX.js").then((n) => n.i), true ? __vite__mapDeps([4,1,5,6,7,8]) : void 0).then((module) => {
7759              module.default.changeLanguage(targetLang);
7760            });
7761            document.cookie = `i18nextLng=${targetLang}; expires=${expiry.toUTCString()}; path=/; SameSite=Lax`;
7762          }
7763        } catch (e) {
7764          console.error("Smart i18n detection failed", e);
7765        }
7766      }
7767    };
7768    checkI18n();
7769  }, []);
7770  const [autoPlayCountdown, setAutoPlayCountdown] = reactExports.useState(null);
7771  const countdownIntervalRef = reactExports.useRef(null);
7772  const lastLoadingRef = reactExports.useRef(false);
7773  const loadStartRef = reactExports.useRef(0);
7774  const activeTrackIdRef = reactExports.useRef(null);
7775  const isPlayingRef = reactExports.useRef(isPlaying);
7776  reactExports.useEffect(() => {
7777    isPlayingRef.current = isPlaying;
7778  }, [isPlaying]);
7779  reactExports.useEffect(() => {
7780    activeTrackIdRef.current = activeTrack ? activeTrack.id : null;
7781  }, [activeTrack]);
7782  reactExports.useEffect(() => {
7783    if (activeTrack) {
7784      if (isPlayingRef.current && activeTrackIdRef.current === activeTrack.id) {
7785        return;
7786      }
7787      const hasWebMidiSupport = typeof navigator !== "undefined" && !!navigator.requestMIDIAccess;
7788      const midiOutputs = synth.getMidiOutputs() || [];
7789      const preferred = typeof window !== "undefined" ? localStorage.getItem("preferredAudioMode") : null;
7790      const best = getBestAudioMode(activeTrack, isSlowMobile(), hasWebMidiSupport, midiOutputs.length > 0, 0, activeView === "watch", engineOrder, preferred);
7791      console.log("[App.tsx] 🔊 Sync Effect running, setting mode to", best.mode, "activeView:", activeView, "isWatchPage:", activeView === "watch", "hasStems:", !!(activeTrack.instruments && activeTrack.instruments.some((i) => i.stemPath)), "hasMasterAudio:", !!(activeTrack.status === "completed" && (activeTrack.opusPath || activeTrack.mp3PathHigh || activeTrack.mp3Path)));
7792      setAudioMode(best.mode);
7793      setIsLiveSynthFallback(best.isLiveSynthFallback);
7794    }
7795  }, [activeTrack == null ? void 0 : activeTrack.id, activeTrack == null ? void 0 : activeTrack.status, activeTrack == null ? void 0 : activeTrack.opusPath, activeTrack == null ? void 0 : activeTrack.mp3PathHigh, setAudioMode, setIsLiveSynthFallback, activeView, engineOrder]);
7796  const parseTimeParam = (tStr) => {
7797    if (!tStr) return null;
7798    if (tStr.includes(":")) {
7799      const parts = tStr.split(":");
7800      if (parts.length === 2) {
7801        const m = parseInt(parts[0], 10);
7802        const s = parseInt(parts[1], 10);
7803        if (!isNaN(m) && !isNaN(s)) {
7804          return m * 60 + s;
7805        }
7806      }
7807    }
7808    const mMatch = tStr.match(/(\d+)m/);
7809    let seconds = 0;
7810    if (mMatch) {
7811      seconds += parseInt(mMatch[1], 10) * 60;
7812      const cleanStr = tStr.replace(mMatch[0], "");
7813      const sOnlyMatch = cleanStr.match(/(\d+)/);
7814      if (sOnlyMatch) {
7815        seconds += parseInt(sOnlyMatch[1], 10);
7816      }
7817      return seconds;
7818    }
7819    const parsed = parseInt(tStr, 10);
7820    if (!isNaN(parsed)) return parsed;
7821    return null;
7822  };
7823  const handleSelectTrack = React.useCallback((track, forcePlay, playlist, keepView) => {
7824    var _a;
7825    const isAlreadyActive = track && activeTrack && track.id === activeTrack.id;
7826    if (!isAlreadyActive) {
7827      setAutoPlayCountdown(null);
7828      setSeekRequest(null);
7829      if (countdownIntervalRef.current) clearInterval(countdownIntervalRef.current);
7830      setCurrentTime(0);
7831    }
7832    if (track && track.id) {
7833      if (playlist !== void 0) {
7834        setActivePlaylist(playlist);
7835      } else if (!isAlreadyActive) {
7836        setActivePlaylist(null);
7837        contextTracksRef.current = tracks;
7838      }
7839      setHistory((prev) => [track, ...prev.filter((t2) => t2.id !== track.id)].slice(0, 100));
7840      setActiveTrack(track);
7841      const metaDur = Number((_a = track.midiMetadata) == null ? void 0 : _a.duration);
7842      const strDur = parseTimeParam(track.duration);
7843      const initialDur = !isNaN(metaDur) && metaDur > 0 ? metaDur : strDur && strDur > 0 ? strDur : 0;
7844      if (initialDur > 0) {
7845        setDuration(initialDur);
7846      }
7847      if (!isAlreadyActive || forcePlay !== void 0) {
7848        setIsPlaying(forcePlay !== void 0 ? forcePlay : autoplay);
7849      }
7850      if (!isAlreadyActive || !isPlaying) {
7851        const hasWebMidiSupport = typeof navigator !== "undefined" && !!navigator.requestMIDIAccess;
7852        const midiOutputs = synth.getMidiOutputs() || [];
7853        const isWatchPage = !keepView || activeView === "watch";
7854        const preferred = typeof window !== "undefined" ? localStorage.getItem("preferredAudioMode") : null;
7855        const best = getBestAudioMode(track, isSlowMobile(), hasWebMidiSupport, midiOutputs.length > 0, 0, isWatchPage, engineOrder, preferred);
7856        setAudioMode(best.mode);
7857        setIsLiveSynthFallback(best.isLiveSynthFallback);
7858      }
7859      if (!isAlreadyActive) {
7860        setPitchSemitones(0);
7861      }
7862      const searchParams = typeof window !== "undefined" ? window.location.search : "";
7863      if (!keepView) {
7864        navigate(generateTrackUrl(track) + searchParams);
7865      } else if (activeView === "watch") {
7866        navigate(generateTrackUrl(track) + searchParams, { replace: true });
7867      }
7868      if (!isAlreadyActive && typeof window !== "undefined") {
7869        const params = new URLSearchParams(window.location.search);
7870        const tVal = params.get("t") || params.get("time");
7871        if (tVal) {
7872          const parsedSecs = parseTimeParam(tVal);
7873          if (parsedSecs !== null && parsedSecs > 0) {
7874            setTimeout(() => {
7875              seekTo(parsedSecs);
7876            }, 150);
7877          }
7878        }
7879      }
7880      if (currentUser) {
7881        api.recordHistory(track.id, currentUser.id);
7882      }
7883    } else {
7884      setActiveTrack(null);
7885      setIsPlaying(false);
7886      navigate("/");
7887    }
7888  }, [autoplay, currentUser, setActiveTrack, setIsPlaying, setCurrentTime, setSeekRequest, setQueue, seekTo, setActivePlaylist, activeView, navigate]);
7889  const handleSelectTrackRef = reactExports.useRef(handleSelectTrack);
7890  reactExports.useEffect(() => {
7891    handleSelectTrackRef.current = handleSelectTrack;
7892  }, [handleSelectTrack]);
7893  reactExports.useEffect(() => {
7894    if (activeView === "watch" && activeViewId) {
7895      const isMismatch = !activeTrack || String(activeTrack.id) !== String(activeViewId);
7896      const isLight = !!(activeTrack && (activeTrack.isLight || activeTrack.pianoRollPath === void 0 || activeTrack.status === "completed" && activeTrack.pianoRollPath === null && activeTrack.scorePath === null));
7897      if (isMismatch || isLight) {
7898        if (isMismatch) setIsLoading(true);
7899        api.getTrack(activeViewId).then((t2) => {
7900          if (t2) {
7901            handleSelectTrackRef.current(t2, isMismatch ? false : void 0, null, true);
7902          } else if (isMismatch) {
7903            setActiveView("home");
7904          }
7905        }).catch(() => {
7906          if (isMismatch) setActiveView("home");
7907        }).finally(() => {
7908          if (isMismatch) setIsLoading(false);
7909        });
7910      }
7911    }
7912  }, [activeView, activeViewId, activeTrack]);
7913  reactExports.useEffect(() => {
7914    const handlePop = () => {
7915      setCurrentLocation({
7916        pathname: window.location.pathname,
7917        search: window.location.search
7918      });
7919    };
7920    window.addEventListener("popstate", handlePop);
7921    return () => window.removeEventListener("popstate", handlePop);
7922  }, []);
7923  reactExports.useEffect(() => {
7924    if (typeof window !== "undefined") {
7925      const queryParams = new URLSearchParams(currentLocation.search);
7926      const urlKey = queryParams.get("key");
7927      if (urlKey) {
7928        localStorage.setItem("digitizer_test_key", urlKey);
7929      }
7930      if (queryParams.get("login") === "true" || queryParams.get("auth") === "true") {
7931        setIsAuthModalOpen(true);
7932      }
7933    }
7934  }, [currentLocation.search]);
7935  reactExports.useEffect(() => {
7936    const path = currentLocation.pathname;
7937    const scoreMatch = path.match(/^\/score(b)?(\d+)-(\d+)(?:-(\d+))?\.html$/);
7938    if (scoreMatch) {
7939      const queryParams = new URLSearchParams(currentLocation.search);
7940      let changed = false;
7941      if (queryParams.get("mode") !== "score") {
7942        queryParams.set("mode", "score");
7943        changed = true;
7944      }
7945      if (queryParams.get("track") !== scoreMatch[3]) {
7946        queryParams.set("track", scoreMatch[3]);
7947        changed = true;
7948      }
7949      if (scoreMatch[4] && queryParams.get("page") !== scoreMatch[4]) {
7950        queryParams.set("page", scoreMatch[4]);
7951        changed = true;
7952      }
7953      if (changed) {
7954        window.history.replaceState(null, "", `${path}?${queryParams.toString()}`);
7955      }
7956    }
7957  }, [currentLocation]);
7958  reactExports.useEffect(() => {
7959    const path = currentLocation.pathname;
7960    if (path.startsWith("/collection-section/")) {
7961      const id = path.split("/").pop() || "";
7962      if (id.includes(":")) {
7963        const [slug, secIdWithSlug] = id.split(":");
7964        navigate(`/collections/${slug}/${secIdWithSlug}`, { replace: true });
7965      }
7966    }
7967  }, [currentLocation, navigate]);
7968  const handleNavigate = React.useCallback((v, id) => {
7969    const cleanedV = v.replace(/^\/+/, "").replace(/\?+/g, "?");
7970    const [viewName, ...queryParts] = cleanedV.split("?");
7971    const queryStr = queryParts.join("&");
7972    let targetUrl = "";
7973    if (viewName === "watch" && id) {
7974      targetUrl = `/watch?v=${id}`;
7975    } else if (viewName === "collection-section" && id && id.includes(":")) {
7976      const [slug, secIdWithSlug] = id.split(":");
7977      targetUrl = `/collections/${slug}/${secIdWithSlug}`;
7978    } else {
7979      const actualView = viewName === "collection" ? "collections" : viewName;
7980      targetUrl = id ? `/${actualView}/${id}` : `/${actualView}`;
7981    }
7982    if (queryStr) {
7983      targetUrl += (targetUrl.includes("?") ? "&" : "?") + queryStr;
7984    }
7985    navigate(targetUrl);
7986  }, [navigate]);
7987  reactExports.useEffect(() => {
7988    const handleGlobalCaptureClick = (e) => {
7989      const target = e.target;
7990      const anchor = target.closest("a");
7991      if (anchor && anchor.getAttribute("href")) {
7992        if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) {
7993          e.stopPropagation();
7994        }
7995      }
7996    };
7997    const handleGlobalLinkClick = (e) => {
7998      const target = e.target;
7999      const anchor = target.closest("a");
8000      if (!anchor) return;
8001      const href = anchor.getAttribute("href");
8002      const targetAttr = anchor.getAttribute("target");
8003      if (!href || targetAttr === "_blank") return;
8004      if (e.defaultPrevented) return;
8005      let isExternal = false;
8006      if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) {
8007        try {
8008          const url = new URL(href, window.location.origin);
8009          if (url.origin !== window.location.origin) {
8010            isExternal = true;
8011          }
8012        } catch {
8013          isExternal = true;
8014        }
8015      }
8016      if (isExternal) return;
8017      if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1 || e.button === 2) return;
8018      e.preventDefault();
8019      let internalPath = href;
8020      try {
8021        const url = new URL(href, window.location.origin);
8022        internalPath = url.pathname + url.search + url.hash;
8023      } catch (err) {
8024      }
8025      navigate(internalPath);
8026    };
8027    document.addEventListener("click", handleGlobalCaptureClick, { capture: true });
8028    document.addEventListener("auxclick", handleGlobalCaptureClick, { capture: true });
8029    document.addEventListener("click", handleGlobalLinkClick);
8030    return () => {
8031      document.removeEventListener("click", handleGlobalCaptureClick, { capture: true });
8032      document.removeEventListener("auxclick", handleGlobalCaptureClick, { capture: true });
8033      document.removeEventListener("click", handleGlobalLinkClick);
8034    };
8035  }, [handleNavigate]);
8036  reactExports.useEffect(() => {
8037    if (currentUser) {
8038      api.getNotifications(currentUser.id).then(setNotifications);
8039      api.getSubscriptions(currentUser.id).then((subs) => setSubscriptions(new Set(subs)));
8040      api.getUserLikes(currentUser.id).then((ids) => setLikedMidis(new Set(ids)));
8041      api.getUserWatchLater(currentUser.id).then((ids) => setWatchLater(new Set(ids)));
8042      api.getUserHistory(currentUser.id).then(setHistory);
8043    }
8044  }, [currentUser == null ? void 0 : currentUser.id]);
8045  reactExports.useEffect(() => {
8046    if (!currentUser) return;
8047    const interval = setInterval(() => {
8048      api.getNotifications(currentUser.id).then(setNotifications).catch(() => {
8049      });
8050    }, 6e4);
8051    return () => clearInterval(interval);
8052  }, [currentUser == null ? void 0 : currentUser.id]);
8053  reactExports.useEffect(() => {
8054    const fetchUnreadMessages = async () => {
8055      if (currentUser && (currentUser.role === "ADMIN" || currentUser.canMessage)) {
8056        try {
8057          const total = await api.getUnreadMessageCount(currentUser.id);
8058          setUnreadMessageCount(total);
8059        } catch {
8060        }
8061      } else {
8062        setUnreadMessageCount(0);
8063      }
8064    };
8065    fetchUnreadMessages();
8066    const interval = setInterval(fetchUnreadMessages, 6e4);
8067    const handleMessageUpdate = () => fetchUnreadMessages();
8068    window.addEventListener("messages-update", handleMessageUpdate);
8069    return () => {
8070      clearInterval(interval);
8071      window.removeEventListener("messages-update", handleMessageUpdate);
8072    };
8073  }, [currentUser == null ? void 0 : currentUser.id]);
8074  reactExports.useEffect(() => {
8075    const handleRequestUpload = (e) => {
8076      if (e.detail) {
8077        handleUploadClick(e.detail);
8078      }
8079    };
8080    window.addEventListener("hamienet:request_upload", handleRequestUpload);
8081    return () => window.removeEventListener("hamienet:request_upload", handleRequestUpload);
8082  }, []);
8083  reactExports.useEffect(() => {
8084    if (activeView === "admin") return;
8085    const fetchContent = async () => {
8086      const isCategoryChange = activeView === "home" && !isLoading;
8087      if (isCategoryChange) {
8088        setIsTracksLoading(true);
8089      } else {
8090        setIsLoading(true);
8091      }
8092      try {
8093        const isExplore = activeView === "explore";
8094        const isTrending = activeCategory === "trending";
8095        const isRecommended = activeCategory === "recommended";
8096        let tracksData;
8097        if (isRecommended) {
8098          tracksData = await api.getRecommendations(currentUser == null ? void 0 : currentUser.id);
8099        } else {
8100          const isHomeDefault = activeView === "home" && activeCategory === "all" && !submittedQuery;
8101          const isMine = activeView === "account";
8102          tracksData = await api.getTracks(
8103            submittedQuery,
8104            activeCategory,
8105            isHomeDefault ? "random" : isTrending ? "popular" : "recent",
8106            isExplore ? 500 : 100,
8107            void 0,
8108            isMine,
8109            isMine ? currentUser == null ? void 0 : currentUser.username : void 0
8110          );
8111        }
8112        if (submittedQuery) {
8113          setIsLyricsLoading(true);
8114          api.searchLyrics(submittedQuery, 1, 30).then((res) => setLyricsResults(res.results)).catch((err) => console.error("[Unified Lyrics Search Error]", err)).finally(() => setIsLyricsLoading(false));
8115        } else {
8116          setLyricsResults([]);
8117        }
8118        const [modulesData, labConfigData] = await Promise.all([
8119          api.getSitePages().catch(() => []),
8120          api.getLabConfig().catch(() => null)
8121        ]);
8122        setTracks(tracksData);
8123        setSiteModules(modulesData);
8124        setLabConfig(labConfigData);
8125      } catch (e) {
8126        console.error("Failed to load content", e);
8127      } finally {
8128        setIsLoading(false);
8129        setIsTracksLoading(false);
8130      }
8131    };
8132    fetchContent();
8133  }, [submittedQuery, activeCategory, activeView, currentUser == null ? void 0 : currentUser.id]);
8134  reactExports.useEffect(() => {
8135    const isNowLoading = isLoading || isTracksLoading;
8136    const wasLoading = lastLoadingRef.current;
8137    if (isNowLoading && !wasLoading) {
8138      loadStartRef.current = performance.now();
8139    } else if (!isNowLoading && wasLoading) {
8140      const duration2 = performance.now() - loadStartRef.current;
8141      if (duration2 > 50) {
8142        uxTracker.logEvent("page_load", {
8143          url: window.location.pathname + window.location.search,
8144          duration: duration2,
8145          metadata: {
8146            view: activeView,
8147            category: activeCategory
8148          }
8149        });
8150      }
8151    }
8152    lastLoadingRef.current = isNowLoading;
8153  }, [isLoading, isTracksLoading, activeView, activeCategory]);
8154  reactExports.useEffect(() => {
8155    if (theme === "dark") {
8156      document.documentElement.classList.add("dark");
8157    } else {
8158      document.documentElement.classList.remove("dark");
8159    }
8160    document.body.classList.remove("fun-mode");
8161    document.body.classList.remove("retro-mode");
8162    if (theme === "fun") {
8163      document.body.classList.add("fun-mode");
8164    } else if (theme === "retro") {
8165      document.body.classList.add("retro-mode");
8166    }
8167  }, [theme]);
8168  reactExports.useEffect(() => {
8169    if (!currentUser) return;
8170    if (activeView === "liked") {
8171      api.getLikedTracks(currentUser.id).then(setLikedTracks);
8172    } else if (activeView === "watchLater") {
8173      api.getWatchLaterTracks(currentUser.id).then(setWatchLaterTracks);
8174    } else if (activeView === "history") {
8175      api.getUserHistory(currentUser.id).then(setHistory);
8176    } else if (activeView === "subscriptions") {
8177      api.getSubscriptionTracks(currentUser.id).then(setSubscriptionTracks);
8178    }
8179  }, [activeView, currentUser]);
8180  const toggleTheme = (targetTheme) => {
8181    setTheme((prev) => {
8182      const next = targetTheme || (prev === "light" ? "dark" : prev === "dark" ? "fun" : prev === "fun" ? "retro" : "light");
8183      localStorage.setItem("hamie_theme", next);
8184      return next;
8185    });
8186  };
8187  const handleUpdateUser = async (updates) => {
8188    if (!currentUser) return;
8189    try {
8190      const updatedUser = await api.updateUser(currentUser.id, updates);
8191      setCurrentUser(updatedUser);
8192      localStorage.setItem("hamie_session", JSON.stringify(updatedUser));
8193      return updatedUser;
8194    } catch (e) {
8195      console.error("Failed to update user:", e);
8196      throw e;
8197    }
8198  };
8199  const handleSearch = (e) => {
8200    if (e) e.preventDefault();
8201    handleNavigate("search?q=" + encodeURIComponent(searchQuery));
8202    setMelodyResults(null);
8203    window.scrollTo(0, 0);
8204  };
8205  const handleMelodySearch = async (notes) => {
8206    setIsSearchingMelody(true);
8207    try {
8208      const results = await api.searchMelody(notes);
8209      setMelodyResults(results);
8210      setIsMelodySearchOpen(false);
8211      setActiveView("home");
8212    } catch (e) {
8213      console.error(e);
8214    } finally {
8215      setIsSearchingMelody(false);
8216    }
8217  };
8218  const toggleMute = (instr) => {
8219    setMutedChannels((prev) => prev.includes(instr) ? prev.filter((i) => i !== instr) : [...prev, instr]);
8220  };
8221  const toggleLike = (id, e) => {
8222    if (e) e.stopPropagation();
8223    if (!currentUser) {
8224      setIsAuthModalOpen(true);
8225      return;
8226    }
8227    setLikedMidis((prev) => {
8228      const next = new Set(prev);
8229      if (next.has(id)) next.delete(id);
8230      else next.add(id);
8231      return next;
8232    });
8233    api.toggleLikeTrack(id, currentUser.id);
8234  };
8235  const toggleWatchLater = (trackId, e) => {
8236    e.stopPropagation();
8237    if (!currentUser) {
8238      setIsAuthModalOpen(true);
8239      return;
8240    }
8241    setWatchLater((prev) => {
8242      const next = new Set(prev);
8243      if (next.has(trackId)) next.delete(trackId);
8244      else next.add(trackId);
8245      return next;
8246    });
8247    api.toggleWatchLaterTrack(trackId, currentUser.id);
8248  };
8249  const toggleSubscription = async (artist, artistId) => {
8250    if (!currentUser) {
8251      setIsAuthModalOpen(true);
8252      return;
8253    }
8254    setSubscriptions((prev) => {
8255      const next = new Set(prev);
8256      if (next.has(artist)) next.delete(artist);
8257      else next.add(artist);
8258      return next;
8259    });
8260    await api.toggleSubscription(currentUser.id, artistId);
8261  };
8262  const handleAddToQueue = (track) => {
8263    setQueue((prev) => [...prev, track]);
8264  };
8265  const handleRemoveFromQueue = (index) => {
8266    setQueue((prev) => prev.filter((_, i) => i !== index));
8267  };
8268  const handleNextTrack = (isManual) => {
8269    const nextPlayState = isManual ? true : isPlaying;
8270    if (queue.length > 0) {
8271      const next = queue[0];
8272      setQueue((prev) => prev.slice(1));
8273      handleSelectTrack(next, nextPlayState, void 0, true);
8274      return;
8275    }
8276    if (activePlaylist && activePlaylist.length > 0 && activeTrack) {
8277      const idx = activePlaylist.findIndex(
8278        (t2) => t2._playlistItemId && activeTrack._playlistItemId ? t2._playlistItemId === activeTrack._playlistItemId : String(t2.id) === String(activeTrack.id)
8279      );
8280      if (idx !== -1) {
8281        const next = activePlaylist[(idx + 1) % activePlaylist.length];
8282        handleSelectTrack(next, nextPlayState, activePlaylist, true);
8283        return;
8284      }
8285    }
8286    const currentFeedTracks = contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks;
8287    if (!activeTrack || currentFeedTracks.length === 0) return;
8288    if (isManual) {
8289      const idx = currentFeedTracks.findIndex((t2) => String(t2.id) === String(activeTrack.id));
8290      if (idx !== -1) {
8291        const next = currentFeedTracks[(idx + 1) % currentFeedTracks.length];
8292        handleSelectTrack(next, true, void 0, true);
8293      }
8294      return;
8295    }
8296    if (autoplay) {
8297      if (autoPlayCountdown !== null) return;
8298      setAutoPlayCountdown(10);
8299      setIsPlaying(false);
8300      countdownIntervalRef.current = setInterval(() => {
8301        setAutoPlayCountdown((prev) => {
8302          if (prev !== null && prev <= 1) {
8303            clearInterval(countdownIntervalRef.current);
8304            const idx = currentFeedTracks.findIndex((t2) => String(t2.id) === String(activeTrack.id));
8305            if (idx !== -1) {
8306              const next = currentFeedTracks[(idx + 1) % currentFeedTracks.length];
8307              handleSelectTrack(next, true, void 0, true);
8308            }
8309            return null;
8310          }
8311          return prev !== null ? prev - 1 : null;
8312        });
8313      }, 1e3);
8314      return;
8315    }
8316    setIsPlaying(false);
8317  };
8318  const handleCancelAutoPlay = () => {
8319    if (countdownIntervalRef.current) clearInterval(countdownIntervalRef.current);
8320    setAutoPlayCountdown(null);
8321  };
8322  const handlePlayNextImmediate = () => {
8323    if (countdownIntervalRef.current) clearInterval(countdownIntervalRef.current);
8324    if (activePlaylist && activePlaylist.length > 0 && activeTrack) {
8325      const idx2 = activePlaylist.findIndex(
8326        (t2) => t2._playlistItemId && activeTrack._playlistItemId ? t2._playlistItemId === activeTrack._playlistItemId : String(t2.id) === String(activeTrack.id)
8327      );
8328      if (idx2 !== -1) {
8329        const next = activePlaylist[(idx2 + 1) % activePlaylist.length];
8330        handleSelectTrack(next, true, activePlaylist, true);
8331        return;
8332      }
8333    }
8334    const currentFeedTracks = contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks;
8335    const idx = currentFeedTracks.findIndex((t2) => String(t2.id) === String(activeTrack == null ? void 0 : activeTrack.id));
8336    if (idx !== -1) {
8337      const next = currentFeedTracks[(idx + 1) % currentFeedTracks.length];
8338      handleSelectTrack(next, true, void 0, true);
8339    }
8340  };
8341  const handlePrevTrack = () => {
8342    const nextPlayState = isPlaying;
8343    if (activePlaylist && activePlaylist.length > 0 && activeTrack) {
8344      const idx2 = activePlaylist.findIndex(
8345        (t2) => t2._playlistItemId && activeTrack._playlistItemId ? t2._playlistItemId === activeTrack._playlistItemId : String(t2.id) === String(activeTrack.id)
8346      );
8347      if (idx2 !== -1) {
8348        const prev = activePlaylist[(idx2 - 1 + activePlaylist.length) % activePlaylist.length];
8349        handleSelectTrack(prev, nextPlayState, activePlaylist, true);
8350        return;
8351      }
8352    }
8353    const currentFeedTracks = contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks;
8354    if (!activeTrack || currentFeedTracks.length === 0) return;
8355    const idx = currentFeedTracks.findIndex((t2) => String(t2.id) === String(activeTrack.id));
8356    if (idx !== -1) {
8357      const prev = currentFeedTracks[(idx - 1 + currentFeedTracks.length) % currentFeedTracks.length];
8358      handleSelectTrack(prev, nextPlayState, void 0, true);
8359    }
8360  };
8361  const handleGoHome = () => {
8362    setSearchQuery("");
8363    setMelodyResults(null);
8364    setSeekRequest(null);
8365    navigate("/");
8366  };
8367  const handleLogout = () => {
8368    localStorage.removeItem("hamie_session");
8369    window.location.href = "/";
8370  };
8371  const handleChannelSelect = (artist) => {
8372    navigate(`/channel?c=${encodeURIComponent(artist)}`);
8373  };
8374  const handleTagSelect = (tag) => {
8375    navigate(`/tag?name=${encodeURIComponent(tag)}`);
8376  };
8377  const handleArtistSelect = (artist) => {
8378    navigate(`/?q=${encodeURIComponent(artist)}`);
8379  };
8380  const handleCategorySelect = (category) => {
8381    navigate("/" + category);
8382  };
8383  const handleMarkNotificationRead = async (id) => {
8384    await api.markNotificationRead(id);
8385    setNotifications((prev) => prev.map((n) => n.id === id ? { ...n, isRead: true } : n));
8386  };
8387  const handleMarkAllNotificationsRead = async () => {
8388    await api.markAllNotificationsRead();
8389    setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true })));
8390  };
8391  const handleNotificationNavigate = (link) => {
8392    try {
8393      const url = new URL(link, window.location.origin);
8394      const trackId = url.searchParams.get("id") || url.searchParams.get("trackId");
8395      const search = url.search;
8396      const view = url.pathname.replace(/^\/+/, "") || "home";
8397      if (trackId) {
8398        handleNavigate(view + search, trackId);
8399      } else {
8400        handleNavigate(view + search);
8401      }
8402    } catch {
8403      window.location.href = link;
8404    }
8405  };
8406  const handleUploadClick = (fileOrMetadata, maybeMetadata) => {
8407    if (fileOrMetadata && fileOrMetadata instanceof File) {
8408      setUploadInitialFile(fileOrMetadata);
8409      setUploadInitialMetadata(maybeMetadata || null);
8410    } else if (fileOrMetadata && typeof fileOrMetadata === "object") {
8411      setUploadInitialFile(null);
8412      setUploadInitialMetadata(fileOrMetadata);
8413    } else {
8414      setUploadInitialFile(null);
8415      setUploadInitialMetadata(maybeMetadata || null);
8416    }
8417    setIsUploadModalOpen(true);
8418  };
8419  const handleAddToPlaylistClick = (track) => {
8420    if (!currentUser) {
8421      setIsAuthModalOpen(true);
8422    } else {
8423      if (track) setPlaylistTrack(track);
8424      else if (activeTrack) setPlaylistTrack(activeTrack);
8425      setIsAddToPlaylistModalOpen(true);
8426    }
8427  };
8428  if (activeView === "admin") {
8429    if (!isSessionLoaded) return /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {});
8430    if (!currentUser || currentUser.role !== "ADMIN") {
8431      return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-screen items-center justify-center bg-black text-white p-10 text-center animate-in fade-in", children: [
8432        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-20 h-20 bg-red-600/20 rounded-full flex items-center justify-center mb-6 animate-pulse", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 bg-red-600 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-2xl font-black", children: "!" }) }) }),
8433        /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-4xl font-black mb-4 tracking-tighter uppercase", children: "Clearance Required" }),
8434        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 mb-8 max-w-md leading-relaxed text-sm", children: "You have entered a restricted area. Access to the command center is limited to authenticated system administrators. Your attempt has been logged for audit." }),
8435        /* @__PURE__ */ jsxRuntimeExports.jsx(
8436          "button",
8437          {
8438            onClick: handleGoHome,
8439            className: "px-8 py-3 bg-white text-black rounded-full font-bold hover:bg-zinc-200 transition-all transform hover:scale-105",
8440            children: "Return to Safety"
8441          }
8442        )
8443      ] });
8444    }
8445    return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdminApp, { onExit: handleGoHome }) });
8446  }
8447  const NotFoundView = ({ error, onBack }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[60vh] text-center p-6 animate-in fade-in zoom-in-95", children: [
8448    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-20 h-20 bg-red-100 dark:bg-red-900/30 rounded-full flex items-center justify-center text-red-500 mb-6 shadow-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsx(OctagonAlert, { size: 40 }) }),
8449    /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-3xl font-bold text-zinc-900 dark:text-white mb-3", children: "MIDI Not Found" }),
8450    /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 max-w-md mb-8", children: error || "We couldn't find the MIDI you were looking for. It may have been deleted, moved, or is temporarily unavailable." }),
8451    /* @__PURE__ */ jsxRuntimeExports.jsx(
8452      "button",
8453      {
8454        onClick: onBack,
8455        className: "px-8 py-3 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold shadow-lg shadow-indigo-500/30 transition-all flex items-center gap-2 active:scale-95",
8456        children: "Back to Home"
8457      }
8458    )
8459  ] });
8460  const AccessDeniedView = ({ title, message, onBack }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[60vh] text-center p-6 animate-in fade-in zoom-in-95", children: [
8461    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-20 h-20 bg-amber-100 dark:bg-amber-900/30 rounded-full flex items-center justify-center text-amber-500 mb-6 shadow-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsx(OctagonAlert, { size: 40 }) }),
8462    /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-3xl font-bold text-zinc-900 dark:text-white mb-3", children: title }),
8463    /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 max-w-md mb-8", children: message }),
8464    /* @__PURE__ */ jsxRuntimeExports.jsx(
8465      "button",
8466      {
8467        onClick: onBack,
8468        className: "px-8 py-3 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold shadow-lg shadow-indigo-500/30 transition-all flex items-center gap-2 active:scale-95",
8469        children: "Back to Home"
8470      }
8471    )
8472  ] });
8473  const isGoogleAGAgent = typeof navigator !== "undefined" && (navigator.userAgent.includes("Antigravity") || navigator.userAgent.includes("Google AG") || navigator.userAgent.includes("HeadlessChrome") || !!navigator.webdriver || localStorage.getItem("ag_agent") === "true" || new URLSearchParams(window.location.search).get("agent") === "true");
8474  const isAdmin = (currentUser == null ? void 0 : currentUser.role) === "ADMIN" || isGoogleAGAgent;
8475  const COMPONENT_REGISTRY = {
8476    "home": /* @__PURE__ */ jsxRuntimeExports.jsx(
8477      HomePage,
8478      {
8479        currentUser,
8480        onNavigate: handleNavigate,
8481        activeCategory,
8482        setActiveCategory,
8483        tracks,
8484        onSelectTrack: handleSelectTrack,
8485        onToggleWatchLater: toggleWatchLater,
8486        watchLaterSet: watchLater,
8487        melodyResults,
8488        onClearMelodyResults: () => setMelodyResults(null),
8489        onAddToQueue: handleAddToQueue,
8490        onAddToPlaylist: handleAddToPlaylistClick,
8491        submittedQuery,
8492        isTracksLoading,
8493        lyricsResults,
8494        isLyricsLoading,
8495        activeView,
8496        setActiveView,
8497        onClearSearch: () => {
8498          setSearchQuery("");
8499          navigate("/");
8500        },
8501        onChannelSelect: handleChannelSelect
8502      }
8503    ),
8504    "explore": /* @__PURE__ */ jsxRuntimeExports.jsx(
8505      ExplorePage,
8506      {
8507        tracks,
8508        onSelectTrack: handleSelectTrack,
8509        onToggleWatchLater: toggleWatchLater,
8510        watchLaterSet: watchLater,
8511        onCategorySelect: handleCategorySelect,
8512        onAddToQueue: handleAddToQueue,
8513        onAddToPlaylist: handleAddToPlaylistClick,
8514        onTagSelect: handleTagSelect
8515      }
8516    ),
8517    "history": /* @__PURE__ */ jsxRuntimeExports.jsx(HistoryPage, { history: history2, onSelectTrack: handleSelectTrack, onToggleWatchLater: toggleWatchLater, watchLaterSet: watchLater }),
8518    "melody-search": /* @__PURE__ */ jsxRuntimeExports.jsx(
8519      MelodySearchPage,
8520      {
8521        onSelectTrack: handleSelectTrack,
8522        onToggleWatchLater: toggleWatchLater,
8523        watchLaterSet: watchLater,
8524        onAddToQueue: handleAddToQueue,
8525        onAddToPlaylist: handleAddToPlaylistClick
8526      }
8527    ),
8528    "newly-uploaded": /* @__PURE__ */ jsxRuntimeExports.jsx(
8529      NewlyUploadedPage,
8530      {
8531        onSelectTrack: handleSelectTrack,
8532        onToggleWatchLater: toggleWatchLater,
8533        watchLaterSet: watchLater,
8534        onAddToQueue: handleAddToQueue,
8535        onAddToPlaylist: handleAddToPlaylistClick
8536      }
8537    ),
8538    "liked": /* @__PURE__ */ jsxRuntimeExports.jsx(LikedPage, { tracks: likedTracks, onSelectTrack: handleSelectTrack, onToggleWatchLater: toggleWatchLater, watchLaterSet: watchLater }),
8539    "watchLater": /* @__PURE__ */ jsxRuntimeExports.jsx(WatchLaterPage, { tracks: watchLaterTracks, onSelectTrack: handleSelectTrack, onToggleWatchLater: toggleWatchLater, watchLaterSet: watchLater }),
8540    "subscriptions": /* @__PURE__ */ jsxRuntimeExports.jsx(SubscriptionsPage, { tracks: subscriptionTracks, onSelectTrack: handleSelectTrack, onToggleWatchLater: toggleWatchLater, watchLaterSet: watchLater }),
8541    "channel": /* @__PURE__ */ jsxRuntimeExports.jsx(
8542      ChannelPage,
8543      {
8544        channelName: selectedChannel,
8545        onSelectTrack: handleSelectTrack,
8546        onToggleWatchLater: toggleWatchLater,
8547        watchLaterSet: watchLater,
8548        currentUser,
8549        onAuthRequested: () => setIsAuthModalOpen(true),
8550        onAddToQueue: handleAddToQueue,
8551        onAddToPlaylist: handleAddToPlaylistClick
8552      }
8553    ),
8554    "tag": /* @__PURE__ */ jsxRuntimeExports.jsx(TagPage, { tagName: selectedTag, tracks: tracks.filter((t2) => t2.tags.includes(selectedTag || "") || t2.artist === selectedTag || t2.title.includes(selectedTag || "")), onSelectTrack: handleSelectTrack, onToggleWatchLater: toggleWatchLater, watchLaterSet: watchLater }),
8555    "collections": /* @__PURE__ */ jsxRuntimeExports.jsx(
8556      CollectionIndexPage,
8557      {
8558        onNavigate: handleNavigate
8559      }
8560    ),
8561    "collection": /* @__PURE__ */ jsxRuntimeExports.jsx(
8562      CollectionPage,
8563      {
8564        slug: activeViewId,
8565        currentUser,
8566        onSelectTrack: handleSelectTrack,
8567        onToggleWatchLater: toggleWatchLater,
8568        watchLaterSet: watchLater,
8569        onAddToQueue: handleAddToQueue,
8570        onAddToPlaylist: handleAddToPlaylistClick,
8571        onNavigate: handleNavigate,
8572        onUploadRequested: handleUploadClick
8573      }
8574    ),
8575    "collection-section": /* @__PURE__ */ jsxRuntimeExports.jsx(
8576      CollectionSectionPage,
8577      {
8578        slug: activeViewId ? activeViewId.split(":")[0] : "",
8579        sectionId: activeViewId ? activeViewId.split(":")[1] : "",
8580        onSelectTrack: handleSelectTrack,
8581        onToggleWatchLater: toggleWatchLater,
8582        watchLaterSet: watchLater,
8583        onAddToQueue: handleAddToQueue,
8584        onAddToPlaylist: handleAddToPlaylistClick,
8585        onNavigate: handleNavigate,
8586        onUploadRequested: handleUploadClick,
8587        currentUser
8588      }
8589    ),
8590    "playlists": /* @__PURE__ */ jsxRuntimeExports.jsx(PlaylistsPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true) }),
8591    "help": /* @__PURE__ */ jsxRuntimeExports.jsx(HelpPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate, initialArticleId: activeViewId, category: activeCategory }),
8592    "terms": /* @__PURE__ */ jsxRuntimeExports.jsx(HelpPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), initialArticleId: "terms" }),
8593    "privacy": /* @__PURE__ */ jsxRuntimeExports.jsx(HelpPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), initialArticleId: "privacy" }),
8594    "messages": /* @__PURE__ */ jsxRuntimeExports.jsx(MessengerPage, { currentUser }),
8595    "account": /* @__PURE__ */ jsxRuntimeExports.jsx(
8596      AccountPage,
8597      {
8598        currentUser,
8599        onAuthRequested: () => setIsAuthModalOpen(true),
8600        onLogout: handleLogout,
8601        onUpdateUser: handleUpdateUser,
8602        tracks,
8603        onSelectTrack: handleSelectTrack,
8604        onToggleWatchLater: toggleWatchLater,
8605        watchLaterSet: watchLater,
8606        onUploadRequested: () => handleUploadClick(),
8607        onNavigate: handleNavigate
8608      }
8609    ),
8610    "converter": /* @__PURE__ */ jsxRuntimeExports.jsx(ConverterPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onPublish: handleUploadClick }),
8611    "editor": /* @__PURE__ */ jsxRuntimeExports.jsx(EditorPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), id: activeViewId, onNavigate: handleNavigate }),
8612    "lyrics": /* @__PURE__ */ jsxRuntimeExports.jsx(LyricsPage, { id: activeViewId, onNavigate: handleNavigate }),
8613    "lyrics-search": /* @__PURE__ */ jsxRuntimeExports.jsx(LyricsSearchPage, { onNavigate: handleNavigate }),
8614    "search": /* @__PURE__ */ jsxRuntimeExports.jsx(
8615      AdvancedSearchPage,
8616      {
8617        searchQuery: submittedQuery,
8618        onSelectTrack: handleSelectTrack,
8619        onToggleWatchLater: toggleWatchLater,
8620        watchLaterSet: watchLater,
8621        onAddToQueue: handleAddToQueue,
8622        onAddToPlaylist: handleAddToPlaylistClick,
8623        theme
8624      }
8625    ),
8626    "algorithm-studio": /* @__PURE__ */ jsxRuntimeExports.jsx(AlgorithmStudio, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate }),
8627    "algorithm-editor": /* @__PURE__ */ jsxRuntimeExports.jsx(AlgorithmEditor, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), id: activeViewId, onNavigate: handleNavigate }),
8628    "algorithm-arena": /* @__PURE__ */ jsxRuntimeExports.jsx(StagingArena, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), id: activeViewId, onNavigate: handleNavigate }),
8629    "tribunal": /* @__PURE__ */ jsxRuntimeExports.jsx(TribunalPage, { currentUser, onNavigate: handleNavigate }),
8630    "lab": /* @__PURE__ */ jsxRuntimeExports.jsx(HamieLabPage, { currentUser, onNavigate: handleNavigate }),
8631    "visualizer": /* @__PURE__ */ jsxRuntimeExports.jsx(MidiVisualizerPage, { currentUser, onNavigate: handleNavigate, theme }),
8632    "keyboard-emulator": /* @__PURE__ */ jsxRuntimeExports.jsx(KeyboardEmulatorPage, { currentUser, onNavigate: handleNavigate, theme }),
8633    "sheet-digitizer": /* @__PURE__ */ jsxRuntimeExports.jsx(SheetMusicDigitizerPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate, initialProjectId: activeViewId }),
8634    "audio-digitizer": /* @__PURE__ */ jsxRuntimeExports.jsx(AudioDigitizerPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate }),
8635    "client-transcriber": /* @__PURE__ */ jsxRuntimeExports.jsx(ClientAudioDigitizerPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate }),
8636    "score-format-converter": /* @__PURE__ */ jsxRuntimeExports.jsx(ScoreConverterPage, { currentUser, onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate }),
8637    "vocal-processor": /* @__PURE__ */ jsxRuntimeExports.jsx(VocalProcessorPage, { currentUser, onNavigate: handleNavigate }),
8638    "notation-editor": /* @__PURE__ */ jsxRuntimeExports.jsx(
8639      NotationEditor,
8640      {
8641        currentUser,
8642        initialProjectId: activeViewId,
8643        onNavigate: handleNavigate,
8644        theme
8645      }
8646    ),
8647    "billing": /* @__PURE__ */ jsxRuntimeExports.jsx(BillingPage, {}),
8648    "dmca": /* @__PURE__ */ jsxRuntimeExports.jsx(DMCAForm, {}),
8649    "verify-email": /* @__PURE__ */ jsxRuntimeExports.jsx(EmailVerification, { onAuthSuccess: (user) => setCurrentUser(user), onNavigate: handleNavigate }),
8650    "reset-password": /* @__PURE__ */ jsxRuntimeExports.jsx(ResetPassword, { onAuthSuccess: (user) => setCurrentUser(user), onAuthRequested: () => setIsAuthModalOpen(true), onNavigate: handleNavigate }),
8651    "soundfonts": /* @__PURE__ */ jsxRuntimeExports.jsx(SoundfontsPage, { onNavigate: handleNavigate, isAdmin }),
8652    "soundfont": /* @__PURE__ */ jsxRuntimeExports.jsx(SoundfontDetailPage, { isAdmin, slug: activeViewId, onNavigate: handleNavigate }),
8653    "soundfonts-upload": /* @__PURE__ */ jsxRuntimeExports.jsx(SoundfontUploadPage, { isAdmin, onNavigate: handleNavigate }),
8654    "personal-space": currentUser ? /* @__PURE__ */ jsxRuntimeExports.jsx(
8655      PersonalSpacePage,
8656      {
8657        currentUser,
8658        onNavigate: handleNavigate
8659      }
8660    ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
8661      AccessDeniedView,
8662      {
8663        title: "Access Denied",
8664        message: "Please sign in or register to view your Personal Space.",
8665        onBack: handleGoHome
8666      }
8667    ),
8668    "not-found": /* @__PURE__ */ jsxRuntimeExports.jsx(NotFoundView, { error: notFoundError, onBack: () => {
8669      setNotFoundError(null);
8670      setActiveView("home");
8671    } })
8672  };
8673  const renderContent = () => {
8674    var _a, _b;
8675    if (activeView === "watch" && activeTrack) {
8676      return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
8677        WatchPage,
8678        {
8679          theme,
8680          activeTrack,
8681          currentUser,
8682          isPlaying,
8683          setIsPlaying,
8684          toggleMute,
8685          mutedChannels,
8686          toggleLike,
8687          likedMidis,
8688          subscribe: () => toggleSubscription(activeTrack.uploader.name, activeTrack.uploader.id),
8689          isSubscribed: subscriptions.has(activeTrack.uploader.name),
8690          toggleSubscription,
8691          handleChannelSelect,
8692          handleSelectTrack,
8693          toggleWatchLater,
8694          watchLater,
8695          allTracks: tracks,
8696          onTagSelect: handleTagSelect,
8697          onArtistSelect: handleArtistSelect,
8698          currentTime,
8699          duration,
8700          onAddToQueue: handleAddToQueue,
8701          onAddToPlaylist: handleAddToPlaylistClick,
8702          onAuthRequested: () => setIsAuthModalOpen(true),
8703          audioMode,
8704          setAudioMode,
8705          isBuffering,
8706          setIsBuffering,
8707          autoPlayCountdown,
8708          onCancelAutoPlay: handleCancelAutoPlay,
8709          onPlayNext: handlePlayNextImmediate,
8710          nextTrack: queue.length > 0 ? queue[0] : activeTrack ? activePlaylist && activePlaylist.length > 0 && activePlaylist.some((p) => String(p.id) === String(activeTrack.id)) ? activePlaylist[(activePlaylist.findIndex((p) => String(p.id) === String(activeTrack.id)) + 1) % activePlaylist.length] : (contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks)[((contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks).findIndex((t2) => String(t2.id) === String(activeTrack.id)) + 1) % (contextTracksRef.current.length > 0 ? contextTracksRef.current : tracks).length] : void 0,
8711          onSeek: (time) => setSeekRequest({ time, timestamp: Date.now() }),
8712          playbackRate,
8713          isGameMode,
8714          setIsGameMode,
8715          audioQuality,
8716          pitchSemitones,
8717          setPitchSemitones,
8718          onUserUpdate: handleUpdateUser
8719        }
8720      ) });
8721    }
8722    if (isLoading) {
8723      return /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {});
8724    }
8725    if (isSearchingMelody) {
8726      return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-20 text-center", children: [
8727        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" }),
8728        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500", children: "Analyzing melody contours..." })
8729      ] });
8730    }
8731    let isViewAllowed = true;
8732    let disabledModuleName = "";
8733    const urlKey = new URLSearchParams(currentLocation.search).get("key");
8734    const storedKey = localStorage.getItem("digitizer_test_key") || (typeof window !== "undefined" ? sessionStorage.getItem("digitizer_test_key") : null);
8735    const envTestKey = ((_a = window.HAMIE_ENV) == null ? void 0 : _a.DIGITIZER_TEST_KEY) || "digitizer-test-2026-key";
8736    const adminKey = "2164dc28-6243-4840-903f-3d3c936f706c";
8737    const hasValidTestKey = urlKey && (urlKey === envTestKey || urlKey === adminKey) || storedKey && (storedKey === envTestKey || storedKey === adminKey);
8738    if (!isAdmin) {
8739      let toolIdToCheck = null;
8740      if (activeView === "sheet-digitizer") {
8741        if (!hasValidTestKey) {
8742          toolIdToCheck = "sheet-digitizer";
8743        }
8744      } else if (activeView === "audio-digitizer") toolIdToCheck = "audio-digitizer";
8745      else if (activeView === "client-transcriber") toolIdToCheck = "client-audio-digitizer";
8746      else if (activeView === "algorithm-studio" || activeView === "algorithm-editor" || activeView === "algorithm-arena") toolIdToCheck = "algorithm-studio";
8747      else if (activeView === "tribunal") toolIdToCheck = "tribunal";
8748      else if (activeView === "client-transcriber") toolIdToCheck = "client-transcriber";
8749      else if (activeView === "score-format-converter") toolIdToCheck = "score-format-converter";
8750      else if (activeView === "vocal-processor") toolIdToCheck = "vocal-processor";
8751      else if (activeView === "converter") toolIdToCheck = "converter";
8752      else if (activeView === "score-format-converter") toolIdToCheck = "score-format-converter";
8753      else if (activeView === "melody-search") toolIdToCheck = "melody-search";
8754      else if (activeView === "visualizer") toolIdToCheck = "midi-visualizer";
8755      else if (activeView === "notation-editor") {
8756        if (!hasValidTestKey) {
8757          toolIdToCheck = "score-editor";
8758        }
8759      } else if (activeView === "editor") {
8760        const params = new URLSearchParams(window.location.search);
8761        const mode = params.get("mode");
8762        if (mode === "score") {
8763          if (!hasValidTestKey) {
8764            toolIdToCheck = "score-editor";
8765          }
8766        } else {
8767          toolIdToCheck = "midi-editor";
8768        }
8769      }
8770      if (toolIdToCheck) {
8771        const toolsList = (labConfig == null ? void 0 : labConfig.tools) || [];
8772        const dbTool = toolsList.find((t2) => t2.id === toolIdToCheck);
8773        const defTool = (_b = DEFAULT_TOOLS) == null ? void 0 : _b.find((t2) => t2.id === toolIdToCheck);
8774        const isEnabled = dbTool ? dbTool.enabled : defTool ? defTool.enabled : true;
8775        if (!isEnabled) {
8776          isViewAllowed = false;
8777          disabledModuleName = defTool ? defTool.name : activeView;
8778        }
8779      }
8780    }
8781    const dynamicModule = siteModules.find((m) => m.id === activeView);
8782    if (dynamicModule && !isAdmin && (!hasValidTestKey || activeView !== "notation-editor") && dynamicModule.status !== "active") {
8783      isViewAllowed = false;
8784      disabledModuleName = dynamicModule.name;
8785    }
8786    if (!isViewAllowed) {
8787      return /* @__PURE__ */ jsxRuntimeExports.jsx(
8788        AccessDeniedView,
8789        {
8790          title: "Module Disabled",
8791          message: `The "${disabledModuleName}" module has been disabled by the administrator. Direct access to this module is restricted.`,
8792          onBack: () => setActiveView("home")
8793        }
8794      );
8795    }
8796    if (COMPONENT_REGISTRY[activeView]) {
8797      return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {}), children: COMPONENT_REGISTRY[activeView] });
8798    }
8799    if (dynamicModule) {
8800      return /* @__PURE__ */ jsxRuntimeExports.jsx(DynamicPageRenderer, { module: dynamicModule });
8801    }
8802    return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingFallback, {}), children: COMPONENT_REGISTRY["home"] });
8803  };
8804  return /* @__PURE__ */ jsxRuntimeExports.jsx(AdminProvider, { currentUser, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-screen bg-white dark:bg-[#0f0f0f] text-zinc-900 dark:text-white flex flex-col font-sans selection:bg-indigo-500/30 transition-colors duration-200 overflow-hidden relative", children: [
8805    /* @__PURE__ */ jsxRuntimeExports.jsx(
8806      AuthModal,
8807      {
8808        isOpen: isAuthModalOpen,
8809        onClose: () => setIsAuthModalOpen(false),
8810        onAuthSuccess: (user) => setCurrentUser(user)
8811      }
8812    ),
8813    /* @__PURE__ */ jsxRuntimeExports.jsx(
8814      UploadModal,
8815      {
8816        isOpen: isUploadModalOpen,
8817        onClose: () => {
8818          setIsUploadModalOpen(false);
8819          setUploadInitialFile(null);
8820          setUploadInitialMetadata(null);
8821        },
8822        currentUser,
8823        initialFile: uploadInitialFile,
8824        initialMetadata: uploadInitialMetadata
8825      }
8826    ),
8827    isMelodySearchOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
8828      MelodyInput,
8829      {
8830        onSearch: handleMelodySearch,
8831        onClose: () => setIsMelodySearchOpen(false)
8832      }
8833    ),
8834    currentUser && playlistTrack && /* @__PURE__ */ jsxRuntimeExports.jsx(
8835      AddToPlaylistModal,
8836      {
8837        isOpen: isAddToPlaylistModalOpen,
8838        onClose: () => {
8839          setIsAddToPlaylistModalOpen(false);
8840          setPlaylistTrack(null);
8841        },
8842        track: playlistTrack,
8843        currentUser
8844      }
8845    ),
8846    currentUser && /* @__PURE__ */ jsxRuntimeExports.jsx(
8847      NotificationsModal,
8848      {
8849        isOpen: isNotificationsModalOpen,
8850        onClose: () => setIsNotificationsModalOpen(false),
8851        previewNotifications: notifications,
8852        onMarkAsRead: handleMarkNotificationRead,
8853        onMarkAllRead: handleMarkAllNotificationsRead,
8854        onNavigate: handleNotificationNavigate
8855      }
8856    ),
8857    !(isFullscreen && activeView === "notation-editor") && /* @__PURE__ */ jsxRuntimeExports.jsx(StaffAdminBar, {}),
8858    !(isFullscreen && activeView === "notation-editor") && /* @__PURE__ */ jsxRuntimeExports.jsx(
8859      Header,
8860      {
8861        isSidebarOpen,
8862        setIsSidebarOpen,
8863        handleGoHome,
8864        searchQuery,
8865        setSearchQuery,
8866        handleSearch,
8867        currentUser,
8868        setIsAuthModalOpen,
8869        onAccountClick: () => {
8870          setActiveView("account");
8871        },
8872        onPersonalSpaceClick: () => {
8873          setActiveView("personal-space");
8874        },
8875        onLogout: handleLogout,
8876        notifications,
8877        onMarkNotificationRead: handleMarkNotificationRead,
8878        onMarkAllNotificationsRead: handleMarkAllNotificationsRead,
8879        onSeeAllNotifications: () => setIsNotificationsModalOpen(true),
8880        toggleTheme,
8881        theme,
8882        onUploadClick: handleUploadClick,
8883        onMelodySearchClick: () => setActiveView("melody-search"),
8884        onNavigateNotification: handleNotificationNavigate,
8885        onNavigate: handleNavigate
8886      }
8887    ),
8888    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 overflow-hidden relative", children: [
8889      !(isFullscreen && activeView === "notation-editor") && /* @__PURE__ */ jsxRuntimeExports.jsx(
8890        Sidebar,
8891        {
8892          isSidebarOpen,
8893          setIsSidebarOpen,
8894          activeView,
8895          setActiveView,
8896          activeTrack,
8897          setActiveTrack: handleSelectTrack,
8898          currentUser,
8899          setIsAuthModalOpen,
8900          subscriptions,
8901          handleChannelSelect,
8902          handleGoHome,
8903          onAdminClick: () => {
8904            var _a;
8905            const adminUrl = (_a = window.HAMIE_ENV) == null ? void 0 : _a.ADMIN_URL;
8906            if (adminUrl) {
8907              window.open(adminUrl, "_blank");
8908            } else {
8909              setActiveView("admin");
8910            }
8911          },
8912          siteModules,
8913          unreadMessageCount,
8914          theme,
8915          toggleTheme,
8916          isMediaPage: [
8917            "watch",
8918            "editor",
8919            "lab",
8920            "sheet-digitizer",
8921            "audio-digitizer",
8922            "client-transcriber",
8923            "algorithm-studio",
8924            "algorithm-editor",
8925            "algorithm-arena",
8926            "tribunal",
8927            "converter",
8928            "melody-search",
8929            "visualizer",
8930            "notation-editor",
8931            "keyboard-emulator"
8932          ].includes(activeView)
8933        }
8934      ),
8935      /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { id: "main-scroll-container", className: `flex-1 overflow-y-auto bg-white dark:bg-[#0f0f0f] custom-scrollbar transition-colors duration-200 ${activeTrack ? "pb-28" : ""} flex flex-col`, children: [
8936        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex-1 ${!(isFullscreen && activeView === "notation-editor") ? "pt-16" : ""}`, children: renderContent() }),
8937        !(isFullscreen && activeView === "notation-editor") && /* @__P
8937URE__ */ jsxRuntimeExports.jsx(Footer, {})
8938      ] }),
8939      /* @__PURE__ */ jsxRuntimeExports.jsx(BackToTop, {})
8940    ] }),
8941    activeTrack && /* @__PURE__ */ jsxRuntimeExports.jsx(
8942      PersistentPlayer,
8943      {
8944        track: activeTrack,
8945        isPlaying,
8946        setIsPlaying,
8947        onTimeUpdate: (curr, dur) => {
8948          setCurrentTime(curr);
8949          setDuration(dur);
8950        },
8951        onPlaybackRateChange: setPlaybackRate,
8952        onNext: handleNextTrack,
8953        onPrev: handlePrevTrack,
8954        toggleLike,
8955        isLiked: likedMidis.has(activeTrack.id),
8956        onTrackClick: activeView === "watch" && String(activeViewId) === String(activeTrack.id) ? void 0 : () => navigate(generateTrackUrl(activeTrack)),
8957        onAddToPlaylist: () => handleAddToPlaylistClick(activeTrack),
8958        queue,
8959        activePlaylist,
8960        playlistLabel,
8961        onClearPlaylist: () => {
8962          setActivePlaylist(null);
8963          setPlaylistLabel(null);
8964        },
8965        onRemoveFromQueue: handleRemoveFromQueue,
8966        onPlayQueueTrack: (track) => {
8967          const idx = queue.findIndex((t2) => t2.id === track.id);
8968          if (idx !== -1) {
8969            const newQueue = [...queue];
8970            newQueue.splice(idx, 1);
8971            setQueue(newQueue);
8972          }
8973          handleSelectTrack(track);
8974        },
8975        activeView,
8976        audioMode,
8977        setAudioMode,
8978        isBuffering,
8979        seekTo: seekRequest,
8980        isGameMode,
8981        currentUser,
8982        audioQuality,
8983        pitchSemitones,
8984        setPitchSemitones
8985      }
8986    )
8987  ] }) });
8988};
8989const App = () => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlayerProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(AppContent, {}) }) }) });
8990if (typeof StyleSheetList !== "undefined" && !StyleSheetList.prototype[Symbol.iterator]) {
8991  StyleSheetList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
8992}
8993if (typeof window !== "undefined") {
8994  const urlParams = new URLSearchParams(window.location.search);
8995  const keyParam = urlParams.get("key");
8996  if (keyParam === "digitizer-test-2026-key") {
8997    sessionStorage.setItem("digitizer_test_key", keyParam);
8998  }
8999  const originalFetch2 = window.fetch;
9000  window.fetch = function(input, init) {
9001    const testKey = sessionStorage.getItem("digitizer_test_key");
9002    if (testKey) {
9003      init = init || {};
9004      let headers = {};
9005      if (init.headers) {
9006        if (init.headers instanceof Headers) {
9007          init.headers.forEach((value, key) => {
9008            headers[key] = value;
9009          });
9010        } else if (Array.isArray(init.headers)) {
9011          init.headers.forEach(([key, value]) => {
9012            headers[key] = value;
9013          });
9014        } else {
9015          headers = { ...init.headers };
9016        }
9017      }
9018      headers["x-digitizer-test-key"] = testKey;
9019      init.headers = headers;
9020    }
9021    return originalFetch2.call(this, input, init);
9022  };
9023}
9024const originalLog = console.log;
9025const originalInfo = console.info;
9026const originalDebug = console.debug;
9027const originalWarn = console.warn;
9028const originalError = console.error;
9029const isAdminUser = () => {
9030  try {
9031    const saved = localStorage.getItem("hamie_session");
9032    if (saved) {
9033      const user = JSON.parse(saved);
9034      return (user == null ? void 0 : user.role) === "ADMIN";
9035    }
9036  } catch {
9037  }
9038  return false;
9039};
9040const isDebugMessage = (args) => {
9041  if (args.length === 0) return false;
9042  const firstArg = args[0];
9043  if (typeof firstArg === "string") {
9044    const cleaned = firstArg.trim();
9045    return cleaned.startsWith("[") || cleaned.startsWith("%c[") || cleaned.includes("[PersistentPlayer]") || cleaned.includes("[parseMidiInstruments]") || cleaned.includes("[HamieSynth]");
9046  }
9047  return false;
9048};
9049console.log = (...args) => {
9050  if (isAdminUser() || !isDebugMessage(args)) {
9051    originalLog.apply(console, args);
9052  }
9053};
9054console.info = (...args) => {
9055  if (isAdminUser() || !isDebugMessage(args)) {
9056    originalInfo.apply(console, args);
9057  }
9058};
9059console.debug = (...args) => {
9060  if (isAdminUser() || !isDebugMessage(args)) {
9061    originalDebug.apply(console, args);
9062  }
9063};
9064console.warn = (...args) => {
9065  if (isAdminUser() || !isDebugMessage(args)) {
9066    originalWarn.apply(console, args);
9067  }
9068};
9069console.error = (...args) => {
9070  if (isAdminUser() || !isDebugMessage(args)) {
9071    originalError.apply(console, args);
9072  }
9073};
9074uxTracker.init();
9075const logError = (message, source, lineno, colno, stack, url) => {
9076  console.error("Global Error Caught:", message);
9077  fetch("/api/log-client-error", {
9078    method: "POST",
9079    headers: { "Content-Type": "application/json" },
9080    body: JSON.stringify({
9081      message: message.toString(),
9082      source,
9083      lineno,
9084      colno,
9085      stack,
9086      url
9087    })
9088  }).catch((e) => console.error("Failed to send error log:", e));
9089  try {
9090    const msgStr = String(message).toLowerCase();
9091    const isChunkError = msgStr.includes("chunk") || msgStr.includes("loading chunk") || msgStr.includes("failed to fetch dynamically imported module") || msgStr.includes("dynamic import");
9092    if (isChunkError) {
9093      const hasAutoReloaded = sessionStorage.getItem("has_auto_reloaded_from_error");
9094      if (!hasAutoReloaded) {
9095        sessionStorage.setItem("has_auto_reloaded_from_error", "true");
9096        window.location.reload();
9097      }
9098    }
9099  } catch (e) {
9100    console.error("Failed to handle auto-reload on global error:", e);
9101  }
9102};
9103window.onerror = (message, source, lineno, colno, error) => {
9104  logError(message, source, lineno, colno, error == null ? void 0 : error.stack, window.location.href);
9105};
9106window.onunhandledrejection = (event) => {
9107  var _a, _b;
9108  logError(
9109    ((_a = event.reason) == null ? void 0 : _a.message) || "Unhandled Rejection",
9110    "",
9111    0,
9112    0,
9113    ((_b = event.reason) == null ? void 0 : _b.stack) || JSON.stringify(event.reason),
9114    window.location.href
9115  );
9116};
9117const container = document.getElementById("root");
9118if (container) {
9119  const root = createRoot(container);
9120  root.render(
9121    /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-screen items-center justify-center bg-[#0f0f0f] text-white", children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) })
9122  );
9123} else {
9124  console.error("DEBUG FATAL: Root container not found in DOM");
9125}
9126export {
9127  VideoCard as V,
9128  useAdmin as a,
9129  usePlayer as u
9130};

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.