@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":"zh.js","names":[],"sources":["../../../src/locales/social/zh.ts"],"sourcesContent":["import type { social as en } from \"./en.js\";\n\nexport const social: typeof en = {\n notifications: {\n \"archived\": \"已归档\",\n \"dismiss\": \"归档\",\n \"restore\": \"恢复到收件箱\",\n \"dismissed\": \"已关闭\",\n \"title\": \"通知\",\n \"filter\": \"通知筛选\",\n \"all\": \"全部\",\n \"unread\": \"未读\",\n \"refresh\": \"刷新通知\",\n \"signInHint\": \"登录后可查看通知。\",\n \"signIn\": \"登录\",\n \"loadFailed\": \"无法加载通知。\",\n \"loading\": \"正在加载通知…\",\n \"empty\": \"暂无通知。\",\n \"emptyUnread\": \"没有未读通知。\",\n \"loadMore\": \"显示更多\",\n \"open\": \"需要处理\",\n \"resolved\": \"已解决\",\n \"markRead\": \"标为已读\",\n \"markUnread\": \"标为未读\",\n \"unreadLabel\": \"通知,有未读内容\"\n },\n\n comments: {\n title: \"评论\",\n count_one: \"{count} 条评论\",\n count_other: \"{count} 条评论\",\n placeholder: \"写评论…\",\n replyPlaceholder: \"写回复…\",\n name: \"名字\",\n namePlaceholder: \"将随评论显示\",\n post: \"发布\",\n posting: \"发布中…\",\n reply: \"回复\",\n replyingTo: \"回复 {name}\",\n empty: \"还没有评论。来发表第一条吧!\",\n loadMore: \"显示更多评论\",\n loading: \"正在加载评论…\",\n loadFailed: \"无法加载评论。\",\n signIn: \"登录后评论\",\n required: \"请先写点内容。\",\n nameRequired: \"请输入名字。\",\n charsLeft_one: \"还剩 {count} 个字符\",\n charsLeft_other: \"还剩 {count} 个字符\",\n submitHint: \"按 {key}+Enter 发布\",\n sort: \"评论排序\",\n newest: \"最新\",\n best: \"最热\",\n showReplies_one: \"显示 {count} 条回复\",\n showReplies_other: \"显示 {count} 条回复\",\n hideReplies: \"隐藏回复\",\n moreReplies: \"显示更多回复\",\n loadingReplies: \"正在加载回复…\",\n anonymous: \"匿名\",\n deleted: \"此评论已删除。\",\n edit: \"编辑\",\n delete: \"删除\",\n actions: \"评论操作\",\n deleteTitle: \"删除这条评论?\",\n deleteBody: \"它会被替换为已删除的提示,回复会保留。\",\n held: \"审核中。在管理员批准前,这条评论只有你能看到。\",\n heldBadge: \"审核中\",\n rejected: \"未发布:{reason}\",\n rejectedNoReason: \"未发布。\",\n rejectedBadge: \"未发布\",\n like: \"赞\",\n dislike: \"踩\",\n showMore: \"展开\",\n showLess: \"收起\",\n restore: \"恢复\",\n locked: \"你不能在这里评论。\",\n rateLimited: \"你评论得太快了。请在 {seconds} 秒后重试。\",\n rateLimitedSoon: \"你评论得太快了。请稍后重试。\",\n ban: \"禁止评论\",\n unban: \"解除评论禁令\",\n },\n bans: {\n globalNotice: \"你不能在本站评论。\",\n ownerNotice: \"你不能评论这位创作者的内容。\",\n reason: \"原因:{reason}\",\n until: \"截至 {date}。\",\n untilLifted: \"直到解除为止。\",\n title: \"禁止 {name} 评论\",\n ownerHelp: \"适用于你的所有内容。对方已有的评论会保留,仍可点赞、收藏和投票。\",\n globalHelp: \"适用于整个网站。对方已有的评论会保留,仍可点赞、收藏和投票。\",\n scope: \"范围\",\n scopeOwner: \"我的内容\",\n scopeGlobal: \"整个网站\",\n duration: \"时长\",\n day: \"1 天\",\n week: \"7 天\",\n month: \"30 天\",\n forever: \"直到解除\",\n custom: \"截至某个日期\",\n endsAt: \"结束\",\n reasonLabel: \"原因(可选)\",\n reasonPlaceholder: \"会显示给该用户\",\n save: \"禁止\",\n update: \"更新禁令\",\n lift: \"解除禁令\",\n liftTitle: \"解除对 {name} 的禁令?\",\n liftBody: \"如果没有其他禁令,对方可以再次评论。\",\n empty: \"没有禁令。\",\n active: \"生效中\",\n expired: \"已过期\",\n bannedAt: \"于 {date} 禁止\",\n add: \"禁止用户\",\n user: \"用户 ID\",\n userPlaceholder: \"该用户的 ID\",\n loadFailed: \"无法加载禁令。\",\n },\n reactions: {\n like: \"赞\",\n dislike: \"踩\",\n signIn: \"登录后表态\",\n failed: \"你的表态未保存。请重试。\",\n },\n favorites: {\n add: \"加入收藏\",\n remove: \"取消收藏\",\n added: \"已加入收藏。\",\n removed: \"已取消收藏。\",\n signIn: \"登录后收藏\",\n },\n poll: {\n votes_one: \"{count} 票\",\n votes_other: \"{count} 票\",\n yourVote: \"你的投票\",\n closes: \"{date} 截止\",\n closed: \"此投票已结束。\",\n signIn: \"登录后投票\",\n answerPlaceholder: \"你的回答\",\n answer: \"提交回答\",\n updateAnswer: \"更新回答\",\n editAnswer: \"编辑回答\",\n yourAnswer: \"你的回答\",\n answers_one: \"{count} 个回答\",\n answers_other: \"{count} 个回答\",\n loadFailed: \"无法加载此投票。\",\n },\n pollEditor: {\n newTitle: \"新投票\",\n editTitle: \"编辑投票\",\n question: \"问题\",\n questionPlaceholder: \"你想问什么?\",\n language: \"语言\",\n kind: \"类型\",\n multipleChoice: \"选择题\",\n freeText: \"自由回答\",\n freeTextHelp: \"读者写下回答,回答会被自动归类。\",\n liveAt: \"上线时间\",\n closesAt: \"截止时间(可选)\",\n active: \"公开可见\",\n inactive: \"对公众隐藏\",\n image: \"问题图片\",\n options: \"选项\",\n option: \"选项 {index}\",\n addOption: \"添加选项\",\n newOption: \"新选项\",\n removeOption: \"删除选项\",\n removeOptionTitle: \"删除此选项?\",\n removeOptionBody: \"它的票数也会被删除。投票至少保留两个选项。\",\n optionImage: \"选项 {index} 的图片\",\n uploadImage: \"添加图片\",\n replaceImage: \"替换图片\",\n removeImage: \"移除图片\",\n create: \"创建投票\",\n creating: \"创建中…\",\n delete: \"删除投票\",\n deleteTitle: \"删除此投票?\",\n deleteBody: \"票数和图片会被删除,且无法撤销。\",\n saved: \"已保存。\",\n created: \"投票已创建。\",\n imageFailed: \"投票已保存,但有一张图片未上传。请在这里重新添加。\",\n minOptions: \"投票至少需要两个选项。\",\n maxOptions: \"投票最多有 {max} 个选项。\",\n questionRequired: \"请填写问题。\",\n optionRequired: \"每个选项都需要文字。\",\n },\n moderation: {\n title: \"评论管理\",\n all: \"全部评论\",\n queue: \"待审队列\",\n bans: \"禁令\",\n kind: \"内容\",\n allKinds: \"全部内容\",\n on: \"位于\",\n published: \"已发布\",\n held: \"已暂扣\",\n rejected: \"已拒绝\",\n deleted: \"已删除\",\n reply: \"回复\",\n delete: \"删除\",\n restore: \"恢复\",\n approve: \"通过\",\n reject: \"拒绝\",\n rejectReason: \"告知作者的原因(可选)\",\n heldBecause: \"暂扣原因:{reason}\",\n empty: \"没有评论。\",\n queueEmpty: \"没有待审内容。\",\n loadFailed: \"无法加载评论。\",\n stale: \"列出后内容已被编辑,请重新审核。\",\n loadMore: \"显示更多\",\n queueComments: \"评论\",\n queuePosts: \"文章\",\n noAccess: \"你没有审核评论的权限。\",\n author: \"作者\",\n authorPlaceholder: \"按用户 ID 筛选\",\n filter: \"筛选\",\n byAuthor: \"{name} 的评论\",\n clearAuthor: \"显示所有人的\",\n theirComments: \"该用户的评论\",\n },\n time: {\n justNow: \"刚刚\",\n },\n contentErrors: {\n forbidden: \"你没有执行此操作的权限。\",\n unauthorized: \"会话已过期。请重新登录。\",\n not_found: \"此内容已不可用。\",\n conflict: \"此内容已在别处更改。请刷新后重试。\",\n rate_limited: \"请求过多。请在 {seconds} 秒后重试。\",\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"zh.js","names":[],"sources":["../../../src/locales/social/zh.ts"],"sourcesContent":["import type { social as en } from \"./en.js\";\n\nexport const social: typeof en = {\n notifications: {\n \"archived\": \"已归档\",\n \"dismiss\": \"归档\",\n \"restore\": \"恢复到收件箱\",\n \"dismissed\": \"已关闭\",\n \"title\": \"通知\",\n \"filter\": \"通知筛选\",\n \"all\": \"全部\",\n \"unread\": \"未读\",\n \"refresh\": \"刷新通知\",\n \"signInHint\": \"登录后可查看通知。\",\n \"signIn\": \"登录\",\n \"loadFailed\": \"无法加载通知。\",\n \"loading\": \"正在加载通知…\",\n \"empty\": \"暂无通知。\",\n \"emptyUnread\": \"没有未读通知。\",\n \"loadMore\": \"显示更多\",\n \"open\": \"需要处理\",\n \"resolved\": \"已解决\",\n \"markRead\": \"标为已读\",\n \"markUnread\": \"标为未读\",\n \"unreadLabel\": \"通知,有未读内容\"\n },\n\n comments: {\n title: \"评论\",\n count_one: \"{count} 条评论\",\n count_other: \"{count} 条评论\",\n placeholder: \"写评论…\",\n replyPlaceholder: \"写回复…\",\n name: \"名字\",\n namePlaceholder: \"将随评论显示\",\n post: \"发布\",\n posting: \"发布中…\",\n reply: \"回复\",\n replyingTo: \"回复 {name}\",\n empty: \"还没有评论。来发表第一条吧!\",\n loadMore: \"显示更多评论\",\n loading: \"正在加载评论…\",\n loadFailed: \"无法加载评论。\",\n signIn: \"登录后评论\",\n required: \"请先写点内容。\",\n nameRequired: \"请输入名字。\",\n charsLeft_one: \"还剩 {count} 个字符\",\n charsLeft_other: \"还剩 {count} 个字符\",\n submitHint: \"按 {key}+Enter 发布\",\n sort: \"评论排序\",\n newest: \"最新\",\n best: \"最热\",\n showReplies_one: \"显示 {count} 条回复\",\n showReplies_other: \"显示 {count} 条回复\",\n hideReplies: \"隐藏回复\",\n moreReplies: \"显示更多回复\",\n loadingReplies: \"正在加载回复…\",\n anonymous: \"匿名\",\n deleted: \"此评论已删除。\",\n edit: \"编辑\",\n delete: \"删除\",\n actions: \"评论操作\",\n deleteTitle: \"删除这条评论?\",\n deleteBody: \"它会被替换为已删除的提示,回复会保留。\",\n held: \"审核中。在管理员批准前,这条评论只有你能看到。\",\n heldBadge: \"审核中\",\n rejected: \"未发布:{reason}\",\n rejectedNoReason: \"未发布。\",\n rejectedBadge: \"未发布\",\n like: \"赞\",\n dislike: \"踩\",\n showMore: \"展开\",\n showLess: \"收起\",\n restore: \"恢复\",\n closed: \"评论已关闭。\",\n locked: \"你不能在这里评论。\",\n rateLimited: \"你评论得太快了。请在 {seconds} 秒后重试。\",\n rateLimitedSoon: \"你评论得太快了。请稍后重试。\",\n ban: \"禁止评论\",\n unban: \"解除评论禁令\",\n },\n bans: {\n globalNotice: \"你不能在本站评论。\",\n ownerNotice: \"你不能评论这位创作者的内容。\",\n reason: \"原因:{reason}\",\n until: \"截至 {date}。\",\n untilLifted: \"直到解除为止。\",\n title: \"禁止 {name} 评论\",\n ownerHelp: \"适用于你的所有内容。对方已有的评论会保留,仍可点赞、收藏和投票。\",\n globalHelp: \"适用于整个网站。对方已有的评论会保留,仍可点赞、收藏和投票。\",\n scope: \"范围\",\n scopeOwner: \"我的内容\",\n scopeGlobal: \"整个网站\",\n duration: \"时长\",\n day: \"1 天\",\n week: \"7 天\",\n month: \"30 天\",\n forever: \"直到解除\",\n custom: \"截至某个日期\",\n endsAt: \"结束\",\n reasonLabel: \"原因(可选)\",\n reasonPlaceholder: \"会显示给该用户\",\n save: \"禁止\",\n update: \"更新禁令\",\n lift: \"解除禁令\",\n liftTitle: \"解除对 {name} 的禁令?\",\n liftBody: \"如果没有其他禁令,对方可以再次评论。\",\n empty: \"没有禁令。\",\n active: \"生效中\",\n expired: \"已过期\",\n bannedAt: \"于 {date} 禁止\",\n add: \"禁止用户\",\n user: \"用户 ID\",\n userPlaceholder: \"该用户的 ID\",\n loadFailed: \"无法加载禁令。\",\n },\n reactions: {\n like: \"赞\",\n dislike: \"踩\",\n signIn: \"登录后表态\",\n failed: \"你的表态未保存。请重试。\",\n },\n favorites: {\n add: \"加入收藏\",\n remove: \"取消收藏\",\n added: \"已加入收藏。\",\n removed: \"已取消收藏。\",\n signIn: \"登录后收藏\",\n },\n poll: {\n votes_one: \"{count} 票\",\n votes_other: \"{count} 票\",\n yourVote: \"你的投票\",\n closes: \"{date} 截止\",\n closed: \"此投票已结束。\",\n signIn: \"登录后投票\",\n answerPlaceholder: \"你的回答\",\n answer: \"提交回答\",\n updateAnswer: \"更新回答\",\n editAnswer: \"编辑回答\",\n yourAnswer: \"你的回答\",\n answers_one: \"{count} 个回答\",\n answers_other: \"{count} 个回答\",\n loadFailed: \"无法加载此投票。\",\n },\n pollEditor: {\n newTitle: \"新投票\",\n editTitle: \"编辑投票\",\n question: \"问题\",\n questionPlaceholder: \"你想问什么?\",\n language: \"语言\",\n kind: \"类型\",\n multipleChoice: \"选择题\",\n freeText: \"自由回答\",\n freeTextHelp: \"读者写下回答,回答会被自动归类。\",\n liveAt: \"上线时间\",\n closesAt: \"截止时间(可选)\",\n active: \"公开可见\",\n inactive: \"对公众隐藏\",\n image: \"问题图片\",\n options: \"选项\",\n option: \"选项 {index}\",\n addOption: \"添加选项\",\n newOption: \"新选项\",\n removeOption: \"删除选项\",\n removeOptionTitle: \"删除此选项?\",\n removeOptionBody: \"它的票数也会被删除。投票至少保留两个选项。\",\n optionImage: \"选项 {index} 的图片\",\n uploadImage: \"添加图片\",\n replaceImage: \"替换图片\",\n removeImage: \"移除图片\",\n create: \"创建投票\",\n creating: \"创建中…\",\n delete: \"删除投票\",\n deleteTitle: \"删除此投票?\",\n deleteBody: \"票数和图片会被删除,且无法撤销。\",\n saved: \"已保存。\",\n created: \"投票已创建。\",\n imageFailed: \"投票已保存,但有一张图片未上传。请在这里重新添加。\",\n minOptions: \"投票至少需要两个选项。\",\n maxOptions: \"投票最多有 {max} 个选项。\",\n questionRequired: \"请填写问题。\",\n optionRequired: \"每个选项都需要文字。\",\n },\n moderation: {\n title: \"评论管理\",\n all: \"全部评论\",\n queue: \"待审队列\",\n bans: \"禁令\",\n kind: \"内容\",\n allKinds: \"全部内容\",\n on: \"位于\",\n published: \"已发布\",\n held: \"已暂扣\",\n rejected: \"已拒绝\",\n deleted: \"已删除\",\n reply: \"回复\",\n delete: \"删除\",\n restore: \"恢复\",\n approve: \"通过\",\n reject: \"拒绝\",\n rejectReason: \"告知作者的原因(可选)\",\n heldBecause: \"暂扣原因:{reason}\",\n empty: \"没有评论。\",\n queueEmpty: \"没有待审内容。\",\n loadFailed: \"无法加载评论。\",\n stale: \"列出后内容已被编辑,请重新审核。\",\n loadMore: \"显示更多\",\n queueComments: \"评论\",\n queueContent: \"文章\",\n noAccess: \"你没有审核评论的权限。\",\n author: \"作者\",\n authorPlaceholder: \"按用户 ID 筛选\",\n filter: \"筛选\",\n byAuthor: \"{name} 的评论\",\n clearAuthor: \"显示所有人的\",\n theirComments: \"该用户的评论\",\n },\n time: {\n justNow: \"刚刚\",\n },\n contentErrors: {\n forbidden: \"你没有执行此操作的权限。\",\n unauthorized: \"会话已过期。请重新登录。\",\n not_found: \"此内容已不可用。\",\n conflict: \"此内容已在别处更改。请刷新后重试。\",\n rate_limited: \"请求过多。请在 {seconds} 秒后重试。\",\n },\n};\n"],"mappings":"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"}
@@ -1,9 +1,15 @@
1
1
  import { admin as e } from "./admin/zh.js";
2
2
  import { social as t } from "./social/zh.js";
3
+ import { groups as n } from "./groups/zh.js";
4
+ import { discovery as r } from "./discovery/zh.js";
5
+ import { content as i } from "./content/zh.js";
3
6
  //#region src/locales/zh.ts
4
- var n = {
7
+ var a = {
5
8
  ...t,
6
9
  ...e,
10
+ ...n,
11
+ ...r,
12
+ ...i,
7
13
  common: {
8
14
  cancel: "取消",
9
15
  close: "关闭",
@@ -261,14 +267,17 @@ var n = {
261
267
  resume_mismatch: "保存的上传属于另一个文件。请重新选择文件。",
262
268
  moderation_rejected: "已被审核拒绝。",
263
269
  comment_banned: "你无法在此评论。",
270
+ comments_closed: "此处评论已关闭。",
271
+ limit_reached: "已达到允许的上限。",
264
272
  comment_too_long: "评论太长了。",
265
273
  commentTooLongBy: "评论最多 {maxChars} 个字符。",
266
274
  not_configured: "此网站未提供该功能。",
275
+ no_membership: "仅限会员的内容需要一个可加入或购买的群组。请先为该群组添加报价。",
267
276
  tenant_mismatch: "我们这边出了点问题,请重试。",
268
277
  gone: "此内容已不可用。"
269
278
  }
270
279
  };
271
280
  //#endregion
272
- export { n as zh };
281
+ export { a as zh };
273
282
 
274
283
  //# sourceMappingURL=zh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { admin } from \"./admin/zh.js\";\nimport { social } from \"./social/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n ...admin,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n lockedOne: \"还有 1 项已锁定\",\n lockedMany: \"还有 {count} 项已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n errors: {\n generic: \"我们这边出了问题,请重试。\",\n network: \"连接已断开,请检查网络后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"图片已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n upgrade_required: \"此项目正在升级,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { admin } from \"./admin/zh.js\";\nimport { social } from \"./social/zh.js\";\nimport { groups } from \"./groups/zh.js\";\nimport { discovery } from \"./discovery/zh.js\";\nimport { content } from \"./content/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n ...admin,\n ...groups,\n ...discovery,\n ...content,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n lockedOne: \"还有 1 项已锁定\",\n lockedMany: \"还有 {count} 项已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n errors: {\n generic: \"我们这边出了问题,请重试。\",\n network: \"连接已断开,请检查网络后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"图片已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n upgrade_required: \"此项目正在升级,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comments_closed: \"此处评论已关闭。\",\n limit_reached: \"已达到允许的上限。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n no_membership: \"仅限会员的内容需要一个可加入或购买的群组。请先为该群组添加报价。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
@@ -39,7 +39,7 @@ export interface AdminLocation {
39
39
  */
40
40
  export declare function useAdminLocation(basePath: string | undefined, navigation: AdminNavigation | undefined): AdminLocation;
41
41
  /** A ContentKit staff area, each behind its own permission. */
42
- export type StaffArea = "comments" | "queue" | "bans" | "polls" | "posts" | "taxonomy";
42
+ export type StaffArea = "comments" | "queue" | "bans" | "polls" | "contents" | "groups";
43
43
  export interface StaffAccess<A extends StaffArea> {
44
44
  loading: boolean;
45
45
  /** Per area; true until known, and when the check itself failed (the area then shows its own error). */
@@ -60,7 +60,7 @@ function v(e, t) {
60
60
  let n = e.indexOf(t);
61
61
  return n < 0 ? [e] : [e.slice(0, n), e.slice(n + 1)];
62
62
  }
63
- var y = {
63
+ var y = "00000000-0000-7000-8000-000000000000", b = {
64
64
  comments: (e, t) => e.comments.adminList({ limit: 1 }, t),
65
65
  queue: (e, t) => e.moderation.held({
66
66
  kind: "comment",
@@ -68,15 +68,17 @@ var y = {
68
68
  }, t),
69
69
  bans: (e, t) => e.bans.list("global", { limit: 1 }, t),
70
70
  polls: (e, t) => e.polls.adminList({ limit: 1 }, t),
71
- posts: (e, t) => e.posts.adminList({ limit: 1 }, t),
72
- taxonomy: (e, t) => e.taxonomy.nodes({ limit: 1 }, t)
71
+ contents: (e, t) => e.contents.manage({ limit: 1 }, t),
72
+ groups: (t, n) => t.groups.members(y, { limit: 1 }, n).catch((t) => {
73
+ if (e(t).code !== "not_found") throw t;
74
+ })
73
75
  };
74
- function b(t, a = {}) {
76
+ function x(t, a = {}) {
75
77
  let o = u(a), { store: s, scope: c } = r(o), l = i(s, n("access", c, ...t), { type: "access" }, async (n) => {
76
78
  let r = {};
77
79
  return await Promise.all(t.map(async (t) => {
78
80
  try {
79
- await y[t](o, n), r[t] = !0;
81
+ await b[t](o, n), r[t] = !0;
80
82
  } catch (n) {
81
83
  let i = e(n);
82
84
  if (i.code !== "forbidden" && i.code !== "unauthorized") throw i;
@@ -92,6 +94,6 @@ function b(t, a = {}) {
92
94
  };
93
95
  }
94
96
  //#endregion
95
- export { g as useAdminLocation, u as useFlowClient, b as useStaffAccess };
97
+ export { g as useAdminLocation, u as useFlowClient, x as useStaffAccess };
96
98
 
97
99
  //# sourceMappingURL=admin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","names":[],"sources":["../../src/react/admin.ts"],"sourcesContent":["import { useCallback, useMemo, useState, useSyncExternalStore } from \"react\";\nimport { createContentKitClient, type ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { TokenSource } from \"../client/http.js\";\nimport { useContentKitClient } from \"./context.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\n/** What a flow needs of the host's auth: an authenticating fetch, as auth-ui's AuthClient and IssuerClient have. */\nexport interface ContentKitAuth {\n authFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n /** The access token, for HLS playlists on the API origin. */\n getAccessToken?: TokenSource;\n}\n\n/** How a flow reaches ContentKit: its own client, or baseUrl with the host's auth; else the provider's client. */\nexport interface FlowClientOptions {\n client?: ContentKitClient;\n baseUrl?: string;\n auth?: ContentKitAuth;\n}\n\n/** The flow's client: `client`, else one made from `baseUrl` and `auth`, else the provider's. */\nexport function useFlowClient({ client, baseUrl, auth }: FlowClientOptions): ContentKitClient {\n const made = useMemo(\n () =>\n client || baseUrl === undefined\n ? null\n : createContentKitClient({\n baseUrl,\n fetch: auth ? (input, init) => auth.authFetch(input, init) : undefined,\n token: auth?.getAccessToken ? () => auth.getAccessToken!() : undefined,\n }),\n [client, baseUrl, auth],\n );\n return useContentKitClient(client ?? made);\n}\n\n/** The host router as a flow sees it. With React Router: `{ pathname, search }` of useLocation() and useNavigate(). */\nexport interface AdminNavigation {\n pathname: string;\n search: string;\n navigate: (to: string, options?: { replace?: boolean }) => void;\n}\n\n/** A query for a view; empty values are left out. */\nexport type AdminQuery = Record<string, string | number | null | undefined>;\n\nexport interface AdminLocation {\n /** The view: the path under basePath, \"\" at its root. */\n view: string;\n query: URLSearchParams;\n go: (view: string, query?: AdminQuery, options?: { replace?: boolean }) => void;\n}\n\nconst MOVED = \"ckui:navigate\";\nconst subscribeHistory = (cb: () => void) => {\n addEventListener(\"popstate\", cb);\n addEventListener(MOVED, cb);\n return () => {\n removeEventListener(\"popstate\", cb);\n removeEventListener(MOVED, cb);\n };\n};\nconst historySnapshot = () => location.pathname + location.search;\nconst noHistory = () => \"\";\n\nfunction encode(query: AdminQuery = {}): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) if (v !== undefined && v !== null && v !== \"\") q.set(k, String(v));\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\n/**\n * A multi-view flow's place: under basePath in the host router (navigation),\n * else in the browser history; without basePath, in memory.\n */\nexport function useAdminLocation(basePath: string | undefined, navigation: AdminNavigation | undefined): AdminLocation {\n const history = useSyncExternalStore(navigation || basePath === undefined ? subscribeNone : subscribeHistory, historySnapshot, noHistory);\n const [memory, setMemory] = useState({ view: \"\", search: \"\" });\n const base = basePath?.replace(/\\/+$/, \"\") ?? \"\";\n let view: string;\n let search: string;\n if (basePath === undefined && !navigation) {\n ({ view, search } = memory);\n } else {\n const at = navigation ? navigation.pathname + navigation.search : history;\n const [pathname, query = \"\"] = splitOnce(at, \"?\");\n search = query ? `?${query}` : \"\";\n view = pathname === base || pathname === `${base}/` ? \"\" : pathname.startsWith(`${base}/`) ? pathname.slice(base.length + 1).replace(/\\/+$/, \"\") : \"\";\n }\n const go = useCallback(\n (to: string, query?: AdminQuery, o: { replace?: boolean } = {}) => {\n const qs = encode(query);\n if (basePath === undefined && !navigation) return setMemory({ view: to, search: qs });\n const url = (to ? `${base}/${to}` : base || \"/\") + qs;\n if (navigation) return navigation.navigate(url, o);\n window.history[o.replace ? \"replaceState\" : \"pushState\"](null, \"\", url);\n dispatchEvent(new Event(MOVED));\n },\n [basePath, base, navigation],\n );\n const query = useMemo(() => new URLSearchParams(search), [search]);\n return { view, query, go };\n}\n\nconst subscribeNone = () => () => {};\n\nfunction splitOnce(s: string, sep: string): [string, string?] {\n const i = s.indexOf(sep);\n return i < 0 ? [s] : [s.slice(0, i), s.slice(i + 1)];\n}\n\n/** A ContentKit staff area, each behind its own permission. */\nexport type StaffArea = \"comments\" | \"queue\" | \"bans\" | \"polls\" | \"posts\" | \"taxonomy\";\n\nconst PROBES: Record<StaffArea, (c: ContentKitClient, signal: AbortSignal) => Promise<unknown>> = {\n comments: (c, signal) => c.comments.adminList({ limit: 1 }, signal),\n queue: (c, signal) => c.moderation.held({ kind: \"comment\", limit: 1 }, signal),\n bans: (c, signal) => c.bans.list(\"global\", { limit: 1 }, signal),\n polls: (c, signal) => c.polls.adminList({ limit: 1 }, signal),\n posts: (c, signal) => c.posts.adminList({ limit: 1 }, signal),\n taxonomy: (c, signal) => c.taxonomy.nodes({ limit: 1 }, signal),\n};\n\nexport interface StaffAccess<A extends StaffArea> {\n loading: boolean;\n /** Per area; true until known, and when the check itself failed (the area then shows its own error). */\n allowed: Record<A, boolean>;\n /** Some area is allowed. */\n any: boolean;\n error?: ContentKitError;\n}\n\n/**\n * What the caller may do in each area: one small read per area, refused\n * (401, 403) or not, shared by every hook asking on the client.\n */\nexport function useStaffAccess<A extends StaffArea>(areas: readonly A[], o: FlowClientOptions = {}): StaffAccess<A> {\n const client = useFlowClient(o);\n const { store, scope } = useContentScope(client);\n const r = useResource<Record<string, boolean>>(store, keyOf(\"access\", scope, ...areas), { type: \"access\" }, async (signal) => {\n const out: Record<string, boolean> = {};\n await Promise.all(\n areas.map(async (a) => {\n try {\n await PROBES[a](client, signal);\n out[a] = true;\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"forbidden\" && err.code !== \"unauthorized\") throw err;\n out[a] = false;\n }\n }),\n );\n return out;\n });\n const allowed = Object.fromEntries(areas.map((a) => [a, r.data?.[a] ?? true])) as Record<A, boolean>;\n return { loading: r.loading || !r.loaded, allowed, any: areas.some((a) => allowed[a]), error: r.error };\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,EAAc,EAAE,WAAQ,YAAS,WAA6C;CAC5F,IAAM,IAAO,QAET,KAAU,MAAY,KAAA,IAClB,OACA,EAAuB;EACrB;EACA,OAAO,KAAQ,GAAO,MAAS,EAAK,UAAU,GAAO,CAAI,IAAI,KAAA;EAC7D,OAAO,GAAM,uBAAuB,EAAK,eAAgB,IAAI,KAAA;CAC/D,CAAC,GACP;EAAC;EAAQ;EAAS;CAAI,CACxB;CACA,OAAO,EAAoB,KAAU,CAAI;AAC3C;AAmBA,IAAM,IAAQ,iBACR,KAAoB,OACxB,iBAAiB,YAAY,CAAE,GAC/B,iBAAiB,GAAO,CAAE,SACb;CAEX,AADA,oBAAoB,YAAY,CAAE,GAClC,oBAAoB,GAAO,CAAE;AAC/B,IAEI,UAAwB,SAAS,WAAW,SAAS,QACrD,UAAkB;AAExB,SAAS,EAAO,IAAoB,CAAC,GAAW;CAC9C,IAAM,IAAI,IAAI,gBAAgB;CAC9B,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAK,GAAG,AAAI,KAAyB,QAAQ,MAAM,MAAI,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;CAC7G,IAAM,IAAI,EAAE,SAAS;CACrB,OAAO,IAAI,IAAI,MAAM;AACvB;AAMA,SAAgB,EAAiB,GAA8B,GAAwD;CACrH,IAAM,IAAU,EAAqB,KAAc,MAAa,KAAA,IAAY,IAAgB,GAAkB,GAAiB,CAAS,GAClI,CAAC,GAAQ,KAAa,EAAS;EAAE,MAAM;EAAI,QAAQ;CAAG,CAAC,GACvD,IAAO,GAAU,QAAQ,QAAQ,EAAE,KAAK,IAC1C,GACA;CACJ,IAAI,MAAa,KAAA,KAAa,CAAC,GAC7B,CAAC,uBAAmB;MACf;EAEL,IAAM,CAAC,GAAU,IAAQ,MAAM,EADpB,IAAa,EAAW,WAAW,EAAW,SAAS,GACrB,GAAG;EAEhD,AADA,IAAS,IAAQ,IAAI,MAAU,IAC/B,IAAO,MAAa,KAAQ,MAAa,GAAG,EAAK,KAAK,KAAK,EAAS,WAAW,GAAG,EAAK,EAAE,IAAI,EAAS,MAAM,EAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,IAAI;CACrJ;CACA,IAAM,IAAK,GACR,GAAY,GAAoB,IAA2B,CAAC,MAAM;EACjE,IAAM,IAAK,EAAO,CAAK;EACvB,IAAI,MAAa,KAAA,KAAa,CAAC,GAAY,OAAO,EAAU;GAAE,MAAM;GAAI,QAAQ;EAAG,CAAC;EACpF,IAAM,KAAO,IAAK,GAAG,EAAK,GAAG,MAAO,KAAQ,OAAO;EACnD,IAAI,GAAY,OAAO,EAAW,SAAS,GAAK,CAAC;EAEjD,AADA,OAAO,QAAQ,EAAE,UAAU,iBAAiB,YAAY,CAAC,MAAM,IAAI,CAAG,GACtE,cAAc,IAAI,MAAM,CAAK,CAAC;CAChC,GACA;EAAC;EAAU;EAAM;CAAU,CAC7B,GACM,IAAQ,QAAc,IAAI,gBAAgB,CAAM,GAAG,CAAC,CAAM,CAAC;CACjE,OAAO;EAAE;EAAM;EAAO;CAAG;AAC3B;AAEA,IAAM,gBAA4B,CAAC;AAEnC,SAAS,EAAU,GAAW,GAAgC;CAC5D,IAAM,IAAI,EAAE,QAAQ,CAAG;CACvB,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,CAAC;AACrD;AAKA,IAAM,IAA4F;CAChG,WAAW,GAAG,MAAW,EAAE,SAAS,UAAU,EAAE,OAAO,EAAE,GAAG,CAAM;CAClE,QAAQ,GAAG,MAAW,EAAE,WAAW,KAAK;EAAE,MAAM;EAAW,OAAO;CAAE,GAAG,CAAM;CAC7E,OAAO,GAAG,MAAW,EAAE,KAAK,KAAK,UAAU,EAAE,OAAO,EAAE,GAAG,CAAM;CAC/D,QAAQ,GAAG,MAAW,EAAE,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,CAAM;CAC5D,QAAQ,GAAG,MAAW,EAAE,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,CAAM;CAC5D,WAAW,GAAG,MAAW,EAAE,SAAS,MAAM,EAAE,OAAO,EAAE,GAAG,CAAM;AAChE;AAeA,SAAgB,EAAoC,GAAqB,IAAuB,CAAC,GAAmB;CAClH,IAAM,IAAS,EAAc,CAAC,GACxB,EAAE,UAAO,aAAU,EAAgB,CAAM,GACzC,IAAI,EAAqC,GAAO,EAAM,UAAU,GAAO,GAAG,CAAK,GAAG,EAAE,MAAM,SAAS,GAAG,OAAO,MAAW;EAC5H,IAAM,IAA+B,CAAC;EAatC,OAZA,MAAM,QAAQ,IACZ,EAAM,IAAI,OAAO,MAAM;GACrB,IAAI;IAEF,AADA,MAAM,EAAO,EAAE,CAAC,GAAQ,CAAM,GAC9B,EAAI,KAAK;GACX,SAAS,GAAG;IACV,IAAM,IAAM,EAAkB,CAAC;IAC/B,IAAI,EAAI,SAAS,eAAe,EAAI,SAAS,gBAAgB,MAAM;IACnE,EAAI,KAAK;GACX;EACF,CAAC,CACH,GACO;CACT,CAAC,GACK,IAAU,OAAO,YAAY,EAAM,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,MAAM,EAAI,CAAC,CAAC;CAC7E,OAAO;EAAE,SAAS,EAAE,WAAW,CAAC,EAAE;EAAQ;EAAS,KAAK,EAAM,MAAM,MAAM,EAAQ,EAAE;EAAG,OAAO,EAAE;CAAM;AACxG"}
1
+ {"version":3,"file":"admin.js","names":[],"sources":["../../src/react/admin.ts"],"sourcesContent":["import { useCallback, useMemo, useState, useSyncExternalStore } from \"react\";\nimport { createContentKitClient, type ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { TokenSource } from \"../client/http.js\";\nimport { useContentKitClient } from \"./context.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\n/** What a flow needs of the host's auth: an authenticating fetch, as auth-ui's AuthClient and IssuerClient have. */\nexport interface ContentKitAuth {\n authFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n /** The access token, for HLS playlists on the API origin. */\n getAccessToken?: TokenSource;\n}\n\n/** How a flow reaches ContentKit: its own client, or baseUrl with the host's auth; else the provider's client. */\nexport interface FlowClientOptions {\n client?: ContentKitClient;\n baseUrl?: string;\n auth?: ContentKitAuth;\n}\n\n/** The flow's client: `client`, else one made from `baseUrl` and `auth`, else the provider's. */\nexport function useFlowClient({ client, baseUrl, auth }: FlowClientOptions): ContentKitClient {\n const made = useMemo(\n () =>\n client || baseUrl === undefined\n ? null\n : createContentKitClient({\n baseUrl,\n fetch: auth ? (input, init) => auth.authFetch(input, init) : undefined,\n token: auth?.getAccessToken ? () => auth.getAccessToken!() : undefined,\n }),\n [client, baseUrl, auth],\n );\n return useContentKitClient(client ?? made);\n}\n\n/** The host router as a flow sees it. With React Router: `{ pathname, search }` of useLocation() and useNavigate(). */\nexport interface AdminNavigation {\n pathname: string;\n search: string;\n navigate: (to: string, options?: { replace?: boolean }) => void;\n}\n\n/** A query for a view; empty values are left out. */\nexport type AdminQuery = Record<string, string | number | null | undefined>;\n\nexport interface AdminLocation {\n /** The view: the path under basePath, \"\" at its root. */\n view: string;\n query: URLSearchParams;\n go: (view: string, query?: AdminQuery, options?: { replace?: boolean }) => void;\n}\n\nconst MOVED = \"ckui:navigate\";\nconst subscribeHistory = (cb: () => void) => {\n addEventListener(\"popstate\", cb);\n addEventListener(MOVED, cb);\n return () => {\n removeEventListener(\"popstate\", cb);\n removeEventListener(MOVED, cb);\n };\n};\nconst historySnapshot = () => location.pathname + location.search;\nconst noHistory = () => \"\";\n\nfunction encode(query: AdminQuery = {}): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) if (v !== undefined && v !== null && v !== \"\") q.set(k, String(v));\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\n/**\n * A multi-view flow's place: under basePath in the host router (navigation),\n * else in the browser history; without basePath, in memory.\n */\nexport function useAdminLocation(basePath: string | undefined, navigation: AdminNavigation | undefined): AdminLocation {\n const history = useSyncExternalStore(navigation || basePath === undefined ? subscribeNone : subscribeHistory, historySnapshot, noHistory);\n const [memory, setMemory] = useState({ view: \"\", search: \"\" });\n const base = basePath?.replace(/\\/+$/, \"\") ?? \"\";\n let view: string;\n let search: string;\n if (basePath === undefined && !navigation) {\n ({ view, search } = memory);\n } else {\n const at = navigation ? navigation.pathname + navigation.search : history;\n const [pathname, query = \"\"] = splitOnce(at, \"?\");\n search = query ? `?${query}` : \"\";\n view = pathname === base || pathname === `${base}/` ? \"\" : pathname.startsWith(`${base}/`) ? pathname.slice(base.length + 1).replace(/\\/+$/, \"\") : \"\";\n }\n const go = useCallback(\n (to: string, query?: AdminQuery, o: { replace?: boolean } = {}) => {\n const qs = encode(query);\n if (basePath === undefined && !navigation) return setMemory({ view: to, search: qs });\n const url = (to ? `${base}/${to}` : base || \"/\") + qs;\n if (navigation) return navigation.navigate(url, o);\n window.history[o.replace ? \"replaceState\" : \"pushState\"](null, \"\", url);\n dispatchEvent(new Event(MOVED));\n },\n [basePath, base, navigation],\n );\n const query = useMemo(() => new URLSearchParams(search), [search]);\n return { view, query, go };\n}\n\nconst subscribeNone = () => () => {};\n\nfunction splitOnce(s: string, sep: string): [string, string?] {\n const i = s.indexOf(sep);\n return i < 0 ? [s] : [s.slice(0, i), s.slice(i + 1)];\n}\n\n/** A ContentKit staff area, each behind its own permission. */\nexport type StaffArea = \"comments\" | \"queue\" | \"bans\" | \"polls\" | \"contents\" | \"groups\";\n\n// No group has this id: staff get not_found, others a refusal.\nconst NO_GROUP = \"00000000-0000-7000-8000-000000000000\";\n\nconst PROBES: Record<StaffArea, (c: ContentKitClient, signal: AbortSignal) => Promise<unknown>> = {\n comments: (c, signal) => c.comments.adminList({ limit: 1 }, signal),\n queue: (c, signal) => c.moderation.held({ kind: \"comment\", limit: 1 }, signal),\n bans: (c, signal) => c.bans.list(\"global\", { limit: 1 }, signal),\n polls: (c, signal) => c.polls.adminList({ limit: 1 }, signal),\n contents: (c, signal) => c.contents.manage({ limit: 1 }, signal),\n groups: (c, signal) =>\n c.groups.members(NO_GROUP, { limit: 1 }, signal).catch((e) => {\n if (toContentKitError(e).code !== \"not_found\") throw e;\n }),\n};\n\nexport interface StaffAccess<A extends StaffArea> {\n loading: boolean;\n /** Per area; true until known, and when the check itself failed (the area then shows its own error). */\n allowed: Record<A, boolean>;\n /** Some area is allowed. */\n any: boolean;\n error?: ContentKitError;\n}\n\n/**\n * What the caller may do in each area: one small read per area, refused\n * (401, 403) or not, shared by every hook asking on the client.\n */\nexport function useStaffAccess<A extends StaffArea>(areas: readonly A[], o: FlowClientOptions = {}): StaffAccess<A> {\n const client = useFlowClient(o);\n const { store, scope } = useContentScope(client);\n const r = useResource<Record<string, boolean>>(store, keyOf(\"access\", scope, ...areas), { type: \"access\" }, async (signal) => {\n const out: Record<string, boolean> = {};\n await Promise.all(\n areas.map(async (a) => {\n try {\n await PROBES[a](client, signal);\n out[a] = true;\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"forbidden\" && err.code !== \"unauthorized\") throw err;\n out[a] = false;\n }\n }),\n );\n return out;\n });\n const allowed = Object.fromEntries(areas.map((a) => [a, r.data?.[a] ?? true])) as Record<A, boolean>;\n return { loading: r.loading || !r.loaded, allowed, any: areas.some((a) => allowed[a]), error: r.error };\n}\n"],"mappings":"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"}
@@ -1,12 +1,14 @@
1
1
  import { ContentKitClient } from '../client/client.js';
2
- import { BanScope, Decision, HeldKind, Reaction, Sort } from '../client/content/types.js';
2
+ import { BanScope, Decision, HeldKind, Reaction, Sort, Target } from '../client/content/types.js';
3
3
  import { ContentKitError } from '../client/errors.js';
4
- import { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts, RefBody } from '../client/generated/wire.js';
4
+ import { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts } from '../client/generated/wire.js';
5
5
  import { UseList } from './use-resource.js';
6
6
  /** Counts after the caller's reaction changes from counts.mine to value. */
7
7
  export declare function withReaction(counts: ReactionCounts, value: Reaction): ReactionCounts;
8
8
  export interface UseCommentsOptions {
9
9
  sort?: Sort;
10
+ /** One language's thread: the list shows it and new comments join it. */
11
+ language?: string;
10
12
  /** Top-level comments per page. Default 20. */
11
13
  pageSize?: number;
12
14
  client?: ContentKitClient;
@@ -25,16 +27,16 @@ export interface UseComments extends UseList<Comment> {
25
27
  /** Restores a deleted comment (moderators). */
26
28
  restore: (id: string) => Promise<void>;
27
29
  }
28
- /** An item's top-level comments, a page at a time, with every write; new comments and replies land in place. */
29
- export declare function useComments(ref: RefBody, o?: UseCommentsOptions): UseComments;
30
+ /** A target's top-level comments, a page at a time, with every write; new comments and replies land in place. */
31
+ export declare function useComments(target: Target, o?: UseCommentsOptions): UseComments;
30
32
  /** A top-level comment's replies, oldest first, a page at a time; enabled false defers the first read. */
31
33
  export declare function useCommentReplies(id: string, o?: {
32
34
  pageSize?: number;
33
35
  enabled?: boolean;
34
36
  client?: ContentKitClient;
35
37
  }): UseList<Comment>;
36
- /** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */
37
- export declare function useCanComment(ref: RefBody, o?: {
38
+ /** Whether the caller may comment on the target, the ban that stops it, and what it may do to others' comments. */
39
+ export declare function useCanComment(target: Target, o?: {
38
40
  client?: ContentKitClient;
39
41
  }): {
40
42
  standing: CommentStanding | null;
@@ -63,7 +65,7 @@ export interface UseModerationQueue extends UseList<HeldItem> {
63
65
  /** Approves (publishes) or rejects at the revision the queue listed; the item leaves the queue. */
64
66
  resolve: (item: HeldItem, decision: Decision, reason?: string) => Promise<void>;
65
67
  }
66
- /** Staff: held comments or posts awaiting review, oldest first (ModerationReview). */
68
+ /** Staff: held comments or content awaiting review, oldest first (ModerationReview). */
67
69
  export declare function useModerationQueue(o?: {
68
70
  kind?: HeldKind;
69
71
  pageSize?: number;
@@ -42,38 +42,48 @@ async function l(e, t, n, r, i) {
42
42
  }
43
43
  }
44
44
  function u(i, o = {}) {
45
- let { client: s, store: c, scope: u } = n(o.client), d = o.pageSize ?? 20, f = o.sort ?? "newest", p = e("comments", u, i.kind, i.id, f, d), m = r(c, p, {
45
+ let { client: s, store: c, scope: u } = n(o.client), d = o.pageSize ?? 20, f = o.sort ?? "newest", p = o.language || void 0, { type: m, id: h, version: g } = i, _ = e("comments", u, m, h, g, p, f, d), v = r(c, _, {
46
46
  type: "comments",
47
- ref: {
48
- kind: i.kind,
49
- id: i.id
47
+ target: {
48
+ type: m,
49
+ id: h,
50
+ version: g
50
51
  }
51
- }, t(d, (e, t) => s.comments.list(i, {
52
+ }, t(d, (e, t) => s.comments.list({
53
+ type: m,
54
+ id: h,
55
+ version: g
56
+ }, {
52
57
  ...e,
53
- sort: f
54
- }, t))), { kind: h, id: g } = i, _ = a((e, t = {}) => s.comments.create({
55
- kind: h,
56
- id: g
58
+ sort: f,
59
+ language: p
60
+ }, t))), y = a((e, t = {}) => s.comments.create({
61
+ type: m,
62
+ id: h,
63
+ version: g
57
64
  }, {
58
65
  body: e,
59
66
  reply_to_id: t.replyTo,
60
- anon_name: t.anonName
67
+ anon_name: t.anonName,
68
+ language: t.replyTo ? void 0 : p
61
69
  }), [
62
70
  s,
71
+ m,
63
72
  h,
64
- g
65
- ]), v = a((e, t) => s.comments.edit(e, t), [s]), y = a((e) => s.comments.delete(e), [s]), b = a((e) => s.comments.restore(e), [s]), x = a((e, t) => l(s, c, u, e, t), [
73
+ g,
74
+ p
75
+ ]), b = a((e, t) => s.comments.edit(e, t), [s]), x = a((e) => s.comments.delete(e), [s]), S = a((e) => s.comments.restore(e), [s]), C = a((e, t) => l(s, c, u, e, t), [
66
76
  s,
67
77
  c,
68
78
  u
69
79
  ]);
70
80
  return {
71
- ...m,
72
- post: _,
73
- edit: v,
74
- remove: y,
75
- react: x,
76
- restore: b
81
+ ...v,
82
+ post: y,
83
+ edit: b,
84
+ remove: x,
85
+ react: C,
86
+ restore: S
77
87
  };
78
88
  }
79
89
  function d(i, a = {}) {
@@ -84,18 +94,23 @@ function d(i, a = {}) {
84
94
  }, t(l, (e, t) => o.comments.replies(i, e, t)));
85
95
  }
86
96
  function f(t, r = {}) {
87
- let { client: a, store: o, scope: s } = n(r.client), c = i(o, e("standing", s, t.kind, t.id), {
97
+ let { client: a, store: o, scope: s } = n(r.client), { type: c, id: l, version: u } = t, d = i(o, e("standing", s, c, l, u), {
88
98
  type: "standing",
89
- ref: {
90
- kind: t.kind,
91
- id: t.id
99
+ target: {
100
+ type: c,
101
+ id: l,
102
+ version: u
92
103
  }
93
- }, (e) => a.comments.standing(t, e));
104
+ }, (e) => a.comments.standing({
105
+ type: c,
106
+ id: l,
107
+ version: u
108
+ }, e));
94
109
  return {
95
- standing: c.data ?? null,
96
- loading: c.loading || !c.loaded,
97
- error: c.error,
98
- reload: c.reload
110
+ standing: d.data ?? null,
111
+ loading: d.loading || !d.loaded,
112
+ error: d.error,
113
+ reload: d.reload
99
114
  };
100
115
  }
101
116
  function p(i = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"comments.js","names":[],"sources":["../../src/react/comments.ts"],"sourcesContent":["import { useCallback } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { BanScope, Decision, HeldKind, Reaction, Sort } from \"../client/content/types.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts, RefBody } from \"../client/generated/wire.js\";\nimport type { Page, ResourceStore } from \"./resources.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** Counts after the caller's reaction changes from counts.mine to value. */\nexport function withReaction(counts: ReactionCounts, value: Reaction): ReactionCounts {\n const likes = counts.likes - (counts.mine === 1 ? 1 : 0) + (value === 1 ? 1 : 0);\n const dislikes = counts.dislikes - (counts.mine === -1 ? 1 : 0) + (value === -1 ? 1 : 0);\n return { likes, dislikes, mine: value };\n}\n\nconst commentLists = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\nconst tokens = new WeakMap<ResourceStore, Map<string, number>>();\n\n/** Sets the caller's reaction to a comment in every list showing it at once; rolls back on failure unless a newer one started. */\nasync function reactToComment(client: ContentKitClient, store: ResourceStore, scope: string, c: Pick<Comment, \"id\" | \"likes\" | \"dislikes\" | \"mine\">, value: Reaction) {\n let t = tokens.get(store);\n if (!t) tokens.set(store, (t = new Map()));\n const token = (t.get(c.id) ?? 0) + 1;\n t.set(c.id, token);\n const reads = new Set<number>();\n const put = (counts: ReactionCounts, rollback = false) =>\n store.patch<Page<Comment>>(\n (tag) => commentLists.includes(tag.type) && tag.readScope === scope,\n (page, readVersion) => {\n if (rollback && !reads.has(readVersion)) return page;\n reads.add(readVersion);\n return { ...page, items: page.items.map((x) => (x.id === c.id ? { ...x, ...counts } : x)) };\n },\n );\n put(withReaction({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, value));\n try {\n await client.comments.react(c.id, value);\n } catch (e) {\n if (t.get(c.id) === token) put({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, true);\n throw e;\n }\n}\n\nexport interface UseCommentsOptions {\n sort?: Sort;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n client?: ContentKitClient;\n}\n\nexport interface UseComments extends UseList<Comment> {\n /** Comments on the item, or replies to a top-level comment (replyTo); anonymous callers give anonName. */\n post: (body: string, o?: { replyTo?: string; anonName?: string }) => Promise<Comment>;\n edit: (id: string, body: string) => Promise<Comment>;\n /** Leaves a tombstone. */\n remove: (id: string) => Promise<void>;\n /** Sets the caller's reaction at once, rolled back if the server refuses it. */\n react: (comment: Comment, value: Reaction) => Promise<void>;\n /** Restores a deleted comment (moderators). */\n restore: (id: string) => Promise<void>;\n}\n\n/** An item's top-level comments, a page at a time, with every write; new comments and replies land in place. */\nexport function useComments(ref: RefBody, o: UseCommentsOptions = {}): UseComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n const sort = o.sort ?? \"newest\";\n const key = keyOf(\"comments\", scope, ref.kind, ref.id, sort, size);\n const list = useList(store, key, { type: \"comments\", ref: { kind: ref.kind, id: ref.id } }, offsetPages(size, (q, signal) => client.comments.list(ref, { ...q, sort }, signal)));\n const { kind, id } = ref;\n const post = useCallback<UseComments[\"post\"]>(\n (body, p = {}) => client.comments.create({ kind, id }, { body, reply_to_id: p.replyTo, anon_name: p.anonName }),\n [client, kind, id],\n );\n const edit = useCallback((cid: string, body: string) => client.comments.edit(cid, body), [client]);\n const remove = useCallback((cid: string) => client.comments.delete(cid), [client]);\n const restore = useCallback((cid: string) => client.comments.restore(cid), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, post, edit, remove, react, restore };\n}\n\n/** A top-level comment's replies, oldest first, a page at a time; enabled false defers the first read. */\nexport function useCommentReplies(id: string, o: { pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseList<Comment> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 10;\n const key = o.enabled === false ? null : keyOf(\"replies\", scope, id, size);\n return useList(store, key, { type: \"replies\", id }, offsetPages(size, (q, signal) => client.comments.replies(id, q, signal)));\n}\n\n/** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\nexport function useCanComment(ref: RefBody, o: { client?: ContentKitClient } = {}) {\n const { client, store, scope } = useContentScope(o.client);\n const r = useResource<CommentStanding>(store, keyOf(\"standing\", scope, ref.kind, ref.id), { type: \"standing\", ref: { kind: ref.kind, id: ref.id } }, (s) =>\n client.comments.standing(ref, s),\n );\n return { standing: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload };\n}\n\n/** The newest published comments across the site, with their items (the host shows titles). */\nexport function useLatestComments(o: { pageSize?: number; client?: ContentKitClient } = {}): UseList<FeedItem> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n return useList(store, keyOf(\"latest\", scope, size), { type: \"latest\" }, offsetPages(size, (q, signal) => client.comments.latest(q, signal), true));\n}\n\nexport interface UseAdminComments extends UseList<AdminComment> {\n remove: (id: string) => Promise<void>;\n restore: (id: string) => Promise<void>;\n react: (comment: Comment, value: Reaction) => Promise<void>;\n}\n\n/** Staff: every comment, newest first, deleted, held and rejected ones with their bodies (root:comments:moderate); or one content kind's, or one author's. */\nexport function useAdminComments(o: { contentKind?: string; author?: string; pageSize?: number; client?: ContentKitClient } = {}): UseAdminComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 25;\n const kind = o.contentKind || undefined;\n const author = o.author || undefined;\n const list = useList(\n store,\n keyOf(\"admin-comments\", scope, kind, author, size),\n { type: \"admin-comments\" },\n offsetPages(size, (q, signal) => client.comments.adminList({ ...q, contentKind: kind, author }, signal)),\n );\n const remove = useCallback((id: string) => client.comments.delete(id), [client]);\n const restore = useCallback((id: string) => client.comments.restore(id), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, remove, restore, react };\n}\n\nexport interface UseModerationQueue extends UseList<HeldItem> {\n /** Approves (publishes) or rejects at the revision the queue listed; the item leaves the queue. */\n resolve: (item: HeldItem, decision: Decision, reason?: string) => Promise<void>;\n}\n\n/** Staff: held comments or posts awaiting review, oldest first (ModerationReview). */\nexport function useModerationQueue(o: { kind?: HeldKind; pageSize?: number; client?: ContentKitClient } = {}): UseModerationQueue {\n const { client, store, scope } = useContentScope(o.client);\n const kind = o.kind ?? \"comment\";\n const size = o.pageSize ?? 20;\n const list = useList<HeldItem>(store, keyOf(\"held\", scope, kind, size), { type: \"held\" }, async (cursor, signal) => {\n const page = await client.moderation.held({ kind, cursor: typeof cursor === \"string\" ? cursor : undefined, limit: size }, signal);\n return { items: page.items, next: page.next ?? null };\n });\n const resolve = useCallback(async (item: HeldItem, d: Decision, reason?: string) => void (await client.moderation.resolve(item, d, reason)), [client]);\n return { ...list, resolve };\n}\n\nexport interface UseCommentBans extends UseList<CommentBan & { id: string }> {\n ban: (user: string, input?: BanInput) => Promise<CommentBan>;\n lift: (user: string) => Promise<void>;\n}\n\n/** The bans of one scope: the caller's own (\"owner\") or the site's (\"global\", CommentBan), newest first. */\nexport function useCommentBans(o: { scope?: BanScope; pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseCommentBans {\n const { client, store, scope: readScope } = useContentScope(o.client);\n const scope = o.scope ?? \"global\";\n const size = o.pageSize ?? 25;\n const list = useList(\n store,\n o.enabled === false ? null : keyOf(\"bans\", readScope, scope, size),\n { type: \"bans\", scope },\n offsetPages(size, async (q, signal) => (await client.bans.list(scope, q, signal)).map((b) => ({ ...b, id: b.user_id }))),\n );\n const ban = useCallback((user: string, input?: BanInput) => client.bans.ban(scope, user, input), [client, scope]);\n const lift = useCallback((user: string) => client.bans.lift(scope, user), [client, scope]);\n return { ...list, ban, lift };\n}\n\nexport interface UseGlobalCommentBan {\n /** The user's site-wide ban, expired or not; null when none. */\n ban: CommentBan | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** Bans them, or replaces their ban. */\n save: (input?: BanInput) => Promise<CommentBan>;\n lift: () => Promise<void>;\n}\n\n/** Staff: one user's site-wide comment ban (root:comments:ban). */\nexport function useGlobalCommentBan(user: string, o: { client?: ContentKitClient } = {}): UseGlobalCommentBan {\n const { client, store, scope } = useContentScope(o.client);\n const r = useResource<CommentBan | null>(store, keyOf(\"ban\", scope, user), { type: \"bans\", scope: \"global\", id: user }, (signal) => client.bans.get(\"global\", user, signal));\n const save = useCallback((input?: BanInput) => client.bans.ban(\"global\", user, input), [client, user]);\n const lift = useCallback(() => client.bans.lift(\"global\", user), [client, user]);\n return { ban: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload, save, lift };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"comments.js","names":[],"sources":["../../src/react/comments.ts"],"sourcesContent":["import { useCallback } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { BanScope, Decision, HeldKind, Reaction, Sort, Target } from \"../client/content/types.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts } from \"../client/generated/wire.js\";\nimport type { Page, ResourceStore } from \"./resources.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** Counts after the caller's reaction changes from counts.mine to value. */\nexport function withReaction(counts: ReactionCounts, value: Reaction): ReactionCounts {\n const likes = counts.likes - (counts.mine === 1 ? 1 : 0) + (value === 1 ? 1 : 0);\n const dislikes = counts.dislikes - (counts.mine === -1 ? 1 : 0) + (value === -1 ? 1 : 0);\n return { likes, dislikes, mine: value };\n}\n\nconst commentLists = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\nconst tokens = new WeakMap<ResourceStore, Map<string, number>>();\n\n/** Sets the caller's reaction to a comment in every list showing it at once; rolls back on failure unless a newer one started. */\nasync function reactToComment(client: ContentKitClient, store: ResourceStore, scope: string, c: Pick<Comment, \"id\" | \"likes\" | \"dislikes\" | \"mine\">, value: Reaction) {\n let t = tokens.get(store);\n if (!t) tokens.set(store, (t = new Map()));\n const token = (t.get(c.id) ?? 0) + 1;\n t.set(c.id, token);\n const reads = new Set<number>();\n const put = (counts: ReactionCounts, rollback = false) =>\n store.patch<Page<Comment>>(\n (tag) => commentLists.includes(tag.type) && tag.readScope === scope,\n (page, readVersion) => {\n if (rollback && !reads.has(readVersion)) return page;\n reads.add(readVersion);\n return { ...page, items: page.items.map((x) => (x.id === c.id ? { ...x, ...counts } : x)) };\n },\n );\n put(withReaction({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, value));\n try {\n await client.comments.react(c.id, value);\n } catch (e) {\n if (t.get(c.id) === token) put({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, true);\n throw e;\n }\n}\n\nexport interface UseCommentsOptions {\n sort?: Sort;\n /** One language's thread: the list shows it and new comments join it. */\n language?: string;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n client?: ContentKitClient;\n}\n\nexport interface UseComments extends UseList<Comment> {\n /** Comments on the item, or replies to a top-level comment (replyTo); anonymous callers give anonName. */\n post: (body: string, o?: { replyTo?: string; anonName?: string }) => Promise<Comment>;\n edit: (id: string, body: string) => Promise<Comment>;\n /** Leaves a tombstone. */\n remove: (id: string) => Promise<void>;\n /** Sets the caller's reaction at once, rolled back if the server refuses it. */\n react: (comment: Comment, value: Reaction) => Promise<void>;\n /** Restores a deleted comment (moderators). */\n restore: (id: string) => Promise<void>;\n}\n\n/** A target's top-level comments, a page at a time, with every write; new comments and replies land in place. */\nexport function useComments(target: Target, o: UseCommentsOptions = {}): UseComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n const sort = o.sort ?? \"newest\";\n const language = o.language || undefined;\n const { type, id, version } = target;\n const key = keyOf(\"comments\", scope, type, id, version, language, sort, size);\n const list = useList(\n store,\n key,\n { type: \"comments\", target: { type, id, version } },\n offsetPages(size, (q, signal) => client.comments.list({ type, id, version }, { ...q, sort, language }, signal)),\n );\n const post = useCallback<UseComments[\"post\"]>(\n (body, p = {}) => client.comments.create({ type, id, version }, { body, reply_to_id: p.replyTo, anon_name: p.anonName, language: p.replyTo ? undefined : language }),\n [client, type, id, version, language],\n );\n const edit = useCallback((cid: string, body: string) => client.comments.edit(cid, body), [client]);\n const remove = useCallback((cid: string) => client.comments.delete(cid), [client]);\n const restore = useCallback((cid: string) => client.comments.restore(cid), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, post, edit, remove, react, restore };\n}\n\n/** A top-level comment's replies, oldest first, a page at a time; enabled false defers the first read. */\nexport function useCommentReplies(id: string, o: { pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseList<Comment> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 10;\n const key = o.enabled === false ? null : keyOf(\"replies\", scope, id, size);\n return useList(store, key, { type: \"replies\", id }, offsetPages(size, (q, signal) => client.comments.replies(id, q, signal)));\n}\n\n/** Whether the caller may comment on the target, the ban that stops it, and what it may do to others' comments. */\nexport function useCanComment(target: Target, o: { client?: ContentKitClient } = {}) {\n const { client, store, scope } = useContentScope(o.client);\n const { type, id, version } = target;\n const r = useResource<CommentStanding>(store, keyOf(\"standing\", scope, type, id, version), { type: \"standing\", target: { type, id, version } }, (s) =>\n client.comments.standing({ type, id, version }, s),\n );\n return { standing: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload };\n}\n\n/** The newest published comments across the site, with their items (the host shows titles). */\nexport function useLatestComments(o: { pageSize?: number; client?: ContentKitClient } = {}): UseList<FeedItem> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n return useList(store, keyOf(\"latest\", scope, size), { type: \"latest\" }, offsetPages(size, (q, signal) => client.comments.latest(q, signal), true));\n}\n\nexport interface UseAdminComments extends UseList<AdminComment> {\n remove: (id: string) => Promise<void>;\n restore: (id: string) => Promise<void>;\n react: (comment: Comment, value: Reaction) => Promise<void>;\n}\n\n/** Staff: every comment, newest first, deleted, held and rejected ones with their bodies (root:comments:moderate); or one content kind's, or one author's. */\nexport function useAdminComments(o: { contentKind?: string; author?: string; pageSize?: number; client?: ContentKitClient } = {}): UseAdminComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 25;\n const kind = o.contentKind || undefined;\n const author = o.author || undefined;\n const list = useList(\n store,\n keyOf(\"admin-comments\", scope, kind, author, size),\n { type: \"admin-comments\" },\n offsetPages(size, (q, signal) => client.comments.adminList({ ...q, contentKind: kind, author }, signal)),\n );\n const remove = useCallback((id: string) => client.comments.delete(id), [client]);\n const restore = useCallback((id: string) => client.comments.restore(id), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, remove, restore, react };\n}\n\nexport interface UseModerationQueue extends UseList<HeldItem> {\n /** Approves (publishes) or rejects at the revision the queue listed; the item leaves the queue. */\n resolve: (item: HeldItem, decision: Decision, reason?: string) => Promise<void>;\n}\n\n/** Staff: held comments or content awaiting review, oldest first (ModerationReview). */\nexport function useModerationQueue(o: { kind?: HeldKind; pageSize?: number; client?: ContentKitClient } = {}): UseModerationQueue {\n const { client, store, scope } = useContentScope(o.client);\n const kind = o.kind ?? \"comment\";\n const size = o.pageSize ?? 20;\n const list = useList<HeldItem>(store, keyOf(\"held\", scope, kind, size), { type: \"held\" }, async (cursor, signal) => {\n const page = await client.moderation.held({ kind, cursor: typeof cursor === \"string\" ? cursor : undefined, limit: size }, signal);\n return { items: page.items, next: page.next ?? null };\n });\n const resolve = useCallback(async (item: HeldItem, d: Decision, reason?: string) => void (await client.moderation.resolve(item, d, reason)), [client]);\n return { ...list, resolve };\n}\n\nexport interface UseCommentBans extends UseList<CommentBan & { id: string }> {\n ban: (user: string, input?: BanInput) => Promise<CommentBan>;\n lift: (user: string) => Promise<void>;\n}\n\n/** The bans of one scope: the caller's own (\"owner\") or the site's (\"global\", CommentBan), newest first. */\nexport function useCommentBans(o: { scope?: BanScope; pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseCommentBans {\n const { client, store, scope: readScope } = useContentScope(o.client);\n const scope = o.scope ?? \"global\";\n const size = o.pageSize ?? 25;\n const list = useList(\n store,\n o.enabled === false ? null : keyOf(\"bans\", readScope, scope, size),\n { type: \"bans\", scope },\n offsetPages(size, async (q, signal) => (await client.bans.list(scope, q, signal)).map((b) => ({ ...b, id: b.user_id }))),\n );\n const ban = useCallback((user: string, input?: BanInput) => client.bans.ban(scope, user, input), [client, scope]);\n const lift = useCallback((user: string) => client.bans.lift(scope, user), [client, scope]);\n return { ...list, ban, lift };\n}\n\nexport interface UseGlobalCommentBan {\n /** The user's site-wide ban, expired or not; null when none. */\n ban: CommentBan | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** Bans them, or replaces their ban. */\n save: (input?: BanInput) => Promise<CommentBan>;\n lift: () => Promise<void>;\n}\n\n/** Staff: one user's site-wide comment ban (root:comments:ban). */\nexport function useGlobalCommentBan(user: string, o: { client?: ContentKitClient } = {}): UseGlobalCommentBan {\n const { client, store, scope } = useContentScope(o.client);\n const r = useResource<CommentBan | null>(store, keyOf(\"ban\", scope, user), { type: \"bans\", scope: \"global\", id: user }, (signal) => client.bans.get(\"global\", user, signal));\n const save = useCallback((input?: BanInput) => client.bans.ban(\"global\", user, input), [client, user]);\n const lift = useCallback(() => client.bans.lift(\"global\", user), [client, user]);\n return { ban: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload, save, lift };\n}\n"],"mappings":";;;AASA,SAAgB,EAAa,GAAwB,GAAiC;CAGpF,OAAO;EAAE,OAFK,EAAO,QAAS,IAAO,SAAS,KAAc,QAAU;EAEtD,UADC,EAAO,WAAY,IAAO,SAAS,MAAe,QAAU;EACnD,MAAM;CAAM;AACxC;AAEA,IAAM,IAAe;CAAC;CAAY;CAAW;CAAU;AAAgB,GACjE,oBAAS,IAAI,QAA4C;AAG/D,eAAe,EAAe,GAA0B,GAAsB,GAAe,GAAwD,GAAiB;CACpK,IAAI,IAAI,EAAO,IAAI,CAAK;CACxB,AAAK,KAAG,EAAO,IAAI,GAAQ,oBAAI,IAAI,IAAI,CAAE;CACzC,IAAM,KAAS,EAAE,IAAI,EAAE,EAAE,KAAK,KAAK;CACnC,EAAE,IAAI,EAAE,IAAI,CAAK;CACjB,IAAM,oBAAQ,IAAI,IAAY,GACxB,KAAO,GAAwB,IAAW,OAC9C,EAAM,OACH,MAAQ,EAAa,SAAS,EAAI,IAAI,KAAK,EAAI,cAAc,IAC7D,GAAM,MACD,KAAY,CAAC,EAAM,IAAI,CAAW,IAAU,KAChD,EAAM,IAAI,CAAW,GACd;EAAE,GAAG;EAAM,OAAO,EAAK,MAAM,KAAK,MAAO,EAAE,OAAO,EAAE,KAAK;GAAE,GAAG;GAAG,GAAG;EAAO,IAAI,CAAE;CAAE,EAE9F;CACF,EAAI,EAAa;EAAE,OAAO,EAAE;EAAO,UAAU,EAAE;EAAU,MAAM,EAAE;CAAK,GAAG,CAAK,CAAC;CAC/E,IAAI;EACF,MAAM,EAAO,SAAS,MAAM,EAAE,IAAI,CAAK;CACzC,SAAS,GAAG;EAEV,MADI,EAAE,IAAI,EAAE,EAAE,MAAM,KAAO,EAAI;GAAE,OAAO,EAAE;GAAO,UAAU,EAAE;GAAU,MAAM,EAAE;EAAK,GAAG,EAAI,GACrF;CACR;AACF;AAwBA,SAAgB,EAAY,GAAgB,IAAwB,CAAC,GAAgB;CACnF,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY,IACrB,IAAO,EAAE,QAAQ,UACjB,IAAW,EAAE,YAAY,KAAA,GACzB,EAAE,SAAM,OAAI,eAAY,GACxB,IAAM,EAAM,YAAY,GAAO,GAAM,GAAI,GAAS,GAAU,GAAM,CAAI,GACtE,IAAO,EACX,GACA,GACA;EAAE,MAAM;EAAY,QAAQ;GAAE;GAAM;GAAI;EAAQ;CAAE,GAClD,EAAY,IAAO,GAAG,MAAW,EAAO,SAAS,KAAK;EAAE;EAAM;EAAI;CAAQ,GAAG;EAAE,GAAG;EAAG;EAAM;CAAS,GAAG,CAAM,CAAC,CAChH,GACM,IAAO,GACV,GAAM,IAAI,CAAC,MAAM,EAAO,SAAS,OAAO;EAAE;EAAM;EAAI;CAAQ,GAAG;EAAE;EAAM,aAAa,EAAE;EAAS,WAAW,EAAE;EAAU,UAAU,EAAE,UAAU,KAAA,IAAY;CAAS,CAAC,GACnK;EAAC;EAAQ;EAAM;EAAI;EAAS;CAAQ,CACtC,GACM,IAAO,GAAa,GAAa,MAAiB,EAAO,SAAS,KAAK,GAAK,CAAI,GAAG,CAAC,CAAM,CAAC,GAC3F,IAAS,GAAa,MAAgB,EAAO,SAAS,OAAO,CAAG,GAAG,CAAC,CAAM,CAAC,GAC3E,IAAU,GAAa,MAAgB,EAAO,SAAS,QAAQ,CAAG,GAAG,CAAC,CAAM,CAAC,GAC7E,IAAQ,GAAa,GAAY,MAAgB,EAAe,GAAQ,GAAO,GAAO,GAAG,CAAC,GAAG;EAAC;EAAQ;EAAO;CAAK,CAAC;CACzH,OAAO;EAAE,GAAG;EAAM;EAAM;EAAM;EAAQ;EAAO;CAAQ;AACvD;AAGA,SAAgB,EAAkB,GAAY,IAAyE,CAAC,GAAqB;CAC3I,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY,IACrB,IAAM,EAAE,YAAY,KAAQ,OAAO,EAAM,WAAW,GAAO,GAAI,CAAI;CACzE,OAAO,EAAQ,GAAO,GAAK;EAAE,MAAM;EAAW;CAAG,GAAG,EAAY,IAAO,GAAG,MAAW,EAAO,SAAS,QAAQ,GAAI,GAAG,CAAM,CAAC,CAAC;AAC9H;AAGA,SAAgB,EAAc,GAAgB,IAAmC,CAAC,GAAG;CACnF,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,EAAE,SAAM,OAAI,eAAY,GACxB,IAAI,EAA6B,GAAO,EAAM,YAAY,GAAO,GAAM,GAAI,CAAO,GAAG;EAAE,MAAM;EAAY,QAAQ;GAAE;GAAM;GAAI;EAAQ;CAAE,IAAI,MAC/I,EAAO,SAAS,SAAS;EAAE;EAAM;EAAI;CAAQ,GAAG,CAAC,CACnD;CACA,OAAO;EAAE,UAAU,EAAE,QAAQ;EAAM,SAAS,EAAE,WAAW,CAAC,EAAE;EAAQ,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AACvG;AAGA,SAAgB,EAAkB,IAAsD,CAAC,GAAsB;CAC7G,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY;CAC3B,OAAO,EAAQ,GAAO,EAAM,UAAU,GAAO,CAAI,GAAG,EAAE,MAAM,SAAS,GAAG,EAAY,IAAO,GAAG,MAAW,EAAO,SAAS,OAAO,GAAG,CAAM,GAAG,EAAI,CAAC;AACnJ;AASA,SAAgB,EAAiB,IAA6F,CAAC,GAAqB;CAClJ,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY,IACrB,IAAO,EAAE,eAAe,KAAA,GACxB,IAAS,EAAE,UAAU,KAAA,GACrB,IAAO,EACX,GACA,EAAM,kBAAkB,GAAO,GAAM,GAAQ,CAAI,GACjD,EAAE,MAAM,iBAAiB,GACzB,EAAY,IAAO,GAAG,MAAW,EAAO,SAAS,UAAU;EAAE,GAAG;EAAG,aAAa;EAAM;CAAO,GAAG,CAAM,CAAC,CACzG,GACM,IAAS,GAAa,MAAe,EAAO,SAAS,OAAO,CAAE,GAAG,CAAC,CAAM,CAAC,GACzE,IAAU,GAAa,MAAe,EAAO,SAAS,QAAQ,CAAE,GAAG,CAAC,CAAM,CAAC,GAC3E,IAAQ,GAAa,GAAY,MAAgB,EAAe,GAAQ,GAAO,GAAO,GAAG,CAAC,GAAG;EAAC;EAAQ;EAAO;CAAK,CAAC;CACzH,OAAO;EAAE,GAAG;EAAM;EAAQ;EAAS;CAAM;AAC3C;AAQA,SAAgB,EAAmB,IAAuE,CAAC,GAAuB;CAChI,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,QAAQ,WACjB,IAAO,EAAE,YAAY,IACrB,IAAO,EAAkB,GAAO,EAAM,QAAQ,GAAO,GAAM,CAAI,GAAG,EAAE,MAAM,OAAO,GAAG,OAAO,GAAQ,MAAW;EAClH,IAAM,IAAO,MAAM,EAAO,WAAW,KAAK;GAAE;GAAM,QAAQ,OAAO,KAAW,WAAW,IAAS,KAAA;GAAW,OAAO;EAAK,GAAG,CAAM;EAChI,OAAO;GAAE,OAAO,EAAK;GAAO,MAAM,EAAK,QAAQ;EAAK;CACtD,CAAC,GACK,IAAU,EAAY,OAAO,GAAgB,GAAa,MAAoB,KAAM,MAAM,EAAO,WAAW,QAAQ,GAAM,GAAG,CAAM,GAAI,CAAC,CAAM,CAAC;CACrJ,OAAO;EAAE,GAAG;EAAM;CAAQ;AAC5B;AAQA,SAAgB,EAAe,IAA2F,CAAC,GAAmB;CAC5I,IAAM,EAAE,WAAQ,UAAO,OAAO,MAAc,EAAgB,EAAE,MAAM,GAC9D,IAAQ,EAAE,SAAS,UACnB,IAAO,EAAE,YAAY,IACrB,IAAO,EACX,GACA,EAAE,YAAY,KAAQ,OAAO,EAAM,QAAQ,GAAW,GAAO,CAAI,GACjE;EAAE,MAAM;EAAQ;CAAM,GACtB,EAAY,GAAM,OAAO,GAAG,OAAY,MAAM,EAAO,KAAK,KAAK,GAAO,GAAG,CAAM,EAAA,CAAG,KAAK,OAAO;EAAE,GAAG;EAAG,IAAI,EAAE;CAAQ,EAAE,CAAC,CACzH,GACM,IAAM,GAAa,GAAc,MAAqB,EAAO,KAAK,IAAI,GAAO,GAAM,CAAK,GAAG,CAAC,GAAQ,CAAK,CAAC,GAC1G,IAAO,GAAa,MAAiB,EAAO,KAAK,KAAK,GAAO,CAAI,GAAG,CAAC,GAAQ,CAAK,CAAC;CACzF,OAAO;EAAE,GAAG;EAAM;EAAK;CAAK;AAC9B;AAcA,SAAgB,EAAoB,GAAc,IAAmC,CAAC,GAAwB;CAC5G,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAI,EAA+B,GAAO,EAAM,OAAO,GAAO,CAAI,GAAG;EAAE,MAAM;EAAQ,OAAO;EAAU,IAAI;CAAK,IAAI,MAAW,EAAO,KAAK,IAAI,UAAU,GAAM,CAAM,CAAC,GACrK,IAAO,GAAa,MAAqB,EAAO,KAAK,IAAI,UAAU,GAAM,CAAK,GAAG,CAAC,GAAQ,CAAI,CAAC,GAC/F,IAAO,QAAkB,EAAO,KAAK,KAAK,UAAU,CAAI,GAAG,CAAC,GAAQ,CAAI,CAAC;CAC/E,OAAO;EAAE,KAAK,EAAE,QAAQ;EAAM,SAAS,EAAE,WAAW,CAAC,EAAE;EAAQ,OAAO,EAAE;EAAO,QAAQ,EAAE;EAAQ;EAAM;CAAK;AAC9G"}
@@ -0,0 +1,57 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { CatalogText, Content, ContentInput, ContentPatch } from '../client/generated/wire.js';
4
+ import { ManageQuery } from '../client/contents.js';
5
+ import { UseList } from './use-resource.js';
6
+ export interface ManageFilter extends Omit<ManageQuery, "limit" | "offset"> {
7
+ pageSize?: number;
8
+ }
9
+ /** The content items the caller manages, newest first, a page at a time. */
10
+ export declare function useManagedContents(f?: ManageFilter, o?: {
11
+ client?: ContentKitClient;
12
+ }): UseList<Content>;
13
+ export interface UseContent {
14
+ /** null before create (or while loading). */
15
+ content: Content | null;
16
+ loading: boolean;
17
+ error?: ContentKitError;
18
+ /** Writes in flight. */
19
+ saving: boolean;
20
+ /** Reads the item again. */
21
+ reload: () => void;
22
+ /** Creates the item as a draft; the hook then edits it. */
23
+ create: (input: ContentInput) => Promise<Content>;
24
+ update: (patch: ContentPatch) => Promise<Content>;
25
+ /** Sets the text in the hook's language; a body from the editor is stored with image references (storedBody). */
26
+ putText: (text: CatalogText) => Promise<Content>;
27
+ /** Publishes now, or schedules for at. */
28
+ publish: (at?: string) => Promise<Content>;
29
+ unpublish: () => Promise<Content>;
30
+ remove: () => Promise<void>;
31
+ restore: () => Promise<Content>;
32
+ /** Uploads the cover (null clears it); resolves with a URL that shows it now. */
33
+ setCover: (file: Blob | null) => Promise<string | null>;
34
+ /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */
35
+ uploadImage: (file: Blob) => Promise<string>;
36
+ /**
37
+ * A URL that shows one of the item's images now, by name ("i-{uuid}") or
38
+ * reference: a fresh upload from its file, else its public file
39
+ * (`content.images`), else, while it has none (an unpublished item's), its
40
+ * signed editor view from an editor read. Anything else is returned as is.
41
+ */
42
+ imageSrc: (image: string | null | undefined) => string | undefined;
43
+ /** The cover through imageSrc. */
44
+ coverSrc: string | undefined;
45
+ /** The language's stored text, its body's images through imageSrc: what an editor shows. */
46
+ text: CatalogText | undefined;
47
+ /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */
48
+ storedBody: (html: string) => string;
49
+ }
50
+ /**
51
+ * One content item of kind for its editors: read, create, text in language,
52
+ * publish, delete, restore, cover and body images. null creates a new item.
53
+ */
54
+ export declare function useContent(id: string | null | undefined, kind: string, language: string, o?: {
55
+ initial?: Content;
56
+ client?: ContentKitClient;
57
+ }): UseContent;