@openrails/contentkit-ui 0.79.0 → 0.81.0

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 (295) hide show
  1. package/README.md +82 -54
  2. package/dist/client/beacons.d.ts +53 -0
  3. package/dist/client/beacons.js +72 -0
  4. package/dist/client/beacons.js.map +1 -0
  5. package/dist/client/catalog.d.ts +2 -2
  6. package/dist/client/catalog.js +1 -1
  7. package/dist/client/catalog.js.map +1 -1
  8. package/dist/client/client.d.ts +24 -11
  9. package/dist/client/client.js +37 -29
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/content/comments.d.ts +12 -8
  12. package/dist/client/content/comments.js +33 -20
  13. package/dist/client/content/comments.js.map +1 -1
  14. package/dist/client/content/favorites.d.ts +24 -9
  15. package/dist/client/content/favorites.js +21 -22
  16. package/dist/client/content/favorites.js.map +1 -1
  17. package/dist/client/content/moderation.d.ts +1 -1
  18. package/dist/client/content/moderation.js.map +1 -1
  19. package/dist/client/content/reactions.d.ts +11 -6
  20. package/dist/client/content/reactions.js +23 -18
  21. package/dist/client/content/reactions.js.map +1 -1
  22. package/dist/client/content/types.d.ts +40 -21
  23. package/dist/client/content/types.js +5 -2
  24. package/dist/client/content/types.js.map +1 -1
  25. package/dist/client/contents.d.ts +55 -0
  26. package/dist/client/contents.js +144 -0
  27. package/dist/client/contents.js.map +1 -0
  28. package/dist/client/discovery.d.ts +126 -0
  29. package/dist/client/discovery.js +125 -0
  30. package/dist/client/discovery.js.map +1 -0
  31. package/dist/client/generated/error-codes.d.ts +1 -1
  32. package/dist/client/generated/error-codes.js +12 -0
  33. package/dist/client/generated/error-codes.js.map +1 -1
  34. package/dist/client/generated/routes.d.ts +1244 -370
  35. package/dist/client/generated/routes.js +1870 -554
  36. package/dist/client/generated/routes.js.map +1 -1
  37. package/dist/client/generated/wire.d.ts +960 -134
  38. package/dist/client/generated/wire.js.map +1 -1
  39. package/dist/client/group-fields.d.ts +25 -0
  40. package/dist/client/group-fields.js +63 -0
  41. package/dist/client/group-fields.js.map +1 -0
  42. package/dist/client/groups.d.ts +58 -0
  43. package/dist/client/groups.js +119 -0
  44. package/dist/client/groups.js.map +1 -0
  45. package/dist/client/http.d.ts +24 -12
  46. package/dist/client/http.js +20 -24
  47. package/dist/client/http.js.map +1 -1
  48. package/dist/client/index.d.ts +11 -6
  49. package/dist/client/media/api.d.ts +1 -1
  50. package/dist/client/media/api.js +4 -2
  51. package/dist/client/media/api.js.map +1 -1
  52. package/dist/client/media/client.d.ts +8 -9
  53. package/dist/client/media/client.js +58 -71
  54. package/dist/client/media/client.js.map +1 -1
  55. package/dist/client/media/ref.d.ts +30 -0
  56. package/dist/client/media/ref.js +10 -3
  57. package/dist/client/media/ref.js.map +1 -1
  58. package/dist/client/offers.d.ts +41 -0
  59. package/dist/client/offers.js +66 -0
  60. package/dist/client/offers.js.map +1 -0
  61. package/dist/client/route.d.ts +2 -0
  62. package/dist/client/route.js +2 -1
  63. package/dist/client/route.js.map +1 -1
  64. package/dist/client/versions.d.ts +20 -0
  65. package/dist/client/versions.js +53 -0
  66. package/dist/client/versions.js.map +1 -0
  67. package/dist/client.js +35 -30
  68. package/dist/components/admin/content-manager.d.ts +80 -0
  69. package/dist/components/admin/content-manager.js +591 -0
  70. package/dist/components/admin/content-manager.js.map +1 -0
  71. package/dist/components/admin/group-manager.d.ts +24 -0
  72. package/dist/components/admin/group-manager.js +244 -0
  73. package/dist/components/admin/group-manager.js.map +1 -0
  74. package/dist/components/admin/media-item-panel.js +1 -1
  75. package/dist/components/admin/media-item-panel.js.map +1 -1
  76. package/dist/components/content/card.d.ts +44 -0
  77. package/dist/components/content/card.js +285 -0
  78. package/dist/components/content/card.js.map +1 -0
  79. package/dist/components/content/editors.d.ts +40 -0
  80. package/dist/components/content/editors.js +764 -0
  81. package/dist/components/content/editors.js.map +1 -0
  82. package/dist/components/content/filters.d.ts +28 -0
  83. package/dist/components/content/filters.js +134 -0
  84. package/dist/components/content/filters.js.map +1 -0
  85. package/dist/components/content/grid.d.ts +62 -0
  86. package/dist/components/content/grid.js +233 -0
  87. package/dist/components/content/grid.js.map +1 -0
  88. package/dist/components/content/index.d.ts +8 -0
  89. package/dist/components/content/listing.d.ts +20 -0
  90. package/dist/components/content/listing.js +150 -0
  91. package/dist/components/content/listing.js.map +1 -0
  92. package/dist/components/content/offer-fields.d.ts +8 -0
  93. package/dist/components/content/offer-fields.js +127 -0
  94. package/dist/components/content/offer-fields.js.map +1 -0
  95. package/dist/components/content/paywall.d.ts +53 -0
  96. package/dist/components/content/paywall.js +221 -0
  97. package/dist/components/content/paywall.js.map +1 -0
  98. package/dist/components/content/search.d.ts +27 -0
  99. package/dist/components/content/search.js +164 -0
  100. package/dist/components/content/search.js.map +1 -0
  101. package/dist/components/groups/banner.d.ts +51 -0
  102. package/dist/components/groups/banner.js +194 -0
  103. package/dist/components/groups/banner.js.map +1 -0
  104. package/dist/components/groups/directory.d.ts +37 -0
  105. package/dist/components/groups/directory.js +117 -0
  106. package/dist/components/groups/directory.js.map +1 -0
  107. package/dist/components/groups/editor.d.ts +44 -0
  108. package/dist/components/groups/editor.js +860 -0
  109. package/dist/components/groups/editor.js.map +1 -0
  110. package/dist/components/groups/fields.d.ts +13 -0
  111. package/dist/components/groups/fields.js +68 -0
  112. package/dist/components/groups/fields.js.map +1 -0
  113. package/dist/components/groups/header.d.ts +25 -0
  114. package/dist/components/groups/header.js +131 -0
  115. package/dist/components/groups/header.js.map +1 -0
  116. package/dist/components/groups/index.d.ts +6 -0
  117. package/dist/components/groups/memberships.d.ts +55 -0
  118. package/dist/components/groups/memberships.js +527 -0
  119. package/dist/components/groups/memberships.js.map +1 -0
  120. package/dist/components/groups/parts.d.ts +49 -0
  121. package/dist/components/groups/parts.js +172 -0
  122. package/dist/components/groups/parts.js.map +1 -0
  123. package/dist/components/media-folder-editor.js +153 -152
  124. package/dist/components/media-folder-editor.js.map +1 -1
  125. package/dist/components/social/comments.d.ts +7 -5
  126. package/dist/components/social/comments.js +6 -2
  127. package/dist/components/social/comments.js.map +1 -1
  128. package/dist/components/social/engagement.d.ts +4 -3
  129. package/dist/components/social/engagement.js.map +1 -1
  130. package/dist/components/social/moderation.d.ts +1 -1
  131. package/dist/components/social/moderation.js +3 -3
  132. package/dist/components/social/moderation.js.map +1 -1
  133. package/dist/editor/rich-text-editor.d.ts +4 -4
  134. package/dist/editor/rich-text-editor.js.map +1 -1
  135. package/dist/index.d.ts +4 -2
  136. package/dist/index.js +23 -9
  137. package/dist/locales/admin/de.js +5 -66
  138. package/dist/locales/admin/de.js.map +1 -1
  139. package/dist/locales/admin/en.d.ts +5 -66
  140. package/dist/locales/admin/en.js +19 -80
  141. package/dist/locales/admin/en.js.map +1 -1
  142. package/dist/locales/admin/es.js +5 -66
  143. package/dist/locales/admin/es.js.map +1 -1
  144. package/dist/locales/admin/ja.js +5 -66
  145. package/dist/locales/admin/ja.js.map +1 -1
  146. package/dist/locales/admin/ko.js +5 -66
  147. package/dist/locales/admin/ko.js.map +1 -1
  148. package/dist/locales/admin/zh.js +5 -66
  149. package/dist/locales/admin/zh.js.map +1 -1
  150. package/dist/locales/content/de.d.ts +2 -0
  151. package/dist/locales/content/de.js +98 -0
  152. package/dist/locales/content/de.js.map +1 -0
  153. package/dist/locales/content/en.d.ts +93 -0
  154. package/dist/locales/content/en.js +98 -0
  155. package/dist/locales/content/en.js.map +1 -0
  156. package/dist/locales/content/es.d.ts +2 -0
  157. package/dist/locales/content/es.js +98 -0
  158. package/dist/locales/content/es.js.map +1 -0
  159. package/dist/locales/content/ja.d.ts +2 -0
  160. package/dist/locales/content/ja.js +98 -0
  161. package/dist/locales/content/ja.js.map +1 -0
  162. package/dist/locales/content/ko.d.ts +2 -0
  163. package/dist/locales/content/ko.js +98 -0
  164. package/dist/locales/content/ko.js.map +1 -0
  165. package/dist/locales/content/zh.d.ts +2 -0
  166. package/dist/locales/content/zh.js +98 -0
  167. package/dist/locales/content/zh.js.map +1 -0
  168. package/dist/locales/de.js +11 -2
  169. package/dist/locales/de.js.map +1 -1
  170. package/dist/locales/discovery/de.d.ts +2 -0
  171. package/dist/locales/discovery/de.js +68 -0
  172. package/dist/locales/discovery/de.js.map +1 -0
  173. package/dist/locales/discovery/en.d.ts +63 -0
  174. package/dist/locales/discovery/en.js +68 -0
  175. package/dist/locales/discovery/en.js.map +1 -0
  176. package/dist/locales/discovery/es.d.ts +2 -0
  177. package/dist/locales/discovery/es.js +68 -0
  178. package/dist/locales/discovery/es.js.map +1 -0
  179. package/dist/locales/discovery/ja.d.ts +2 -0
  180. package/dist/locales/discovery/ja.js +68 -0
  181. package/dist/locales/discovery/ja.js.map +1 -0
  182. package/dist/locales/discovery/ko.d.ts +2 -0
  183. package/dist/locales/discovery/ko.js +68 -0
  184. package/dist/locales/discovery/ko.js.map +1 -0
  185. package/dist/locales/discovery/zh.d.ts +2 -0
  186. package/dist/locales/discovery/zh.js +68 -0
  187. package/dist/locales/discovery/zh.js.map +1 -0
  188. package/dist/locales/en.d.ts +288 -67
  189. package/dist/locales/en.js +11 -2
  190. package/dist/locales/en.js.map +1 -1
  191. package/dist/locales/es.js +11 -2
  192. package/dist/locales/es.js.map +1 -1
  193. package/dist/locales/groups/de.d.ts +2 -0
  194. package/dist/locales/groups/de.js +133 -0
  195. package/dist/locales/groups/de.js.map +1 -0
  196. package/dist/locales/groups/en.d.ts +128 -0
  197. package/dist/locales/groups/en.js +133 -0
  198. package/dist/locales/groups/en.js.map +1 -0
  199. package/dist/locales/groups/es.d.ts +2 -0
  200. package/dist/locales/groups/es.js +133 -0
  201. package/dist/locales/groups/es.js.map +1 -0
  202. package/dist/locales/groups/ja.d.ts +2 -0
  203. package/dist/locales/groups/ja.js +133 -0
  204. package/dist/locales/groups/ja.js.map +1 -0
  205. package/dist/locales/groups/ko.d.ts +2 -0
  206. package/dist/locales/groups/ko.js +133 -0
  207. package/dist/locales/groups/ko.js.map +1 -0
  208. package/dist/locales/groups/zh.d.ts +2 -0
  209. package/dist/locales/groups/zh.js +133 -0
  210. package/dist/locales/groups/zh.js.map +1 -0
  211. package/dist/locales/ja.js +11 -2
  212. package/dist/locales/ja.js.map +1 -1
  213. package/dist/locales/ko.js +11 -2
  214. package/dist/locales/ko.js.map +1 -1
  215. package/dist/locales/social/de.js +2 -1
  216. package/dist/locales/social/de.js.map +1 -1
  217. package/dist/locales/social/en.d.ts +2 -1
  218. package/dist/locales/social/en.js +2 -1
  219. package/dist/locales/social/en.js.map +1 -1
  220. package/dist/locales/social/es.js +2 -1
  221. package/dist/locales/social/es.js.map +1 -1
  222. package/dist/locales/social/ja.js +2 -1
  223. package/dist/locales/social/ja.js.map +1 -1
  224. package/dist/locales/social/ko.js +2 -1
  225. package/dist/locales/social/ko.js.map +1 -1
  226. package/dist/locales/social/zh.js +2 -1
  227. package/dist/locales/social/zh.js.map +1 -1
  228. package/dist/locales/zh.js +11 -2
  229. package/dist/locales/zh.js.map +1 -1
  230. package/dist/react/admin.d.ts +1 -1
  231. package/dist/react/admin.js +8 -6
  232. package/dist/react/admin.js.map +1 -1
  233. package/dist/react/comments.d.ts +9 -7
  234. package/dist/react/comments.js +42 -27
  235. package/dist/react/comments.js.map +1 -1
  236. package/dist/react/contents.d.ts +57 -0
  237. package/dist/react/contents.js +214 -0
  238. package/dist/react/contents.js.map +1 -0
  239. package/dist/react/context.d.ts +1 -1
  240. package/dist/react/context.js.map +1 -1
  241. package/dist/react/crop.js +45 -44
  242. package/dist/react/crop.js.map +1 -1
  243. package/dist/react/detail.d.ts +30 -0
  244. package/dist/react/detail.js +42 -0
  245. package/dist/react/detail.js.map +1 -0
  246. package/dist/react/discovery.d.ts +138 -0
  247. package/dist/react/discovery.js +299 -0
  248. package/dist/react/discovery.js.map +1 -0
  249. package/dist/react/engagement.d.ts +5 -5
  250. package/dist/react/engagement.js +55 -41
  251. package/dist/react/engagement.js.map +1 -1
  252. package/dist/react/folder.js +54 -53
  253. package/dist/react/folder.js.map +1 -1
  254. package/dist/react/groups.d.ts +115 -0
  255. package/dist/react/groups.js +229 -0
  256. package/dist/react/groups.js.map +1 -0
  257. package/dist/react/index.d.ts +5 -1
  258. package/dist/react/public.d.ts +6 -5
  259. package/dist/react/public.js +19 -22
  260. package/dist/react/public.js.map +1 -1
  261. package/dist/react/read.js +105 -106
  262. package/dist/react/read.js.map +1 -1
  263. package/dist/react/resources.d.ts +2 -2
  264. package/dist/react/resources.js +69 -40
  265. package/dist/react/resources.js.map +1 -1
  266. package/dist/react/store.js +41 -39
  267. package/dist/react/store.js.map +1 -1
  268. package/dist/react/upload.js +54 -53
  269. package/dist/react/upload.js.map +1 -1
  270. package/dist/react/use-resource.d.ts +2 -1
  271. package/dist/react/use-resource.js +1 -0
  272. package/dist/react/use-resource.js.map +1 -1
  273. package/dist/react/versions.d.ts +57 -0
  274. package/dist/react/versions.js +94 -0
  275. package/dist/react/versions.js.map +1 -0
  276. package/dist/react/video.js +87 -86
  277. package/dist/react/video.js.map +1 -1
  278. package/dist/react.js +9 -5
  279. package/dist/styles.css +1 -1
  280. package/package.json +1 -1
  281. package/dist/client/content/posts.d.ts +0 -52
  282. package/dist/client/content/posts.js +0 -84
  283. package/dist/client/content/posts.js.map +0 -1
  284. package/dist/client/taxonomy.d.ts +0 -34
  285. package/dist/client/taxonomy.js +0 -84
  286. package/dist/client/taxonomy.js.map +0 -1
  287. package/dist/components/admin/post-manager.d.ts +0 -45
  288. package/dist/components/admin/post-manager.js +0 -602
  289. package/dist/components/admin/post-manager.js.map +0 -1
  290. package/dist/components/admin/taxonomy-manager.d.ts +0 -54
  291. package/dist/components/admin/taxonomy-manager.js +0 -976
  292. package/dist/components/admin/taxonomy-manager.js.map +0 -1
  293. package/dist/react/posts.d.ts +0 -61
  294. package/dist/react/posts.js +0 -183
  295. package/dist/react/posts.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and posts to approve or reject; and the site's\n * comment bans. Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = { comments: t(\"moderation.all\"), queue: t(\"moderation.queue\"), bans: t(\"moderation.bans\") };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList>\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s}>\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"post\" ? \"post\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"post\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"post\" ? \"moderation.queuePosts\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and content to approve or reject; and the site's\n * comment bans. Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = { comments: t(\"moderation.all\"), queue: t(\"moderation.queue\"), bans: t(\"moderation.bans\") };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList>\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s}>\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"content\" ? \"content\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"content\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"content\" ? \"moderation.queueContent\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { PostEditorProps } from '../components/admin/post-manager.js';
3
- export type RichTextEditorProps = PostEditorProps & {
2
+ import { BodyEditorProps } from '../components/admin/content-manager.js';
3
+ export type RichTextEditorProps = BodyEditorProps & {
4
4
  className?: string;
5
5
  };
6
6
  /**
@@ -9,5 +9,5 @@ export type RichTextEditorProps = PostEditorProps & {
9
9
  * HTML.
10
10
  */
11
11
  export declare function RichTextEditor({ value, onChange, uploadImage, disabled, placeholder, className, ...aria }: RichTextEditorProps): import('react').JSX.Element;
12
- /** A render prop for PostManager: `renderEditor={richTextEditor}`. */
13
- export declare function richTextEditor(props: PostEditorProps): ReactNode;
12
+ /** A render prop for ContentManager: `renderEditor={richTextEditor}`. */
13
+ export declare function richTextEditor(props: BodyEditorProps): ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/editor/rich-text-editor.tsx"],"sourcesContent":["import {\n ArrowTurnBackwardIcon,\n ArrowTurnForwardIcon,\n Heading02Icon,\n Heading03Icon,\n Image01Icon,\n LeftToRightListBulletIcon,\n LeftToRightListNumberIcon,\n Link01Icon,\n QuoteDownIcon,\n TextBoldIcon,\n TextItalicIcon,\n TextStrikethroughIcon,\n TextUnderlineIcon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport Image from \"@tiptap/extension-image\";\nimport { Placeholder } from \"@tiptap/extensions\";\nimport { EditorContent, useEditor, useEditorState, type Editor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { cn } from \"cn\";\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { PostEditorProps } from \"../components/admin/post-manager.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { ErrorLine } from \"../components/social/parts.js\";\n\nexport type RichTextEditorProps = PostEditorProps & {\n className?: string;\n};\n\nconst content =\n \"min-h-48 px-3 py-2 outline-none break-words [&_p]:my-2 [&_h2]:mt-4 [&_h2]:mb-2 [&_h2]:text-lg [&_h2]:font-semibold [&_h3]:mt-3 [&_h3]:mb-1.5 [&_h3]:font-semibold \" +\n \"[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6 [&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground \" +\n \"[&_a]:text-primary [&_a]:underline [&_img]:my-2 [&_img]:max-h-96 [&_img]:max-w-full [&_img]:rounded-md [&_img.ProseMirror-selectednode]:ring-2 [&_img.ProseMirror-selectednode]:ring-ring \" +\n \"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_pre]:my-2 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 \" +\n \"[&_p.is-editor-empty:first-child]:before:pointer-events-none [&_p.is-editor-empty:first-child]:before:float-left [&_p.is-editor-empty:first-child]:before:h-0 [&_p.is-editor-empty:first-child]:before:text-muted-foreground [&_p.is-editor-empty:first-child]:before:content-[attr(data-placeholder)]\";\n\n/**\n * A rich-text field over Tiptap: headings, emphasis, lists, quotes, links\n * and images (picked, pasted or dropped, through uploadImage). Its value is\n * HTML.\n */\nexport function RichTextEditor({ value, onChange, uploadImage, disabled, placeholder, className, ...aria }: RichTextEditorProps) {\n const { t } = useMessages();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [uploading, setUploading] = useState(0);\n const upload = useRef(uploadImage);\n const change = useRef(onChange);\n useEffect(() => {\n upload.current = uploadImage;\n change.current = onChange;\n });\n const insertImages = async (ed: Editor, files: File[], pos?: number) => {\n if (!upload.current) return;\n setError(null);\n for (const file of files) {\n setUploading((n) => n + 1);\n try {\n const src = await upload.current(file);\n const chain = ed.chain().focus();\n (pos === undefined ? chain : chain.setTextSelection(pos)).setImage({ src, alt: \"\" }).run();\n } catch (e) {\n setError(toContentKitError(e));\n } finally {\n setUploading((n) => n - 1);\n }\n }\n };\n const images = (list: FileList | null | undefined) => [...(list ?? [])].filter((f) => f.type.startsWith(\"image/\"));\n const editor = useEditor({\n extensions: [\n StarterKit.configure({ heading: { levels: [2, 3] }, link: { openOnClick: false, autolink: true, defaultProtocol: \"https\" } }),\n Image.configure({ inline: false }),\n Placeholder.configure({ placeholder: placeholder ?? t(\"editor.placeholder\") }),\n ],\n content: value,\n editable: !disabled,\n immediatelyRender: false,\n editorProps: {\n attributes: { class: content, role: \"textbox\", \"aria-multiline\": \"true\", ...(aria[\"aria-label\"] ? { \"aria-label\": aria[\"aria-label\"] } : {}) },\n handlePaste: (view, event) => {\n const files = images(event.clipboardData?.files);\n if (!files.length || !upload.current || !editorRef.current) return false;\n void insertImages(editorRef.current, files);\n return true;\n },\n handleDrop: (view, event) => {\n const files = images((event as DragEvent).dataTransfer?.files);\n if (!files.length || !upload.current || !editorRef.current) return false;\n const at = view.posAtCoords({ left: event.clientX, top: event.clientY });\n void insertImages(editorRef.current, files, at?.pos);\n return true;\n },\n },\n onUpdate: ({ editor }) => change.current(editor.isEmpty ? \"\" : editor.getHTML()),\n });\n const editorRef = useRef<Editor | null>(null);\n editorRef.current = editor;\n // A value from outside (a load, a reset) replaces the document.\n useEffect(() => {\n if (!editor) return;\n const html = editor.isEmpty ? \"\" : editor.getHTML();\n if (value !== html) editor.commands.setContent(value, { emitUpdate: false });\n }, [editor, value]);\n useEffect(() => {\n editor?.setEditable(!disabled);\n }, [editor, disabled]);\n return (\n <div className={cn(\"grid overflow-hidden rounded-lg border border-input bg-background focus-within:ring-2 focus-within:ring-ring/50\", className)} data-ckui=\"rich-text-editor\">\n {editor && <Toolbar editor={editor} disabled={disabled} canUpload={!!uploadImage} uploading={uploading > 0} onImages={(files) => void insertImages(editor, files)} />}\n <EditorContent editor={editor} />\n {error && <ErrorLine error={error} className=\"px-3 pb-2\" />}\n {uploading > 0 && (\n <p className=\"px-3 pb-2 text-xs text-muted-foreground\" role=\"status\">\n {t(\"editor.uploading\")}\n </p>\n )}\n </div>\n );\n}\n\n/** A render prop for PostManager: `renderEditor={richTextEditor}`. */\nexport function richTextEditor(props: PostEditorProps): ReactNode {\n return <RichTextEditor {...props} />;\n}\n\nfunction Toolbar({ editor, disabled, canUpload, uploading, onImages }: { editor: Editor; disabled?: boolean; canUpload: boolean; uploading: boolean; onImages: (files: File[]) => void }) {\n const { t } = useMessages();\n const id = useId();\n const file = useRef<HTMLInputElement>(null);\n const [linking, setLinking] = useState<string | null>(null);\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n bold: e.isActive(\"bold\"),\n italic: e.isActive(\"italic\"),\n underline: e.isActive(\"underline\"),\n strike: e.isActive(\"strike\"),\n h2: e.isActive(\"heading\", { level: 2 }),\n h3: e.isActive(\"heading\", { level: 3 }),\n bullet: e.isActive(\"bulletList\"),\n ordered: e.isActive(\"orderedList\"),\n quote: e.isActive(\"blockquote\"),\n link: e.isActive(\"link\"),\n href: (e.getAttributes(\"link\").href as string | undefined) ?? \"\",\n undo: e.can().undo(),\n redo: e.can().redo(),\n }),\n });\n const tool = (label: string, icon: IconSvgElement, active: boolean | undefined, run: () => void, enabled = true) => (\n <Button\n key={label}\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n className={cn(active && \"bg-accent text-accent-foreground\")}\n aria-label={label}\n title={label}\n aria-pressed={active === undefined ? undefined : active}\n disabled={disabled || !enabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={run}\n >\n <HugeiconsIcon icon={icon} />\n </Button>\n );\n const c = () => editor.chain().focus();\n const saveLink = () => {\n const href = linking?.trim();\n if (href) c().extendMarkRange(\"link\").setLink({ href }).run();\n else c().extendMarkRange(\"link\").unsetLink().run();\n setLinking(null);\n };\n return (\n <div className=\"grid gap-1 border-b border-border bg-muted/40 p-1\">\n <div className=\"flex flex-wrap items-center gap-0.5\" role=\"toolbar\" aria-label={t(\"editor.toolbar\")}>\n {tool(t(\"editor.bold\"), TextBoldIcon, state.bold, () => c().toggleBold().run())}\n {tool(t(\"editor.italic\"), TextItalicIcon, state.italic, () => c().toggleItalic().run())}\n {tool(t(\"editor.underline\"), TextUnderlineIcon, state.underline, () => c().toggleUnderline().run())}\n {tool(t(\"editor.strike\"), TextStrikethroughIcon, state.strike, () => c().toggleStrike().run())}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.heading2\"), Heading02Icon, state.h2, () => c().toggleHeading({ level: 2 }).run())}\n {tool(t(\"editor.heading3\"), Heading03Icon, state.h3, () => c().toggleHeading({ level: 3 }).run())}\n {tool(t(\"editor.bulletList\"), LeftToRightListBulletIcon, state.bullet, () => c().toggleBulletList().run())}\n {tool(t(\"editor.orderedList\"), LeftToRightListNumberIcon, state.ordered, () => c().toggleOrderedList().run())}\n {tool(t(\"editor.quote\"), QuoteDownIcon, state.quote, () => c().toggleBlockquote().run())}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.link\"), Link01Icon, state.link, () => setLinking(state.href))}\n {canUpload && tool(t(\"editor.image\"), Image01Icon, undefined, () => file.current?.click(), !uploading)}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.undo\"), ArrowTurnBackwardIcon, undefined, () => c().undo().run(), state.undo)}\n {tool(t(\"editor.redo\"), ArrowTurnForwardIcon, undefined, () => c().redo().run(), state.redo)}\n <input\n ref={file}\n type=\"file\"\n accept=\"image/*\"\n multiple\n hidden\n aria-label={t(\"editor.image\")}\n onChange={(e) => {\n const files = [...(e.target.files ?? [])];\n e.target.value = \"\";\n if (files.length) onImages(files);\n }}\n />\n </div>\n {linking !== null && (\n <form\n className=\"flex flex-wrap items-center gap-2 px-1 pb-1\"\n onSubmit={(e) => {\n e.preventDefault();\n saveLink();\n }}\n >\n <label htmlFor={`${id}-href`} className=\"sr-only\">\n {t(\"editor.linkUrl\")}\n </label>\n <Input id={`${id}-href`} className=\"h-8 min-w-48 flex-1\" type=\"url\" placeholder=\"https://\" value={linking} autoFocus onChange={(e) => setLinking(e.target.value)} />\n <Button type=\"submit\" size=\"sm\">\n {t(linking.trim() ? \"editor.applyLink\" : \"editor.removeLink\")}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setLinking(null)}>\n {t(\"common.cancel\")}\n </Button>\n </form>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/editor/rich-text-editor.tsx"],"sourcesContent":["import {\n ArrowTurnBackwardIcon,\n ArrowTurnForwardIcon,\n Heading02Icon,\n Heading03Icon,\n Image01Icon,\n LeftToRightListBulletIcon,\n LeftToRightListNumberIcon,\n Link01Icon,\n QuoteDownIcon,\n TextBoldIcon,\n TextItalicIcon,\n TextStrikethroughIcon,\n TextUnderlineIcon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport Image from \"@tiptap/extension-image\";\nimport { Placeholder } from \"@tiptap/extensions\";\nimport { EditorContent, useEditor, useEditorState, type Editor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { cn } from \"cn\";\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { BodyEditorProps } from \"../components/admin/content-manager.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { ErrorLine } from \"../components/social/parts.js\";\n\nexport type RichTextEditorProps = BodyEditorProps & {\n className?: string;\n};\n\nconst content =\n \"min-h-48 px-3 py-2 outline-none break-words [&_p]:my-2 [&_h2]:mt-4 [&_h2]:mb-2 [&_h2]:text-lg [&_h2]:font-semibold [&_h3]:mt-3 [&_h3]:mb-1.5 [&_h3]:font-semibold \" +\n \"[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6 [&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground \" +\n \"[&_a]:text-primary [&_a]:underline [&_img]:my-2 [&_img]:max-h-96 [&_img]:max-w-full [&_img]:rounded-md [&_img.ProseMirror-selectednode]:ring-2 [&_img.ProseMirror-selectednode]:ring-ring \" +\n \"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_pre]:my-2 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 \" +\n \"[&_p.is-editor-empty:first-child]:before:pointer-events-none [&_p.is-editor-empty:first-child]:before:float-left [&_p.is-editor-empty:first-child]:before:h-0 [&_p.is-editor-empty:first-child]:before:text-muted-foreground [&_p.is-editor-empty:first-child]:before:content-[attr(data-placeholder)]\";\n\n/**\n * A rich-text field over Tiptap: headings, emphasis, lists, quotes, links\n * and images (picked, pasted or dropped, through uploadImage). Its value is\n * HTML.\n */\nexport function RichTextEditor({ value, onChange, uploadImage, disabled, placeholder, className, ...aria }: RichTextEditorProps) {\n const { t } = useMessages();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [uploading, setUploading] = useState(0);\n const upload = useRef(uploadImage);\n const change = useRef(onChange);\n useEffect(() => {\n upload.current = uploadImage;\n change.current = onChange;\n });\n const insertImages = async (ed: Editor, files: File[], pos?: number) => {\n if (!upload.current) return;\n setError(null);\n for (const file of files) {\n setUploading((n) => n + 1);\n try {\n const src = await upload.current(file);\n const chain = ed.chain().focus();\n (pos === undefined ? chain : chain.setTextSelection(pos)).setImage({ src, alt: \"\" }).run();\n } catch (e) {\n setError(toContentKitError(e));\n } finally {\n setUploading((n) => n - 1);\n }\n }\n };\n const images = (list: FileList | null | undefined) => [...(list ?? [])].filter((f) => f.type.startsWith(\"image/\"));\n const editor = useEditor({\n extensions: [\n StarterKit.configure({ heading: { levels: [2, 3] }, link: { openOnClick: false, autolink: true, defaultProtocol: \"https\" } }),\n Image.configure({ inline: false }),\n Placeholder.configure({ placeholder: placeholder ?? t(\"editor.placeholder\") }),\n ],\n content: value,\n editable: !disabled,\n immediatelyRender: false,\n editorProps: {\n attributes: { class: content, role: \"textbox\", \"aria-multiline\": \"true\", ...(aria[\"aria-label\"] ? { \"aria-label\": aria[\"aria-label\"] } : {}) },\n handlePaste: (view, event) => {\n const files = images(event.clipboardData?.files);\n if (!files.length || !upload.current || !editorRef.current) return false;\n void insertImages(editorRef.current, files);\n return true;\n },\n handleDrop: (view, event) => {\n const files = images((event as DragEvent).dataTransfer?.files);\n if (!files.length || !upload.current || !editorRef.current) return false;\n const at = view.posAtCoords({ left: event.clientX, top: event.clientY });\n void insertImages(editorRef.current, files, at?.pos);\n return true;\n },\n },\n onUpdate: ({ editor }) => change.current(editor.isEmpty ? \"\" : editor.getHTML()),\n });\n const editorRef = useRef<Editor | null>(null);\n editorRef.current = editor;\n // A value from outside (a load, a reset) replaces the document.\n useEffect(() => {\n if (!editor) return;\n const html = editor.isEmpty ? \"\" : editor.getHTML();\n if (value !== html) editor.commands.setContent(value, { emitUpdate: false });\n }, [editor, value]);\n useEffect(() => {\n editor?.setEditable(!disabled);\n }, [editor, disabled]);\n return (\n <div className={cn(\"grid overflow-hidden rounded-lg border border-input bg-background focus-within:ring-2 focus-within:ring-ring/50\", className)} data-ckui=\"rich-text-editor\">\n {editor && <Toolbar editor={editor} disabled={disabled} canUpload={!!uploadImage} uploading={uploading > 0} onImages={(files) => void insertImages(editor, files)} />}\n <EditorContent editor={editor} />\n {error && <ErrorLine error={error} className=\"px-3 pb-2\" />}\n {uploading > 0 && (\n <p className=\"px-3 pb-2 text-xs text-muted-foreground\" role=\"status\">\n {t(\"editor.uploading\")}\n </p>\n )}\n </div>\n );\n}\n\n/** A render prop for ContentManager: `renderEditor={richTextEditor}`. */\nexport function richTextEditor(props: BodyEditorProps): ReactNode {\n return <RichTextEditor {...props} />;\n}\n\nfunction Toolbar({ editor, disabled, canUpload, uploading, onImages }: { editor: Editor; disabled?: boolean; canUpload: boolean; uploading: boolean; onImages: (files: File[]) => void }) {\n const { t } = useMessages();\n const id = useId();\n const file = useRef<HTMLInputElement>(null);\n const [linking, setLinking] = useState<string | null>(null);\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n bold: e.isActive(\"bold\"),\n italic: e.isActive(\"italic\"),\n underline: e.isActive(\"underline\"),\n strike: e.isActive(\"strike\"),\n h2: e.isActive(\"heading\", { level: 2 }),\n h3: e.isActive(\"heading\", { level: 3 }),\n bullet: e.isActive(\"bulletList\"),\n ordered: e.isActive(\"orderedList\"),\n quote: e.isActive(\"blockquote\"),\n link: e.isActive(\"link\"),\n href: (e.getAttributes(\"link\").href as string | undefined) ?? \"\",\n undo: e.can().undo(),\n redo: e.can().redo(),\n }),\n });\n const tool = (label: string, icon: IconSvgElement, active: boolean | undefined, run: () => void, enabled = true) => (\n <Button\n key={label}\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n className={cn(active && \"bg-accent text-accent-foreground\")}\n aria-label={label}\n title={label}\n aria-pressed={active === undefined ? undefined : active}\n disabled={disabled || !enabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={run}\n >\n <HugeiconsIcon icon={icon} />\n </Button>\n );\n const c = () => editor.chain().focus();\n const saveLink = () => {\n const href = linking?.trim();\n if (href) c().extendMarkRange(\"link\").setLink({ href }).run();\n else c().extendMarkRange(\"link\").unsetLink().run();\n setLinking(null);\n };\n return (\n <div className=\"grid gap-1 border-b border-border bg-muted/40 p-1\">\n <div className=\"flex flex-wrap items-center gap-0.5\" role=\"toolbar\" aria-label={t(\"editor.toolbar\")}>\n {tool(t(\"editor.bold\"), TextBoldIcon, state.bold, () => c().toggleBold().run())}\n {tool(t(\"editor.italic\"), TextItalicIcon, state.italic, () => c().toggleItalic().run())}\n {tool(t(\"editor.underline\"), TextUnderlineIcon, state.underline, () => c().toggleUnderline().run())}\n {tool(t(\"editor.strike\"), TextStrikethroughIcon, state.strike, () => c().toggleStrike().run())}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.heading2\"), Heading02Icon, state.h2, () => c().toggleHeading({ level: 2 }).run())}\n {tool(t(\"editor.heading3\"), Heading03Icon, state.h3, () => c().toggleHeading({ level: 3 }).run())}\n {tool(t(\"editor.bulletList\"), LeftToRightListBulletIcon, state.bullet, () => c().toggleBulletList().run())}\n {tool(t(\"editor.orderedList\"), LeftToRightListNumberIcon, state.ordered, () => c().toggleOrderedList().run())}\n {tool(t(\"editor.quote\"), QuoteDownIcon, state.quote, () => c().toggleBlockquote().run())}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.link\"), Link01Icon, state.link, () => setLinking(state.href))}\n {canUpload && tool(t(\"editor.image\"), Image01Icon, undefined, () => file.current?.click(), !uploading)}\n <span className=\"mx-1 h-5 w-px bg-border\" aria-hidden />\n {tool(t(\"editor.undo\"), ArrowTurnBackwardIcon, undefined, () => c().undo().run(), state.undo)}\n {tool(t(\"editor.redo\"), ArrowTurnForwardIcon, undefined, () => c().redo().run(), state.redo)}\n <input\n ref={file}\n type=\"file\"\n accept=\"image/*\"\n multiple\n hidden\n aria-label={t(\"editor.image\")}\n onChange={(e) => {\n const files = [...(e.target.files ?? [])];\n e.target.value = \"\";\n if (files.length) onImages(files);\n }}\n />\n </div>\n {linking !== null && (\n <form\n className=\"flex flex-wrap items-center gap-2 px-1 pb-1\"\n onSubmit={(e) => {\n e.preventDefault();\n saveLink();\n }}\n >\n <label htmlFor={`${id}-href`} className=\"sr-only\">\n {t(\"editor.linkUrl\")}\n </label>\n <Input id={`${id}-href`} className=\"h-8 min-w-48 flex-1\" type=\"url\" placeholder=\"https://\" value={linking} autoFocus onChange={(e) => setLinking(e.target.value)} />\n <Button type=\"submit\" size=\"sm\">\n {t(linking.trim() ? \"editor.applyLink\" : \"editor.removeLink\")}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setLinking(null)}>\n {t(\"common.cancel\")}\n </Button>\n </form>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiCA,IAAM,IACJ;AAWF,SAAgB,EAAe,EAAE,UAAO,aAAU,gBAAa,aAAU,gBAAa,cAAW,GAAG,KAA6B;CAC/H,IAAM,EAAE,SAAM,EAAY,GACpB,CAAC,GAAO,KAAY,EAAiC,IAAI,GACzD,CAAC,GAAW,KAAgB,EAAS,CAAC,GACtC,IAAS,EAAO,CAAW,GAC3B,IAAS,EAAO,CAAQ;CAC9B,QAAgB;EAEd,AADA,EAAO,UAAU,GACjB,EAAO,UAAU;CACnB,CAAC;CACD,IAAM,IAAe,OAAO,GAAY,GAAe,MAAiB;EACjE,MAAO,SACZ;KAAS,IAAI;GACb,KAAK,IAAM,KAAQ,GAAO;IACxB,GAAc,MAAM,IAAI,CAAC;IACzB,IAAI;KACF,IAAM,IAAM,MAAM,EAAO,QAAQ,CAAI,GAC/B,IAAQ,EAAG,MAAM,CAAC,CAAC,MAAM;KAC/B,CAAC,MAAQ,KAAA,IAAY,IAAQ,EAAM,iBAAiB,CAAG,EAAA,CAAG,SAAS;MAAE;MAAK,KAAK;KAAG,CAAC,CAAC,CAAC,IAAI;IAC3F,SAAS,GAAG;KACV,EAAS,EAAkB,CAAC,CAAC;IAC/B,UAAU;KACR,GAAc,MAAM,IAAI,CAAC;IAC3B;GACF;EAZa;CAaf,GACM,KAAU,MAAsC,CAAC,GAAI,KAAQ,CAAC,CAAE,CAAC,CAAC,QAAQ,MAAM,EAAE,KAAK,WAAW,QAAQ,CAAC,GAC3G,IAAS,EAAU;EACvB,YAAY;GACV,EAAW,UAAU;IAAE,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE;IAAG,MAAM;KAAE,aAAa;KAAO,UAAU;KAAM,iBAAiB;IAAQ;GAAE,CAAC;GAC5H,EAAM,UAAU,EAAE,QAAQ,GAAM,CAAC;GACjC,EAAY,UAAU,EAAE,aAAa,KAAe,EAAE,oBAAoB,EAAE,CAAC;EAC/E;EACA,SAAS;EACT,UAAU,CAAC;EACX,mBAAmB;EACnB,aAAa;GACX,YAAY;IAAE,OAAO;IAAS,MAAM;IAAW,kBAAkB;IAAQ,GAAI,EAAK,gBAAgB,EAAE,cAAc,EAAK,cAAc,IAAI,CAAC;GAAG;GAC7I,cAAc,GAAM,MAAU;IAC5B,IAAM,IAAQ,EAAO,EAAM,eAAe,KAAK;IAG/C,OAFI,CAAC,EAAM,UAAU,CAAC,EAAO,WAAW,CAAC,EAAU,UAAgB,MACnE,EAAkB,EAAU,SAAS,CAAK,GACnC;GACT;GACA,aAAa,GAAM,MAAU;IAC3B,IAAM,IAAQ,EAAQ,EAAoB,cAAc,KAAK;IAC7D,IAAI,CAAC,EAAM,UAAU,CAAC,EAAO,WAAW,CAAC,EAAU,SAAS,OAAO;IACnE,IAAM,IAAK,EAAK,YAAY;KAAE,MAAM,EAAM;KAAS,KAAK,EAAM;IAAQ,CAAC;IAEvE,OADA,EAAkB,EAAU,SAAS,GAAO,GAAI,GAAG,GAC5C;GACT;EACF;EACA,WAAW,EAAE,gBAAa,EAAO,QAAQ,EAAO,UAAU,KAAK,EAAO,QAAQ,CAAC;CACjF,CAAC,GACK,IAAY,EAAsB,IAAI;CAW5C,OAVA,EAAU,UAAU,GAEpB,QAAgB;EACT,KAED,OADS,EAAO,UAAU,KAAK,EAAO,QAAQ,MAC9B,EAAO,SAAS,WAAW,GAAO,EAAE,YAAY,GAAM,CAAC;CAC7E,GAAG,CAAC,GAAQ,CAAK,CAAC,GAClB,QAAgB;EACd,GAAQ,YAAY,CAAC,CAAQ;CAC/B,GAAG,CAAC,GAAQ,CAAQ,CAAC,GAEnB,kBAAC,OAAD;EAAK,WAAW,EAAG,mHAAmH,CAAS;EAAG,aAAU;EAA5J,UAAA;GACG,KAAU,kBAAC,GAAD;IAAiB;IAAkB;IAAU,WAAW,CAAC,CAAC;IAAa,WAAW,IAAY;IAAG,WAAW,MAAU,KAAK,EAAa,GAAQ,CAAK;GAAI,CAAA;GACpK,kBAAC,GAAD,EAAuB,UAAS,CAAA;GAC/B,KAAS,kBAAC,GAAD;IAAkB;IAAO,WAAU;GAAa,CAAA;GACzD,IAAY,KACX,kBAAC,KAAD;IAAG,WAAU;IAA0C,MAAK;IACzD,UAAA,EAAE,kBAAkB;GACpB,CAAA;EAEF;;AAET;AAGA,SAAgB,EAAe,GAAmC;CAChE,OAAO,kBAAC,GAAD,EAAgB,GAAI,EAAQ,CAAA;AACrC;AAEA,SAAS,EAAQ,EAAE,WAAQ,aAAU,cAAW,cAAW,eAA+H;CACxL,IAAM,EAAE,SAAM,EAAY,GACpB,IAAK,EAAM,GACX,IAAO,EAAyB,IAAI,GACpC,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,IAAQ,EAAe;EAC3B;EACA,WAAW,EAAE,QAAQ,SAAS;GAC5B,MAAM,EAAE,SAAS,MAAM;GACvB,QAAQ,EAAE,SAAS,QAAQ;GAC3B,WAAW,EAAE,SAAS,WAAW;GACjC,QAAQ,EAAE,SAAS,QAAQ;GAC3B,IAAI,EAAE,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;GACtC,IAAI,EAAE,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;GACtC,QAAQ,EAAE,SAAS,YAAY;GAC/B,SAAS,EAAE,SAAS,aAAa;GACjC,OAAO,EAAE,SAAS,YAAY;GAC9B,MAAM,EAAE,SAAS,MAAM;GACvB,MAAO,EAAE,cAAc,MAAM,CAAC,CAAC,QAA+B;GAC9D,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK;GACnB,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK;EACrB;CACF,CAAC,GACK,KAAQ,GAAe,GAAsB,GAA6B,GAAiB,IAAU,OACzG,kBAAC,GAAD;EAEE,MAAK;EACL,MAAK;EACL,SAAQ;EACR,WAAW,EAAG,KAAU,kCAAkC;EAC1D,cAAY;EACZ,OAAO;EACP,gBAAc,MAAW,KAAA,IAAY,KAAA,IAAY;EACjD,UAAU,KAAY,CAAC;EACvB,cAAc,MAAM,EAAE,eAAe;EACrC,SAAS;EAET,UAAA,kBAAC,GAAD,EAAqB,QAAO,CAAA;CACtB,GAbD,CAaC,GAEJ,UAAU,EAAO,MAAM,CAAC,CAAC,MAAM,GAC/B,UAAiB;EACrB,IAAM,IAAO,GAAS,KAAK;EAG3B,AAFI,IAAM,EAAE,CAAC,CAAC,gBAAgB,MAAM,CAAC,CAAC,QAAQ,EAAE,QAAK,CAAC,CAAC,CAAC,IAAI,IACvD,EAAE,CAAC,CAAC,gBAAgB,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,GACjD,EAAW,IAAI;CACjB;CACA,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;GAAsC,MAAK;GAAU,cAAY,EAAE,gBAAgB;GAAlG,UAAA;IACG,EAAK,EAAE,aAAa,GAAG,GAAc,EAAM,YAAY,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;IAC7E,EAAK,EAAE,eAAe,GAAG,GAAgB,EAAM,cAAc,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC;IACrF,EAAK,EAAE,kBAAkB,GAAG,GAAmB,EAAM,iBAAiB,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC;IACjG,EAAK,EAAE,eAAe,GAAG,GAAuB,EAAM,cAAc,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC;IAC7F,kBAAC,QAAD;KAAM,WAAU;KAA0B,eAAA;IAAa,CAAA;IACtD,EAAK,EAAE,iBAAiB,GAAG,GAAe,EAAM,UAAU,EAAE,CAAC,CAAC,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/F,EAAK,EAAE,iBAAiB,GAAG,GAAe,EAAM,UAAU,EAAE,CAAC,CAAC,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/F,EAAK,EAAE,mBAAmB,GAAG,GAA2B,EAAM,cAAc,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC;IACxG,EAAK,EAAE,oBAAoB,GAAG,GAA2B,EAAM,eAAe,EAAE,CAAC,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC;IAC3G,EAAK,EAAE,cAAc,GAAG,GAAe,EAAM,aAAa,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC;IACvF,kBAAC,QAAD;KAAM,WAAU;KAA0B,eAAA;IAAa,CAAA;IACtD,EAAK,EAAE,aAAa,GAAG,GAAY,EAAM,YAAY,EAAW,EAAM,IAAI,CAAC;IAC3E,KAAa,EAAK,EAAE,cAAc,GAAG,GAAa,KAAA,SAAiB,EAAK,SAAS,MAAM,GAAG,CAAC,CAAS;IACrG,kBAAC,QAAD;KAAM,WAAU;KAA0B,eAAA;IAAa,CAAA;IACtD,EAAK,EAAE,aAAa,GAAG,GAAuB,KAAA,SAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,EAAM,IAAI;IAC3F,EAAK,EAAE,aAAa,GAAG,GAAsB,KAAA,SAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,EAAM,IAAI;IAC3F,kBAAC,SAAD;KACE,KAAK;KACL,MAAK;KACL,QAAO;KACP,UAAA;KACA,QAAA;KACA,cAAY,EAAE,cAAc;KAC5B,WAAW,MAAM;MACf,IAAM,IAAQ,CAAC,GAAI,EAAE,OAAO,SAAS,CAAC,CAAE;MAExC,AADA,EAAE,OAAO,QAAQ,IACb,EAAM,UAAQ,EAAS,CAAK;KAClC;IACD,CAAA;GACE;EACJ,CAAA,GAAA,MAAY,QACX,kBAAC,QAAD;GACE,WAAU;GACV,WAAW,MAAM;IAEf,AADA,EAAE,eAAe,GACjB,EAAS;GACX;GALF,UAAA;IAOE,kBAAC,SAAD;KAAO,SAAS,GAAG,EAAG;KAAQ,WAAU;KACrC,UAAA,EAAE,gBAAgB;IACd,CAAA;IACP,kBAAC,GAAD;KAAO,IAAI,GAAG,EAAG;KAAQ,WAAU;KAAsB,MAAK;KAAM,aAAY;KAAW,OAAO;KAAS,WAAA;KAAU,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;IAAI,CAAA;IACnK,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KACxB,UAAA,EAAE,EAAQ,KAAK,IAAI,qBAAqB,mBAAmB;IACtD,CAAA;IACR,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,SAAQ;KAAQ,eAAe,EAAW,IAAI;KAC3E,UAAA,EAAE,eAAe;IACZ,CAAA;GACJ;EAEL,CAAA,CAAA;;AAET"}
package/dist/index.d.ts CHANGED
@@ -19,10 +19,12 @@ export { HoverPreview, type HoverPreviewProps } from './components/hover-preview
19
19
  export { LazyMount, type LazyMountProps } from './components/lazy-mount.js';
20
20
  export { MediaReadinessNotice, type MediaReadinessNoticeProps } from './components/media-readiness-notice.js';
21
21
  export * from './components/social/index.js';
22
+ export * from './components/groups/index.js';
23
+ export * from './components/content/index.js';
22
24
  export { UserCommentStanding, useUserCommentStandingAccess, type UserCommentStandingProps } from './components/admin/user-comment-standing.js';
23
25
  export { PollManager, pollState, usePollManagerAccess, type PollManagerProps } from './components/admin/poll-manager.js';
24
- export { PostManager, postStatus, usePostManagerAccess, type PostEditorProps, type PostManagerProps, type PostStatus } from './components/admin/post-manager.js';
25
- export { TaxonomyManager, slugify, useTaxonomyManagerAccess, type TaxonomyKind, type TaxonomyManagerProps, type TaxonomyRelation, } from './components/admin/taxonomy-manager.js';
26
+ export { ContentEditor, ContentManager, contentStatus, useContentManagerAccess, type BodyEditorProps, type ContentEditorProps, type ContentManagerProps, type ContentStatus } from './components/admin/content-manager.js';
27
+ export { GroupManager, useGroupManagerAccess, type GroupManagerProps } from './components/admin/group-manager.js';
26
28
  export { MediaItemPanel, type MediaItemPanelProps } from './components/admin/media-item-panel.js';
27
29
  export { UploadQueue, type UploadQueueProps } from './components/admin/upload-queue.js';
28
30
  export { NoAccess, Pager, useConfirm, type AdminFlowProps, type AdminRouteProps, type ConfirmOptions, type ContentKitGuard } from './components/admin/shell.js';