@hadoku/watchparty-ui 0.44.13 → 0.44.16

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-BmZPZ-JU.js → AdminPanel-CJtacvRu.js} +13 -13
  2. package/dist/chunks/{AdminPanel-BmZPZ-JU.js.map → AdminPanel-CJtacvRu.js.map} +1 -1
  3. package/dist/chunks/{GameModule-k9Cccia2.js → GameModule-BvqNQcVk.js} +158 -158
  4. package/dist/chunks/{GameModule-k9Cccia2.js.map → GameModule-BvqNQcVk.js.map} +1 -1
  5. package/dist/chunks/{MapEditor-ChMOAaVW.js → MapEditor-Bc0_hjZQ.js} +2 -2
  6. package/dist/chunks/{MapEditor-ChMOAaVW.js.map → MapEditor-Bc0_hjZQ.js.map} +1 -1
  7. package/dist/chunks/{MediaDrawer-C7esBn07.js → MediaDrawer-CgoGUHwV.js} +39 -39
  8. package/dist/chunks/{MediaDrawer-C7esBn07.js.map → MediaDrawer-CgoGUHwV.js.map} +1 -1
  9. package/dist/chunks/{PlaylistPanel-C17X31ES.js → PlaylistPanel-BmKnGMqa.js} +27 -27
  10. package/dist/chunks/{PlaylistPanel-C17X31ES.js.map → PlaylistPanel-BmKnGMqa.js.map} +1 -1
  11. package/dist/chunks/{Room-gmERzNR6.js → Room-CoE8L9aw.js} +1841 -1871
  12. package/dist/chunks/Room-CoE8L9aw.js.map +1 -0
  13. package/dist/chunks/{index-CR-0msod.js → index-Buah62TP.js} +2625 -2457
  14. package/dist/chunks/index-Buah62TP.js.map +1 -0
  15. package/dist/chunks/{submissionsApi-DsHETYSv.js → submissionsApi-DXsG0ItW.js} +15 -15
  16. package/dist/chunks/{submissionsApi-DsHETYSv.js.map → submissionsApi-DXsG0ItW.js.map} +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/room-chrome.css +1 -1
  19. package/dist/room-chrome.d.ts +1 -1
  20. package/dist/room-chrome.js +1293 -1397
  21. package/dist/room-chrome.js.map +1 -1
  22. package/dist/watchparty-ui.css +1 -1
  23. package/package.json +2 -2
  24. package/dist/chunks/CharacterModal-CCb4K-A7.js +0 -158
  25. package/dist/chunks/CharacterModal-CCb4K-A7.js.map +0 -1
  26. package/dist/chunks/Room-gmERzNR6.js.map +0 -1
  27. package/dist/chunks/index-CR-0msod.js.map +0 -1
@@ -1,27 +1,32 @@
1
- import { jsx as l, jsxs as b, Fragment as Ne } from "react/jsx-runtime";
2
- import Bt, { useState as P, useRef as J, useMemo as Q, useCallback as N, useEffect as O, createContext as Gt, useContext as Wt, useId as On, useEffectEvent as Kt } from "react";
3
- import { themePrefs as ce, Icon as Rn, THEMES as Dn, useHadokuThemeOptional as Ln } from "@wolffm/themes";
1
+ import { jsx as c, jsxs as v, Fragment as le } from "react/jsx-runtime";
2
+ import $t, { useState as R, useRef as z, useMemo as Q, useCallback as E, useEffect as D, createContext as Bt, useContext as Pn, useId as Nn, useEffectEvent as Gt } from "react";
3
+ import { themePrefs as ae, Icon as Rn, THEMES as Dn, useHadokuThemeOptional as On } from "@wolffm/themes";
4
4
  import { z as r } from "zod";
5
- import { createCdnClient as jn, kindOfType as Un, MAX_BYTES as Fn, measureImage as Hn, filesFromClipboard as $n, filesFromDrop as Bn, CdnError as ze } from "@wolffm/cdn-client";
6
- import { create as Gn } from "zustand";
7
- import { createPrefsClient as Wn, getDeviceId as Kn, DEVICE_ID_KEY as qn } from "@wolffm/prefs-client";
8
- import { io as Vn } from "socket.io-client";
9
- import { resolveIdentityReassert as zn } from "@wolffm/rooms-protocol";
5
+ import { createCdnClient as Mn, kindOfType as Ln, MAX_BYTES as jn, measureImage as Un, filesFromClipboard as Fn, filesFromDrop as Hn, CdnError as qe } from "@wolffm/cdn-client";
6
+ import { create as $n } from "zustand";
7
+ import { createPrefsClient as Bn, getDeviceId as Gn, DEVICE_ID_KEY as Kn } from "@wolffm/prefs-client";
8
+ import { io as Wn } from "socket.io-client";
9
+ import { createAvatarClient as qn, AvatarError as ht } from "@wolffm/prefs-client/avatar";
10
+ import { resolveIdentityReassert as Vn } from "@wolffm/rooms-protocol";
10
11
  import { createPortal as Yn } from "react-dom";
11
- import { useCatalogueSearch as Xn } from "@wolffm/cdn-client/react";
12
- const qt = "https://cdn.hadoku.me", Vt = /^[0-9a-f]{64}\.(?:png|jpe?g|gif|webp|avif)$/;
13
- function et(e) {
14
- return typeof e == "string" && Vt.test(e);
15
- }
16
- const tt = 8080, zt = 5173, Jn = "/watchparty/ws", Yt = 500, Zn = 1e3, Y = 4, Xt = typeof process < "u" && process.env ? process.env : {};
17
- Xt.PRODUCTION_UI_URL;
18
- Xt.PRODUCTION_SERVER_URL;
19
- const Ce = r.enum(["S", "A", "B", "C", "D", "F"]), bt = 2e4, Qn = 300;
12
+ import { useCatalogueSearch as zn } from "@wolffm/cdn-client/react";
13
+ const Kt = "https://cdn.hadoku.me", Wt = /^[0-9a-f]{64}\.(?:png|jpe?g|gif|webp|avif)$/;
14
+ function Je(e) {
15
+ return typeof e == "string" && Wt.test(e);
16
+ }
17
+ const Qe = 8080, qt = 5173, Xn = "/watchparty/ws", Vt = 500, Jn = 1e3, V = 4, Yt = typeof process < "u" && process.env ? process.env : {};
18
+ Yt.PRODUCTION_UI_URL;
19
+ Yt.PRODUCTION_SERVER_URL;
20
+ const xe = r.enum(["S", "A", "B", "C", "D", "F"]), gt = 2e4, Qn = 300;
20
21
  r.object({
21
22
  name: r.string().min(1).max(32).optional(),
22
- sprite: r.string().min(1).max(32).optional()
23
+ sprite: r.string().min(1).max(32).optional(),
24
+ /** "My avatar settings just changed — re-read my picture" (the fleet
25
+ * settings popout's `hadoku:avatar` event). Carries no picture: the server
26
+ * resolves it from prefs-api by registry userId, never from the client. */
27
+ avatar: r.literal(!0).optional()
23
28
  });
24
- const I = {
29
+ const w = {
25
30
  JOIN: "join",
26
31
  SNAPSHOT: "snapshot",
27
32
  CONTROL_ACK: "control_ack",
@@ -39,7 +44,7 @@ const I = {
39
44
  // 'ready_state' here = playback-readiness gate (distinct from READY_EVENTS.STATE = "ready check" UX feature)
40
45
  READY_STATE: "ready_state",
41
46
  ERROR: "error"
42
- }, er = r.enum(["play", "pause", "seek", "load", "home"]), tr = r.object({
47
+ }, Zn = r.enum(["play", "pause", "seek", "load", "home"]), er = r.object({
43
48
  framed: r.boolean(),
44
49
  depth: r.number().int().nonnegative().max(10)
45
50
  });
@@ -47,7 +52,7 @@ r.object({
47
52
  room_key: r.string(),
48
53
  name: r.string().min(1).max(32).optional(),
49
54
  sprite: r.string().min(1).max(32).optional(),
50
- embed: tr.optional(),
55
+ embed: er.optional(),
51
56
  /** Which media-delivery path this client resolved to ('range' | 'fragment').
52
57
  * Optional so older clients still validate. Reported so a party's logs can be split by
53
58
  * arm — without it an A/B rollout is unmeasurable, exactly as entry path was before
@@ -95,13 +100,13 @@ r.object({
95
100
  });
96
101
  r.object({
97
102
  client_seq: r.number().int().positive(),
98
- kind: er,
103
+ kind: Zn,
99
104
  to_ms: r.number().int().nonnegative().optional(),
100
105
  media_id: r.string().optional()
101
106
  // leader_id removed - not needed since set_leader control was removed
102
107
  });
103
- const Jt = r.object({
104
- key: r.string().regex(Vt, "not a CDN image key"),
108
+ const zt = r.object({
109
+ key: r.string().regex(Wt, "not a CDN image key"),
105
110
  /**
106
111
  * Intrinsic pixels, or **0 meaning "unknown"**.
107
112
  *
@@ -114,8 +119,8 @@ const Jt = r.object({
114
119
  * text went with it, with nothing said. Widening is backward compatible —
115
120
  * every message already in history carries positive dimensions.
116
121
  */
117
- w: r.number().int().nonnegative().max(bt),
118
- h: r.number().int().nonnegative().max(bt),
122
+ w: r.number().int().nonnegative().max(gt),
123
+ h: r.number().int().nonnegative().max(gt),
119
124
  /**
120
125
  * Human-readable description, rendered as the `alt` text.
121
126
  *
@@ -132,11 +137,11 @@ const Jt = r.object({
132
137
  title: r.string().max(Qn).optional()
133
138
  });
134
139
  r.object({
135
- text: r.string().max(Yt),
140
+ text: r.string().max(Vt),
136
141
  style: r.string().max(64).optional(),
137
- images: r.array(Jt).max(Y).optional()
142
+ images: r.array(zt).max(V).optional()
138
143
  }).refine((e) => e.text.length > 0 || (e.images?.length ?? 0) > 0, { message: "message must carry text or at least one image" });
139
- const nr = r.enum(["join", "leave", "media_change", "identity_change", "recap"]);
144
+ const tr = r.enum(["join", "leave", "media_change", "identity_change", "recap"]);
140
145
  r.object({
141
146
  id: r.string(),
142
147
  client_id: r.string(),
@@ -149,11 +154,11 @@ r.object({
149
154
  ts: r.number(),
150
155
  // Optional so a client on an older bundle still validates a message that
151
156
  // has none, and so system messages (which never carry images) stay unchanged.
152
- images: r.array(Jt).max(Y).optional(),
157
+ images: r.array(zt).max(V).optional(),
153
158
  isSystem: r.boolean().optional(),
154
- systemType: nr.optional()
159
+ systemType: tr.optional()
155
160
  });
156
- const rr = r.object({
161
+ const nr = r.object({
157
162
  avg_download_rate_bps: r.number().nonnegative(),
158
163
  buffered_ahead_ms: r.number().nonnegative(),
159
164
  rtt_ms: r.number().nonnegative(),
@@ -195,7 +200,7 @@ const rr = r.object({
195
200
  * socket shows here as a growing number while everything else looks fine. */
196
201
  pong_age_ms: r.number().int().nonnegative().optional(),
197
202
  reported_at: r.number().int().nonnegative()
198
- }), or = rr.extend({
203
+ }), rr = nr.extend({
199
204
  client_id: r.string(),
200
205
  name: r.string(),
201
206
  sprite_id: r.string().nullable(),
@@ -206,10 +211,10 @@ const rr = r.object({
206
211
  stale_ms: r.number().int().nonnegative().optional()
207
212
  });
208
213
  r.object({
209
- clients: r.array(or),
214
+ clients: r.array(rr),
210
215
  server_ts: r.number().int().nonnegative()
211
216
  });
212
- const ir = r.object({
217
+ const or = r.object({
213
218
  client_id: r.string(),
214
219
  sprite_id: r.string().nullable(),
215
220
  name: r.string(),
@@ -227,13 +232,13 @@ const ir = r.object({
227
232
  video_ready_state: r.number().int().default(-1)
228
233
  });
229
234
  r.object({
230
- clients: r.array(ir),
235
+ clients: r.array(or),
231
236
  server_ts: r.number().int().nonnegative()
232
237
  });
233
238
  r.object({
234
239
  media_id: r.string()
235
240
  });
236
- const nt = r.enum(["super_upvote", "upvote", "mid", "downvote"]), ar = r.object({
241
+ const Ze = r.enum(["super_upvote", "upvote", "mid", "downvote"]), ar = r.object({
237
242
  mediaId: r.string(),
238
243
  displayName: r.string(),
239
244
  // Enrichment fields — present for flat-playlist ingest output, absent for
@@ -244,10 +249,10 @@ const nt = r.enum(["super_upvote", "upvote", "mid", "downvote"]), ar = r.object(
244
249
  durationMs: r.number().optional(),
245
250
  uploadDate: r.string().optional()
246
251
  // YouTube upload date 'YYYY-MM-DD'; drives playlist order
247
- }), sr = r.enum(["basic", "vote"]), Zt = r.object({
252
+ }), ir = r.enum(["basic", "vote"]), Xt = r.object({
248
253
  id: r.string(),
249
254
  name: r.string(),
250
- type: sr.default("vote"),
255
+ type: ir.default("vote"),
251
256
  videos: r.array(ar),
252
257
  cover: r.string().optional()
253
258
  // media-relative path to the playlist cover image
@@ -257,28 +262,28 @@ r.object({ index: r.number().int().min(0) });
257
262
  r.object({
258
263
  playlistId: r.string(),
259
264
  mediaId: r.string(),
260
- vote: nt
265
+ vote: Ze
261
266
  });
262
267
  r.object({ voterKey: r.string().min(1).max(128) });
263
268
  r.object({ playlistId: r.string(), mediaIds: r.array(r.string()) });
264
269
  r.object({
265
- playlist: Zt.nullable(),
270
+ playlist: Xt.nullable(),
266
271
  activeIndex: r.number().int().nullable(),
267
- myVotes: r.record(r.string(), nt)
272
+ myVotes: r.record(r.string(), Ze)
268
273
  // mediaId -> VoteValue
269
274
  });
270
- const lr = r.object({
275
+ const sr = r.object({
271
276
  clientId: r.string(),
272
277
  name: r.string(),
273
278
  votedCount: r.number().int().nonnegative(),
274
279
  totalVideos: r.number().int().nonnegative()
275
280
  });
276
281
  r.object({
277
- progress: r.array(lr)
282
+ progress: r.array(sr)
278
283
  });
279
284
  r.object({
280
- playlist: Zt,
281
- votes: r.record(r.string(), r.record(r.string(), nt)),
285
+ playlist: Xt,
286
+ votes: r.record(r.string(), r.record(r.string(), Ze)),
282
287
  // votes[clientId][mediaId]
283
288
  names: r.record(r.string(), r.string()),
284
289
  // clientId -> display name
@@ -290,46 +295,46 @@ r.object({
290
295
  * reveal is closed ignores it, rather than having the popup reappear. */
291
296
  update: r.boolean().optional()
292
297
  });
293
- const ye = {
298
+ const be = {
294
299
  STATE: "playlist_state",
295
300
  VOTE_PROGRESS: "playlist_vote_progress",
296
301
  RESULTS: "playlist_results",
297
302
  /** Server -> the removed voter: forget the ballot you hold in localStorage. */
298
303
  VOTES_CLEARED: "playlist_votes_cleared"
299
- }, Qt = r.object({
304
+ }, Jt = r.object({
300
305
  id: r.string(),
301
306
  displayName: r.string(),
302
307
  thumbnail: r.string().optional()
303
308
  });
304
309
  r.object({
305
- items: r.array(Qt)
310
+ items: r.array(Jt)
306
311
  });
307
- const en = r.partialRecord(Ce, r.array(r.string()));
308
- r.object({ board: en });
309
- const cr = r.object({
312
+ const Qt = r.partialRecord(xe, r.array(r.string()));
313
+ r.object({ board: Qt });
314
+ const lr = r.object({
310
315
  mediaId: r.string(),
311
- tier: Ce,
312
- absoluteTier: Ce,
316
+ tier: xe,
317
+ absoluteTier: xe,
313
318
  /** Tier level + within-row position. Integer part is the tier. */
314
319
  score: r.number(),
315
320
  /** The consensus mean that decided the tier. Never the displayed number. */
316
321
  placementScore: r.number(),
317
322
  subRankOffset: r.number(),
318
- votes: r.record(r.string(), Ce)
323
+ votes: r.record(r.string(), xe)
319
324
  // opaque voterId -> the letter they gave
320
325
  });
321
326
  r.object({
322
- items: r.array(Qt),
323
- placed: r.array(cr),
327
+ items: r.array(Jt),
328
+ placed: r.array(lr),
324
329
  unrated: r.array(r.string()),
325
330
  spread: r.number(),
326
331
  nudged: r.boolean(),
327
332
  names: r.record(r.string(), r.string()),
328
333
  sprites: r.record(r.string(), r.string()),
329
334
  voterCount: r.number().int().nonnegative(),
330
- myBoard: en
335
+ myBoard: Qt
331
336
  });
332
- const dr = {
337
+ const cr = {
333
338
  /** Broadcast to EVERY connected socket when anyone saves. Carries no board —
334
339
  * it is a "re-fetch" ping, because the shared result is identical for all
335
340
  * viewers but `myBoard` is not, and one payload cannot serve both. */
@@ -342,10 +347,10 @@ r.object({
342
347
  ready: r.array(r.string())
343
348
  // clientIds who have flagged ready
344
349
  });
345
- const ur = {
350
+ const dr = {
346
351
  STATE: "ready_check_state"
347
352
  // distinct from buffer-readiness 'ready_state'
348
- }, tn = {
353
+ }, Zt = {
349
354
  ACTIVE: "game_active",
350
355
  // server -> room (room-wide active game id; null = none)
351
356
  SYNC: "game_sync"
@@ -371,7 +376,7 @@ r.object({
371
376
  r.object({
372
377
  gameId: r.string().min(1).max(32)
373
378
  });
374
- const mr = r.object({
379
+ const ur = r.object({
375
380
  clientId: r.string(),
376
381
  playerId: r.number().int().min(0),
377
382
  name: r.string(),
@@ -385,12 +390,12 @@ r.object({
385
390
  gameId: r.string(),
386
391
  hostClientId: r.string().nullable(),
387
392
  seed: r.number().int(),
388
- players: r.array(mr)
393
+ players: r.array(ur)
389
394
  });
390
395
  r.object({ d: r.unknown() });
391
396
  r.object({ d: r.unknown() });
392
397
  r.object({ fromClientId: r.string(), d: r.unknown() });
393
- const pr = r.object({
398
+ const mr = r.object({
394
399
  playerId: r.number().int().min(0),
395
400
  clientId: r.string().nullable(),
396
401
  name: r.string(),
@@ -431,7 +436,7 @@ const pr = r.object({
431
436
  r.object({
432
437
  winnerPlayerId: r.number().int().min(0).nullable(),
433
438
  draw: r.boolean(),
434
- players: r.array(pr),
439
+ players: r.array(mr),
435
440
  // Optional session metadata + round-level extras (Phase C).
436
441
  sessionOver: r.boolean().optional(),
437
442
  roundNo: r.number().int().min(0).optional(),
@@ -439,7 +444,7 @@ r.object({
439
444
  maxMultiKill: r.number().int().min(0).optional(),
440
445
  maxChainKill: r.number().int().min(0).optional()
441
446
  });
442
- const nn = [
447
+ const fr = [
443
448
  {
444
449
  id: "fantasy",
445
450
  characters: [
@@ -483,37 +488,37 @@ const nn = [
483
488
  { id: "mentor", color: "#e1e0dc" }
484
489
  ]
485
490
  }
486
- ], yt = 8, rt = nn.flatMap((e) => e.characters), Le = rt.map((e) => e.id), fr = Object.fromEntries(rt.map((e) => [e.id, e.color])), hr = Object.fromEntries(rt.map((e) => [e.id, "rev" in e ? e.rev : 1]));
491
+ ], et = fr.flatMap((e) => e.characters), Me = et.map((e) => e.id), pr = Object.fromEntries(et.map((e) => [e.id, e.color])), hr = Object.fromEntries(et.map((e) => [e.id, "rev" in e ? e.rev : 1]));
487
492
  function gr(e) {
488
493
  const t = hr[e];
489
494
  return `/media/sprites/square_${e}.png${t > 1 ? `?v=${t}` : ""}`;
490
495
  }
491
- function fe(e) {
492
- return !!e && Le.includes(e);
496
+ function tt(e) {
497
+ return !!e && Me.includes(e);
493
498
  }
494
- const br = 5e3, rn = { mode: "off", percent: 0 }, yr = ["off", "on", "ab"];
499
+ const br = 5e3, en = { mode: "off", percent: 0 }, yr = ["off", "on", "ab"];
495
500
  function vr(e) {
496
501
  return typeof e == "string" && yr.includes(e);
497
502
  }
498
503
  function Sr(e) {
499
- const t = e && typeof e == "object" ? e : {}, n = vr(t.mode) ? t.mode : rn.mode, o = Number(t.percent), i = Number.isFinite(o) ? Math.min(100, Math.max(0, Math.round(o))) : 0;
500
- return { mode: n, percent: i };
504
+ const t = e && typeof e == "object" ? e : {}, n = vr(t.mode) ? t.mode : en.mode, o = Number(t.percent), a = Number.isFinite(o) ? Math.min(100, Math.max(0, Math.round(o))) : 0;
505
+ return { mode: n, percent: a };
501
506
  }
502
- function wr(e) {
507
+ function _r(e) {
503
508
  let t = 2166136261;
504
509
  for (let n = 0; n < e.length; n++)
505
510
  t ^= e.charCodeAt(n), t = Math.imul(t, 16777619) >>> 0;
506
511
  return t >>> 0;
507
512
  }
508
- function _r(e, t, n) {
513
+ function wr(e, t, n) {
509
514
  if (n === !0)
510
515
  return "fragment";
511
516
  if (n === !1)
512
517
  return "range";
513
518
  const o = Sr(e);
514
- return o.mode === "on" ? "fragment" : o.mode === "off" || o.percent <= 0 ? "range" : o.percent >= 100 || t && wr(t) % 100 < o.percent ? "fragment" : "range";
519
+ return o.mode === "on" ? "fragment" : o.mode === "off" || o.percent <= 0 ? "range" : o.percent >= 100 || t && _r(t) % 100 < o.percent ? "fragment" : "range";
515
520
  }
516
- const on = r.enum(["url", "magnet", "upload"]), xr = r.enum([
521
+ const tn = r.enum(["url", "magnet", "upload"]), xr = r.enum([
517
522
  "pending",
518
523
  "approved",
519
524
  "fetching",
@@ -521,7 +526,7 @@ const on = r.enum(["url", "magnet", "upload"]), xr = r.enum([
521
526
  "ready",
522
527
  "rejected",
523
528
  "failed"
524
- ]), vt = {
529
+ ]), bt = {
525
530
  pending: ["approved", "rejected"],
526
531
  approved: ["fetching", "rejected", "failed"],
527
532
  fetching: ["ingesting", "failed"],
@@ -530,12 +535,12 @@ const on = r.enum(["url", "magnet", "upload"]), xr = r.enum([
530
535
  rejected: [],
531
536
  failed: ["approved"]
532
537
  };
533
- Object.keys(vt).filter((e) => vt[e].length === 0);
534
- const Cr = r.object({
538
+ Object.keys(bt).filter((e) => bt[e].length === 0);
539
+ const Ir = r.object({
535
540
  id: r.string().min(1),
536
541
  /** Stable userId from whoami. Never the raw `X-User-Key`. */
537
542
  ownerUserId: r.string().min(1),
538
- kind: on,
543
+ kind: tn,
539
544
  /** The magnet URI, the https URL, or the filetransfer binding id. */
540
545
  source: r.string().min(1),
541
546
  title: r.string().optional(),
@@ -558,19 +563,19 @@ const Cr = r.object({
558
563
  /** Size once known. 0 means "known to be empty"; absent means "not yet known" —
559
564
  * the same distinction `chatImages` draws for image dimensions. */
560
565
  bytes: r.number().int().nonnegative().optional()
561
- }), kr = 4096, an = 300, Ir = 100;
566
+ }), Cr = 4096, nn = 300, Tr = 100;
562
567
  r.object({
563
- kind: on,
564
- source: r.string().min(1).max(kr),
565
- title: r.string().min(1).max(an).optional(),
568
+ kind: tn,
569
+ source: r.string().min(1).max(Cr),
570
+ title: r.string().min(1).max(nn).optional(),
566
571
  /** Required for `kind: 'magnet'` by `checkSource`; ignored for `url`/`upload`. */
567
- series: r.string().min(1).max(Ir).optional()
572
+ series: r.string().min(1).max(Tr).optional()
568
573
  });
569
574
  r.object({
570
- reason: r.string().min(1).max(an).optional()
575
+ reason: r.string().min(1).max(nn).optional()
571
576
  });
572
577
  r.object({
573
- submissions: r.array(Cr)
578
+ submissions: r.array(Ir)
574
579
  });
575
580
  r.object({
576
581
  error: r.literal("submissions/quota_exceeded"),
@@ -579,29 +584,29 @@ r.object({
579
584
  /** The cap that was applied, from the server's config. */
580
585
  capBytes: r.number().int().nonnegative()
581
586
  });
582
- const Tr = /^[a-z0-9][a-z0-9-]{0,31}$/, St = "https://game-catalog.invalid";
583
- function Er(e) {
587
+ const kr = /^[a-z0-9][a-z0-9-]{0,31}$/, yt = "https://game-catalog.invalid";
588
+ function Ar(e) {
584
589
  if (!e.startsWith("/") || e.startsWith("//") || /[\\\s]/.test(e) || [...e].some((t) => t.charCodeAt(0) < 32 || t.charCodeAt(0) === 127))
585
590
  return !1;
586
591
  try {
587
- return new URL(e, St).origin === St;
592
+ return new URL(e, yt).origin === yt;
588
593
  } catch {
589
594
  return !1;
590
595
  }
591
596
  }
592
- const sn = r.string().min(2).max(512).refine(Er, {
597
+ const rn = r.string().min(2).max(512).refine(Ar, {
593
598
  message: "must be a root-relative path on the page origin (e.g. /cardtable/embed/x.js)"
594
- }), wt = {
595
- id: r.string().regex(Tr),
599
+ }), vt = {
600
+ id: r.string().regex(kr),
596
601
  name: r.string().min(1).max(64),
597
- cover: sn.optional()
598
- }, Ar = r.discriminatedUnion("kind", [
599
- r.object({ ...wt, kind: r.literal("iframe") }).strict(),
600
- r.object({ ...wt, kind: r.literal("module"), url: sn }).strict()
602
+ cover: rn.optional()
603
+ }, Er = r.discriminatedUnion("kind", [
604
+ r.object({ ...vt, kind: r.literal("iframe") }).strict(),
605
+ r.object({ ...vt, kind: r.literal("module"), url: rn }).strict()
601
606
  ]);
602
- r.array(Ar).max(32).refine((e) => new Set(e.map((t) => t.id)).size === e.length, { message: "game ids must be unique" });
603
- const ot = r.enum(["anilist", "tmdb"]), Nr = r.object({
604
- source: ot,
607
+ r.array(Er).max(32).refine((e) => new Set(e.map((t) => t.id)).size === e.length, { message: "game ids must be unique" });
608
+ const nt = r.enum(["anilist", "tmdb"]), Pr = r.object({
609
+ source: nt,
605
610
  /** The provider's own id, as a string (AniList and TMDB both use integers). */
606
611
  id: r.string(),
607
612
  title: r.string(),
@@ -622,11 +627,11 @@ const ot = r.enum(["anilist", "tmdb"]), Nr = r.object({
622
627
  addable: r.boolean()
623
628
  });
624
629
  r.object({
625
- results: r.array(Nr),
630
+ results: r.array(Pr),
626
631
  /** Providers that answered. TMDB is absent when no token is configured. */
627
- sources: r.array(ot)
632
+ sources: r.array(nt)
628
633
  });
629
- const Pr = r.object({
634
+ const Nr = r.object({
630
635
  id: r.string(),
631
636
  url: r.string(),
632
637
  title: r.string(),
@@ -634,35 +639,35 @@ const Pr = r.object({
634
639
  durationS: r.number().nullable(),
635
640
  thumb: r.string().nullable()
636
641
  });
637
- r.object({ results: r.array(Pr) });
642
+ r.object({ results: r.array(Nr) });
638
643
  r.object({
639
- source: ot,
644
+ source: nt,
640
645
  id: r.string().regex(/^\d{1,12}$/)
641
646
  });
642
647
  r.object({ series: r.string(), title: r.string() });
643
- let ve = null, ln = qt;
644
- function Mr(e) {
645
- e.cdnBase && (ln = e.cdnBase);
648
+ let ye = null, on = Kt;
649
+ function Rr(e) {
650
+ e.cdnBase && (on = e.cdnBase);
646
651
  }
647
- function cn() {
648
- return ln.replace(/\/+$/, "");
652
+ function an() {
653
+ return on.replace(/\/+$/, "");
649
654
  }
650
- function je() {
651
- const e = cn();
652
- return (!ve || ve.base !== e) && (ve = { base: e, client: jn({ cdnBase: e }) }), ve.client;
655
+ function Le() {
656
+ const e = an();
657
+ return (!ye || ye.base !== e) && (ye = { base: e, client: Mn({ cdnBase: e }) }), ye.client;
653
658
  }
654
659
  function Pe(e) {
655
- return !et(e) || !cn() ? null : je().urlFor(e);
660
+ return !Je(e) || !an() ? null : Le().urlFor(e);
656
661
  }
657
- function Or(e) {
662
+ function Dr(e) {
658
663
  return e.kind === "file";
659
664
  }
660
- class dn extends Error {
665
+ class sn extends Error {
661
666
  constructor(t) {
662
667
  super(t), this.name = "ChatImageUploadError";
663
668
  }
664
669
  }
665
- function ke(e) {
670
+ function Ie(e) {
666
671
  switch (e) {
667
672
  case "unauthenticated":
668
673
  return "Sign in to paste images";
@@ -682,27 +687,27 @@ function ke(e) {
682
687
  return "Image upload failed";
683
688
  }
684
689
  }
685
- function Rr(e) {
686
- return e instanceof ze ? ke(e.reason) : e instanceof dn ? e.message : "Image upload failed";
690
+ function Or(e) {
691
+ return e instanceof qe ? Ie(e.reason) : e instanceof sn ? e.message : "Image upload failed";
687
692
  }
688
- function Dr(e) {
693
+ function Mr(e) {
689
694
  const t = e.type.toLowerCase().split(";")[0].trim();
690
- return t && Un(t) !== "image" ? ke("unsupported-type") : e.size > Fn.image ? ke("too-large") : e.size === 0 ? ke("invalid") : null;
695
+ return t && Ln(t) !== "image" ? Ie("unsupported-type") : e.size > jn.image ? Ie("too-large") : e.size === 0 ? Ie("invalid") : null;
691
696
  }
692
697
  function Lr(e) {
693
- return $n(e);
698
+ return Fn(e);
694
699
  }
695
700
  function jr(e) {
696
- return Bn(e);
701
+ return Hn(e);
697
702
  }
698
703
  async function Ur(e) {
699
- const t = await Hn(e);
704
+ const t = await Un(e);
700
705
  return { w: t?.width ?? 0, h: t?.height ?? 0 };
701
706
  }
702
707
  async function Fr(e, t = {}) {
703
- const n = await je().upload(e, { signal: t.signal });
704
- if (!et(n.key))
705
- throw new dn("Image upload returned an unusable key");
708
+ const n = await Le().upload(e, { signal: t.signal });
709
+ if (!Je(n.key))
710
+ throw new sn("Image upload returned an unusable key");
706
711
  return { key: n.key, deduplicated: n.deduplicated === !0 };
707
712
  }
708
713
  async function Hr(e, t = {}) {
@@ -712,8 +717,8 @@ async function Hr(e, t = {}) {
712
717
  n.push({ key: o.key, w: o.w, h: o.h, ...o.title ? { title: o.title } : {} });
713
718
  continue;
714
719
  }
715
- const { key: i } = await Fr(o.file, t);
716
- n.push({ key: i, w: o.w, h: o.h });
720
+ const { key: a } = await Fr(o.file, t);
721
+ n.push({ key: a, w: o.w, h: o.h });
717
722
  }
718
723
  return n;
719
724
  }
@@ -721,138 +726,138 @@ function $r(e) {
721
726
  return {
722
727
  error: e instanceof Error ? e.message : String(e),
723
728
  name: e instanceof Error ? e.name : typeof e,
724
- reason: e instanceof ze ? e.reason : void 0,
725
- status: e instanceof ze ? e.status : void 0
729
+ reason: e instanceof qe ? e.reason : void 0,
730
+ status: e instanceof qe ? e.status : void 0
726
731
  };
727
732
  }
728
- const Ye = 240, Z = 8, Br = 28, un = 240;
733
+ const Ve = 240, X = 8, Br = 28, ln = 240;
729
734
  function Gr(e, t, n) {
730
- let o = t.left - Ye - Z;
731
- o < Z && (o = t.right + Z), o = Math.min(o, n.width - Ye - Z), o = Math.max(Z, o);
732
- const i = un + Br;
733
- let s = e.top + e.height / 2 - i / 2;
734
- return s = Math.min(s, n.height - i - Z), s = Math.max(Z, s), { left: o, top: s };
735
- }
736
- const mn = "/dataplatform/api/memes", Wr = 120;
737
- function Kr({ onClose: e, onPick: t, onUnavailable: n }) {
738
- const [o, i] = P(""), s = J(null), a = J(null), c = je(), { items: f, loading: g, error: p, hasMore: D, loadMore: M } = Xn(
739
- c,
740
- mn,
735
+ let o = t.left - Ve - X;
736
+ o < X && (o = t.right + X), o = Math.min(o, n.width - Ve - X), o = Math.max(X, o);
737
+ const a = ln + Br;
738
+ let i = e.top + e.height / 2 - a / 2;
739
+ return i = Math.min(i, n.height - a - X), i = Math.max(X, i), { left: o, top: i };
740
+ }
741
+ const cn = "/dataplatform/api/memes", Kr = 120;
742
+ function Wr({ onClose: e, onPick: t, onUnavailable: n }) {
743
+ const [o, a] = R(""), i = z(null), s = z(null), l = Le(), { items: p, loading: h, error: f, hasMore: L, loadMore: O } = zn(
744
+ l,
745
+ cn,
741
746
  o.trim()
742
- ), T = p?.reason ?? null, C = Q(() => {
743
- const S = /* @__PURE__ */ new Set();
744
- return f.filter((R) => S.has(R.key) ? !1 : (S.add(R.key), !0));
745
- }, [f]), [h, _] = P(null), w = J(null), k = N(() => {
746
- w.current !== null && (window.clearTimeout(w.current), w.current = null);
747
- }, []), u = N(() => {
748
- k(), _(null);
749
- }, [k]), v = typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(hover: hover)").matches, E = N(
750
- (S, R, L = !1) => {
751
- const F = s.current;
752
- if (!F) return;
753
- const W = () => _({
754
- item: S,
755
- ...Gr(R.getBoundingClientRect(), F.getBoundingClientRect(), {
747
+ ), M = f?.reason ?? null, C = Q(() => {
748
+ const x = /* @__PURE__ */ new Set();
749
+ return p.filter((P) => x.has(P.key) ? !1 : (x.add(P.key), !0));
750
+ }, [p]), [m, S] = R(null), y = z(null), I = E(() => {
751
+ y.current !== null && (window.clearTimeout(y.current), y.current = null);
752
+ }, []), g = E(() => {
753
+ I(), S(null);
754
+ }, [I]), A = typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(hover: hover)").matches, N = E(
755
+ (x, P, T = !1) => {
756
+ const pe = i.current;
757
+ if (!pe) return;
758
+ const he = () => S({
759
+ item: x,
760
+ ...Gr(P.getBoundingClientRect(), pe.getBoundingClientRect(), {
756
761
  width: window.innerWidth,
757
762
  height: window.innerHeight
758
763
  })
759
764
  });
760
- k(), L ? W() : w.current = window.setTimeout(W, Wr);
765
+ I(), T ? he() : y.current = window.setTimeout(he, Kr);
761
766
  },
762
- [k]
767
+ [I]
763
768
  );
764
- O(() => () => k(), [k]), O(() => {
765
- T === "unavailable" && n?.();
766
- }, [T, n]), O(() => {
767
- a.current?.focus();
768
- }, []), O(() => {
769
- const S = (L) => {
770
- L.key === "Escape" && (L.stopPropagation(), e());
771
- }, R = (L) => {
772
- s.current && !s.current.contains(L.target) && e();
769
+ D(() => () => I(), [I]), D(() => {
770
+ M === "unavailable" && n?.();
771
+ }, [M, n]), D(() => {
772
+ s.current?.focus();
773
+ }, []), D(() => {
774
+ const x = (T) => {
775
+ T.key === "Escape" && (T.stopPropagation(), e());
776
+ }, P = (T) => {
777
+ i.current && !i.current.contains(T.target) && e();
773
778
  };
774
- return document.addEventListener("keydown", S), document.addEventListener("mousedown", R), () => {
775
- document.removeEventListener("keydown", S), document.removeEventListener("mousedown", R);
779
+ return document.addEventListener("keydown", x), document.addEventListener("mousedown", P), () => {
780
+ document.removeEventListener("keydown", x), document.removeEventListener("mousedown", P);
776
781
  };
777
782
  }, [e]);
778
- const U = N((S) => {
779
- t(S), e();
780
- }, [t, e]), H = !g && !p && C.length === 0;
781
- return /* @__PURE__ */ b(
783
+ const U = E((x) => {
784
+ t(x), e();
785
+ }, [t, e]), F = !h && !f && C.length === 0;
786
+ return /* @__PURE__ */ v(
782
787
  "div",
783
788
  {
784
- ref: s,
789
+ ref: i,
785
790
  role: "dialog",
786
791
  "aria-label": "Meme library",
787
792
  "data-testid": "meme-picker",
788
793
  className: "absolute bottom-full left-0 right-0 mb-2 z-30 flex max-h-80 flex-col rounded border border-border bg-bg-card shadow-lg",
789
794
  children: [
790
- /* @__PURE__ */ l("div", { className: "border-b border-border-light p-2", children: /* @__PURE__ */ l(
795
+ /* @__PURE__ */ c("div", { className: "border-b border-border-light p-2", children: /* @__PURE__ */ c(
791
796
  "input",
792
797
  {
793
- ref: a,
798
+ ref: s,
794
799
  type: "text",
795
800
  value: o,
796
- onChange: (S) => i(S.target.value),
801
+ onChange: (x) => a(x.target.value),
797
802
  placeholder: "Search memes...",
798
803
  "aria-label": "Search memes",
799
804
  "data-testid": "meme-search",
800
805
  className: "w-full rounded border border-border-light bg-bg-alt px-2 py-1 text-sm text-text placeholder:text-text-secondary focus:outline-none focus:border-primary"
801
806
  }
802
807
  ) }),
803
- /* @__PURE__ */ b(
808
+ /* @__PURE__ */ v(
804
809
  "div",
805
810
  {
806
811
  className: "flex-1 overflow-y-auto p-2",
807
- onScroll: u,
808
- onMouseLeave: u,
812
+ onScroll: g,
813
+ onMouseLeave: g,
809
814
  children: [
810
- p && /* @__PURE__ */ l(
815
+ f && /* @__PURE__ */ c(
811
816
  "p",
812
817
  {
813
818
  className: "m-2 p-2 text-center text-sm bg-danger-bg text-on-danger-bg rounded",
814
819
  role: "alert",
815
- "data-reason": T,
816
- children: T === "unauthenticated" ? "Sign in to browse memes" : T === "forbidden" ? "Your account can't browse the meme library" : "Meme library is unavailable"
820
+ "data-reason": M,
821
+ children: M === "unauthenticated" ? "Sign in to browse memes" : M === "forbidden" ? "Your account can't browse the meme library" : "Meme library is unavailable"
817
822
  }
818
823
  ),
819
- H && /* @__PURE__ */ l("p", { className: "p-4 text-center text-sm text-text-secondary", children: o.trim() ? `No memes match "${o.trim()}"` : "The meme library is empty" }),
820
- C.length > 0 && /* @__PURE__ */ l("div", { className: "grid grid-cols-3 gap-1.5 sm:grid-cols-4", children: C.map((S) => {
821
- const R = Pe(S.key);
822
- return R ? /* @__PURE__ */ l(
824
+ F && /* @__PURE__ */ c("p", { className: "p-4 text-center text-sm text-text-secondary", children: o.trim() ? `No memes match "${o.trim()}"` : "The meme library is empty" }),
825
+ C.length > 0 && /* @__PURE__ */ c("div", { className: "grid grid-cols-3 gap-1.5 sm:grid-cols-4", children: C.map((x) => {
826
+ const P = Pe(x.key);
827
+ return P ? /* @__PURE__ */ c(
823
828
  "button",
824
829
  {
825
830
  type: "button",
826
- onClick: () => U(S),
827
- title: S.title,
831
+ onClick: () => U(x),
832
+ title: x.title,
828
833
  "data-testid": "meme-result",
829
- onMouseEnter: (L) => {
830
- v && E(S, L.currentTarget);
834
+ onMouseEnter: (T) => {
835
+ A && N(x, T.currentTarget);
831
836
  },
832
- onMouseLeave: u,
833
- onFocus: (L) => E(S, L.currentTarget, !0),
834
- onBlur: u,
837
+ onMouseLeave: g,
838
+ onFocus: (T) => N(x, T.currentTarget, !0),
839
+ onBlur: g,
835
840
  className: "group relative aspect-square overflow-hidden rounded border border-border-light bg-bg-alt focus:outline-none focus:border-primary hover:border-primary",
836
- children: /* @__PURE__ */ l(
841
+ children: /* @__PURE__ */ c(
837
842
  "img",
838
843
  {
839
- src: R,
840
- alt: S.title || "Meme",
844
+ src: P,
845
+ alt: x.title || "Meme",
841
846
  loading: "lazy",
842
847
  decoding: "async",
843
848
  className: "h-full w-full object-cover"
844
849
  }
845
850
  )
846
851
  },
847
- S.key
852
+ x.key
848
853
  ) : null;
849
854
  }) }),
850
- g && /* @__PURE__ */ l("p", { className: "p-2 text-center text-xs text-text-secondary", children: "Loading…" }),
851
- D && !g && /* @__PURE__ */ l(
855
+ h && /* @__PURE__ */ c("p", { className: "p-2 text-center text-xs text-text-secondary", children: "Loading…" }),
856
+ L && !h && /* @__PURE__ */ c(
852
857
  "button",
853
858
  {
854
859
  type: "button",
855
- onClick: M,
860
+ onClick: O,
856
861
  "data-testid": "meme-load-more",
857
862
  className: "mt-2 w-full rounded border border-border-light py-1 text-xs text-text-secondary hover:text-text hover:border-primary",
858
863
  children: "Load more"
@@ -861,15 +866,15 @@ function Kr({ onClose: e, onPick: t, onUnavailable: n }) {
861
866
  ]
862
867
  }
863
868
  ),
864
- h && /* @__PURE__ */ b(
869
+ m && /* @__PURE__ */ v(
865
870
  "div",
866
871
  {
867
872
  "data-testid": "meme-preview",
868
873
  className: "fixed z-50 flex flex-col gap-1 rounded border border-border-light bg-bg-card p-1.5 shadow-2xl",
869
874
  style: {
870
- left: h.left,
871
- top: h.top,
872
- width: Ye,
875
+ left: m.left,
876
+ top: m.top,
877
+ width: Ve,
873
878
  /* The placement maths reserves exactly PREVIEW_PX of width.
874
879
  Without border-box the padding and border sit OUTSIDE that
875
880
  number and the box laps over the panel it is meant to sit
@@ -880,18 +885,18 @@ function Kr({ onClose: e, onPick: t, onUnavailable: n }) {
880
885
  },
881
886
  "aria-hidden": "true",
882
887
  children: [
883
- /* @__PURE__ */ l(
888
+ /* @__PURE__ */ c(
884
889
  "img",
885
890
  {
886
- src: Pe(h.item.key) ?? void 0,
891
+ src: Pe(m.item.key) ?? void 0,
887
892
  alt: "",
888
893
  className: "block w-full rounded bg-bg object-contain",
889
- style: { height: "auto", maxHeight: un }
894
+ style: { height: "auto", maxHeight: ln }
890
895
  }
891
896
  ),
892
- h.item.title && /* One line. A long title must not change the box height, because
897
+ m.item.title && /* One line. A long title must not change the box height, because
893
898
  the box height is what the placement maths clamped against. */
894
- /* @__PURE__ */ l("span", { className: "overflow-hidden text-ellipsis whitespace-nowrap text-center text-[10px] text-text-secondary", children: h.item.title })
899
+ /* @__PURE__ */ c("span", { className: "overflow-hidden text-ellipsis whitespace-nowrap text-center text-[10px] text-text-secondary", children: m.item.title })
895
900
  ]
896
901
  }
897
902
  )
@@ -900,68 +905,65 @@ function Kr({ onClose: e, onPick: t, onUnavailable: n }) {
900
905
  );
901
906
  }
902
907
  function qr({ open: e, ...t }) {
903
- return e ? /* @__PURE__ */ l(Kr, { ...t }) : null;
908
+ return e ? /* @__PURE__ */ c(Wr, { ...t }) : null;
904
909
  }
905
- const it = {
906
- serverOrigin: `http://localhost:${tt}`,
910
+ const rt = {
911
+ serverOrigin: `http://localhost:${Qe}`,
907
912
  defaultRoomKey: "dev-room",
908
- cdnBase: qt
913
+ cdnBase: Kt
909
914
  };
910
- let Ie = { ...it };
915
+ let Ce = { ...rt };
911
916
  function Vr(e) {
912
917
  const t = Object.fromEntries(
913
918
  Object.entries(e).filter(([n, o]) => o !== void 0)
914
919
  );
915
- Ie = { ...Ie, ...t }, Mr({ cdnBase: Ie.cdnBase });
916
- }
917
- function he() {
918
- return Ie;
920
+ Ce = { ...Ce, ...t }, Rr({ cdnBase: Ce.cdnBase });
919
921
  }
920
- const pn = Gt(it);
921
- function zr() {
922
- return Wt(pn);
922
+ function me() {
923
+ return Ce;
923
924
  }
924
- function fn(e) {
925
+ const Yr = Bt(rt);
926
+ function zr(e) {
925
927
  return e.serverOrigin?.replace(/\/$/, "") || "";
926
928
  }
927
- function hn() {
928
- return fn(he());
929
+ function dn() {
930
+ return zr(me());
929
931
  }
930
- const gn = Gt(null);
931
- function Yr() {
932
- return Wt(gn);
932
+ const un = Bt(null);
933
+ function Xr() {
934
+ return Pn(un);
933
935
  }
934
- const oe = {
936
+ const te = {
935
937
  ANON_KEY: "watchparty_preferences",
936
938
  DEBUG_FLAG: "wp:debug",
937
939
  REPORT_LOGS_FLAG: "wp:reportLogs",
938
940
  voteKey: (e, t, n) => `watchparty_vote:${e}:${t}:${n}`
939
941
  };
940
- function Ue() {
942
+ function je() {
941
943
  try {
942
944
  return globalThis.localStorage ?? null;
943
945
  } catch {
944
946
  return null;
945
947
  }
946
948
  }
947
- function G(e) {
949
+ function B(e) {
948
950
  try {
949
- return Ue()?.getItem(e) ?? null;
951
+ return je()?.getItem(e) ?? null;
950
952
  } catch {
951
953
  return null;
952
954
  }
953
955
  }
954
- function at(e, t) {
956
+ function ot(e, t) {
955
957
  try {
956
- const n = Ue();
958
+ const n = je();
957
959
  return n ? (n.setItem(e, t), !0) : !1;
958
960
  } catch {
959
961
  return !1;
960
962
  }
961
963
  }
962
- function Xr() {
964
+ function Jr() {
963
965
  try {
964
- const e = Ue();
966
+ const e = je();
965
967
  if (!e) return [];
966
968
  const t = [];
967
969
  for (let n = 0; n < e.length; n++) {
@@ -973,51 +975,51 @@ function Xr() {
973
975
  return [];
974
976
  }
975
977
  }
976
- function Me(e) {
978
+ function Ne(e) {
977
979
  try {
978
- Ue()?.removeItem(e);
980
+ je()?.removeItem(e);
979
981
  } catch {
980
982
  }
981
983
  }
982
- function Jr(e) {
983
- const t = G(e);
984
+ function Qr(e) {
985
+ const t = B(e);
984
986
  return t === null ? null : t === "true" ? !0 : t === "false" ? !1 : null;
985
987
  }
986
- const bn = /* @__PURE__ */ new Map();
987
- let yn = !1;
988
- function Fe(e, t) {
989
- bn.set(e, t), yn && vn(e, t);
988
+ const mn = /* @__PURE__ */ new Map();
989
+ let fn = !1;
990
+ function Ue(e, t) {
991
+ mn.set(e, t), fn && pn(e, t);
990
992
  }
991
993
  function Zr() {
992
- yn = !0;
993
- for (const [e, t] of bn) vn(e, t);
994
+ fn = !0;
995
+ for (const [e, t] of mn) pn(e, t);
994
996
  }
995
- function vn(e, t) {
997
+ function pn(e, t) {
996
998
  typeof window > "u" || (window[e] = t);
997
999
  }
998
- const de = [], Qr = 500;
999
- let Te = [], Xe = null;
1000
- const eo = 1e3, to = 200;
1001
- let Je = null;
1002
- function Sn(e) {
1003
- Je = e;
1000
+ const ie = [], eo = 500;
1001
+ let Te = [], Ye = null;
1002
+ const to = 1e3, no = 200;
1003
+ let ze = null;
1004
+ function hn(e) {
1005
+ ze = e;
1004
1006
  }
1005
- function wn() {
1006
- return G(oe.REPORT_LOGS_FLAG) === "true";
1007
+ function gn() {
1008
+ return B(te.REPORT_LOGS_FLAG) === "true";
1007
1009
  }
1008
- function no() {
1010
+ function ro() {
1009
1011
  if (typeof window > "u") return !1;
1010
1012
  if (window.__WP_DEBUG__ || new URLSearchParams(window.location.search).get("debug") === "true") return !0;
1011
- const t = G(oe.DEBUG_FLAG);
1013
+ const t = B(te.DEBUG_FLAG);
1012
1014
  return t !== null && t !== "false";
1013
1015
  }
1014
- function ro() {
1015
- const e = G(oe.DEBUG_FLAG);
1016
+ function oo() {
1017
+ const e = B(te.DEBUG_FLAG);
1016
1018
  if (!e || e === "true") return null;
1017
1019
  const t = e.split(",").map((n) => n.trim().toLowerCase()).filter(Boolean);
1018
1020
  return t.length > 0 ? new Set(t) : null;
1019
1021
  }
1020
- function oo() {
1022
+ function ao() {
1021
1023
  return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
1022
1024
  hour12: !1,
1023
1025
  hour: "2-digit",
@@ -1027,76 +1029,76 @@ function oo() {
1027
1029
  });
1028
1030
  }
1029
1031
  function io(e) {
1030
- de.push(e), de.length > Qr && de.shift();
1032
+ ie.push(e), ie.length > eo && ie.shift();
1031
1033
  }
1032
- const $e = {
1034
+ const Fe = {
1033
1035
  debug: "color: #64748b; font-weight: normal;",
1034
1036
  warn: "color: #f59e0b; font-weight: bold;",
1035
1037
  error: "color: #ef4444; font-weight: bold;",
1036
1038
  category: "color: #3b82f6; font-weight: bold;",
1037
1039
  time: "color: #6b7280; font-weight: normal;"
1038
1040
  };
1039
- function ao(e) {
1041
+ function so(e) {
1040
1042
  try {
1041
1043
  return JSON.stringify(e);
1042
1044
  } catch {
1043
1045
  return "[unserializable]";
1044
1046
  }
1045
1047
  }
1046
- function st(e, t, n, o, i, s, a) {
1047
- const c = {
1048
+ function at(e, t, n, o, a, i, s) {
1049
+ const l = {
1048
1050
  level: e,
1049
- category: i,
1050
- message: s,
1051
- data: a,
1051
+ category: a,
1052
+ message: i,
1053
+ data: s,
1052
1054
  timestamp: Date.now()
1053
1055
  };
1054
- if (io(c), (n || wn()) && _t(c), !o) {
1055
- if (!no()) return;
1056
- const p = ro();
1057
- if (p && !p.has(i.toLowerCase())) return;
1056
+ if (io(l), (n || gn()) && St(l), !o) {
1057
+ if (!ro()) return;
1058
+ const f = oo();
1059
+ if (f && !f.has(a.toLowerCase())) return;
1058
1060
  }
1059
- const f = oo(), g = a !== void 0 ? " " + ao(a) : "";
1061
+ const p = ao(), h = s !== void 0 ? " " + so(s) : "";
1060
1062
  t(
1061
- `%c[${f}]%c [${i}]%c ${s}${g}`,
1062
- $e.time,
1063
- $e.category,
1064
- $e[e]
1063
+ `%c[${p}]%c [${a}]%c ${i}${h}`,
1064
+ Fe.time,
1065
+ Fe.category,
1066
+ Fe[e]
1065
1067
  );
1066
1068
  }
1067
1069
  function d(e, t, n) {
1068
- st("debug", console.log, !1, !1, e, t, n);
1069
- }
1070
- function A(e, t, n) {
1071
- st("warn", console.warn, !0, !1, e, t, n);
1070
+ at("debug", console.log, !1, !1, e, t, n);
1072
1071
  }
1073
- function ue(e, t, n) {
1074
- st("error", console.error, !0, !0, e, t, n);
1072
+ function k(e, t, n) {
1073
+ at("warn", console.warn, !0, !1, e, t, n);
1075
1074
  }
1076
- function so() {
1077
- return [...de];
1075
+ function ke(e, t, n) {
1076
+ at("error", console.error, !0, !0, e, t, n);
1078
1077
  }
1079
1078
  function lo() {
1080
- de.length = 0;
1079
+ return [...ie];
1081
1080
  }
1082
- function co(e) {
1081
+ function co() {
1082
+ ie.length = 0;
1083
+ }
1084
+ function uo(e) {
1083
1085
  const t = e && e.length > 0 ? e.join(",") : "true";
1084
- at(oe.DEBUG_FLAG, t), console.log("[wp] Debug mode enabled");
1086
+ ot(te.DEBUG_FLAG, t), console.log("[wp] Debug mode enabled");
1085
1087
  }
1086
- function uo() {
1087
- Me(oe.DEBUG_FLAG), console.log("[wp] Debug mode disabled");
1088
+ function mo() {
1089
+ Ne(te.DEBUG_FLAG), console.log("[wp] Debug mode disabled");
1088
1090
  }
1089
- const mo = 1024, po = 5e4;
1090
- function fo(e) {
1091
+ const fo = 1024, po = 5e4;
1092
+ function ho(e) {
1091
1093
  if (e == null) return e;
1092
1094
  try {
1093
1095
  const t = JSON.stringify(e);
1094
- if (t.length <= mo) return e;
1096
+ if (t.length <= fo) return e;
1095
1097
  if (typeof e == "object" && !Array.isArray(e)) {
1096
1098
  const n = {};
1097
- for (const [o, i] of Object.entries(e)) {
1098
- const s = JSON.stringify(i);
1099
- n[o] = s && s.length > 200 ? `[truncated ${s.length} chars]` : i;
1099
+ for (const [o, a] of Object.entries(e)) {
1100
+ const i = JSON.stringify(a);
1101
+ n[o] = i && i.length > 200 ? `[truncated ${i.length} chars]` : a;
1100
1102
  }
1101
1103
  return n;
1102
1104
  }
@@ -1105,120 +1107,114 @@ function fo(e) {
1105
1107
  return "[unserializable]";
1106
1108
  }
1107
1109
  }
1108
- async function ho() {
1110
+ async function go() {
1109
1111
  if (Te.length === 0) return;
1110
1112
  const e = Te;
1111
- Te = [], Xe = null;
1113
+ Te = [], Ye = null;
1112
1114
  try {
1113
- let n = he().serverOrigin || "";
1115
+ let n = me().serverOrigin || "";
1114
1116
  if ((n === "/" || !n) && typeof window < "u") {
1115
- const c = window.location;
1116
- (c.hostname === "localhost" || c.hostname === "127.0.0.1") && c.port === String(zt) ? n = `${c.protocol}//${c.hostname}:${tt}` : n = c.origin;
1117
+ const l = window.location;
1118
+ (l.hostname === "localhost" || l.hostname === "127.0.0.1") && l.port === String(qt) ? n = `${l.protocol}//${l.hostname}:${Qe}` : n = l.origin;
1117
1119
  }
1118
- const o = `${n.replace(/\/$/, "")}/api/client-log`, i = e.map((c) => ({
1119
- level: c.level,
1120
- category: c.category,
1121
- message: c.message,
1122
- data: fo(c.data),
1123
- timestamp: c.timestamp,
1120
+ const o = `${n.replace(/\/$/, "")}/api/client-log`, a = e.map((l) => ({
1121
+ level: l.level,
1122
+ category: l.category,
1123
+ message: l.message,
1124
+ data: ho(l.data),
1125
+ timestamp: l.timestamp,
1124
1126
  url: typeof window < "u" ? window.location.href : void 0,
1125
1127
  userAgent: typeof navigator < "u" ? navigator.userAgent : void 0,
1126
- room_key: Je?.room_key,
1127
- client_id: Je?.client_id
1128
+ room_key: ze?.room_key,
1129
+ client_id: ze?.client_id
1128
1130
  }));
1129
- let s = [], a = 0;
1130
- for (const c of i) {
1131
- const f = JSON.stringify(c).length + 1;
1132
- a + f > po && s.length > 0 && (await fetch(o, {
1131
+ let i = [], s = 0;
1132
+ for (const l of a) {
1133
+ const p = JSON.stringify(l).length + 1;
1134
+ s + p > po && i.length > 0 && (await fetch(o, {
1133
1135
  method: "POST",
1134
1136
  headers: { "Content-Type": "application/json" },
1135
- body: JSON.stringify({ logs: s })
1136
- }), s = [], a = 0), s.push(c), a += f;
1137
+ body: JSON.stringify({ logs: i })
1138
+ }), i = [], s = 0), i.push(l), s += p;
1137
1139
  }
1138
- s.length > 0 && await fetch(o, {
1140
+ i.length > 0 && await fetch(o, {
1139
1141
  method: "POST",
1140
1142
  headers: { "Content-Type": "application/json" },
1141
- body: JSON.stringify({ logs: s })
1143
+ body: JSON.stringify({ logs: i })
1142
1144
  });
1143
1145
  } catch (t) {
1144
- console.warn("[wp] log flush failed (client logs may be lost):", ge(t).error);
1146
+ console.warn("[wp] log flush failed (client logs may be lost):", fe(t).error);
1145
1147
  }
1146
1148
  }
1147
- function _t(e) {
1148
- if (Te.push(e), !Xe) {
1149
- const t = wn() ? to : eo;
1150
- Xe = setTimeout(ho, t);
1149
+ function St(e) {
1150
+ if (Te.push(e), !Ye) {
1151
+ const t = gn() ? no : to;
1152
+ Ye = setTimeout(go, t);
1151
1153
  }
1152
1154
  }
1153
- function ge(e) {
1155
+ function fe(e) {
1154
1156
  return e instanceof Error ? { error: e.message, name: e.name } : { error: String(e), name: typeof e };
1155
1157
  }
1156
- Fe("__wpDebug", {
1157
- enable: co,
1158
- disable: uo,
1159
- history: so,
1160
- clear: lo
1158
+ Ue("__wpDebug", {
1159
+ enable: uo,
1160
+ disable: mo,
1161
+ history: lo,
1162
+ clear: co
1161
1163
  });
1162
- const X = {
1164
+ const Y = {
1163
1165
  resolved: !1,
1164
1166
  userType: "public",
1165
1167
  accountId: null
1166
1168
  };
1167
- function lt() {
1168
- return X.accountId ? X.accountId : X.resolved && X.userType === "public" ? "anon" : null;
1169
- }
1170
- function go(e) {
1171
- X.resolved = !0, e.userType && (X.userType = e.userType), e.userId !== void 0 && (X.accountId = e.userId && e.userId !== "public" ? e.userId : null);
1169
+ function it() {
1170
+ return Y.accountId ? Y.accountId : Y.resolved && Y.userType === "public" ? "anon" : null;
1172
1171
  }
1173
- function ct() {
1174
- return X.accountId ?? "public";
1172
+ function bo(e) {
1173
+ Y.resolved = !0, e.userType && (Y.userType = e.userType), e.userId !== void 0 && (Y.accountId = e.userId && e.userId !== "public" ? e.userId : null);
1175
1174
  }
1176
- function bo() {
1177
- return lt() ?? "unscoped";
1175
+ function st() {
1176
+ return Y.accountId ?? "public";
1178
1177
  }
1179
1178
  function yo() {
1180
- return nn.flatMap((e) => {
1181
- const t = e.characters.map((o) => o.id), n = [];
1182
- for (let o = 0; o < t.length; o += yt)
1183
- n.push(t.slice(o, o + yt));
1184
- return n;
1185
- });
1186
- }
1187
- function xt(e, t) {
1188
- const n = e.findIndex((o) => o.some((i) => i === t));
1189
- return n < 0 ? 0 : n;
1179
+ return it() ?? "unscoped";
1190
1180
  }
1191
- function _n(e) {
1192
- return fe(e) ? fr[e] : "var(--color-neutral-bg)";
1181
+ function bn(e) {
1182
+ return tt(e) ? pr[e] : "var(--color-neutral-bg)";
1193
1183
  }
1194
- function He(e) {
1195
- return fe(e) ? `${he().serverOrigin.replace(/\/$/, "")}${gr(e)}` : void 0;
1184
+ function vo(e) {
1185
+ return tt(e) ? `${me().serverOrigin.replace(/\/$/, "")}${gr(e)}` : void 0;
1196
1186
  }
1197
- let pe = null, Se = null;
1198
- function vo() {
1199
- return pe ? Promise.resolve() : Se || (Se = (async () => {
1187
+ let ce = null, ve = null;
1188
+ function So() {
1189
+ return ce ? Promise.resolve() : ve || (ve = (async () => {
1200
1190
  try {
1201
- const e = he().serverOrigin.replace(/\/$/, ""), t = await fetch(`${e}/media/sprites/names.json`, { cache: "no-cache" });
1191
+ const e = me().serverOrigin.replace(/\/$/, ""), t = await fetch(`${e}/media/sprites/names.json`, { cache: "no-cache" });
1202
1192
  if (!t.ok) return;
1203
1193
  const n = await t.json();
1204
- pe = n, Fe("__DISPLAY_NAMES__", n), d("characters", "Loaded display names", n);
1194
+ ce = n, Ue("__DISPLAY_NAMES__", n), d("characters", "Loaded display names", n);
1205
1195
  } catch (e) {
1206
- d("characters", "display names fetch failed (degrading to sprite IDs)", ge(e));
1196
+ d("characters", "display names fetch failed (degrading to sprite IDs)", fe(e));
1207
1197
  }
1208
- })(), Se);
1198
+ })(), ve);
1209
1199
  }
1210
- function te(e) {
1211
- return pe?.[e] || e.charAt(0).toUpperCase() + e.slice(1);
1200
+ function de(e) {
1201
+ return ce?.[e] || e.charAt(0).toUpperCase() + e.slice(1);
1212
1202
  }
1213
- function ne(e) {
1203
+ function J(e) {
1214
1204
  const t = e.toLowerCase();
1215
- return Le.some((n) => n === t) ? !0 : pe ? Object.values(pe).some((n) => n.toLowerCase() === t) : !1;
1205
+ return Me.some((n) => n === t) ? !0 : ce ? Object.values(ce).some((n) => n.toLowerCase() === t) : !1;
1216
1206
  }
1217
- const So = r.object({
1207
+ const _o = r.object({
1218
1208
  // Identity. There is deliberately NO `name`: a person's name is their
1219
1209
  // hadoku key-registry name, resolved by the server from whoami and renamed
1220
1210
  // through hadoku_site's POST /session/name (operator decision 2026-10-08).
1221
1211
  // A watchparty-only copy disagreed with cardtable's and is not read.
1212
+ //
1213
+ // `sprite` is neither read nor written by watchparty any more: the
1214
+ // character is chosen in the fleet settings popout and read from prefs-api
1215
+ // (lib/avatar.ts). It stays in the schema because prefs-api still reads it:
1216
+ // it is the fallback character for a person who never chose one at the
1217
+ // top level (operator decision 2026-10-31).
1222
1218
  sprite: r.string().optional(),
1223
1219
  // Chat settings
1224
1220
  chatCollapsed: r.boolean().optional(),
@@ -1233,21 +1229,21 @@ const So = r.object({
1233
1229
  preferredAudioLang: r.string().optional(),
1234
1230
  subtitlesEnabled: r.boolean().optional(),
1235
1231
  subtitleStyle: r.string().optional()
1236
- }), me = Wn({
1232
+ }), se = Bn({
1237
1233
  appId: "watchparty",
1238
- schema: So
1234
+ schema: _o
1239
1235
  }), wo = /* @__PURE__ */ new Set(["theme", "themeMode"]);
1240
- function Oe(e) {
1241
- const t = lt();
1236
+ function Re(e) {
1237
+ const t = it();
1242
1238
  return t ? `prefs-cache:${t}:${e}` : null;
1243
1239
  }
1244
- function _o() {
1245
- return lt() === null;
1240
+ function xo() {
1241
+ return it() === null;
1246
1242
  }
1247
- function Ct(e) {
1248
- const t = Oe(e);
1243
+ function _t(e) {
1244
+ const t = Re(e);
1249
1245
  if (!t) return {};
1250
- const n = G(t);
1246
+ const n = B(t);
1251
1247
  if (!n) return {};
1252
1248
  try {
1253
1249
  return JSON.parse(n)?.blob ?? {};
@@ -1255,35 +1251,35 @@ function Ct(e) {
1255
1251
  return {};
1256
1252
  }
1257
1253
  }
1258
- function xo() {
1254
+ function Io() {
1259
1255
  let e = 0;
1260
- for (const t of Xr()) {
1261
- const n = t.startsWith("prefs-cache:") && t.endsWith(":watchparty"), o = t === Re || t.startsWith(`${Re}:`) && !t.endsWith(":_serverSynced_v1");
1256
+ for (const t of Jr()) {
1257
+ const n = t.startsWith("prefs-cache:") && t.endsWith(":watchparty"), o = t === De || t.startsWith(`${De}:`) && !t.endsWith(":_serverSynced_v1");
1262
1258
  if (!n && !o) continue;
1263
- const i = G(t);
1264
- if (!i) continue;
1265
- let s;
1259
+ const a = B(t);
1260
+ if (!a) continue;
1261
+ let i;
1266
1262
  try {
1267
- s = JSON.parse(i);
1263
+ i = JSON.parse(a);
1268
1264
  } catch {
1269
1265
  continue;
1270
1266
  }
1271
- const a = n ? s?.blob : s;
1272
- if (!a || typeof a != "object" || !("name" in a)) continue;
1273
- const { name: c, ...f } = a, g = n ? { ...s, blob: f } : f;
1274
- at(t, JSON.stringify(g)) && e++;
1267
+ const s = n ? i?.blob : i;
1268
+ if (!s || typeof s != "object" || !("name" in s)) continue;
1269
+ const { name: l, ...p } = s, h = n ? { ...i, blob: p } : p;
1270
+ ot(t, JSON.stringify(h)) && e++;
1275
1271
  }
1276
1272
  return e > 0 && d("preferences", "Stripped retired name from cached prefs rows", { rewritten: e }), e;
1277
1273
  }
1278
- let xn = null, Cn = null;
1279
- const kt = /* @__PURE__ */ new Set();
1280
- function dt() {
1281
- if (_o())
1274
+ let yn = null, vn = null;
1275
+ const wt = /* @__PURE__ */ new Set();
1276
+ function lt() {
1277
+ if (xo())
1282
1278
  return d("preferences", "Boot read deferred — session pending, identity unresolved"), {};
1283
- const e = Ct("portfolio"), t = Ct("watchparty"), n = Object.keys(e).length > 0 ? e : xn ?? {}, o = Object.keys(t).length > 0 ? t : Cn ?? {}, i = ct() || "public";
1284
- if (kt.has(i))
1279
+ const e = _t("portfolio"), t = _t("watchparty"), n = Object.keys(e).length > 0 ? e : yn ?? {}, o = Object.keys(t).length > 0 ? t : vn ?? {}, a = st() || "public";
1280
+ if (wt.has(a))
1285
1281
  return d("preferences", "Loaded (sync, SDK-only after first read)", {
1286
- uid: i,
1282
+ uid: a,
1287
1283
  sdkThemeKeys: Object.keys(n).length,
1288
1284
  sdkAppKeys: Object.keys(o).length,
1289
1285
  // 'mem' indicates in-memory backstop fired (cache was empty,
@@ -1291,73 +1287,72 @@ function dt() {
1291
1287
  themeSource: Object.keys(e).length > 0 ? "cache" : "mem",
1292
1288
  appSource: Object.keys(t).length > 0 ? "cache" : "mem"
1293
1289
  }), { ...o, ...n };
1294
- kt.add(i);
1295
- const s = In() ?? {}, a = { ...s, ...o, ...n };
1290
+ wt.add(a);
1291
+ const i = _n() ?? {}, s = { ...i, ...o, ...n };
1296
1292
  return d("preferences", "Loaded (sync, first read — legacy + SDK)", {
1297
- uid: i,
1293
+ uid: a,
1298
1294
  sdkThemeKeys: Object.keys(n).length,
1299
1295
  sdkAppKeys: Object.keys(o).length,
1300
- legacyKeys: Object.keys(s).length,
1296
+ legacyKeys: Object.keys(i).length,
1301
1297
  themeSource: Object.keys(e).length > 0 ? "cache" : "mem",
1302
1298
  appSource: Object.keys(t).length > 0 ? "cache" : "mem"
1303
- }), a;
1299
+ }), s;
1304
1300
  }
1305
- let kn = !1;
1301
+ let Sn = !1;
1306
1302
  function Co() {
1307
- const e = ct();
1303
+ const e = st();
1308
1304
  return !e || e === "public";
1309
1305
  }
1310
- function K(e, t) {
1311
- Co() && (kn = !0), wo.has(e) ? ce.save({ [e]: t }, { scope: "device" }).catch((n) => A("preferences", "themePrefs.save failed", { key: e, err: n?.message })) : me.save({ [e]: t }).catch((n) => A("preferences", "watchpartyPrefs.save failed", { key: e, err: n?.message })), d("preferences", "Saved (via SDK)", { key: e, value: t });
1306
+ function G(e, t) {
1307
+ Co() && (Sn = !0), wo.has(e) ? ae.save({ [e]: t }, { scope: "device" }).catch((n) => k("preferences", "themePrefs.save failed", { key: e, err: n?.message })) : se.save({ [e]: t }).catch((n) => k("preferences", "watchpartyPrefs.save failed", { key: e, err: n?.message })), d("preferences", "Saved (via SDK)", { key: e, value: t });
1312
1308
  }
1313
- let we = null, It = !1;
1314
- function ko() {
1315
- return we || (we = (async () => {
1309
+ let Se = null, xt = !1;
1310
+ function To() {
1311
+ return Se || (Se = (async () => {
1316
1312
  if (typeof window > "u") return !1;
1317
1313
  try {
1318
- It || (It = !0, ce.subscribe((a) => {
1319
- xn = a, At("portfolio", a), Nt("theme");
1320
- }), me.subscribe((a) => {
1321
- Cn = a, At("watchparty", a), Nt("watchparty");
1314
+ xt || (xt = !0, ae.subscribe((s) => {
1315
+ yn = s, Tt("portfolio", s), kt("theme");
1316
+ }), se.subscribe((s) => {
1317
+ vn = s, Tt("watchparty", s), kt("watchparty");
1322
1318
  }));
1323
1319
  const [e, t] = await Promise.all([
1324
- ce.read(),
1325
- me.read()
1320
+ ae.read(),
1321
+ se.read()
1326
1322
  ]);
1327
1323
  try {
1328
- const { useAppStore: a } = await Promise.resolve().then(() => En);
1329
- a.getState().rehydrateFromPrefs();
1330
- } catch (a) {
1331
- A("preferences", "inline rehydrate after SDK read failed", { err: a?.message });
1324
+ const { useAppStore: s } = await Promise.resolve().then(() => xn);
1325
+ s.getState().rehydrateFromPrefs();
1326
+ } catch (s) {
1327
+ k("preferences", "inline rehydrate after SDK read failed", { err: s?.message });
1332
1328
  }
1333
- const n = Oe("portfolio"), o = Oe("watchparty"), i = n ? G(n) : null, s = o ? G(o) : null;
1329
+ const n = Re("portfolio"), o = Re("watchparty"), a = n ? B(n) : null, i = o ? B(o) : null;
1334
1330
  return d("preferences", "sdk.read", {
1335
1331
  themeKeys: Object.keys(e ?? {}),
1336
1332
  appKeys: Object.keys(t ?? {}),
1337
1333
  appName: t?.name,
1338
- appSprite: t?.sprite,
1339
1334
  // Post-fetch cache state: should be non-zero if SDK wrote it.
1340
- sdkThemeBytesPost: i?.length ?? 0,
1341
- sdkAppBytesPost: s?.length ?? 0
1342
- }), await Io(e, t), kn ? (d("preferences", "Promoting this load's pre-auth picks to the signed-in user"), await Promise.all([
1343
- ce.promoteAnonPrefsToUser().catch(() => {
1335
+ sdkThemeBytesPost: a?.length ?? 0,
1336
+ sdkAppBytesPost: i?.length ?? 0
1337
+ }), await ko(e, t), Sn ? (d("preferences", "Promoting this load's pre-auth picks to the signed-in user"), await Promise.all([
1338
+ ae.promoteAnonPrefsToUser().catch(() => {
1344
1339
  }),
1345
- me.promoteAnonPrefsToUser().catch(() => {
1340
+ se.promoteAnonPrefsToUser().catch(() => {
1346
1341
  })
1347
1342
  ])) : d("preferences", "Skipping anon promotion — no pre-auth writes this load"), d("preferences", "First-load SDK sync complete"), !0;
1348
1343
  } catch (e) {
1349
- return A("preferences", "First-load SDK sync failed (non-fatal)", { err: e?.message }), !1;
1344
+ return k("preferences", "First-load SDK sync failed (non-fatal)", { err: e?.message }), !1;
1350
1345
  }
1351
- })(), we);
1346
+ })(), Se);
1352
1347
  }
1353
- const Re = "watchparty_preferences";
1354
- xo();
1355
- function Ze() {
1356
- const e = ct();
1357
- return !e || e === "public" ? Re : `${Re}:${e}`;
1348
+ const De = "watchparty_preferences";
1349
+ Io();
1350
+ function Xe() {
1351
+ const e = st();
1352
+ return !e || e === "public" ? De : `${De}:${e}`;
1358
1353
  }
1359
- function In() {
1360
- const e = G(Ze());
1354
+ function _n() {
1355
+ const e = B(Xe());
1361
1356
  if (!e) return null;
1362
1357
  try {
1363
1358
  const t = JSON.parse(e);
@@ -1366,40 +1361,40 @@ function In() {
1366
1361
  return null;
1367
1362
  }
1368
1363
  }
1369
- function Tt() {
1370
- Me(Ze()), Me(`${Ze()}:_serverSynced_v1`);
1364
+ function It() {
1365
+ Ne(Xe()), Ne(`${Xe()}:_serverSynced_v1`);
1371
1366
  }
1372
- async function Io(e, t) {
1373
- const n = In();
1367
+ async function ko(e, t) {
1368
+ const n = _n();
1374
1369
  if (!n || Object.keys(n).length === 0) return;
1375
- const o = Object.keys(e ?? {}).length > 0, i = Object.keys(t ?? {}).length > 0, s = {}, a = {};
1376
- for (const [g, p] of Object.entries(n))
1377
- o || (g === "theme" && typeof p == "string" ? s.theme = p : g === "themeMode" && (p === "simple" || p === "advanced") && (s.themeMode = p)), i || (g === "sprite" && typeof p == "string" ? a.sprite = p : g === "chatCollapsed" && typeof p == "boolean" ? a.chatCollapsed = p : g === "floatingChatEnabled" && typeof p == "boolean" ? a.floatingChatEnabled = p : g === "volume" && typeof p == "number" && p >= 0 && p <= 1 ? a.volume = p : g === "muted" && typeof p == "boolean" ? a.muted = p : g === "preferredAudioLang" && typeof p == "string" ? a.preferredAudioLang = p : g === "subtitlesEnabled" && typeof p == "boolean" ? a.subtitlesEnabled = p : g === "subtitleStyle" && typeof p == "string" && (a.subtitleStyle = p));
1378
- const c = Object.keys(s), f = Object.keys(a);
1379
- if (c.length === 0 && f.length === 0) {
1380
- Tt();
1370
+ const o = Object.keys(e ?? {}).length > 0, a = Object.keys(t ?? {}).length > 0, i = {}, s = {};
1371
+ for (const [h, f] of Object.entries(n))
1372
+ o || (h === "theme" && typeof f == "string" ? i.theme = f : h === "themeMode" && (f === "simple" || f === "advanced") && (i.themeMode = f)), a || (h === "chatCollapsed" && typeof f == "boolean" ? s.chatCollapsed = f : h === "floatingChatEnabled" && typeof f == "boolean" ? s.floatingChatEnabled = f : h === "volume" && typeof f == "number" && f >= 0 && f <= 1 ? s.volume = f : h === "muted" && typeof f == "boolean" ? s.muted = f : h === "preferredAudioLang" && typeof f == "string" ? s.preferredAudioLang = f : h === "subtitlesEnabled" && typeof f == "boolean" ? s.subtitlesEnabled = f : h === "subtitleStyle" && typeof f == "string" && (s.subtitleStyle = f));
1373
+ const l = Object.keys(i), p = Object.keys(s);
1374
+ if (l.length === 0 && p.length === 0) {
1375
+ It();
1381
1376
  return;
1382
1377
  }
1383
1378
  d("preferences", "Migrating legacy localStorage prefs to SDK", {
1384
- themeKeys: c,
1385
- appKeys: f,
1379
+ themeKeys: l,
1380
+ appKeys: p,
1386
1381
  sdkHasTheme: o,
1387
- sdkHasApp: i
1382
+ sdkHasApp: a
1388
1383
  });
1389
1384
  try {
1390
1385
  await Promise.all([
1391
- c.length > 0 ? ce.save(s, { scope: "device" }) : Promise.resolve(),
1392
- f.length > 0 ? me.save(a) : Promise.resolve()
1393
- ]), Tt(), d("preferences", "Legacy migration complete");
1394
- } catch (g) {
1395
- A("preferences", "Legacy migration save failed — will retry next load", {
1396
- err: g?.message
1386
+ l.length > 0 ? ae.save(i, { scope: "device" }) : Promise.resolve(),
1387
+ p.length > 0 ? se.save(s) : Promise.resolve()
1388
+ ]), It(), d("preferences", "Legacy migration complete");
1389
+ } catch (h) {
1390
+ k("preferences", "Legacy migration save failed — will retry next load", {
1391
+ err: h?.message
1397
1392
  });
1398
1393
  }
1399
1394
  }
1400
- let Et = !1;
1401
- function At(e, t) {
1402
- const n = Oe(e);
1395
+ let Ct = !1;
1396
+ function Tt(e, t) {
1397
+ const n = Re(e);
1403
1398
  if (n)
1404
1399
  try {
1405
1400
  const o = {
@@ -1410,41 +1405,41 @@ function At(e, t) {
1410
1405
  };
1411
1406
  localStorage.setItem(n, JSON.stringify(o));
1412
1407
  } catch (o) {
1413
- Et || (Et = !0, A("preferences", "writeThrough localStorage failed (in-memory backstop only)", {
1408
+ Ct || (Ct = !0, k("preferences", "writeThrough localStorage failed (in-memory backstop only)", {
1414
1409
  appId: e,
1415
1410
  err: o?.message,
1416
1411
  name: o?.name
1417
1412
  }));
1418
1413
  }
1419
1414
  }
1420
- function Nt(e) {
1421
- Promise.resolve().then(() => En).then(({ useAppStore: t }) => {
1415
+ function kt(e) {
1416
+ Promise.resolve().then(() => xn).then(({ useAppStore: t }) => {
1422
1417
  try {
1423
1418
  t.getState().rehydrateFromPrefs();
1424
1419
  } catch (n) {
1425
- A("preferences", "rehydrateFromPrefs threw", { reason: e, err: n?.message });
1420
+ k("preferences", "rehydrateFromPrefs threw", { reason: e, err: n?.message });
1426
1421
  }
1427
- }).catch((t) => A("preferences", "store import failed", { reason: e, err: t?.message }));
1422
+ }).catch((t) => k("preferences", "store import failed", { reason: e, err: t?.message }));
1428
1423
  }
1429
- const Tn = 320, De = 240, ut = 640, To = 320, Eo = 36, Pt = 16;
1430
- function V(e, t) {
1431
- if (!Number.isFinite(e)) return Tn;
1432
- let n = ut;
1433
- return t !== void 0 && Number.isFinite(t) && t > 0 && (n = Math.min(n, t - To)), n = Math.max(n, De), Math.round(Math.min(n, Math.max(De, e)));
1424
+ const wn = 320, Oe = 240, ct = 640, Ao = 320, Eo = 36, At = 16;
1425
+ function W(e, t) {
1426
+ if (!Number.isFinite(e)) return wn;
1427
+ let n = ct;
1428
+ return t !== void 0 && Number.isFinite(t) && t > 0 && (n = Math.min(n, t - Ao)), n = Math.max(n, Oe), Math.round(Math.min(n, Math.max(Oe, e)));
1434
1429
  }
1435
- function Mt(e, t, n, o) {
1436
- return V(e + (t - n), o);
1430
+ function Et(e, t, n, o) {
1431
+ return W(e + (t - n), o);
1437
1432
  }
1438
- function Ao(e, t, n) {
1433
+ function Po(e, t, n) {
1439
1434
  switch (t) {
1440
1435
  case "ArrowLeft":
1441
- return V(e + Pt, n);
1436
+ return W(e + At, n);
1442
1437
  case "ArrowRight":
1443
- return V(e - Pt, n);
1438
+ return W(e - At, n);
1444
1439
  case "Home":
1445
- return V(De, n);
1440
+ return W(Oe, n);
1446
1441
  case "End":
1447
- return V(ut, n);
1442
+ return W(ct, n);
1448
1443
  default:
1449
1444
  return null;
1450
1445
  }
@@ -1452,22 +1447,21 @@ function Ao(e, t, n) {
1452
1447
  function No(e, t) {
1453
1448
  return { collapsed: e, width: e ? Eo : t };
1454
1449
  }
1455
- const q = dt(), m = Gn((e, t) => ({
1450
+ const K = lt(), u = $n((e, t) => ({
1456
1451
  ready: !1,
1457
1452
  name: "",
1458
- sprite: q.sprite || null,
1453
+ sprite: null,
1459
1454
  me: { clientId: null, nextClientSeq: 1 },
1460
1455
  snapshot: { roomId: null, mediaId: null, playheadMs: 0, playing: !1, serverSeq: 0, leaderId: null },
1461
1456
  roster: {},
1462
1457
  chat: [],
1463
- chatCollapsed: q.chatCollapsed ?? !1,
1464
- chromeChatCollapsed: q.chromeChatCollapsed ?? !1,
1465
- chromeChatWidth: V(q.chromeChatWidth ?? Tn),
1458
+ chatCollapsed: K.chatCollapsed ?? !1,
1459
+ chromeChatCollapsed: K.chromeChatCollapsed ?? !1,
1460
+ chromeChatWidth: W(K.chromeChatWidth ?? wn),
1466
1461
  // Hardcoded on for everyone — the per-user toggle was removed from the
1467
1462
  // control bar; FloatingChat + toggleFloatingChat code is kept for future
1468
1463
  // re-enablement but the value is no longer user-controllable.
1469
1464
  floatingChatEnabled: !0,
1470
- characterModalOpen: !1,
1471
1465
  drift: { rtts: [], offsetMs: 0 },
1472
1466
  snapshotReceivedAt: Date.now(),
1473
1467
  loadGeneration: 0,
@@ -1475,11 +1469,11 @@ const q = dt(), m = Gn((e, t) => ({
1475
1469
  bufferState: { isBuffering: !1, bufferedEndMs: 0 },
1476
1470
  toasts: [],
1477
1471
  mediaPrefs: {
1478
- volume: q.volume ?? 1,
1479
- muted: q.muted ?? !1,
1480
- preferredAudioLang: q.preferredAudioLang || "jpn",
1481
- subtitlesEnabled: q.subtitlesEnabled ?? !0,
1482
- subtitleStyle: ["classic", "cinema", "minimal", "contrast"].includes(q.subtitleStyle) ? q.subtitleStyle : "classic"
1472
+ volume: K.volume ?? 1,
1473
+ muted: K.muted ?? !1,
1474
+ preferredAudioLang: K.preferredAudioLang || "jpn",
1475
+ subtitlesEnabled: K.subtitlesEnabled ?? !0,
1476
+ subtitleStyle: ["classic", "cinema", "minimal", "contrast"].includes(K.subtitleStyle) ? K.subtitleStyle : "classic"
1483
1477
  },
1484
1478
  healthSnapshot: null,
1485
1479
  sidebarTab: "chat",
@@ -1503,55 +1497,52 @@ const q = dt(), m = Gn((e, t) => ({
1503
1497
  setReady: () => {
1504
1498
  d("store", "App ready"), e({ ready: !0 });
1505
1499
  },
1506
- setIdentity: (n, o) => {
1507
- d("store", "Identity set", { name: n, sprite: o }), e({ name: n, sprite: o }), o && K("sprite", o);
1508
- },
1509
1500
  setSessionIdentity: (n, o) => {
1510
1501
  d("store", "Session identity set (not persisted)", { name: n, sprite: o }), e({ name: n, sprite: o });
1511
1502
  },
1512
1503
  ensureAutoIdentity: () => e((n) => {
1513
1504
  if (n.name) return {};
1514
1505
  if (n.sprite) {
1515
- const i = te(n.sprite);
1516
- return d("store", "Auto-identity: deriving name from saved sprite", { name: i, sprite: n.sprite }), { name: i };
1506
+ const a = de(n.sprite);
1507
+ return d("store", "Auto-identity: deriving name from the requested sprite", { name: a, sprite: n.sprite }), { name: a };
1517
1508
  }
1518
- const o = new Set(Object.values(n.roster).map((i) => i.sprite).filter(Boolean));
1509
+ const o = new Set(Object.values(n.roster).map((a) => a.sprite).filter(Boolean));
1519
1510
  d("store", "ensureAutoIdentity checking", {
1520
1511
  rosterSize: Object.keys(n.roster).length,
1521
1512
  usedSprites: Array.from(o)
1522
1513
  });
1523
- for (const i of Le)
1524
- if (!o.has(i)) {
1525
- const s = te(i);
1526
- return d("store", "Auto-identity assigned", { name: s, sprite: i }), { name: s, sprite: i };
1514
+ for (const a of Me)
1515
+ if (!o.has(a)) {
1516
+ const i = de(a);
1517
+ return d("store", "Auto-identity assigned", { name: i, sprite: a }), { name: i, sprite: a };
1527
1518
  }
1528
- return A("store", "All characters taken, leaving identity unset"), {};
1519
+ return k("store", "All characters taken, leaving identity unset"), {};
1529
1520
  }),
1530
1521
  ensureMe: (n) => {
1531
1522
  d("store", "Client ID set", { clientId: n }), e((o) => ({ me: { ...o.me, clientId: n } }));
1532
1523
  },
1533
1524
  applySnapshot: (n) => e((o) => {
1534
- const i = { ...o.roster };
1525
+ const a = { ...o.roster };
1535
1526
  if (n.users)
1536
- for (const f of n.users) i[f.clientId] = f;
1537
- const s = n.mediaId !== void 0 ? n.mediaId : o.snapshot.mediaId, a = n.leaderId !== void 0 ? n.leaderId : o.snapshot.leaderId;
1527
+ for (const p of n.users) a[p.clientId] = p;
1528
+ const i = n.mediaId !== void 0 ? n.mediaId : o.snapshot.mediaId, s = n.leaderId !== void 0 ? n.leaderId : o.snapshot.leaderId;
1538
1529
  d("store", "Snapshot applied", {
1539
- mediaId: s,
1530
+ mediaId: i,
1540
1531
  playing: n.playing ?? o.snapshot.playing,
1541
1532
  serverSeq: n.serverSeq ?? o.snapshot.serverSeq,
1542
1533
  userCount: n.users?.length ?? 0
1543
1534
  });
1544
- const c = n.playheadMs ?? o.snapshot.playheadMs;
1535
+ const l = n.playheadMs ?? o.snapshot.playheadMs;
1545
1536
  return {
1546
1537
  snapshot: {
1547
1538
  roomId: n.roomId !== void 0 ? n.roomId : o.snapshot.roomId,
1548
- mediaId: s,
1549
- playheadMs: c,
1539
+ mediaId: i,
1540
+ playheadMs: l,
1550
1541
  playing: n.playing ?? o.snapshot.playing,
1551
1542
  serverSeq: n.serverSeq ?? o.snapshot.serverSeq,
1552
- leaderId: a
1543
+ leaderId: s
1553
1544
  },
1554
- roster: i,
1545
+ roster: a,
1555
1546
  // Always update — initial snapshot on join often has playheadMs=0
1556
1547
  // matching the store default, so the old playheadChanged guard left
1557
1548
  // snapshotReceivedAt at page-load time. This inflated viewer correction
@@ -1564,8 +1555,8 @@ const q = dt(), m = Gn((e, t) => ({
1564
1555
  },
1565
1556
  removeUser: (n) => {
1566
1557
  d("store", "User removed", { clientId: n }), e((o) => {
1567
- const i = { ...o.roster };
1568
- return delete i[n], { roster: i };
1558
+ const a = { ...o.roster };
1559
+ return delete a[n], { roster: a };
1569
1560
  });
1570
1561
  },
1571
1562
  appendChat: (n) => {
@@ -1574,21 +1565,20 @@ const q = dt(), m = Gn((e, t) => ({
1574
1565
  setChat: (n) => e({ chat: n.slice(-500) }),
1575
1566
  toggleChatCollapsed: () => e((n) => {
1576
1567
  const o = !n.chatCollapsed;
1577
- return K("chatCollapsed", o), { chatCollapsed: o };
1568
+ return G("chatCollapsed", o), { chatCollapsed: o };
1578
1569
  }),
1579
1570
  toggleChromeChatCollapsed: () => e((n) => {
1580
1571
  const o = !n.chromeChatCollapsed;
1581
- return K("chromeChatCollapsed", o), { chromeChatCollapsed: o };
1572
+ return G("chromeChatCollapsed", o), { chromeChatCollapsed: o };
1582
1573
  }),
1583
1574
  setChromeChatWidth: (n) => e(() => {
1584
- const o = V(n);
1585
- return K("chromeChatWidth", o), { chromeChatWidth: o };
1575
+ const o = W(n);
1576
+ return G("chromeChatWidth", o), { chromeChatWidth: o };
1586
1577
  }),
1587
1578
  toggleFloatingChat: () => e((n) => {
1588
1579
  const o = !n.floatingChatEnabled;
1589
- return K("floatingChatEnabled", o), { floatingChatEnabled: o };
1580
+ return G("floatingChatEnabled", o), { floatingChatEnabled: o };
1590
1581
  }),
1591
- setCharacterModalOpen: (n) => e({ characterModalOpen: n }),
1592
1582
  allocateClientSeq: () => {
1593
1583
  const n = t().me.nextClientSeq;
1594
1584
  return e((o) => ({ me: { ...o.me, nextClientSeq: n + 1 } })), n;
@@ -1605,28 +1595,28 @@ const q = dt(), m = Gn((e, t) => ({
1605
1595
  snapshotReceivedAt: Date.now()
1606
1596
  }));
1607
1597
  },
1608
- addRttSample: (n, o, i) => e((s) => {
1609
- const a = [...s.drift.rtts, n].slice(-5);
1610
- let c = s.drift.offsetMs;
1611
- if (o && i) {
1612
- const f = i + n / 2;
1613
- c = o - f;
1598
+ addRttSample: (n, o, a) => e((i) => {
1599
+ const s = [...i.drift.rtts, n].slice(-5);
1600
+ let l = i.drift.offsetMs;
1601
+ if (o && a) {
1602
+ const p = a + n / 2;
1603
+ l = o - p;
1614
1604
  }
1615
- return { drift: { rtts: a, offsetMs: c } };
1605
+ return { drift: { rtts: s, offsetMs: l } };
1616
1606
  }),
1617
1607
  pushToast: (n, o) => {
1618
- d("store", "Toast pushed", { kind: n, msg: o }), e((i) => ({ toasts: [...i.toasts, { id: Math.random().toString(36).slice(2), kind: n, msg: o, ts: Date.now() }].slice(-5) }));
1608
+ d("store", "Toast pushed", { kind: n, msg: o }), e((a) => ({ toasts: [...a.toasts, { id: Math.random().toString(36).slice(2), kind: n, msg: o, ts: Date.now() }].slice(-5) }));
1619
1609
  },
1620
- popToast: (n) => e((o) => ({ toasts: o.toasts.filter((i) => i.id !== n) })),
1610
+ popToast: (n) => e((o) => ({ toasts: o.toasts.filter((a) => a.id !== n) })),
1621
1611
  setReadiness: (n) => e({ readiness: { mediaId: n.mediaId, ready: n.ready, readyCount: n.readyCount, total: n.total } }),
1622
1612
  setBufferState: (n) => e((o) => ({ bufferState: { ...o.bufferState, ...n } })),
1623
1613
  setMutedForAutoplay: (n) => e({ mutedForAutoplay: n }),
1624
1614
  // Media preference actions
1625
- setVolume: (n) => e((o) => (K("volume", n), { mediaPrefs: { ...o.mediaPrefs, volume: n } })),
1626
- setMuted: (n) => e((o) => (K("muted", n), { mediaPrefs: { ...o.mediaPrefs, muted: n } })),
1627
- setPreferredAudioLang: (n) => e((o) => (K("preferredAudioLang", n), { mediaPrefs: { ...o.mediaPrefs, preferredAudioLang: n } })),
1628
- setSubtitlesEnabled: (n) => e((o) => (K("subtitlesEnabled", n), { mediaPrefs: { ...o.mediaPrefs, subtitlesEnabled: n } })),
1629
- setSubtitleStyle: (n) => e((o) => (K("subtitleStyle", n), { mediaPrefs: { ...o.mediaPrefs, subtitleStyle: n } })),
1615
+ setVolume: (n) => e((o) => (G("volume", n), { mediaPrefs: { ...o.mediaPrefs, volume: n } })),
1616
+ setMuted: (n) => e((o) => (G("muted", n), { mediaPrefs: { ...o.mediaPrefs, muted: n } })),
1617
+ setPreferredAudioLang: (n) => e((o) => (G("preferredAudioLang", n), { mediaPrefs: { ...o.mediaPrefs, preferredAudioLang: n } })),
1618
+ setSubtitlesEnabled: (n) => e((o) => (G("subtitlesEnabled", n), { mediaPrefs: { ...o.mediaPrefs, subtitlesEnabled: n } })),
1619
+ setSubtitleStyle: (n) => e((o) => (G("subtitleStyle", n), { mediaPrefs: { ...o.mediaPrefs, subtitleStyle: n } })),
1630
1620
  setHealthSnapshot: (n) => e({ healthSnapshot: n }),
1631
1621
  setBufferStatus: (n) => e({ bufferStatus: n }),
1632
1622
  applyPlaylistState: (n) => e({
@@ -1649,9 +1639,9 @@ const q = dt(), m = Gn((e, t) => ({
1649
1639
  * board or saves its own, which are the two moments its draft is not ahead of
1650
1640
  * the server.
1651
1641
  */
1652
- setTierlistSnapshot: (n, o) => e((i) => ({
1642
+ setTierlistSnapshot: (n, o) => e((a) => ({
1653
1643
  tierlist: n,
1654
- tierlistDraft: o ? { ...n.myBoard } : i.tierlistDraft
1644
+ tierlistDraft: o ? { ...n.myBoard } : a.tierlistDraft
1655
1645
  })),
1656
1646
  /**
1657
1647
  * Move one item on the draft board. Local only — nothing reaches the server
@@ -1662,17 +1652,17 @@ const q = dt(), m = Gn((e, t) => ({
1662
1652
  * present-with-a-low-value — is what keeps "not rated" distinguishable from
1663
1653
  * "rated F" in placeTierlist.
1664
1654
  */
1665
- moveTierlistItem: (n, o, i) => e((s) => {
1666
- const a = {};
1667
- for (const [c, f] of Object.entries(s.tierlistDraft)) {
1668
- const g = (f ?? []).filter((p) => p !== n);
1669
- g.length > 0 && (a[c] = g);
1655
+ moveTierlistItem: (n, o, a) => e((i) => {
1656
+ const s = {};
1657
+ for (const [l, p] of Object.entries(i.tierlistDraft)) {
1658
+ const h = (p ?? []).filter((f) => f !== n);
1659
+ h.length > 0 && (s[l] = h);
1670
1660
  }
1671
1661
  if (o !== null) {
1672
- const c = a[o] ?? [], f = Math.max(0, Math.min(i ?? c.length, c.length));
1673
- c.splice(f, 0, n), a[o] = c;
1662
+ const l = s[o] ?? [], p = Math.max(0, Math.min(a ?? l.length, l.length));
1663
+ l.splice(p, 0, n), s[o] = l;
1674
1664
  }
1675
- return { tierlistDraft: a };
1665
+ return { tierlistDraft: s };
1676
1666
  }),
1677
1667
  setReadyCheckState: (n) => e({ readyCheck: n }),
1678
1668
  setActiveGameId: (n) => e({ activeGameId: n }),
@@ -1681,82 +1671,119 @@ const q = dt(), m = Gn((e, t) => ({
1681
1671
  setGameSessionWins: (n) => e({ gameSessionWins: { ...n } }),
1682
1672
  setGameBotCount: (n) => e({ gameBotCount: Math.max(0, Math.floor(n)) }),
1683
1673
  rehydrateFromPrefs: () => e((n) => {
1684
- const o = dt(), i = {};
1685
- if (o.sprite !== void 0 && (i.sprite = o.sprite || null), o.chatCollapsed !== void 0 && (i.chatCollapsed = o.chatCollapsed), o.chromeChatCollapsed !== void 0 && (i.chromeChatCollapsed = o.chromeChatCollapsed), o.chromeChatWidth !== void 0 && (i.chromeChatWidth = V(o.chromeChatWidth)), ["volume", "muted", "preferredAudioLang", "subtitlesEnabled", "subtitleStyle"].some((c) => o[c] !== void 0)) {
1686
- const c = { ...n.mediaPrefs };
1687
- o.volume !== void 0 && (c.volume = o.volume), o.muted !== void 0 && (c.muted = o.muted), o.preferredAudioLang !== void 0 && (c.preferredAudioLang = o.preferredAudioLang), o.subtitlesEnabled !== void 0 && (c.subtitlesEnabled = o.subtitlesEnabled), o.subtitleStyle !== void 0 && ["classic", "cinema", "minimal", "contrast"].includes(o.subtitleStyle) && (c.subtitleStyle = o.subtitleStyle), i.mediaPrefs = c;
1674
+ const o = lt(), a = {};
1675
+ if (o.chatCollapsed !== void 0 && (a.chatCollapsed = o.chatCollapsed), o.chromeChatCollapsed !== void 0 && (a.chromeChatCollapsed = o.chromeChatCollapsed), o.chromeChatWidth !== void 0 && (a.chromeChatWidth = W(o.chromeChatWidth)), ["volume", "muted", "preferredAudioLang", "subtitlesEnabled", "subtitleStyle"].some((l) => o[l] !== void 0)) {
1676
+ const l = { ...n.mediaPrefs };
1677
+ o.volume !== void 0 && (l.volume = o.volume), o.muted !== void 0 && (l.muted = o.muted), o.preferredAudioLang !== void 0 && (l.preferredAudioLang = o.preferredAudioLang), o.subtitlesEnabled !== void 0 && (l.subtitlesEnabled = o.subtitlesEnabled), o.subtitleStyle !== void 0 && ["classic", "cinema", "minimal", "contrast"].includes(o.subtitleStyle) && (l.subtitleStyle = o.subtitleStyle), a.mediaPrefs = l;
1688
1678
  }
1689
- return i;
1679
+ return a;
1690
1680
  }),
1691
1681
  setSidebarTab: (n) => e((o) => o.sidebarTab === n && n !== "chat" ? { sidebarTab: "chat" } : { sidebarTab: n, chatCollapsed: !1 })
1692
1682
  }));
1693
- Fe("__ZUSTAND_STORE__", m);
1694
- const En = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1683
+ Ue("__ZUSTAND_STORE__", u);
1684
+ const xn = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1695
1685
  __proto__: null,
1696
- useAppStore: m
1686
+ useAppStore: u
1697
1687
  }, Symbol.toStringTag, { value: "Module" }));
1698
- function Po(e) {
1699
- return `${hn()}/media/${e}`.replace(/\\/g, "/");
1688
+ function Ro(e) {
1689
+ return `${dn()}/media/${e}`.replace(/\\/g, "/");
1700
1690
  }
1701
- function Mo(e, t) {
1702
- return Po(t ?? `${e}/poster.jpg`);
1691
+ function Do(e, t) {
1692
+ return Ro(t ?? `${e}/poster.jpg`);
1703
1693
  }
1704
1694
  function Oo(e) {
1705
- return `${hn()}${e}`.replace(/\\/g, "/");
1695
+ return `${dn()}${e}`.replace(/\\/g, "/");
1706
1696
  }
1707
- async function Ro(e) {
1697
+ async function Mo(e) {
1708
1698
  try {
1709
1699
  const t = await fetch(Oo("/api/tierlist"));
1710
1700
  if (!t.ok)
1711
- return A("tierlist", "Board fetch failed", { status: t.status }), null;
1701
+ return k("tierlist", "Board fetch failed", { status: t.status }), null;
1712
1702
  const n = await t.json();
1713
- return m.getState().setTierlistSnapshot(n, e), d("tierlist", "Board loaded", {
1703
+ return u.getState().setTierlistSnapshot(n, e), d("tierlist", "Board loaded", {
1714
1704
  items: n.items.length,
1715
1705
  voters: n.voterCount,
1716
1706
  placed: n.placed.length
1717
1707
  }), n;
1718
1708
  } catch (t) {
1719
- return A("tierlist", "Board fetch threw", ge(t)), null;
1709
+ return k("tierlist", "Board fetch threw", fe(t)), null;
1720
1710
  }
1721
1711
  }
1722
- let Ot = !1;
1723
- function Do() {
1724
- if (Ot || typeof Image > "u") return;
1725
- const e = m.getState().tierlist;
1712
+ let Pt = !1;
1713
+ function Lo() {
1714
+ if (Pt || typeof Image > "u") return;
1715
+ const e = u.getState().tierlist;
1726
1716
  if (!(!e || e.items.length === 0)) {
1727
- Ot = !0;
1717
+ Pt = !0;
1728
1718
  for (const t of e.items) {
1729
1719
  const n = new Image();
1730
- n.decoding = "async", n.src = Mo(t.id, t.thumbnail);
1720
+ n.decoding = "async", n.src = Do(t.id, t.thumbnail);
1731
1721
  }
1732
1722
  d("tierlist", "Warming posters", { count: e.items.length });
1733
1723
  }
1734
1724
  }
1735
- function Lo() {
1736
- const e = m.getState(), t = new Set(Object.values(e.roster).map((s) => s.sprite).filter(Boolean)), n = Le.find((s) => !t.has(s));
1725
+ function jo() {
1726
+ const e = u.getState(), t = new Set(Object.values(e.roster).map((i) => i.sprite).filter(Boolean)), n = Me.find((i) => !t.has(i));
1737
1727
  if (!n)
1738
- return A("socket", "sprite_taken: no available sprites to retry with"), null;
1739
- const o = !!e.name && !ne(e.name), i = o ? e.name : te(n);
1740
- return d("socket", "sprite_taken: retrying with next available", { from: e.sprite, to: n, name: i, keptCustomName: o }), e.setSessionIdentity(i, n), { name: i, sprite: n };
1728
+ return k("socket", "sprite_taken: no available sprites to retry with"), null;
1729
+ const o = !!e.name && !J(e.name), a = o ? e.name : de(n);
1730
+ return d("socket", "sprite_taken: retrying with next available", { from: e.sprite, to: n, name: a, keptCustomName: o }), e.setSessionIdentity(a, n), { name: a, sprite: n };
1741
1731
  }
1742
- function jo(e) {
1743
- const t = m.getState(), n = e ? t.roster[e] : void 0;
1732
+ function Uo(e) {
1733
+ const t = u.getState(), n = e ? t.roster[e] : void 0;
1744
1734
  return !n || !!n.sprite && n.sprite === t.sprite;
1745
1735
  }
1746
- const Uo = 10;
1747
- function Fo(e = typeof window < "u" ? window : void 0) {
1736
+ const Nt = "hadoku:avatar";
1737
+ function Fo(e) {
1738
+ return e === "discord" || e === "steam" || e === "upload";
1739
+ }
1740
+ function In(e) {
1741
+ const t = e?.sprite ?? e?.avatar?.sprite ?? null;
1742
+ return tt(t) ? t : null;
1743
+ }
1744
+ async function Ho(e = {}) {
1745
+ const t = e.fetch ?? (typeof fetch > "u" ? void 0 : fetch), n = typeof window > "u" ? void 0 : window.location?.origin, o = e.baseUrl ?? (n ? `${n}/prefs` : void 0);
1746
+ if (!t || !o) return null;
1747
+ try {
1748
+ const a = await qn({ baseUrl: o, fetch: t }).get(), i = In(a);
1749
+ return d("avatar", "Requested character from prefs-api", { sprite: i }), i;
1750
+ } catch (a) {
1751
+ return a instanceof ht && (a.status === 401 || a.status === 403 || a.status === 404) ? d("avatar", "No avatar settings here (signed out, or no prefs-api on this origin)", { status: a.status }) : k("avatar", "Avatar settings read failed — keeping the auto-assigned character", {
1752
+ status: a instanceof ht ? a.status : void 0,
1753
+ ...fe(a)
1754
+ }), null;
1755
+ }
1756
+ }
1757
+ function Rt(e) {
1758
+ if (!e) return !1;
1759
+ const t = u.getState();
1760
+ if (t.sprite === e) return !1;
1761
+ const o = !!(t.me.clientId && t.roster[t.me.clientId]?.registryName) || !t.name || !J(t.name) ? t.name : de(e);
1762
+ return t.setSessionIdentity(o, e), !0;
1763
+ }
1764
+ function $o(e, t = typeof window > "u" ? void 0 : window) {
1765
+ if (!t) return () => {
1766
+ };
1767
+ const n = (o) => {
1768
+ const a = o.detail;
1769
+ !a || typeof a != "object" || e(a);
1770
+ };
1771
+ return t.addEventListener(Nt, n), () => t.removeEventListener(Nt, n);
1772
+ }
1773
+ const Bo = 10;
1774
+ function Go(e = typeof window < "u" ? window : void 0) {
1748
1775
  if (!e) return { framed: !1, depth: 0 };
1749
1776
  try {
1750
1777
  let t = 0, n = e;
1751
- for (; n !== n.parent && t < Uo; )
1778
+ for (; n !== n.parent && t < Bo; )
1752
1779
  t++, n = n.parent;
1753
1780
  return { framed: e.top ? e.top !== e.self : t > 0, depth: t };
1754
1781
  } catch {
1755
1782
  return { framed: !1, depth: 0 };
1756
1783
  }
1757
1784
  }
1758
- const Ho = "watchparty_fragment_delivery";
1759
- function $o() {
1785
+ const Ko = "watchparty_fragment_delivery";
1786
+ function Wo() {
1760
1787
  if (typeof window > "u") return null;
1761
1788
  try {
1762
1789
  const e = new URLSearchParams(window.location.search).get("fragmentDelivery");
@@ -1764,93 +1791,93 @@ function $o() {
1764
1791
  if (e === "0" || e === "false") return !1;
1765
1792
  } catch {
1766
1793
  }
1767
- return Jr(Ho);
1794
+ return Qr(Ko);
1768
1795
  }
1769
- let Bo = { ...rn }, Be = null;
1770
- const Rt = "watchparty_delivery_client_id";
1771
- function Go() {
1772
- const e = G(Rt);
1796
+ let qo = { ...en }, He = null;
1797
+ const Dt = "watchparty_delivery_client_id";
1798
+ function Vo() {
1799
+ const e = B(Dt);
1773
1800
  if (e) return e;
1774
1801
  const t = globalThis.crypto?.randomUUID?.() ?? `d${Math.random().toString(36).slice(2)}`;
1775
- return at(Rt, t) ? t : "";
1802
+ return ot(Dt, t) ? t : "";
1776
1803
  }
1777
- function Wo() {
1778
- return Be === null && (Be = _r(Bo, Go(), $o())), Be;
1804
+ function Yo() {
1805
+ return He === null && (He = wr(qo, Vo(), Wo())), He;
1779
1806
  }
1780
- function Ko() {
1781
- return Wo();
1807
+ function zo() {
1808
+ return Yo();
1782
1809
  }
1783
- let Ee;
1784
- function An() {
1785
- return G(qn) || void 0;
1810
+ let Ae;
1811
+ function Cn() {
1812
+ return B(Kn) || void 0;
1786
1813
  }
1787
- function qo() {
1788
- if (Ee) return;
1789
- const e = An();
1814
+ function Xo() {
1815
+ if (Ae) return;
1816
+ const e = Cn();
1790
1817
  if (e) {
1791
- Ee = e;
1818
+ Ae = e;
1792
1819
  return;
1793
1820
  }
1794
- Kn().then((t) => {
1795
- t && (Ee = t);
1821
+ Gn().then((t) => {
1822
+ t && (Ae = t);
1796
1823
  }).catch(() => {
1797
1824
  d("identity", "device id unavailable — joining without one");
1798
1825
  });
1799
1826
  }
1800
- function Vo() {
1801
- return Ee ??= An();
1827
+ function Jo() {
1828
+ return Ae ??= Cn();
1802
1829
  }
1803
- function zo(e, t) {
1830
+ function Qo(e, t) {
1804
1831
  let n = 0;
1805
1832
  for (const o of e.videos) {
1806
- const i = oe.voteKey(t, e.id, o.mediaId);
1807
- globalThis.localStorage?.getItem(i) != null && n++, Me(i);
1833
+ const a = te.voteKey(t, e.id, o.mediaId);
1834
+ globalThis.localStorage?.getItem(a) != null && n++, Ne(a);
1808
1835
  }
1809
1836
  return n;
1810
1837
  }
1811
- const Yo = 2e4, Xo = 1e4;
1812
- function Jo() {
1838
+ const Zo = 2e4, ea = 1e4;
1839
+ function ta() {
1813
1840
  return { unansweredSince: null, lastPongAt: null, lastTickAt: null };
1814
1841
  }
1815
- function Zo(e, t) {
1842
+ function na(e, t) {
1816
1843
  e.lastPongAt = t, e.unansweredSince = null;
1817
1844
  }
1818
- function Qo(e, t, n) {
1819
- const o = e.lastTickAt !== null && t - e.lastTickAt > Xo;
1820
- return e.lastTickAt = t, n ? o || e.unansweredSince === null ? (e.unansweredSince = t, !1) : t - e.unansweredSince > Yo : (e.unansweredSince = null, !1);
1845
+ function ra(e, t, n) {
1846
+ const o = e.lastTickAt !== null && t - e.lastTickAt > ea;
1847
+ return e.lastTickAt = t, n ? o || e.unansweredSince === null ? (e.unansweredSince = t, !1) : t - e.unansweredSince > Zo : (e.unansweredSince = null, !1);
1821
1848
  }
1822
- function ei(e, t) {
1849
+ function oa(e, t) {
1823
1850
  return e.lastPongAt === null ? void 0 : Math.max(0, t - e.lastPongAt);
1824
1851
  }
1825
- let x = null, $ = null, Ae = null, B = null, z = null;
1826
- const ti = oi(), ee = Jo();
1827
- let _e = null, Ge = null, Dt = 0, mt = null, Qe = !1, pt = !1, le = !1, Nn = null;
1828
- function ni(e) {
1829
- const t = ie(), n = m.getState();
1852
+ let _ = null, H = null, Ee = null, $ = null, q = null;
1853
+ const aa = la(), Z = ta();
1854
+ let _e = null, $e = null, Ot = 0, dt = null, ue = !1, ut = !1, oe = !1, Tn = null;
1855
+ function ia(e) {
1856
+ const t = ne(), n = u.getState();
1830
1857
  if (n.me.clientId && n.roster[n.me.clientId])
1831
- return (e.name || e.sprite) && (d("socket", "Identity update (already joined)", { name: e.name, sprite: e.sprite }), t.emit(I.IDENTITY_UPDATE, { name: e.name, sprite: e.sprite })), t;
1832
- const o = ri(e);
1833
- return z = o, mt = { name: o.name, sprite: o.sprite }, t.connected ? (d("socket", "Emitting join", o), t.emit(I.JOIN, o)) : t.once("connect", () => {
1834
- d("socket", "Emitting join", o), t.emit(I.JOIN, o);
1858
+ return (e.name || e.sprite) && (d("socket", "Identity update (already joined)", { name: e.name, sprite: e.sprite }), t.emit(w.IDENTITY_UPDATE, { name: e.name, sprite: e.sprite })), t;
1859
+ const o = sa(e);
1860
+ return q = o, dt = { name: o.name, sprite: o.sprite }, t.connected ? (d("socket", "Emitting join", o), t.emit(w.JOIN, o)) : t.once("connect", () => {
1861
+ d("socket", "Emitting join", o), t.emit(w.JOIN, o);
1835
1862
  }), t;
1836
1863
  }
1837
- function ri(e) {
1864
+ function sa(e) {
1838
1865
  return {
1839
1866
  room_key: e.room_key,
1840
1867
  name: e.name,
1841
1868
  sprite: e.sprite,
1842
1869
  // Entry-path telemetry — see lib/embedContext.ts. Sent on every join AND
1843
1870
  // rejoin (lastJoinPayload carries it), so a reconnect keeps the attribution.
1844
- embed: Fo(),
1845
- delivery_arm: Ko(),
1871
+ embed: Go(),
1872
+ delivery_arm: zo(),
1846
1873
  // Identity tier input — see lib/deviceId.ts. Sent on every join AND rejoin
1847
1874
  // (lastJoinPayload carries it) so a reconnect lands on the same
1848
1875
  // ParticipantId rather than minting a new person.
1849
- device_id: Vo(),
1850
- tab_id: ti
1876
+ device_id: Jo(),
1877
+ tab_id: aa
1851
1878
  };
1852
1879
  }
1853
- function oi() {
1880
+ function la() {
1854
1881
  const e = globalThis.crypto;
1855
1882
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
1856
1883
  const t = new Uint8Array(16);
@@ -1858,67 +1885,67 @@ function oi() {
1858
1885
  else for (let n = 0; n < t.length; n++) t[n] = Math.floor(Math.random() * 256);
1859
1886
  return Array.from(t, (n) => n.toString(16).padStart(2, "0")).join("");
1860
1887
  }
1861
- function ie() {
1862
- if (!x) {
1863
- let t = he().serverOrigin || "/";
1888
+ function ne() {
1889
+ if (!_) {
1890
+ let t = me().serverOrigin || "/";
1864
1891
  if (t === "/" && typeof window < "u") {
1865
1892
  const o = window.location;
1866
- (o.hostname === "localhost" || o.hostname === "127.0.0.1") && o.port === String(zt) && (t = `${o.protocol}//${o.hostname}:${tt}`, d("socket", "Using dev fallback origin", { origin: t }));
1893
+ (o.hostname === "localhost" || o.hostname === "127.0.0.1") && o.port === String(qt) && (t = `${o.protocol}//${o.hostname}:${Qe}`, d("socket", "Using dev fallback origin", { origin: t }));
1867
1894
  }
1868
1895
  const n = new URL(t.startsWith("http") ? t : `${window.location.origin}${t}`).origin;
1869
- x = Vn(n, { path: Jn, transports: ["websocket", "polling"] }), d("socket", "Socket initialized", { origin: t }), Fe("__SOCKET__", x), B && (clearTimeout(B), B = null), $ = null, Ae = setInterval(() => {
1870
- if (Qo(ee, Date.now(), !!x?.connected)) {
1871
- A("socket", "PONG-WATCHDOG no time_pong for 20s — replacing the connection", {
1872
- sid: x?.id,
1873
- lastPongAgeMs: ei(ee, Date.now()) ?? null
1874
- }), Ge = "pong-watchdog", ee.unansweredSince = null, x?.io.engine?.close();
1896
+ _ = Wn(n, { path: Xn, transports: ["websocket", "polling"] }), d("socket", "Socket initialized", { origin: t }), Ue("__SOCKET__", _), $ && (clearTimeout($), $ = null), H = null, Ee = setInterval(() => {
1897
+ if (ra(Z, Date.now(), !!_?.connected)) {
1898
+ k("socket", "PONG-WATCHDOG no time_pong for 20s — replacing the connection", {
1899
+ sid: _?.id,
1900
+ lastPongAgeMs: oa(Z, Date.now()) ?? null
1901
+ }), $e = "pong-watchdog", Z.unansweredSince = null, _?.io.engine?.close();
1875
1902
  return;
1876
1903
  }
1877
- hi();
1878
- }, br), x.on("connect", () => {
1879
- pt = !1;
1880
- const o = x?.id;
1881
- if (d("socket", "Connected", { id: o, isReconnect: le }), o)
1904
+ va();
1905
+ }, br), _.on("connect", () => {
1906
+ ut = !1;
1907
+ const o = _?.id;
1908
+ if (d("socket", "Connected", { id: o, isReconnect: oe }), o)
1882
1909
  try {
1883
- m.getState().ensureMe(o), m.setState((i) => ({ me: { ...i.me, nextClientSeq: 1 } })), d("socket", "Reset client sequence for new connection");
1884
- } catch (i) {
1885
- ue("socket", "Failed to initialize store on connect", { error: i.message, sid: o });
1910
+ u.getState().ensureMe(o), u.setState((a) => ({ me: { ...a.me, nextClientSeq: 1 } })), d("socket", "Reset client sequence for new connection");
1911
+ } catch (a) {
1912
+ ke("socket", "Failed to initialize store on connect", { error: a.message, sid: o });
1886
1913
  }
1887
- if (le && z) {
1888
- const i = m.getState();
1889
- Dt += 1;
1890
- const s = {
1891
- ...z,
1892
- name: i.name,
1893
- sprite: i.sprite,
1914
+ if (oe && q) {
1915
+ const a = u.getState();
1916
+ Ot += 1;
1917
+ const i = {
1918
+ ...q,
1919
+ name: a.name,
1920
+ sprite: a.sprite,
1894
1921
  reconnect: {
1895
- count: Dt,
1922
+ count: Ot,
1896
1923
  reason: (_e?.reason ?? "unknown").slice(0, 64),
1897
1924
  offline_ms: _e ? Math.max(0, Date.now() - _e.at) : 0
1898
1925
  }
1899
- }, a = Nn;
1900
- if (d("socket", "Re-emitting join after reconnect", { room_key: s.room_key, name: s.name, sprite: s.sprite }), a) {
1901
- let c = !1;
1902
- const f = () => {
1903
- c || (c = !0, x.off(I.SNAPSHOT, f), x.emit(I.CLIENT_READY, { media_id: a }), d("socket", "Re-emitted client_ready after reconnect snapshot", { mediaId: a }));
1926
+ }, s = Tn;
1927
+ if (d("socket", "Re-emitting join after reconnect", { room_key: i.room_key, name: i.name, sprite: i.sprite }), s) {
1928
+ let l = !1;
1929
+ const p = () => {
1930
+ l || (l = !0, _.off(w.SNAPSHOT, p), _.emit(w.CLIENT_READY, { media_id: s }), d("socket", "Re-emitted client_ready after reconnect snapshot", { mediaId: s }));
1904
1931
  };
1905
- x.on(I.SNAPSHOT, f), setTimeout(() => {
1906
- c || (c = !0, x.off(I.SNAPSHOT, f), A("socket", "client_ready re-emit timed out waiting for snapshot", { mediaId: a }));
1932
+ _.on(w.SNAPSHOT, p), setTimeout(() => {
1933
+ l || (l = !0, _.off(w.SNAPSHOT, p), k("socket", "client_ready re-emit timed out waiting for snapshot", { mediaId: s }));
1907
1934
  }, 1e4);
1908
1935
  }
1909
- mt = { name: s.name, sprite: s.sprite }, x.emit(I.JOIN, s), window.dispatchEvent(new CustomEvent("watchparty:socket-reconnect"));
1910
- } else le && A("socket", "Reconnected — no join payload, not re-joining any room");
1911
- le = !0;
1912
- }), x.on("disconnect", (o) => {
1913
- Sn(null), _e = { reason: Ge ?? o, at: Date.now() }, Ge = null, d("socket", "Disconnected", { reason: o });
1914
- }), x.on("connect_error", (o) => ue("socket", "Connection error", { message: o?.message })), x.on("reconnect_attempt", (o) => A("socket", "Reconnect attempt", { attempt: o })), x.on("reconnect_failed", () => ue("socket", "Reconnect failed")), pi(x);
1936
+ dt = { name: i.name, sprite: i.sprite }, _.emit(w.JOIN, i), window.dispatchEvent(new CustomEvent("watchparty:socket-reconnect"));
1937
+ } else oe && k("socket", "Reconnected — no join payload, not re-joining any room");
1938
+ oe = !0;
1939
+ }), _.on("disconnect", (o) => {
1940
+ hn(null), _e = { reason: $e ?? o, at: Date.now() }, $e = null, d("socket", "Disconnected", { reason: o });
1941
+ }), _.on("connect_error", (o) => ke("socket", "Connection error", { message: o?.message })), _.on("reconnect_attempt", (o) => k("socket", "Reconnect attempt", { attempt: o })), _.on("reconnect_failed", () => ke("socket", "Reconnect failed")), ba(_);
1915
1942
  }
1916
- return x;
1943
+ return _;
1917
1944
  }
1918
- function We(e) {
1945
+ function Be(e) {
1919
1946
  return e.play_state === "playing" || e.playing === !0;
1920
1947
  }
1921
- function Lt(e, t) {
1948
+ function Mt(e, t) {
1922
1949
  return {
1923
1950
  id: String(e.id ?? e.server_seq ?? t ?? `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`),
1924
1951
  clientId: e.client_id,
@@ -1930,52 +1957,52 @@ function Lt(e, t) {
1930
1957
  // Attachments arrive from other clients, so the key shape is re-checked
1931
1958
  // here at the boundary rather than trusted at render time. A malformed
1932
1959
  // entry is dropped, not rendered and not allowed near an <img src>.
1933
- images: Array.isArray(e.images) ? e.images.filter((n) => !!n && et(n.key)).slice(0, Y) : void 0,
1960
+ images: Array.isArray(e.images) ? e.images.filter((n) => !!n && Je(n.key)).slice(0, V) : void 0,
1934
1961
  isSystem: e.isSystem || !1,
1935
1962
  systemType: e.systemType
1936
1963
  };
1937
1964
  }
1938
- function ii(e) {
1939
- e.on(I.SNAPSHOT, (t) => {
1940
- e.emit(tn.SYNC), m.getState().applySnapshot({
1965
+ function ca(e) {
1966
+ e.on(w.SNAPSHOT, (t) => {
1967
+ e.emit(Zt.SYNC), u.getState().applySnapshot({
1941
1968
  roomId: t.room_id ?? null,
1942
1969
  mediaId: t.media_id ?? null,
1943
1970
  playheadMs: t.playhead_ms || 0,
1944
- playing: We(t),
1971
+ playing: Be(t),
1945
1972
  serverSeq: t.server_seq || 0,
1946
1973
  leaderId: t.leader_id || null
1947
- }), m.getState().name || d("socket", "Deferring identity to presence event (snapshot carries no roster)");
1948
- const n = m.getState(), o = x?.id, s = (o ? n.roster[o] : void 0) ?? mt, a = zn(
1974
+ }), u.getState().name || d("socket", "Deferring identity to presence event (snapshot carries no roster)");
1975
+ const n = u.getState(), o = _?.id, i = (o ? n.roster[o] : void 0) ?? dt, s = Vn(
1949
1976
  { name: n.name, avatarId: n.sprite },
1950
- s ? { name: s.name, avatarId: s.sprite ?? null } : void 0,
1977
+ i ? { name: i.name, avatarId: i.sprite ?? null } : void 0,
1951
1978
  // A join that carried no name counts as a default one, so a name that
1952
1979
  // landed after the join (prefs) is still asserted.
1953
- (c) => c === "" || ne(c)
1980
+ (l) => l === "" || J(l)
1954
1981
  );
1955
- if (a) {
1956
- const c = {
1957
- ...a.name !== void 0 ? { name: a.name } : {},
1958
- ...a.avatarId !== void 0 && !pt ? { sprite: a.avatarId } : {}
1982
+ if (s) {
1983
+ const l = {
1984
+ ...s.name !== void 0 ? { name: s.name } : {},
1985
+ ...s.avatarId !== void 0 && !ut ? { sprite: s.avatarId } : {}
1959
1986
  };
1960
- Object.keys(c).length > 0 && (d("socket", "Identity update on snapshot", c), e.emit(I.IDENTITY_UPDATE, c));
1987
+ Object.keys(l).length > 0 && (d("socket", "Identity update on snapshot", l), e.emit(w.IDENTITY_UPDATE, l));
1961
1988
  }
1962
1989
  d("socket", "Received snapshot", {
1963
1990
  mediaId: t.media_id ?? null,
1964
1991
  playheadMs: t.playhead_ms || 0,
1965
- playing: We(t),
1992
+ playing: Be(t),
1966
1993
  leaderId: t.leader_id || null,
1967
1994
  serverSeq: t.server_seq || 0,
1968
1995
  rosterSize: Object.keys(n.roster).length
1969
- }), z?.room_key && x?.id && Sn({ room_key: z.room_key, client_id: x.id });
1970
- }), e.on(I.CONTROL_BROADCAST, (t) => {
1971
- const n = m.getState();
1996
+ }), q?.room_key && _?.id && hn({ room_key: q.room_key, client_id: _.id });
1997
+ }), e.on(w.CONTROL_BROADCAST, (t) => {
1998
+ const n = u.getState();
1972
1999
  if (Object.prototype.hasOwnProperty.call(t, "media_id")) {
1973
2000
  const o = t.media_id ?? null;
1974
2001
  n.snapshot.mediaId !== o && (n.applySnapshot({ mediaId: o }), o && n.setReadiness({ mediaId: o, ready: !1, readyCount: 0, total: 0 }), n.bumpLoadGeneration());
1975
2002
  }
1976
2003
  n.updateControlState({
1977
2004
  playheadMs: t.playhead_ms,
1978
- playing: We(t),
2005
+ playing: Be(t),
1979
2006
  serverSeq: t.server_seq,
1980
2007
  leaderId: t.leader_id ?? n.snapshot.leaderId
1981
2008
  }), d("socket", "Control broadcast received", {
@@ -1985,131 +2012,132 @@ function ii(e) {
1985
2012
  playState: t.play_state,
1986
2013
  mediaId: t.media_id
1987
2014
  });
1988
- }), e.on(I.CONTROL_ACK, (t) => {
2015
+ }), e.on(w.CONTROL_ACK, (t) => {
1989
2016
  typeof t?.client_seq == "number" && typeof t?.server_seq == "number" && d("socket", "Control ack received", t);
1990
- }), e.on(I.TIME_PONG, (t) => {
1991
- if (Zo(ee, Date.now()), $ && $.ts) {
1992
- const n = Date.now() - $.ts;
1993
- m.getState().addRttSample(n, t?.server_ts, $.ts), $ = null, B && (clearTimeout(B), B = null), d("socket", "Time sync RTT", { rtt: n, offset: m.getState().drift.offsetMs });
2017
+ }), e.on(w.TIME_PONG, (t) => {
2018
+ if (na(Z, Date.now()), H && H.ts) {
2019
+ const n = Date.now() - H.ts;
2020
+ u.getState().addRttSample(n, t?.server_ts, H.ts), H = null, $ && (clearTimeout($), $ = null), d("socket", "Time sync RTT", { rtt: n, offset: u.getState().drift.offsetMs });
1994
2021
  }
1995
- }), e.on(I.READY_STATE, (t) => {
2022
+ }), e.on(w.READY_STATE, (t) => {
1996
2023
  if (!t) return;
1997
- const n = t.media_id ?? null, o = typeof t.ready_count == "number" ? t.ready_count : 0, i = typeof t.total == "number" ? t.total : 0, s = t.ready === !0;
1998
- m.getState().setReadiness({ mediaId: n, ready: s, readyCount: o, total: i }), d("socket", "Ready state received", { mediaId: n, readyCount: o, total: i, ready: s });
2024
+ const n = t.media_id ?? null, o = typeof t.ready_count == "number" ? t.ready_count : 0, a = typeof t.total == "number" ? t.total : 0, i = t.ready === !0;
2025
+ u.getState().setReadiness({ mediaId: n, ready: i, readyCount: o, total: a }), d("socket", "Ready state received", { mediaId: n, readyCount: o, total: a, ready: i });
1999
2026
  });
2000
2027
  }
2001
- function ai(e) {
2002
- e.on(I.PRESENCE, (t) => {
2028
+ function da(e) {
2029
+ e.on(w.PRESENCE, (t) => {
2003
2030
  if (!t) return;
2004
2031
  if (Array.isArray(t.users)) {
2005
2032
  const o = {};
2006
- for (const c of t.users)
2007
- o[c.client_id] = {
2008
- clientId: c.client_id,
2009
- ...c.author ? { author: c.author } : {},
2010
- name: c.name || "Anon",
2011
- sprite: c.sprite_id ?? c.sprite ?? null,
2012
- isLeader: c.is_leader,
2013
- ...c.registry_name ? { registryName: !0 } : {}
2033
+ for (const l of t.users)
2034
+ o[l.client_id] = {
2035
+ clientId: l.client_id,
2036
+ ...l.author ? { author: l.author } : {},
2037
+ name: l.name || "Anon",
2038
+ sprite: l.sprite_id ?? l.sprite ?? null,
2039
+ isLeader: l.is_leader,
2040
+ ...l.registry_name ? { registryName: !0 } : {},
2041
+ ...l.avatar_url && Fo(l.avatar_source) ? { avatarUrl: l.avatar_url, avatarSource: l.avatar_source } : {}
2014
2042
  };
2015
- const i = m.getState(), s = x?.id, a = s ? o[s] : void 0;
2016
- if (a?.registryName)
2017
- m.setState({ roster: o }), (a.name !== i.name || a.sprite && a.sprite !== i.sprite) && (d("socket", "Adopting registry name", { from: i.name, to: a.name }), m.getState().setSessionIdentity(a.name, a.sprite ?? i.sprite));
2018
- else if (s && o[s]) {
2019
- const c = o[s].name, f = o[s].sprite;
2020
- if (i.name && !ne(i.name) && (o[s].name = i.name), m.setState({ roster: o }), f && f !== i.sprite) {
2021
- const g = i.name && !ne(i.name), p = g ? i.name : te(f);
2022
- d("socket", "Server reassigned sprite", { from: i.sprite, to: f, newName: p, preservingCustomName: g }), m.getState().setSessionIdentity(p, f), p !== c && e.emit(I.IDENTITY_UPDATE, { name: p });
2043
+ const a = u.getState(), i = _?.id, s = i ? o[i] : void 0;
2044
+ if (s?.registryName)
2045
+ u.setState({ roster: o }), (s.name !== a.name || s.sprite && s.sprite !== a.sprite) && (d("socket", "Adopting registry name", { from: a.name, to: s.name }), u.getState().setSessionIdentity(s.name, s.sprite ?? a.sprite));
2046
+ else if (i && o[i]) {
2047
+ const l = o[i].name, p = o[i].sprite;
2048
+ if (a.name && !J(a.name) && (o[i].name = a.name), u.setState({ roster: o }), p && p !== a.sprite) {
2049
+ const h = a.name && !J(a.name), f = h ? a.name : de(p);
2050
+ d("socket", "Server reassigned sprite", { from: a.sprite, to: p, newName: f, preservingCustomName: h }), u.getState().setSessionIdentity(f, p), f !== l && e.emit(w.IDENTITY_UPDATE, { name: f });
2023
2051
  }
2024
2052
  }
2025
2053
  }
2026
2054
  d("socket", "Received presence update", t);
2027
- const n = m.getState();
2055
+ const n = u.getState();
2028
2056
  if (!n.name) {
2029
2057
  n.ensureAutoIdentity?.();
2030
- const o = m.getState();
2031
- o.name && (d("socket", "Auto-assigned identity on presence", { name: o.name, sprite: o.sprite }), e.emit(I.IDENTITY_UPDATE, { name: o.name, sprite: o.sprite || void 0 }));
2058
+ const o = u.getState();
2059
+ o.name && (d("socket", "Auto-assigned identity on presence", { name: o.name, sprite: o.sprite }), e.emit(w.IDENTITY_UPDATE, { name: o.name, sprite: o.sprite || void 0 }));
2032
2060
  }
2033
2061
  });
2034
2062
  }
2035
- function si(e) {
2036
- e.on(I.CHAT_BROADCAST, (t) => {
2037
- const n = Lt(t, t.server_seq);
2038
- m.getState().appendChat(n), d("chat", "Message received", n);
2039
- }), e.on(I.CHAT_HISTORY, (t) => {
2063
+ function ua(e) {
2064
+ e.on(w.CHAT_BROADCAST, (t) => {
2065
+ const n = Mt(t, t.server_seq);
2066
+ u.getState().appendChat(n), d("chat", "Message received", n);
2067
+ }), e.on(w.CHAT_HISTORY, (t) => {
2040
2068
  if (!t?.messages || !Array.isArray(t.messages)) return;
2041
- const n = t.messages.map((o) => Lt(o));
2042
- m.getState().setChat(n), d("chat", "History loaded", { count: n.length });
2069
+ const n = t.messages.map((o) => Mt(o));
2070
+ u.getState().setChat(n), d("chat", "History loaded", { count: n.length });
2043
2071
  });
2044
2072
  }
2045
- function li(e) {
2046
- e.on(ye.STATE, (t) => {
2047
- m.getState().applyPlaylistState({
2073
+ function ma(e) {
2074
+ e.on(be.STATE, (t) => {
2075
+ u.getState().applyPlaylistState({
2048
2076
  playlist: t?.playlist ?? null,
2049
2077
  activeIndex: t?.activeIndex ?? null,
2050
2078
  myVotes: t?.myVotes ?? {}
2051
2079
  }), d("playlist", "Playlist state received", { playlistId: t?.playlist?.id ?? null, activeIndex: t?.activeIndex, myVoteCount: Object.keys(t?.myVotes || {}).length });
2052
- }), e.on(ye.VOTE_PROGRESS, (t) => {
2053
- m.getState().setVoteProgress(t?.progress ?? []), d("playlist", "Vote progress", { progressCount: t?.progress?.length });
2054
- }), e.on(ye.RESULTS, (t) => {
2055
- m.getState().setPlaylistResults(t ?? null), d("playlist", "Results received", { playlistId: t?.playlist?.id, update: t?.update === !0 });
2056
- }), e.on(ye.VOTES_CLEARED, (t) => {
2080
+ }), e.on(be.VOTE_PROGRESS, (t) => {
2081
+ u.getState().setVoteProgress(t?.progress ?? []), d("playlist", "Vote progress", { progressCount: t?.progress?.length });
2082
+ }), e.on(be.RESULTS, (t) => {
2083
+ u.getState().setPlaylistResults(t ?? null), d("playlist", "Results received", { playlistId: t?.playlist?.id, update: t?.update === !0 });
2084
+ }), e.on(be.VOTES_CLEARED, (t) => {
2057
2085
  if (!t?.playlistId || !Array.isArray(t.mediaIds)) return;
2058
- const n = zo(
2059
- { id: t.playlistId, videos: t.mediaIds.map((i) => ({ mediaId: i })) },
2060
- bo()
2061
- ), o = m.getState();
2086
+ const n = Qo(
2087
+ { id: t.playlistId, videos: t.mediaIds.map((a) => ({ mediaId: a })) },
2088
+ yo()
2089
+ ), o = u.getState();
2062
2090
  o.playlist?.id === t.playlistId && o.applyPlaylistState({ playlist: o.playlist, activeIndex: o.activePlaylistIndex, myVotes: {} }), d("playlist", "Votes cleared by leader", { playlistId: t.playlistId, removed: n });
2063
2091
  });
2064
2092
  }
2065
- function ci(e) {
2066
- e.on(dr.UPDATED, () => {
2067
- d("tierlist", "Someone saved — refreshing the board"), Ro(!1);
2093
+ function fa(e) {
2094
+ e.on(cr.UPDATED, () => {
2095
+ d("tierlist", "Someone saved — refreshing the board"), Mo(!1);
2068
2096
  });
2069
2097
  }
2070
- function di(e) {
2071
- e.on(ur.STATE, (t) => {
2098
+ function pa(e) {
2099
+ e.on(dr.STATE, (t) => {
2072
2100
  if (!t || typeof t.active != "boolean") return;
2073
2101
  const n = Array.isArray(t.ready) ? t.ready.filter((o) => typeof o == "string") : [];
2074
- m.getState().setReadyCheckState({ active: t.active, ready: n }), d("ready-check", "State received", { active: t.active, readyCount: n.length });
2102
+ u.getState().setReadyCheckState({ active: t.active, ready: n }), d("ready-check", "State received", { active: t.active, readyCount: n.length });
2075
2103
  });
2076
2104
  }
2077
- function ui(e) {
2078
- e.on(tn.ACTIVE, (t) => {
2105
+ function ha(e) {
2106
+ e.on(Zt.ACTIVE, (t) => {
2079
2107
  const n = typeof t?.gameId == "string" ? t.gameId : null;
2080
- m.getState().setActiveGameId(n), t?.sessionWins && typeof t.sessionWins == "object" ? m.getState().setGameSessionWins(t.sessionWins) : n === null && m.getState().setGameSessionWins({}), typeof t?.botCount == "number" ? m.getState().setGameBotCount(t.botCount) : n === null && m.getState().setGameBotCount(0), d("game", "game_active", { gameId: n, sessionWins: t?.sessionWins, botCount: t?.botCount });
2108
+ u.getState().setActiveGameId(n), t?.sessionWins && typeof t.sessionWins == "object" ? u.getState().setGameSessionWins(t.sessionWins) : n === null && u.getState().setGameSessionWins({}), typeof t?.botCount == "number" ? u.getState().setGameBotCount(t.botCount) : n === null && u.getState().setGameBotCount(0), d("game", "game_active", { gameId: n, sessionWins: t?.sessionWins, botCount: t?.botCount });
2081
2109
  });
2082
2110
  }
2083
- function mi(e) {
2084
- e.on(I.HEALTH_SNAPSHOT, (t) => {
2085
- m.getState().setHealthSnapshot(t);
2086
- }), e.on(I.BUFFER_STATUS, (t) => {
2087
- m.getState().setBufferStatus(t);
2111
+ function ga(e) {
2112
+ e.on(w.HEALTH_SNAPSHOT, (t) => {
2113
+ u.getState().setHealthSnapshot(t);
2114
+ }), e.on(w.BUFFER_STATUS, (t) => {
2115
+ u.getState().setBufferStatus(t);
2088
2116
  });
2089
2117
  }
2090
- function pi(e) {
2091
- ii(e), ai(e), si(e), mi(e), li(e), ci(e), di(e), ui(e), e.on(I.ERROR, (t) => {
2118
+ function ba(e) {
2119
+ ca(e), da(e), ua(e), ga(e), ma(e), fa(e), pa(e), ha(e), e.on(w.ERROR, (t) => {
2092
2120
  const n = t?.code || "error/unknown";
2093
2121
  if (n === "conflict" && t?.message === "sprite_held_by_you") {
2094
- pt = !0, d("socket", "sprite held by this account's other connection: keeping the server-assigned sprite");
2122
+ ut = !0, d("socket", "sprite held by this account's other connection: keeping the server-assigned sprite");
2095
2123
  return;
2096
2124
  }
2097
2125
  if (n === "conflict" && t?.message === "sprite_taken") {
2098
- if (jo(x?.id)) {
2126
+ if (Uo(_?.id)) {
2099
2127
  d("socket", "sprite_taken: stale, the server already has our sprite");
2100
2128
  return;
2101
2129
  }
2102
- Qe = !0;
2103
- let a;
2130
+ ue = !0;
2131
+ let s;
2104
2132
  try {
2105
- a = Lo();
2133
+ s = jo();
2106
2134
  } finally {
2107
- Qe = !1;
2135
+ ue = !1;
2108
2136
  }
2109
- a && e.emit(I.IDENTITY_UPDATE, a);
2137
+ s && e.emit(w.IDENTITY_UPDATE, s);
2110
2138
  return;
2111
2139
  }
2112
- const i = {
2140
+ const a = {
2113
2141
  "control/forbidden": "You are not the leader.",
2114
2142
  "control/stale_seq": "Stale control sequence.",
2115
2143
  "control/seq_gap": "Control sequence gap detected.",
@@ -2127,44 +2155,55 @@ function pi(e) {
2127
2155
  "recap/not_found": "No recap available for the previous episode."
2128
2156
  }[n] || n;
2129
2157
  if (n.startsWith("recap/")) {
2130
- d("socket", `recap unavailable: ${i}`, { code: n }), m.getState().pushToast("info", i);
2158
+ d("socket", `recap unavailable: ${a}`, { code: n }), u.getState().pushToast("info", a);
2131
2159
  return;
2132
2160
  }
2133
- (n.startsWith("auth/") ? A : ue)("socket", `Server error: ${i}`, { code: n, raw: t }), m.getState().pushToast("error", i);
2161
+ (n.startsWith("auth/") ? k : ke)("socket", `Server error: ${a}`, { code: n, raw: t }), u.getState().pushToast("error", a);
2134
2162
  });
2135
2163
  }
2136
- function fi(e, t) {
2137
- ie().emit(I.CHAT_SEND, t?.length ? { text: e, images: t } : { text: e });
2164
+ function ya(e, t) {
2165
+ ne().emit(w.CHAT_SEND, t?.length ? { text: e, images: t } : { text: e });
2138
2166
  }
2139
- function hi() {
2140
- $ || ($ = { ts: Date.now() }, ie().emit(I.TIME_PING, {}), B = setTimeout(() => {
2141
- $ && (d("socket", "Time ping timed out after 10s, clearing inflight"), $ = null), B = null;
2167
+ function va() {
2168
+ H || (H = { ts: Date.now() }, ne().emit(w.TIME_PING, {}), $ = setTimeout(() => {
2169
+ H && (d("socket", "Time ping timed out after 10s, clearing inflight"), H = null), $ = null;
2142
2170
  }, 1e4));
2143
2171
  }
2144
- function gi(e, t) {
2145
- const n = {};
2146
- e && (n.name = e), t && (n.sprite = t), !(!n.name && !n.sprite) && (d("socket", "Sending identity update", n), ie().emit(I.IDENTITY_UPDATE, n));
2172
+ function kn(e) {
2173
+ const t = u.getState(), n = t.me.clientId;
2174
+ if (!n || !t.roster[n]) {
2175
+ Rt(e), d("socket", "Avatar change before join: the join will carry it", { sprite: e });
2176
+ return;
2177
+ }
2178
+ ue = !0;
2179
+ try {
2180
+ Rt(e);
2181
+ } finally {
2182
+ ue = !1;
2183
+ }
2184
+ const o = u.getState(), a = { avatar: !0 };
2185
+ o.sprite && o.sprite !== t.sprite && (a.sprite = o.sprite), o.name && o.name !== t.name && (a.name = o.name), d("socket", "Sending avatar change", a), ne().emit(w.IDENTITY_UPDATE, a);
2147
2186
  }
2148
- let xe = { name: "", sprite: "" };
2149
- m.subscribe((e, t) => {
2150
- if (e.name === t.name && e.sprite === t.sprite || Qe) return;
2187
+ let we = { name: "", sprite: "" };
2188
+ u.subscribe((e, t) => {
2189
+ if (e.name === t.name && e.sprite === t.sprite || ue) return;
2151
2190
  const n = e.me.clientId;
2152
2191
  if (!n) return;
2153
2192
  const o = e.roster[n];
2154
2193
  if (!o) return;
2155
- const i = !!e.name && !ne(e.name) && e.name !== xe.name && (ne(o.name || "") || o.name !== e.name), s = !!e.sprite && e.sprite !== xe.sprite && o.sprite !== e.sprite;
2156
- if (!i && !s) return;
2157
- const a = {};
2158
- i && (a.name = e.name, xe.name = e.name), s && (a.sprite = e.sprite, xe.sprite = e.sprite), d("socket", "Identity reassert (store change)", a), x?.connected ? x.emit(I.IDENTITY_UPDATE, a) : ie().emit(I.IDENTITY_UPDATE, a);
2194
+ const a = !!e.name && !J(e.name) && e.name !== we.name && (J(o.name || "") || o.name !== e.name), i = !!e.sprite && e.sprite !== we.sprite && o.sprite !== e.sprite;
2195
+ if (!a && !i) return;
2196
+ const s = {};
2197
+ a && (s.name = e.name, we.name = e.name), i && (s.sprite = e.sprite, we.sprite = e.sprite), d("socket", "Identity reassert (store change)", s), _?.connected ? _.emit(w.IDENTITY_UPDATE, s) : ne().emit(w.IDENTITY_UPDATE, s);
2159
2198
  });
2160
- function bi() {
2161
- Ae && (clearInterval(Ae), Ae = null), B && (clearTimeout(B), B = null), $ = null, ee.unansweredSince = null, ee.lastTickAt = null;
2199
+ function Sa() {
2200
+ Ee && (clearInterval(Ee), Ee = null), $ && (clearTimeout($), $ = null), H = null, Z.unansweredSince = null, Z.lastTickAt = null;
2162
2201
  }
2163
- function yi() {
2164
- d("socket", "clearJoinState called", { hadPayload: z !== null, room_key: z?.room_key }), z = null, le = !1, Nn = null;
2202
+ function _a() {
2203
+ d("socket", "clearJoinState called", { hadPayload: q !== null, room_key: q?.room_key }), q = null, oe = !1, Tn = null;
2165
2204
  }
2166
- function jt() {
2167
- d("socket", "leaveRoom called", { room_key: z?.room_key }), yi(), bi(), x && (x.disconnect(), x = null), m.setState({
2205
+ function Lt() {
2206
+ d("socket", "leaveRoom called", { room_key: q?.room_key }), _a(), Sa(), _ && (_.disconnect(), _ = null), u.setState({
2168
2207
  me: { clientId: null, nextClientSeq: 1 },
2169
2208
  roster: {},
2170
2209
  snapshot: { roomId: null, mediaId: null, playheadMs: 0, playing: !1, serverSeq: 0, leaderId: null },
@@ -2180,22 +2219,54 @@ function jt() {
2180
2219
  gameSessionWins: {}
2181
2220
  });
2182
2221
  }
2183
- function vi() {
2184
- d("chat", "Emitting recap_request"), ie().emit("recap_request");
2222
+ function wa() {
2223
+ d("chat", "Emitting recap_request"), ne().emit("recap_request");
2185
2224
  }
2186
- function Si(e) {
2225
+ function xa() {
2226
+ D(() => $o((e) => {
2227
+ const t = In(e);
2228
+ d("avatar", "Avatar changed in the settings popout", { sprite: t, source: e.avatar?.source }), kn(t);
2229
+ }), []);
2230
+ }
2231
+ function Ia(e, t) {
2232
+ if (t) {
2233
+ for (const n of Object.values(e))
2234
+ if (n.author === t && n.avatarUrl) return n.avatarUrl;
2235
+ }
2236
+ }
2237
+ function jt(e) {
2238
+ return e.some((t) => t === "steam");
2239
+ }
2240
+ function An({ sprite: e, url: t, alt: n = "", className: o, fallback: a }) {
2241
+ const [i, s] = R(null);
2242
+ if (t && t !== i)
2243
+ return /* @__PURE__ */ c(
2244
+ "img",
2245
+ {
2246
+ src: t,
2247
+ alt: n,
2248
+ referrerPolicy: "no-referrer",
2249
+ className: o,
2250
+ draggable: !1,
2251
+ onError: () => s(t)
2252
+ }
2253
+ );
2254
+ const l = vo(e);
2255
+ return l ? /* @__PURE__ */ c("img", { src: l, alt: n, crossOrigin: "anonymous", className: o, draggable: !1 }) : /* @__PURE__ */ c(le, { children: a });
2256
+ }
2257
+ function Ca(e) {
2187
2258
  return !(e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey);
2188
2259
  }
2189
- function wi({ src: e, title: t, onClose: n }) {
2190
- O(() => {
2191
- const s = (a) => {
2192
- a.key === "Escape" && (a.stopPropagation(), n());
2260
+ function Ta({ src: e, title: t, onClose: n }) {
2261
+ D(() => {
2262
+ const i = (s) => {
2263
+ s.key === "Escape" && (s.stopPropagation(), n());
2193
2264
  };
2194
- return document.addEventListener("keydown", s, !0), () => document.removeEventListener("keydown", s, !0);
2265
+ return document.addEventListener("keydown", i, !0), () => document.removeEventListener("keydown", i, !0);
2195
2266
  }, [n]);
2196
- const o = Yr(), i = typeof document > "u" ? null : document.fullscreenElement ?? o ?? document.body;
2197
- return i ? Yn(
2198
- /* @__PURE__ */ b(
2267
+ const o = Xr(), a = typeof document > "u" ? null : document.fullscreenElement ?? o ?? document.body;
2268
+ return a ? Yn(
2269
+ /* @__PURE__ */ v(
2199
2270
  "div",
2200
2271
  {
2201
2272
  className: "fixed inset-0 z-[10000] flex flex-col items-center justify-center bg-black/85 p-6",
@@ -2205,18 +2276,18 @@ function wi({ src: e, title: t, onClose: n }) {
2205
2276
  "aria-modal": "true",
2206
2277
  "aria-label": t || "Image",
2207
2278
  children: [
2208
- /* @__PURE__ */ l(
2279
+ /* @__PURE__ */ c(
2209
2280
  "img",
2210
2281
  {
2211
2282
  src: e,
2212
2283
  alt: t ?? "",
2213
- onClick: (s) => s.stopPropagation(),
2284
+ onClick: (i) => i.stopPropagation(),
2214
2285
  className: "max-h-full max-w-full rounded object-contain shadow-2xl",
2215
2286
  "data-testid": "chat-image-lightbox-img"
2216
2287
  }
2217
2288
  ),
2218
- t && /* @__PURE__ */ l("p", { className: "mt-3 max-w-full truncate text-center text-sm text-white/80", children: t }),
2219
- /* @__PURE__ */ l(
2289
+ t && /* @__PURE__ */ c("p", { className: "mt-3 max-w-full truncate text-center text-sm text-white/80", children: t }),
2290
+ /* @__PURE__ */ c(
2220
2291
  "button",
2221
2292
  {
2222
2293
  type: "button",
@@ -2229,16 +2300,16 @@ function wi({ src: e, title: t, onClose: n }) {
2229
2300
  ]
2230
2301
  }
2231
2302
  ),
2232
- i
2303
+ a
2233
2304
  ) : null;
2234
2305
  }
2235
- const Ke = 240, _i = 320;
2236
- function xi({ image: e }) {
2237
- const [t, n] = P(!1), o = Pe(e.key);
2306
+ const Ge = 240, ka = 320;
2307
+ function Aa({ image: e }) {
2308
+ const [t, n] = R(!1), o = Pe(e.key);
2238
2309
  if (!o) return null;
2239
- const i = e.w > 0 && e.h > 0, s = i ? Math.min(1, _i / e.w, Ke / e.h) : 1;
2240
- return /* @__PURE__ */ b(Ne, { children: [
2241
- /* @__PURE__ */ l(
2310
+ const a = e.w > 0 && e.h > 0, i = a ? Math.min(1, ka / e.w, Ge / e.h) : 1;
2311
+ return /* @__PURE__ */ v(le, { children: [
2312
+ /* @__PURE__ */ c(
2242
2313
  "a",
2243
2314
  {
2244
2315
  href: o,
@@ -2246,26 +2317,26 @@ function xi({ image: e }) {
2246
2317
  rel: "noreferrer noopener",
2247
2318
  className: "block max-w-full cursor-zoom-in",
2248
2319
  "data-testid": "chat-image",
2249
- onClick: (a) => {
2250
- Si(a) && (a.preventDefault(), n(!0));
2320
+ onClick: (s) => {
2321
+ Ca(s) && (s.preventDefault(), n(!0));
2251
2322
  },
2252
- children: /* @__PURE__ */ l(
2323
+ children: /* @__PURE__ */ c(
2253
2324
  "img",
2254
2325
  {
2255
2326
  src: o,
2256
2327
  alt: e.title ?? "",
2257
2328
  loading: "lazy",
2258
2329
  decoding: "async",
2259
- width: i ? Math.round(e.w * s) : void 0,
2260
- height: i ? Math.round(e.h * s) : void 0,
2330
+ width: a ? Math.round(e.w * i) : void 0,
2331
+ height: a ? Math.round(e.h * i) : void 0,
2261
2332
  className: "rounded border border-border-light object-contain bg-bg-alt/30",
2262
- style: i ? { aspectRatio: `${e.w} / ${e.h}`, maxWidth: "100%", maxHeight: Ke } : { maxWidth: "100%", maxHeight: Ke }
2333
+ style: a ? { aspectRatio: `${e.w} / ${e.h}`, maxWidth: "100%", maxHeight: Ge } : { maxWidth: "100%", maxHeight: Ge }
2263
2334
  }
2264
2335
  )
2265
2336
  }
2266
2337
  ),
2267
- t && /* @__PURE__ */ l(
2268
- wi,
2338
+ t && /* @__PURE__ */ c(
2339
+ Ta,
2269
2340
  {
2270
2341
  src: o,
2271
2342
  title: e.title,
@@ -2274,80 +2345,81 @@ function xi({ image: e }) {
2274
2345
  )
2275
2346
  ] });
2276
2347
  }
2277
- function Ci({ images: e }) {
2278
- return e?.length ? /* @__PURE__ */ l("div", { className: "flex flex-wrap gap-1 mt-1", "data-testid": "chat-images", children: e.map((t, n) => /* @__PURE__ */ l(xi, { image: t }, `${t.key}-${n}`)) }) : null;
2348
+ function Ea({ images: e }) {
2349
+ return e?.length ? /* @__PURE__ */ c("div", { className: "flex flex-wrap gap-1 mt-1", "data-testid": "chat-images", children: e.map((t, n) => /* @__PURE__ */ c(Aa, { image: t }, `${t.key}-${n}`)) }) : null;
2279
2350
  }
2280
- function ki(e, t) {
2351
+ function Pa(e, t) {
2281
2352
  return e.author && t.author ? e.author === t.author : !!t.clientId && e.clientId === t.clientId;
2282
2353
  }
2283
- function qe(e) {
2354
+ function Ke(e) {
2284
2355
  return new Date(e).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
2285
2356
  }
2286
- const Ii = Bt.memo(function({ message: t }) {
2287
- const n = m((a) => a.me.clientId), o = m((a) => a.me.clientId ? a.roster[a.me.clientId]?.author : void 0), i = ki(t, { clientId: n, author: o });
2357
+ const Na = $t.memo(function({ message: t }) {
2358
+ const n = u((l) => l.me.clientId), o = u((l) => l.me.clientId ? l.roster[l.me.clientId]?.author : void 0), a = Pa(t, { clientId: n, author: o }), i = u((l) => Ia(l.roster, t.author));
2288
2359
  if (t.isSystem && t.systemType === "recap")
2289
- return /* @__PURE__ */ l("div", { className: "px-2 py-1.5", children: /* @__PURE__ */ b("div", { className: "border-l-2 border-border-light pl-2.5 py-0.5", children: [
2290
- /* @__PURE__ */ b("div", { className: "text-[10px] uppercase tracking-wide text-text-muted mb-0.5", children: [
2360
+ return /* @__PURE__ */ c("div", { className: "px-2 py-1.5", children: /* @__PURE__ */ v("div", { className: "border-l-2 border-border-light pl-2.5 py-0.5", children: [
2361
+ /* @__PURE__ */ v("div", { className: "text-[10px] uppercase tracking-wide text-text-muted mb-0.5", children: [
2291
2362
  "Recap",
2292
- /* @__PURE__ */ l("span", { className: "ml-1.5 normal-case tracking-normal opacity-60", children: qe(t.ts) })
2363
+ /* @__PURE__ */ c("span", { className: "ml-1.5 normal-case tracking-normal opacity-60", children: Ke(t.ts) })
2293
2364
  ] }),
2294
- /* @__PURE__ */ l("p", { className: "text-[13px] text-text-secondary leading-relaxed whitespace-pre-wrap break-words", children: t.text })
2365
+ /* @__PURE__ */ c("p", { className: "text-[13px] text-text-secondary leading-relaxed whitespace-pre-wrap break-words", children: t.text })
2295
2366
  ] }) });
2296
2367
  if (t.isSystem)
2297
- return /* @__PURE__ */ b("div", { className: "px-2 py-0.5 text-[11px] text-text-muted italic text-center leading-tight", children: [
2368
+ return /* @__PURE__ */ v("div", { className: "px-2 py-0.5 text-[11px] text-text-muted italic text-center leading-tight", children: [
2298
2369
  t.text,
2299
- /* @__PURE__ */ l("span", { className: "ml-1 text-[10px] opacity-50", children: qe(t.ts) })
2370
+ /* @__PURE__ */ c("span", { className: "ml-1 text-[10px] opacity-50", children: Ke(t.ts) })
2300
2371
  ] });
2301
- const s = _n(t.sprite);
2302
- return /* @__PURE__ */ b("div", { className: `flex items-start gap-2 px-2 py-1 hover:bg-bg-card/20 ${i ? "bg-bg-card/30" : ""}`, children: [
2303
- fe(t.sprite) ? /* @__PURE__ */ l(
2304
- "img",
2372
+ const s = bn(t.sprite);
2373
+ return /* @__PURE__ */ v("div", { className: `flex items-start gap-2 px-2 py-1 hover:bg-bg-card/20 ${a ? "bg-bg-card/30" : ""}`, children: [
2374
+ /* @__PURE__ */ c(
2375
+ An,
2305
2376
  {
2306
- src: He(t.sprite),
2307
- alt: t.sprite,
2308
- crossOrigin: "anonymous",
2309
- className: "w-8 h-8 rounded-full object-cover flex-shrink-0"
2377
+ sprite: t.sprite,
2378
+ url: i,
2379
+ alt: t.name,
2380
+ className: "w-8 h-8 rounded-full object-cover flex-shrink-0",
2381
+ fallback: /* @__PURE__ */ c("span", { className: "w-8 h-8 bg-bg-alt/50 rounded-full flex-shrink-0" })
2310
2382
  }
2311
- ) : /* @__PURE__ */ l("span", { className: "w-8 h-8 bg-bg-alt/50 rounded-full flex-shrink-0" }),
2312
- /* @__PURE__ */ b("div", { className: "flex-1 min-w-0 -mt-0.5", children: [
2313
- /* @__PURE__ */ l("span", { className: "font-medium text-[13px]", style: { color: s }, children: t.name }),
2314
- /* @__PURE__ */ l("span", { className: "text-[10px] text-text-muted opacity-60 ml-1.5", children: qe(t.ts) }),
2315
- t.text && /* @__PURE__ */ l("p", { className: "text-[13px] text-text break-words leading-snug m-0", children: t.text }),
2316
- /* @__PURE__ */ l(Ci, { images: t.images })
2383
+ ),
2384
+ /* @__PURE__ */ v("div", { className: "flex-1 min-w-0 -mt-0.5", children: [
2385
+ /* @__PURE__ */ c("span", { className: "font-medium text-[13px]", style: { color: s }, children: t.name }),
2386
+ /* @__PURE__ */ c("span", { className: "text-[10px] text-text-muted opacity-60 ml-1.5", children: Ke(t.ts) }),
2387
+ t.text && /* @__PURE__ */ c("p", { className: "text-[13px] text-text break-words leading-snug m-0", children: t.text }),
2388
+ /* @__PURE__ */ c(Ea, { images: t.images })
2317
2389
  ] })
2318
2390
  ] });
2319
2391
  });
2320
- function Ti() {
2321
- const e = m((i) => i.chat), t = J(null), n = J(!1), o = N(() => {
2322
- const i = t.current;
2323
- if (!i) return;
2324
- const { scrollHeight: s, scrollTop: a, clientHeight: c } = i, f = s - a - c;
2325
- n.current = f > 40;
2392
+ function Ra() {
2393
+ const e = u((a) => a.chat), t = z(null), n = z(!1), o = E(() => {
2394
+ const a = t.current;
2395
+ if (!a) return;
2396
+ const { scrollHeight: i, scrollTop: s, clientHeight: l } = a, p = i - s - l;
2397
+ n.current = p > 40;
2326
2398
  }, []);
2327
- return O(() => {
2328
- const i = t.current;
2329
- i && !n.current && i.scrollTo({ top: i.scrollHeight, behavior: "smooth" });
2330
- }, [e.length]), /* @__PURE__ */ l(
2399
+ return D(() => {
2400
+ const a = t.current;
2401
+ a && !n.current && a.scrollTo({ top: a.scrollHeight, behavior: "smooth" });
2402
+ }, [e.length]), /* @__PURE__ */ c(
2331
2403
  "div",
2332
2404
  {
2333
2405
  ref: t,
2334
2406
  onScroll: o,
2335
2407
  className: "flex-1 overflow-y-auto overflow-x-hidden chat-messages",
2336
- children: e.length === 0 ? /* @__PURE__ */ l("div", { className: "p-4 text-xs text-text-muted text-center", children: "No messages yet. Be the first to say something!" }) : /* @__PURE__ */ l(Ne, { children: e.map((i, s) => {
2337
- const a = s > 0 ? e[s - 1] : null, c = a && i.ts - a.ts > 1440 * 60 * 1e3;
2338
- return /* @__PURE__ */ b(Bt.Fragment, { children: [
2339
- c && /* @__PURE__ */ b("div", { className: "flex items-center gap-2 px-4 py-2 my-1", children: [
2340
- /* @__PURE__ */ l("div", { className: "flex-1 h-px bg-border" }),
2341
- /* @__PURE__ */ l("span", { className: "text-[10px] text-text-muted whitespace-nowrap", children: new Date(i.ts).toLocaleDateString([], { weekday: "short", month: "short", day: "numeric" }) }),
2342
- /* @__PURE__ */ l("div", { className: "flex-1 h-px bg-border" })
2408
+ children: e.length === 0 ? /* @__PURE__ */ c("div", { className: "p-4 text-xs text-text-muted text-center", children: "No messages yet. Be the first to say something!" }) : /* @__PURE__ */ c(le, { children: e.map((a, i) => {
2409
+ const s = i > 0 ? e[i - 1] : null, l = s && a.ts - s.ts > 1440 * 60 * 1e3;
2410
+ return /* @__PURE__ */ v($t.Fragment, { children: [
2411
+ l && /* @__PURE__ */ v("div", { className: "flex items-center gap-2 px-4 py-2 my-1", children: [
2412
+ /* @__PURE__ */ c("div", { className: "flex-1 h-px bg-border" }),
2413
+ /* @__PURE__ */ c("span", { className: "text-[10px] text-text-muted whitespace-nowrap", children: new Date(a.ts).toLocaleDateString([], { weekday: "short", month: "short", day: "numeric" }) }),
2414
+ /* @__PURE__ */ c("div", { className: "flex-1 h-px bg-border" })
2343
2415
  ] }),
2344
- /* @__PURE__ */ l(Ii, { message: i })
2345
- ] }, i.id);
2416
+ /* @__PURE__ */ c(Na, { message: a })
2417
+ ] }, a.id);
2346
2418
  }) })
2347
2419
  }
2348
2420
  );
2349
2421
  }
2350
- const ft = [
2422
+ const mt = [
2351
2423
  {
2352
2424
  name: "recap",
2353
2425
  description: "Summarise the previous episode of what is loaded",
@@ -2361,99 +2433,93 @@ const ft = [
2361
2433
  usage: "/tierlist",
2362
2434
  scope: "local"
2363
2435
  },
2364
- {
2365
- name: "character",
2366
- description: "Change your character",
2367
- usage: "/character",
2368
- scope: "local"
2369
- },
2370
2436
  {
2371
2437
  name: "help",
2372
2438
  description: "List the available commands",
2373
2439
  usage: "/help",
2374
2440
  scope: "local"
2375
2441
  }
2376
- ], re = "/";
2377
- function Ei(e) {
2378
- if (!e.startsWith(re)) return null;
2379
- const t = e.slice(re.length);
2442
+ ], ee = "/";
2443
+ function Da(e) {
2444
+ if (!e.startsWith(ee)) return null;
2445
+ const t = e.slice(ee.length);
2380
2446
  return /\s/.test(t) ? null : t.toLowerCase();
2381
2447
  }
2382
- function Ai(e, t = ft) {
2448
+ function Oa(e, t = mt) {
2383
2449
  const n = e.toLowerCase();
2384
- if (!n) return [...t].sort((i, s) => i.name.localeCompare(s.name));
2450
+ if (!n) return [...t].sort((a, i) => a.name.localeCompare(i.name));
2385
2451
  const o = [];
2386
- for (const i of t) {
2387
- const s = i.name.toLowerCase();
2388
- s.startsWith(n) ? o.push({ cmd: i, rank: 0 }) : s.includes(n) && o.push({ cmd: i, rank: 1 });
2452
+ for (const a of t) {
2453
+ const i = a.name.toLowerCase();
2454
+ i.startsWith(n) ? o.push({ cmd: a, rank: 0 }) : i.includes(n) && o.push({ cmd: a, rank: 1 });
2389
2455
  }
2390
- return o.sort((i, s) => i.rank - s.rank || i.cmd.name.localeCompare(s.cmd.name)).map((i) => i.cmd);
2456
+ return o.sort((a, i) => a.rank - i.rank || a.cmd.name.localeCompare(i.cmd.name)).map((a) => a.cmd);
2391
2457
  }
2392
- function Ni(e) {
2393
- return `${re}${e.name} `;
2458
+ function Ma(e) {
2459
+ return `${ee}${e.name} `;
2394
2460
  }
2395
- function Ve(e, t) {
2461
+ function We(e, t) {
2396
2462
  return t <= 0 ? 0 : (e % t + t) % t;
2397
2463
  }
2398
- function Pi(e, t = ft) {
2464
+ function La(e, t = mt) {
2399
2465
  const n = e.trim();
2400
2466
  if (!n) return null;
2401
- if (!n.startsWith(re)) return { kind: "chat", text: n };
2402
- const o = n.slice(re.length), i = o.search(/\s/), s = (i === -1 ? o : o.slice(0, i)).toLowerCase(), a = i === -1 ? "" : o.slice(i).trim(), c = t.find((f) => f.name.toLowerCase() === s);
2403
- return c ? { kind: "command", command: c, args: a } : { kind: "chat", text: n };
2467
+ if (!n.startsWith(ee)) return { kind: "chat", text: n };
2468
+ const o = n.slice(ee.length), a = o.search(/\s/), i = (a === -1 ? o : o.slice(0, a)).toLowerCase(), s = a === -1 ? "" : o.slice(a).trim(), l = t.find((p) => p.name.toLowerCase() === i);
2469
+ return l ? { kind: "command", command: l, args: s } : { kind: "chat", text: n };
2404
2470
  }
2405
- function Mi(e, t) {
2406
- return e.requiresMedia && !t.hasMedia ? `${re}${e.name} needs an episode loaded.` : null;
2471
+ function ja(e, t) {
2472
+ return e.requiresMedia && !t.hasMedia ? `${ee}${e.name} needs an episode loaded.` : null;
2407
2473
  }
2408
- function Oi({
2474
+ function Ua({
2409
2475
  commands: e,
2410
2476
  activeIndex: t,
2411
2477
  hasMedia: n,
2412
2478
  onPick: o,
2413
- onHover: i,
2414
- listboxId: s
2479
+ onHover: a,
2480
+ listboxId: i
2415
2481
  }) {
2416
- return e.length === 0 ? null : /* @__PURE__ */ l(
2482
+ return e.length === 0 ? null : /* @__PURE__ */ c(
2417
2483
  "ul",
2418
2484
  {
2419
- id: s,
2485
+ id: i,
2420
2486
  role: "listbox",
2421
2487
  "aria-label": "Slash commands",
2422
2488
  className: `absolute bottom-full left-0 right-0 z-30 max-h-56 overflow-y-auto
2423
2489
  bg-bg-card border border-border-light rounded shadow-lg
2424
2490
  list-none m-0 p-0 py-1 mb-1`,
2425
- children: e.map((a, c) => {
2426
- const f = !!a.requiresMedia && !n, g = c === t;
2427
- return /* @__PURE__ */ b(
2491
+ children: e.map((s, l) => {
2492
+ const p = !!s.requiresMedia && !n, h = l === t;
2493
+ return /* @__PURE__ */ v(
2428
2494
  "li",
2429
2495
  {
2430
- id: `${s}-opt-${a.name}`,
2496
+ id: `${i}-opt-${s.name}`,
2431
2497
  role: "option",
2432
- "aria-selected": g,
2433
- "aria-disabled": f,
2434
- onMouseDown: (p) => {
2435
- p.preventDefault(), o(a);
2498
+ "aria-selected": h,
2499
+ "aria-disabled": p,
2500
+ onMouseDown: (f) => {
2501
+ f.preventDefault(), o(s);
2436
2502
  },
2437
- onMouseEnter: () => i(c),
2438
- className: `px-3 py-1.5 cursor-pointer flex items-baseline gap-2 ${g ? "bg-bg-alt" : ""} ${f ? "opacity-50" : ""}`,
2503
+ onMouseEnter: () => a(l),
2504
+ className: `px-3 py-1.5 cursor-pointer flex items-baseline gap-2 ${h ? "bg-bg-alt" : ""} ${p ? "opacity-50" : ""}`,
2439
2505
  children: [
2440
- /* @__PURE__ */ l("span", { className: "text-sm font-semibold text-text shrink-0", children: a.usage }),
2441
- /* @__PURE__ */ l("span", { className: "text-xs text-text-secondary truncate", children: f ? "needs an episode loaded" : a.description })
2506
+ /* @__PURE__ */ c("span", { className: "text-sm font-semibold text-text shrink-0", children: s.usage }),
2507
+ /* @__PURE__ */ c("span", { className: "text-xs text-text-secondary truncate", children: p ? "needs an episode loaded" : s.description })
2442
2508
  ]
2443
2509
  },
2444
- a.name
2510
+ s.name
2445
2511
  );
2446
2512
  })
2447
2513
  }
2448
2514
  );
2449
2515
  }
2450
- function Ri({
2516
+ function Fa({
2451
2517
  pending: e,
2452
2518
  uploading: t,
2453
2519
  onRemove: n
2454
2520
  }) {
2455
- return e.length ? /* @__PURE__ */ l("div", { className: "flex flex-wrap gap-1.5 pb-1.5", "data-testid": "chat-attachment-strip", children: e.map((o) => /* @__PURE__ */ b("div", { className: "relative", children: [
2456
- /* @__PURE__ */ l(
2521
+ return e.length ? /* @__PURE__ */ c("div", { className: "flex flex-wrap gap-1.5 pb-1.5", "data-testid": "chat-attachment-strip", children: e.map((o) => /* @__PURE__ */ v("div", { className: "relative", children: [
2522
+ /* @__PURE__ */ c(
2457
2523
  "img",
2458
2524
  {
2459
2525
  src: o.previewUrl,
@@ -2461,7 +2527,7 @@ function Ri({
2461
2527
  className: `h-14 w-14 rounded border border-border-light object-cover ${t ? "opacity-50" : ""}`
2462
2528
  }
2463
2529
  ),
2464
- /* @__PURE__ */ l(
2530
+ /* @__PURE__ */ c(
2465
2531
  "button",
2466
2532
  {
2467
2533
  type: "button",
@@ -2474,245 +2540,242 @@ function Ri({
2474
2540
  )
2475
2541
  ] }, o.id)) }) : null;
2476
2542
  }
2477
- function Di() {
2478
- const [e, t] = P([]), [n, o] = P(!1), i = m((h) => h.pushToast), s = J(/* @__PURE__ */ new Set());
2479
- O(() => () => {
2480
- for (const h of s.current) URL.revokeObjectURL(h);
2481
- s.current.clear();
2543
+ function Ha() {
2544
+ const [e, t] = R([]), [n, o] = R(!1), a = u((m) => m.pushToast), i = z(/* @__PURE__ */ new Set());
2545
+ D(() => () => {
2546
+ for (const m of i.current) URL.revokeObjectURL(m);
2547
+ i.current.clear();
2482
2548
  }, []);
2483
- const a = N((h) => {
2484
- Or(h) && (URL.revokeObjectURL(h.previewUrl), s.current.delete(h.previewUrl));
2485
- }, []), c = N(async (h) => {
2486
- if (!h.length) return;
2487
- const _ = [];
2488
- for (const w of h) {
2489
- const k = Dr(w);
2490
- if (k) {
2491
- i("error", k);
2549
+ const s = E((m) => {
2550
+ Dr(m) && (URL.revokeObjectURL(m.previewUrl), i.current.delete(m.previewUrl));
2551
+ }, []), l = E(async (m) => {
2552
+ if (!m.length) return;
2553
+ const S = [];
2554
+ for (const y of m) {
2555
+ const I = Mr(y);
2556
+ if (I) {
2557
+ a("error", I);
2492
2558
  continue;
2493
2559
  }
2494
- const { w: u, h: v } = await Ur(w), E = URL.createObjectURL(w);
2495
- s.current.add(E), _.push({
2560
+ const { w: g, h: A } = await Ur(y), N = URL.createObjectURL(y);
2561
+ i.current.add(N), S.push({
2496
2562
  kind: "file",
2497
2563
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2498
- file: w,
2499
- previewUrl: E,
2500
- w: u,
2501
- h: v
2564
+ file: y,
2565
+ previewUrl: N,
2566
+ w: g,
2567
+ h: A
2502
2568
  });
2503
2569
  }
2504
- _.length && t((w) => {
2505
- const k = Y - w.length;
2506
- if (k <= 0) {
2507
- i("info", `Up to ${Y} images per message`);
2508
- for (const u of _) a(u);
2509
- return w;
2570
+ S.length && t((y) => {
2571
+ const I = V - y.length;
2572
+ if (I <= 0) {
2573
+ a("info", `Up to ${V} images per message`);
2574
+ for (const g of S) s(g);
2575
+ return y;
2510
2576
  }
2511
- if (_.length > k) {
2512
- i("info", `Up to ${Y} images per message`);
2513
- for (const u of _.slice(k)) a(u);
2577
+ if (S.length > I) {
2578
+ a("info", `Up to ${V} images per message`);
2579
+ for (const g of S.slice(I)) s(g);
2514
2580
  }
2515
- return [...w, ..._.slice(0, k)];
2581
+ return [...y, ...S.slice(0, I)];
2516
2582
  });
2517
- }, [i, a]), f = N((h) => {
2518
- const _ = Pe(h.key);
2519
- _ && (t((w) => w.length >= Y ? (i("info", `Up to ${Y} images per message`), w) : [...w, {
2583
+ }, [a, s]), p = E((m) => {
2584
+ const S = Pe(m.key);
2585
+ S && (t((y) => y.length >= V ? (a("info", `Up to ${V} images per message`), y) : [...y, {
2520
2586
  kind: "remote",
2521
2587
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2522
- key: h.key,
2523
- previewUrl: _,
2588
+ key: m.key,
2589
+ previewUrl: S,
2524
2590
  // The catalogue spells "unknown" as null; the wire schema spells it
2525
2591
  // as 0, and the renderer falls back to natural flow on that.
2526
- w: h.width ?? 0,
2527
- h: h.height ?? 0,
2528
- title: h.title || void 0
2529
- }]), je().catalogue(mn).recordUse(h.key));
2530
- }, [i]), g = N((h) => {
2531
- t((_) => {
2532
- const w = _.find((k) => k.id === h);
2533
- return w && a(w), _.filter((k) => k.id !== h);
2592
+ w: m.width ?? 0,
2593
+ h: m.height ?? 0,
2594
+ title: m.title || void 0
2595
+ }]), Le().catalogue(cn).recordUse(m.key));
2596
+ }, [a]), h = E((m) => {
2597
+ t((S) => {
2598
+ const y = S.find((I) => I.id === m);
2599
+ return y && s(y), S.filter((I) => I.id !== m);
2534
2600
  });
2535
- }, [a]), p = N(() => {
2536
- t((h) => {
2537
- for (const _ of h) a(_);
2601
+ }, [s]), f = E(() => {
2602
+ t((m) => {
2603
+ for (const S of m) s(S);
2538
2604
  return [];
2539
2605
  });
2540
- }, [a]), D = N((h) => {
2541
- const _ = Lr(h.clipboardData);
2542
- _.length && (h.preventDefault(), c(_));
2543
- }, [c]), M = N((h) => {
2544
- const _ = jr(h.dataTransfer);
2545
- _.length && (h.preventDefault(), c(_));
2546
- }, [c]), T = N((h) => {
2547
- h.dataTransfer?.types?.includes("Files") && h.preventDefault();
2548
- }, []), C = N(async () => {
2606
+ }, [s]), L = E((m) => {
2607
+ const S = Lr(m.clipboardData);
2608
+ S.length && (m.preventDefault(), l(S));
2609
+ }, [l]), O = E((m) => {
2610
+ const S = jr(m.dataTransfer);
2611
+ S.length && (m.preventDefault(), l(S));
2612
+ }, [l]), M = E((m) => {
2613
+ m.dataTransfer?.types?.includes("Files") && m.preventDefault();
2614
+ }, []), C = E(async () => {
2549
2615
  if (!e.length) return [];
2550
2616
  o(!0);
2551
2617
  try {
2552
2618
  return await Hr(e);
2553
- } catch (h) {
2554
- return A("chat", "Image upload failed", $r(h)), i("error", Rr(h)), null;
2619
+ } catch (m) {
2620
+ return k("chat", "Image upload failed", $r(m)), a("error", Or(m)), null;
2555
2621
  } finally {
2556
2622
  o(!1);
2557
2623
  }
2558
- }, [e, i]);
2624
+ }, [e, a]);
2559
2625
  return {
2560
2626
  pending: e,
2561
2627
  uploading: n,
2562
- addFiles: c,
2563
- addMeme: f,
2564
- remove: g,
2565
- clear: p,
2628
+ addFiles: l,
2629
+ addMeme: p,
2630
+ remove: h,
2631
+ clear: f,
2566
2632
  uploadPending: C,
2567
- onPaste: D,
2568
- onDrop: M,
2569
- onDragOver: T
2633
+ onPaste: L,
2634
+ onDrop: O,
2635
+ onDragOver: M
2570
2636
  };
2571
2637
  }
2572
- const Ut = Yt, Li = Zn;
2573
- function ji({ commands: e = ft }) {
2574
- const [t, n] = P(""), [o, i] = P(!1), [s, a] = P({ q: null, i: 0 }), [c, f] = P(!1), g = J(null), p = On(), [D, M] = P(!1), [T, C] = P(!1), h = Di(), _ = m((y) => y.snapshot.mediaId), w = m((y) => y.pushToast), k = m((y) => y.setTierlistOpen), u = m((y) => y.setCharacterModalOpen), v = !!_, E = Ei(t), U = Q(
2575
- () => E === null ? [] : Ai(E, e),
2576
- [E, e]
2577
- ), H = E !== null && !c && U.length > 0, S = s.q === E ? Ve(s.i, U.length) : 0, R = N(
2578
- (y) => a({ q: E, i: y }),
2579
- [E]
2580
- ), L = N((y, j) => {
2581
- const ae = Mi(y, { hasMedia: v });
2582
- if (ae) {
2583
- w("info", ae);
2638
+ const Ut = Vt, $a = Jn;
2639
+ function Ba({ commands: e = mt }) {
2640
+ const [t, n] = R(""), [o, a] = R(!1), [i, s] = R({ q: null, i: 0 }), [l, p] = R(!1), h = z(null), f = Nn(), [L, O] = R(!1), [M, C] = R(!1), m = Ha(), S = u((b) => b.snapshot.mediaId), y = u((b) => b.pushToast), I = u((b) => b.setTierlistOpen), g = !!S, A = Da(t), N = Q(
2641
+ () => A === null ? [] : Oa(A, e),
2642
+ [A, e]
2643
+ ), U = A !== null && !l && N.length > 0, F = i.q === A ? We(i.i, N.length) : 0, x = E(
2644
+ (b) => s({ q: A, i: b }),
2645
+ [A]
2646
+ ), P = E((b, j) => {
2647
+ const re = ja(b, { hasMedia: g });
2648
+ if (re) {
2649
+ y("info", re);
2584
2650
  return;
2585
2651
  }
2586
- switch (y.name) {
2652
+ switch (b.name) {
2587
2653
  case "help":
2588
- w("info", e.map((Mn) => Mn.usage).join(" "));
2654
+ y("info", e.map((En) => En.usage).join(" "));
2589
2655
  break;
2590
2656
  case "recap":
2591
- vi();
2657
+ wa();
2592
2658
  break;
2593
2659
  case "tierlist":
2594
- Do(), k(!0);
2595
- break;
2596
- case "character":
2597
- u(!0);
2660
+ Lo(), I(!0);
2598
2661
  break;
2599
2662
  default:
2600
- w("info", `${y.usage} is not implemented yet.`);
2663
+ y("info", `${b.usage} is not implemented yet.`);
2601
2664
  }
2602
- }, [v, w, e, k, u]), F = N((y) => {
2603
- n(Ni(y)), f(!1), g.current?.focus();
2604
- }, []), W = N(async (y) => {
2605
- if (y.preventDefault(), o || h.uploading) return;
2606
- const j = Pi(t, e);
2607
- if (!j && !h.pending.length) return;
2608
- if (j?.kind === "command" && h.pending.length) {
2609
- w("info", "Send images on their own, not with a command.");
2665
+ }, [g, y, e, I]), T = E((b) => {
2666
+ n(Ma(b)), p(!1), h.current?.focus();
2667
+ }, []), pe = E(async (b) => {
2668
+ if (b.preventDefault(), o || m.uploading) return;
2669
+ const j = La(t, e);
2670
+ if (!j && !m.pending.length) return;
2671
+ if (j?.kind === "command" && m.pending.length) {
2672
+ y("info", "Send images on their own, not with a command.");
2610
2673
  return;
2611
2674
  }
2612
2675
  if (j?.kind === "command") {
2613
- L(j.command, j.args), n(""), !document.hidden && document.hasFocus() && g.current?.focus();
2676
+ P(j.command, j.args), n(""), !document.hidden && document.hasFocus() && h.current?.focus();
2614
2677
  return;
2615
2678
  }
2616
- const ae = await h.uploadPending();
2617
- ae !== null && (fi(j?.text ?? "", ae), n(""), h.clear(), i(!0), setTimeout(() => i(!1), Li), !document.hidden && document.hasFocus() && g.current?.focus());
2618
- }, [t, o, L, h, w, e]), Pn = N((y) => {
2619
- if (y.key === "Escape" && H) {
2620
- y.preventDefault(), f(!0);
2679
+ const re = await m.uploadPending();
2680
+ re !== null && (ya(j?.text ?? "", re), n(""), m.clear(), a(!0), setTimeout(() => a(!1), $a), !document.hidden && document.hasFocus() && h.current?.focus());
2681
+ }, [t, o, P, m, y, e]), he = E((b) => {
2682
+ if (b.key === "Escape" && U) {
2683
+ b.preventDefault(), p(!0);
2621
2684
  return;
2622
2685
  }
2623
- if (H) {
2624
- if (y.key === "ArrowDown")
2625
- y.preventDefault(), R(Ve(S + 1, U.length));
2626
- else if (y.key === "ArrowUp")
2627
- y.preventDefault(), R(Ve(S - 1, U.length));
2628
- else if (y.key === "Tab") {
2629
- y.preventDefault();
2630
- const j = U[S];
2631
- j && F(j);
2632
- } else if (y.key === "Enter") {
2633
- y.preventDefault();
2634
- const j = U[S];
2635
- j && F(j);
2686
+ if (U) {
2687
+ if (b.key === "ArrowDown")
2688
+ b.preventDefault(), x(We(F + 1, N.length));
2689
+ else if (b.key === "ArrowUp")
2690
+ b.preventDefault(), x(We(F - 1, N.length));
2691
+ else if (b.key === "Tab") {
2692
+ b.preventDefault();
2693
+ const j = N[F];
2694
+ j && T(j);
2695
+ } else if (b.key === "Enter") {
2696
+ b.preventDefault();
2697
+ const j = N[F];
2698
+ j && T(j);
2636
2699
  }
2637
2700
  }
2638
- }, [H, U, S, F, R]);
2639
- O(() => {
2640
- const y = () => {
2641
- document.hidden || !document.hasFocus() || (g.current?.focus(), g.current?.scrollIntoView({ block: "nearest" }));
2701
+ }, [U, N, F, T, x]);
2702
+ D(() => {
2703
+ const b = () => {
2704
+ document.hidden || !document.hasFocus() || (h.current?.focus(), h.current?.scrollIntoView({ block: "nearest" }));
2642
2705
  };
2643
- return window.addEventListener("watchparty:focus-chat-input", y), () => window.removeEventListener("watchparty:focus-chat-input", y);
2706
+ return window.addEventListener("watchparty:focus-chat-input", b), () => window.removeEventListener("watchparty:focus-chat-input", b);
2644
2707
  }, []);
2645
- const be = Ut - t.length, ht = be <= 100, gt = H ? U[S] : void 0;
2646
- return /* @__PURE__ */ b(
2708
+ const ge = Ut - t.length, ft = ge <= 100, pt = U ? N[F] : void 0;
2709
+ return /* @__PURE__ */ v(
2647
2710
  "form",
2648
2711
  {
2649
- onSubmit: (y) => {
2650
- W(y);
2712
+ onSubmit: (b) => {
2713
+ pe(b);
2651
2714
  },
2652
- onPaste: h.onPaste,
2653
- onDrop: h.onDrop,
2654
- onDragOver: h.onDragOver,
2715
+ onPaste: m.onPaste,
2716
+ onDrop: m.onDrop,
2717
+ onDragOver: m.onDragOver,
2655
2718
  className: "p-2 border-t border-border",
2656
2719
  children: [
2657
- /* @__PURE__ */ l(Ri, { pending: h.pending, uploading: h.uploading, onRemove: h.remove }),
2658
- /* @__PURE__ */ b("div", { className: "relative", children: [
2659
- H && /* @__PURE__ */ l(
2660
- Oi,
2720
+ /* @__PURE__ */ c(Fa, { pending: m.pending, uploading: m.uploading, onRemove: m.remove }),
2721
+ /* @__PURE__ */ v("div", { className: "relative", children: [
2722
+ U && /* @__PURE__ */ c(
2723
+ Ua,
2661
2724
  {
2662
- commands: U,
2663
- activeIndex: S,
2664
- hasMedia: v,
2665
- onPick: F,
2666
- onHover: R,
2667
- listboxId: p
2725
+ commands: N,
2726
+ activeIndex: F,
2727
+ hasMedia: g,
2728
+ onPick: T,
2729
+ onHover: x,
2730
+ listboxId: f
2668
2731
  }
2669
2732
  ),
2670
- /* @__PURE__ */ l(
2733
+ /* @__PURE__ */ c(
2671
2734
  qr,
2672
2735
  {
2673
- open: D,
2674
- onClose: () => M(!1),
2675
- onPick: h.addMeme,
2736
+ open: L,
2737
+ onClose: () => O(!1),
2738
+ onPick: m.addMeme,
2676
2739
  onUnavailable: () => {
2677
- C(!0), M(!1);
2740
+ C(!0), O(!1);
2678
2741
  }
2679
2742
  }
2680
2743
  ),
2681
- /* @__PURE__ */ b("div", { className: "flex items-center gap-1.5", children: [
2682
- /* @__PURE__ */ b("div", { className: "relative flex-1", children: [
2683
- /* @__PURE__ */ l(
2744
+ /* @__PURE__ */ v("div", { className: "flex items-center gap-1.5", children: [
2745
+ /* @__PURE__ */ v("div", { className: "relative flex-1", children: [
2746
+ /* @__PURE__ */ c(
2684
2747
  "input",
2685
2748
  {
2686
- ref: g,
2749
+ ref: h,
2687
2750
  type: "text",
2688
2751
  value: t,
2689
- onChange: (y) => {
2690
- n(y.target.value), f(!1);
2752
+ onChange: (b) => {
2753
+ n(b.target.value), p(!1);
2691
2754
  },
2692
- onKeyDown: Pn,
2755
+ onKeyDown: he,
2693
2756
  maxLength: Ut,
2694
2757
  placeholder: "Send a message",
2695
2758
  role: "combobox",
2696
- "aria-expanded": H,
2697
- "aria-controls": H ? p : void 0,
2698
- "aria-activedescendant": gt ? `${p}-opt-${gt.name}` : void 0,
2759
+ "aria-expanded": U,
2760
+ "aria-controls": U ? f : void 0,
2761
+ "aria-activedescendant": pt ? `${f}-opt-${pt.name}` : void 0,
2699
2762
  "aria-autocomplete": "list",
2700
- className: `w-full bg-bg-card border border-border-light rounded px-3 py-1.5 text-sm focus:outline-none focus:border-text-muted ${ht ? "pr-12" : ""}`
2763
+ className: `w-full bg-bg-card border border-border-light rounded px-3 py-1.5 text-sm focus:outline-none focus:border-text-muted ${ft ? "pr-12" : ""}`
2701
2764
  }
2702
2765
  ),
2703
- /* @__PURE__ */ l("span", { className: `absolute right-2 top-1/2 -translate-y-1/2 text-[10px] ${be <= 50 ? be <= 0 ? "bg-danger-bg text-on-danger-bg px-1 rounded" : "bg-primary-bg text-on-primary-bg px-1 rounded" : "text-text-muted"}`, children: ht && be })
2766
+ /* @__PURE__ */ c("span", { className: `absolute right-2 top-1/2 -translate-y-1/2 text-[10px] ${ge <= 50 ? ge <= 0 ? "bg-danger-bg text-on-danger-bg px-1 rounded" : "bg-primary-bg text-on-primary-bg px-1 rounded" : "text-text-muted"}`, children: ft && ge })
2704
2767
  ] }),
2705
- !T && /* @__PURE__ */ l(
2768
+ !M && /* @__PURE__ */ c(
2706
2769
  "button",
2707
2770
  {
2708
2771
  type: "button",
2709
- onClick: () => M((y) => !y),
2772
+ onClick: () => O((b) => !b),
2710
2773
  "aria-label": "Add a meme",
2711
- "aria-expanded": D,
2774
+ "aria-expanded": L,
2712
2775
  title: "Add a meme",
2713
2776
  "data-testid": "meme-button",
2714
- className: `shrink-0 rounded border border-border-light px-2 py-1.5 text-sm hover:border-primary ${D ? "text-text border-primary" : "text-text-secondary"}`,
2715
- children: /* @__PURE__ */ l(Rn, { name: "image" })
2777
+ className: `shrink-0 rounded border border-border-light px-2 py-1.5 text-sm hover:border-primary ${L ? "text-text border-primary" : "text-text-secondary"}`,
2778
+ children: /* @__PURE__ */ c(Rn, { name: "image" })
2716
2779
  }
2717
2780
  )
2718
2781
  ] })
@@ -2721,35 +2784,36 @@ function ji({ commands: e = ft }) {
2721
2784
  }
2722
2785
  );
2723
2786
  }
2724
- function Ui() {
2725
- const e = m((n) => n.roster), t = Object.values(e);
2726
- return t.length ? /* @__PURE__ */ l("div", { className: "flex flex-col gap-1", children: t.map((n) => {
2727
- const o = _n(n.sprite);
2728
- return /* @__PURE__ */ b("div", { className: "flex items-center gap-2 py-0.5", children: [
2729
- fe(n.sprite) ? /* @__PURE__ */ l(
2730
- "img",
2787
+ function Ga() {
2788
+ const e = u((n) => n.roster), t = Object.values(e);
2789
+ return t.length ? /* @__PURE__ */ c("div", { className: "flex flex-col gap-1", children: t.map((n) => {
2790
+ const o = bn(n.sprite);
2791
+ return /* @__PURE__ */ v("div", { className: "flex items-center gap-2 py-0.5", children: [
2792
+ /* @__PURE__ */ c(
2793
+ An,
2731
2794
  {
2732
- src: He(n.sprite),
2733
- alt: n.sprite,
2734
- crossOrigin: "anonymous",
2735
- className: "w-6 h-6 rounded-full object-cover flex-shrink-0"
2795
+ sprite: n.sprite,
2796
+ url: n.avatarUrl,
2797
+ alt: n.name || "Anon",
2798
+ className: "w-6 h-6 rounded-full object-cover flex-shrink-0",
2799
+ fallback: /* @__PURE__ */ c("span", { className: "w-6 h-6 bg-bg-alt rounded-full flex-shrink-0" })
2736
2800
  }
2737
- ) : /* @__PURE__ */ l("span", { className: "w-6 h-6 bg-bg-alt rounded-full flex-shrink-0" }),
2738
- /* @__PURE__ */ l("span", { className: "text-xs font-medium truncate", style: { color: o }, children: n.name || "Anon" }),
2739
- n.isLeader && /* @__PURE__ */ l("span", { className: "ml-auto px-1.5 py-0.5 rounded text-[9px] font-medium bg-success/20 text-success", children: "Leader" })
2801
+ ),
2802
+ /* @__PURE__ */ c("span", { className: "text-xs font-medium truncate", style: { color: o }, children: n.name || "Anon" }),
2803
+ n.isLeader && /* @__PURE__ */ c("span", { className: "ml-auto px-1.5 py-0.5 rounded text-[9px] font-medium bg-success/20 text-success", children: "Leader" })
2740
2804
  ] }, n.clientId);
2741
- }) }) : /* @__PURE__ */ l("div", { className: "text-xs text-text-muted", children: "No users online" });
2805
+ }) }) : /* @__PURE__ */ c("div", { className: "text-xs text-text-muted", children: "No users online" });
2742
2806
  }
2743
- function Fi() {
2744
- const e = m((i) => Object.keys(i.roster).length), [t, n] = P(!1), o = J(null);
2745
- return O(() => {
2807
+ function Ka() {
2808
+ const e = u((a) => Object.keys(a.roster).length), [t, n] = R(!1), o = z(null);
2809
+ return D(() => {
2746
2810
  if (!t) return;
2747
- function i(s) {
2748
- o.current && !o.current.contains(s.target) && n(!1);
2811
+ function a(i) {
2812
+ o.current && !o.current.contains(i.target) && n(!1);
2749
2813
  }
2750
- return document.addEventListener("mousedown", i), () => document.removeEventListener("mousedown", i);
2751
- }, [t]), /* @__PURE__ */ b("div", { className: "relative", ref: o, children: [
2752
- /* @__PURE__ */ b(
2814
+ return document.addEventListener("mousedown", a), () => document.removeEventListener("mousedown", a);
2815
+ }, [t]), /* @__PURE__ */ v("div", { className: "relative", ref: o, children: [
2816
+ /* @__PURE__ */ v(
2753
2817
  "button",
2754
2818
  {
2755
2819
  onClick: () => {
@@ -2758,401 +2822,233 @@ function Fi() {
2758
2822
  className: "p-1.5 rounded text-text-secondary hover:text-white hover:bg-bg-alt transition-colors flex items-center gap-1 cursor-pointer",
2759
2823
  title: "Users",
2760
2824
  children: [
2761
- /* @__PURE__ */ l("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" }) }),
2762
- /* @__PURE__ */ l("span", { className: "text-xs", children: e })
2825
+ /* @__PURE__ */ c("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ c("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" }) }),
2826
+ /* @__PURE__ */ c("span", { className: "text-xs", children: e })
2763
2827
  ]
2764
2828
  }
2765
2829
  ),
2766
- t && /* @__PURE__ */ b("div", { className: "absolute top-full right-0 mt-1 bg-bg-card rounded-lg shadow-xl p-3 max-h-64 overflow-y-auto z-50 w-max min-w-[180px] max-w-[220px]", children: [
2767
- /* @__PURE__ */ b("div", { className: "text-[10px] uppercase tracking-wide text-text-muted mb-2", children: [
2830
+ t && /* @__PURE__ */ v("div", { className: "absolute top-full right-0 mt-1 bg-bg-card rounded-lg shadow-xl p-3 max-h-64 overflow-y-auto z-50 w-max min-w-[180px] max-w-[220px]", children: [
2831
+ /* @__PURE__ */ v("div", { className: "text-[10px] uppercase tracking-wide text-text-muted mb-2", children: [
2768
2832
  e,
2769
2833
  " online"
2770
2834
  ] }),
2771
- /* @__PURE__ */ l(Ui, {})
2835
+ /* @__PURE__ */ c(Ga, {})
2772
2836
  ] })
2773
2837
  ] });
2774
2838
  }
2775
- function Hi() {
2776
- const e = m((n) => n.sprite), t = m((n) => n.setCharacterModalOpen);
2777
- return /* @__PURE__ */ l(
2778
- "button",
2779
- {
2780
- onClick: () => {
2781
- d("chat", "Opening character modal", { currentSprite: e }), t(!0);
2782
- },
2783
- className: "p-1 rounded hover:bg-bg-alt transition-colors cursor-pointer",
2784
- title: "Change character",
2785
- children: fe(e) ? /* @__PURE__ */ l(
2786
- "img",
2787
- {
2788
- src: He(e),
2789
- alt: e,
2790
- crossOrigin: "anonymous",
2791
- className: "w-6 h-6 rounded object-cover",
2792
- fetchPriority: "high"
2793
- }
2794
- ) : /* @__PURE__ */ l("div", { className: "w-6 h-6 rounded bg-bg-alt flex items-center justify-center", children: /* @__PURE__ */ l("svg", { className: "w-4 h-4 text-text-secondary", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }) })
2795
- }
2796
- );
2797
- }
2798
- const se = yo();
2799
- function $i({ open: e, onClose: t, roomKey: n, tierlist: o }) {
2800
- const i = zr(), { name: s, sprite: a, setIdentity: c } = m(), f = m((u) => !!(u.me.clientId && u.roster[u.me.clientId]?.registryName)), [g, p] = P(a), [D, M] = P(/* @__PURE__ */ new Set()), [T, C] = P(() => xt(se, a));
2801
- O(() => {
2802
- e && (p(a), C(xt(se, a)));
2803
- }, [e, a]), O(() => {
2804
- if (!e || !n) return;
2805
- const u = `${fn(i)}/api/rooms/${encodeURIComponent(n)}/sprites`;
2806
- fetch(u).then((v) => v.ok ? v.json() : (A("character", "sprites fetch non-OK", { status: v.status }), Promise.reject(new Error(`status ${v.status}`)))).then((v) => M(new Set(v.used ?? []))).catch((v) => {
2807
- ue("character", "sprites fetch failed", ge(v)), M(/* @__PURE__ */ new Set());
2808
- });
2809
- }, [e, n]);
2810
- const h = () => {
2811
- const u = f || !g ? s : te(g);
2812
- d("character", "Updating identity", { name: u, sprite: g }), c(u, g), gi(u, g || void 0), t();
2813
- }, _ = (u) => u === a ? !0 : !D.has(u), w = (u) => {
2814
- u.target === u.currentTarget && t();
2815
- };
2816
- O(() => {
2817
- if (!e) return;
2818
- const u = (v) => {
2819
- v.key === "Escape" ? t() : v.key === "ArrowLeft" ? C((E) => Math.max(0, E - 1)) : v.key === "ArrowRight" && C((E) => Math.min(se.length - 1, E + 1));
2820
- };
2821
- return window.addEventListener("keydown", u), () => window.removeEventListener("keydown", u);
2822
- }, [e, t]);
2823
- const k = g !== a;
2824
- return e ? /* @__PURE__ */ l(
2825
- "div",
2826
- {
2827
- className: "fixed inset-0 bg-black/70 flex items-center justify-center z-50",
2828
- onMouseDown: w,
2829
- role: "dialog",
2830
- "aria-modal": "true",
2831
- "aria-label": "Change character",
2832
- style: { willChange: "opacity" },
2833
- children: /* @__PURE__ */ b("div", { className: "bg-bg border border-border rounded-lg shadow-xl w-[340px] mx-4 overflow-hidden", children: [
2834
- /* @__PURE__ */ b("div", { className: "p-4 space-y-4 text-sm", children: [
2835
- /* @__PURE__ */ b("div", { children: [
2836
- /* @__PURE__ */ b("div", { className: "flex items-center justify-between mb-1", children: [
2837
- /* @__PURE__ */ l("label", { className: "text-xs uppercase tracking-wide opacity-70", children: f ? "Display Name" : "Character" }),
2838
- /* @__PURE__ */ l(
2839
- "button",
2840
- {
2841
- type: "button",
2842
- onClick: t,
2843
- className: "text-text-secondary hover:text-white hover:bg-bg-alt rounded p-1 transition-colors cursor-pointer",
2844
- "aria-label": "Close",
2845
- children: /* @__PURE__ */ l("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2846
- }
2847
- )
2848
- ] }),
2849
- f && /* @__PURE__ */ l("div", { className: "modal-input", "aria-readonly": "true", children: s })
2850
- ] }),
2851
- /* @__PURE__ */ b("div", { children: [
2852
- f && /* @__PURE__ */ l("label", { className: "block text-xs uppercase tracking-wide mb-2 opacity-70", children: "Character" }),
2853
- /* @__PURE__ */ b("div", { className: "h-[152px] flex items-center gap-1", children: [
2854
- /* @__PURE__ */ l(Ft, { direction: "prev", hidden: T === 0, onClick: () => C(T - 1) }),
2855
- /* @__PURE__ */ l("div", { className: "grid grid-cols-4 gap-2 h-full flex-1 content-start", children: (se[T] ?? []).map((u) => {
2856
- const v = _(u);
2857
- return /* @__PURE__ */ b(
2858
- "button",
2859
- {
2860
- onClick: () => {
2861
- if (!v) {
2862
- d("character", "Sprite unavailable", { sprite: u });
2863
- return;
2864
- }
2865
- d("character", "Sprite selected", { sprite: u }), p(u);
2866
- },
2867
- disabled: !v,
2868
- className: `relative group rounded-lg p-1 flex flex-col items-center gap-1 transition-all ${g === u ? "ring-2 ring-success ring-offset-2 ring-offset-bg bg-bg-card cursor-pointer" : v ? "hover:bg-bg-card border border-transparent hover:border-border-light cursor-pointer" : "opacity-40 cursor-not-allowed"}`,
2869
- children: [
2870
- /* @__PURE__ */ l(
2871
- "img",
2872
- {
2873
- src: He(u),
2874
- alt: u,
2875
- crossOrigin: "anonymous",
2876
- className: `w-12 h-12 object-cover rounded ${v ? "" : "grayscale"}`
2877
- }
2878
- ),
2879
- /* @__PURE__ */ l("span", { className: "text-[10px] opacity-70 group-hover:opacity-100 whitespace-nowrap", children: te(u) }),
2880
- !v && /* @__PURE__ */ l("span", { className: "absolute inset-0 flex items-center justify-center bg-bg/60 rounded", children: /* @__PURE__ */ l("span", { className: "text-[8px] font-medium bg-danger-bg text-on-danger-bg px-1 rounded", children: "In Use" }) })
2881
- ]
2882
- },
2883
- u
2884
- );
2885
- }) }),
2886
- /* @__PURE__ */ l(Ft, { direction: "next", hidden: T >= se.length - 1, onClick: () => C(T + 1) })
2887
- ] })
2888
- ] })
2889
- ] }),
2890
- /* @__PURE__ */ b("div", { className: "px-4 pb-4 flex justify-end gap-2", children: [
2891
- o && /* @__PURE__ */ b(
2892
- "button",
2893
- {
2894
- type: "button",
2895
- onClick: o.open,
2896
- onMouseEnter: o.warm,
2897
- onFocus: o.warm,
2898
- className: "modal-btn-cancel mr-auto inline-flex items-center gap-1",
2899
- "aria-label": "Tierlist",
2900
- children: [
2901
- /* @__PURE__ */ l("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 6h16M4 12h11M4 18h6" }) }),
2902
- "Tierlist"
2903
- ]
2904
- }
2905
- ),
2906
- /* @__PURE__ */ l(
2907
- "button",
2908
- {
2909
- onClick: t,
2910
- className: "modal-btn-cancel",
2911
- children: "Cancel"
2912
- }
2913
- ),
2914
- /* @__PURE__ */ l(
2915
- "button",
2916
- {
2917
- onClick: h,
2918
- disabled: !k,
2919
- className: "modal-btn-update",
2920
- style: k ? void 0 : {
2921
- opacity: 0.4,
2922
- cursor: "not-allowed",
2923
- filter: "grayscale(50%)"
2924
- },
2925
- children: "Update"
2926
- }
2927
- )
2928
- ] })
2929
- ] })
2930
- }
2931
- ) : null;
2932
- }
2933
- function Ft({ direction: e, hidden: t, onClick: n }) {
2934
- return /* @__PURE__ */ l(
2935
- "button",
2936
- {
2937
- type: "button",
2938
- onClick: n,
2939
- className: "char-page-btn",
2940
- "data-hidden": t ? "1" : void 0,
2941
- disabled: t,
2942
- "aria-hidden": t,
2943
- tabIndex: t ? -1 : void 0,
2944
- "aria-label": e === "prev" ? "Previous characters" : "Next characters",
2945
- children: /* @__PURE__ */ l("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: e === "prev" ? "M15 19l-7-7 7-7" : "M9 5l7 7-7 7" }) })
2946
- }
2947
- );
2948
- }
2949
- function Ht({ className: e = "fixed bottom-4 left-1/2 -translate-x-1/2" }) {
2950
- const t = m((o) => o.toasts), n = m((o) => o.popToast);
2951
- return O(() => {
2952
- const o = t.map((i) => setTimeout(() => n(i.id), 4e3));
2839
+ function Wa({ sources: e }) {
2840
+ const t = u((n) => jt(Object.values(n.roster).map((o) => o.avatarSource)));
2841
+ return (e ? jt(e) : t) ? /* @__PURE__ */ v("p", { className: "m-0 px-2 py-0.5 text-[10px] text-text-muted", children: [
2842
+ /* @__PURE__ */ c("a", { href: "https://store.steampowered.com/", target: "_blank", rel: "noopener noreferrer", className: "underline", children: "Steam avatars via the Steam Web API" }),
2843
+ " · not affiliated with Valve"
2844
+ ] }) : null;
2845
+ }
2846
+ function Ft({ className: e = "fixed bottom-4 left-1/2 -translate-x-1/2" }) {
2847
+ const t = u((o) => o.toasts), n = u((o) => o.popToast);
2848
+ return D(() => {
2849
+ const o = t.map((a) => setTimeout(() => n(a.id), 4e3));
2953
2850
  return () => {
2954
2851
  o.forEach(clearTimeout);
2955
2852
  };
2956
- }, [t, n]), /* @__PURE__ */ l("div", { className: `${e} flex flex-col gap-2 z-50`, children: t.map((o) => /* @__PURE__ */ l("div", { className: `px-3 py-2 rounded text-sm shadow font-medium border ${o.kind === "error" ? "bg-danger-bg text-on-danger-bg border-danger" : "bg-bg-card border-border-light text-text"}`, children: o.msg }, o.id)) });
2853
+ }, [t, n]), /* @__PURE__ */ c("div", { className: `${e} flex flex-col gap-2 z-50`, children: t.map((o) => /* @__PURE__ */ c("div", { className: `px-3 py-2 rounded text-sm shadow font-medium border ${o.kind === "error" ? "bg-danger-bg text-on-danger-bg border-danger" : "bg-bg-card border-border-light text-text"}`, children: o.msg }, o.id)) });
2957
2854
  }
2958
- const $t = (e) => typeof e == "string" && Dn.includes(e);
2959
- function Bi(e, t, n) {
2960
- return e || ($t(t) ? t : $t(n) ? n : "dark");
2855
+ const Ht = (e) => typeof e == "string" && Dn.includes(e);
2856
+ function qa(e, t, n) {
2857
+ return e || (Ht(t) ? t : Ht(n) ? n : "dark");
2961
2858
  }
2962
- function Gi(e, t) {
2859
+ function Va(e, t) {
2963
2860
  if (t(e.clientWidth), typeof ResizeObserver > "u") return () => {
2964
2861
  };
2965
2862
  const n = new ResizeObserver(() => t(e.clientWidth));
2966
2863
  return n.observe(e), () => n.disconnect();
2967
2864
  }
2968
- function Wi(e) {
2865
+ function Ya(e) {
2969
2866
  const t = e?.trim() ?? "";
2970
2867
  return t ? { source: "host", name: t } : { source: "auto" };
2971
2868
  }
2972
- function Ki(e) {
2869
+ function za(e) {
2973
2870
  if (e.source !== "host") return;
2974
- const { setSessionIdentity: t, sprite: n } = m.getState();
2871
+ const { setSessionIdentity: t, sprite: n } = u.getState();
2975
2872
  t(e.name, n);
2976
2873
  }
2977
- function qi({ className: e = "w-4 h-4" }) {
2978
- return /* @__PURE__ */ b("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
2979
- /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 4v16" }),
2980
- /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12l-5 4v-8l5 4z" })
2874
+ function Xa({ className: e = "w-4 h-4" }) {
2875
+ return /* @__PURE__ */ v("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
2876
+ /* @__PURE__ */ c("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 4v16" }),
2877
+ /* @__PURE__ */ c("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12l-5 4v-8l5 4z" })
2981
2878
  ] });
2982
2879
  }
2983
- function Vi({ className: e = "w-4 h-4" }) {
2984
- return /* @__PURE__ */ b("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
2985
- /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M20 4v16" }),
2986
- /* @__PURE__ */ l("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12l5 4v-8l-5 4z" })
2880
+ function Ja({ className: e = "w-4 h-4" }) {
2881
+ return /* @__PURE__ */ v("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
2882
+ /* @__PURE__ */ c("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M20 4v16" }),
2883
+ /* @__PURE__ */ c("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12l5 4v-8l-5 4z" })
2987
2884
  ] });
2988
2885
  }
2989
- const zi = 1500;
2990
- function sa({
2886
+ const Qa = 1500;
2887
+ function mi({
2991
2888
  roomKey: e,
2992
2889
  serverOrigin: t,
2993
2890
  userType: n,
2994
2891
  userId: o,
2995
- theme: i,
2996
- name: s,
2997
- onLeave: a,
2998
- onLayoutChange: c,
2999
- collapsible: f = !0,
3000
- debug: g,
3001
- children: p
2892
+ theme: a,
2893
+ name: i,
2894
+ onLeave: s,
2895
+ onLayoutChange: l,
2896
+ collapsible: p = !0,
2897
+ debug: h,
2898
+ children: f
3002
2899
  }) {
3003
- const [D, M] = P(null), { setFrame: T, ...C } = Yi(c, f), h = m((S) => S.characterModalOpen), _ = m((S) => S.setCharacterModalOpen), w = Kt(() => s), k = Q(() => (go({ userType: n, userId: o ?? null }), dt().theme), [n, o]), u = Ln(), v = u?.theme, E = Q(
3004
- () => Bi(i, v, k),
3005
- [i, v, k]
2900
+ const [L, O] = R(null), { setFrame: M, ...C } = Za(l, p), m = Gt(() => i), S = Q(() => (bo({ userType: n, userId: o ?? null }), lt().theme), [n, o]), y = On(), I = y?.theme, g = Q(
2901
+ () => qa(a, I, S),
2902
+ [a, I, S]
3006
2903
  );
3007
- O(() => {
3008
- }, [i, u !== null]);
3009
- const H = Q(
3010
- () => ({ ...it, serverOrigin: t, defaultRoomKey: e }),
2904
+ D(() => {
2905
+ }, [a, y !== null]);
2906
+ const N = Q(
2907
+ () => ({ ...rt, serverOrigin: t, defaultRoomKey: e }),
3011
2908
  [t, e]
3012
2909
  );
3013
- return O(() => {
3014
- g && Zr();
3015
- }, [g]), O(() => {
3016
- Vr({ serverOrigin: t, defaultRoomKey: e }), m.getState().rehydrateFromPrefs(), qo();
3017
- let S = !1;
3018
- const R = vo();
2910
+ return D(() => {
2911
+ h && Zr();
2912
+ }, [h]), xa(), D(() => {
2913
+ Vr({ serverOrigin: t, defaultRoomKey: e }), u.getState().rehydrateFromPrefs(), Xo();
2914
+ let U = !1;
2915
+ const F = So();
3019
2916
  return (async () => {
3020
2917
  try {
3021
2918
  await Promise.race([
3022
2919
  Promise.all([
3023
- R,
3024
- ko().then((W) => {
3025
- W && m.getState().rehydrateFromPrefs();
3026
- })
2920
+ F,
2921
+ To().then((T) => {
2922
+ T && u.getState().rehydrateFromPrefs();
2923
+ }),
2924
+ // The character chosen in the settings popout (lib/avatar.ts).
2925
+ Ho().then(kn)
3027
2926
  ]),
3028
- new Promise((W) => setTimeout(W, zi))
2927
+ new Promise((T) => setTimeout(T, Qa))
3029
2928
  ]);
3030
- } catch (W) {
3031
- A("room-chrome", "pre-join wait threw, proceeding anyway", ge(W));
2929
+ } catch (T) {
2930
+ k("room-chrome", "pre-join wait threw, proceeding anyway", fe(T));
3032
2931
  }
3033
- if (S) return;
3034
- const L = Wi(w());
3035
- m.getState().ensureAutoIdentity(), Ki(L);
3036
- const F = m.getState();
3037
- d("room-chrome", "Joining room", { roomKey: e, name: F.name, sprite: F.sprite }), ni({ room_key: e, name: F.name || void 0, sprite: F.sprite || void 0 });
2932
+ if (U) return;
2933
+ const x = Ya(m());
2934
+ u.getState().ensureAutoIdentity(), za(x);
2935
+ const P = u.getState();
2936
+ d("room-chrome", "Joining room", { roomKey: e, name: P.name, sprite: P.sprite }), ia({ room_key: e, name: P.name || void 0, sprite: P.sprite || void 0 });
3038
2937
  })(), () => {
3039
- S = !0, jt();
2938
+ U = !0, Lt();
3040
2939
  };
3041
- }, [e, t, n, o]), /* @__PURE__ */ l(pn.Provider, { value: H, children: /* @__PURE__ */ l(gn.Provider, { value: D, children: /* @__PURE__ */ b("div", { ref: T, className: "wp-room-chrome-frame", children: [
3042
- /* @__PURE__ */ l("div", { className: "wp-room-chrome-frame__content", children: p }),
3043
- /* @__PURE__ */ b(
2940
+ }, [e, t, n, o]), /* @__PURE__ */ c(Yr.Provider, { value: N, children: /* @__PURE__ */ c(un.Provider, { value: L, children: /* @__PURE__ */ v("div", { ref: M, className: "wp-room-chrome-frame", children: [
2941
+ /* @__PURE__ */ c("div", { className: "wp-room-chrome-frame__content", children: f }),
2942
+ /* @__PURE__ */ c(
3044
2943
  "aside",
3045
2944
  {
3046
- ref: M,
2945
+ ref: O,
3047
2946
  className: "wp-room-chrome",
3048
- "data-theme": E,
2947
+ "data-theme": g,
3049
2948
  "data-collapsed": C.collapsed ? "1" : "0",
3050
2949
  style: { "--wp-room-chrome-width": `${C.state.width}px` },
3051
- children: [
3052
- C.collapsed ? /* @__PURE__ */ b(Ne, { children: [
3053
- /* @__PURE__ */ l(
2950
+ children: C.collapsed ? /* @__PURE__ */ v(le, { children: [
2951
+ /* @__PURE__ */ c(
2952
+ "button",
2953
+ {
2954
+ onClick: C.toggleCollapsed,
2955
+ className: "wp-room-chrome__icon-btn",
2956
+ title: "Show chat",
2957
+ "aria-label": "Show chat",
2958
+ "aria-expanded": !1,
2959
+ children: /* @__PURE__ */ c(Ja, {})
2960
+ }
2961
+ ),
2962
+ /* @__PURE__ */ c(Ft, { className: "absolute bottom-2 right-full mr-2 w-72" })
2963
+ ] }) : /* @__PURE__ */ v(le, { children: [
2964
+ /* @__PURE__ */ c(
2965
+ "div",
2966
+ {
2967
+ role: "separator",
2968
+ "aria-orientation": "vertical",
2969
+ "aria-label": "Resize chat",
2970
+ "aria-valuemin": Oe,
2971
+ "aria-valuemax": ct,
2972
+ "aria-valuenow": C.state.width,
2973
+ tabIndex: 0,
2974
+ className: "wp-room-chrome__resize",
2975
+ "data-dragging": C.dragging ? "1" : "0",
2976
+ onPointerDown: C.onPointerDown,
2977
+ onPointerMove: C.onPointerMove,
2978
+ onPointerUp: C.onPointerEnd,
2979
+ onPointerCancel: C.onPointerEnd,
2980
+ onKeyDown: C.onKeyDown
2981
+ }
2982
+ ),
2983
+ /* @__PURE__ */ v("div", { className: "flex items-center gap-1 px-2 py-1.5 border-b border-border", children: [
2984
+ /* @__PURE__ */ c(Ka, {}),
2985
+ /* @__PURE__ */ c("div", { className: "flex-1" }),
2986
+ s && /* @__PURE__ */ c(
3054
2987
  "button",
3055
2988
  {
3056
- onClick: C.toggleCollapsed,
3057
- className: "wp-room-chrome__icon-btn",
3058
- title: "Show chat",
3059
- "aria-label": "Show chat",
3060
- "aria-expanded": !1,
3061
- children: /* @__PURE__ */ l(Vi, {})
2989
+ onClick: () => {
2990
+ Lt(), s();
2991
+ },
2992
+ className: "wp-room-chrome__leave",
2993
+ title: "Leave room",
2994
+ "aria-label": "Leave room",
2995
+ children: "Leave"
3062
2996
  }
3063
2997
  ),
3064
- /* @__PURE__ */ l(Ht, { className: "absolute bottom-2 right-full mr-2 w-72" })
3065
- ] }) : /* @__PURE__ */ b(Ne, { children: [
3066
- /* @__PURE__ */ l(
3067
- "div",
2998
+ p && /* @__PURE__ */ c(
2999
+ "button",
3068
3000
  {
3069
- role: "separator",
3070
- "aria-orientation": "vertical",
3071
- "aria-label": "Resize chat",
3072
- "aria-valuemin": De,
3073
- "aria-valuemax": ut,
3074
- "aria-valuenow": C.state.width,
3075
- tabIndex: 0,
3076
- className: "wp-room-chrome__resize",
3077
- "data-dragging": C.dragging ? "1" : "0",
3078
- onPointerDown: C.onPointerDown,
3079
- onPointerMove: C.onPointerMove,
3080
- onPointerUp: C.onPointerEnd,
3081
- onPointerCancel: C.onPointerEnd,
3082
- onKeyDown: C.onKeyDown
3001
+ onClick: C.toggleCollapsed,
3002
+ className: "wp-room-chrome__icon-btn",
3003
+ title: "Hide chat",
3004
+ "aria-label": "Hide chat",
3005
+ "aria-expanded": !0,
3006
+ children: /* @__PURE__ */ c(Xa, {})
3083
3007
  }
3084
- ),
3085
- /* @__PURE__ */ b("div", { className: "flex items-center gap-1 px-2 py-1.5 border-b border-border", children: [
3086
- /* @__PURE__ */ l(Fi, {}),
3087
- /* @__PURE__ */ l(Hi, {}),
3088
- /* @__PURE__ */ l("div", { className: "flex-1" }),
3089
- a && /* @__PURE__ */ l(
3090
- "button",
3091
- {
3092
- onClick: () => {
3093
- jt(), a();
3094
- },
3095
- className: "wp-room-chrome__leave",
3096
- title: "Leave room",
3097
- "aria-label": "Leave room",
3098
- children: "Leave"
3099
- }
3100
- ),
3101
- f && /* @__PURE__ */ l(
3102
- "button",
3103
- {
3104
- onClick: C.toggleCollapsed,
3105
- className: "wp-room-chrome__icon-btn",
3106
- title: "Hide chat",
3107
- "aria-label": "Hide chat",
3108
- "aria-expanded": !0,
3109
- children: /* @__PURE__ */ l(qi, {})
3110
- }
3111
- )
3112
- ] }),
3113
- /* @__PURE__ */ l(Ti, {}),
3114
- /* @__PURE__ */ l(ji, { commands: [] }),
3115
- /* @__PURE__ */ l(Ht, { className: "absolute bottom-14 left-2 right-2" })
3008
+ )
3116
3009
  ] }),
3117
- h && /* @__PURE__ */ l($i, { open: !0, onClose: () => _(!1), roomKey: e })
3118
- ]
3010
+ /* @__PURE__ */ c(Ra, {}),
3011
+ /* @__PURE__ */ c(Wa, {}),
3012
+ /* @__PURE__ */ c(Ba, { commands: [] }),
3013
+ /* @__PURE__ */ c(Ft, { className: "absolute bottom-14 left-2 right-2" })
3014
+ ] })
3119
3015
  }
3120
3016
  )
3121
3017
  ] }) }) });
3122
3018
  }
3123
- function Yi(e, t) {
3124
- const n = m((u) => u.chromeChatCollapsed) && t, o = m((u) => u.chromeChatWidth), i = m((u) => u.toggleChromeChatCollapsed), s = m((u) => u.setChromeChatWidth), [a, c] = P(null), [f, g] = P(void 0), [p, D] = P(null);
3125
- O(() => {
3126
- if (a)
3127
- return Gi(a, g);
3128
- }, [a]);
3129
- const M = V(p?.current ?? o, f), T = Q(() => No(n, M), [n, M]), C = Kt((u) => e?.(u));
3130
- return O(() => {
3131
- C(T);
3132
- }, [T]), {
3133
- setFrame: c,
3019
+ function Za(e, t) {
3020
+ const n = u((g) => g.chromeChatCollapsed) && t, o = u((g) => g.chromeChatWidth), a = u((g) => g.toggleChromeChatCollapsed), i = u((g) => g.setChromeChatWidth), [s, l] = R(null), [p, h] = R(void 0), [f, L] = R(null);
3021
+ D(() => {
3022
+ if (s)
3023
+ return Va(s, h);
3024
+ }, [s]);
3025
+ const O = W(f?.current ?? o, p), M = Q(() => No(n, O), [n, O]), C = Gt((g) => e?.(g));
3026
+ return D(() => {
3027
+ C(M);
3028
+ }, [M]), {
3029
+ setFrame: l,
3134
3030
  collapsed: n,
3135
- state: T,
3136
- dragging: p !== null,
3137
- toggleCollapsed: i,
3138
- onPointerDown: (u) => {
3139
- u.button === 0 && (u.preventDefault(), u.currentTarget.setPointerCapture(u.pointerId), D({ pointerId: u.pointerId, startX: u.clientX, startWidth: M, current: M }));
3031
+ state: M,
3032
+ dragging: f !== null,
3033
+ toggleCollapsed: a,
3034
+ onPointerDown: (g) => {
3035
+ g.button === 0 && (g.preventDefault(), g.currentTarget.setPointerCapture(g.pointerId), L({ pointerId: g.pointerId, startX: g.clientX, startWidth: O, current: O }));
3140
3036
  },
3141
- onPointerMove: (u) => {
3142
- !p || p.pointerId !== u.pointerId || D({ ...p, current: Mt(p.startWidth, p.startX, u.clientX, f) });
3037
+ onPointerMove: (g) => {
3038
+ !f || f.pointerId !== g.pointerId || L({ ...f, current: Et(f.startWidth, f.startX, g.clientX, p) });
3143
3039
  },
3144
- onPointerEnd: (u) => {
3145
- if (!p || p.pointerId !== u.pointerId) return;
3146
- const v = Mt(p.startWidth, p.startX, u.clientX, f);
3147
- D(null), v !== o && s(v);
3040
+ onPointerEnd: (g) => {
3041
+ if (!f || f.pointerId !== g.pointerId) return;
3042
+ const A = Et(f.startWidth, f.startX, g.clientX, p);
3043
+ L(null), A !== o && i(A);
3148
3044
  },
3149
- onKeyDown: (u) => {
3150
- const v = Ao(M, u.key, f);
3151
- v !== null && (u.preventDefault(), v !== o && s(v));
3045
+ onKeyDown: (g) => {
3046
+ const A = Po(O, g.key, p);
3047
+ A !== null && (g.preventDefault(), A !== o && i(A));
3152
3048
  }
3153
3049
  };
3154
3050
  }
3155
3051
  export {
3156
- sa as RoomChrome
3052
+ mi as RoomChrome
3157
3053
  };
3158
3054
  //# sourceMappingURL=room-chrome.js.map