@burdenoff/microfe-comradecircle 2026.525.1 → 2026.704.2

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 (107) hide show
  1. package/dist/comradecircle/ComradeCircleRoot.js +75 -95
  2. package/dist/comradecircle/ComradeCircleRoot.js.map +1 -1
  3. package/dist/comradecircle/ComradeCircleRoutes.js +58 -37
  4. package/dist/comradecircle/ComradeCircleRoutes.js.map +1 -1
  5. package/dist/comradecircle/components/Avatar.js +47 -0
  6. package/dist/comradecircle/components/Avatar.js.map +1 -0
  7. package/dist/comradecircle/components/AvatarUploadField.js +80 -0
  8. package/dist/comradecircle/components/AvatarUploadField.js.map +1 -0
  9. package/dist/comradecircle/components/CircleCard.js +118 -0
  10. package/dist/comradecircle/components/CircleCard.js.map +1 -0
  11. package/dist/comradecircle/components/CommentThread.js +105 -0
  12. package/dist/comradecircle/components/CommentThread.js.map +1 -0
  13. package/dist/comradecircle/components/CreateCircleDialog.js +159 -0
  14. package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -0
  15. package/dist/comradecircle/components/CreateEventDialog.js +210 -0
  16. package/dist/comradecircle/components/CreateEventDialog.js.map +1 -0
  17. package/dist/comradecircle/components/EmptyState.js +35 -0
  18. package/dist/comradecircle/components/EmptyState.js.map +1 -0
  19. package/dist/comradecircle/components/EventCard.js +93 -0
  20. package/dist/comradecircle/components/EventCard.js.map +1 -0
  21. package/dist/comradecircle/components/FileImage.js +20 -0
  22. package/dist/comradecircle/components/FileImage.js.map +1 -0
  23. package/dist/comradecircle/components/FilterTabs.js +27 -0
  24. package/dist/comradecircle/components/FilterTabs.js.map +1 -0
  25. package/dist/comradecircle/components/ImageAttachments.js +97 -0
  26. package/dist/comradecircle/components/ImageAttachments.js.map +1 -0
  27. package/dist/comradecircle/components/MemberIdentity.js +44 -0
  28. package/dist/comradecircle/components/MemberIdentity.js.map +1 -0
  29. package/dist/comradecircle/components/MiniBar.js +22 -0
  30. package/dist/comradecircle/components/MiniBar.js.map +1 -0
  31. package/dist/comradecircle/components/PageLayout.js +38 -0
  32. package/dist/comradecircle/components/PageLayout.js.map +1 -0
  33. package/dist/comradecircle/components/Pagination.js +50 -0
  34. package/dist/comradecircle/components/Pagination.js.map +1 -0
  35. package/dist/comradecircle/components/PollCard.js +97 -0
  36. package/dist/comradecircle/components/PollCard.js.map +1 -0
  37. package/dist/comradecircle/components/PostCard.js +163 -0
  38. package/dist/comradecircle/components/PostCard.js.map +1 -0
  39. package/dist/comradecircle/components/PostComposer.js +87 -0
  40. package/dist/comradecircle/components/PostComposer.js.map +1 -0
  41. package/dist/comradecircle/components/ReactionBar.js +122 -0
  42. package/dist/comradecircle/components/ReactionBar.js.map +1 -0
  43. package/dist/comradecircle/components/StateViews.js +49 -0
  44. package/dist/comradecircle/components/StateViews.js.map +1 -0
  45. package/dist/comradecircle/components/StatusBadge.js +68 -0
  46. package/dist/comradecircle/components/StatusBadge.js.map +1 -0
  47. package/dist/comradecircle/components/Tabs.js +31 -0
  48. package/dist/comradecircle/components/Tabs.js.map +1 -0
  49. package/dist/comradecircle/components/TagInput.js +49 -0
  50. package/dist/comradecircle/components/TagInput.js.map +1 -0
  51. package/dist/comradecircle/context/ComradeCircleContext.js +85 -0
  52. package/dist/comradecircle/context/ComradeCircleContext.js.map +1 -0
  53. package/dist/comradecircle/context/index.js +1 -0
  54. package/dist/comradecircle/hooks/index.js +3 -0
  55. package/dist/comradecircle/hooks/useComradeCircleApi.js +419 -0
  56. package/dist/comradecircle/hooks/useComradeCircleApi.js.map +1 -0
  57. package/dist/comradecircle/hooks/useCursorPager.js +25 -0
  58. package/dist/comradecircle/hooks/useCursorPager.js.map +1 -0
  59. package/dist/comradecircle/hooks/useFiles.js +78 -0
  60. package/dist/comradecircle/hooks/useFiles.js.map +1 -0
  61. package/dist/comradecircle/index.js +5 -2
  62. package/dist/comradecircle/lib/graphql-cache.js +9 -0
  63. package/dist/comradecircle/lib/graphql-cache.js.map +1 -0
  64. package/dist/comradecircle/lib/toast.js +39 -0
  65. package/dist/comradecircle/lib/toast.js.map +1 -0
  66. package/dist/comradecircle/pages/FeedPage.js +65 -0
  67. package/dist/comradecircle/pages/FeedPage.js.map +1 -0
  68. package/dist/comradecircle/pages/circles/CircleDetailPage.js +651 -0
  69. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -0
  70. package/dist/comradecircle/pages/circles/CirclesPage.js +129 -0
  71. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -0
  72. package/dist/comradecircle/pages/events/EventDetailPage.js +276 -0
  73. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -0
  74. package/dist/comradecircle/pages/events/EventsPage.js +102 -0
  75. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -0
  76. package/dist/comradecircle/pages/members/MemberProfilePage.js +138 -0
  77. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -0
  78. package/dist/comradecircle/pages/members/MembersPage.js +153 -0
  79. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -0
  80. package/dist/comradecircle/pages/profile/MyProfilePage.js +266 -0
  81. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -0
  82. package/dist/comradecircle/utils/cn.js +10 -0
  83. package/dist/comradecircle/utils/cn.js.map +1 -0
  84. package/dist/comradecircle/utils/formatters.js +51 -0
  85. package/dist/comradecircle/utils/formatters.js.map +1 -0
  86. package/dist/comradecircle/utils/navigation.js +2 -1
  87. package/dist/comradecircle/utils/navigation.js.map +1 -1
  88. package/dist/comradecircle/utils/slug.js +8 -0
  89. package/dist/comradecircle/utils/slug.js.map +1 -0
  90. package/dist/index.js +6 -4
  91. package/dist/node_modules/clsx/dist/clsx.js +18 -0
  92. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  93. package/dist/node_modules/sonner/dist/index.js +163 -0
  94. package/dist/node_modules/sonner/dist/index.js.map +1 -0
  95. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
  96. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  97. package/package.json +3 -2
  98. package/dist/comradecircle/pages/CommunitiesPage.js +0 -299
  99. package/dist/comradecircle/pages/CommunitiesPage.js.map +0 -1
  100. package/dist/comradecircle/pages/CreatorPage.js +0 -399
  101. package/dist/comradecircle/pages/CreatorPage.js.map +0 -1
  102. package/dist/comradecircle/pages/DashboardPage.js +0 -265
  103. package/dist/comradecircle/pages/DashboardPage.js.map +0 -1
  104. package/dist/comradecircle/providers/ComradeCircleProvider.js +0 -27
  105. package/dist/comradecircle/providers/ComradeCircleProvider.js.map +0 -1
  106. package/dist/comradecircle/store/comradecircleStore.js +0 -345
  107. package/dist/comradecircle/store/comradecircleStore.js.map +0 -1
@@ -0,0 +1,266 @@
1
+ import { useComradeCircle as e } from "../../context/ComradeCircleContext.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import "../../context/index.js";
4
+ import { useComProfile as n, useComUpsertProfile as r } from "../../hooks/useComradeCircleApi.js";
5
+ import "../../hooks/index.js";
6
+ import { Avatar as i } from "../../components/Avatar.js";
7
+ import { describeError as a, notify as o } from "../../lib/toast.js";
8
+ import { formatEnumLabel as s } from "../../utils/formatters.js";
9
+ import { ErrorView as c, LoadingView as l } from "../../components/StateViews.js";
10
+ import { StatusBadge as u } from "../../components/StatusBadge.js";
11
+ import { TagInput as d } from "../../components/TagInput.js";
12
+ import { AvatarUploadField as f } from "../../components/AvatarUploadField.js";
13
+ import { useState as p } from "react";
14
+ import { Save as m } from "lucide-react";
15
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
16
+ import { Button as _, GlassCard as v, Input as y, Label as b, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Switch as E, Textarea as D } from "@burdenoff/fe-libs/ui";
17
+ //#region src/comradecircle/pages/profile/MyProfilePage.tsx
18
+ var O = [
19
+ "ONLINE",
20
+ "AWAY",
21
+ "BUSY",
22
+ "OFFLINE"
23
+ ];
24
+ function k() {
25
+ let { currentUser: r } = e(), i = r?.id, { data: a, isLoading: o, isError: s, refetch: u } = n(i), d = r?.name ?? r?.email ?? "";
26
+ return o ? /* @__PURE__ */ h(t, {
27
+ title: "My Profile",
28
+ children: /* @__PURE__ */ h(l, { label: "Loading your profile…" })
29
+ }) : s ? /* @__PURE__ */ h(t, {
30
+ title: "My Profile",
31
+ children: /* @__PURE__ */ h(c, {
32
+ title: "We couldn't load your profile",
33
+ message: "Something went wrong while fetching your profile. Retry before editing so you don't overwrite saved details.",
34
+ onRetry: () => u()
35
+ })
36
+ }) : /* @__PURE__ */ h(t, {
37
+ title: "My Profile",
38
+ description: "Your profile is your identity across every circle — it's how members recognise, find and follow you.",
39
+ children: /* @__PURE__ */ h(A, {
40
+ initialProfile: a ?? null,
41
+ identityName: d
42
+ }, a?.id ?? "new")
43
+ });
44
+ }
45
+ function A({ initialProfile: e, identityName: t }) {
46
+ let n = r(), [c, l] = p(e?.displayName ?? t), [k, A] = p(e?.headline ?? ""), [j, M] = p(e?.bio ?? ""), [N, P] = p(e?.interests ?? []), [F, I] = p(e?.skills ?? []), [L, R] = p(e?.presence ?? "ONLINE"), [z, B] = p(e?.discoverable ?? !0), [V, H] = p(e?.avatar?.id ?? null), U = c.trim();
47
+ return /* @__PURE__ */ g("div", {
48
+ className: "grid gap-6 lg:grid-cols-[1fr_320px]",
49
+ children: [/* @__PURE__ */ h(v, {
50
+ treatment: "plain",
51
+ className: "order-2 p-5 sm:p-6 lg:order-1",
52
+ children: /* @__PURE__ */ g("div", {
53
+ className: "flex flex-col gap-5",
54
+ children: [
55
+ /* @__PURE__ */ g("div", {
56
+ className: "grid gap-1.5",
57
+ children: [/* @__PURE__ */ h(b, { children: "Avatar" }), /* @__PURE__ */ h(f, {
58
+ fileId: V,
59
+ name: U || t,
60
+ onUploaded: H
61
+ })]
62
+ }),
63
+ /* @__PURE__ */ g("div", {
64
+ className: "grid gap-1.5",
65
+ children: [/* @__PURE__ */ h(b, {
66
+ htmlFor: "profile-name",
67
+ children: "Display name"
68
+ }), /* @__PURE__ */ h(y, {
69
+ id: "profile-name",
70
+ value: c,
71
+ onChange: (e) => l(e.target.value),
72
+ placeholder: "How members see you",
73
+ required: !0,
74
+ "aria-required": "true"
75
+ })]
76
+ }),
77
+ /* @__PURE__ */ g("div", {
78
+ className: "grid gap-1.5",
79
+ children: [/* @__PURE__ */ h(b, {
80
+ htmlFor: "profile-headline",
81
+ children: "Headline"
82
+ }), /* @__PURE__ */ h(y, {
83
+ id: "profile-headline",
84
+ value: k,
85
+ onChange: (e) => A(e.target.value),
86
+ placeholder: "e.g. Community organiser · Photographer"
87
+ })]
88
+ }),
89
+ /* @__PURE__ */ g("div", {
90
+ className: "grid gap-1.5",
91
+ children: [/* @__PURE__ */ h(b, {
92
+ htmlFor: "profile-bio",
93
+ children: "Bio"
94
+ }), /* @__PURE__ */ h(D, {
95
+ id: "profile-bio",
96
+ value: j,
97
+ onChange: (e) => M(e.target.value),
98
+ rows: 4,
99
+ placeholder: "Tell circles a little about yourself."
100
+ })]
101
+ }),
102
+ /* @__PURE__ */ g("div", {
103
+ className: "grid gap-1.5",
104
+ children: [/* @__PURE__ */ h(b, {
105
+ htmlFor: "profile-interests",
106
+ children: "Interests"
107
+ }), /* @__PURE__ */ h(d, {
108
+ id: "profile-interests",
109
+ value: N,
110
+ onChange: P,
111
+ placeholder: "Add an interest…"
112
+ })]
113
+ }),
114
+ /* @__PURE__ */ g("div", {
115
+ className: "grid gap-1.5",
116
+ children: [/* @__PURE__ */ h(b, {
117
+ htmlFor: "profile-skills",
118
+ children: "Skills"
119
+ }), /* @__PURE__ */ h(d, {
120
+ id: "profile-skills",
121
+ value: F,
122
+ onChange: I,
123
+ placeholder: "Add a skill…"
124
+ })]
125
+ }),
126
+ /* @__PURE__ */ g("div", {
127
+ className: "grid gap-1.5",
128
+ children: [/* @__PURE__ */ h(b, {
129
+ htmlFor: "profile-presence",
130
+ children: "Presence"
131
+ }), /* @__PURE__ */ g(x, {
132
+ value: L,
133
+ onValueChange: (e) => R(e),
134
+ children: [/* @__PURE__ */ h(w, {
135
+ id: "profile-presence",
136
+ children: /* @__PURE__ */ h(T, {})
137
+ }), /* @__PURE__ */ h(S, { children: O.map((e) => /* @__PURE__ */ h(C, {
138
+ value: e,
139
+ children: s(e)
140
+ }, e)) })]
141
+ })]
142
+ }),
143
+ /* @__PURE__ */ g("div", {
144
+ className: "flex items-start justify-between gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5",
145
+ children: [/* @__PURE__ */ g("div", {
146
+ className: "min-w-0",
147
+ children: [/* @__PURE__ */ h(b, {
148
+ htmlFor: "profile-discoverable",
149
+ children: "List me in the member directory"
150
+ }), /* @__PURE__ */ h("p", {
151
+ className: "mt-1 text-xs text-text-tertiary",
152
+ children: "When on, your profile appears in the discoverable member directory so others can find and follow you. Turn it off to stay unlisted."
153
+ })]
154
+ }), /* @__PURE__ */ h(E, {
155
+ id: "profile-discoverable",
156
+ checked: z,
157
+ onCheckedChange: B,
158
+ "aria-label": "List me in the member directory",
159
+ className: "mt-0.5 shrink-0"
160
+ })]
161
+ }),
162
+ /* @__PURE__ */ h("div", {
163
+ className: "flex justify-end pt-1",
164
+ children: /* @__PURE__ */ g(_, {
165
+ onClick: async () => {
166
+ let e = c.trim();
167
+ if (!e) {
168
+ o.error("Display name required", "Add a name so members can recognise you.");
169
+ return;
170
+ }
171
+ try {
172
+ await n.mutateAsync({
173
+ displayName: e,
174
+ headline: k.trim() || null,
175
+ bio: j.trim() || null,
176
+ avatarFileId: V,
177
+ interests: N,
178
+ skills: F,
179
+ presence: L,
180
+ discoverable: z
181
+ }), o.success("Profile saved");
182
+ } catch (e) {
183
+ o.error("Could not save profile", a(e));
184
+ }
185
+ },
186
+ disabled: n.isPending,
187
+ children: [/* @__PURE__ */ h(m, { className: "mr-1.5 h-4 w-4" }), n.isPending ? "Saving…" : "Save profile"]
188
+ })
189
+ })
190
+ ]
191
+ })
192
+ }), /* @__PURE__ */ h("div", {
193
+ className: "order-1 lg:order-2",
194
+ children: /* @__PURE__ */ g(v, {
195
+ treatment: "plain",
196
+ className: "p-5 lg:sticky lg:top-6",
197
+ children: [
198
+ /* @__PURE__ */ h("p", {
199
+ className: "mb-4 text-xs font-semibold uppercase tracking-wide text-text-tertiary",
200
+ children: "Preview"
201
+ }),
202
+ /* @__PURE__ */ g("div", {
203
+ className: "flex flex-col items-center gap-3 text-center",
204
+ children: [
205
+ /* @__PURE__ */ h(i, {
206
+ name: U || t,
207
+ size: "xl",
208
+ presence: L,
209
+ fileId: V
210
+ }),
211
+ /* @__PURE__ */ g("div", {
212
+ className: "min-w-0",
213
+ children: [/* @__PURE__ */ h("h3", {
214
+ className: "truncate text-lg font-semibold text-text-primary",
215
+ children: U || "Your name"
216
+ }), k.trim() && /* @__PURE__ */ h("p", {
217
+ className: "mt-0.5 text-sm text-text-secondary",
218
+ children: k.trim()
219
+ })]
220
+ }),
221
+ /* @__PURE__ */ h(u, { status: L })
222
+ ]
223
+ }),
224
+ j.trim() && /* @__PURE__ */ h("p", {
225
+ className: "mt-4 whitespace-pre-wrap break-words text-center text-sm text-text-secondary",
226
+ children: j.trim()
227
+ }),
228
+ N.length > 0 && /* @__PURE__ */ g("div", {
229
+ className: "mt-5",
230
+ children: [/* @__PURE__ */ h("p", {
231
+ className: "mb-1.5 text-xs font-medium text-text-tertiary",
232
+ children: "Interests"
233
+ }), /* @__PURE__ */ h("div", {
234
+ className: "flex flex-wrap gap-1.5",
235
+ children: N.map((e) => /* @__PURE__ */ h("span", {
236
+ className: "rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
237
+ children: e
238
+ }, e))
239
+ })]
240
+ }),
241
+ F.length > 0 && /* @__PURE__ */ g("div", {
242
+ className: "mt-4",
243
+ children: [/* @__PURE__ */ h("p", {
244
+ className: "mb-1.5 text-xs font-medium text-text-tertiary",
245
+ children: "Skills"
246
+ }), /* @__PURE__ */ h("div", {
247
+ className: "flex flex-wrap gap-1.5",
248
+ children: F.map((e) => /* @__PURE__ */ h("span", {
249
+ className: "rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary",
250
+ children: e
251
+ }, e))
252
+ })]
253
+ }),
254
+ !z && /* @__PURE__ */ h("p", {
255
+ className: "mt-5 text-center text-xs text-text-tertiary",
256
+ children: "Hidden from the member directory."
257
+ })
258
+ ]
259
+ })
260
+ })]
261
+ });
262
+ }
263
+ //#endregion
264
+ export { k as MyProfilePage };
265
+
266
+ //# sourceMappingURL=MyProfilePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MyProfilePage.js","names":[],"sources":["../../../../src/comradecircle/pages/profile/MyProfilePage.tsx"],"sourcesContent":["/**\n * MyProfilePage — create or edit the caller's own social profile.\n * @module comradecircle/pages/profile\n *\n * A member's {@link ComProfile} is their identity across every circle — the\n * name, headline, presence and avatar initials that `MemberIdentity` resolves\n * everywhere a `memberId` appears. This page reads the caller's own profile\n * (`comProfile` by `currentUser.id`) and upserts it (`comUpsertProfile`). It\n * works both when a profile already exists (edit) and when none does yet\n * (create). The editor is only mounted once the profile query has settled and\n * seeds its fields via lazy state initialisers (keyed on the profile id), so no\n * state-syncing effect is needed. A live preview card mirrors the form.\n */\n\nimport { useState } from 'react';\nimport { Save } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n GlassCard,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { AvatarUploadField } from '../../components/AvatarUploadField';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { TagInput } from '../../components/TagInput';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useComProfile, useComUpsertProfile } from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { formatEnumLabel } from '../../utils';\nimport { notify, describeError } from '../../lib/toast';\nimport type { ComPresence, ComProfile } from '../../types';\n\nconst PRESENCE_OPTIONS: ComPresence[] = ['ONLINE', 'AWAY', 'BUSY', 'OFFLINE'];\n\nexport function MyProfilePage() {\n const { currentUser } = useComradeCircle();\n const memberId = currentUser?.id;\n const { data: profile, isLoading, isError, refetch } = useComProfile(memberId);\n\n // Seed create-mode from the shell identity so the name field is not blank.\n const identityName = currentUser?.name ?? currentUser?.email ?? '';\n\n if (isLoading) {\n return (\n <PageLayout title=\"My Profile\">\n <LoadingView label=\"Loading your profile…\" />\n </PageLayout>\n );\n }\n\n // A missing profile is NOT an error (the resolver returns null → create mode).\n // Only a genuine fetch failure lands here — surface it rather than silently\n // showing a blank form, which an upsert save could use to overwrite an\n // existing profile the caller never actually saw.\n if (isError) {\n return (\n <PageLayout title=\"My Profile\">\n <ErrorView\n title=\"We couldn't load your profile\"\n message=\"Something went wrong while fetching your profile. Retry before editing so you don't overwrite saved details.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"My Profile\"\n description=\"Your profile is your identity across every circle — it's how members recognise, find and follow you.\"\n >\n <ProfileEditor\n key={profile?.id ?? 'new'}\n initialProfile={profile ?? null}\n identityName={identityName}\n />\n </PageLayout>\n );\n}\n\ninterface ProfileEditorProps {\n initialProfile: ComProfile | null;\n identityName: string;\n}\n\nfunction ProfileEditor({ initialProfile, identityName }: ProfileEditorProps) {\n const upsert = useComUpsertProfile();\n\n const [displayName, setDisplayName] = useState(initialProfile?.displayName ?? identityName);\n const [headline, setHeadline] = useState(initialProfile?.headline ?? '');\n const [bio, setBio] = useState(initialProfile?.bio ?? '');\n const [interests, setInterests] = useState<string[]>(initialProfile?.interests ?? []);\n const [skills, setSkills] = useState<string[]>(initialProfile?.skills ?? []);\n const [presence, setPresence] = useState<ComPresence>(initialProfile?.presence ?? 'ONLINE');\n const [discoverable, setDiscoverable] = useState(initialProfile?.discoverable ?? true);\n const [avatarFileId, setAvatarFileId] = useState<string | null>(\n initialProfile?.avatar?.id ?? null\n );\n\n const previewName = displayName.trim();\n\n const handleSave = async () => {\n const name = displayName.trim();\n if (!name) {\n notify.error('Display name required', 'Add a name so members can recognise you.');\n return;\n }\n try {\n await upsert.mutateAsync({\n displayName: name,\n headline: headline.trim() || null,\n bio: bio.trim() || null,\n avatarFileId,\n interests,\n skills,\n presence,\n discoverable,\n });\n notify.success('Profile saved');\n } catch (err) {\n notify.error('Could not save profile', describeError(err));\n }\n };\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[1fr_320px]\">\n {/* Form */}\n <GlassCard treatment=\"plain\" className=\"order-2 p-5 sm:p-6 lg:order-1\">\n <div className=\"flex flex-col gap-5\">\n <div className=\"grid gap-1.5\">\n <Label>Avatar</Label>\n <AvatarUploadField\n fileId={avatarFileId}\n name={previewName || identityName}\n onUploaded={setAvatarFileId}\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-name\">Display name</Label>\n <Input\n id=\"profile-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n placeholder=\"How members see you\"\n required\n aria-required=\"true\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-headline\">Headline</Label>\n <Input\n id=\"profile-headline\"\n value={headline}\n onChange={(e) => setHeadline(e.target.value)}\n placeholder=\"e.g. Community organiser · Photographer\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-bio\">Bio</Label>\n <Textarea\n id=\"profile-bio\"\n value={bio}\n onChange={(e) => setBio(e.target.value)}\n rows={4}\n placeholder=\"Tell circles a little about yourself.\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-interests\">Interests</Label>\n <TagInput\n id=\"profile-interests\"\n value={interests}\n onChange={setInterests}\n placeholder=\"Add an interest…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-skills\">Skills</Label>\n <TagInput\n id=\"profile-skills\"\n value={skills}\n onChange={setSkills}\n placeholder=\"Add a skill…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-presence\">Presence</Label>\n <Select value={presence} onValueChange={(v) => setPresence(v as ComPresence)}>\n <SelectTrigger id=\"profile-presence\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRESENCE_OPTIONS.map((p) => (\n <SelectItem key={p} value={p}>\n {formatEnumLabel(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-start justify-between gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5\">\n <div className=\"min-w-0\">\n <Label htmlFor=\"profile-discoverable\">List me in the member directory</Label>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n When on, your profile appears in the discoverable member directory so others can\n find and follow you. Turn it off to stay unlisted.\n </p>\n </div>\n <Switch\n id=\"profile-discoverable\"\n checked={discoverable}\n onCheckedChange={setDiscoverable}\n aria-label=\"List me in the member directory\"\n className=\"mt-0.5 shrink-0\"\n />\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button onClick={handleSave} disabled={upsert.isPending}>\n <Save className=\"mr-1.5 h-4 w-4\" />\n {upsert.isPending ? 'Saving…' : 'Save profile'}\n </Button>\n </div>\n </div>\n </GlassCard>\n\n {/* Live preview */}\n <div className=\"order-1 lg:order-2\">\n <GlassCard treatment=\"plain\" className=\"p-5 lg:sticky lg:top-6\">\n <p className=\"mb-4 text-xs font-semibold uppercase tracking-wide text-text-tertiary\">\n Preview\n </p>\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <Avatar\n name={previewName || identityName}\n size=\"xl\"\n presence={presence}\n fileId={avatarFileId}\n />\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary\">\n {previewName || 'Your name'}\n </h3>\n {headline.trim() && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{headline.trim()}</p>\n )}\n </div>\n <StatusBadge status={presence} />\n </div>\n\n {bio.trim() && (\n <p className=\"mt-4 whitespace-pre-wrap break-words text-center text-sm text-text-secondary\">\n {bio.trim()}\n </p>\n )}\n\n {interests.length > 0 && (\n <div className=\"mt-5\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Interests</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {interests.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {skills.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Skills</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {skills.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {!discoverable && (\n <p className=\"mt-5 text-center text-xs text-text-tertiary\">\n Hidden from the member directory.\n </p>\n )}\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import e from "../../node_modules/clsx/dist/clsx.js";
2
+ import { twMerge as t } from "../../node_modules/tailwind-merge/dist/bundle-mjs.js";
3
+ //#region src/comradecircle/utils/cn.ts
4
+ function n(...n) {
5
+ return t(e(n));
6
+ }
7
+ //#endregion
8
+ export { n as cn };
9
+
10
+ //# sourceMappingURL=cn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cn.js","names":[],"sources":["../../../src/comradecircle/utils/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAGA,SAAgB,EAAG,GAAG,GAAsB;AAC1C,QAAO,EAAQ,EAAK,EAAO,CAAC"}
@@ -0,0 +1,51 @@
1
+ //#region src/comradecircle/utils/formatters.ts
2
+ function e(e, t) {
3
+ if (!e) return "—";
4
+ let n = typeof e == "string" ? new Date(e) : e;
5
+ return Number.isNaN(n.getTime()) ? "—" : new Intl.DateTimeFormat(void 0, {
6
+ day: "numeric",
7
+ month: "short",
8
+ year: "numeric",
9
+ ...t
10
+ }).format(n);
11
+ }
12
+ function t(e, t) {
13
+ if (!e) return "—";
14
+ let n = typeof e == "string" ? new Date(e) : e;
15
+ return Number.isNaN(n.getTime()) ? "—" : new Intl.DateTimeFormat(void 0, {
16
+ day: "numeric",
17
+ month: "short",
18
+ year: "numeric",
19
+ hour: "numeric",
20
+ minute: "2-digit",
21
+ ...t
22
+ }).format(n);
23
+ }
24
+ function n(e) {
25
+ if (!e) return "—";
26
+ let t = typeof e == "string" ? new Date(e) : e;
27
+ if (Number.isNaN(t.getTime())) return "—";
28
+ let n = /* @__PURE__ */ new Date(), r = t.getTime() - n.getTime(), i = r >= 0, a = Math.floor(Math.abs(r) / 1e3), o = Math.floor(a / 60), s = Math.floor(o / 60), c = Math.floor(s / 24), l = Math.floor(c / 7), u = Math.floor(c / 30), d = Math.floor(c / 365), f = (e, t) => {
29
+ let n = `${e} ${t}${e === 1 ? "" : "s"}`;
30
+ return i ? `in ${n}` : `${n} ago`;
31
+ };
32
+ return a < 60 ? i ? "soon" : "just now" : o < 60 ? f(o, "min") : s < 24 ? f(s, "hour") : c < 7 ? f(c, "day") : l < 4 ? f(l, "week") : u < 12 ? f(u, "month") : f(d, "year");
33
+ }
34
+ function r(e, t) {
35
+ return e == null || Number.isNaN(e) ? "—" : new Intl.NumberFormat(void 0, t).format(e);
36
+ }
37
+ function i(e, t) {
38
+ if (e == null || Number.isNaN(e)) return "—";
39
+ let n = r(e, { maximumFractionDigits: 2 });
40
+ return t ? `${n} ${t}` : n;
41
+ }
42
+ function a(e, t = 0) {
43
+ return e == null || Number.isNaN(e) ? "—" : `${e.toFixed(t)}%`;
44
+ }
45
+ function o(e) {
46
+ return e ? e.toLowerCase().replace(/_/g, " ").replace(/\b\w/g, (e) => e.toUpperCase()) : "—";
47
+ }
48
+ //#endregion
49
+ export { e as formatDate, t as formatDateTime, o as formatEnumLabel, r as formatNumber, a as formatPercent, i as formatQuantity, n as formatRelativeTime };
50
+
51
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","names":[],"sources":["../../../src/comradecircle/utils/formatters.ts"],"sourcesContent":["/**\n * Formatting utilities for the ComradeCircle microfrontend\n * @module comradecircle/utils/formatters\n */\n\n/** Format a date string or Date into a human-readable date. */\nexport function formatDate(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n ...options,\n }).format(d);\n}\n\n/** Format a date-time into a date + time string. */\nexport function formatDateTime(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n ...options,\n }).format(d);\n}\n\n/** Format a date into a relative time string (e.g. \"2 hours ago\", \"in 3 days\"). */\nexport function formatRelativeTime(date: string | Date | null | undefined): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n\n const now = new Date();\n const diffMs = d.getTime() - now.getTime();\n const future = diffMs >= 0;\n const absSeconds = Math.floor(Math.abs(diffMs) / 1000);\n const minutes = Math.floor(absSeconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(days / 30);\n const years = Math.floor(days / 365);\n\n const phrase = (value: number, unit: string): string => {\n const label = `${value} ${unit}${value !== 1 ? 's' : ''}`;\n return future ? `in ${label}` : `${label} ago`;\n };\n\n if (absSeconds < 60) return future ? 'soon' : 'just now';\n if (minutes < 60) return phrase(minutes, 'min');\n if (hours < 24) return phrase(hours, 'hour');\n if (days < 7) return phrase(days, 'day');\n if (weeks < 4) return phrase(weeks, 'week');\n if (months < 12) return phrase(months, 'month');\n return phrase(years, 'year');\n}\n\n/** Format a number with locale grouping. */\nexport function formatNumber(\n value: number | null | undefined,\n options?: Partial<Intl.NumberFormatOptions>\n): string {\n if (value == null || Number.isNaN(value)) return '—';\n return new Intl.NumberFormat(undefined, options).format(value);\n}\n\n/** Format a quantity with an optional unit suffix (e.g. \"1,200 kg\"). */\nexport function formatQuantity(value: number | null | undefined, unit?: string | null): string {\n if (value == null || Number.isNaN(value)) return '—';\n const num = formatNumber(value, { maximumFractionDigits: 2 });\n return unit ? `${num} ${unit}` : num;\n}\n\n/** Format a percentage (0–100) into a rounded \"%\" string. */\nexport function formatPercent(value: number | null | undefined, fractionDigits = 0): string {\n if (value == null || Number.isNaN(value)) return '—';\n return `${value.toFixed(fractionDigits)}%`;\n}\n\n/**\n * Format a mass of CO2e (kg) into a compact, human-scaled string:\n * < 1,000 kg → \"kg CO₂e\"; ≥ 1,000 kg → \"t CO₂e\" (tonnes).\n */\nexport function formatCo2e(kg: number | null | undefined): string {\n if (kg == null || Number.isNaN(kg)) return '—';\n if (Math.abs(kg) >= 1000) {\n return `${formatNumber(kg / 1000, { maximumFractionDigits: 2 })} t CO₂e`;\n }\n return `${formatNumber(kg, { maximumFractionDigits: 1 })} kg CO₂e`;\n}\n\n/** Format coordinates into a compact \"lat, lng\" string. */\nexport function formatCoords(\n lat: number | null | undefined,\n lng: number | null | undefined\n): string {\n if (lat == null || lng == null) return '—';\n return `${lat.toFixed(4)}, ${lng.toFixed(4)}`;\n}\n\n/** Turn an ENUM_LIKE_TOKEN into a human \"Enum Like Token\" label. */\nexport function formatEnumLabel(value: string | null | undefined): string {\n if (!value) return '—';\n return value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n"],"mappings":";AAMA,SAAgB,EACd,GACA,GACQ;AACR,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AAEtD,QADI,OAAO,MAAM,EAAE,SAAS,CAAC,GAAS,MAC/B,IAAI,KAAK,eAAe,KAAA,GAAW;EACxC,KAAK;EACL,OAAO;EACP,MAAM;EACN,GAAG;EACJ,CAAC,CAAC,OAAO,EAAE;;AAId,SAAgB,EACd,GACA,GACQ;AACR,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AAEtD,QADI,OAAO,MAAM,EAAE,SAAS,CAAC,GAAS,MAC/B,IAAI,KAAK,eAAe,KAAA,GAAW;EACxC,KAAK;EACL,OAAO;EACP,MAAM;EACN,MAAM;EACN,QAAQ;EACR,GAAG;EACJ,CAAC,CAAC,OAAO,EAAE;;AAId,SAAgB,EAAmB,GAAgD;AACjF,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AACtD,KAAI,OAAO,MAAM,EAAE,SAAS,CAAC,CAAE,QAAO;CAEtC,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAS,EAAE,SAAS,GAAG,EAAI,SAAS,EACpC,IAAS,KAAU,GACnB,IAAa,KAAK,MAAM,KAAK,IAAI,EAAO,GAAG,IAAK,EAChD,IAAU,KAAK,MAAM,IAAa,GAAG,EACrC,IAAQ,KAAK,MAAM,IAAU,GAAG,EAChC,IAAO,KAAK,MAAM,IAAQ,GAAG,EAC7B,IAAQ,KAAK,MAAM,IAAO,EAAE,EAC5B,IAAS,KAAK,MAAM,IAAO,GAAG,EAC9B,IAAQ,KAAK,MAAM,IAAO,IAAI,EAE9B,KAAU,GAAe,MAAyB;EACtD,IAAM,IAAQ,GAAG,EAAM,GAAG,IAAO,MAAU,IAAU,KAAN;AAC/C,SAAO,IAAS,MAAM,MAAU,GAAG,EAAM;;AAS3C,QANI,IAAa,KAAW,IAAS,SAAS,aAC1C,IAAU,KAAW,EAAO,GAAS,MAAM,GAC3C,IAAQ,KAAW,EAAO,GAAO,OAAO,GACxC,IAAO,IAAU,EAAO,GAAM,MAAM,GACpC,IAAQ,IAAU,EAAO,GAAO,OAAO,GACvC,IAAS,KAAW,EAAO,GAAQ,QAAQ,GACxC,EAAO,GAAO,OAAO;;AAI9B,SAAgB,EACd,GACA,GACQ;AAER,QADI,KAAS,QAAQ,OAAO,MAAM,EAAM,GAAS,MAC1C,IAAI,KAAK,aAAa,KAAA,GAAW,EAAQ,CAAC,OAAO,EAAM;;AAIhE,SAAgB,EAAe,GAAkC,GAA8B;AAC7F,KAAI,KAAS,QAAQ,OAAO,MAAM,EAAM,CAAE,QAAO;CACjD,IAAM,IAAM,EAAa,GAAO,EAAE,uBAAuB,GAAG,CAAC;AAC7D,QAAO,IAAO,GAAG,EAAI,GAAG,MAAS;;AAInC,SAAgB,EAAc,GAAkC,IAAiB,GAAW;AAE1F,QADI,KAAS,QAAQ,OAAO,MAAM,EAAM,GAAS,MAC1C,GAAG,EAAM,QAAQ,EAAe,CAAC;;AAyB1C,SAAgB,EAAgB,GAA0C;AAExE,QADK,IACE,EACJ,aAAa,CACb,QAAQ,MAAM,IAAI,CAClB,QAAQ,UAAU,MAAS,EAAK,aAAa,CAAC,GAJ9B"}
@@ -1,6 +1,7 @@
1
1
  //#region src/comradecircle/utils/navigation.ts
2
2
  function e(e, t) {
3
- return (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
3
+ let n = e.endsWith("/") ? e.slice(0, -1) : e;
4
+ return !t || t === "" ? n || "/" : n + (t.startsWith("/") ? t : `/${t}`);
4
5
  }
5
6
  //#endregion
6
7
  export { e as joinPath };
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","names":[],"sources":["../../../src/comradecircle/utils/navigation.ts"],"sourcesContent":["export function joinPath(basePath: string, relativePath: string): string {\n const cleanBase = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n const cleanPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;\n return cleanBase + cleanPath;\n}\n"],"mappings":";AAAA,SAAgB,EAAS,GAAkB,GAA8B;AAGvE,SAFkB,EAAS,SAAS,IAAI,GAAG,EAAS,MAAM,GAAG,GAAG,GAAG,MACjD,EAAa,WAAW,IAAI,GAAG,IAAe,IAAI"}
1
+ {"version":3,"file":"navigation.js","names":[],"sources":["../../../src/comradecircle/utils/navigation.ts"],"sourcesContent":["/**\n * Navigation utilities for the ComradeCircle microfrontend\n * @module comradecircle/utils\n */\n\n/**\n * Join basePath with a relative path, correctly handling slashes.\n *\n * @example\n * joinPath('/comradecircle', 'sites') // => '/comradecircle/sites'\n * joinPath('/comradecircle/', 'sites') // => '/comradecircle/sites'\n * joinPath('/comradecircle', '') // => '/comradecircle'\n */\nexport function joinPath(basePath: string, relativePath: string): string {\n const cleanBase = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n if (!relativePath || relativePath === '') {\n return cleanBase || '/';\n }\n const cleanPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;\n return cleanBase + cleanPath;\n}\n"],"mappings":";AAaA,SAAgB,EAAS,GAAkB,GAA8B;CACvE,IAAM,IAAY,EAAS,SAAS,IAAI,GAAG,EAAS,MAAM,GAAG,GAAG,GAAG;AAKnE,QAJI,CAAC,KAAgB,MAAiB,KAC7B,KAAa,MAGf,KADW,EAAa,WAAW,IAAI,GAAG,IAAe,IAAI"}
@@ -0,0 +1,8 @@
1
+ //#region src/comradecircle/utils/slug.ts
2
+ function e(e) {
3
+ return e.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
4
+ }
5
+ //#endregion
6
+ export { e as slugify };
7
+
8
+ //# sourceMappingURL=slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.js","names":[],"sources":["../../../src/comradecircle/utils/slug.ts"],"sourcesContent":["/**\n * slugify — turn a display name into a URL/handle-safe slug.\n * @module comradecircle/utils\n */\nexport function slugify(value: string): string {\n return value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n}\n"],"mappings":";AAIA,SAAgB,EAAQ,GAAuB;AAC7C,QAAO,EACJ,aAAa,CACb,MAAM,CACN,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,MAAM,GAAG,GAAG"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
- import { useComradeCircle as e } from "./comradecircle/providers/ComradeCircleProvider.js";
2
- import { useComradeCircleStore as t } from "./comradecircle/store/comradecircleStore.js";
3
- import { ComradeCircleRoot as n } from "./comradecircle/ComradeCircleRoot.js";
1
+ import { joinPath as e } from "./comradecircle/utils/navigation.js";
2
+ import { ComradeCircleProvider as t, useComradeCircle as n } from "./comradecircle/context/ComradeCircleContext.js";
3
+ import { cn as r } from "./comradecircle/utils/cn.js";
4
+ import { formatDate as i, formatDateTime as a, formatEnumLabel as o, formatNumber as s, formatPercent as c, formatQuantity as l, formatRelativeTime as u } from "./comradecircle/utils/formatters.js";
5
+ import { ComradeCircleRoot as d } from "./comradecircle/ComradeCircleRoot.js";
4
6
  import "./comradecircle/index.js";
5
- export { n as ComradeCircleRoot, e as useComradeCircle, t as useComradeCircleStore };
7
+ export { t as ComradeCircleProvider, d as ComradeCircleRoot, r as cn, i as formatDate, a as formatDateTime, o as formatEnumLabel, s as formatNumber, c as formatPercent, l as formatQuantity, u as formatRelativeTime, e as joinPath, n as useComradeCircle };
@@ -0,0 +1,18 @@
1
+ //#region node_modules/clsx/dist/clsx.mjs
2
+ function e(t) {
3
+ var n, r, i = "";
4
+ if (typeof t == "string" || typeof t == "number") i += t;
5
+ else if (typeof t == "object") if (Array.isArray(t)) {
6
+ var a = t.length;
7
+ for (n = 0; n < a; n++) t[n] && (r = e(t[n])) && (i && (i += " "), i += r);
8
+ } else for (r in t) t[r] && (i && (i += " "), i += r);
9
+ return i;
10
+ }
11
+ function t() {
12
+ for (var t, n, r = 0, i = "", a = arguments.length; r < a; r++) (t = arguments[r]) && (n = e(t)) && (i && (i += " "), i += n);
13
+ return i;
14
+ }
15
+ //#endregion
16
+ export { t as default };
17
+
18
+ //# sourceMappingURL=clsx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clsx.js","names":[],"sources":["../../../../node_modules/clsx/dist/clsx.mjs"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;"],"x_google_ignoreList":[0],"mappings":";AAAA,SAAS,EAAE,GAAE;CAAC,IAAI,GAAE,GAAE,IAAE;AAAG,KAAa,OAAO,KAAjB,YAA8B,OAAO,KAAjB,SAAmB,MAAG;UAAoB,OAAO,KAAjB,SAAmB,KAAG,MAAM,QAAQ,EAAE,EAAC;EAAC,IAAI,IAAE,EAAE;AAAO,OAAI,IAAE,GAAE,IAAE,GAAE,IAAI,GAAE,OAAK,IAAE,EAAE,EAAE,GAAG,MAAI,MAAI,KAAG,MAAK,KAAG;OAAQ,MAAI,KAAK,EAAE,GAAE,OAAK,MAAI,KAAG,MAAK,KAAG;AAAG,QAAO;;AAAE,SAAgB,IAAM;AAAC,MAAI,IAAI,GAAE,GAAE,IAAE,GAAE,IAAE,IAAG,IAAE,UAAU,QAAO,IAAE,GAAE,IAAI,EAAC,IAAE,UAAU,QAAM,IAAE,EAAE,EAAE,MAAI,MAAI,KAAG,MAAK,KAAG;AAAG,QAAO"}