@openrails/contentkit-ui 0.103.2 → 0.104.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/README.md +111 -49
  2. package/dist/client/beacons.d.ts +3 -13
  3. package/dist/client/beacons.js +8 -46
  4. package/dist/client/beacons.js.map +1 -1
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +45 -43
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/content/favorites.d.ts +2 -0
  9. package/dist/client/content/favorites.js.map +1 -1
  10. package/dist/client/content/types.d.ts +3 -0
  11. package/dist/client/content/types.js.map +1 -1
  12. package/dist/client/contents.d.ts +7 -1
  13. package/dist/client/contents.js +8 -0
  14. package/dist/client/contents.js.map +1 -1
  15. package/dist/client/discovery.d.ts +34 -5
  16. package/dist/client/discovery.js +23 -2
  17. package/dist/client/discovery.js.map +1 -1
  18. package/dist/client/engagement.d.ts +70 -0
  19. package/dist/client/engagement.js +191 -0
  20. package/dist/client/engagement.js.map +1 -0
  21. package/dist/client/generated/routes.d.ts +74 -35
  22. package/dist/client/generated/routes.js +119 -60
  23. package/dist/client/generated/routes.js.map +1 -1
  24. package/dist/client/generated/wire.d.ts +88 -85
  25. package/dist/client/generated/wire.js.map +1 -1
  26. package/dist/client/guest-reading-history.d.ts +40 -0
  27. package/dist/client/guest-reading-history.js +144 -0
  28. package/dist/client/guest-reading-history.js.map +1 -0
  29. package/dist/client/index.d.ts +5 -3
  30. package/dist/client/media/windows.d.ts +2 -0
  31. package/dist/client/media/windows.js +4 -1
  32. package/dist/client/media/windows.js.map +1 -1
  33. package/dist/client/stats.d.ts +4 -20
  34. package/dist/client/stats.js +0 -11
  35. package/dist/client/stats.js.map +1 -1
  36. package/dist/client/versions.d.ts +8 -6
  37. package/dist/client/versions.js +15 -10
  38. package/dist/client/versions.js.map +1 -1
  39. package/dist/client.js +24 -22
  40. package/dist/components/admin/bulk-drafts.d.ts +17 -0
  41. package/dist/components/admin/bulk-drafts.js +41 -0
  42. package/dist/components/admin/bulk-drafts.js.map +1 -0
  43. package/dist/components/admin/content-admin.d.ts +1 -1
  44. package/dist/components/admin/content-admin.js.map +1 -1
  45. package/dist/components/admin/content-manager.d.ts +11 -3
  46. package/dist/components/admin/content-manager.js +751 -394
  47. package/dist/components/admin/content-manager.js.map +1 -1
  48. package/dist/components/admin/group-manager.d.ts +8 -3
  49. package/dist/components/admin/group-manager.js +218 -146
  50. package/dist/components/admin/group-manager.js.map +1 -1
  51. package/dist/components/admin/page-notices.d.ts +16 -0
  52. package/dist/components/admin/page-notices.js +113 -0
  53. package/dist/components/admin/page-notices.js.map +1 -0
  54. package/dist/components/admin/page-picker.d.ts +38 -0
  55. package/dist/components/admin/page-picker.js +159 -0
  56. package/dist/components/admin/page-picker.js.map +1 -0
  57. package/dist/components/admin/quick-search.d.ts +14 -0
  58. package/dist/components/admin/quick-search.js +60 -0
  59. package/dist/components/admin/quick-search.js.map +1 -0
  60. package/dist/components/admin/reader-content-editor.js +382 -321
  61. package/dist/components/admin/reader-content-editor.js.map +1 -1
  62. package/dist/components/admin/reader-manifest.js +181 -179
  63. package/dist/components/admin/reader-manifest.js.map +1 -1
  64. package/dist/components/admin/shell.d.ts +10 -2
  65. package/dist/components/admin/shell.js +114 -42
  66. package/dist/components/admin/shell.js.map +1 -1
  67. package/dist/components/admin/stats-dashboard.d.ts +4 -9
  68. package/dist/components/admin/stats-dashboard.js +303 -207
  69. package/dist/components/admin/stats-dashboard.js.map +1 -1
  70. package/dist/components/admin/upload-queue.d.ts +14 -0
  71. package/dist/components/admin/upload-queue.js +115 -98
  72. package/dist/components/admin/upload-queue.js.map +1 -1
  73. package/dist/components/content/card.d.ts +31 -3
  74. package/dist/components/content/card.js +324 -158
  75. package/dist/components/content/card.js.map +1 -1
  76. package/dist/components/content/daily-releases.js +144 -190
  77. package/dist/components/content/daily-releases.js.map +1 -1
  78. package/dist/components/content/editor-save.js +27 -25
  79. package/dist/components/content/editor-save.js.map +1 -1
  80. package/dist/components/content/editors.js +274 -253
  81. package/dist/components/content/editors.js.map +1 -1
  82. package/dist/components/content/favorites.d.ts +6 -0
  83. package/dist/components/content/favorites.js +47 -0
  84. package/dist/components/content/favorites.js.map +1 -0
  85. package/dist/components/content/filter-dialog.js +19 -15
  86. package/dist/components/content/filter-dialog.js.map +1 -1
  87. package/dist/components/content/filters.d.ts +2 -0
  88. package/dist/components/content/filters.js +5 -1
  89. package/dist/components/content/filters.js.map +1 -1
  90. package/dist/components/content/grid.d.ts +44 -16
  91. package/dist/components/content/grid.js +308 -270
  92. package/dist/components/content/grid.js.map +1 -1
  93. package/dist/components/content/history.d.ts +6 -0
  94. package/dist/components/content/history.js +111 -0
  95. package/dist/components/content/history.js.map +1 -0
  96. package/dist/components/content/index.d.ts +6 -3
  97. package/dist/components/content/listing.d.ts +14 -3
  98. package/dist/components/content/listing.js +65 -56
  99. package/dist/components/content/listing.js.map +1 -1
  100. package/dist/components/content/offer-fields.js +6 -6
  101. package/dist/components/content/paged-grid.d.ts +10 -0
  102. package/dist/components/content/paged-grid.js +116 -0
  103. package/dist/components/content/paged-grid.js.map +1 -0
  104. package/dist/components/content/pagination.d.ts +19 -0
  105. package/dist/components/content/pagination.js +97 -0
  106. package/dist/components/content/pagination.js.map +1 -0
  107. package/dist/components/content/paywall.d.ts +9 -0
  108. package/dist/components/content/paywall.js +27 -3
  109. package/dist/components/content/paywall.js.map +1 -1
  110. package/dist/components/content/post-list.d.ts +37 -0
  111. package/dist/components/content/post-list.js +203 -0
  112. package/dist/components/content/post-list.js.map +1 -0
  113. package/dist/components/content/reader-settings.d.ts +2 -0
  114. package/dist/components/content/reader-settings.js +1 -1
  115. package/dist/components/content/reader-settings.js.map +1 -1
  116. package/dist/components/content/search.d.ts +11 -4
  117. package/dist/components/content/search.js +22 -11
  118. package/dist/components/content/search.js.map +1 -1
  119. package/dist/components/content/summary-tile.d.ts +14 -0
  120. package/dist/components/content/summary-tile.js +63 -0
  121. package/dist/components/content/summary-tile.js.map +1 -0
  122. package/dist/components/groups/banner.d.ts +6 -2
  123. package/dist/components/groups/banner.js +79 -61
  124. package/dist/components/groups/banner.js.map +1 -1
  125. package/dist/components/groups/directory.d.ts +12 -2
  126. package/dist/components/groups/directory.js +168 -105
  127. package/dist/components/groups/directory.js.map +1 -1
  128. package/dist/components/groups/editor.js +10 -10
  129. package/dist/components/groups/editor.js.map +1 -1
  130. package/dist/components/groups/entity-chips.d.ts +61 -0
  131. package/dist/components/groups/entity-chips.js +382 -0
  132. package/dist/components/groups/entity-chips.js.map +1 -0
  133. package/dist/components/groups/group-members.d.ts +29 -0
  134. package/dist/components/groups/group-members.js +474 -0
  135. package/dist/components/groups/group-members.js.map +1 -0
  136. package/dist/components/groups/header.js +53 -45
  137. package/dist/components/groups/header.js.map +1 -1
  138. package/dist/components/groups/index.d.ts +3 -1
  139. package/dist/components/groups/memberships.d.ts +3 -36
  140. package/dist/components/groups/memberships.js +105 -480
  141. package/dist/components/groups/memberships.js.map +1 -1
  142. package/dist/components/groups/scoped.d.ts +23 -0
  143. package/dist/components/groups/scoped.js +92 -0
  144. package/dist/components/groups/scoped.js.map +1 -0
  145. package/dist/components/media-folder-editor.d.ts +5 -0
  146. package/dist/components/media-folder-editor.js +419 -231
  147. package/dist/components/media-folder-editor.js.map +1 -1
  148. package/dist/components/sortable-list.d.ts +5 -1
  149. package/dist/components/sortable-list.js +97 -81
  150. package/dist/components/sortable-list.js.map +1 -1
  151. package/dist/components/video-mini-player.d.ts +4 -1
  152. package/dist/components/video-mini-player.js +77 -71
  153. package/dist/components/video-mini-player.js.map +1 -1
  154. package/dist/components/video-player.d.ts +4 -1
  155. package/dist/components/video-player.js +280 -276
  156. package/dist/components/video-player.js.map +1 -1
  157. package/dist/index.d.ts +2 -1
  158. package/dist/index.js +41 -34
  159. package/dist/locales/admin/de.js +80 -47
  160. package/dist/locales/admin/de.js.map +1 -1
  161. package/dist/locales/admin/en.d.ts +74 -41
  162. package/dist/locales/admin/en.js +80 -47
  163. package/dist/locales/admin/en.js.map +1 -1
  164. package/dist/locales/admin/es.js +80 -47
  165. package/dist/locales/admin/es.js.map +1 -1
  166. package/dist/locales/admin/ja.js +80 -47
  167. package/dist/locales/admin/ja.js.map +1 -1
  168. package/dist/locales/admin/ko.js +80 -47
  169. package/dist/locales/admin/ko.js.map +1 -1
  170. package/dist/locales/admin/zh.js +80 -47
  171. package/dist/locales/admin/zh.js.map +1 -1
  172. package/dist/locales/de.js +1 -0
  173. package/dist/locales/de.js.map +1 -1
  174. package/dist/locales/discovery/de.js +25 -2
  175. package/dist/locales/discovery/de.js.map +1 -1
  176. package/dist/locales/discovery/en.d.ts +24 -1
  177. package/dist/locales/discovery/en.js +25 -2
  178. package/dist/locales/discovery/en.js.map +1 -1
  179. package/dist/locales/discovery/es.js +25 -2
  180. package/dist/locales/discovery/es.js.map +1 -1
  181. package/dist/locales/discovery/ja.js +25 -2
  182. package/dist/locales/discovery/ja.js.map +1 -1
  183. package/dist/locales/discovery/ko.js +25 -2
  184. package/dist/locales/discovery/ko.js.map +1 -1
  185. package/dist/locales/discovery/zh.js +25 -2
  186. package/dist/locales/discovery/zh.js.map +1 -1
  187. package/dist/locales/en.d.ts +119 -44
  188. package/dist/locales/en.js +1 -0
  189. package/dist/locales/en.js.map +1 -1
  190. package/dist/locales/es.js +1 -0
  191. package/dist/locales/es.js.map +1 -1
  192. package/dist/locales/groups/de.js +22 -4
  193. package/dist/locales/groups/de.js.map +1 -1
  194. package/dist/locales/groups/en.d.ts +20 -2
  195. package/dist/locales/groups/en.js +22 -4
  196. package/dist/locales/groups/en.js.map +1 -1
  197. package/dist/locales/groups/es.js +22 -4
  198. package/dist/locales/groups/es.js.map +1 -1
  199. package/dist/locales/groups/ja.js +22 -4
  200. package/dist/locales/groups/ja.js.map +1 -1
  201. package/dist/locales/groups/ko.js +22 -4
  202. package/dist/locales/groups/ko.js.map +1 -1
  203. package/dist/locales/groups/zh.js +22 -4
  204. package/dist/locales/groups/zh.js.map +1 -1
  205. package/dist/locales/ja.js +1 -0
  206. package/dist/locales/ja.js.map +1 -1
  207. package/dist/locales/ko.js +1 -0
  208. package/dist/locales/ko.js.map +1 -1
  209. package/dist/locales/zh.js +1 -0
  210. package/dist/locales/zh.js.map +1 -1
  211. package/dist/react/discovery.d.ts +36 -17
  212. package/dist/react/discovery.js +305 -221
  213. package/dist/react/discovery.js.map +1 -1
  214. package/dist/react/groups.d.ts +19 -3
  215. package/dist/react/groups.js +49 -22
  216. package/dist/react/groups.js.map +1 -1
  217. package/dist/react/index.d.ts +7 -4
  218. package/dist/react/page-notices.d.ts +13 -0
  219. package/dist/react/page-notices.js +38 -0
  220. package/dist/react/page-notices.js.map +1 -0
  221. package/dist/react/post-reads.d.ts +23 -0
  222. package/dist/react/post-reads.js +90 -0
  223. package/dist/react/post-reads.js.map +1 -0
  224. package/dist/react/remembered-sort.d.ts +5 -0
  225. package/dist/react/remembered-sort.js +29 -0
  226. package/dist/react/remembered-sort.js.map +1 -0
  227. package/dist/react/resources.d.ts +3 -1
  228. package/dist/react/resources.js +6 -2
  229. package/dist/react/resources.js.map +1 -1
  230. package/dist/react/stats.d.ts +3 -7
  231. package/dist/react/stats.js +2 -6
  232. package/dist/react/stats.js.map +1 -1
  233. package/dist/react/use-engagement.d.ts +28 -0
  234. package/dist/react/use-engagement.js +226 -0
  235. package/dist/react/use-engagement.js.map +1 -0
  236. package/dist/react/use-resource.d.ts +4 -3
  237. package/dist/react/use-resource.js +2 -1
  238. package/dist/react/use-resource.js.map +1 -1
  239. package/dist/react.js +29 -26
  240. package/dist/styles.css +1 -1
  241. package/dist/ui/dialog.d.ts +2 -1
  242. package/dist/ui/dialog.js +23 -20
  243. package/dist/ui/dialog.js.map +1 -1
  244. package/dist/urls/index.d.ts +1 -1
  245. package/dist/urls.js +2 -2
  246. package/dist/urls.js.map +1 -1
  247. package/package.json +1 -1
  248. package/dist/components/admin/page-review.d.ts +0 -13
  249. package/dist/components/admin/page-review.js +0 -225
  250. package/dist/components/admin/page-review.js.map +0 -1
  251. package/dist/react/page-review.d.ts +0 -14
  252. package/dist/react/page-review.js +0 -37
  253. package/dist/react/page-review.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"stats-dashboard.js","names":[],"sources":["../../../src/components/admin/stats-dashboard.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport type { Period } from \"../../client/discovery.js\";\nimport type { RankedContent } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useFlowClient } from \"../../react/admin.js\";\nimport { useActivity, useTopContents } from \"../../react/stats.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { AdminRoot, LoadFailed, NoAccess, Rows, type AdminFlowProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\ntype Metric = \"views\" | \"comments\";\n\nexport interface StatsDashboardProps extends AdminFlowProps {\n /** Content kinds offered: codes, or codes with labels; the first shows first. */\n kinds: readonly Choice[];\n /** A ranked item's link; default its path. */\n contentHref?: (content: RankedContent) => string | undefined;\n /** Rows in the ranking. Default 10. */\n limit?: number;\n}\n\nconst choices = (list: readonly Choice[]) => list.map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst DAYS = [7, 30, 90] as const;\nconst METRICS: readonly Metric[] = [\"views\", \"comments\"];\nconst PERIODS: readonly Period[] = [\"7d\", \"30d\", \"90d\", \"365d\", \"all\"];\nconst refused = (e?: ContentKitError) => e?.code === \"forbidden\" || e?.code === \"unauthorized\";\n\n/**\n * Content statistics for staff (root:content:view-restricted): a kind's views\n * per day (view sessions, distinct and signed-in viewers) and its content\n * ranked by views or comments over a period. Views need the signal plane;\n * without it the dashboard says so and still ranks by comments.\n */\nexport function StatsDashboard({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: StatsDashboardProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"stats-dashboard\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Dashboard {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<StatsDashboardProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Dashboard({ kinds, contentHref, limit = 10, client }: Inner) {\n const { t } = useMessages();\n const options = choices(kinds);\n const [kind, setKind] = useState(options[0]?.value ?? \"\");\n const [days, setDays] = useState<number>(7);\n const [metric, setMetric] = useState<Metric>(\"views\");\n const [period, setPeriod] = useState<Period>(\"30d\");\n const activity = useActivity(kind ? { kind, days } : null, { client });\n const top = useTopContents(kind ? { kind, metric, period, limit } : null, { client });\n if (refused(activity.error) || refused(top.error)) return <NoAccess>{t(\"stats.noAccess\")}</NoAccess>;\n const rows = activity.data?.days ?? [];\n const most = Math.max(1, ...rows.map((d) => d.views));\n const total = rows.reduce((n, d) => n + d.views, 0);\n return (\n <>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"stats.title\")}</h2>\n {options.length > 1 && (\n <Pick label={t(\"stats.kind\")} value={kind} onChange={setKind} options={options} />\n )}\n </div>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-activity\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-medium\">{t(\"stats.activity\")}</h3>\n <Pick label={t(\"stats.days\")} value={String(days)} onChange={(v) => setDays(Number(v))} options={DAYS.map((d) => ({ value: String(d), label: t(\"stats.lastDays\", { count: d }) }))} />\n </div>\n {activity.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : activity.error ? (\n <LoadFailed error={activity.error} message={t(\"stats.loadFailed\")} retry={activity.reload} />\n ) : activity.loading && !activity.data ? (\n <Rows count={2} />\n ) : !rows.length ? (\n <p className=\"text-muted-foreground\">{t(\"stats.noActivity\")}</p>\n ) : (\n <table className=\"w-full text-left tabular-nums\" aria-busy={activity.loading}>\n <thead className=\"text-xs text-muted-foreground\">\n <tr>\n <th className=\"py-1 font-medium\">{t(\"stats.date\")}</th>\n <th className=\"py-1 font-medium\">{t(\"stats.views\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.viewers\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.users\")}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((d) => (\n <tr key={d.date} className=\"border-t border-border\" data-ckui=\"stats-day\">\n <td className=\"py-1.5 pe-3 whitespace-nowrap\">{d.date}</td>\n <td className=\"w-1/2 py-1.5 pe-3\">\n <span className=\"flex items-center gap-2\">\n <span aria-hidden className=\"h-2 rounded-full bg-primary\" style={{ width: `${Math.max(2, (d.views / most) * 100)}%` }} />\n {d.views}\n </span>\n </td>\n <td className=\"py-1.5 text-end\">{d.viewers}</td>\n <td className=\"py-1.5 text-end\">{d.users}</td>\n </tr>\n ))}\n </tbody>\n <tfoot>\n <tr className=\"border-t border-border font-medium\">\n <td className=\"py-1.5\">{t(\"stats.total\")}</td>\n <td className=\"py-1.5\" data-ckui=\"stats-total\">\n {total}\n </td>\n <td />\n <td />\n </tr>\n </tfoot>\n </table>\n )}\n </section>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-top\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-medium\">{t(\"stats.top\")}</h3>\n <div className=\"flex flex-wrap gap-2\">\n <Pick label={t(\"stats.metric\")} value={metric} onChange={(v) => setMetric(v as Metric)} options={METRICS.map((x) => ({ value: x, label: t(`stats.metric_${x}`) }))} />\n <Pick label={t(\"stats.period\")} value={period} onChange={(v) => setPeriod(v as Period)} options={PERIODS.map((x) => ({ value: x, label: t(`stats.period_${x}`) }))} />\n </div>\n </div>\n {top.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : top.error ? (\n <LoadFailed error={top.error} message={t(\"stats.loadFailed\")} retry={top.reload} />\n ) : top.loading && !top.data ? (\n <Rows count={3} />\n ) : !top.data?.contents.length ? (\n <p className=\"text-muted-foreground\">{t(\"stats.noTop\")}</p>\n ) : (\n <ol className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={top.loading}>\n {top.data.contents.map((c, i) => {\n const href = contentHref ? contentHref(c) : c.path;\n return (\n <li key={c.id} className=\"flex items-center gap-3 p-2.5\" data-ckui=\"stats-ranked\">\n <span className=\"w-6 text-end text-muted-foreground tabular-nums\">{i + 1}</span>\n {href ? (\n <a className=\"min-w-0 flex-1 break-words hover:underline\" href={href}>\n {c.title}\n </a>\n ) : (\n <span className=\"min-w-0 flex-1 break-words\">{c.title}</span>\n )}\n <span className=\"tabular-nums\" data-ckui=\"stats-value\">\n {c.value}\n </span>\n </li>\n );\n })}\n </ol>\n )}\n </section>\n </>\n );\n}\n\nfunction Pick({ label, value, onChange, options }: { label: string; value: string; onChange: (v: string) => void; options: { value: string; label: string }[] }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => options.find((o) => o.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stats-dashboard.js","names":[],"sources":["../../../src/components/admin/stats-dashboard.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport { formatDuration } from \"../../client/gallery.js\";\nimport type { ActivityDay } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useFlowClient } from \"../../react/admin.js\";\nimport { useActivity } from \"../../react/stats.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { AdminRoot, LoadFailed, NoAccess, Rows, type AdminFlowProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\n\nexport interface StatsDashboardProps extends AdminFlowProps {\n /** Content kinds offered: codes, or codes with labels; the first shows first. */\n kinds: readonly Choice[];\n}\n\nconst choices = (list: readonly Choice[]) => list.map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst DAYS = [10, 30, 90] as const;\nconst refused = (e?: ContentKitError) => e?.code === \"forbidden\" || e?.code === \"unauthorized\";\n\n// Validated categorical slots (light / dark steps), stacked bottom to top.\nconst AUDIENCES = [\n { key: \"anon\", fill: \"bg-[#2a78d6] dark:bg-[#3987e5]\" },\n { key: \"free\", fill: \"bg-[#eb6834] dark:bg-[#d95926]\" },\n { key: \"premium\", fill: \"bg-[#1baf7a] dark:bg-[#199e70]\" },\n] as const;\n\n/**\n * Staff statistics from measured attention: distinct people per UTC day who\n * read or played at least one item, by audience (anonymous, free, premium).\n * People are distinct within a day only, so days are never summed into a\n * period-unique total.\n */\nexport function StatsDashboard({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: StatsDashboardProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"stats-dashboard\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Dashboard {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<StatsDashboardProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Dashboard({ kinds, client }: Inner) {\n const { t } = useMessages();\n const options = choices(kinds);\n const [kind, setKind] = useState(options[0]?.value ?? \"\");\n const [days, setDays] = useState<number>(DAYS[0]);\n const activity = useActivity(kind ? { kind, days } : null, { client });\n if (refused(activity.error)) return <NoAccess>{t(\"stats.noAccess\")}</NoAccess>;\n // Newest first from the server; the chart reads oldest to newest.\n const rows = [...(activity.data?.days ?? [])].reverse();\n return (\n <>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"stats.title\")}</h2>\n {options.length > 1 && <Pick label={t(\"stats.kind\")} value={kind} onChange={setKind} options={options} />}\n </div>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-activity\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"grid gap-0.5\">\n <h3 className=\"font-medium\">{t(\"stats.activity\")}</h3>\n <p className=\"text-xs text-muted-foreground\">{t(\"stats.visitorsNote\")}</p>\n </div>\n <Pick\n label={t(\"stats.days\")}\n value={String(days)}\n onChange={(v) => setDays(Number(v))}\n options={DAYS.map((d) => ({ value: String(d), label: t(\"stats.lastDays\", { count: d }) }))}\n />\n </div>\n {activity.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : activity.error ? (\n <LoadFailed error={activity.error} message={t(\"stats.loadFailed\")} retry={activity.reload} />\n ) : activity.loading && !activity.data ? (\n <Rows count={3} />\n ) : (\n <div className=\"grid gap-4\" aria-busy={activity.loading}>\n <Today day={rows.at(-1)} />\n <Chart rows={rows} />\n <DayTable rows={rows} />\n </div>\n )}\n </section>\n </>\n );\n}\n\nfunction Today({ day }: { day?: ActivityDay }) {\n const { t } = useMessages();\n const tiles = [\n { label: t(\"stats.visitors\"), value: day?.readers ?? 0, fill: undefined },\n ...AUDIENCES.map((a) => ({ label: t(`stats.${a.key}`), value: day?.[a.key] ?? 0, fill: a.fill })),\n ];\n return (\n <div className=\"grid gap-2\" data-ckui=\"stats-today\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {t(\"stats.today\")}\n {day && day.active_ms > 0 && (\n <span className=\"font-normal\">\n {\" · \"}\n {t(\"stats.activeTime\")} {formatDuration(Math.floor(day.active_ms / 1000))}\n </span>\n )}\n </p>\n <dl className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n {tiles.map((tile) => (\n <div key={tile.label} className=\"grid gap-0.5 rounded-lg border border-border p-3\">\n <dt className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n {tile.fill && <span aria-hidden className={cn(\"size-2.5 rounded-sm\", tile.fill)} />}\n {tile.label}\n </dt>\n <dd className=\"text-xl font-semibold tabular-nums\">{tile.value}</dd>\n </div>\n ))}\n </dl>\n </div>\n );\n}\n\nfunction Chart({ rows }: { rows: ActivityDay[] }) {\n const m = useMessages();\n const { t } = m;\n const [active, setActive] = useState<number | null>(null);\n const most = Math.max(0, ...rows.map((d) => d.readers));\n const scale = most || 1;\n const every = Math.ceil(rows.length / 10);\n const short = new Intl.DateTimeFormat(m.language, { month: \"short\", day: \"numeric\", timeZone: \"UTC\" });\n const long = new Intl.DateTimeFormat(m.language, { dateStyle: \"medium\", timeZone: \"UTC\" });\n const at = (date: string) => new Date(`${date}T00:00:00Z`);\n const shown = active === null ? undefined : rows[active];\n return (\n <figure className=\"grid gap-2\" data-ckui=\"stats-chart\">\n <figcaption className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground\">\n {AUDIENCES.map((a) => (\n <span key={a.key} className=\"flex items-center gap-1.5\">\n <span aria-hidden className={cn(\"size-2.5 rounded-sm\", a.fill)} />\n {t(`stats.${a.key}`)}\n </span>\n ))}\n <span className=\"ms-auto tabular-nums\">{t(\"stats.peak\", { count: most })}</span>\n </figcaption>\n <div className=\"relative\">\n <div className=\"pointer-events-none absolute inset-x-0 top-0 border-t border-dashed border-border\" aria-hidden />\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 border-t border-dashed border-border/60\" aria-hidden />\n <ol className=\"relative flex h-48 items-end gap-1 border-b border-border\" onMouseLeave={() => setActive(null)}>\n {rows.map((d, i) => (\n <li\n key={d.date}\n tabIndex={0}\n data-ckui=\"stats-day\"\n aria-label={`${long.format(at(d.date))}: ${t(\"stats.visitors\")} ${d.readers}, ${t(\"stats.anon\")} ${d.anon}, ${t(\"stats.free\")} ${d.free}, ${t(\"stats.premium\")} ${d.premium}`}\n className=\"flex h-full min-w-0 flex-1 cursor-default flex-col justify-end rounded-sm outline-none hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:ring-1 focus-visible:ring-ring\"\n onMouseEnter={() => setActive(i)}\n onFocus={() => setActive(i)}\n onBlur={() => setActive(null)}\n >\n {/* 2px surface gaps between segments; the top segment carries the 4px rounded end. */}\n <span className=\"mx-auto flex w-full max-w-10 flex-col-reverse gap-[2px]\" style={{ height: `${(d.readers / scale) * 100}%` }}>\n {AUDIENCES.map((a, j) => {\n const value = d[a.key];\n if (!value) return null;\n const top = AUDIENCES.slice(j + 1).every((b) => !d[b.key]);\n return <span key={a.key} className={cn(\"min-h-[2px] w-full\", a.fill, top && \"rounded-t-[4px]\")} style={{ flexGrow: value, flexBasis: 0 }} />;\n })}\n </span>\n </li>\n ))}\n </ol>\n {shown && active !== null && (\n <div\n role=\"tooltip\"\n className=\"pointer-events-none absolute -top-2 z-10 grid min-w-36 -translate-y-full gap-1 rounded-md border border-border bg-popover p-2 text-xs text-popover-foreground shadow-md\"\n style={{ left: `clamp(0%, ${((active + 0.5) / rows.length) * 100}% - 4.5rem, calc(100% - 9rem))` }}\n >\n <p className=\"font-medium\">{long.format(at(shown.date))}</p>\n <p className=\"flex justify-between gap-3 tabular-nums\">\n <span>{t(\"stats.visitors\")}</span>\n <span className=\"font-semibold\">{shown.readers}</span>\n </p>\n {[...AUDIENCES].reverse().map((a) => (\n <p key={a.key} className=\"flex items-center justify-between gap-3 tabular-nums text-muted-foreground\">\n <span className=\"flex items-center gap-1.5\">\n <span aria-hidden className={cn(\"size-2 rounded-sm\", a.fill)} />\n {t(`stats.${a.key}`)}\n </span>\n <span className=\"text-popover-foreground\">{shown[a.key]}</span>\n </p>\n ))}\n </div>\n )}\n </div>\n <ol aria-hidden className=\"flex gap-1 text-[11px] text-muted-foreground tabular-nums\">\n {rows.map((d, i) => (\n <li key={d.date} className=\"min-w-0 flex-1 overflow-visible text-center whitespace-nowrap\">\n {(rows.length - 1 - i) % every === 0 ? short.format(at(d.date)) : \"\"}\n </li>\n ))}\n </ol>\n {most === 0 && <p className=\"text-muted-foreground\">{t(\"stats.noActivity\")}</p>}\n </figure>\n );\n}\n\nfunction DayTable({ rows }: { rows: ActivityDay[] }) {\n const { t } = useMessages();\n return (\n <details className=\"text-sm\">\n <summary className=\"cursor-pointer text-xs text-muted-foreground\">{t(\"stats.showTable\")}</summary>\n <table className=\"mt-2 w-full text-left tabular-nums\">\n <thead className=\"text-xs text-muted-foreground\">\n <tr>\n <th className=\"py-1 font-medium\">{t(\"stats.date\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.visitors\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.anon\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.free\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.premium\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.activeTime\")}</th>\n </tr>\n </thead>\n <tbody>\n {[...rows].reverse().map((d) => (\n <tr key={d.date} className=\"border-t border-border\">\n <td className=\"py-1.5 pe-3 whitespace-nowrap\">{d.date}</td>\n <td className=\"py-1.5 text-end\">{d.readers}</td>\n <td className=\"py-1.5 text-end\">{d.anon}</td>\n <td className=\"py-1.5 text-end\">{d.free}</td>\n <td className=\"py-1.5 text-end\">{d.premium}</td>\n <td className=\"py-1.5 text-end\">{formatDuration(Math.floor(d.active_ms / 1000))}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </details>\n );\n}\n\nfunction Pick({ label, value, onChange, options }: { label: string; value: string; onChange: (v: string) => void; options: { value: string; label: string }[] }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => options.find((o) => o.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { ContentKitClient } from '../../client/client.js';
1
2
  import { FileInfo, RefBody } from '../../client/generated/wire.js';
2
3
  import { AdminFlowProps } from './shell.js';
3
4
  export interface UploadQueueProps extends AdminFlowProps {
@@ -5,6 +6,13 @@ export interface UploadQueueProps extends AdminFlowProps {
5
6
  createItem: (file: File) => Promise<RefBody>;
6
7
  /** The upload path in each item. Default "source". */
7
8
  path?: string;
9
+ /** Where a file uploads in its new item (path and media type), in place of path. */
10
+ target?: (ref: RefBody, file: File, signal: AbortSignal) => Promise<{
11
+ path: string;
12
+ type?: string;
13
+ }>;
14
+ /** Opens a file's item: an Open button once it has one. */
15
+ onOpen?: (ref: RefBody) => void;
8
16
  /** The file picker's accept, e.g. "video/*". */
9
17
  accept?: string;
10
18
  /** Files uploading at once. Default 2. */
@@ -21,3 +29,9 @@ export interface UploadQueueProps extends AdminFlowProps {
21
29
  * the same item. Leaving the page while files upload asks first.
22
30
  */
23
31
  export declare function UploadQueue({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: UploadQueueProps): import('react').JSX.Element;
32
+ type Inner = Omit<UploadQueueProps, "client" | "baseUrl" | "auth" | "guard" | "appearance" | "className" | "language" | "messages"> & {
33
+ client: ContentKitClient;
34
+ };
35
+ /** The queue inside an admin flow that already has its root (ContentManager's bulk upload). */
36
+ export declare function UploadQueueBody(p: Inner): import('react').JSX.Element;
37
+ export {};
@@ -43,133 +43,144 @@ function w({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className:
43
43
  language: o,
44
44
  messages: s,
45
45
  guard: r,
46
- children: /* @__PURE__ */ g(T, {
46
+ children: /* @__PURE__ */ g(E, {
47
47
  ...c,
48
48
  client: d
49
49
  })
50
50
  });
51
51
  }
52
- function T({ client: l, createItem: u, path: w = "source", accept: T, concurrency: E = 2, onUploaded: D, storageKey: O = "ckui.uploads", onError: k }) {
53
- let A = r(), { t: j } = A, M = i(k), N = a(), [P, F] = m([]), [I, L] = m(() => Object.values(S(O))), [R, z] = m(!1), B = p(null), V = p(/* @__PURE__ */ new Map()), H = p({
52
+ function T(e) {
53
+ return /* @__PURE__ */ g(E, { ...e });
54
+ }
55
+ function E({ client: l, createItem: u, path: w = "source", target: T, accept: E, concurrency: D = 2, onUploaded: O, onOpen: k, storageKey: A = "ckui.uploads", onError: j }) {
56
+ let M = r(), { t: N } = M, P = i(j), F = a(), [I, L] = m([]), [R, z] = m(() => Object.values(S(A))), [B, V] = m(!1), H = p(null), U = p(/* @__PURE__ */ new Map()), W = p({
54
57
  createItem: u,
55
- onUploaded: D
58
+ onUploaded: O,
59
+ place: T
56
60
  });
57
61
  f(() => {
58
- H.current = {
62
+ W.current = {
59
63
  createItem: u,
60
- onUploaded: D
64
+ onUploaded: O,
65
+ place: T
61
66
  };
62
67
  });
63
- let U = d((e, t) => F((n) => n.map((n) => n.id === e ? {
68
+ let G = d((e, t) => L((n) => n.map((n) => n.id === e ? {
64
69
  ...n,
65
70
  ...t
66
- } : n)), []), W = d(async (n) => {
71
+ } : n)), []), K = d(async (n) => {
67
72
  let r = new AbortController();
68
- V.current.set(n.id, r);
69
- let i = S(O)[n.key];
73
+ U.current.set(n.id, r);
74
+ let i = S(A)[n.key];
70
75
  try {
71
76
  let a = n.ref ?? i?.ref;
72
77
  if (!a) {
73
- U(n.id, {
78
+ G(n.id, {
74
79
  status: "creating",
75
80
  error: void 0
76
- }), a = await H.current.createItem(n.file);
81
+ }), a = await W.current.createItem(n.file);
77
82
  let e = a;
78
- C(O, (t) => void (t[n.key] = {
83
+ C(A, (t) => void (t[n.key] = {
79
84
  ref: e,
80
85
  name: n.file.name,
81
86
  size: n.file.size
82
87
  }));
83
88
  }
84
89
  let o = a;
85
- U(n.id, {
86
- status: "uploading",
90
+ G(n.id, {
87
91
  ref: a,
88
92
  error: void 0
89
93
  });
90
- let s = {
91
- ref: a,
94
+ let s = W.current.place, c = s ? await F(() => s(o, n.file, r.signal)) : {
92
95
  path: w,
96
+ type: void 0
97
+ };
98
+ G(n.id, { status: "uploading" });
99
+ let u = {
100
+ ref: a,
101
+ path: c.path,
102
+ type: c.type,
93
103
  signal: r.signal,
94
- onProgress: (e) => U(n.id, { progress: e }),
95
- onState: (e) => C(O, (t) => void (t[n.key] = {
104
+ onProgress: (e) => G(n.id, { progress: e }),
105
+ onState: (e) => C(A, (t) => void (t[n.key] = {
96
106
  ref: o,
97
107
  name: n.file.name,
98
108
  size: n.file.size,
99
109
  state: e ?? void 0
100
110
  }))
101
- }, c = await N(async () => {
111
+ }, d = await F(async () => {
102
112
  try {
103
113
  return await l.media.upload(n.file, {
104
- ...s,
114
+ ...u,
105
115
  resume: i?.state
106
116
  });
107
117
  } catch (t) {
108
- if (i?.state && e(t).code === "resume_mismatch") return l.media.upload(n.file, s);
118
+ if (i?.state && e(t).code === "resume_mismatch") return l.media.upload(n.file, u);
109
119
  throw t;
110
120
  }
111
121
  });
112
- U(n.id, { status: "committing" });
113
- let u = await N(() => l.media.commit(o, [{
122
+ G(n.id, { status: "committing" });
123
+ let f = await F(() => l.media.commit(o, [{
114
124
  op: "put",
115
- path: c.path,
116
- blob: c.blob
125
+ path: d.path,
126
+ source_name: d.sourceName,
127
+ blob: d.blob
117
128
  }], { signal: r.signal }));
118
- C(O, (e) => void delete e[n.key]), U(n.id, {
129
+ C(A, (e) => void delete e[n.key]), G(n.id, {
119
130
  status: "done",
120
131
  progress: void 0
121
132
  });
122
- let d = u.find((e) => t(e.path, c.path));
123
- d && H.current.onUploaded?.(o, d);
133
+ let p = f.find((e) => t(e.path, d.path));
134
+ p && W.current.onUploaded?.(o, p);
124
135
  } catch (t) {
125
136
  let r = e(t);
126
- r.code === "aborted" ? U(n.id, { status: "paused" }) : (U(n.id, {
137
+ r.code === "aborted" ? G(n.id, { status: "paused" }) : (G(n.id, {
127
138
  status: "failed",
128
139
  error: r
129
- }), M(r, "upload", n.file.name));
140
+ }), P(r, "upload", n.file.name));
130
141
  } finally {
131
- V.current.delete(n.id);
142
+ U.current.delete(n.id);
132
143
  }
133
144
  }, [
134
145
  l,
135
146
  w,
136
- O,
137
- N,
138
- U,
139
- M
147
+ A,
148
+ F,
149
+ G,
150
+ P
140
151
  ]);
141
152
  f(() => {
142
- let e = P.filter((e) => y.includes(e.status)).length, t = P.filter((e) => e.status === "queued").slice(0, Math.max(0, E - e));
153
+ let e = I.filter((e) => y.includes(e.status)).length, t = I.filter((e) => e.status === "queued").slice(0, Math.max(0, D - e));
143
154
  if (t.length) {
144
- F((e) => e.map((e) => t.some((t) => t.id === e.id) ? {
155
+ L((e) => e.map((e) => t.some((t) => t.id === e.id) ? {
145
156
  ...e,
146
157
  status: "creating"
147
158
  } : e));
148
- for (let e of t) W(e);
159
+ for (let e of t) K(e);
149
160
  }
150
161
  }, [
151
- P,
152
- E,
153
- W
162
+ I,
163
+ D,
164
+ K
154
165
  ]);
155
- let G = P.some((e) => y.includes(e.status) || e.status === "queued");
166
+ let q = I.some((e) => y.includes(e.status) || e.status === "queued");
156
167
  f(() => {
157
- if (!G) return;
168
+ if (!q) return;
158
169
  let e = (e) => {
159
170
  e.preventDefault(), e.returnValue = "";
160
171
  };
161
172
  return addEventListener("beforeunload", e), () => removeEventListener("beforeunload", e);
162
- }, [G]), f(() => {
163
- let e = V.current;
173
+ }, [q]), f(() => {
174
+ let e = U.current;
164
175
  return () => {
165
176
  for (let t of e.values()) t.abort();
166
177
  };
167
178
  }, []);
168
- let K = (e) => {
169
- let t = S(O), n = [];
179
+ let J = (e) => {
180
+ let t = S(A), n = [];
170
181
  for (let r of e) {
171
182
  let e = b(r);
172
- P.some((t) => t.key === e && t.status !== "done") || n.some((t) => t.key === e) || n.push({
183
+ I.some((t) => t.key === e && t.status !== "done") || n.some((t) => t.key === e) || n.push({
173
184
  id: `u${++x}`,
174
185
  file: r,
175
186
  key: e,
@@ -178,70 +189,70 @@ function T({ client: l, createItem: u, path: w = "source", accept: T, concurrenc
178
189
  resumed: !!t[e]
179
190
  });
180
191
  }
181
- n.length && (L((e) => e.filter((e) => !n.some((t) => t.file.name === e.name && t.file.size === e.size))), F((e) => [...e, ...n]));
182
- }, q = (e) => {
183
- for (let [t, n] of V.current) (!e || t === e) && n.abort();
184
- F((t) => t.map((t) => t.status === "queued" && (!e || t.id === e) ? {
192
+ n.length && (z((e) => e.filter((e) => !n.some((t) => t.file.name === e.name && t.file.size === e.size))), L((e) => [...e, ...n]));
193
+ }, Y = (e) => {
194
+ for (let [t, n] of U.current) (!e || t === e) && n.abort();
195
+ L((t) => t.map((t) => t.status === "queued" && (!e || t.id === e) ? {
185
196
  ...t,
186
197
  status: "paused"
187
198
  } : t));
188
- }, J = (e) => F((t) => t.map((t) => (t.status === "paused" || t.status === "failed") && (!e || t.id === e) ? {
199
+ }, X = (e) => L((t) => t.map((t) => (t.status === "paused" || t.status === "failed") && (!e || t.id === e) ? {
189
200
  ...t,
190
201
  status: "queued",
191
202
  error: void 0
192
- } : t)), Y = (e) => {
193
- V.current.get(e.id)?.abort(), e.status !== "done" && C(O, (t) => void delete t[e.key]), F((t) => t.filter((t) => t.id !== e.id));
194
- }, X = (e) => {
195
- C(O, (t) => {
203
+ } : t)), Z = (e) => {
204
+ U.current.get(e.id)?.abort(), e.status !== "done" && C(A, (t) => void delete t[e.key]), L((t) => t.filter((t) => t.id !== e.id));
205
+ }, Q = (e) => {
206
+ C(A, (t) => {
196
207
  for (let [n, r] of Object.entries(t)) r.name === e.name && r.size === e.size && delete t[n];
197
- }), L((t) => t.filter((t) => t !== e));
198
- }, Z = P.filter((e) => e.status === "done").length, Q = P.some((e) => y.includes(e.status) || e.status === "queued"), ee = P.some((e) => e.status === "paused" || e.status === "failed"), $ = (e) => e.progress?.total ? Math.round(e.progress.loaded / e.progress.total * 100) : 0;
208
+ }), z((t) => t.filter((t) => t !== e));
209
+ }, ee = I.filter((e) => e.status === "done").length, te = I.some((e) => y.includes(e.status) || e.status === "queued"), ne = I.some((e) => e.status === "paused" || e.status === "failed"), $ = (e) => e.progress?.total ? Math.round(e.progress.loaded / e.progress.total * 100) : 0;
199
210
  return /* @__PURE__ */ _(h, { children: [
200
211
  /* @__PURE__ */ _("div", {
201
- className: v("grid justify-items-center gap-2 rounded-lg border-2 border-dashed border-border p-6 text-center transition-colors", R && "border-primary bg-accent"),
212
+ className: v("grid justify-items-center gap-2 rounded-lg border-2 border-dashed border-border p-6 text-center transition-colors", B && "border-primary bg-accent"),
202
213
  "data-ckui": "upload-drop",
203
214
  onDragOver: (e) => {
204
- e.preventDefault(), z(!0);
215
+ e.preventDefault(), V(!0);
205
216
  },
206
- onDragLeave: () => z(!1),
217
+ onDragLeave: () => V(!1),
207
218
  onDrop: (e) => {
208
- e.preventDefault(), z(!1), K(e.dataTransfer.files);
219
+ e.preventDefault(), V(!1), J(e.dataTransfer.files);
209
220
  },
210
221
  children: [
211
222
  /* @__PURE__ */ g("p", {
212
223
  className: "text-muted-foreground",
213
- children: j("uploadQueue.drop")
224
+ children: N("uploadQueue.drop")
214
225
  }),
215
226
  /* @__PURE__ */ g(o, {
216
227
  size: "sm",
217
- onClick: () => B.current?.click(),
218
- children: j("uploadQueue.add")
228
+ onClick: () => H.current?.click(),
229
+ children: N("uploadQueue.add")
219
230
  }),
220
231
  /* @__PURE__ */ g("input", {
221
- ref: B,
232
+ ref: H,
222
233
  type: "file",
223
234
  multiple: !0,
224
235
  hidden: !0,
225
- accept: T,
226
- "aria-label": j("uploadQueue.add"),
236
+ accept: E,
237
+ "aria-label": N("uploadQueue.add"),
227
238
  onChange: (e) => {
228
- K(e.target.files ?? []), e.target.value = "";
239
+ J(e.target.files ?? []), e.target.value = "";
229
240
  }
230
241
  })
231
242
  ]
232
243
  }),
233
- I.length > 0 && /* @__PURE__ */ _("div", {
244
+ R.length > 0 && /* @__PURE__ */ _("div", {
234
245
  className: "grid gap-2 rounded-lg border border-border p-3",
235
246
  "data-ckui": "upload-waiting",
236
247
  role: "status",
237
248
  children: [
238
249
  /* @__PURE__ */ g("p", {
239
250
  className: "font-medium",
240
- children: j("uploadQueue.unfinished")
251
+ children: N("uploadQueue.unfinished")
241
252
  }),
242
253
  /* @__PURE__ */ g("ul", {
243
254
  className: "grid gap-1",
244
- children: I.map((e) => /* @__PURE__ */ _("li", {
255
+ children: R.map((e) => /* @__PURE__ */ _("li", {
245
256
  className: "flex flex-wrap items-center gap-2",
246
257
  children: [
247
258
  /* @__PURE__ */ g("span", {
@@ -255,44 +266,44 @@ function T({ client: l, createItem: u, path: w = "source", accept: T, concurrenc
255
266
  /* @__PURE__ */ g(o, {
256
267
  size: "xs",
257
268
  variant: "ghost",
258
- onClick: () => X(e),
259
- children: j("uploadQueue.forget")
269
+ onClick: () => Q(e),
270
+ children: N("uploadQueue.forget")
260
271
  })
261
272
  ]
262
273
  }, `${e.name}:${e.size}`))
263
274
  }),
264
275
  /* @__PURE__ */ g("p", {
265
276
  className: "text-xs text-muted-foreground",
266
- children: j("uploadQueue.unfinishedHint")
277
+ children: N("uploadQueue.unfinishedHint")
267
278
  })
268
279
  ]
269
280
  }),
270
- P.length > 0 && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", {
281
+ I.length > 0 && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", {
271
282
  className: "flex flex-wrap items-center gap-2",
272
283
  children: [/* @__PURE__ */ g("p", {
273
284
  className: "text-muted-foreground",
274
285
  "aria-live": "polite",
275
- children: j("uploadQueue.summary", {
276
- done: Z,
277
- total: P.length
286
+ children: N("uploadQueue.summary", {
287
+ done: ee,
288
+ total: I.length
278
289
  })
279
290
  }), /* @__PURE__ */ _("div", {
280
291
  className: "ms-auto flex gap-2",
281
- children: [Q && /* @__PURE__ */ g(o, {
292
+ children: [te && /* @__PURE__ */ g(o, {
282
293
  size: "sm",
283
294
  variant: "outline",
284
- onClick: () => q(),
285
- children: j("uploadQueue.pauseAll")
286
- }), ee && /* @__PURE__ */ g(o, {
295
+ onClick: () => Y(),
296
+ children: N("uploadQueue.pauseAll")
297
+ }), ne && /* @__PURE__ */ g(o, {
287
298
  size: "sm",
288
299
  variant: "outline",
289
- onClick: () => J(),
290
- children: j("uploadQueue.resumeAll")
300
+ onClick: () => X(),
301
+ children: N("uploadQueue.resumeAll")
291
302
  })]
292
303
  })]
293
304
  }), /* @__PURE__ */ g("ul", {
294
305
  className: "grid divide-y divide-border rounded-lg border border-border",
295
- children: P.map((e) => /* @__PURE__ */ _("li", {
306
+ children: I.map((e) => /* @__PURE__ */ _("li", {
296
307
  className: "grid gap-1.5 p-3",
297
308
  "data-ckui": "upload-entry",
298
309
  "data-status": e.status,
@@ -310,11 +321,11 @@ function T({ client: l, createItem: u, path: w = "source", accept: T, concurrenc
310
321
  }),
311
322
  e.resumed && e.status !== "done" && /* @__PURE__ */ g(s, {
312
323
  variant: "secondary",
313
- children: j("uploadQueue.resumed")
324
+ children: N("uploadQueue.resumed")
314
325
  }),
315
326
  /* @__PURE__ */ g(s, {
316
327
  variant: e.status === "failed" ? "destructive" : e.status === "done" ? "outline" : "secondary",
317
- children: j(`uploadQueue.${e.status}`)
328
+ children: N(`uploadQueue.${e.status}`)
318
329
  })
319
330
  ]
320
331
  }),
@@ -330,27 +341,33 @@ function T({ client: l, createItem: u, path: w = "source", accept: T, concurrenc
330
341
  style: { width: `${$(e)}%` }
331
342
  })
332
343
  }),
333
- e.error && /* @__PURE__ */ g(c, { children: A.error(e.error) }),
344
+ e.error && /* @__PURE__ */ g(c, { children: M.error(e.error) }),
334
345
  /* @__PURE__ */ _("div", {
335
346
  className: "flex flex-wrap gap-2",
336
347
  children: [
337
348
  (y.includes(e.status) || e.status === "queued") && /* @__PURE__ */ g(o, {
338
349
  size: "xs",
339
350
  variant: "ghost",
340
- onClick: () => q(e.id),
341
- children: j("uploadQueue.pause")
351
+ onClick: () => Y(e.id),
352
+ children: N("uploadQueue.pause")
342
353
  }),
343
354
  (e.status === "paused" || e.status === "failed") && /* @__PURE__ */ g(o, {
344
355
  size: "xs",
345
356
  variant: "outline",
346
- onClick: () => J(e.id),
347
- children: j(e.status === "failed" ? "common.retry" : "uploadQueue.resume")
357
+ onClick: () => X(e.id),
358
+ children: N(e.status === "failed" ? "common.retry" : "uploadQueue.resume")
359
+ }),
360
+ k && e.ref && (e.status === "done" || e.status === "failed") && /* @__PURE__ */ g(o, {
361
+ size: "xs",
362
+ variant: "outline",
363
+ onClick: () => k(e.ref),
364
+ children: N("uploadQueue.open")
348
365
  }),
349
366
  e.status !== "done" && /* @__PURE__ */ g(o, {
350
367
  size: "xs",
351
368
  variant: "ghost",
352
- onClick: () => Y(e),
353
- children: j("common.remove")
369
+ onClick: () => Z(e),
370
+ children: N("common.remove")
354
371
  })
355
372
  ]
356
373
  })
@@ -360,6 +377,6 @@ function T({ client: l, createItem: u, path: w = "source", accept: T, concurrenc
360
377
  ] });
361
378
  }
362
379
  //#endregion
363
- export { w as UploadQueue };
380
+ export { w as UploadQueue, T as UploadQueueBody };
364
381
 
365
382
  //# sourceMappingURL=upload-queue.js.map