@hadoku/task-ui-components 4.23.8 → 4.23.9

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.
@@ -8,8 +8,9 @@
8
8
  * screens stack the two.
9
9
  *
10
10
  * Cosmetics (`@wolffm/prefs-client/cosmetics`, bundled at build like the avatar
11
- * client): earned items equip on click; items not yet earned show how to earn
12
- * them and can be tried on in the preview, which saves nothing. An overlay
11
+ * client): what the person owns equips on click (every shown item is free but
12
+ * three earned in a Major); an earned item not yet earned shows how to earn
13
+ * it and can be tried on in the preview, which saves nothing. An overlay
13
14
  * image can be placed: drag it on the big preview, turn it with the top handle,
14
15
  * resize it with the corner handle, or use the sliders. Every move goes through
15
16
  * `fitPlacement`, the same function prefs-api runs before it stores one, so the
@@ -23,6 +24,8 @@ import React from 'react';
23
24
  export interface AvatarBuilderProps {
24
25
  /** The person's display name, for the preview. */
25
26
  name: string | null;
27
+ /** Admins also see archived items (greyed; nobody can select one). */
28
+ isAdmin?: boolean;
26
29
  }
27
- export declare function AvatarBuilder({ name }: AvatarBuilderProps): React.JSX.Element;
30
+ export declare function AvatarBuilder({ name, isAdmin }: AvatarBuilderProps): React.JSX.Element;
28
31
  //# sourceMappingURL=AvatarBuilder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarBuilder.d.ts","sourceRoot":"","sources":["../../src/components/AvatarBuilder.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAA4D,MAAM,OAAO,CAAA;AAqUhF,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,kBAAkB,qBAsUzD"}
1
+ {"version":3,"file":"AvatarBuilder.d.ts","sourceRoot":"","sources":["../../src/components/AvatarBuilder.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAA4D,MAAM,OAAO,CAAA;AAoUhF,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,OAAe,EAAE,EAAE,kBAAkB,qBA2U1E"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
3
3
  import { Icon } from "@wolffm/themes";
4
- import { ensureCosmeticsStyles, COSMETICS_BY_ID, PLACEMENT_DEFAULT, fitPlacement, avatarHtml, nameHtml, messageHtml, PLACEMENT_LIMITS, COSMETIC_SLOTS, COSMETICS, placementTransform, cleanLoadout, wornCosmetics } from "../vendor/wolffm/prefs-client/cosmetics.js";
4
+ import { ensureCosmeticsStyles, COSMETICS_BY_ID, PLACEMENT_DEFAULT, fitPlacement, avatarHtml, nameHtml, messageHtml, PLACEMENT_LIMITS, COSMETIC_SLOTS, placementTransform, COSMETICS, cleanLoadout, wornCosmetics } from "../vendor/wolffm/prefs-client/cosmetics.js";
5
5
  import { AvatarPicker } from "./AvatarPicker.js";
6
6
  import { AVATAR_EVENT } from "../lib/avatarEvent.js";
7
7
  import { avatarClient, avatarErrorText, fetchCharacters, portraitUrl } from "../lib/avatarData.js";
@@ -129,7 +129,6 @@ const S = {
129
129
  color: "var(--color-text-muted)"
130
130
  },
131
131
  tabOn: { color: "var(--color-text)", borderBottomColor: "var(--color-primary)" },
132
- count: { fontSize: "0.68rem", marginLeft: 4, opacity: 0.8, fontVariantNumeric: "tabular-nums" },
133
132
  pane: { display: "flex", flexDirection: "column", gap: 10, paddingTop: 10 },
134
133
  grid: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(112px, 1fr))", gap: 8 },
135
134
  tile: {
@@ -264,7 +263,7 @@ function useDragScroll() {
264
263
  }, [el]);
265
264
  return setEl;
266
265
  }
267
- function AvatarBuilder({ name }) {
266
+ function AvatarBuilder({ name, isAdmin = false }) {
268
267
  const [settings, setSettings] = useState(null);
269
268
  const [sets, setSets] = useState(null);
270
269
  const [busy, setBusy] = useState(false);
@@ -493,8 +492,7 @@ function AvatarBuilder({ name }) {
493
492
  ] }),
494
493
  /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
495
494
  /* @__PURE__ */ jsx("div", { ref: tabStrip, style: S.tabs, role: "tablist", "aria-label": "Avatar", children: tabs.map((t) => {
496
- const items = t === "picture" ? [] : COSMETICS.filter((i) => i.slot === t);
497
- return /* @__PURE__ */ jsxs(
495
+ return /* @__PURE__ */ jsx(
498
496
  "button",
499
497
  {
500
498
  type: "button",
@@ -504,16 +502,16 @@ function AvatarBuilder({ name }) {
504
502
  onClick: (e) => {
505
503
  setTab(t);
506
504
  setTryOn(null);
507
- e.currentTarget.scrollIntoView({ block: "nearest", inline: "nearest" });
505
+ const strip = e.currentTarget.parentElement;
506
+ if (strip && strip.scrollWidth > strip.clientWidth) {
507
+ const right = tabs.indexOf(t) >= tabs.length / 2;
508
+ strip.scrollTo({
509
+ left: right ? strip.scrollWidth - strip.clientWidth : 0,
510
+ behavior: "smooth"
511
+ });
512
+ }
508
513
  },
509
- children: [
510
- tabLabel(t),
511
- t !== "picture" && /* @__PURE__ */ jsxs("span", { style: S.count, children: [
512
- items.filter((i) => owned.has(i.id)).length,
513
- "/",
514
- items.length
515
- ] })
516
- ]
514
+ children: tabLabel(t)
517
515
  },
518
516
  t
519
517
  );
@@ -528,6 +526,7 @@ function AvatarBuilder({ name }) {
528
526
  tryOn,
529
527
  showLocked,
530
528
  onShowLocked: setShowLocked,
529
+ isAdmin,
531
530
  preview: (id) => {
532
531
  const only = { [tab]: id ?? void 0 };
533
532
  if (tab === "overlay") return avatarHtml({ src: picture, size: 40, worn: only });
@@ -649,14 +648,15 @@ function SlotGrid({
649
648
  tryOn,
650
649
  showLocked,
651
650
  onShowLocked,
651
+ isAdmin,
652
652
  preview,
653
653
  colour,
654
654
  onEquip,
655
655
  onTry
656
656
  }) {
657
- const items = COSMETICS.filter((i) => i.slot === slot);
658
- const locked = items.filter((i) => !owned.has(i.id));
659
- const shown = showLocked ? items : items.filter((i) => owned.has(i.id));
657
+ const items = COSMETICS.filter((i) => i.slot === slot && (!i.hidden || isAdmin));
658
+ const locked = items.filter((i) => !i.hidden && !owned.has(i.id));
659
+ const shown = showLocked ? items : items.filter((i) => i.hidden || owned.has(i.id));
660
660
  return /* @__PURE__ */ jsxs(Fragment, { children: [
661
661
  /* @__PURE__ */ jsxs("div", { style: S.grid, children: [
662
662
  /* @__PURE__ */ jsxs(
@@ -673,6 +673,23 @@ function SlotGrid({
673
673
  }
674
674
  ),
675
675
  shown.map((item) => {
676
+ if (item.hidden) {
677
+ return /* @__PURE__ */ jsxs(
678
+ "button",
679
+ {
680
+ type: "button",
681
+ disabled: true,
682
+ "aria-label": `${item.label}, archived`,
683
+ style: { ...S.tile, cursor: "not-allowed", opacity: 0.45 },
684
+ children: [
685
+ /* @__PURE__ */ jsx(Html, { html: preview(item.id), style: { ...S.tilePv, ...colour } }),
686
+ /* @__PURE__ */ jsx("span", { style: S.tileName, children: item.label }),
687
+ /* @__PURE__ */ jsx("span", { style: S.tileEarn, children: "Archived" })
688
+ ]
689
+ },
690
+ item.id
691
+ );
692
+ }
676
693
  const own = owned.has(item.id);
677
694
  const on = equipped === item.id;
678
695
  const trying = tryOn?.slot === slot && tryOn.id === item.id;
@@ -681,7 +698,7 @@ function SlotGrid({
681
698
  {
682
699
  type: "button",
683
700
  "aria-pressed": own ? on : trying,
684
- "aria-label": own ? item.label : `${item.label}, locked. ${item.earn}. Try it on.`,
701
+ "aria-label": own ? item.label : `${item.label}, locked. ${item.earn ?? ""}. Try it on.`,
685
702
  style: {
686
703
  ...S.tile,
687
704
  ...on ? S.tileOn : {},
@@ -708,9 +725,12 @@ function SlotGrid({
708
725
  );
709
726
  })
710
727
  ] }),
711
- /* @__PURE__ */ jsxs("div", { style: S.foot, children: [
712
- /* @__PURE__ */ jsx("span", { children: locked.length ? `${locked.length} still to earn. Pick one to try it on.` : "You have earned all of these." }),
713
- locked.length > 0 && /* @__PURE__ */ jsxs("label", { children: [
728
+ locked.length > 0 && /* @__PURE__ */ jsxs("div", { style: S.foot, children: [
729
+ /* @__PURE__ */ jsxs("span", { children: [
730
+ locked.length === 1 ? "One to earn." : `${locked.length} to earn.`,
731
+ " Pick one to try it on."
732
+ ] }),
733
+ /* @__PURE__ */ jsxs("label", { children: [
714
734
  /* @__PURE__ */ jsx(
715
735
  "input",
716
736
  {
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarBuilder.js","sources":["../../src/components/AvatarBuilder.tsx"],"sourcesContent":["/**\n * AvatarBuilder — the body of AvatarDialog: the person's avatar and what they\n * wear with it, with a live preview.\n *\n * Layout (operator's pick, 2026-11-01): a preview column on the left (the\n * avatar large, with the name, and a chat line as others see it), and on the\n * right tabs for the Picture (AvatarPicker) and each cosmetic slot. Narrow\n * screens stack the two.\n *\n * Cosmetics (`@wolffm/prefs-client/cosmetics`, bundled at build like the avatar\n * client): earned items equip on click; items not yet earned show how to earn\n * them and can be tried on in the preview, which saves nothing. An overlay\n * image can be placed: drag it on the big preview, turn it with the top handle,\n * resize it with the corner handle, or use the sliders. Every move goes through\n * `fitPlacement`, the same function prefs-api runs before it stores one, so the\n * preview never shows a spot the server would refuse.\n *\n * Styles are inline for the reason AvatarPicker's are: apps compile this\n * package's CSS at their own build. Tokens only; the cosmetics' own colours come\n * from the person's character colour (and, for overlay images, the art).\n */\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Icon } from '@wolffm/themes'\nimport type { AvatarSettings, LoadoutUpdate } from '@wolffm/prefs-client/avatar'\nimport {\n COSMETICS,\n COSMETICS_BY_ID,\n COSMETIC_SLOTS,\n PLACEMENT_DEFAULT,\n PLACEMENT_LIMITS,\n avatarHtml,\n cleanLoadout,\n ensureCosmeticsStyles,\n fitPlacement,\n messageHtml,\n nameHtml,\n placementTransform,\n wornCosmetics,\n type AvatarCosmetics,\n type CosmeticSlot,\n type Loadout,\n type OverlayArt,\n type Placement\n} from '@wolffm/prefs-client/cosmetics'\nimport { AvatarPicker } from './AvatarPicker'\nimport { AVATAR_EVENT } from '../lib/avatarEvent'\nimport {\n avatarClient,\n avatarErrorText,\n fetchCharacters,\n portraitUrl,\n type CharacterSet\n} from '../lib/avatarData'\n\ntype Tab = 'picture' | CosmeticSlot\n\nconst SLOT_LABEL: Record<CosmeticSlot, string> = {\n effect: 'Effect',\n gradient: 'Gradient',\n font: 'Font',\n overlay: 'Overlay',\n message: 'Message'\n}\n\nconst SLIDERS = [\n ['x', 'Left/right', '%'],\n ['y', 'Up/down', '%'],\n ['r', 'Rotate', '°'],\n ['s', 'Size', '%']\n] as const\n\nconst S = {\n body: { display: 'grid', gap: 16, alignItems: 'start' },\n side: { display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 },\n stage: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: 12,\n padding: '48px 12px 18px',\n borderRadius: 10,\n background: 'var(--color-bg-alt)',\n border: '1px solid var(--color-border)',\n touchAction: 'none',\n userSelect: 'none'\n },\n tryOn: {\n position: 'absolute',\n left: 8,\n right: 8,\n top: 8,\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '4px 8px',\n borderRadius: 6,\n background: 'var(--color-primary-bg)',\n color: 'var(--color-on-primary-bg)',\n fontSize: '0.72rem'\n },\n linkBtn: {\n border: 0,\n background: 'none',\n color: 'inherit',\n textDecoration: 'underline',\n cursor: 'pointer',\n fontSize: 'inherit',\n padding: 0,\n flex: 'none'\n },\n chat: {\n display: 'flex',\n flexDirection: 'column',\n gap: 10,\n padding: 12,\n borderRadius: 10,\n background: 'var(--color-bg)',\n border: '1px solid var(--color-border)'\n },\n chatHead: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n fontSize: '0.72rem',\n color: 'var(--color-text-muted)'\n },\n msg: { display: 'flex', gap: 10, alignItems: 'flex-start' },\n who: { minHeight: 22, display: 'flex', alignItems: 'center' },\n line: { fontSize: '0.85rem', marginTop: 2 },\n place: {\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: 12,\n borderRadius: 10,\n border: '1px solid var(--color-border)'\n },\n placeHead: { display: 'flex', flexDirection: 'column', gap: 2, fontSize: '0.8rem' },\n hint: { fontSize: '0.72rem', color: 'var(--color-text-muted)' },\n slider: {\n display: 'grid',\n gridTemplateColumns: '64px 1fr 40px',\n alignItems: 'center',\n gap: 6,\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n },\n out: { textAlign: 'right', color: 'var(--color-text)', fontVariantNumeric: 'tabular-nums' },\n toggles: {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 10,\n alignItems: 'center',\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n },\n tabs: {\n display: 'flex',\n gap: 2,\n cursor: 'grab',\n touchAction: 'pan-x',\n borderBottom: '1px solid var(--color-border)',\n overflowX: 'auto',\n scrollbarWidth: 'none'\n },\n tab: {\n flex: 'none',\n padding: '7px 11px',\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: 'transparent',\n borderTop: 0,\n borderLeft: 0,\n borderRight: 0,\n background: 'none',\n cursor: 'pointer',\n fontSize: '0.82rem',\n fontWeight: 500,\n color: 'var(--color-text-muted)'\n },\n tabOn: { color: 'var(--color-text)', borderBottomColor: 'var(--color-primary)' },\n count: { fontSize: '0.68rem', marginLeft: 4, opacity: 0.8, fontVariantNumeric: 'tabular-nums' },\n pane: { display: 'flex', flexDirection: 'column', gap: 10, paddingTop: 10 },\n grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(112px, 1fr))', gap: 8 },\n tile: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: 5,\n padding: 6,\n borderRadius: 8,\n border: '1px solid var(--color-border)',\n background: 'var(--color-bg)',\n color: 'var(--color-text)',\n cursor: 'pointer',\n textAlign: 'left',\n font: 'inherit'\n },\n tileOn: {\n borderColor: 'var(--color-primary)',\n boxShadow: 'inset 0 0 0 1px var(--color-primary)'\n },\n tileTrying: { borderStyle: 'dashed', borderColor: 'var(--color-primary)' },\n tilePv: {\n height: 58,\n borderRadius: 6,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'var(--color-bg-alt)',\n overflow: 'hidden',\n padding: '0 8px',\n fontSize: '0.8rem'\n },\n tileName: { fontSize: '0.8rem', fontWeight: 500, display: 'flex', alignItems: 'center', gap: 5 },\n tileEarn: { fontSize: '0.7rem', lineHeight: 1.3, color: 'var(--color-text-muted)' },\n foot: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n gap: 8,\n flexWrap: 'wrap',\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n }\n} satisfies Record<string, React.CSSProperties>\n\n/**\n * Trusted HTML from the cosmetics renderer, which escapes every string it is\n * given. Memoised on the string and the style: React re-sets innerHTML on every\n * render even when the string is the same, and replacing the nodes under the\n * pointer between mousedown and mouseup swallows the click.\n */\nconst Html = React.memo(\n function Html({ html, style }: { html: string; style?: React.CSSProperties }) {\n return <span style={style} dangerouslySetInnerHTML={{ __html: html }} />\n },\n (a, b) => a.html === b.html && JSON.stringify(a.style) === JSON.stringify(b.style)\n)\n\n/**\n * Play a tile's message reveal again. A finished reveal holds no animation (it\n * fills backwards only), so its markup is re-set. This runs synchronously in\n * mouseenter, before any press, so it cannot split a click; the memo above keeps\n * React from touching it afterwards.\n */\nfunction replayPreview(tile: HTMLElement) {\n const pv = tile.firstElementChild\n if (!pv) return\n const markup = pv.innerHTML\n pv.innerHTML = markup\n}\n\nfunction useNarrow(): boolean {\n const query = '(max-width: 640px)'\n const [narrow, setNarrow] = useState(() => window.matchMedia(query).matches)\n useEffect(() => {\n const mq = window.matchMedia(query)\n const on = () => setNarrow(mq.matches)\n mq.addEventListener('change', on)\n return () => mq.removeEventListener('change', on)\n }, [])\n return narrow\n}\n\n/** Move every drawing of one overlay in `root` to `p`, without re-rendering (smooth while dragging). */\nfunction applyPlacement(root: HTMLElement | null, id: string, p: Placement) {\n root?.querySelectorAll<HTMLElement>(`.hc-art[data-hc-art=\"${id}\"]`).forEach(ol => {\n ol.style.zIndex = p.back ? '0' : '3'\n const adj = ol.firstElementChild as HTMLElement | null\n if (adj) adj.style.transform = placementTransform(p)\n })\n}\n\n/** Settings with a new loadout, and what is worn recomputed from it. */\nfunction withLoadout(s: AvatarSettings, loadout: Loadout): AvatarSettings {\n return {\n ...s,\n cosmetics: { ...s.cosmetics, loadout },\n avatar: { ...s.avatar, cosmetics: wornCosmetics(loadout) }\n }\n}\n\n/**\n * A row that overflows sideways scrolls by dragging it (the operator's ask: the\n * tab strip is wider than its column and hides its scrollbar) and by the\n * mouse wheel. A drag of more than a few pixels is not a click.\n */\nfunction useDragScroll<T extends HTMLElement>() {\n // A callback ref, not useRef: the row appears only once the settings load,\n // after the first render, and the listeners must attach when it does.\n const [el, setEl] = useState<T | null>(null)\n useEffect(() => {\n if (!el) return\n let start: { x: number; left: number } | null = null\n let dragged = false\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return\n start = { x: e.clientX, left: el.scrollLeft }\n dragged = false\n }\n const move = (e: PointerEvent) => {\n if (!start) return\n const dx = e.clientX - start.x\n if (!dragged && Math.abs(dx) > 4) {\n dragged = true\n el.setPointerCapture(e.pointerId)\n el.style.cursor = 'grabbing'\n }\n if (dragged) el.scrollLeft = start.left - dx\n }\n const up = () => {\n start = null\n el.style.cursor = ''\n }\n // The click that ends a drag is not a tab choice.\n const click = (e: MouseEvent) => {\n if (!dragged) return\n e.preventDefault()\n e.stopPropagation()\n dragged = false\n }\n const wheel = (e: globalThis.WheelEvent) => {\n if (el.scrollWidth <= el.clientWidth || Math.abs(e.deltaX) > Math.abs(e.deltaY)) return\n e.preventDefault()\n el.scrollLeft += e.deltaY\n }\n el.addEventListener('pointerdown', down)\n el.addEventListener('pointermove', move)\n el.addEventListener('pointerup', up)\n el.addEventListener('pointercancel', up)\n el.addEventListener('click', click, true)\n el.addEventListener('wheel', wheel, { passive: false })\n return () => {\n el.removeEventListener('pointerdown', down)\n el.removeEventListener('pointermove', move)\n el.removeEventListener('pointerup', up)\n el.removeEventListener('pointercancel', up)\n el.removeEventListener('click', click, true)\n el.removeEventListener('wheel', wheel)\n }\n }, [el])\n return setEl\n}\n\nexport interface AvatarBuilderProps {\n /** The person's display name, for the preview. */\n name: string | null\n}\n\nexport function AvatarBuilder({ name }: AvatarBuilderProps) {\n const [settings, setSettings] = useState<AvatarSettings | null>(null)\n const [sets, setSets] = useState<CharacterSet[] | null>(null)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [tab, setTab] = useState<Tab>('picture')\n const [tryOn, setTryOn] = useState<{ slot: CosmeticSlot; id: string } | null>(null)\n const [showLocked, setShowLocked] = useState(true)\n const [replay, setReplay] = useState(0)\n const narrow = useNarrow()\n const root = useRef<HTMLDivElement>(null)\n const tabStrip = useDragScroll<HTMLDivElement>()\n // Only the newest cosmetics request may set state: equips sent quickly in a\n // row can answer out of order.\n const seq = useRef(0)\n\n useEffect(() => ensureCosmeticsStyles(), [])\n\n useEffect(() => {\n let live = true\n avatarClient\n .get()\n .then(s => live && setSettings(s))\n .catch((err: unknown) => live && setError(avatarErrorText(err)))\n fetchCharacters()\n .then(s => live && setSets(s))\n .catch((err: unknown) => live && setError(avatarErrorText(err)))\n return () => {\n live = false\n }\n }, [])\n\n const announce = (next: AvatarSettings) =>\n // The page under the popout (a watchparty room, a game) re-reads the avatar\n // from this instead of waiting for its next join.\n window.dispatchEvent(new window.CustomEvent<AvatarSettings>(AVATAR_EVENT, { detail: next }))\n\n const run = useCallback(async (work: () => Promise<AvatarSettings>) => {\n setBusy(true)\n setError(null)\n try {\n const next = await work()\n setSettings(next)\n announce(next)\n } catch (err) {\n setError(avatarErrorText(err))\n } finally {\n setBusy(false)\n }\n }, [])\n\n /** Save a cosmetics change, showing `optimistic` at once. A failure re-reads the truth. */\n const wear = useCallback(\n async (update: LoadoutUpdate, optimistic: (s: AvatarSettings) => AvatarSettings) => {\n const mine = ++seq.current\n setError(null)\n setSettings(s => (s ? optimistic(s) : s))\n try {\n const next = await avatarClient.setCosmetics(update)\n if (mine !== seq.current) return\n setSettings(next)\n announce(next)\n } catch (err) {\n if (mine !== seq.current) return\n setError(avatarErrorText(err))\n avatarClient\n .get()\n .then(s => mine === seq.current && setSettings(s))\n .catch((e: unknown) => setError(avatarErrorText(e)))\n }\n },\n []\n )\n\n const owned = useMemo(() => new Set(settings?.cosmetics.owned ?? []), [settings])\n const loadout = settings?.cosmetics.loadout\n const equippedOverlay = loadout?.equipped.overlay ?? null\n const art: OverlayArt | undefined = equippedOverlay\n ? COSMETICS_BY_ID.get(equippedOverlay)?.art\n : undefined\n const editing = tab === 'overlay' && !tryOn && !!art && !!equippedOverlay\n\n // The placement being edited, live; saved a beat after the last change.\n const [live, setLive] = useState<Placement>(PLACEMENT_DEFAULT)\n const saveTimer = useRef<ReturnType<typeof setTimeout>>(undefined)\n const dragging = useRef(false)\n useEffect(() => {\n if (dragging.current || !equippedOverlay) return\n setLive(loadout?.placement[equippedOverlay] ?? PLACEMENT_DEFAULT)\n }, [equippedOverlay, loadout])\n useEffect(() => () => clearTimeout(saveTimer.current), [])\n\n const place = useCallback(\n (raw: Partial<Placement>, save: 'later' | 'now' | 'no') => {\n if (!art || !equippedOverlay) return\n const p = fitPlacement(art, raw)\n setLive(p)\n applyPlacement(root.current, equippedOverlay, p)\n clearTimeout(saveTimer.current)\n if (save === 'no') return\n const commit = () =>\n void wear({ placement: { [equippedOverlay]: p } }, s =>\n withLoadout(s, {\n ...s.cosmetics.loadout,\n placement: { ...s.cosmetics.loadout.placement, [equippedOverlay]: p }\n })\n )\n if (save === 'now') commit()\n else saveTimer.current = setTimeout(commit, 400)\n },\n [art, equippedOverlay, wear]\n )\n\n if (!settings) {\n return (\n <span className=\"settings-popout__hint\" role={error ? 'alert' : undefined}>\n {error ?? '…'}\n </span>\n )\n }\n\n const characters = new Map((sets ?? []).flatMap(s => s.characters).map(c => [c.id, c]))\n const character = settings.sprite ? characters.get(settings.sprite) : undefined\n const picture = settings.avatar.url ?? (character ? portraitUrl(character) : null)\n // Every name cosmetic derives its colours from the character's.\n const colour: React.CSSProperties =\n character && /^#[0-9a-f]{6}$/i.test(character.color)\n ? ({ '--hc-base': character.color } as React.CSSProperties)\n : {}\n const displayName = name || 'You'\n\n const worn: AvatarCosmetics = { ...(settings.avatar.cosmetics ?? {}) }\n if (tryOn) {\n worn[tryOn.slot] = tryOn.id\n if (tryOn.slot === 'overlay') delete worn.placement\n }\n\n const equip = (slot: CosmeticSlot, id: string | null) => {\n setTryOn(null)\n void wear({ equip: { [slot]: id } }, s => {\n const l = s.cosmetics.loadout\n return withLoadout(s, cleanLoadout({ ...l, equipped: { ...l.equipped, [slot]: id } }, owned))\n })\n }\n\n const stage = (\n <Stage\n html={\n avatarHtml({ src: picture, size: 112, worn }) + nameHtml(displayName, worn, { large: true })\n }\n editing={editing}\n overlayId={equippedOverlay}\n live={live}\n onPlace={place}\n dragging={dragging}\n style={colour}\n banner={\n tryOn && (\n <div style={S.tryOn}>\n <Icon name=\"lock\" size={12} />\n <span style={{ flex: 1, minWidth: 0 }}>\n Trying on {COSMETICS_BY_ID.get(tryOn.id)?.label} ·{' '}\n {COSMETICS_BY_ID.get(tryOn.id)?.earn}\n </span>\n <button type=\"button\" style={S.linkBtn} onClick={() => setTryOn(null)}>\n Take off\n </button>\n </div>\n )\n }\n />\n )\n\n const chat = (\n <div style={S.chat}>\n <div style={S.chatHead}>\n <span>In chat</span>\n <button\n type=\"button\"\n className=\"settings-popout__btn\"\n onClick={() => setReplay(n => n + 1)}\n >\n Replay\n </button>\n </div>\n <div style={S.msg}>\n <Html html={avatarHtml({ src: picture, size: 36, worn })} style={colour} />\n <div style={{ minWidth: 0, ...colour }}>\n <div style={S.who}>\n <Html html={nameHtml(displayName, worn)} />\n </div>\n <div style={S.line}>\n <Html key={replay} html={messageHtml('gg, that last hand was brutal', worn)} />\n </div>\n </div>\n </div>\n </div>\n )\n\n const placement = editing && art && (\n <div style={S.place} role=\"group\" aria-label=\"Placement\">\n <div style={S.placeHead}>\n <strong>Placement</strong>\n <span style={S.hint}>\n Drag it on the avatar. The top handle turns it, the corner resizes it.\n </span>\n </div>\n {SLIDERS.map(([k, label, unit]) => (\n <label key={k} style={S.slider}>\n <span>{label}</span>\n <input\n type=\"range\"\n min={PLACEMENT_LIMITS[k][0]}\n max={PLACEMENT_LIMITS[k][1]}\n value={live[k]}\n onChange={e => place({ ...live, [k]: Number(e.currentTarget.value) }, 'later')}\n style={{ width: '100%', accentColor: 'var(--color-primary)' }}\n />\n <output style={S.out}>\n {live[k]}\n {unit}\n </output>\n </label>\n ))}\n <div style={S.toggles}>\n <label>\n <input\n type=\"checkbox\"\n checked={live.flip}\n onChange={e => place({ ...live, flip: e.currentTarget.checked }, 'now')}\n />{' '}\n Mirror\n </label>\n <label>\n <input\n type=\"checkbox\"\n checked={live.back}\n onChange={e => place({ ...live, back: e.currentTarget.checked }, 'now')}\n />{' '}\n Behind\n </label>\n <button\n type=\"button\"\n className=\"settings-popout__btn\"\n onClick={() => place(PLACEMENT_DEFAULT, 'now')}\n >\n Reset\n </button>\n </div>\n </div>\n )\n\n const tabs: Tab[] = ['picture', ...COSMETIC_SLOTS]\n const tabLabel = (t: Tab) => (t === 'picture' ? 'Picture' : SLOT_LABEL[t])\n\n return (\n <div\n ref={root}\n style={{ ...S.body, gridTemplateColumns: narrow ? 'minmax(0, 1fr)' : '240px minmax(0, 1fr)' }}\n >\n <div style={S.side}>\n {stage}\n {chat}\n {placement}\n </div>\n <div style={{ minWidth: 0 }}>\n <div ref={tabStrip} style={S.tabs} role=\"tablist\" aria-label=\"Avatar\">\n {tabs.map(t => {\n const items = t === 'picture' ? [] : COSMETICS.filter(i => i.slot === t)\n return (\n <button\n key={t}\n type=\"button\"\n role=\"tab\"\n aria-selected={tab === t}\n style={{ ...S.tab, ...(tab === t ? S.tabOn : {}) }}\n onClick={e => {\n setTab(t)\n setTryOn(null)\n e.currentTarget.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }}\n >\n {tabLabel(t)}\n {t !== 'picture' && (\n <span style={S.count}>\n {items.filter(i => owned.has(i.id)).length}/{items.length}\n </span>\n )}\n </button>\n )\n })}\n </div>\n <div style={S.pane} role=\"tabpanel\" aria-label={tabLabel(tab)}>\n {tab === 'picture' ? (\n <AvatarPicker settings={settings} sets={sets} busy={busy} run={run} />\n ) : (\n <SlotGrid\n slot={tab}\n owned={owned}\n equipped={loadout?.equipped[tab] ?? null}\n tryOn={tryOn}\n showLocked={showLocked}\n onShowLocked={setShowLocked}\n preview={id => {\n const only: AvatarCosmetics = { [tab]: id ?? undefined }\n if (tab === 'overlay') return avatarHtml({ src: picture, size: 40, worn: only })\n if (tab === 'message') return messageHtml('gg, rematch?', only, { keep: true })\n // A name tile shows the item on top of what is worn in the other name slots.\n return nameHtml(displayName, { ...worn, [tab]: id ?? undefined })\n }}\n colour={colour}\n onEquip={id => equip(tab, id)}\n onTry={id =>\n setTryOn(t => (t?.slot === tab && t.id === id ? null : { slot: tab, id }))\n }\n />\n )}\n {error && (\n <span className=\"settings-popout__error\" role=\"alert\">\n {error}\n </span>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n\ninterface StageProps {\n html: string\n editing: boolean\n overlayId: string | null\n live: Placement\n onPlace: (p: Partial<Placement>, save: 'later' | 'now' | 'no') => void\n dragging: React.MutableRefObject<boolean>\n style: React.CSSProperties\n banner: React.ReactNode\n}\n\n/** The big preview, and the placement handles drawn on its overlay while editing. */\nfunction Stage({ html, editing, overlayId, live, onPlace, dragging, style, banner }: StageProps) {\n const box = useRef<HTMLDivElement>(null)\n const drag = useRef<{\n mode: string\n p0: Placement\n x0: number\n y0: number\n cx: number\n cy: number\n a0: number\n d0: number\n size: number\n last: Placement\n } | null>(null)\n\n // The handles live inside the overlay's placement box, so they turn and\n // scale with it. Re-added whenever the preview is redrawn.\n useEffect(() => {\n const adj = box.current?.querySelector('.hc-art .hc-adj')\n if (!editing || !adj) return\n const gz = document.createElement('span')\n gz.dataset.g = 'move'\n gz.setAttribute('aria-hidden', 'true')\n gz.style.cssText =\n 'position:absolute;inset:-3px;border:1.5px dashed var(--color-primary);border-radius:6px;pointer-events:auto;cursor:move'\n const handle = (g: string, css: string) => {\n const h = document.createElement('i')\n h.dataset.g = g\n h.style.cssText = `position:absolute;pointer-events:auto;${css}`\n gz.appendChild(h)\n }\n handle(\n 'rot',\n 'left:50%;top:-20px;width:12px;height:12px;margin-left:-6px;border-radius:50%;background:var(--color-primary);cursor:grab'\n )\n handle(\n 'scale',\n 'right:-7px;bottom:-7px;width:12px;height:12px;border-radius:3px;background:var(--color-bg-card);border:2px solid var(--color-primary);cursor:nwse-resize'\n )\n adj.appendChild(gz)\n return () => gz.remove()\n }, [html, editing])\n\n const down = (e: React.PointerEvent) => {\n const g = (e.target as HTMLElement).closest<HTMLElement>('[data-g]')\n const adj = g?.closest('.hc-adj')\n const avatar = box.current?.querySelector('.hc-av')\n if (!g || !adj || !avatar || !editing || !overlayId) return\n e.preventDefault()\n g.setPointerCapture(e.pointerId)\n const r = adj.getBoundingClientRect()\n const cx = r.left + r.width / 2\n const cy = r.top + r.height / 2\n const size = avatar.getBoundingClientRect().width\n dragging.current = true\n drag.current = {\n mode: g.dataset.g ?? 'move',\n p0: live,\n x0: e.clientX,\n y0: e.clientY,\n cx,\n cy,\n a0: Math.atan2(e.clientY - cy, e.clientX - cx),\n d0: Math.hypot(e.clientX - cx, e.clientY - cy) || 1,\n size,\n last: live\n }\n }\n const move = (e: React.PointerEvent) => {\n const d = drag.current\n if (!d) return\n let next: Partial<Placement> = d.p0\n if (d.mode === 'move') {\n next = {\n ...d.p0,\n x: d.p0.x + ((e.clientX - d.x0) / d.size) * 100,\n y: d.p0.y + ((e.clientY - d.y0) / d.size) * 100\n }\n } else if (d.mode === 'rot') {\n const a = Math.atan2(e.clientY - d.cy, e.clientX - d.cx)\n next = { ...d.p0, r: d.p0.r + ((a - d.a0) * 180) / Math.PI }\n } else {\n next = { ...d.p0, s: (d.p0.s * Math.hypot(e.clientX - d.cx, e.clientY - d.cy)) / d.d0 }\n }\n d.last = next as Placement\n onPlace(next, 'no')\n }\n const up = () => {\n const d = drag.current\n if (!d) return\n drag.current = null\n dragging.current = false\n onPlace(d.last, 'now')\n }\n\n return (\n <div\n ref={box}\n role=\"group\"\n aria-label=\"Preview\"\n style={{ ...S.stage, ...style }}\n onPointerDown={down}\n onPointerMove={move}\n onPointerUp={up}\n onPointerCancel={up}\n >\n {banner}\n <Html html={html} style={{ display: 'contents' }} />\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n\ninterface SlotGridProps {\n slot: CosmeticSlot\n owned: ReadonlySet<string>\n equipped: string | null\n tryOn: { slot: CosmeticSlot; id: string } | null\n showLocked: boolean\n onShowLocked: (on: boolean) => void\n preview: (id: string | null) => string\n colour: React.CSSProperties\n onEquip: (id: string | null) => void\n onTry: (id: string) => void\n}\n\n/** One slot's items: None, then everything, earned or not. */\nfunction SlotGrid({\n slot,\n owned,\n equipped,\n tryOn,\n showLocked,\n onShowLocked,\n preview,\n colour,\n onEquip,\n onTry\n}: SlotGridProps) {\n const items = COSMETICS.filter(i => i.slot === slot)\n const locked = items.filter(i => !owned.has(i.id))\n const shown = showLocked ? items : items.filter(i => owned.has(i.id))\n\n return (\n <>\n <div style={S.grid}>\n <button\n type=\"button\"\n aria-pressed={equipped === null}\n style={{ ...S.tile, ...(equipped === null ? S.tileOn : {}) }}\n onClick={() => onEquip(null)}\n >\n <Html html={preview(null)} style={{ ...S.tilePv, ...colour }} />\n <span style={S.tileName}>None</span>\n </button>\n {shown.map(item => {\n const own = owned.has(item.id)\n const on = equipped === item.id\n const trying = tryOn?.slot === slot && tryOn.id === item.id\n return (\n <button\n key={item.id}\n type=\"button\"\n aria-pressed={own ? on : trying}\n aria-label={own ? item.label : `${item.label}, locked. ${item.earn}. Try it on.`}\n style={{\n ...S.tile,\n ...(on ? S.tileOn : {}),\n ...(trying ? S.tileTrying : {})\n }}\n onClick={() => (own ? onEquip(item.id) : onTry(item.id))}\n // A message reveal plays once; hovering its tile plays it again.\n onMouseEnter={e => slot === 'message' && replayPreview(e.currentTarget)}\n >\n <Html\n html={preview(item.id)}\n style={{ ...S.tilePv, ...colour, opacity: own ? 1 : 0.55 }}\n />\n <span style={{ ...S.tileName, color: own ? undefined : 'var(--color-text-muted)' }}>\n {!own && <Icon name=\"lock\" size={12} />}\n {item.label}\n </span>\n {!own && <span style={S.tileEarn}>{item.earn}</span>}\n </button>\n )\n })}\n </div>\n <div style={S.foot}>\n <span>\n {locked.length\n ? `${locked.length} still to earn. Pick one to try it on.`\n : 'You have earned all of these.'}\n </span>\n {locked.length > 0 && (\n <label>\n <input\n type=\"checkbox\"\n checked={showLocked}\n onChange={e => onShowLocked(e.currentTarget.checked)}\n />{' '}\n Show locked\n </label>\n )}\n </div>\n </>\n )\n}\n"],"names":["Html","live"],"mappings":"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"}
1
+ {"version":3,"file":"AvatarBuilder.js","sources":["../../src/components/AvatarBuilder.tsx"],"sourcesContent":["/**\n * AvatarBuilder — the body of AvatarDialog: the person's avatar and what they\n * wear with it, with a live preview.\n *\n * Layout (operator's pick, 2026-11-01): a preview column on the left (the\n * avatar large, with the name, and a chat line as others see it), and on the\n * right tabs for the Picture (AvatarPicker) and each cosmetic slot. Narrow\n * screens stack the two.\n *\n * Cosmetics (`@wolffm/prefs-client/cosmetics`, bundled at build like the avatar\n * client): what the person owns equips on click (every shown item is free but\n * three earned in a Major); an earned item not yet earned shows how to earn\n * it and can be tried on in the preview, which saves nothing. An overlay\n * image can be placed: drag it on the big preview, turn it with the top handle,\n * resize it with the corner handle, or use the sliders. Every move goes through\n * `fitPlacement`, the same function prefs-api runs before it stores one, so the\n * preview never shows a spot the server would refuse.\n *\n * Styles are inline for the reason AvatarPicker's are: apps compile this\n * package's CSS at their own build. Tokens only; the cosmetics' own colours come\n * from the person's character colour (and, for overlay images, the art).\n */\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Icon } from '@wolffm/themes'\nimport type { AvatarSettings, LoadoutUpdate } from '@wolffm/prefs-client/avatar'\nimport {\n COSMETICS,\n COSMETICS_BY_ID,\n COSMETIC_SLOTS,\n PLACEMENT_DEFAULT,\n PLACEMENT_LIMITS,\n avatarHtml,\n cleanLoadout,\n ensureCosmeticsStyles,\n fitPlacement,\n messageHtml,\n nameHtml,\n placementTransform,\n wornCosmetics,\n type AvatarCosmetics,\n type CosmeticSlot,\n type Loadout,\n type OverlayArt,\n type Placement\n} from '@wolffm/prefs-client/cosmetics'\nimport { AvatarPicker } from './AvatarPicker'\nimport { AVATAR_EVENT } from '../lib/avatarEvent'\nimport {\n avatarClient,\n avatarErrorText,\n fetchCharacters,\n portraitUrl,\n type CharacterSet\n} from '../lib/avatarData'\n\ntype Tab = 'picture' | CosmeticSlot\n\nconst SLOT_LABEL: Record<CosmeticSlot, string> = {\n effect: 'Effect',\n gradient: 'Gradient',\n font: 'Font',\n overlay: 'Overlay',\n message: 'Message'\n}\n\nconst SLIDERS = [\n ['x', 'Left/right', '%'],\n ['y', 'Up/down', '%'],\n ['r', 'Rotate', '°'],\n ['s', 'Size', '%']\n] as const\n\nconst S = {\n body: { display: 'grid', gap: 16, alignItems: 'start' },\n side: { display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 },\n stage: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: 12,\n padding: '48px 12px 18px',\n borderRadius: 10,\n background: 'var(--color-bg-alt)',\n border: '1px solid var(--color-border)',\n touchAction: 'none',\n userSelect: 'none'\n },\n tryOn: {\n position: 'absolute',\n left: 8,\n right: 8,\n top: 8,\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '4px 8px',\n borderRadius: 6,\n background: 'var(--color-primary-bg)',\n color: 'var(--color-on-primary-bg)',\n fontSize: '0.72rem'\n },\n linkBtn: {\n border: 0,\n background: 'none',\n color: 'inherit',\n textDecoration: 'underline',\n cursor: 'pointer',\n fontSize: 'inherit',\n padding: 0,\n flex: 'none'\n },\n chat: {\n display: 'flex',\n flexDirection: 'column',\n gap: 10,\n padding: 12,\n borderRadius: 10,\n background: 'var(--color-bg)',\n border: '1px solid var(--color-border)'\n },\n chatHead: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n fontSize: '0.72rem',\n color: 'var(--color-text-muted)'\n },\n msg: { display: 'flex', gap: 10, alignItems: 'flex-start' },\n who: { minHeight: 22, display: 'flex', alignItems: 'center' },\n line: { fontSize: '0.85rem', marginTop: 2 },\n place: {\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: 12,\n borderRadius: 10,\n border: '1px solid var(--color-border)'\n },\n placeHead: { display: 'flex', flexDirection: 'column', gap: 2, fontSize: '0.8rem' },\n hint: { fontSize: '0.72rem', color: 'var(--color-text-muted)' },\n slider: {\n display: 'grid',\n gridTemplateColumns: '64px 1fr 40px',\n alignItems: 'center',\n gap: 6,\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n },\n out: { textAlign: 'right', color: 'var(--color-text)', fontVariantNumeric: 'tabular-nums' },\n toggles: {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 10,\n alignItems: 'center',\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n },\n tabs: {\n display: 'flex',\n gap: 2,\n cursor: 'grab',\n touchAction: 'pan-x',\n borderBottom: '1px solid var(--color-border)',\n overflowX: 'auto',\n scrollbarWidth: 'none'\n },\n tab: {\n flex: 'none',\n padding: '7px 11px',\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: 'transparent',\n borderTop: 0,\n borderLeft: 0,\n borderRight: 0,\n background: 'none',\n cursor: 'pointer',\n fontSize: '0.82rem',\n fontWeight: 500,\n color: 'var(--color-text-muted)'\n },\n tabOn: { color: 'var(--color-text)', borderBottomColor: 'var(--color-primary)' },\n pane: { display: 'flex', flexDirection: 'column', gap: 10, paddingTop: 10 },\n grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(112px, 1fr))', gap: 8 },\n tile: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: 5,\n padding: 6,\n borderRadius: 8,\n border: '1px solid var(--color-border)',\n background: 'var(--color-bg)',\n color: 'var(--color-text)',\n cursor: 'pointer',\n textAlign: 'left',\n font: 'inherit'\n },\n tileOn: {\n borderColor: 'var(--color-primary)',\n boxShadow: 'inset 0 0 0 1px var(--color-primary)'\n },\n tileTrying: { borderStyle: 'dashed', borderColor: 'var(--color-primary)' },\n tilePv: {\n height: 58,\n borderRadius: 6,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'var(--color-bg-alt)',\n overflow: 'hidden',\n padding: '0 8px',\n fontSize: '0.8rem'\n },\n tileName: { fontSize: '0.8rem', fontWeight: 500, display: 'flex', alignItems: 'center', gap: 5 },\n tileEarn: { fontSize: '0.7rem', lineHeight: 1.3, color: 'var(--color-text-muted)' },\n foot: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n gap: 8,\n flexWrap: 'wrap',\n fontSize: '0.75rem',\n color: 'var(--color-text-muted)'\n }\n} satisfies Record<string, React.CSSProperties>\n\n/**\n * Trusted HTML from the cosmetics renderer, which escapes every string it is\n * given. Memoised on the string and the style: React re-sets innerHTML on every\n * render even when the string is the same, and replacing the nodes under the\n * pointer between mousedown and mouseup swallows the click.\n */\nconst Html = React.memo(\n function Html({ html, style }: { html: string; style?: React.CSSProperties }) {\n return <span style={style} dangerouslySetInnerHTML={{ __html: html }} />\n },\n (a, b) => a.html === b.html && JSON.stringify(a.style) === JSON.stringify(b.style)\n)\n\n/**\n * Play a tile's message reveal again. A finished reveal holds no animation (it\n * fills backwards only), so its markup is re-set. This runs synchronously in\n * mouseenter, before any press, so it cannot split a click; the memo above keeps\n * React from touching it afterwards.\n */\nfunction replayPreview(tile: HTMLElement) {\n const pv = tile.firstElementChild\n if (!pv) return\n const markup = pv.innerHTML\n pv.innerHTML = markup\n}\n\nfunction useNarrow(): boolean {\n const query = '(max-width: 640px)'\n const [narrow, setNarrow] = useState(() => window.matchMedia(query).matches)\n useEffect(() => {\n const mq = window.matchMedia(query)\n const on = () => setNarrow(mq.matches)\n mq.addEventListener('change', on)\n return () => mq.removeEventListener('change', on)\n }, [])\n return narrow\n}\n\n/** Move every drawing of one overlay in `root` to `p`, without re-rendering (smooth while dragging). */\nfunction applyPlacement(root: HTMLElement | null, id: string, p: Placement) {\n root?.querySelectorAll<HTMLElement>(`.hc-art[data-hc-art=\"${id}\"]`).forEach(ol => {\n ol.style.zIndex = p.back ? '0' : '3'\n const adj = ol.firstElementChild as HTMLElement | null\n if (adj) adj.style.transform = placementTransform(p)\n })\n}\n\n/** Settings with a new loadout, and what is worn recomputed from it. */\nfunction withLoadout(s: AvatarSettings, loadout: Loadout): AvatarSettings {\n return {\n ...s,\n cosmetics: { ...s.cosmetics, loadout },\n avatar: { ...s.avatar, cosmetics: wornCosmetics(loadout) }\n }\n}\n\n/**\n * A row that overflows sideways scrolls by dragging it (the operator's ask: the\n * tab strip is wider than its column and hides its scrollbar) and by the\n * mouse wheel. A drag of more than a few pixels is not a click.\n */\nfunction useDragScroll<T extends HTMLElement>() {\n // A callback ref, not useRef: the row appears only once the settings load,\n // after the first render, and the listeners must attach when it does.\n const [el, setEl] = useState<T | null>(null)\n useEffect(() => {\n if (!el) return\n let start: { x: number; left: number } | null = null\n let dragged = false\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return\n start = { x: e.clientX, left: el.scrollLeft }\n dragged = false\n }\n const move = (e: PointerEvent) => {\n if (!start) return\n const dx = e.clientX - start.x\n if (!dragged && Math.abs(dx) > 4) {\n dragged = true\n el.setPointerCapture(e.pointerId)\n el.style.cursor = 'grabbing'\n }\n if (dragged) el.scrollLeft = start.left - dx\n }\n const up = () => {\n start = null\n el.style.cursor = ''\n }\n // The click that ends a drag is not a tab choice.\n const click = (e: MouseEvent) => {\n if (!dragged) return\n e.preventDefault()\n e.stopPropagation()\n dragged = false\n }\n const wheel = (e: globalThis.WheelEvent) => {\n if (el.scrollWidth <= el.clientWidth || Math.abs(e.deltaX) > Math.abs(e.deltaY)) return\n e.preventDefault()\n el.scrollLeft += e.deltaY\n }\n el.addEventListener('pointerdown', down)\n el.addEventListener('pointermove', move)\n el.addEventListener('pointerup', up)\n el.addEventListener('pointercancel', up)\n el.addEventListener('click', click, true)\n el.addEventListener('wheel', wheel, { passive: false })\n return () => {\n el.removeEventListener('pointerdown', down)\n el.removeEventListener('pointermove', move)\n el.removeEventListener('pointerup', up)\n el.removeEventListener('pointercancel', up)\n el.removeEventListener('click', click, true)\n el.removeEventListener('wheel', wheel)\n }\n }, [el])\n return setEl\n}\n\nexport interface AvatarBuilderProps {\n /** The person's display name, for the preview. */\n name: string | null\n /** Admins also see archived items (greyed; nobody can select one). */\n isAdmin?: boolean\n}\n\nexport function AvatarBuilder({ name, isAdmin = false }: AvatarBuilderProps) {\n const [settings, setSettings] = useState<AvatarSettings | null>(null)\n const [sets, setSets] = useState<CharacterSet[] | null>(null)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [tab, setTab] = useState<Tab>('picture')\n const [tryOn, setTryOn] = useState<{ slot: CosmeticSlot; id: string } | null>(null)\n const [showLocked, setShowLocked] = useState(true)\n const [replay, setReplay] = useState(0)\n const narrow = useNarrow()\n const root = useRef<HTMLDivElement>(null)\n const tabStrip = useDragScroll<HTMLDivElement>()\n // Only the newest cosmetics request may set state: equips sent quickly in a\n // row can answer out of order.\n const seq = useRef(0)\n\n useEffect(() => ensureCosmeticsStyles(), [])\n\n useEffect(() => {\n let live = true\n avatarClient\n .get()\n .then(s => live && setSettings(s))\n .catch((err: unknown) => live && setError(avatarErrorText(err)))\n fetchCharacters()\n .then(s => live && setSets(s))\n .catch((err: unknown) => live && setError(avatarErrorText(err)))\n return () => {\n live = false\n }\n }, [])\n\n const announce = (next: AvatarSettings) =>\n // The page under the popout (a watchparty room, a game) re-reads the avatar\n // from this instead of waiting for its next join.\n window.dispatchEvent(new window.CustomEvent<AvatarSettings>(AVATAR_EVENT, { detail: next }))\n\n const run = useCallback(async (work: () => Promise<AvatarSettings>) => {\n setBusy(true)\n setError(null)\n try {\n const next = await work()\n setSettings(next)\n announce(next)\n } catch (err) {\n setError(avatarErrorText(err))\n } finally {\n setBusy(false)\n }\n }, [])\n\n /** Save a cosmetics change, showing `optimistic` at once. A failure re-reads the truth. */\n const wear = useCallback(\n async (update: LoadoutUpdate, optimistic: (s: AvatarSettings) => AvatarSettings) => {\n const mine = ++seq.current\n setError(null)\n setSettings(s => (s ? optimistic(s) : s))\n try {\n const next = await avatarClient.setCosmetics(update)\n if (mine !== seq.current) return\n setSettings(next)\n announce(next)\n } catch (err) {\n if (mine !== seq.current) return\n setError(avatarErrorText(err))\n avatarClient\n .get()\n .then(s => mine === seq.current && setSettings(s))\n .catch((e: unknown) => setError(avatarErrorText(e)))\n }\n },\n []\n )\n\n const owned = useMemo(() => new Set(settings?.cosmetics.owned ?? []), [settings])\n const loadout = settings?.cosmetics.loadout\n const equippedOverlay = loadout?.equipped.overlay ?? null\n const art: OverlayArt | undefined = equippedOverlay\n ? COSMETICS_BY_ID.get(equippedOverlay)?.art\n : undefined\n const editing = tab === 'overlay' && !tryOn && !!art && !!equippedOverlay\n\n // The placement being edited, live; saved a beat after the last change.\n const [live, setLive] = useState<Placement>(PLACEMENT_DEFAULT)\n const saveTimer = useRef<ReturnType<typeof setTimeout>>(undefined)\n const dragging = useRef(false)\n useEffect(() => {\n if (dragging.current || !equippedOverlay) return\n setLive(loadout?.placement[equippedOverlay] ?? PLACEMENT_DEFAULT)\n }, [equippedOverlay, loadout])\n useEffect(() => () => clearTimeout(saveTimer.current), [])\n\n const place = useCallback(\n (raw: Partial<Placement>, save: 'later' | 'now' | 'no') => {\n if (!art || !equippedOverlay) return\n const p = fitPlacement(art, raw)\n setLive(p)\n applyPlacement(root.current, equippedOverlay, p)\n clearTimeout(saveTimer.current)\n if (save === 'no') return\n const commit = () =>\n void wear({ placement: { [equippedOverlay]: p } }, s =>\n withLoadout(s, {\n ...s.cosmetics.loadout,\n placement: { ...s.cosmetics.loadout.placement, [equippedOverlay]: p }\n })\n )\n if (save === 'now') commit()\n else saveTimer.current = setTimeout(commit, 400)\n },\n [art, equippedOverlay, wear]\n )\n\n if (!settings) {\n return (\n <span className=\"settings-popout__hint\" role={error ? 'alert' : undefined}>\n {error ?? '…'}\n </span>\n )\n }\n\n const characters = new Map((sets ?? []).flatMap(s => s.characters).map(c => [c.id, c]))\n const character = settings.sprite ? characters.get(settings.sprite) : undefined\n const picture = settings.avatar.url ?? (character ? portraitUrl(character) : null)\n // Every name cosmetic derives its colours from the character's.\n const colour: React.CSSProperties =\n character && /^#[0-9a-f]{6}$/i.test(character.color)\n ? ({ '--hc-base': character.color } as React.CSSProperties)\n : {}\n const displayName = name || 'You'\n\n const worn: AvatarCosmetics = { ...(settings.avatar.cosmetics ?? {}) }\n if (tryOn) {\n worn[tryOn.slot] = tryOn.id\n if (tryOn.slot === 'overlay') delete worn.placement\n }\n\n const equip = (slot: CosmeticSlot, id: string | null) => {\n setTryOn(null)\n void wear({ equip: { [slot]: id } }, s => {\n const l = s.cosmetics.loadout\n return withLoadout(s, cleanLoadout({ ...l, equipped: { ...l.equipped, [slot]: id } }, owned))\n })\n }\n\n const stage = (\n <Stage\n html={\n avatarHtml({ src: picture, size: 112, worn }) + nameHtml(displayName, worn, { large: true })\n }\n editing={editing}\n overlayId={equippedOverlay}\n live={live}\n onPlace={place}\n dragging={dragging}\n style={colour}\n banner={\n tryOn && (\n <div style={S.tryOn}>\n <Icon name=\"lock\" size={12} />\n <span style={{ flex: 1, minWidth: 0 }}>\n Trying on {COSMETICS_BY_ID.get(tryOn.id)?.label} ·{' '}\n {COSMETICS_BY_ID.get(tryOn.id)?.earn}\n </span>\n <button type=\"button\" style={S.linkBtn} onClick={() => setTryOn(null)}>\n Take off\n </button>\n </div>\n )\n }\n />\n )\n\n const chat = (\n <div style={S.chat}>\n <div style={S.chatHead}>\n <span>In chat</span>\n <button\n type=\"button\"\n className=\"settings-popout__btn\"\n onClick={() => setReplay(n => n + 1)}\n >\n Replay\n </button>\n </div>\n <div style={S.msg}>\n <Html html={avatarHtml({ src: picture, size: 36, worn })} style={colour} />\n <div style={{ minWidth: 0, ...colour }}>\n <div style={S.who}>\n <Html html={nameHtml(displayName, worn)} />\n </div>\n <div style={S.line}>\n <Html key={replay} html={messageHtml('gg, that last hand was brutal', worn)} />\n </div>\n </div>\n </div>\n </div>\n )\n\n const placement = editing && art && (\n <div style={S.place} role=\"group\" aria-label=\"Placement\">\n <div style={S.placeHead}>\n <strong>Placement</strong>\n <span style={S.hint}>\n Drag it on the avatar. The top handle turns it, the corner resizes it.\n </span>\n </div>\n {SLIDERS.map(([k, label, unit]) => (\n <label key={k} style={S.slider}>\n <span>{label}</span>\n <input\n type=\"range\"\n min={PLACEMENT_LIMITS[k][0]}\n max={PLACEMENT_LIMITS[k][1]}\n value={live[k]}\n onChange={e => place({ ...live, [k]: Number(e.currentTarget.value) }, 'later')}\n style={{ width: '100%', accentColor: 'var(--color-primary)' }}\n />\n <output style={S.out}>\n {live[k]}\n {unit}\n </output>\n </label>\n ))}\n <div style={S.toggles}>\n <label>\n <input\n type=\"checkbox\"\n checked={live.flip}\n onChange={e => place({ ...live, flip: e.currentTarget.checked }, 'now')}\n />{' '}\n Mirror\n </label>\n <label>\n <input\n type=\"checkbox\"\n checked={live.back}\n onChange={e => place({ ...live, back: e.currentTarget.checked }, 'now')}\n />{' '}\n Behind\n </label>\n <button\n type=\"button\"\n className=\"settings-popout__btn\"\n onClick={() => place(PLACEMENT_DEFAULT, 'now')}\n >\n Reset\n </button>\n </div>\n </div>\n )\n\n const tabs: Tab[] = ['picture', ...COSMETIC_SLOTS]\n const tabLabel = (t: Tab) => (t === 'picture' ? 'Picture' : SLOT_LABEL[t])\n\n return (\n <div\n ref={root}\n style={{ ...S.body, gridTemplateColumns: narrow ? 'minmax(0, 1fr)' : '240px minmax(0, 1fr)' }}\n >\n <div style={S.side}>\n {stage}\n {chat}\n {placement}\n </div>\n <div style={{ minWidth: 0 }}>\n <div ref={tabStrip} style={S.tabs} role=\"tablist\" aria-label=\"Avatar\">\n {tabs.map(t => {\n return (\n <button\n key={t}\n type=\"button\"\n role=\"tab\"\n aria-selected={tab === t}\n style={{ ...S.tab, ...(tab === t ? S.tabOn : {}) }}\n onClick={e => {\n setTab(t)\n setTryOn(null)\n // Where the strip overflows (narrow screens), a tab in its\n // right half shifts the strip to the end and one in its left\n // half to the start, so the tabs past the chosen one show.\n const strip = e.currentTarget.parentElement\n if (strip && strip.scrollWidth > strip.clientWidth) {\n const right = tabs.indexOf(t) >= tabs.length / 2\n strip.scrollTo({\n left: right ? strip.scrollWidth - strip.clientWidth : 0,\n behavior: 'smooth'\n })\n }\n }}\n >\n {tabLabel(t)}\n </button>\n )\n })}\n </div>\n <div style={S.pane} role=\"tabpanel\" aria-label={tabLabel(tab)}>\n {tab === 'picture' ? (\n <AvatarPicker settings={settings} sets={sets} busy={busy} run={run} />\n ) : (\n <SlotGrid\n slot={tab}\n owned={owned}\n equipped={loadout?.equipped[tab] ?? null}\n tryOn={tryOn}\n showLocked={showLocked}\n onShowLocked={setShowLocked}\n isAdmin={isAdmin}\n preview={id => {\n const only: AvatarCosmetics = { [tab]: id ?? undefined }\n if (tab === 'overlay') return avatarHtml({ src: picture, size: 40, worn: only })\n if (tab === 'message') return messageHtml('gg, rematch?', only, { keep: true })\n // A name tile shows the item on top of what is worn in the other name slots.\n return nameHtml(displayName, { ...worn, [tab]: id ?? undefined })\n }}\n colour={colour}\n onEquip={id => equip(tab, id)}\n onTry={id =>\n setTryOn(t => (t?.slot === tab && t.id === id ? null : { slot: tab, id }))\n }\n />\n )}\n {error && (\n <span className=\"settings-popout__error\" role=\"alert\">\n {error}\n </span>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n\ninterface StageProps {\n html: string\n editing: boolean\n overlayId: string | null\n live: Placement\n onPlace: (p: Partial<Placement>, save: 'later' | 'now' | 'no') => void\n dragging: React.MutableRefObject<boolean>\n style: React.CSSProperties\n banner: React.ReactNode\n}\n\n/** The big preview, and the placement handles drawn on its overlay while editing. */\nfunction Stage({ html, editing, overlayId, live, onPlace, dragging, style, banner }: StageProps) {\n const box = useRef<HTMLDivElement>(null)\n const drag = useRef<{\n mode: string\n p0: Placement\n x0: number\n y0: number\n cx: number\n cy: number\n a0: number\n d0: number\n size: number\n last: Placement\n } | null>(null)\n\n // The handles live inside the overlay's placement box, so they turn and\n // scale with it. Re-added whenever the preview is redrawn.\n useEffect(() => {\n const adj = box.current?.querySelector('.hc-art .hc-adj')\n if (!editing || !adj) return\n const gz = document.createElement('span')\n gz.dataset.g = 'move'\n gz.setAttribute('aria-hidden', 'true')\n gz.style.cssText =\n 'position:absolute;inset:-3px;border:1.5px dashed var(--color-primary);border-radius:6px;pointer-events:auto;cursor:move'\n const handle = (g: string, css: string) => {\n const h = document.createElement('i')\n h.dataset.g = g\n h.style.cssText = `position:absolute;pointer-events:auto;${css}`\n gz.appendChild(h)\n }\n handle(\n 'rot',\n 'left:50%;top:-20px;width:12px;height:12px;margin-left:-6px;border-radius:50%;background:var(--color-primary);cursor:grab'\n )\n handle(\n 'scale',\n 'right:-7px;bottom:-7px;width:12px;height:12px;border-radius:3px;background:var(--color-bg-card);border:2px solid var(--color-primary);cursor:nwse-resize'\n )\n adj.appendChild(gz)\n return () => gz.remove()\n }, [html, editing])\n\n const down = (e: React.PointerEvent) => {\n const g = (e.target as HTMLElement).closest<HTMLElement>('[data-g]')\n const adj = g?.closest('.hc-adj')\n const avatar = box.current?.querySelector('.hc-av')\n if (!g || !adj || !avatar || !editing || !overlayId) return\n e.preventDefault()\n g.setPointerCapture(e.pointerId)\n const r = adj.getBoundingClientRect()\n const cx = r.left + r.width / 2\n const cy = r.top + r.height / 2\n const size = avatar.getBoundingClientRect().width\n dragging.current = true\n drag.current = {\n mode: g.dataset.g ?? 'move',\n p0: live,\n x0: e.clientX,\n y0: e.clientY,\n cx,\n cy,\n a0: Math.atan2(e.clientY - cy, e.clientX - cx),\n d0: Math.hypot(e.clientX - cx, e.clientY - cy) || 1,\n size,\n last: live\n }\n }\n const move = (e: React.PointerEvent) => {\n const d = drag.current\n if (!d) return\n let next: Partial<Placement> = d.p0\n if (d.mode === 'move') {\n next = {\n ...d.p0,\n x: d.p0.x + ((e.clientX - d.x0) / d.size) * 100,\n y: d.p0.y + ((e.clientY - d.y0) / d.size) * 100\n }\n } else if (d.mode === 'rot') {\n const a = Math.atan2(e.clientY - d.cy, e.clientX - d.cx)\n next = { ...d.p0, r: d.p0.r + ((a - d.a0) * 180) / Math.PI }\n } else {\n next = { ...d.p0, s: (d.p0.s * Math.hypot(e.clientX - d.cx, e.clientY - d.cy)) / d.d0 }\n }\n d.last = next as Placement\n onPlace(next, 'no')\n }\n const up = () => {\n const d = drag.current\n if (!d) return\n drag.current = null\n dragging.current = false\n onPlace(d.last, 'now')\n }\n\n return (\n <div\n ref={box}\n role=\"group\"\n aria-label=\"Preview\"\n style={{ ...S.stage, ...style }}\n onPointerDown={down}\n onPointerMove={move}\n onPointerUp={up}\n onPointerCancel={up}\n >\n {banner}\n <Html html={html} style={{ display: 'contents' }} />\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n\ninterface SlotGridProps {\n slot: CosmeticSlot\n owned: ReadonlySet<string>\n equipped: string | null\n tryOn: { slot: CosmeticSlot; id: string } | null\n showLocked: boolean\n onShowLocked: (on: boolean) => void\n /** Admins see archived items, greyed and not selectable. */\n isAdmin: boolean\n preview: (id: string | null) => string\n colour: React.CSSProperties\n onEquip: (id: string | null) => void\n onTry: (id: string) => void\n}\n\n/**\n * One slot's items: None, then the shown items, free or earned. Archived items\n * show only to admins, greyed and not selectable; nobody can wear one.\n */\nfunction SlotGrid({\n slot,\n owned,\n equipped,\n tryOn,\n showLocked,\n onShowLocked,\n isAdmin,\n preview,\n colour,\n onEquip,\n onTry\n}: SlotGridProps) {\n const items = COSMETICS.filter(i => i.slot === slot && (!i.hidden || isAdmin))\n const locked = items.filter(i => !i.hidden && !owned.has(i.id))\n const shown = showLocked ? items : items.filter(i => i.hidden || owned.has(i.id))\n\n return (\n <>\n <div style={S.grid}>\n <button\n type=\"button\"\n aria-pressed={equipped === null}\n style={{ ...S.tile, ...(equipped === null ? S.tileOn : {}) }}\n onClick={() => onEquip(null)}\n >\n <Html html={preview(null)} style={{ ...S.tilePv, ...colour }} />\n <span style={S.tileName}>None</span>\n </button>\n {shown.map(item => {\n if (item.hidden) {\n return (\n <button\n key={item.id}\n type=\"button\"\n disabled\n aria-label={`${item.label}, archived`}\n style={{ ...S.tile, cursor: 'not-allowed', opacity: 0.45 }}\n >\n <Html html={preview(item.id)} style={{ ...S.tilePv, ...colour }} />\n <span style={S.tileName}>{item.label}</span>\n <span style={S.tileEarn}>Archived</span>\n </button>\n )\n }\n const own = owned.has(item.id)\n const on = equipped === item.id\n const trying = tryOn?.slot === slot && tryOn.id === item.id\n return (\n <button\n key={item.id}\n type=\"button\"\n aria-pressed={own ? on : trying}\n aria-label={own ? item.label : `${item.label}, locked. ${item.earn ?? ''}. Try it on.`}\n style={{\n ...S.tile,\n ...(on ? S.tileOn : {}),\n ...(trying ? S.tileTrying : {})\n }}\n onClick={() => (own ? onEquip(item.id) : onTry(item.id))}\n // A message reveal plays once; hovering its tile plays it again.\n onMouseEnter={e => slot === 'message' && replayPreview(e.currentTarget)}\n >\n <Html\n html={preview(item.id)}\n style={{ ...S.tilePv, ...colour, opacity: own ? 1 : 0.55 }}\n />\n <span style={{ ...S.tileName, color: own ? undefined : 'var(--color-text-muted)' }}>\n {!own && <Icon name=\"lock\" size={12} />}\n {item.label}\n </span>\n {!own && <span style={S.tileEarn}>{item.earn}</span>}\n </button>\n )\n })}\n </div>\n {locked.length > 0 && (\n <div style={S.foot}>\n <span>\n {locked.length === 1 ? 'One to earn.' : `${locked.length} to earn.`} Pick one to try it\n on.\n </span>\n <label>\n <input\n type=\"checkbox\"\n checked={showLocked}\n onChange={e => onShowLocked(e.currentTarget.checked)}\n />{' '}\n Show locked\n </label>\n </div>\n )}\n </>\n )\n}\n"],"names":["Html","live"],"mappings":"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"}
@@ -19,6 +19,8 @@ export interface AvatarDialogProps {
19
19
  returnFocus?: React.RefObject<HTMLElement | null>;
20
20
  /** The person's display name, shown in the preview. */
21
21
  name?: string | null;
22
+ /** Admins also see archived cosmetics. */
23
+ isAdmin?: boolean;
22
24
  }
23
- export declare function AvatarDialog({ open, onClose, returnFocus, name }: AvatarDialogProps): React.ReactPortal | null;
25
+ export declare function AvatarDialog({ open, onClose, returnFocus, name, isAdmin }: AvatarDialogProps): React.ReactPortal | null;
24
26
  //# sourceMappingURL=AvatarDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarDialog.d.ts","sourceRoot":"","sources":["../../src/components/AvatarDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAmC,MAAM,OAAO,CAAA;AAuCvD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,mEAAmE;IACnE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACjD,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAW,EAAE,EAAE,iBAAiB,4BAwD1F"}
1
+ {"version":3,"file":"AvatarDialog.d.ts","sourceRoot":"","sources":["../../src/components/AvatarDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAmC,MAAM,OAAO,CAAA;AAuCvD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,mEAAmE;IACnE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACjD,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,OAAO,EACP,WAAW,EACX,IAAW,EACX,OAAe,EAChB,EAAE,iBAAiB,4BAwDnB"}
@@ -36,7 +36,13 @@ const S = {
36
36
  head: { display: "flex", alignItems: "center", gap: 10 },
37
37
  title: { margin: 0, flex: 1, fontSize: "1.05rem", fontWeight: 600 }
38
38
  };
39
- function AvatarDialog({ open, onClose, returnFocus, name = null }) {
39
+ function AvatarDialog({
40
+ open,
41
+ onClose,
42
+ returnFocus,
43
+ name = null,
44
+ isAdmin = false
45
+ }) {
40
46
  const titleId = useId();
41
47
  const cardRef = useRef(null);
42
48
  const onCloseRef = useRef(onClose);
@@ -82,7 +88,7 @@ function AvatarDialog({ open, onClose, returnFocus, name = null }) {
82
88
  }
83
89
  )
84
90
  ] }),
85
- /* @__PURE__ */ jsx(AvatarBuilder, { name })
91
+ /* @__PURE__ */ jsx(AvatarBuilder, { name, isAdmin })
86
92
  ]
87
93
  }
88
94
  ) }),
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarDialog.js","sources":["../../src/components/AvatarDialog.tsx"],"sourcesContent":["/**\n * AvatarDialog — the avatar builder (picture, character and cosmetics, with a\n * live preview) in its own dialog, opened from the settings popout's \"Change\".\n * The popout is 300px wide; the builder needs the room.\n *\n * Rendered into document.body, so no ancestor of the popout (a transform, an\n * overflow, a stacking context) can clip or trap it. The popout stays open\n * underneath: closing the dialog returns to it, and focus goes back to Change.\n *\n * Styles are inline for the reason AvatarPicker's are: apps compile this\n * package's CSS at their own build, so a new stylesheet rule reaches an app only\n * when it rebuilds. Tokens only.\n */\nimport React, { useEffect, useId, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport { AvatarBuilder } from './AvatarBuilder'\n\nconst S: Record<string, React.CSSProperties> = {\n overlay: {\n position: 'fixed',\n inset: 0,\n // Above the popout's panel (100) and its click-catcher (99).\n zIndex: 1000,\n display: 'flex',\n alignItems: 'flex-start',\n justifyContent: 'center',\n boxSizing: 'border-box',\n padding: '6vh 16px',\n background: 'var(--color-bg-overlay)'\n },\n card: {\n boxSizing: 'border-box',\n width: 'min(720px, 100%)',\n maxHeight: '88vh',\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 14,\n padding: 20,\n borderRadius: 12,\n background: 'var(--color-bg-card)',\n border: '1px solid var(--color-border)',\n boxShadow: '0 8px 28px rgba(0, 0, 0, 0.18)',\n color: 'var(--color-text)',\n // Outside the app's root, so nothing passes the app's font down to it.\n fontFamily: 'var(--font-family)',\n outline: 'none'\n },\n head: { display: 'flex', alignItems: 'center', gap: 10 },\n title: { margin: 0, flex: 1, fontSize: '1.05rem', fontWeight: 600 }\n}\n\nexport interface AvatarDialogProps {\n open: boolean\n onClose: () => void\n /** Where focus goes when the dialog closes (the Change button). */\n returnFocus?: React.RefObject<HTMLElement | null>\n /** The person's display name, shown in the preview. */\n name?: string | null\n}\n\nexport function AvatarDialog({ open, onClose, returnFocus, name = null }: AvatarDialogProps) {\n const titleId = useId()\n const cardRef = useRef<HTMLDivElement>(null)\n const onCloseRef = useRef(onClose)\n onCloseRef.current = onClose\n\n useEffect(() => {\n if (!open) return\n cardRef.current?.focus()\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return\n e.preventDefault()\n // Stop it here: closing re-arms the popout's own Escape listener on window\n // (React flushes a keypress's effects at once), and this same keypress\n // would reach it next and close the popout too.\n e.stopPropagation()\n onCloseRef.current()\n }\n document.addEventListener('keydown', onKey)\n const back = returnFocus?.current\n return () => {\n document.removeEventListener('keydown', onKey)\n back?.focus()\n }\n }, [open, returnFocus])\n\n if (!open || typeof document === 'undefined') return null\n\n return createPortal(\n <div style={S.overlay} onClick={onClose}>\n <div\n ref={cardRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n style={S.card}\n onClick={e => e.stopPropagation()}\n >\n <div style={S.head}>\n <h2 id={titleId} style={S.title}>\n Avatar\n </h2>\n <button\n type=\"button\"\n className=\"settings-popout__btn settings-popout__btn--primary\"\n onClick={onClose}\n >\n Done\n </button>\n </div>\n <AvatarBuilder name={name} />\n </div>\n </div>,\n document.body\n )\n}\n"],"names":[],"mappings":";;;;AAiBA,MAAM,IAAyC;AAAA,EAC7C,SAAS;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA;AAAA,IAEP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,EAAA;AAAA,EAEd,MAAM;AAAA,IACJ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA;AAAA,IAEP,YAAY;AAAA,IACZ,SAAS;AAAA,EAAA;AAAA,EAEX,MAAM,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAA;AAAA,EACpD,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,WAAW,YAAY,IAAA;AAChE;AAWO,SAAS,aAAa,EAAE,MAAM,SAAS,aAAa,OAAO,QAA2B;AAC3F,QAAM,UAAU,MAAA;AAChB,QAAM,UAAU,OAAuB,IAAI;AAC3C,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,YAAQ,SAAS,MAAA;AACjB,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,SAAU;AACxB,QAAE,eAAA;AAIF,QAAE,gBAAA;AACF,iBAAW,QAAA;AAAA,IACb;AACA,aAAS,iBAAiB,WAAW,KAAK;AAC1C,UAAM,OAAO,aAAa;AAC1B,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,KAAK;AAC7C,YAAM,MAAA;AAAA,IACR;AAAA,EACF,GAAG,CAAC,MAAM,WAAW,CAAC;AAEtB,MAAI,CAAC,QAAQ,OAAO,aAAa,YAAa,QAAO;AAErD,SAAO;AAAA,wBACJ,OAAA,EAAI,OAAO,EAAE,SAAS,SAAS,SAC9B,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,cAAW;AAAA,QACX,mBAAiB;AAAA,QACjB,UAAU;AAAA,QACV,OAAO,EAAE;AAAA,QACT,SAAS,CAAA,MAAK,EAAE,gBAAA;AAAA,QAEhB,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,OAAO,EAAE,MACZ,UAAA;AAAA,YAAA,oBAAC,QAAG,IAAI,SAAS,OAAO,EAAE,OAAO,UAAA,UAEjC;AAAA,YACA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS;AAAA,gBACV,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UAED,GACF;AAAA,UACA,oBAAC,iBAAc,KAAA,CAAY;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAE/B;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
1
+ {"version":3,"file":"AvatarDialog.js","sources":["../../src/components/AvatarDialog.tsx"],"sourcesContent":["/**\n * AvatarDialog — the avatar builder (picture, character and cosmetics, with a\n * live preview) in its own dialog, opened from the settings popout's \"Change\".\n * The popout is 300px wide; the builder needs the room.\n *\n * Rendered into document.body, so no ancestor of the popout (a transform, an\n * overflow, a stacking context) can clip or trap it. The popout stays open\n * underneath: closing the dialog returns to it, and focus goes back to Change.\n *\n * Styles are inline for the reason AvatarPicker's are: apps compile this\n * package's CSS at their own build, so a new stylesheet rule reaches an app only\n * when it rebuilds. Tokens only.\n */\nimport React, { useEffect, useId, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport { AvatarBuilder } from './AvatarBuilder'\n\nconst S: Record<string, React.CSSProperties> = {\n overlay: {\n position: 'fixed',\n inset: 0,\n // Above the popout's panel (100) and its click-catcher (99).\n zIndex: 1000,\n display: 'flex',\n alignItems: 'flex-start',\n justifyContent: 'center',\n boxSizing: 'border-box',\n padding: '6vh 16px',\n background: 'var(--color-bg-overlay)'\n },\n card: {\n boxSizing: 'border-box',\n width: 'min(720px, 100%)',\n maxHeight: '88vh',\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 14,\n padding: 20,\n borderRadius: 12,\n background: 'var(--color-bg-card)',\n border: '1px solid var(--color-border)',\n boxShadow: '0 8px 28px rgba(0, 0, 0, 0.18)',\n color: 'var(--color-text)',\n // Outside the app's root, so nothing passes the app's font down to it.\n fontFamily: 'var(--font-family)',\n outline: 'none'\n },\n head: { display: 'flex', alignItems: 'center', gap: 10 },\n title: { margin: 0, flex: 1, fontSize: '1.05rem', fontWeight: 600 }\n}\n\nexport interface AvatarDialogProps {\n open: boolean\n onClose: () => void\n /** Where focus goes when the dialog closes (the Change button). */\n returnFocus?: React.RefObject<HTMLElement | null>\n /** The person's display name, shown in the preview. */\n name?: string | null\n /** Admins also see archived cosmetics. */\n isAdmin?: boolean\n}\n\nexport function AvatarDialog({\n open,\n onClose,\n returnFocus,\n name = null,\n isAdmin = false\n}: AvatarDialogProps) {\n const titleId = useId()\n const cardRef = useRef<HTMLDivElement>(null)\n const onCloseRef = useRef(onClose)\n onCloseRef.current = onClose\n\n useEffect(() => {\n if (!open) return\n cardRef.current?.focus()\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return\n e.preventDefault()\n // Stop it here: closing re-arms the popout's own Escape listener on window\n // (React flushes a keypress's effects at once), and this same keypress\n // would reach it next and close the popout too.\n e.stopPropagation()\n onCloseRef.current()\n }\n document.addEventListener('keydown', onKey)\n const back = returnFocus?.current\n return () => {\n document.removeEventListener('keydown', onKey)\n back?.focus()\n }\n }, [open, returnFocus])\n\n if (!open || typeof document === 'undefined') return null\n\n return createPortal(\n <div style={S.overlay} onClick={onClose}>\n <div\n ref={cardRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n style={S.card}\n onClick={e => e.stopPropagation()}\n >\n <div style={S.head}>\n <h2 id={titleId} style={S.title}>\n Avatar\n </h2>\n <button\n type=\"button\"\n className=\"settings-popout__btn settings-popout__btn--primary\"\n onClick={onClose}\n >\n Done\n </button>\n </div>\n <AvatarBuilder name={name} isAdmin={isAdmin} />\n </div>\n </div>,\n document.body\n )\n}\n"],"names":[],"mappings":";;;;AAiBA,MAAM,IAAyC;AAAA,EAC7C,SAAS;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA;AAAA,IAEP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,EAAA;AAAA,EAEd,MAAM;AAAA,IACJ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA;AAAA,IAEP,YAAY;AAAA,IACZ,SAAS;AAAA,EAAA;AAAA,EAEX,MAAM,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAA;AAAA,EACpD,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,WAAW,YAAY,IAAA;AAChE;AAaO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AACZ,GAAsB;AACpB,QAAM,UAAU,MAAA;AAChB,QAAM,UAAU,OAAuB,IAAI;AAC3C,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,YAAQ,SAAS,MAAA;AACjB,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,SAAU;AACxB,QAAE,eAAA;AAIF,QAAE,gBAAA;AACF,iBAAW,QAAA;AAAA,IACb;AACA,aAAS,iBAAiB,WAAW,KAAK;AAC1C,UAAM,OAAO,aAAa;AAC1B,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,KAAK;AAC7C,YAAM,MAAA;AAAA,IACR;AAAA,EACF,GAAG,CAAC,MAAM,WAAW,CAAC;AAEtB,MAAI,CAAC,QAAQ,OAAO,aAAa,YAAa,QAAO;AAErD,SAAO;AAAA,wBACJ,OAAA,EAAI,OAAO,EAAE,SAAS,SAAS,SAC9B,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,cAAW;AAAA,QACX,mBAAiB;AAAA,QACjB,UAAU;AAAA,QACV,OAAO,EAAE;AAAA,QACT,SAAS,CAAA,MAAK,EAAE,gBAAA;AAAA,QAEhB,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,OAAO,EAAE,MACZ,UAAA;AAAA,YAAA,oBAAC,QAAG,IAAI,SAAS,OAAO,EAAE,OAAO,UAAA,UAEjC;AAAA,YACA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS;AAAA,gBACV,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UAED,GACF;AAAA,UACA,oBAAC,eAAA,EAAc,MAAY,QAAA,CAAkB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAEjD;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectedSettings.d.ts","sourceRoot":"","sources":["../../src/components/ConnectedSettings.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAA0D,MAAM,OAAO,CAAA;AAW9E,OAAO,EAWL,KAAK,IAAI,EACV,MAAM,uBAAuB,CAAA;AAC9B,OAAO,mBAAmB,CAAA;AAE1B,MAAM,WAAW,sBAAsB;IACrC;;;;;;+EAM2E;IAC3E,QAAQ,CAAC,EAAE,IAAI,CAAA;IACf,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,mEAAmE;IACnE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC5C;;;;;4EAKwE;IACxE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B;;6EAEyE;IACzE,OAAO,CAAC,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CAC9B;AAmID,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,QAAQ,EACd,YAAY,EACZ,QAAQ,EACR,OAAO,EACR,EAAE,sBAAsB,qBAq0BxB"}
1
+ {"version":3,"file":"ConnectedSettings.d.ts","sourceRoot":"","sources":["../../src/components/ConnectedSettings.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAA0D,MAAM,OAAO,CAAA;AAW9E,OAAO,EAWL,KAAK,IAAI,EACV,MAAM,uBAAuB,CAAA;AAC9B,OAAO,mBAAmB,CAAA;AAE1B,MAAM,WAAW,sBAAsB;IACrC;;;;;;+EAM2E;IAC3E,QAAQ,CAAC,EAAE,IAAI,CAAA;IACf,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,mEAAmE;IACnE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC5C;;;;;4EAKwE;IACxE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B;;6EAEyE;IACzE,OAAO,CAAC,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CAC9B;AAmID,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,QAAQ,EACd,YAAY,EACZ,QAAQ,EACR,OAAO,EACR,EAAE,sBAAsB,qBAs0BxB"}
@@ -498,7 +498,8 @@ function ConnectedSettings({
498
498
  open: avatarOpen,
499
499
  onClose: () => setAvatarOpen(false),
500
500
  returnFocus: avatarBtn,
501
- name: displayName
501
+ name: displayName,
502
+ isAdmin: userType === "admin"
502
503
  }
503
504
  )
504
505
  ] }),