@burdenoff/microfe-comradecircle 2026.704.2 → 2026.704.3

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 (41) hide show
  1. package/dist/comradecircle/components/Avatar.js +40 -17
  2. package/dist/comradecircle/components/Avatar.js.map +1 -1
  3. package/dist/comradecircle/components/AvatarUploadField.js +25 -14
  4. package/dist/comradecircle/components/AvatarUploadField.js.map +1 -1
  5. package/dist/comradecircle/components/CircleCard.js +54 -52
  6. package/dist/comradecircle/components/CircleCard.js.map +1 -1
  7. package/dist/comradecircle/components/CommentThread.js +80 -64
  8. package/dist/comradecircle/components/CommentThread.js.map +1 -1
  9. package/dist/comradecircle/components/EventCard.js +84 -58
  10. package/dist/comradecircle/components/EventCard.js.map +1 -1
  11. package/dist/comradecircle/components/MemberIdentity.js +2 -1
  12. package/dist/comradecircle/components/MemberIdentity.js.map +1 -1
  13. package/dist/comradecircle/components/PollCard.js +65 -44
  14. package/dist/comradecircle/components/PollCard.js.map +1 -1
  15. package/dist/comradecircle/components/PostCard.js +1 -1
  16. package/dist/comradecircle/components/PostCard.js.map +1 -1
  17. package/dist/comradecircle/components/ReactionBar.js +1 -1
  18. package/dist/comradecircle/components/ReactionBar.js.map +1 -1
  19. package/dist/comradecircle/components/StateViews.js +1 -16
  20. package/dist/comradecircle/components/StateViews.js.map +1 -1
  21. package/dist/comradecircle/components/StatusBadge.js +1 -1
  22. package/dist/comradecircle/components/StatusBadge.js.map +1 -1
  23. package/dist/comradecircle/hooks/useComradeCircleApi.js +1 -1
  24. package/dist/comradecircle/hooks/useComradeCircleApi.js.map +1 -1
  25. package/dist/comradecircle/pages/FeedPage.js +79 -33
  26. package/dist/comradecircle/pages/FeedPage.js.map +1 -1
  27. package/dist/comradecircle/pages/circles/CircleDetailPage.js +354 -243
  28. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
  29. package/dist/comradecircle/pages/circles/CirclesPage.js +97 -47
  30. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
  31. package/dist/comradecircle/pages/events/EventDetailPage.js +193 -126
  32. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
  33. package/dist/comradecircle/pages/events/EventsPage.js +74 -34
  34. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
  35. package/dist/comradecircle/pages/members/MemberProfilePage.js +154 -82
  36. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
  37. package/dist/comradecircle/pages/members/MembersPage.js +140 -71
  38. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
  39. package/dist/comradecircle/pages/profile/MyProfilePage.js +298 -202
  40. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
  41. package/package.json +1 -1
@@ -7,41 +7,64 @@ var i = {
7
7
  sm: "h-8 w-8 text-xs",
8
8
  md: "h-10 w-10 text-sm",
9
9
  lg: "h-14 w-14 text-lg",
10
- xl: "h-20 w-20 text-2xl"
10
+ xl: "h-20 w-20 text-2xl",
11
+ "2xl": "h-28 w-28 text-4xl"
11
12
  }, a = {
12
13
  ONLINE: "bg-status-success-text",
13
14
  AWAY: "bg-status-warning-text",
14
15
  BUSY: "bg-status-error-text",
15
16
  OFFLINE: "bg-text-tertiary"
16
- };
17
- function o(e) {
17
+ }, o = {
18
+ xs: "h-2 w-2",
19
+ sm: "h-2.5 w-2.5",
20
+ md: "h-2.5 w-2.5",
21
+ lg: "h-3 w-3",
22
+ xl: "h-3.5 w-3.5",
23
+ "2xl": "h-4 w-4"
24
+ }, s = [
25
+ "bg-blue-500/20 text-blue-700 dark:text-blue-200",
26
+ "bg-violet-500/20 text-violet-700 dark:text-violet-200",
27
+ "bg-emerald-500/20 text-emerald-700 dark:text-emerald-200",
28
+ "bg-amber-500/20 text-amber-700 dark:text-amber-100",
29
+ "bg-rose-500/20 text-rose-700 dark:text-rose-200",
30
+ "bg-cyan-500/20 text-cyan-700 dark:text-cyan-200",
31
+ "bg-fuchsia-500/20 text-fuchsia-700 dark:text-fuchsia-200",
32
+ "bg-teal-500/20 text-teal-700 dark:text-teal-200"
33
+ ];
34
+ function c(e) {
35
+ if (!e) return s[0];
36
+ let t = 0;
37
+ for (let n = 0; n < e.length; n += 1) t = t * 31 + e.charCodeAt(n) | 0;
38
+ return s[Math.abs(t) % s.length];
39
+ }
40
+ function l(e) {
18
41
  if (!e) return "?";
19
42
  let t = e.trim().split(/\s+/).filter(Boolean);
20
43
  return t.length === 0 ? "?" : t.length === 1 ? t[0].slice(0, 2).toUpperCase() : (t[0][0] + t[t.length - 1][0]).toUpperCase();
21
44
  }
22
- var s = ({ name: s, size: c = "md", presence: l, fileId: u, className: d }) => {
23
- let f = /* @__PURE__ */ n("span", {
45
+ var u = ({ name: s, size: u = "md", presence: d, fileId: f, seed: p, ring: m = !1, className: h }) => {
46
+ let g = /* @__PURE__ */ n("span", {
24
47
  "aria-hidden": "true",
25
- className: e("flex h-full w-full items-center justify-center rounded-full bg-[var(--color-accent-soft)] font-semibold text-text-primary", i[c]),
26
- children: o(s)
48
+ className: e("flex h-full w-full items-center justify-center rounded-full font-semibold", c(p ?? s), i[u]),
49
+ children: l(s)
27
50
  });
28
51
  return /* @__PURE__ */ r("span", {
29
- className: e("relative inline-flex shrink-0", d),
52
+ className: e("relative inline-flex shrink-0", h),
30
53
  children: [/* @__PURE__ */ n("span", {
31
- className: e("inline-flex items-center justify-center overflow-hidden rounded-full ring-1 ring-border-seam", i[c]),
32
- children: u ? /* @__PURE__ */ n(t, {
33
- fileId: u,
54
+ className: e("inline-flex items-center justify-center overflow-hidden rounded-full", m ? "ring-2 ring-[var(--color-accent-soft)] shadow-[0_0_0_4px_var(--color-accent-soft)]" : "ring-1 ring-border-seam", i[u]),
55
+ children: f ? /* @__PURE__ */ n(t, {
56
+ fileId: f,
34
57
  alt: s ?? "Avatar",
35
58
  className: "h-full w-full rounded-full",
36
- fallback: f
37
- }) : f
38
- }), l && /* @__PURE__ */ n("span", {
39
- className: e("absolute -bottom-0.5 -right-0.5 h-2.5 w-2.5 rounded-full ring-2 ring-bg-surface", a[l], l === "ONLINE" && "motion-safe:animate-vc-glow-pulse"),
40
- "aria-label": `Presence: ${l.toLowerCase()}`
59
+ fallback: g
60
+ }) : g
61
+ }), d && /* @__PURE__ */ n("span", {
62
+ className: e("absolute -bottom-0.5 -right-0.5 rounded-full ring-2 ring-bg-surface", o[u], a[d], d === "ONLINE" && "motion-safe:animate-vc-glow-pulse"),
63
+ "aria-label": `Presence: ${d.toLowerCase()}`
41
64
  })]
42
65
  });
43
66
  };
44
67
  //#endregion
45
- export { s as Avatar };
68
+ export { u as Avatar };
46
69
 
47
70
  //# sourceMappingURL=Avatar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.js","names":[],"sources":["../../../src/comradecircle/components/Avatar.tsx"],"sourcesContent":["/**\n * Avatar — initials-based, token-only avatar with an optional presence dot.\n * @module comradecircle/components\n *\n * File-backed avatars are not rendered here: in this subgraph `File` resolves\n * `id` only (no URL), so we render deterministic initials on the brand\n * accent-soft tint. Clean, theme-aware, and never a broken image.\n */\n\nimport type { FC } from 'react';\nimport { cn } from '../utils/cn';\nimport { FileImage } from './FileImage';\nimport type { ComPresence } from '../types';\n\ntype AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\nconst sizeClass: Record<AvatarSize, string> = {\n xs: 'h-6 w-6 text-[0.6rem]',\n sm: 'h-8 w-8 text-xs',\n md: 'h-10 w-10 text-sm',\n lg: 'h-14 w-14 text-lg',\n xl: 'h-20 w-20 text-2xl',\n};\n\nconst presenceDot: Record<ComPresence, string> = {\n ONLINE: 'bg-status-success-text',\n AWAY: 'bg-status-warning-text',\n BUSY: 'bg-status-error-text',\n OFFLINE: 'bg-text-tertiary',\n};\n\nexport function initialsOf(name: string | null | undefined): string {\n if (!name) return '?';\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '?';\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]![0]! + parts[parts.length - 1]![0]!).toUpperCase();\n}\n\ninterface AvatarProps {\n name?: string | null;\n size?: AvatarSize;\n presence?: ComPresence | null;\n /** wspace-files-svc file id — when set + resolvable, renders the image. */\n fileId?: string | null;\n className?: string;\n}\n\nexport const Avatar: FC<AvatarProps> = ({ name, size = 'md', presence, fileId, className }) => {\n const initials = (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'flex h-full w-full items-center justify-center rounded-full bg-[var(--color-accent-soft)] font-semibold text-text-primary',\n sizeClass[size]\n )}\n >\n {initialsOf(name)}\n </span>\n );\n\n return (\n <span className={cn('relative inline-flex shrink-0', className)}>\n <span\n className={cn(\n 'inline-flex items-center justify-center overflow-hidden rounded-full ring-1 ring-border-seam',\n sizeClass[size]\n )}\n >\n {fileId ? (\n <FileImage\n fileId={fileId}\n alt={name ?? 'Avatar'}\n className=\"h-full w-full rounded-full\"\n fallback={initials}\n />\n ) : (\n initials\n )}\n </span>\n {presence && (\n <span\n className={cn(\n 'absolute -bottom-0.5 -right-0.5 h-2.5 w-2.5 rounded-full ring-2 ring-bg-surface',\n presenceDot[presence],\n presence === 'ONLINE' && 'motion-safe:animate-vc-glow-pulse'\n )}\n aria-label={`Presence: ${presence.toLowerCase()}`}\n />\n )}\n </span>\n );\n};\n"],"mappings":";;;;AAgBA,IAAM,IAAwC;CAC5C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL,EAEK,IAA2C;CAC/C,QAAQ;CACR,MAAM;CACN,MAAM;CACN,SAAS;CACV;AAED,SAAgB,EAAW,GAAyC;AAClE,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAQ,EAAK,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,QAAQ;AAGtD,QAFI,EAAM,WAAW,IAAU,MAC3B,EAAM,WAAW,IAAU,EAAM,GAAI,MAAM,GAAG,EAAE,CAAC,aAAa,IAC1D,EAAM,GAAI,KAAM,EAAM,EAAM,SAAS,GAAI,IAAK,aAAa;;AAYrE,IAAa,KAA2B,EAAE,SAAM,UAAO,MAAM,aAAU,WAAQ,mBAAgB;CAC7F,IAAM,IACJ,kBAAC,QAAD;EACE,eAAY;EACZ,WAAW,EACT,6HACA,EAAU,GACX;YAEA,EAAW,EAAK;EACZ,CAAA;AAGT,QACE,kBAAC,QAAD;EAAM,WAAW,EAAG,iCAAiC,EAAU;YAA/D,CACE,kBAAC,QAAD;GACE,WAAW,EACT,gGACA,EAAU,GACX;aAEA,IACC,kBAAC,GAAD;IACU;IACR,KAAK,KAAQ;IACb,WAAU;IACV,UAAU;IACV,CAAA,GAEF;GAEG,CAAA,EACN,KACC,kBAAC,QAAD;GACE,WAAW,EACT,mFACA,EAAY,IACZ,MAAa,YAAY,oCAC1B;GACD,cAAY,aAAa,EAAS,aAAa;GAC/C,CAAA,CAEC"}
1
+ {"version":3,"file":"Avatar.js","names":[],"sources":["../../../src/comradecircle/components/Avatar.tsx"],"sourcesContent":["/**\n * Avatar — initials or image avatar with a deterministic tint + presence dot.\n * @module comradecircle/components\n *\n * When a wspace-files-svc `fileId` is present the image is rendered; otherwise\n * we show initials on a per-identity tint (hashed from the name) so grids of\n * avatars read as distinct, colourful, and alive — while staying theme-aware\n * (each tint ships a light + dark text pairing). An optional presence dot and a\n * soft accent ring round out the treatment.\n */\n\nimport type { FC } from 'react';\nimport { cn } from '../utils/cn';\nimport { FileImage } from './FileImage';\nimport type { ComPresence } from '../types';\n\ntype AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nconst sizeClass: Record<AvatarSize, string> = {\n xs: 'h-6 w-6 text-[0.6rem]',\n sm: 'h-8 w-8 text-xs',\n md: 'h-10 w-10 text-sm',\n lg: 'h-14 w-14 text-lg',\n xl: 'h-20 w-20 text-2xl',\n '2xl': 'h-28 w-28 text-4xl',\n};\n\nconst presenceDot: Record<ComPresence, string> = {\n ONLINE: 'bg-status-success-text',\n AWAY: 'bg-status-warning-text',\n BUSY: 'bg-status-error-text',\n OFFLINE: 'bg-text-tertiary',\n};\n\nconst presenceDotSize: Record<AvatarSize, string> = {\n xs: 'h-2 w-2',\n sm: 'h-2.5 w-2.5',\n md: 'h-2.5 w-2.5',\n lg: 'h-3 w-3',\n xl: 'h-3.5 w-3.5',\n '2xl': 'h-4 w-4',\n};\n\n// Decorative per-identity tints. Not semantic status — just a stable, colourful\n// identity cue. Each ships a dark-mode and light-mode readable text pairing so\n// initials stay legible under either theme.\nconst TINTS = [\n 'bg-blue-500/20 text-blue-700 dark:text-blue-200',\n 'bg-violet-500/20 text-violet-700 dark:text-violet-200',\n 'bg-emerald-500/20 text-emerald-700 dark:text-emerald-200',\n 'bg-amber-500/20 text-amber-700 dark:text-amber-100',\n 'bg-rose-500/20 text-rose-700 dark:text-rose-200',\n 'bg-cyan-500/20 text-cyan-700 dark:text-cyan-200',\n 'bg-fuchsia-500/20 text-fuchsia-700 dark:text-fuchsia-200',\n 'bg-teal-500/20 text-teal-700 dark:text-teal-200',\n] as const;\n\nfunction tintFor(seed: string | null | undefined): string {\n if (!seed) return TINTS[0]!;\n let hash = 0;\n for (let i = 0; i < seed.length; i += 1) hash = (hash * 31 + seed.charCodeAt(i)) | 0;\n return TINTS[Math.abs(hash) % TINTS.length]!;\n}\n\nexport function initialsOf(name: string | null | undefined): string {\n if (!name) return '?';\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '?';\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]![0]! + parts[parts.length - 1]![0]!).toUpperCase();\n}\n\ninterface AvatarProps {\n name?: string | null;\n size?: AvatarSize;\n presence?: ComPresence | null;\n /** wspace-files-svc file id — when set + resolvable, renders the image. */\n fileId?: string | null;\n /** Stable seed for the tint (defaults to name). Pass an id for stability. */\n seed?: string | null;\n /** Add a soft accent ring + glow (hero/profile treatment). */\n ring?: boolean;\n className?: string;\n}\n\nexport const Avatar: FC<AvatarProps> = ({\n name,\n size = 'md',\n presence,\n fileId,\n seed,\n ring = false,\n className,\n}) => {\n const initials = (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'flex h-full w-full items-center justify-center rounded-full font-semibold',\n tintFor(seed ?? name),\n sizeClass[size]\n )}\n >\n {initialsOf(name)}\n </span>\n );\n\n return (\n <span className={cn('relative inline-flex shrink-0', className)}>\n <span\n className={cn(\n 'inline-flex items-center justify-center overflow-hidden rounded-full',\n ring\n ? 'ring-2 ring-[var(--color-accent-soft)] shadow-[0_0_0_4px_var(--color-accent-soft)]'\n : 'ring-1 ring-border-seam',\n sizeClass[size]\n )}\n >\n {fileId ? (\n <FileImage\n fileId={fileId}\n alt={name ?? 'Avatar'}\n className=\"h-full w-full rounded-full\"\n fallback={initials}\n />\n ) : (\n initials\n )}\n </span>\n {presence && (\n <span\n className={cn(\n 'absolute -bottom-0.5 -right-0.5 rounded-full ring-2 ring-bg-surface',\n presenceDotSize[size],\n presenceDot[presence],\n presence === 'ONLINE' && 'motion-safe:animate-vc-glow-pulse'\n )}\n aria-label={`Presence: ${presence.toLowerCase()}`}\n />\n )}\n </span>\n );\n};\n"],"mappings":";;;;AAkBA,IAAM,IAAwC;CAC5C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACR,EAEK,IAA2C;CAC/C,QAAQ;CACR,MAAM;CACN,MAAM;CACN,SAAS;CACV,EAEK,IAA8C;CAClD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACR,EAKK,IAAQ;CACZ;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,EAAQ,GAAyC;AACxD,KAAI,CAAC,EAAM,QAAO,EAAM;CACxB,IAAI,IAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,EAAK,QAAQ,KAAK,EAAG,KAAQ,IAAO,KAAK,EAAK,WAAW,EAAE,GAAI;AACnF,QAAO,EAAM,KAAK,IAAI,EAAK,GAAG,EAAM;;AAGtC,SAAgB,EAAW,GAAyC;AAClE,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAQ,EAAK,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,QAAQ;AAGtD,QAFI,EAAM,WAAW,IAAU,MAC3B,EAAM,WAAW,IAAU,EAAM,GAAI,MAAM,GAAG,EAAE,CAAC,aAAa,IAC1D,EAAM,GAAI,KAAM,EAAM,EAAM,SAAS,GAAI,IAAK,aAAa;;AAgBrE,IAAa,KAA2B,EACtC,SACA,UAAO,MACP,aACA,WACA,SACA,UAAO,IACP,mBACI;CACJ,IAAM,IACJ,kBAAC,QAAD;EACE,eAAY;EACZ,WAAW,EACT,6EACA,EAAQ,KAAQ,EAAK,EACrB,EAAU,GACX;YAEA,EAAW,EAAK;EACZ,CAAA;AAGT,QACE,kBAAC,QAAD;EAAM,WAAW,EAAG,iCAAiC,EAAU;YAA/D,CACE,kBAAC,QAAD;GACE,WAAW,EACT,wEACA,IACI,uFACA,2BACJ,EAAU,GACX;aAEA,IACC,kBAAC,GAAD;IACU;IACR,KAAK,KAAQ;IACb,WAAU;IACV,UAAU;IACV,CAAA,GAEF;GAEG,CAAA,EACN,KACC,kBAAC,QAAD;GACE,WAAW,EACT,uEACA,EAAgB,IAChB,EAAY,IACZ,MAAa,YAAY,oCAC1B;GACD,cAAY,aAAa,EAAS,aAAa;GAC/C,CAAA,CAEC"}
@@ -11,20 +11,31 @@ function m({ fileId: m, name: h, onUploaded: g, label: _ = "Upload avatar", disa
11
11
  let y = s(null), b = o(), [x, S] = c(!1), [C, w] = c(null);
12
12
  return /* @__PURE__ */ f("div", {
13
13
  className: "flex items-center gap-4",
14
- children: [/* @__PURE__ */ f("div", {
15
- className: "relative",
16
- children: [C ? /* @__PURE__ */ d("img", {
17
- src: C,
18
- alt: h ?? "Avatar",
19
- className: "h-16 w-16 rounded-full object-cover ring-1 ring-border-seam"
20
- }) : /* @__PURE__ */ d(r, {
21
- name: h,
22
- fileId: m,
23
- size: "lg"
24
- }), x && /* @__PURE__ */ d("span", {
25
- className: "absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/60",
26
- children: /* @__PURE__ */ d(u, { className: "h-5 w-5 animate-spin text-text-primary" })
27
- })]
14
+ children: [/* @__PURE__ */ f("button", {
15
+ type: "button",
16
+ onClick: () => y.current?.click(),
17
+ disabled: v || x,
18
+ "aria-label": _,
19
+ className: "group relative h-16 w-16 shrink-0 rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:cursor-not-allowed",
20
+ children: [
21
+ C ? /* @__PURE__ */ d("img", {
22
+ src: C,
23
+ alt: h ?? "Avatar",
24
+ className: "h-16 w-16 rounded-full object-cover ring-1 ring-border-seam"
25
+ }) : /* @__PURE__ */ d(r, {
26
+ name: h,
27
+ fileId: m,
28
+ size: "lg"
29
+ }),
30
+ /* @__PURE__ */ d("span", {
31
+ className: "absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/60 opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100",
32
+ children: /* @__PURE__ */ d(l, { className: "h-5 w-5 text-text-primary" })
33
+ }),
34
+ x && /* @__PURE__ */ d("span", {
35
+ className: "absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/70",
36
+ children: /* @__PURE__ */ d(u, { className: "h-5 w-5 animate-spin text-text-primary" })
37
+ })
38
+ ]
28
39
  }), /* @__PURE__ */ f("div", { children: [
29
40
  /* @__PURE__ */ d("input", {
30
41
  ref: y,
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarUploadField.js","names":[],"sources":["../../../src/comradecircle/components/AvatarUploadField.tsx"],"sourcesContent":["/**\n * AvatarUploadField — upload a circular avatar image to wspace-files-svc.\n * @module comradecircle/components\n *\n * On select, uploads via {@link uploadImageToFiles} and hands the resulting file\n * id back to the caller (persisted as avatarFileId). Shows the current avatar\n * (by file id) or the freshly-uploaded preview; falls back to initials.\n */\n\nimport { useId, useRef, useState } from 'react';\nimport { Camera, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\nimport { Avatar } from './Avatar';\nimport { uploadImageToFiles, FILE_FOLDERS } from '../hooks/useFiles';\nimport { notify, describeError } from '../lib/toast';\n\ninterface AvatarUploadFieldProps {\n /** Current avatar file id (persisted). */\n fileId?: string | null;\n /** Name used for the initials fallback. */\n name?: string | null;\n /** Called with the new file id after a successful upload. */\n onUploaded: (fileId: string) => void;\n label?: string;\n disabled?: boolean;\n}\n\nconst MAX_BYTES = 5 * 1024 * 1024;\n\nexport function AvatarUploadField({\n fileId,\n name,\n onUploaded,\n label = 'Upload avatar',\n disabled = false,\n}: AvatarUploadFieldProps) {\n const inputRef = useRef<HTMLInputElement | null>(null);\n const inputId = useId();\n const [uploading, setUploading] = useState(false);\n const [previewUrl, setPreviewUrl] = useState<string | null>(null);\n\n const handleSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n event.target.value = '';\n if (!file) return;\n if (!file.type.startsWith('image/')) {\n notify.error('Unsupported file', 'Please choose an image.');\n return;\n }\n if (file.size > MAX_BYTES) {\n notify.error('Image too large', 'Please choose an image under 5 MB.');\n return;\n }\n setUploading(true);\n try {\n const uploaded = await uploadImageToFiles(file, FILE_FOLDERS.avatars);\n setPreviewUrl(uploaded.url);\n onUploaded(uploaded.id);\n notify.success('Avatar updated');\n } catch (err) {\n notify.error('Upload failed', describeError(err));\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div className=\"flex items-center gap-4\">\n <div className=\"relative\">\n {previewUrl ? (\n <img\n src={previewUrl}\n alt={name ?? 'Avatar'}\n className=\"h-16 w-16 rounded-full object-cover ring-1 ring-border-seam\"\n />\n ) : (\n <Avatar name={name} fileId={fileId} size=\"lg\" />\n )}\n {uploading && (\n <span className=\"absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/60\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-primary\" />\n </span>\n )}\n </div>\n <div>\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n accept=\"image/*\"\n className=\"sr-only\"\n onChange={handleSelect}\n disabled={disabled || uploading}\n />\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n disabled={disabled || uploading}\n className={cn(\n 'inline-flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken',\n (disabled || uploading) && 'cursor-not-allowed opacity-60'\n )}\n >\n <Camera className=\"h-4 w-4\" /> {label}\n </button>\n <p className=\"mt-1 text-xs text-text-tertiary\">PNG or JPG, up to 5 MB.</p>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA2BA,IAAM,IAAY,IAAI,OAAO;AAE7B,SAAgB,EAAkB,EAChC,WACA,SACA,eACA,WAAQ,iBACR,cAAW,MACc;CACzB,IAAM,IAAW,EAAgC,KAAK,EAChD,IAAU,GAAO,EACjB,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAY,KAAiB,EAAwB,KAAK;AA2BjE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,IACC,kBAAC,OAAD;IACE,KAAK;IACL,KAAK,KAAQ;IACb,WAAU;IACV,CAAA,GAEF,kBAAC,GAAD;IAAc;IAAc;IAAQ,MAAK;IAAO,CAAA,EAEjD,KACC,kBAAC,QAAD;IAAM,WAAU;cACd,kBAAC,GAAD,EAAS,WAAU,0CAA2C,CAAA;IACzD,CAAA,CAEL;MACN,kBAAC,OAAD,EAAA,UAAA;GACE,kBAAC,SAAD;IACE,KAAK;IACL,IAAI;IACJ,MAAK;IACL,QAAO;IACP,WAAU;IACV,UAlDa,OAAO,MAA+C;KACzE,IAAM,IAAO,EAAM,OAAO,QAAQ;AAClC,WAAM,OAAO,QAAQ,IAChB,GACL;UAAI,CAAC,EAAK,KAAK,WAAW,SAAS,EAAE;AACnC,SAAO,MAAM,oBAAoB,0BAA0B;AAC3D;;AAEF,UAAI,EAAK,OAAO,GAAW;AACzB,SAAO,MAAM,mBAAmB,qCAAqC;AACrE;;AAEF,QAAa,GAAK;AAClB,UAAI;OACF,IAAM,IAAW,MAAM,EAAmB,GAAM,EAAa,QAAQ;AAGrE,OAFA,EAAc,EAAS,IAAI,EAC3B,EAAW,EAAS,GAAG,EACvB,EAAO,QAAQ,iBAAiB;eACzB,GAAK;AACZ,SAAO,MAAM,iBAAiB,EAAc,EAAI,CAAC;gBACzC;AACR,SAAa,GAAM;;;;IA8Bf,UAAU,KAAY;IACtB,CAAA;GACF,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,SAAS,OAAO;IACxC,UAAU,KAAY;IACtB,WAAW,EACT,6KACC,KAAY,MAAc,gCAC5B;cAPH;KASE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;;KAAE;KACzB;;GACT,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAA2B,CAAA;GACtE,EAAA,CAAA,CACF"}
1
+ {"version":3,"file":"AvatarUploadField.js","names":[],"sources":["../../../src/comradecircle/components/AvatarUploadField.tsx"],"sourcesContent":["/**\n * AvatarUploadField — upload a circular avatar image to wspace-files-svc.\n * @module comradecircle/components\n *\n * On select, uploads via {@link uploadImageToFiles} and hands the resulting file\n * id back to the caller (persisted as avatarFileId). Shows the current avatar\n * (by file id) or the freshly-uploaded preview; falls back to initials.\n */\n\nimport { useId, useRef, useState } from 'react';\nimport { Camera, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\nimport { Avatar } from './Avatar';\nimport { uploadImageToFiles, FILE_FOLDERS } from '../hooks/useFiles';\nimport { notify, describeError } from '../lib/toast';\n\ninterface AvatarUploadFieldProps {\n /** Current avatar file id (persisted). */\n fileId?: string | null;\n /** Name used for the initials fallback. */\n name?: string | null;\n /** Called with the new file id after a successful upload. */\n onUploaded: (fileId: string) => void;\n label?: string;\n disabled?: boolean;\n}\n\nconst MAX_BYTES = 5 * 1024 * 1024;\n\nexport function AvatarUploadField({\n fileId,\n name,\n onUploaded,\n label = 'Upload avatar',\n disabled = false,\n}: AvatarUploadFieldProps) {\n const inputRef = useRef<HTMLInputElement | null>(null);\n const inputId = useId();\n const [uploading, setUploading] = useState(false);\n const [previewUrl, setPreviewUrl] = useState<string | null>(null);\n\n const handleSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n event.target.value = '';\n if (!file) return;\n if (!file.type.startsWith('image/')) {\n notify.error('Unsupported file', 'Please choose an image.');\n return;\n }\n if (file.size > MAX_BYTES) {\n notify.error('Image too large', 'Please choose an image under 5 MB.');\n return;\n }\n setUploading(true);\n try {\n const uploaded = await uploadImageToFiles(file, FILE_FOLDERS.avatars);\n setPreviewUrl(uploaded.url);\n onUploaded(uploaded.id);\n notify.success('Avatar updated');\n } catch (err) {\n notify.error('Upload failed', describeError(err));\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n disabled={disabled || uploading}\n aria-label={label}\n className=\"group relative h-16 w-16 shrink-0 rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:cursor-not-allowed\"\n >\n {previewUrl ? (\n <img\n src={previewUrl}\n alt={name ?? 'Avatar'}\n className=\"h-16 w-16 rounded-full object-cover ring-1 ring-border-seam\"\n />\n ) : (\n <Avatar name={name} fileId={fileId} size=\"lg\" />\n )}\n {/* Hover overlay: click the image directly to change it. */}\n <span className=\"absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/60 opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100\">\n <Camera className=\"h-5 w-5 text-text-primary\" />\n </span>\n {uploading && (\n <span className=\"absolute inset-0 flex items-center justify-center rounded-full bg-bg-canvas/70\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-primary\" />\n </span>\n )}\n </button>\n <div>\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n accept=\"image/*\"\n className=\"sr-only\"\n onChange={handleSelect}\n disabled={disabled || uploading}\n />\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n disabled={disabled || uploading}\n className={cn(\n 'inline-flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken',\n (disabled || uploading) && 'cursor-not-allowed opacity-60'\n )}\n >\n <Camera className=\"h-4 w-4\" /> {label}\n </button>\n <p className=\"mt-1 text-xs text-text-tertiary\">PNG or JPG, up to 5 MB.</p>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA2BA,IAAM,IAAY,IAAI,OAAO;AAE7B,SAAgB,EAAkB,EAChC,WACA,SACA,eACA,WAAQ,iBACR,cAAW,MACc;CACzB,IAAM,IAAW,EAAgC,KAAK,EAChD,IAAU,GAAO,EACjB,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAY,KAAiB,EAAwB,KAAK;AA2BjE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAS,SAAS,OAAO;GACxC,UAAU,KAAY;GACtB,cAAY;GACZ,WAAU;aALZ;IAOG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK,KAAQ;KACb,WAAU;KACV,CAAA,GAEF,kBAAC,GAAD;KAAc;KAAc;KAAQ,MAAK;KAAO,CAAA;IAGlD,kBAAC,QAAD;KAAM,WAAU;eACd,kBAAC,GAAD,EAAQ,WAAU,6BAA8B,CAAA;KAC3C,CAAA;IACN,KACC,kBAAC,QAAD;KAAM,WAAU;eACd,kBAAC,GAAD,EAAS,WAAU,0CAA2C,CAAA;KACzD,CAAA;IAEF;MACT,kBAAC,OAAD,EAAA,UAAA;GACE,kBAAC,SAAD;IACE,KAAK;IACL,IAAI;IACJ,MAAK;IACL,QAAO;IACP,WAAU;IACV,UA5Da,OAAO,MAA+C;KACzE,IAAM,IAAO,EAAM,OAAO,QAAQ;AAClC,WAAM,OAAO,QAAQ,IAChB,GACL;UAAI,CAAC,EAAK,KAAK,WAAW,SAAS,EAAE;AACnC,SAAO,MAAM,oBAAoB,0BAA0B;AAC3D;;AAEF,UAAI,EAAK,OAAO,GAAW;AACzB,SAAO,MAAM,mBAAmB,qCAAqC;AACrE;;AAEF,QAAa,GAAK;AAClB,UAAI;OACF,IAAM,IAAW,MAAM,EAAmB,GAAM,EAAa,QAAQ;AAGrE,OAFA,EAAc,EAAS,IAAI,EAC3B,EAAW,EAAS,GAAG,EACvB,EAAO,QAAQ,iBAAiB;eACzB,GAAK;AACZ,SAAO,MAAM,iBAAiB,EAAc,EAAI,CAAC;gBACzC;AACR,SAAa,GAAM;;;;IAwCf,UAAU,KAAY;IACtB,CAAA;GACF,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,SAAS,OAAO;IACxC,UAAU,KAAY;IACtB,WAAW,EACT,6KACC,KAAY,MAAc,gCAC5B;cAPH;KASE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;;KAAE;KACzB;;GACT,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAA2B,CAAA;GACtE,EAAA,CAAA,CACF"}
@@ -1,111 +1,113 @@
1
- import { useComJoinCircle as e, useComLeaveCircle as t } from "../hooks/useComradeCircleApi.js";
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useComJoinCircle as t, useComLeaveCircle as n } from "../hooks/useComradeCircleApi.js";
2
3
  import "../hooks/index.js";
3
- import { Avatar as n } from "./Avatar.js";
4
- import { describeError as r, notify as i } from "../lib/toast.js";
5
- import { formatEnumLabel as a, formatNumber as o } from "../utils/formatters.js";
6
- import { StatusBadge as s } from "./StatusBadge.js";
7
- import { Hash as c, Users as l } from "lucide-react";
8
- import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
9
- import { Button as p, GlassCard as m } from "@burdenoff/fe-libs/ui";
4
+ import { Avatar as r } from "./Avatar.js";
5
+ import { describeError as i, notify as a } from "../lib/toast.js";
6
+ import { formatEnumLabel as o, formatNumber as s } from "../utils/formatters.js";
7
+ import { StatusBadge as c } from "./StatusBadge.js";
8
+ import { Hash as l, Users as u } from "lucide-react";
9
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { Button as m, GlassCard as h } from "@burdenoff/fe-libs/ui";
10
11
  //#region src/comradecircle/components/CircleCard.tsx
11
- function h({ circle: h, membership: g, onOpen: _ }) {
12
- let v = e(), y = t(), b = g?.status === "ACTIVE";
13
- return /* @__PURE__ */ f(m, {
12
+ function g({ circle: g, membership: _, onOpen: v }) {
13
+ let y = t(), b = n(), x = _?.status === "ACTIVE";
14
+ return /* @__PURE__ */ p(h, {
14
15
  treatment: "plain",
15
- className: "flex flex-col p-5",
16
+ className: e("flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]", (_?.role === "OWNER" || _?.role === "ADMIN") && "ring-1 ring-[var(--color-accent-soft)]"),
16
17
  children: [
17
- /* @__PURE__ */ f("div", {
18
+ /* @__PURE__ */ p("div", {
18
19
  className: "flex items-start gap-3",
19
- children: [/* @__PURE__ */ d(n, {
20
- name: h.name,
20
+ children: [/* @__PURE__ */ f(r, {
21
+ name: g.name,
22
+ seed: g.id,
21
23
  size: "lg",
22
- fileId: h.avatar?.id
23
- }), /* @__PURE__ */ f("div", {
24
+ fileId: g.avatar?.id
25
+ }), /* @__PURE__ */ p("div", {
24
26
  className: "min-w-0 flex-1",
25
27
  children: [
26
- /* @__PURE__ */ d("button", {
28
+ /* @__PURE__ */ f("button", {
27
29
  type: "button",
28
- onClick: _,
30
+ onClick: v,
29
31
  className: "truncate text-left text-base font-semibold text-text-primary hover:underline",
30
- children: h.name
32
+ children: g.name
31
33
  }),
32
- h.topic && /* @__PURE__ */ d("p", {
34
+ g.topic && /* @__PURE__ */ f("p", {
33
35
  className: "truncate text-sm text-text-secondary",
34
- children: h.topic
36
+ children: g.topic
35
37
  }),
36
- /* @__PURE__ */ f("div", {
38
+ /* @__PURE__ */ p("div", {
37
39
  className: "mt-1.5 flex flex-wrap items-center gap-1.5",
38
40
  children: [
39
- /* @__PURE__ */ d(s, { status: h.visibility }),
40
- h.status !== "ACTIVE" && /* @__PURE__ */ d(s, { status: h.status }),
41
- b && g && /* @__PURE__ */ d(s, { status: g.role })
41
+ /* @__PURE__ */ f(c, { status: g.visibility }),
42
+ g.status !== "ACTIVE" && /* @__PURE__ */ f(c, { status: g.status }),
43
+ x && _ && /* @__PURE__ */ f(c, { status: _.role })
42
44
  ]
43
45
  })
44
46
  ]
45
47
  })]
46
48
  }),
47
- h.description && /* @__PURE__ */ d("p", {
49
+ g.description && /* @__PURE__ */ f("p", {
48
50
  className: "mt-3 line-clamp-2 text-sm text-text-secondary",
49
- children: h.description
51
+ children: g.description
50
52
  }),
51
- h.tags.length > 0 && /* @__PURE__ */ d("div", {
53
+ g.tags.length > 0 && /* @__PURE__ */ f("div", {
52
54
  className: "mt-3 flex flex-wrap gap-1.5",
53
- children: h.tags.slice(0, 4).map((e) => /* @__PURE__ */ f("span", {
55
+ children: g.tags.slice(0, 4).map((e) => /* @__PURE__ */ p("span", {
54
56
  className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
55
57
  children: ["#", e]
56
58
  }, e))
57
59
  }),
58
- /* @__PURE__ */ f("div", {
60
+ /* @__PURE__ */ p("div", {
59
61
  className: "mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3",
60
- children: [/* @__PURE__ */ f("div", {
62
+ children: [/* @__PURE__ */ p("div", {
61
63
  className: "flex items-center gap-3 text-xs text-text-tertiary",
62
- children: [/* @__PURE__ */ f("span", {
64
+ children: [/* @__PURE__ */ p("span", {
63
65
  className: "inline-flex items-center gap-1",
64
66
  children: [
65
- /* @__PURE__ */ d(l, { className: "h-3.5 w-3.5" }),
67
+ /* @__PURE__ */ f(u, { className: "h-3.5 w-3.5" }),
66
68
  " ",
67
- o(h.memberCount)
69
+ s(g.memberCount)
68
70
  ]
69
- }), /* @__PURE__ */ f("span", {
71
+ }), /* @__PURE__ */ p("span", {
70
72
  className: "inline-flex items-center gap-1",
71
73
  children: [
72
- /* @__PURE__ */ d(c, { className: "h-3.5 w-3.5" }),
74
+ /* @__PURE__ */ f(l, { className: "h-3.5 w-3.5" }),
73
75
  " ",
74
- h.channels?.length ?? 0
76
+ g.channels?.length ?? 0
75
77
  ]
76
78
  })]
77
- }), /* @__PURE__ */ d("div", {
79
+ }), /* @__PURE__ */ f("div", {
78
80
  className: "flex items-center gap-2",
79
- children: b ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(p, {
81
+ children: x ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(m, {
80
82
  size: "sm",
81
83
  variant: "outline",
82
84
  onClick: async () => {
83
85
  try {
84
- await y.mutateAsync(h.id), i.success("Left circle", `You have left ${h.name}.`);
86
+ await b.mutateAsync(g.id), a.success("Left circle", `You have left ${g.name}.`);
85
87
  } catch (e) {
86
- i.error("Could not leave", r(e));
88
+ a.error("Could not leave", i(e));
87
89
  }
88
90
  },
89
- disabled: y.isPending,
91
+ disabled: b.isPending,
90
92
  children: "Leave"
91
- }), /* @__PURE__ */ d(p, {
93
+ }), /* @__PURE__ */ f(m, {
92
94
  size: "sm",
93
- onClick: _,
95
+ onClick: v,
94
96
  children: "Open"
95
- })] }) : h.status === "ACTIVE" ? /* @__PURE__ */ d(p, {
97
+ })] }) : g.status === "ACTIVE" ? /* @__PURE__ */ f(m, {
96
98
  size: "sm",
97
99
  onClick: async () => {
98
100
  try {
99
- await v.mutateAsync(h.id), i.success("Joined circle", `You are now a member of ${h.name}.`);
101
+ await y.mutateAsync(g.id), a.success("Joined circle", `You are now a member of ${g.name}.`);
100
102
  } catch (e) {
101
- i.error("Could not join", r(e));
103
+ a.error("Could not join", i(e));
102
104
  }
103
105
  },
104
- disabled: v.isPending,
106
+ disabled: y.isPending,
105
107
  children: "Join"
106
- }) : /* @__PURE__ */ d("span", {
108
+ }) : /* @__PURE__ */ f("span", {
107
109
  className: "text-xs text-text-tertiary",
108
- children: a(h.status)
110
+ children: o(g.status)
109
111
  })
110
112
  })]
111
113
  })
@@ -113,6 +115,6 @@ function h({ circle: h, membership: g, onOpen: _ }) {
113
115
  });
114
116
  }
115
117
  //#endregion
116
- export { h as CircleCard };
118
+ export { g as CircleCard };
117
119
 
118
120
  //# sourceMappingURL=CircleCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CircleCard.js","names":[],"sources":["../../../src/comradecircle/components/CircleCard.tsx"],"sourcesContent":["/**\n * CircleCard — a discovery/overview tile for a community circle.\n * @module comradecircle/components\n *\n * Writes `comJoinCircle` / `comLeaveCircle`. When the caller already belongs to\n * the circle the card shows their role and an Open action; otherwise a Join\n * action. Membership is supplied by the parent (which knows the caller's set).\n */\n\nimport { Users, Hash } from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { Avatar } from './Avatar';\nimport { StatusBadge } from './StatusBadge';\nimport { useComJoinCircle, useComLeaveCircle } from '../hooks';\nimport { formatEnumLabel, formatNumber } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComCircle, ComMembership } from '../types';\n\ninterface CircleCardProps {\n circle: ComCircle;\n membership?: ComMembership | null;\n onOpen: () => void;\n}\n\nexport function CircleCard({ circle, membership, onOpen }: CircleCardProps) {\n const join = useComJoinCircle();\n const leave = useComLeaveCircle();\n const isMember = membership?.status === 'ACTIVE';\n\n const handleJoin = async () => {\n try {\n await join.mutateAsync(circle.id);\n notify.success('Joined circle', `You are now a member of ${circle.name}.`);\n } catch (err) {\n notify.error('Could not join', describeError(err));\n }\n };\n\n const handleLeave = async () => {\n try {\n await leave.mutateAsync(circle.id);\n notify.success('Left circle', `You have left ${circle.name}.`);\n } catch (err) {\n notify.error('Could not leave', describeError(err));\n }\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start gap-3\">\n <Avatar name={circle.name} size=\"lg\" fileId={circle.avatar?.id} />\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"truncate text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {circle.name}\n </button>\n {circle.topic && <p className=\"truncate text-sm text-text-secondary\">{circle.topic}</p>}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={circle.visibility} />\n {circle.status !== 'ACTIVE' && <StatusBadge status={circle.status} />}\n {isMember && membership && <StatusBadge status={membership.role} />}\n </div>\n </div>\n </div>\n\n {circle.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{circle.description}</p>\n )}\n\n {circle.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {circle.tags.slice(0, 4).map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3\">\n <div className=\"flex items-center gap-3 text-xs text-text-tertiary\">\n <span className=\"inline-flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" /> {formatNumber(circle.memberCount)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Hash className=\"h-3.5 w-3.5\" /> {circle.channels?.length ?? 0}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n {isMember ? (\n <>\n <Button size=\"sm\" variant=\"outline\" onClick={handleLeave} disabled={leave.isPending}>\n Leave\n </Button>\n <Button size=\"sm\" onClick={onOpen}>\n Open\n </Button>\n </>\n ) : circle.status === 'ACTIVE' ? (\n <Button size=\"sm\" onClick={handleJoin} disabled={join.isPending}>\n Join\n </Button>\n ) : (\n <span className=\"text-xs text-text-tertiary\">{formatEnumLabel(circle.status)}</span>\n )}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,EAAW,EAAE,WAAQ,eAAY,aAA2B;CAC1E,IAAM,IAAO,GAAkB,EACzB,IAAQ,GAAmB,EAC3B,IAAW,GAAY,WAAW;AAoBxC,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,MAAM,EAAO;KAAM,MAAK;KAAK,QAAQ,EAAO,QAAQ;KAAM,CAAA,EAClE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAET,EAAO;OACD,CAAA;MACR,EAAO,SAAS,kBAAC,KAAD;OAAG,WAAU;iBAAwC,EAAO;OAAU,CAAA;MACvF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAa,QAAQ,EAAO,YAAc,CAAA;QACzC,EAAO,WAAW,YAAY,kBAAC,GAAD,EAAa,QAAQ,EAAO,QAAU,CAAA;QACpE,KAAY,KAAc,kBAAC,GAAD,EAAa,QAAQ,EAAW,MAAQ,CAAA;QAC/D;;MACF;OACF;;GAEL,EAAO,eACN,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAO;IAAgB,CAAA;GAGtF,EAAO,KAAK,SAAS,KACpB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC5B,kBAAC,QAAD;KAEE,WAAU;eAFZ,CAGC,KACG,EACG;OAJA,EAIA,CACP;IACE,CAAA;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;OAAE,EAAa,EAAO,YAAY;OAC9D;SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA;;OAAE,EAAO,UAAU,UAAU;OACxD;QACH;QACN,kBAAC,OAAD;KAAK,WAAU;eACZ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAU,SA3D5B,YAAY;AAC9B,WAAI;AAEF,QADA,MAAM,EAAM,YAAY,EAAO,GAAG,EAClC,EAAO,QAAQ,eAAe,iBAAiB,EAAO,KAAK,GAAG;gBACvD,GAAK;AACZ,UAAO,MAAM,mBAAmB,EAAc,EAAI,CAAC;;;MAsDe,UAAU,EAAM;gBAAW;MAE5E,CAAA,EACT,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAS;gBAAQ;MAE1B,CAAA,CACR,EAAA,CAAA,GACD,EAAO,WAAW,WACpB,kBAAC,GAAD;MAAQ,MAAK;MAAK,SA5ET,YAAY;AAC7B,WAAI;AAEF,QADA,MAAM,EAAK,YAAY,EAAO,GAAG,EACjC,EAAO,QAAQ,iBAAiB,2BAA2B,EAAO,KAAK,GAAG;gBACnE,GAAK;AACZ,UAAO,MAAM,kBAAkB,EAAc,EAAI,CAAC;;;MAuEL,UAAU,EAAK;gBAAW;MAExD,CAAA,GAET,kBAAC,QAAD;MAAM,WAAU;gBAA8B,EAAgB,EAAO,OAAO;MAAQ,CAAA;KAElF,CAAA,CACF;;GACI"}
1
+ {"version":3,"file":"CircleCard.js","names":[],"sources":["../../../src/comradecircle/components/CircleCard.tsx"],"sourcesContent":["/**\n * CircleCard — a discovery/overview tile for a community circle.\n * @module comradecircle/components\n *\n * Writes `comJoinCircle` / `comLeaveCircle`. When the caller already belongs to\n * the circle the card shows their role and an Open action; otherwise a Join\n * action. Membership is supplied by the parent (which knows the caller's set).\n */\n\nimport { Users, Hash } from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { Avatar } from './Avatar';\nimport { StatusBadge } from './StatusBadge';\nimport { useComJoinCircle, useComLeaveCircle } from '../hooks';\nimport { formatEnumLabel, formatNumber } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComCircle, ComMembership } from '../types';\n\ninterface CircleCardProps {\n circle: ComCircle;\n membership?: ComMembership | null;\n onOpen: () => void;\n}\n\nexport function CircleCard({ circle, membership, onOpen }: CircleCardProps) {\n const join = useComJoinCircle();\n const leave = useComLeaveCircle();\n const isMember = membership?.status === 'ACTIVE';\n\n const handleJoin = async () => {\n try {\n await join.mutateAsync(circle.id);\n notify.success('Joined circle', `You are now a member of ${circle.name}.`);\n } catch (err) {\n notify.error('Could not join', describeError(err));\n }\n };\n\n const handleLeave = async () => {\n try {\n await leave.mutateAsync(circle.id);\n notify.success('Left circle', `You have left ${circle.name}.`);\n } catch (err) {\n notify.error('Could not leave', describeError(err));\n }\n };\n\n const isOwner = membership?.role === 'OWNER' || membership?.role === 'ADMIN';\n\n return (\n <GlassCard\n treatment=\"plain\"\n className={cn(\n 'flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]',\n isOwner && 'ring-1 ring-[var(--color-accent-soft)]'\n )}\n >\n <div className=\"flex items-start gap-3\">\n <Avatar name={circle.name} seed={circle.id} size=\"lg\" fileId={circle.avatar?.id} />\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"truncate text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {circle.name}\n </button>\n {circle.topic && <p className=\"truncate text-sm text-text-secondary\">{circle.topic}</p>}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={circle.visibility} />\n {circle.status !== 'ACTIVE' && <StatusBadge status={circle.status} />}\n {isMember && membership && <StatusBadge status={membership.role} />}\n </div>\n </div>\n </div>\n\n {circle.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{circle.description}</p>\n )}\n\n {circle.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {circle.tags.slice(0, 4).map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3\">\n <div className=\"flex items-center gap-3 text-xs text-text-tertiary\">\n <span className=\"inline-flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" /> {formatNumber(circle.memberCount)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Hash className=\"h-3.5 w-3.5\" /> {circle.channels?.length ?? 0}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n {isMember ? (\n <>\n <Button size=\"sm\" variant=\"outline\" onClick={handleLeave} disabled={leave.isPending}>\n Leave\n </Button>\n <Button size=\"sm\" onClick={onOpen}>\n Open\n </Button>\n </>\n ) : circle.status === 'ACTIVE' ? (\n <Button size=\"sm\" onClick={handleJoin} disabled={join.isPending}>\n Join\n </Button>\n ) : (\n <span className=\"text-xs text-text-tertiary\">{formatEnumLabel(circle.status)}</span>\n )}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;;;AAyBA,SAAgB,EAAW,EAAE,WAAQ,eAAY,aAA2B;CAC1E,IAAM,IAAO,GAAkB,EACzB,IAAQ,GAAmB,EAC3B,IAAW,GAAY,WAAW;AAsBxC,QACE,kBAAC,GAAD;EACE,WAAU;EACV,WAAW,EACT,6HANU,GAAY,SAAS,WAAW,GAAY,SAAS,YAOpD,yCACZ;YALH;GAOE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,MAAM,EAAO;KAAM,MAAM,EAAO;KAAI,MAAK;KAAK,QAAQ,EAAO,QAAQ;KAAM,CAAA,EACnF,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAET,EAAO;OACD,CAAA;MACR,EAAO,SAAS,kBAAC,KAAD;OAAG,WAAU;iBAAwC,EAAO;OAAU,CAAA;MACvF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAa,QAAQ,EAAO,YAAc,CAAA;QACzC,EAAO,WAAW,YAAY,kBAAC,GAAD,EAAa,QAAQ,EAAO,QAAU,CAAA;QACpE,KAAY,KAAc,kBAAC,GAAD,EAAa,QAAQ,EAAW,MAAQ,CAAA;QAC/D;;MACF;OACF;;GAEL,EAAO,eACN,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAO;IAAgB,CAAA;GAGtF,EAAO,KAAK,SAAS,KACpB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC5B,kBAAC,QAAD;KAEE,WAAU;eAFZ,CAGC,KACG,EACG;OAJA,EAIA,CACP;IACE,CAAA;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;OAAE,EAAa,EAAO,YAAY;OAC9D;SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA;;OAAE,EAAO,UAAU,UAAU;OACxD;QACH;QACN,kBAAC,OAAD;KAAK,WAAU;eACZ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAU,SAnE5B,YAAY;AAC9B,WAAI;AAEF,QADA,MAAM,EAAM,YAAY,EAAO,GAAG,EAClC,EAAO,QAAQ,eAAe,iBAAiB,EAAO,KAAK,GAAG;gBACvD,GAAK;AACZ,UAAO,MAAM,mBAAmB,EAAc,EAAI,CAAC;;;MA8De,UAAU,EAAM;gBAAW;MAE5E,CAAA,EACT,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAS;gBAAQ;MAE1B,CAAA,CACR,EAAA,CAAA,GACD,EAAO,WAAW,WACpB,kBAAC,GAAD;MAAQ,MAAK;MAAK,SApFT,YAAY;AAC7B,WAAI;AAEF,QADA,MAAM,EAAK,YAAY,EAAO,GAAG,EACjC,EAAO,QAAQ,iBAAiB,2BAA2B,EAAO,KAAK,GAAG;gBACnE,GAAK;AACZ,UAAO,MAAM,kBAAkB,EAAc,EAAI,CAAC;;;MA+EL,UAAU,EAAK;gBAAW;MAExD,CAAA,GAET,kBAAC,QAAD;MAAM,WAAU;gBAA8B,EAAgB,EAAO,OAAO;MAAQ,CAAA;KAElF,CAAA,CACF;;GACI"}
@@ -1,17 +1,20 @@
1
- import { cn as e } from "../utils/cn.js";
2
- import { useComAddComment as t, useComComments as n } from "../hooks/useComradeCircleApi.js";
1
+ import { useComradeCircle as e } from "../context/ComradeCircleContext.js";
2
+ import { cn as t } from "../utils/cn.js";
3
+ import "../context/index.js";
4
+ import { useComAddComment as n, useComComments as r } from "../hooks/useComradeCircleApi.js";
3
5
  import "../hooks/index.js";
4
- import { MemberIdentity as r } from "./MemberIdentity.js";
5
- import { describeError as i, notify as a } from "../lib/toast.js";
6
- import { formatRelativeTime as o } from "../utils/formatters.js";
7
- import { useMemo as s, useState as c } from "react";
8
- import { CornerDownRight as l, Send as u } from "lucide-react";
9
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
10
- import { Button as p, Spinner as m, Textarea as h } from "@burdenoff/fe-libs/ui";
6
+ import { Avatar as i } from "./Avatar.js";
7
+ import { MemberIdentity as a } from "./MemberIdentity.js";
8
+ import { describeError as o, notify as s } from "../lib/toast.js";
9
+ import { formatRelativeTime as c } from "../utils/formatters.js";
10
+ import { useMemo as l, useState as u } from "react";
11
+ import { CornerDownRight as d, MessageCircle as f, Send as p } from "lucide-react";
12
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
13
+ import { Button as g, Spinner as _, Textarea as v } from "@burdenoff/fe-libs/ui";
11
14
  //#region src/comradecircle/components/CommentThread.tsx
12
- function g({ postId: g }) {
13
- let { data: _, isLoading: v } = n(g), y = t(), [b, x] = c(""), [S, C] = c(null), { roots: w, repliesByParent: T } = s(() => {
14
- let e = _ ?? [], t = /* @__PURE__ */ new Map(), n = [];
15
+ function y({ postId: y }) {
16
+ let { currentUser: b } = e(), { data: x, isLoading: S } = r(y), C = n(), [w, T] = u(""), [E, D] = u(null), { roots: O, repliesByParent: k } = l(() => {
17
+ let e = x ?? [], t = /* @__PURE__ */ new Map(), n = [];
15
18
  for (let r of e) if (r.parentId) {
16
19
  let e = t.get(r.parentId) ?? [];
17
20
  e.push(r), t.set(r.parentId, e);
@@ -20,86 +23,99 @@ function g({ postId: g }) {
20
23
  roots: n,
21
24
  repliesByParent: t
22
25
  };
23
- }, [_]), E = async () => {
24
- let e = b.trim();
26
+ }, [x]), A = async () => {
27
+ let e = w.trim();
25
28
  if (e) try {
26
- await y.mutateAsync({
27
- postId: g,
29
+ await C.mutateAsync({
30
+ postId: y,
28
31
  body: e,
29
- parentId: S?.id ?? null
30
- }), x(""), C(null);
32
+ parentId: E?.id ?? null
33
+ }), T(""), D(null);
31
34
  } catch (e) {
32
- a.error("Could not add comment", i(e));
35
+ s.error("Could not add comment", o(e));
33
36
  }
34
- }, D = (t, n = !1) => /* @__PURE__ */ f("div", {
35
- className: e("flex flex-col gap-1", n && "ml-8"),
36
- children: [/* @__PURE__ */ f("div", {
37
+ }, j = (e, n = !1) => /* @__PURE__ */ h("div", {
38
+ className: t("flex flex-col gap-1", n && "ml-8"),
39
+ children: [/* @__PURE__ */ h("div", {
37
40
  className: "rounded-xl bg-bg-sunken/60 px-3 py-2",
38
- children: [/* @__PURE__ */ f("div", {
41
+ children: [/* @__PURE__ */ h("div", {
39
42
  className: "flex items-center justify-between gap-2",
40
- children: [/* @__PURE__ */ d(r, {
41
- memberId: t.authorId,
43
+ children: [/* @__PURE__ */ m(a, {
44
+ memberId: e.authorId,
42
45
  size: "xs",
43
- suffix: o(t.createdAt)
44
- }), !n && /* @__PURE__ */ f("button", {
46
+ suffix: c(e.createdAt)
47
+ }), !n && /* @__PURE__ */ h("button", {
45
48
  type: "button",
46
- onClick: () => C(t),
49
+ onClick: () => D(e),
47
50
  className: "inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-text-primary",
48
- children: [/* @__PURE__ */ d(l, { className: "h-3 w-3" }), " Reply"]
51
+ children: [/* @__PURE__ */ m(d, { className: "h-3 w-3" }), " Reply"]
49
52
  })]
50
- }), /* @__PURE__ */ d("p", {
53
+ }), /* @__PURE__ */ m("p", {
51
54
  className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-primary",
52
- children: t.body
55
+ children: e.body
53
56
  })]
54
- }), (T.get(t.id) ?? []).map((e) => D(e, !0))]
55
- }, t.id);
56
- return /* @__PURE__ */ f("div", {
57
+ }), (k.get(e.id) ?? []).map((e) => j(e, !0))]
58
+ }, e.id);
59
+ return /* @__PURE__ */ h("div", {
57
60
  className: "mt-3 border-t border-border-seam pt-3",
58
- children: [v ? /* @__PURE__ */ d("div", {
61
+ children: [S ? /* @__PURE__ */ m("div", {
59
62
  className: "flex justify-center py-4",
60
- children: /* @__PURE__ */ d(m, { size: "sm" })
61
- }) : w.length === 0 ? /* @__PURE__ */ d("p", {
62
- className: "py-2 text-center text-sm text-text-tertiary",
63
- children: "No comments yet. Start the conversation."
64
- }) : /* @__PURE__ */ d("div", {
63
+ children: /* @__PURE__ */ m(_, { size: "sm" })
64
+ }) : O.length === 0 ? /* @__PURE__ */ h("div", {
65
+ className: "flex flex-col items-center gap-1.5 py-4 text-center",
66
+ children: [/* @__PURE__ */ m(f, { className: "h-6 w-6 text-text-tertiary opacity-60" }), /* @__PURE__ */ m("p", {
67
+ className: "text-sm text-text-tertiary",
68
+ children: "No comments yet — start the conversation."
69
+ })]
70
+ }) : /* @__PURE__ */ m("div", {
65
71
  className: "flex flex-col gap-3",
66
- children: w.map((e) => D(e))
67
- }), /* @__PURE__ */ f("div", {
72
+ children: O.map((e) => j(e))
73
+ }), /* @__PURE__ */ h("div", {
68
74
  className: "mt-3 flex flex-col gap-2",
69
- children: [S && /* @__PURE__ */ f("div", {
75
+ children: [E && /* @__PURE__ */ h("div", {
70
76
  className: "flex items-center justify-between rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary",
71
- children: [/* @__PURE__ */ d("span", {
77
+ children: [/* @__PURE__ */ m("span", {
72
78
  className: "truncate",
73
79
  children: "Replying to a comment"
74
- }), /* @__PURE__ */ d("button", {
80
+ }), /* @__PURE__ */ m("button", {
75
81
  type: "button",
76
- onClick: () => C(null),
82
+ onClick: () => D(null),
77
83
  className: "text-text-tertiary hover:text-text-primary",
78
84
  children: "Cancel"
79
85
  })]
80
- }), /* @__PURE__ */ f("div", {
86
+ }), /* @__PURE__ */ h("div", {
81
87
  className: "flex items-end gap-2",
82
- children: [/* @__PURE__ */ d(h, {
83
- value: b,
84
- onChange: (e) => x(e.target.value),
85
- placeholder: "Add a comment…",
86
- rows: 1,
87
- className: "min-h-[40px] resize-none",
88
- onKeyDown: (e) => {
89
- e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), E());
90
- }
91
- }), /* @__PURE__ */ d(p, {
92
- size: "sm",
93
- onClick: E,
94
- disabled: !b.trim() || y.isPending,
95
- "aria-label": "Post comment",
96
- children: /* @__PURE__ */ d(u, { className: "h-4 w-4" })
97
- })]
88
+ children: [
89
+ /* @__PURE__ */ m(i, {
90
+ name: b?.name ?? b?.email ?? "You",
91
+ seed: b?.id,
92
+ size: "sm",
93
+ className: "mb-1"
94
+ }),
95
+ /* @__PURE__ */ m(v, {
96
+ value: w,
97
+ onChange: (e) => T(e.target.value),
98
+ placeholder: "Add a comment…",
99
+ rows: 1,
100
+ className: "min-h-[40px] resize-none",
101
+ onKeyDown: (e) => {
102
+ e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), A());
103
+ }
104
+ }),
105
+ /* @__PURE__ */ m(g, {
106
+ size: "icon",
107
+ onClick: A,
108
+ disabled: !w.trim() || C.isPending,
109
+ "aria-label": "Post comment",
110
+ className: "shrink-0 rounded-full",
111
+ children: /* @__PURE__ */ m(p, { className: "h-4 w-4" })
112
+ })
113
+ ]
98
114
  })]
99
115
  })]
100
116
  });
101
117
  }
102
118
  //#endregion
103
- export { g as CommentThread };
119
+ export { y as CommentThread };
104
120
 
105
121
  //# sourceMappingURL=CommentThread.js.map