@hadoku/watchparty-ui 0.45.10 → 0.45.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/chunks/{AdminPanel-lSj6jiWo.js → AdminPanel-NqY9bqdB.js} +14 -14
  2. package/dist/chunks/{AdminPanel-lSj6jiWo.js.map → AdminPanel-NqY9bqdB.js.map} +1 -1
  3. package/dist/chunks/MediaDrawer-DnJ2bjL7.js +710 -0
  4. package/dist/chunks/MediaDrawer-DnJ2bjL7.js.map +1 -0
  5. package/dist/chunks/{PlaylistPanel-BI_3fwNX.js → PlaylistPanel-DKjA4zxR.js} +32 -32
  6. package/dist/chunks/{PlaylistPanel-BI_3fwNX.js.map → PlaylistPanel-DKjA4zxR.js.map} +1 -1
  7. package/dist/chunks/{Room-C7keqQhT.js → Room-H5597D2i.js} +303 -304
  8. package/dist/chunks/Room-H5597D2i.js.map +1 -0
  9. package/dist/chunks/{index-CHJG2cwy.js → index-BLeEknCO.js} +3138 -3301
  10. package/dist/chunks/index-BLeEknCO.js.map +1 -0
  11. package/dist/chunks/{submissionsApi-6-0TbFTF.js → submissionsApi-CL9vNf_o.js} +28 -28
  12. package/dist/chunks/{submissionsApi-6-0TbFTF.js.map → submissionsApi-CL9vNf_o.js.map} +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/room-chrome.js +1003 -1093
  15. package/dist/room-chrome.js.map +1 -1
  16. package/dist/watchparty-ui.css +1 -1
  17. package/package.json +3 -3
  18. package/dist/chunks/GameModule-Ydp4apeG.js +0 -1512
  19. package/dist/chunks/GameModule-Ydp4apeG.js.map +0 -1
  20. package/dist/chunks/MapEditor-B7r-mr3e.js +0 -733
  21. package/dist/chunks/MapEditor-B7r-mr3e.js.map +0 -1
  22. package/dist/chunks/MediaDrawer-yoaRrRtv.js +0 -711
  23. package/dist/chunks/MediaDrawer-yoaRrRtv.js.map +0 -1
  24. package/dist/chunks/Room-C7keqQhT.js.map +0 -1
  25. package/dist/chunks/cover-AUIrlUMF.js +0 -5
  26. package/dist/chunks/cover-AUIrlUMF.js.map +0 -1
  27. package/dist/chunks/index-CHJG2cwy.js.map +0 -1
@@ -1,711 +0,0 @@
1
- import { jsxs as u, jsx as a, Fragment as j } from "react/jsx-runtime";
2
- import { useState as x, useEffect as O, useRef as ue, useMemo as Se, useCallback as Ce } from "react";
3
- import { g as X, ay as K, d as E, an as Be, w as q, Y as H, az as Oe, aA as De, h as ie, t as Pe, G as $e, aB as Ge } from "./index-CHJG2cwy.js";
4
- import { D as Ee, a as re, u as je, s as qe, b as He } from "./Room-C7keqQhT.js";
5
- import { s as Ae, a as Y, g as pe, f as We, b as me, h as ze, u as Ve, c as Ye, e as Je, d as Ke, i as Qe, j as Xe, k as Ze } from "./submissionsApi-6-0TbFTF.js";
6
- import { Icon as ne } from "@wolffm/themes";
7
- import { b as et } from "./cover-AUIrlUMF.js";
8
- const se = /* @__PURE__ */ new Map(), ee = /* @__PURE__ */ new Map(), tt = 6e4;
9
- async function Q(e = "") {
10
- const r = Date.now(), t = se.get(e);
11
- if (t && r - t.ts < tt) return t.data;
12
- const l = ee.get(e);
13
- if (l) return l;
14
- const o = (async () => {
15
- const g = `${X()}/api/catalog${e ? `?prefix=${encodeURIComponent(e)}` : ""}`, c = await fetch(g, { cache: "no-store" });
16
- if (!c.ok) throw new Error(`catalog_http_${c.status}`);
17
- const y = await c.json();
18
- return K() && E("catalog", "Catalog fetched", { prefix: e, dirs: y.dirs?.length, files: y.files?.length }), se.set(e, { ts: r, data: y }), y;
19
- })();
20
- ee.set(e, o);
21
- try {
22
- return await o;
23
- } finally {
24
- ee.delete(e);
25
- }
26
- }
27
- function at(e) {
28
- se.clear();
29
- }
30
- async function rt() {
31
- if (K())
32
- try {
33
- const e = ["output/fmp4/anime", "output/fmp4/movie", "output/fmp4/tv"];
34
- for (const r of e) {
35
- const t = await Q(r);
36
- E("catalog", "[probe] catalog " + r, { dirs: t.dirs.length, files: t.files.length });
37
- const l = t.files[0];
38
- if (l) {
39
- const o = nt(), s = `${o}/${l.id}/index.json`.replace(/\\/g, "/"), g = `${o}/${l.id}/${Be}`.replace(/\\/g, "/");
40
- await ge(s, "index"), await ge(g, "fragment", { headers: { Range: "bytes=0-1023" } });
41
- }
42
- }
43
- } catch (e) {
44
- q("catalog", "[probe] error", { error: e instanceof Error ? e.message : String(e) });
45
- }
46
- }
47
- function nt() {
48
- return "/media";
49
- }
50
- async function ge(e, r, t) {
51
- const l = performance.now(), o = await fetch(e, t), s = Math.round(performance.now() - l);
52
- return E("catalog", "[probe] " + r, { url: e, status: o.status, ms: s, bytes: o.headers.get("content-length") }), o;
53
- }
54
- const st = [
55
- "yuuna",
56
- "gushingovermagicalgirls",
57
- "immoralguild",
58
- "ingokudanchi",
59
- "isekaionsenparadise"
60
- ], fe = (e) => e.toLowerCase().replace(/[^a-z0-9]/g, "");
61
- function it(e, r) {
62
- return new Set(r.map(fe)).has(fe(e));
63
- }
64
- function lt(e) {
65
- return it(e, st);
66
- }
67
- function he(e, r) {
68
- const t = /* @__PURE__ */ new Map();
69
- for (const l of e) {
70
- const o = /* @__PURE__ */ new Map();
71
- for (const s of r(l))
72
- o.set(s.name, s.expanded === !0);
73
- t.set(l.name, { expanded: l.expanded === !0, children: o });
74
- }
75
- return t;
76
- }
77
- function be(e, r, t, l) {
78
- return e.map((o) => {
79
- const s = r.get(o.name);
80
- if (!s) return o;
81
- const c = t(o).map((v) => {
82
- const h = s.children.get(v.name);
83
- return h === void 0 ? v : { ...v, expanded: h };
84
- });
85
- return l({ ...o, expanded: s.expanded }, c);
86
- });
87
- }
88
- const ot = "Anime";
89
- function J(e) {
90
- return e === ot;
91
- }
92
- function dt(e, r, t) {
93
- return e.map(
94
- (l) => t(
95
- { ...l, expanded: J(l.name) },
96
- r(l).map((o) => ({ ...o, expanded: !1 }))
97
- )
98
- );
99
- }
100
- const xe = "output/fmp4", ye = [
101
- { key: "anime", label: "Anime" },
102
- { key: "movie", label: "Movie" },
103
- { key: "tv", label: "TV" }
104
- ];
105
- async function te(e, r) {
106
- try {
107
- return await e(r);
108
- } catch (t) {
109
- return q("catalog", "Fetch failed", { prefix: r, ...H(t) }), null;
110
- }
111
- }
112
- async function ve(e) {
113
- const [r, t] = await Promise.all([
114
- Promise.all(ye.map((c) => te(e, `${xe}/${c.key}`))),
115
- te(e, `${xe}/playlists`)
116
- ]), l = await Promise.all(r.map((c) => Promise.all((c?.dirs ?? []).map(async (y) => {
117
- const h = ((await te(e, y.path))?.files ?? []).map(($) => ({ type: "episode", name: $.title, mediaId: $.id }));
118
- return h.length ? { type: "series", name: y.name, episodes: h, expanded: !1 } : null;
119
- })))), o = [], s = [];
120
- ye.forEach((c, y) => {
121
- const v = [];
122
- for (const h of l[y])
123
- h && (c.key === "anime" && lt(h.name) ? s.push(h) : v.push(h));
124
- v.length && o.push({ type: "category", name: c.label, series: v, expanded: J(c.label) });
125
- });
126
- const g = (t?.dirs ?? []).map((c) => ({ type: "playlist", name: c.name, playlistId: c.name }));
127
- return g.length && o.push({ type: "playlist_category", name: "Playlists", playlists: g, expanded: J("Playlists") }), s.length && o.push({ type: "category", name: "Ecchi", series: s, expanded: J("Ecchi") }), o;
128
- }
129
- const ct = /^(?:https?|magnet):/i;
130
- function Me(e) {
131
- const r = e.trim();
132
- return r.length >= Oe && r.length <= De && !ct.test(r);
133
- }
134
- function we(e) {
135
- return e ? `${X()}${e}` : null;
136
- }
137
- function ut(e) {
138
- if (e === null || !Number.isFinite(e) || e < 0) return "";
139
- const r = Math.floor(e % 60), t = Math.floor(e / 60) % 60, l = Math.floor(e / 3600), o = String(r).padStart(2, "0");
140
- return l > 0 ? `${l}:${String(t).padStart(2, "0")}:${o}` : `${t}:${o}`;
141
- }
142
- async function Ie(e, r) {
143
- try {
144
- const t = await fetch(`${X()}${e}`, { signal: r });
145
- return t.ok ? { ok: !0, value: await t.json() } : { ok: !1, status: t.status };
146
- } catch (t) {
147
- return r.aborted ? { ok: !1, status: "aborted" } : (q("search", "Search request threw", H(t)), { ok: !1, status: "network" });
148
- }
149
- }
150
- function pt(e, r) {
151
- return Ie(`/api/search/shows?q=${encodeURIComponent(e.trim())}`, r);
152
- }
153
- function mt(e, r) {
154
- return Ie(`/api/search/youtube?q=${encodeURIComponent(e.trim())}`, r);
155
- }
156
- async function gt(e, r) {
157
- try {
158
- const t = await fetch(`${X()}/api/watchlist`, {
159
- method: "POST",
160
- headers: { "Content-Type": "application/json" },
161
- body: JSON.stringify({ source: e, id: r })
162
- });
163
- if (t.status === 201) {
164
- const l = await t.json();
165
- return E("search", "Added to watch list", { source: e, id: r, series: l.series }), { ok: !0, value: l };
166
- }
167
- return { ok: !1, code: t.status === 409 ? "exists" : "failed", status: t.status };
168
- } catch (t) {
169
- return q("search", "Watch-list add threw", H(t)), { ok: !1, code: "failed", status: "network" };
170
- }
171
- }
172
- const ft = 350;
173
- function ht({ query: e, onSubmitted: r }) {
174
- const t = e.trim(), l = Me(t), [o, s] = x(null), [g, c] = x(null), [y, v] = x({});
175
- if (O(() => {
176
- if (!l) return;
177
- const d = new AbortController(), N = setTimeout(() => {
178
- pt(t, d.signal).then((f) => {
179
- d.signal.aborted || s({ q: t, outcome: f.ok ? { ok: !0, value: f.value.results } : f });
180
- }), mt(t, d.signal).then((f) => {
181
- d.signal.aborted || c({ q: t, outcome: f.ok ? { ok: !0, value: f.value.results } : f });
182
- });
183
- }, ft);
184
- return () => {
185
- clearTimeout(N), d.abort();
186
- };
187
- }, [t, l]), !l) return null;
188
- const h = o?.q === t ? o.outcome : null, $ = g?.q === t ? g.outcome : null;
189
- async function A(d) {
190
- const N = `${d.source}:${d.id}`;
191
- v((k) => ({ ...k, [N]: "adding" }));
192
- const f = await gt(d.source, d.id);
193
- v((k) => ({ ...k, [N]: f.ok || f.code === "exists" ? "added" : `error ${f.status}` }));
194
- }
195
- async function R(d) {
196
- const N = `yt:${d.id}`;
197
- v((k) => ({ ...k, [N]: "adding" }));
198
- const f = await Ae(d.url, d.title);
199
- f.ok ? (v((k) => ({ ...k, [N]: "added" })), r(f.value)) : v((k) => ({ ...k, [N]: Y(f.failure) }));
200
- }
201
- const U = h?.ok && $?.ok && h.value.length === 0 && $.value.length === 0;
202
- return /* @__PURE__ */ u("div", { className: "border-b border-border", "data-testid": "media-search", children: [
203
- U && /* @__PURE__ */ a("div", { className: "px-3 py-2 text-[11px] text-text-muted", children: "no results" }),
204
- /* @__PURE__ */ a(Ne, { label: "Shows", answer: h, testId: "search-shows", children: h?.ok && h.value.map((d) => {
205
- const N = `${d.source}:${d.id}`, f = d.onWatchlist ? "added" : y[N];
206
- return /* @__PURE__ */ a(
207
- ke,
208
- {
209
- testId: `show-${N}`,
210
- image: we(d.poster),
211
- imageClass: "w-8 h-11",
212
- title: d.title,
213
- meta: [d.year, d.format, d.episodes ? `${d.episodes} ep` : null],
214
- state: f,
215
- canAdd: d.addable,
216
- addLabel: `Add ${d.title} to watch list`,
217
- onAdd: () => {
218
- A(d);
219
- }
220
- },
221
- N
222
- );
223
- }) }),
224
- /* @__PURE__ */ a(Ne, { label: "YouTube", answer: $, testId: "search-youtube", children: $?.ok && $.value.map((d) => /* @__PURE__ */ a(
225
- ke,
226
- {
227
- testId: `video-${d.id}`,
228
- image: we(d.thumb),
229
- imageClass: "w-16 h-9",
230
- title: d.title,
231
- meta: [d.channel, ut(d.durationS)],
232
- state: y[`yt:${d.id}`],
233
- canAdd: !0,
234
- addLabel: `Request ${d.title}`,
235
- onAdd: () => {
236
- R(d);
237
- }
238
- },
239
- d.id
240
- )) })
241
- ] });
242
- }
243
- function Ne({ label: e, answer: r, testId: t, children: l }) {
244
- if (r?.ok && r.value.length === 0) return null;
245
- const o = r && !r.ok && r.status !== "aborted";
246
- return /* @__PURE__ */ u("div", { "data-testid": t, "data-loaded": r ? "true" : "false", children: [
247
- /* @__PURE__ */ u("div", { className: "px-3 pt-2 pb-1 flex items-center gap-2 text-[10px] uppercase tracking-wider text-text-muted", children: [
248
- /* @__PURE__ */ a("span", { children: e }),
249
- !r && /* @__PURE__ */ a("span", { className: "text-text-tertiary", children: "…" }),
250
- o && /* @__PURE__ */ a("span", { className: "bg-danger-bg text-on-danger-bg px-1 rounded normal-case", children: "unavailable" })
251
- ] }),
252
- l
253
- ] });
254
- }
255
- function ke({ testId: e, image: r, imageClass: t, title: l, meta: o, state: s, canAdd: g, addLabel: c, onAdd: y }) {
256
- const v = o.filter((h) => h !== null && h !== "").join(" · ");
257
- return /* @__PURE__ */ u("div", { className: "px-3 py-1 flex items-center gap-2", "data-testid": e, "data-state": s ?? "", children: [
258
- r ? /* @__PURE__ */ a("img", { src: r, alt: "", loading: "lazy", className: `${t} object-cover rounded shrink-0 bg-bg-alt` }) : /* @__PURE__ */ a("div", { className: `${t} rounded shrink-0 bg-bg-alt` }),
259
- /* @__PURE__ */ u("div", { className: "flex-1 min-w-0", children: [
260
- /* @__PURE__ */ a("div", { className: "text-[11px] text-text truncate", title: l, children: l }),
261
- v && /* @__PURE__ */ a("div", { className: "text-[10px] text-text-muted truncate", children: v })
262
- ] }),
263
- s === "added" ? /* @__PURE__ */ a(ne, { name: "check", variant: "tint", family: "success", round: !0, className: "shrink-0" }) : s && s !== "adding" ? /* @__PURE__ */ a("span", { className: "text-[10px] bg-danger-bg text-on-danger-bg px-1 rounded shrink-0", children: s }) : g && /* @__PURE__ */ a(
264
- "button",
265
- {
266
- onClick: y,
267
- disabled: s === "adding",
268
- "aria-label": c,
269
- className: "drawer-btn shrink-0",
270
- children: /* @__PURE__ */ a(ne, { name: "plus" })
271
- }
272
- )
273
- ] });
274
- }
275
- const bt = 4e3, xt = {
276
- pending: "bg-neutral-bg text-on-neutral-bg",
277
- approved: "bg-primary-bg text-on-primary-bg",
278
- fetching: "bg-primary-bg text-on-primary-bg",
279
- ingesting: "bg-primary-bg text-on-primary-bg",
280
- ready: "bg-success-bg text-on-success-bg",
281
- rejected: "bg-danger-bg text-on-danger-bg",
282
- failed: "bg-danger-bg text-on-danger-bg"
283
- };
284
- function yt({ item: e }) {
285
- const r = e, t = e.title || e.source;
286
- return /* @__PURE__ */ u(
287
- "div",
288
- {
289
- className: "px-3 py-1.5 border-b border-border/50 flex items-center gap-2",
290
- "data-submission-id": e.id,
291
- "data-submission-state": e.state,
292
- "data-submission-kind": e.kind,
293
- "data-submission-series": r.series ?? "",
294
- children: [
295
- /* @__PURE__ */ a("span", { className: "text-[10px] uppercase tracking-wider text-text-muted shrink-0", children: e.kind }),
296
- /* @__PURE__ */ a("span", { className: "text-[11px] text-text-tertiary truncate flex-1", title: e.source, children: r.series ? `${r.series} — ${t}` : t }),
297
- /* @__PURE__ */ a("span", { className: `text-[10px] uppercase tracking-wider px-1 rounded ${xt[e.state] ?? "text-text-muted"}`, children: e.state }),
298
- e.error && /* @__PURE__ */ a("span", { className: "text-[10px] bg-danger-bg text-on-danger-bg px-1 rounded truncate max-w-[10rem]", children: e.error })
299
- ]
300
- }
301
- );
302
- }
303
- function vt({ open: e, onClose: r }) {
304
- const [t, l] = x(""), [o, s] = x(""), [g, c] = x(""), [y, v] = x(!1), [h, $] = x([]), [A, R] = x(!1), [U, d] = x(null), [N, f] = x(null), [k, W] = x(null), [I, z] = x(null), [T, D] = x(null), [P, _] = x(!1), [V, n] = x(null), [p, m] = x(null), w = ue(null), S = ue(!1), L = Se(() => pe(t), [t]) === "magnet" && t.trim().length > 0, le = Me(t), B = Ce(() => We().then((i) => {
305
- S.current && (i.ok ? (f(i.value), W(null)) : W(i.failure));
306
- }), []);
307
- O(() => {
308
- if (!e) return;
309
- S.current = !0, B();
310
- const i = setInterval(() => {
311
- B();
312
- }, bt);
313
- return () => {
314
- S.current = !1, clearInterval(i);
315
- };
316
- }, [e, B]), O(() => {
317
- if (!e) return;
318
- let i = !0;
319
- return me().then((b) => {
320
- i && b.ok && z(b.value);
321
- }), () => {
322
- i = !1;
323
- };
324
- }, [e]), O(() => {
325
- if (!e) return;
326
- let i = !0;
327
- return Q("output/fmp4/anime").then((b) => {
328
- i && $(b.dirs.map((C) => C.name));
329
- }).catch((b) => {
330
- E("media", "series suggestions unavailable", H(b));
331
- }), () => {
332
- i = !1;
333
- };
334
- }, [e]);
335
- function Re(i) {
336
- if (l(i), pe(i) === "magnet" && i.trim().length > 0 && !y) {
337
- const C = Je(i);
338
- C && c(C);
339
- }
340
- }
341
- function Te() {
342
- l(""), s(""), c(""), v(!1);
343
- }
344
- const oe = !L || o.trim().length > 0 && ze(g);
345
- async function de() {
346
- const i = t.trim();
347
- if (!i || A || le || L && !oe) return;
348
- R(!0), d(null);
349
- const b = L ? await Ke(i, { title: g.trim() || void 0, series: o.trim() }) : await Ae(i);
350
- if (R(!1), !b.ok) {
351
- d(b.failure);
352
- return;
353
- }
354
- E("submissions", "Submit accepted", { id: b.value.id, state: b.value.state }), Te(), f((C) => [b.value, ...(C ?? []).filter((M) => M.id !== b.value.id)]), B();
355
- }
356
- function _e(i) {
357
- const b = i.target.files?.[0] ?? null;
358
- if (m(null), b && I && b.size > I.maxBytes) {
359
- D(null), m({ code: "too_large", maxBytes: I.maxBytes }), w.current && (w.current.value = "");
360
- return;
361
- }
362
- D(b);
363
- }
364
- async function Ue() {
365
- if (!T || P) return;
366
- _(!0), m(null);
367
- let i = I;
368
- if (!i) {
369
- const M = await me();
370
- if (!M.ok) {
371
- _(!1), m(M.failure);
372
- return;
373
- }
374
- i = M.value, z(i);
375
- }
376
- if (T.size > i.maxBytes) {
377
- _(!1), m({ code: "too_large", maxBytes: i.maxBytes });
378
- return;
379
- }
380
- const b = await Qe(T, i, (M, Z, Fe) => {
381
- n({ sent: M, total: Z, phase: Fe });
382
- });
383
- if (!b.ok) {
384
- _(!1), n(null), m(b.failure);
385
- return;
386
- }
387
- const C = await Xe(b.value.fileId, T.name);
388
- if (_(!1), n(null), !C.ok) {
389
- m(C.failure);
390
- return;
391
- }
392
- E("submissions", "Upload accepted", { id: C.value.id }), D(null), w.current && (w.current.value = ""), f((M) => [C.value, ...(M ?? []).filter((Z) => Z.id !== C.value.id)]), B();
393
- }
394
- const ce = p ? "maxBytes" in p || p.code === "target_unavailable" || p.code === "upload_failed" ? Ve(p) : Y(p) : null;
395
- return /* @__PURE__ */ u(
396
- Ee,
397
- {
398
- open: e,
399
- onClose: r,
400
- header: /* @__PURE__ */ u(j, { children: [
401
- /* @__PURE__ */ a("span", { className: "text-xs uppercase tracking-wider text-text-muted", children: "Add media" }),
402
- /* @__PURE__ */ a(
403
- "button",
404
- {
405
- onClick: () => {
406
- B();
407
- },
408
- "aria-label": "Refresh submissions",
409
- className: `${re} ml-auto`,
410
- children: /* @__PURE__ */ a(ne, { name: "refresh" })
411
- }
412
- )
413
- ] }),
414
- children: [
415
- /* @__PURE__ */ u("div", { className: "p-3 border-b border-border flex flex-col gap-2", children: [
416
- /* @__PURE__ */ a(
417
- "input",
418
- {
419
- value: t,
420
- onChange: (i) => Re(i.target.value),
421
- onKeyDown: (i) => {
422
- i.key === "Enter" && !L && de();
423
- },
424
- "aria-label": "Media link",
425
- placeholder: "search, https:// or magnet:",
426
- className: "w-full bg-bg-alt border border-border focus:border-border-light outline-none px-2 py-1 rounded text-xs"
427
- }
428
- ),
429
- L && /* @__PURE__ */ u("div", { className: "flex flex-col gap-1.5", "data-testid": "magnet-fields", children: [
430
- /* @__PURE__ */ a(
431
- "input",
432
- {
433
- value: o,
434
- onChange: (i) => s(i.target.value),
435
- list: "add-media-series-options",
436
- "aria-label": "Series",
437
- placeholder: "series",
438
- className: "w-full bg-bg-alt border border-border focus:border-border-light outline-none px-2 py-1 rounded text-xs"
439
- }
440
- ),
441
- /* @__PURE__ */ a("datalist", { id: "add-media-series-options", children: h.map((i) => /* @__PURE__ */ a("option", { value: i }, i)) }),
442
- /* @__PURE__ */ a(
443
- "input",
444
- {
445
- value: g,
446
- onChange: (i) => {
447
- v(!0), c(i.target.value);
448
- },
449
- "aria-label": "Title",
450
- placeholder: "title (SxxExx)",
451
- className: "w-full bg-bg-alt border border-border focus:border-border-light outline-none px-2 py-1 rounded text-xs"
452
- }
453
- )
454
- ] }),
455
- /* @__PURE__ */ u("div", { className: "flex items-center gap-2", children: [
456
- /* @__PURE__ */ a(
457
- "button",
458
- {
459
- onClick: () => {
460
- de();
461
- },
462
- disabled: A || !t.trim() || le || L && !oe,
463
- "aria-label": "Submit media",
464
- className: "drawer-btn drawer-btn-primary",
465
- children: A ? "Submitting…" : "Submit"
466
- }
467
- ),
468
- U && /* @__PURE__ */ a("span", { "data-testid": "submit-failure", className: "text-[11px] bg-danger-bg text-on-danger-bg px-1.5 py-0.5 rounded", children: Y(U) })
469
- ] })
470
- ] }),
471
- /* @__PURE__ */ a(
472
- ht,
473
- {
474
- query: t,
475
- onSubmitted: (i) => f((b) => [i, ...(b ?? []).filter((C) => C.id !== i.id)])
476
- }
477
- ),
478
- /* @__PURE__ */ u("div", { className: "p-3 border-b border-border flex flex-col gap-2", children: [
479
- /* @__PURE__ */ u("div", { className: "flex items-center gap-2", children: [
480
- /* @__PURE__ */ a(
481
- "input",
482
- {
483
- ref: w,
484
- type: "file",
485
- onChange: _e,
486
- "aria-label": "Upload file",
487
- className: "text-[11px] flex-1 min-w-0"
488
- }
489
- ),
490
- /* @__PURE__ */ a(
491
- "button",
492
- {
493
- onClick: () => {
494
- Ue();
495
- },
496
- disabled: !T || P,
497
- "aria-label": "Upload",
498
- className: "drawer-btn drawer-btn-primary",
499
- children: P ? V?.phase === "assembling" ? "Assembling…" : "Uploading…" : "Upload"
500
- }
501
- )
502
- ] }),
503
- /* @__PURE__ */ a("span", { className: "text-[10px] text-text-muted", "data-testid": "upload-cap", children: I ? `cap ${Ye(I.maxBytes)}` : "" }),
504
- ce && /* @__PURE__ */ a("span", { "data-testid": "upload-failure", className: "text-[11px] bg-danger-bg text-on-danger-bg px-1.5 py-0.5 rounded", children: ce })
505
- ] }),
506
- /* @__PURE__ */ u("div", { className: "flex-1 overflow-auto", "data-testid": "my-submissions", children: [
507
- k && /* @__PURE__ */ a("div", { className: "mx-3 my-2 px-3 py-2 text-[11px] bg-danger-bg text-on-danger-bg rounded", children: Y(k) }),
508
- N?.map((i) => /* @__PURE__ */ a(yt, { item: i }, i.id))
509
- ] })
510
- ]
511
- }
512
- );
513
- }
514
- const wt = { bomberman: et };
515
- function Le() {
516
- $e(null), E("game", "game_select", { gameId: null });
517
- }
518
- function Nt({ onDone: e }) {
519
- const r = ie((s) => s.activeGameId), t = Pe();
520
- function l(s) {
521
- $e(s), E("game", "game_select", { gameId: s }), e();
522
- }
523
- function o() {
524
- Le(), e();
525
- }
526
- return /* @__PURE__ */ a("div", { className: "flex-1 overflow-auto p-3", children: /* @__PURE__ */ a("div", { className: "grid grid-cols-2 gap-3", children: t.map((s) => {
527
- const g = r === s.id, c = s.cover ?? wt[s.id];
528
- return /* @__PURE__ */ u(
529
- "button",
530
- {
531
- onClick: () => g ? o() : l(s.id),
532
- className: `group relative aspect-square overflow-hidden rounded-lg border-2 transition-all cursor-pointer ${g ? "border-primary shadow-lg shadow-primary/30" : "border-border hover:border-primary"}`,
533
- title: g ? `Stop ${s.name}` : `Start ${s.name}`,
534
- children: [
535
- c ? /* @__PURE__ */ a(
536
- "img",
537
- {
538
- src: c,
539
- alt: s.name,
540
- className: "absolute inset-0 w-full h-full object-cover transition-transform group-hover:scale-105"
541
- }
542
- ) : /* @__PURE__ */ a("div", { className: "absolute inset-0 bg-bg-alt" }),
543
- /* @__PURE__ */ a("div", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/85 via-black/40 to-transparent px-2 pt-6 pb-2", children: /* @__PURE__ */ a("div", { className: "text-sm font-medium text-white leading-tight", children: s.name }) }),
544
- g && /* @__PURE__ */ a("span", { className: "absolute top-2 left-2 px-1.5 py-0.5 bg-primary text-[10px] uppercase tracking-wide rounded text-white", children: "Active" })
545
- ]
546
- },
547
- s.id
548
- );
549
- }) }) });
550
- }
551
- function kt({ onDone: e }) {
552
- return ie((t) => t.activeGameId) ? /* @__PURE__ */ a("button", { onClick: () => {
553
- Le(), e();
554
- }, className: "drawer-btn drawer-btn-danger ml-auto", children: "Stop game" }) : null;
555
- }
556
- const G = (e) => e.type === "category" ? e.series : [], ae = (e, r) => e.type === "category" ? { ...e, series: r } : e;
557
- function Lt({ open: e, onClose: r }) {
558
- const [t, l] = x(!1), [o, s] = x(null), [g, c] = x([]), [y, v] = x(""), [h, $] = x(!1), [A, R] = x("watch"), U = ie((n) => n.snapshot.mediaId), d = Ze(), N = Ce(() => ve(Q).then((n) => {
559
- c((p) => be(n, he(p, G), G, ae)), K() && rt();
560
- }).catch((n) => {
561
- s(n instanceof Error && n.message || "catalog_error");
562
- }).finally(() => l(!1)), []), f = je();
563
- O(() => {
564
- if (!f) return;
565
- let n = !0;
566
- return ve(Q).then(
567
- (p) => {
568
- n && c((m) => be(p, he(m, G), G, ae));
569
- },
570
- (p) => q("catalog", "Catalog prefetch failed", H(p))
571
- ), () => {
572
- n = !1;
573
- };
574
- }, [f]);
575
- const [k, W] = x(!1);
576
- e !== k && (W(e), e && (R("watch"), c((n) => dt(n, G, ae)), l(!0), s(null))), O(() => {
577
- e && N();
578
- }, [e, N]);
579
- function I(n) {
580
- qe(n), E("catalog", "control_load", { mediaId: n }), r();
581
- }
582
- function z(n) {
583
- Ge(n), E("catalog", "playlist_load", { playlistId: n }), r();
584
- }
585
- function T() {
586
- He(), E("controls", "Unload media clicked (media drawer)"), r();
587
- }
588
- function D(n, p) {
589
- c((m) => m.map((w, S) => S !== n || !("series" in w) ? w : { ...w, series: w.series.map((F, L) => L !== p ? F : { ...F, expanded: !F.expanded }) }));
590
- }
591
- function P(n) {
592
- c((p) => p.map((m, w) => w !== n ? m : { ...m, expanded: !m.expanded }));
593
- }
594
- function _() {
595
- at(), l(!0), s(null), N();
596
- }
597
- const V = Se(() => {
598
- if (!y.trim()) return g;
599
- const n = y.toLowerCase();
600
- return g.map((p) => {
601
- if (p.type === "playlist_category") {
602
- const w = p.playlists.filter((S) => S.name.toLowerCase().includes(n));
603
- return w.length ? { ...p, playlists: w, expanded: !0 } : p.name.toLowerCase().includes(n) ? { ...p, expanded: !0 } : null;
604
- }
605
- const m = p.series.map((w) => {
606
- const S = w.episodes.filter((F) => F.name.toLowerCase().includes(n));
607
- return S.length ? { ...w, episodes: S, expanded: !0 } : w.name.toLowerCase().includes(n) ? { ...w, expanded: !0 } : null;
608
- }).filter(Boolean);
609
- return m.length ? { ...p, series: m, expanded: !0 } : p.name.toLowerCase().includes(n) ? { ...p, expanded: !0 } : null;
610
- }).filter(Boolean);
611
- }, [g, y]);
612
- return /* @__PURE__ */ u(j, { children: [
613
- /* @__PURE__ */ a(vt, { open: h, onClose: () => $(!1) }),
614
- /* @__PURE__ */ a(
615
- Ee,
616
- {
617
- open: e && !h,
618
- onClose: r,
619
- header: /* @__PURE__ */ u(j, { children: [
620
- /* @__PURE__ */ a("div", { className: "tl-tabs", role: "tablist", "aria-label": "Media drawer", children: ["watch", "play"].map((n) => /* @__PURE__ */ a(
621
- "button",
622
- {
623
- role: "tab",
624
- "aria-selected": A === n,
625
- onClick: () => R(n),
626
- className: "tl-tab",
627
- children: n === "watch" ? "Watch" : "Play"
628
- },
629
- n
630
- )) }),
631
- A === "play" && /* @__PURE__ */ a(kt, { onDone: r }),
632
- A === "watch" && /* @__PURE__ */ u(j, { children: [
633
- d && /* @__PURE__ */ a(
634
- "button",
635
- {
636
- onClick: () => $(!0),
637
- "aria-label": "Add Media",
638
- className: re,
639
- children: "Add"
640
- }
641
- ),
642
- U && /* @__PURE__ */ a(
643
- "button",
644
- {
645
- onClick: T,
646
- title: "Unload the current media for everyone in the room",
647
- className: "drawer-btn drawer-btn-primary",
648
- children: "Unload"
649
- }
650
- ),
651
- /* @__PURE__ */ u("button", { onClick: _, disabled: t, className: `${re} inline-flex items-center gap-1.5`, children: [
652
- t && /* @__PURE__ */ a("span", { className: "inline-block w-3 h-3 border-2 border-text-muted border-t-transparent rounded-full animate-spin" }),
653
- t ? "Reloading…" : "Reload"
654
- ] }),
655
- /* @__PURE__ */ a("input", { value: y, onChange: (n) => v(n.target.value), placeholder: "search", className: "ml-auto bg-bg-alt border border-border focus:border-border-light outline-none px-2 py-1 rounded text-xs" })
656
- ] })
657
- ] }),
658
- footer: K() ? /* @__PURE__ */ a("div", { className: "p-2 text-[10px] text-text-muted border-t border-border flex justify-end", children: /* @__PURE__ */ a("span", { className: "opacity-70", children: "verbose" }) }) : void 0,
659
- children: A === "play" ? /* @__PURE__ */ a(Nt, { onDone: r }) : /* @__PURE__ */ u(j, { children: [
660
- t && !g.length && /* @__PURE__ */ a("div", { className: "px-4 py-2 text-xs text-text-muted", children: "Loading catalog…" }),
661
- o && /* @__PURE__ */ a("div", { className: "mx-3 px-3 py-2 text-xs bg-danger-bg text-on-danger-bg rounded", children: o }),
662
- /* @__PURE__ */ u("div", { className: "flex-1 overflow-auto text-sm py-2", children: [
663
- V.map((n, p) => /* @__PURE__ */ u("div", { className: "px-3 pb-3", children: [
664
- /* @__PURE__ */ u(
665
- "div",
666
- {
667
- className: "flex items-center gap-2 cursor-pointer select-none group",
668
- "data-category": n.name,
669
- "data-expanded": n.expanded ? "true" : "false",
670
- onClick: () => P(p),
671
- children: [
672
- /* @__PURE__ */ a("span", { className: "text-xs uppercase tracking-wider text-text-muted group-hover:text-text-secondary", children: n.name }),
673
- /* @__PURE__ */ a("span", { className: "text-[10px] opacity-50", children: n.expanded ? "−" : "+" })
674
- ]
675
- }
676
- ),
677
- n.expanded && n.type === "playlist_category" && /* @__PURE__ */ u("div", { className: "mt-2 space-y-1", children: [
678
- n.playlists.map((m) => /* @__PURE__ */ u("button", { onClick: () => z(m.playlistId), className: "w-full text-left px-2 py-1 rounded hover:bg-bg-alt flex items-center gap-2 group", children: [
679
- /* @__PURE__ */ a("span", { className: "text-[11px] text-text-tertiary group-hover:text-text-secondary truncate", children: m.name }),
680
- /* @__PURE__ */ a("span", { className: "ml-auto text-[10px] text-text underline decoration-primary underline-offset-2 opacity-0 group-hover:opacity-100 transition-opacity", children: "Load" })
681
- ] }, m.playlistId)),
682
- n.playlists.length === 0 && /* @__PURE__ */ a("div", { className: "text-[11px] text-text-muted italic", children: "(no playlists)" })
683
- ] }),
684
- n.expanded && n.type === "category" && /* @__PURE__ */ u("div", { className: "mt-2 space-y-2", children: [
685
- n.series.map((m, w) => /* @__PURE__ */ u("div", { children: [
686
- /* @__PURE__ */ u("div", { className: "flex items-center gap-2 cursor-pointer text-text-secondary hover:text-text", onClick: () => D(p, w), children: [
687
- /* @__PURE__ */ a("span", { className: "text-xs font-medium", children: m.name }),
688
- /* @__PURE__ */ a("span", { className: "text-[10px] opacity-40", children: m.expanded ? "−" : "+" })
689
- ] }),
690
- m.expanded && /* @__PURE__ */ u("div", { className: "mt-1 ml-3 border-l border-border pl-3 space-y-1", children: [
691
- m.episodes.map((S) => /* @__PURE__ */ u("button", { onClick: () => I(S.mediaId), className: "w-full text-left px-2 py-1 rounded hover:bg-bg-alt flex items-center gap-2 group", children: [
692
- /* @__PURE__ */ a("span", { className: "text-[11px] text-text-tertiary group-hover:text-text-secondary truncate", children: S.name }),
693
- /* @__PURE__ */ a("span", { className: "ml-auto text-[10px] text-text underline decoration-primary underline-offset-2 opacity-0 group-hover:opacity-100 transition-opacity", children: "Load" })
694
- ] }, S.mediaId)),
695
- m.episodes.length === 0 && /* @__PURE__ */ a("div", { className: "text-[11px] text-text-muted italic", children: "(no episodes)" })
696
- ] })
697
- ] }, m.name)),
698
- n.series.length === 0 && /* @__PURE__ */ a("div", { className: "text-[11px] text-text-muted italic", children: "(empty)" })
699
- ] })
700
- ] }, n.name)),
701
- !t && !V.length && /* @__PURE__ */ a("div", { className: "px-4 py-6 text-center text-xs text-text-muted", children: "No matches" })
702
- ] })
703
- ] })
704
- }
705
- )
706
- ] });
707
- }
708
- export {
709
- Lt as default
710
- };
711
- //# sourceMappingURL=MediaDrawer-yoaRrRtv.js.map