@openrails/contentkit-ui 0.104.0-rc.1 → 0.104.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +6 -6
  2. package/dist/client/beacons.js +4 -1
  3. package/dist/client/beacons.js.map +1 -1
  4. package/dist/client/contents.d.ts +9 -1
  5. package/dist/client/contents.js +73 -55
  6. package/dist/client/contents.js.map +1 -1
  7. package/dist/client/discovery.d.ts +6 -2
  8. package/dist/client/discovery.js +1 -1
  9. package/dist/client/discovery.js.map +1 -1
  10. package/dist/client/generated/error-codes.d.ts +1 -1
  11. package/dist/client/generated/error-codes.js +4 -0
  12. package/dist/client/generated/error-codes.js.map +1 -1
  13. package/dist/client/generated/routes.d.ts +43 -30
  14. package/dist/client/generated/routes.js +70 -63
  15. package/dist/client/generated/routes.js.map +1 -1
  16. package/dist/client/generated/wire.d.ts +36 -74
  17. package/dist/client/generated/wire.js.map +1 -1
  18. package/dist/client/index.d.ts +1 -1
  19. package/dist/client/media/windows.d.ts +2 -0
  20. package/dist/client/media/windows.js +4 -1
  21. package/dist/client/media/windows.js.map +1 -1
  22. package/dist/client/stats.d.ts +4 -20
  23. package/dist/client/stats.js +0 -11
  24. package/dist/client/stats.js.map +1 -1
  25. package/dist/client/versions.d.ts +8 -6
  26. package/dist/client/versions.js +15 -10
  27. package/dist/client/versions.js.map +1 -1
  28. package/dist/components/admin/bulk-drafts.d.ts +17 -0
  29. package/dist/components/admin/bulk-drafts.js +41 -0
  30. package/dist/components/admin/bulk-drafts.js.map +1 -0
  31. package/dist/components/admin/content-admin.d.ts +1 -1
  32. package/dist/components/admin/content-admin.js.map +1 -1
  33. package/dist/components/admin/content-manager.d.ts +11 -3
  34. package/dist/components/admin/content-manager.js +739 -380
  35. package/dist/components/admin/content-manager.js.map +1 -1
  36. package/dist/components/admin/group-manager.d.ts +8 -3
  37. package/dist/components/admin/group-manager.js +218 -146
  38. package/dist/components/admin/group-manager.js.map +1 -1
  39. package/dist/components/admin/page-notices.d.ts +16 -0
  40. package/dist/components/admin/page-notices.js +113 -0
  41. package/dist/components/admin/page-notices.js.map +1 -0
  42. package/dist/components/admin/page-picker.d.ts +38 -0
  43. package/dist/components/admin/page-picker.js +159 -0
  44. package/dist/components/admin/page-picker.js.map +1 -0
  45. package/dist/components/admin/reader-content-editor.js +537 -500
  46. package/dist/components/admin/reader-content-editor.js.map +1 -1
  47. package/dist/components/admin/reader-manifest.js +181 -179
  48. package/dist/components/admin/reader-manifest.js.map +1 -1
  49. package/dist/components/admin/shell.d.ts +10 -2
  50. package/dist/components/admin/shell.js +114 -42
  51. package/dist/components/admin/shell.js.map +1 -1
  52. package/dist/components/admin/stats-dashboard.d.ts +4 -9
  53. package/dist/components/admin/stats-dashboard.js +300 -202
  54. package/dist/components/admin/stats-dashboard.js.map +1 -1
  55. package/dist/components/admin/upload-queue.d.ts +14 -0
  56. package/dist/components/admin/upload-queue.js +115 -98
  57. package/dist/components/admin/upload-queue.js.map +1 -1
  58. package/dist/components/content/card.d.ts +31 -3
  59. package/dist/components/content/card.js +324 -158
  60. package/dist/components/content/card.js.map +1 -1
  61. package/dist/components/content/daily-releases.js +14 -12
  62. package/dist/components/content/daily-releases.js.map +1 -1
  63. package/dist/components/content/editor-save.js +27 -25
  64. package/dist/components/content/editor-save.js.map +1 -1
  65. package/dist/components/content/editors.js +274 -253
  66. package/dist/components/content/editors.js.map +1 -1
  67. package/dist/components/content/favorites.d.ts +1 -1
  68. package/dist/components/content/favorites.js +36 -22
  69. package/dist/components/content/favorites.js.map +1 -1
  70. package/dist/components/content/grid.d.ts +11 -4
  71. package/dist/components/content/grid.js +52 -43
  72. package/dist/components/content/grid.js.map +1 -1
  73. package/dist/components/content/history.js +83 -63
  74. package/dist/components/content/history.js.map +1 -1
  75. package/dist/components/content/index.d.ts +5 -3
  76. package/dist/components/content/listing.d.ts +14 -3
  77. package/dist/components/content/listing.js +65 -56
  78. package/dist/components/content/listing.js.map +1 -1
  79. package/dist/components/content/offer-fields.js +6 -6
  80. package/dist/components/content/paged-grid.d.ts +10 -0
  81. package/dist/components/content/paged-grid.js +116 -0
  82. package/dist/components/content/paged-grid.js.map +1 -0
  83. package/dist/components/content/pagination.d.ts +19 -0
  84. package/dist/components/content/pagination.js +97 -0
  85. package/dist/components/content/pagination.js.map +1 -0
  86. package/dist/components/content/paywall.d.ts +9 -0
  87. package/dist/components/content/paywall.js +27 -3
  88. package/dist/components/content/paywall.js.map +1 -1
  89. package/dist/components/content/post-list.d.ts +37 -0
  90. package/dist/components/content/post-list.js +203 -0
  91. package/dist/components/content/post-list.js.map +1 -0
  92. package/dist/components/content/reader-settings.d.ts +2 -0
  93. package/dist/components/content/reader-settings.js +1 -1
  94. package/dist/components/content/reader-settings.js.map +1 -1
  95. package/dist/components/groups/banner.d.ts +6 -2
  96. package/dist/components/groups/banner.js +79 -61
  97. package/dist/components/groups/banner.js.map +1 -1
  98. package/dist/components/groups/directory.d.ts +12 -2
  99. package/dist/components/groups/directory.js +168 -105
  100. package/dist/components/groups/directory.js.map +1 -1
  101. package/dist/components/groups/editor.js +10 -10
  102. package/dist/components/groups/editor.js.map +1 -1
  103. package/dist/components/groups/entity-chips.d.ts +61 -0
  104. package/dist/components/groups/entity-chips.js +382 -0
  105. package/dist/components/groups/entity-chips.js.map +1 -0
  106. package/dist/components/groups/group-members.d.ts +29 -0
  107. package/dist/components/groups/group-members.js +474 -0
  108. package/dist/components/groups/group-members.js.map +1 -0
  109. package/dist/components/groups/header.js +53 -45
  110. package/dist/components/groups/header.js.map +1 -1
  111. package/dist/components/groups/index.d.ts +3 -1
  112. package/dist/components/groups/memberships.d.ts +3 -36
  113. package/dist/components/groups/memberships.js +105 -480
  114. package/dist/components/groups/memberships.js.map +1 -1
  115. package/dist/components/groups/scoped.d.ts +23 -0
  116. package/dist/components/groups/scoped.js +92 -0
  117. package/dist/components/groups/scoped.js.map +1 -0
  118. package/dist/components/media-folder-editor.d.ts +5 -0
  119. package/dist/components/media-folder-editor.js +418 -230
  120. package/dist/components/media-folder-editor.js.map +1 -1
  121. package/dist/components/social/poll.js +57 -48
  122. package/dist/components/social/poll.js.map +1 -1
  123. package/dist/components/sortable-list.d.ts +5 -1
  124. package/dist/components/sortable-list.js +97 -81
  125. package/dist/components/sortable-list.js.map +1 -1
  126. package/dist/index.d.ts +1 -1
  127. package/dist/index.js +41 -37
  128. package/dist/locales/admin/de.js +78 -45
  129. package/dist/locales/admin/de.js.map +1 -1
  130. package/dist/locales/admin/en.d.ts +74 -41
  131. package/dist/locales/admin/en.js +78 -45
  132. package/dist/locales/admin/en.js.map +1 -1
  133. package/dist/locales/admin/es.js +78 -45
  134. package/dist/locales/admin/es.js.map +1 -1
  135. package/dist/locales/admin/ja.js +78 -45
  136. package/dist/locales/admin/ja.js.map +1 -1
  137. package/dist/locales/admin/ko.js +78 -45
  138. package/dist/locales/admin/ko.js.map +1 -1
  139. package/dist/locales/admin/zh.js +78 -45
  140. package/dist/locales/admin/zh.js.map +1 -1
  141. package/dist/locales/de.js +2 -0
  142. package/dist/locales/de.js.map +1 -1
  143. package/dist/locales/discovery/de.js +25 -1
  144. package/dist/locales/discovery/de.js.map +1 -1
  145. package/dist/locales/discovery/en.d.ts +24 -0
  146. package/dist/locales/discovery/en.js +25 -1
  147. package/dist/locales/discovery/en.js.map +1 -1
  148. package/dist/locales/discovery/es.js +25 -1
  149. package/dist/locales/discovery/es.js.map +1 -1
  150. package/dist/locales/discovery/ja.js +25 -1
  151. package/dist/locales/discovery/ja.js.map +1 -1
  152. package/dist/locales/discovery/ko.js +25 -1
  153. package/dist/locales/discovery/ko.js.map +1 -1
  154. package/dist/locales/discovery/zh.js +25 -1
  155. package/dist/locales/discovery/zh.js.map +1 -1
  156. package/dist/locales/en.d.ts +120 -43
  157. package/dist/locales/en.js +2 -0
  158. package/dist/locales/en.js.map +1 -1
  159. package/dist/locales/es.js +2 -0
  160. package/dist/locales/es.js.map +1 -1
  161. package/dist/locales/groups/de.js +22 -4
  162. package/dist/locales/groups/de.js.map +1 -1
  163. package/dist/locales/groups/en.d.ts +20 -2
  164. package/dist/locales/groups/en.js +22 -4
  165. package/dist/locales/groups/en.js.map +1 -1
  166. package/dist/locales/groups/es.js +22 -4
  167. package/dist/locales/groups/es.js.map +1 -1
  168. package/dist/locales/groups/ja.js +22 -4
  169. package/dist/locales/groups/ja.js.map +1 -1
  170. package/dist/locales/groups/ko.js +22 -4
  171. package/dist/locales/groups/ko.js.map +1 -1
  172. package/dist/locales/groups/zh.js +22 -4
  173. package/dist/locales/groups/zh.js.map +1 -1
  174. package/dist/locales/ja.js +2 -0
  175. package/dist/locales/ja.js.map +1 -1
  176. package/dist/locales/ko.js +2 -0
  177. package/dist/locales/ko.js.map +1 -1
  178. package/dist/locales/zh.js +2 -0
  179. package/dist/locales/zh.js.map +1 -1
  180. package/dist/react/contents.js +1 -1
  181. package/dist/react/contents.js.map +1 -1
  182. package/dist/react/discovery.d.ts +16 -0
  183. package/dist/react/discovery.js +69 -37
  184. package/dist/react/discovery.js.map +1 -1
  185. package/dist/react/groups.d.ts +19 -3
  186. package/dist/react/groups.js +49 -22
  187. package/dist/react/groups.js.map +1 -1
  188. package/dist/react/index.d.ts +6 -4
  189. package/dist/react/page-notices.d.ts +13 -0
  190. package/dist/react/page-notices.js +38 -0
  191. package/dist/react/page-notices.js.map +1 -0
  192. package/dist/react/post-reads.d.ts +23 -0
  193. package/dist/react/post-reads.js +90 -0
  194. package/dist/react/post-reads.js.map +1 -0
  195. package/dist/react/remembered-sort.d.ts +5 -0
  196. package/dist/react/remembered-sort.js +29 -0
  197. package/dist/react/remembered-sort.js.map +1 -0
  198. package/dist/react/resources.js +2 -2
  199. package/dist/react/resources.js.map +1 -1
  200. package/dist/react/stats.d.ts +3 -7
  201. package/dist/react/stats.js +2 -6
  202. package/dist/react/stats.js.map +1 -1
  203. package/dist/react/use-engagement.d.ts +2 -0
  204. package/dist/react/use-engagement.js +40 -34
  205. package/dist/react/use-engagement.js.map +1 -1
  206. package/dist/react.js +29 -27
  207. package/dist/styles.css +1 -1
  208. package/dist/urls/index.d.ts +1 -1
  209. package/dist/urls.js +2 -2
  210. package/dist/urls.js.map +1 -1
  211. package/package.json +1 -1
  212. package/dist/components/admin/page-review.d.ts +0 -13
  213. package/dist/components/admin/page-review.js +0 -225
  214. package/dist/components/admin/page-review.js.map +0 -1
  215. package/dist/react/page-review.d.ts +0 -14
  216. package/dist/react/page-review.js +0 -37
  217. package/dist/react/page-review.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"reader-manifest.js","names":[],"sources":["../../../src/components/admin/reader-manifest.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Anchor, BackgroundCue, ForegroundCue, ReaderMeta, ReaderPlan } from \"../../client/generated/wire.js\";\nimport { emptyReaderMeta, matchAudio, readerMetaProblems, readerNames, type AudioMatch, type ReaderMetaProblem, type ReaderNames } from \"../../client/reader-meta.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useReaderPlan } from \"../../react/versions.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { ErrorLine, useAction } from \"../social/parts.js\";\nimport { useEditorSaveTask } from \"../content/editor-save.js\";\nimport { LoadFailed, Rows } from \"./shell.js\";\n\nexport interface ReaderManifestEditorProps {\n content: string;\n version: string;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst NONE = \"__none__\";\n\n/** A page or an anchor as one select value. */\nconst placeOf = (page?: string, anchor?: string) => (anchor !== undefined ? `anchor:${anchor}` : page !== undefined ? `page:${page}` : NONE);\nconst fromPlace = (v: string): { page?: string; anchor?: string } =>\n v.startsWith(\"anchor:\") ? { anchor: v.slice(7) } : v.startsWith(\"page:\") ? { page: v.slice(5) } : {};\n\n/** Only what the server stores: no empty layers, defaults or lists. */\nfunction clean(meta: ReaderMeta): ReaderMeta {\n const auto = meta.defaults?.auto_advance_seconds;\n return {\n schema_version: 1,\n ...(auto !== undefined ? { defaults: { auto_advance_seconds: auto } } : {}),\n anchors: (meta.anchors ?? []).map((a) => ({ id: a.id.trim(), page: a.page, offset_percent: a.offset_percent, ...(a.duration_seconds !== undefined ? { duration_seconds: a.duration_seconds } : {}) })),\n background: (meta.background ?? []).map((c) => {\n const layer = c.layer?.trim();\n return { ...c, ...(layer ? { layer } : { layer: undefined }) };\n }),\n foreground: meta.foreground ?? [],\n };\n}\n\nconst num = (v: string): number => (v.trim() === \"\" ? Number.NaN : Number(v));\nconst shown = (n: number | undefined) => (n === undefined || Number.isNaN(n) ? \"\" : String(n));\n\n/**\n * Editors: a reader version's manifest over its published pages and audio:\n * scroll anchors, foreground cues (one track at a page or anchor, matched\n * from file names in one step), background tracks over a range, and the\n * auto-advance default. Page order is the version's media order.\n */\nexport function ReaderManifestEditor({ content, version, client: own, onError, className, appearance }: ReaderManifestEditorProps) {\n const { t } = useMessages();\n const client = useContentKitClient(own);\n const plan = useReaderPlan(content, version, { client });\n const names = useMemo<ReaderNames>(() => (plan.plan ? readerNames(plan.plan) : { pages: [], files: [] }), [plan.plan]);\n const [basis, setBasis] = useState<ReaderPlan | null>(null);\n const [meta, setMeta] = useState<ReaderMeta | null>(null);\n const [dirty, setDirty] = useState(false);\n const [saved, setSaved] = useState(false);\n const [matched, setMatched] = useState<AudioMatch | null>(null);\n const save = useAction(\"reader.save\", onError);\n // A fresh plan replaces the draft unless it has unsaved edits.\n if (plan.plan && plan.plan !== basis && !dirty) {\n setBasis(plan.plan);\n setMeta(plan.plan.reader ? structuredClone(plan.plan.reader) : emptyReaderMeta());\n }\n const problems = useMemo(() => (meta ? readerMetaProblems(meta, names) : []), [meta, names]);\n const edit = (fn: (m: ReaderMeta) => ReaderMeta) => {\n setMeta((m) => (m ? fn(m) : m));\n setDirty(true);\n setSaved(false);\n };\n const latest=useRef(meta);latest.current=meta;\n const submit=async()=>{\n if(!meta||problems.length)return false;\n setSaved(false);const snapshot=meta;\n const ok=await save.run(async()=>{await client.versions.setReader(content,version,clean(snapshot));return true;});\n if(!ok||latest.current!==snapshot)return false;\n setDirty(false);setSaved(true);return true;\n };\n useEditorSaveTask(`reader-meta:${version}`,{label:t(\"readerManifest.title\"),dirty,pending:save.pending,valid:!problems.length,save:submit,order:30});\n const body = (() => {\n if (plan.loading && !plan.plan && !plan.pending) return <Rows count={2} />;\n if (plan.pending) return <p className=\"text-muted-foreground\">{t(\"readerManifest.pending\")}</p>;\n if (plan.error && !plan.plan) return <LoadFailed error={plan.error} message={t(\"readerManifest.loadFailed\")} retry={plan.reload} />;\n if (!meta) return null;\n const anchors = meta.anchors ?? [];\n const anchorIds = anchors.map((a) => a.id).filter(Boolean);\n return (\n <form className=\"grid gap-4\" onSubmit={(event)=>{event.preventDefault();void submit();}}>\n <p className=\"text-muted-foreground\">{t(\"readerManifest.summary\", { pages: names.pages.length, files: names.files.length })}</p>\n <Defaults meta={meta} edit={edit} />\n <Group title={t(\"readerManifest.anchors\")} add={t(\"readerManifest.addAnchor\")} disabled={!names.pages.length}\n onAdd={() => edit((m) => ({ ...m, anchors: [...(m.anchors ?? []), { id: nextId(m.anchors ?? []), page: names.pages[0]!, offset_percent: 0 }] }))}>\n {anchors.map((a, i) => (\n <AnchorRow key={i} index={i} anchor={a} pages={names.pages} onChange={(next) => edit((m) => ({ ...m, anchors: replace(m.anchors, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, anchors: drop(m.anchors, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.foreground\")} add={t(\"readerManifest.addCue\")} disabled={!names.files.length || !names.pages.length}\n onAdd={() => edit((m) => ({ ...m, foreground: [...(m.foreground ?? []), { file: names.files[0]!, page: names.pages[0]! }] }))}\n extra={\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={!names.files.length} onClick={() => {\n const r = matchAudio(names, anchorIds);\n setMatched(r);\n if (r.foreground.length) edit((m) => ({ ...m, foreground: r.foreground }));\n }}>\n {t(\"readerManifest.match\")}\n </Button>\n }>\n {matched && <MatchReport match={matched} />}\n {(meta.foreground ?? []).map((c, i) => (\n <ForegroundRow key={i} index={i} cue={c} names={names} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, foreground: replace(m.foreground, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, foreground: drop(m.foreground, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.background\")} add={t(\"readerManifest.addTrack\")} disabled={!names.files.length}\n onAdd={() => edit((m) => ({ ...m, background: [...(m.background ?? []), { file: names.files[0]! }] }))}>\n {(meta.background ?? []).map((c, i) => (\n <BackgroundRow key={i} index={i} cue={c} names={names} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, background: replace(m.background, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, background: drop(m.background, i) }))} />\n ))}\n </Group>\n {problems.length > 0 && <Problems problems={problems} />}\n <ErrorLine error={save.error} />\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button type=\"submit\" size=\"sm\" disabled={save.pending || !dirty || problems.length > 0}>\n {save.pending ? t(\"common.saving\") : t(\"common.save\")}\n </Button>\n {dirty && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => {\n setDirty(false);\n setBasis(null);\n setMatched(null);\n }}>\n {t(\"readerManifest.revert\")}\n </Button>\n )}\n {saved && <span role=\"status\" className=\"text-muted-foreground\">{t(\"readerManifest.saved\")}</span>}\n </div>\n </form>\n );\n })();\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 text-sm\", className)} data-ckui=\"reader-manifest\">\n <h4 className=\"font-medium\">{t(\"readerManifest.title\")}</h4>\n {body}\n </ContentKitUiRoot>\n );\n}\n\nfunction nextId(anchors: readonly Anchor[]): string {\n const taken = new Set(anchors.map((a) => a.id));\n let n = anchors.length + 1;\n while (taken.has(`a${n}`)) n++;\n return `a${n}`;\n}\n\nconst replace = <T,>(list: T[] | undefined, i: number, next: T): T[] => (list ?? []).map((x, j) => (j === i ? next : x));\nconst drop = <T,>(list: T[] | undefined, i: number): T[] => (list ?? []).filter((_, j) => j !== i);\n\nfunction Group({ title, add, disabled, onAdd, extra, children }: { title: string; add: string; disabled: boolean; onAdd: () => void; extra?: ReactNode; children: ReactNode }) {\n return (\n <fieldset className=\"grid gap-2 rounded-md border border-border p-3\">\n <legend className=\"px-1 font-medium\">{title}</legend>\n {children}\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={disabled} onClick={onAdd}>\n {add}\n </Button>\n {extra}\n </div>\n </fieldset>\n );\n}\n\nfunction Defaults({ meta, edit }: { meta: ReaderMeta; edit: (fn: (m: ReaderMeta) => ReaderMeta) => void }) {\n const { t } = useMessages();\n const id = useId();\n const auto = meta.defaults?.auto_advance_seconds;\n return (\n <div className=\"grid max-w-xs gap-1.5\">\n <Label htmlFor={`${id}-auto`}>{t(\"readerManifest.autoAdvance\")}</Label>\n <Input id={`${id}-auto`} className=\"h-8\" type=\"number\" min={0} step=\"any\" value={shown(auto)}\n onChange={(e) => edit((m) => ({ ...m, defaults: e.target.value.trim() === \"\" ? undefined : { auto_advance_seconds: num(e.target.value) } }))} />\n </div>\n );\n}\n\nfunction NameSelect({ label, value, names, onChange }: { label: string; value: string; names: readonly string[]; onChange: (v: string) => void }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {names.map((n) => (\n <SelectItem key={n} value={n}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction PlaceSelect({ label, value, pages, anchors, none, onChange }: { label: string; value: string; pages: readonly string[]; anchors: readonly string[]; none?: string; onChange: (v: string) => void }) {\n const { t } = useMessages();\n const text = (v: string) => (v === NONE ? (none ?? \"\") : v.startsWith(\"anchor:\") ? t(\"readerManifest.anchorNamed\", { id: v.slice(7) }) : t(\"readerManifest.pageNamed\", { name: v.slice(5) }));\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{text}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {none !== undefined && <SelectItem value={NONE}>{none}</SelectItem>}\n {anchors.map((a) => (\n <SelectItem key={`a-${a}`} value={`anchor:${a}`}>\n {text(`anchor:${a}`)}\n </SelectItem>\n ))}\n {pages.map((p) => (\n <SelectItem key={`p-${p}`} value={`page:${p}`}>\n {text(`page:${p}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction Remove({ label, onRemove }: { label: string; onRemove: () => void }) {\n const { t } = useMessages();\n return (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" aria-label={label} onClick={onRemove}>\n {t(\"common.remove\")}\n </Button>\n );\n}\n\nfunction AnchorRow({ index, anchor: a, pages, onChange, onRemove }: { index: number; anchor: Anchor; pages: readonly string[]; onChange: (a: Anchor) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-anchor\">\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.anchorId\", { n })} value={a.id} onChange={(e) => onChange({ ...a, id: e.target.value })} />\n <NameSelect label={t(\"readerManifest.anchorPage\", { n })} value={a.page} names={pages} onChange={(page) => onChange({ ...a, page })} />\n <Input className=\"h-8 w-24\" type=\"number\" min={0} max={100} step=\"any\" aria-label={t(\"readerManifest.offset\", { n })} value={shown(a.offset_percent)}\n onChange={(e) => onChange({ ...a, offset_percent: num(e.target.value) })} />\n <Input className=\"h-8 w-28\" type=\"number\" min={0} step=\"any\" aria-label={t(\"readerManifest.duration\", { n })} placeholder={t(\"readerManifest.durationPlaceholder\")}\n value={shown(a.duration_seconds)} onChange={(e) => onChange({ ...a, duration_seconds: e.target.value.trim() === \"\" ? undefined : num(e.target.value) })} />\n <Remove label={t(\"readerManifest.removeAnchor\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction ForegroundRow({ index, cue: c, names, anchors, onChange, onRemove }: { index: number; cue: ForegroundCue; names: ReaderNames; anchors: readonly string[]; onChange: (c: ForegroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-foreground\">\n <NameSelect label={t(\"readerManifest.cueFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <PlaceSelect label={t(\"readerManifest.cueAt\", { n })} value={placeOf(c.page, c.anchor)} pages={names.pages} anchors={anchors} onChange={(v) => onChange({ file: c.file, ...fromPlace(v) })} />\n <Remove label={t(\"readerManifest.removeCue\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction BackgroundRow({ index, cue: c, names, anchors, onChange, onRemove }: { index: number; cue: BackgroundCue; names: ReaderNames; anchors: readonly string[]; onChange: (c: BackgroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n const from = fromPlace(placeOf(c.from_page, c.from_anchor));\n const to = fromPlace(placeOf(c.to_page, c.to_anchor));\n const set = (f: typeof from, tt: typeof to) => onChange({ file: c.file, loop: c.loop, layer: c.layer, from_page: f.page, from_anchor: f.anchor, to_page: tt.page, to_anchor: tt.anchor });\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-background\">\n <NameSelect label={t(\"readerManifest.trackFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <PlaceSelect label={t(\"readerManifest.trackFrom\", { n })} none={t(\"readerManifest.start\")} value={placeOf(c.from_page, c.from_anchor)} pages={names.pages} anchors={anchors}\n onChange={(v) => set(fromPlace(v), to)} />\n <PlaceSelect label={t(\"readerManifest.trackTo\", { n })} none={t(\"readerManifest.end\")} value={placeOf(c.to_page, c.to_anchor)} pages={names.pages} anchors={anchors}\n onChange={(v) => set(from, fromPlace(v))} />\n <label className=\"flex items-center gap-1.5\">\n <Switch size=\"sm\" checked={!!c.loop} onCheckedChange={(on) => onChange({ ...c, loop: on || undefined })} aria-label={t(\"readerManifest.loopN\", { n })} />\n {t(\"readerManifest.loop\")}\n </label>\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.layer\", { n })} placeholder={t(\"readerManifest.layerPlaceholder\")} value={c.layer ?? \"\"}\n onChange={(e) => onChange({ ...c, layer: e.target.value || undefined })} />\n <Remove label={t(\"readerManifest.removeTrack\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction MatchReport({ match }: { match: AudioMatch }) {\n const { t } = useMessages();\n return (\n <div className=\"grid gap-1 rounded-md bg-muted p-2 text-xs\" role=\"status\">\n <p>{match.foreground.length ? t(\"readerManifest.matched\", { count: match.foreground.length }) : t(\"readerManifest.matchedNone\")}</p>\n {match.unmatchedPages.length > 0 && <p>{t(\"readerManifest.pagesWithoutAudio\", { names: match.unmatchedPages.join(\", \") })}</p>}\n {match.unmatchedFiles.length > 0 && <p>{t(\"readerManifest.audioWithoutPlace\", { names: match.unmatchedFiles.join(\", \") })}</p>}\n </div>\n );\n}\n\nfunction Problems({ problems }: { problems: ReaderMetaProblem[] }) {\n const { t } = useMessages();\n const where = (p: ReaderMetaProblem) => t(`readerManifest.at_${p.at}`, { n: p.index + 1 });\n return (\n <ul className=\"grid gap-0.5 text-destructive\" role=\"alert\" data-ckui=\"reader-problems\">\n {problems.map((p, i) => (\n <li key={i}>{t(`readerManifest.problem_${p.problem}`, { where: where(p) })}</li>\n ))}\n </ul>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reader-manifest.js","names":[],"sources":["../../../src/components/admin/reader-manifest.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Anchor, BackgroundCue, ForegroundCue, ReaderMeta, ReaderPlan } from \"../../client/generated/wire.js\";\nimport { emptyReaderMeta, matchAudio, readerMetaProblems, readerNames, type AudioMatch, type ReaderMetaProblem, type ReaderNames } from \"../../client/reader-meta.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useReaderPlan } from \"../../react/versions.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { ErrorLine, useAction } from \"../social/parts.js\";\nimport { useEditorSaveTask } from \"../content/editor-save.js\";\nimport { LoadFailed, Rows } from \"./shell.js\";\nimport { AudioPreview, PagePicker, usePlanMedia, type PlanMedia } from \"./page-picker.js\";\n\nexport interface ReaderManifestEditorProps {\n content: string;\n version: string;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst NONE = \"__none__\";\n\n/** A page (its name) or an anchor (\"anchor:id\") as one picker value. */\nconst placeOf = (page?: string, anchor?: string) => (anchor !== undefined ? `anchor:${anchor}` : page !== undefined ? page : NONE);\nconst fromPlace = (v: string): { page?: string; anchor?: string } => (v.startsWith(\"anchor:\") ? { anchor: v.slice(7) } : v === NONE ? {} : { page: v });\n\n/** Only what the server stores: no empty layers, defaults or lists. */\nfunction clean(meta: ReaderMeta): ReaderMeta {\n const auto = meta.defaults?.auto_advance_seconds;\n return {\n schema_version: 1,\n ...(auto !== undefined ? { defaults: { auto_advance_seconds: auto } } : {}),\n anchors: (meta.anchors ?? []).map((a) => ({ id: a.id.trim(), page: a.page, offset_percent: a.offset_percent, ...(a.duration_seconds !== undefined ? { duration_seconds: a.duration_seconds } : {}) })),\n background: (meta.background ?? []).map((c) => {\n const layer = c.layer?.trim();\n return { ...c, ...(layer ? { layer } : { layer: undefined }) };\n }),\n foreground: meta.foreground ?? [],\n };\n}\n\nconst num = (v: string): number => (v.trim() === \"\" ? Number.NaN : Number(v));\nconst shown = (n: number | undefined) => (n === undefined || Number.isNaN(n) ? \"\" : String(n));\n\n/**\n * Editors: a reader version's manifest over its published pages and audio:\n * scroll anchors, foreground cues (one track at a page or anchor, matched\n * from file names in one step), background tracks over a range, and the\n * auto-advance default. Page order is the version's media order.\n */\nexport function ReaderManifestEditor({ content, version, client: own, onError, className, appearance }: ReaderManifestEditorProps) {\n const { t } = useMessages();\n const client = useContentKitClient(own);\n const plan = useReaderPlan(content, version, { client });\n const names = useMemo<ReaderNames>(() => (plan.plan ? readerNames(plan.plan) : { pages: [], files: [] }), [plan.plan]);\n const media = usePlanMedia(plan.plan, client);\n const [basis, setBasis] = useState<ReaderPlan | null>(null);\n const [meta, setMeta] = useState<ReaderMeta | null>(null);\n const [dirty, setDirty] = useState(false);\n const [saved, setSaved] = useState(false);\n const [matched, setMatched] = useState<AudioMatch | null>(null);\n const save = useAction(\"reader.save\", onError);\n // A fresh plan replaces the draft unless it has unsaved edits.\n if (plan.plan && plan.plan !== basis && !dirty) {\n setBasis(plan.plan);\n setMeta(plan.plan.reader ? structuredClone(plan.plan.reader) : emptyReaderMeta());\n }\n const problems = useMemo(() => (meta ? readerMetaProblems(meta, names) : []), [meta, names]);\n const edit = (fn: (m: ReaderMeta) => ReaderMeta) => {\n setMeta((m) => (m ? fn(m) : m));\n setDirty(true);\n setSaved(false);\n };\n const latest=useRef(meta);latest.current=meta;\n const submit=async()=>{\n if(!meta||problems.length)return false;\n setSaved(false);const snapshot=meta;\n const ok=await save.run(async()=>{await client.versions.setReader(content,version,clean(snapshot));return true;});\n if(!ok||latest.current!==snapshot)return false;\n setDirty(false);setSaved(true);return true;\n };\n useEditorSaveTask(`reader-meta:${version}`,{label:t(\"readerManifest.title\"),dirty,pending:save.pending,valid:!problems.length,save:submit,order:30});\n const body = (() => {\n if (plan.loading && !plan.plan && !plan.pending) return <Rows count={2} />;\n if (plan.pending) return <p className=\"text-muted-foreground\">{t(\"readerManifest.pending\")}</p>;\n if (plan.error && !plan.plan) return <LoadFailed error={plan.error} message={t(\"readerManifest.loadFailed\")} retry={plan.reload} />;\n if (!meta) return null;\n const anchors = meta.anchors ?? [];\n const anchorIds = anchors.map((a) => a.id).filter(Boolean);\n return (\n <form className=\"grid gap-4\" onSubmit={(event)=>{event.preventDefault();void submit();}}>\n <p className=\"text-muted-foreground\">{t(\"readerManifest.summary\", { pages: names.pages.length, files: names.files.length })}</p>\n <Defaults meta={meta} edit={edit} />\n <Group title={t(\"readerManifest.anchors\")} add={t(\"readerManifest.addAnchor\")} disabled={!names.pages.length}\n onAdd={() => edit((m) => ({ ...m, anchors: [...(m.anchors ?? []), { id: nextId(m.anchors ?? []), page: names.pages[0]!, offset_percent: 0 }] }))}>\n {anchors.map((a, i) => (\n <AnchorRow key={i} index={i} anchor={a} media={media} onChange={(next) => edit((m) => ({ ...m, anchors: replace(m.anchors, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, anchors: drop(m.anchors, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.foreground\")} add={t(\"readerManifest.addCue\")} disabled={!names.files.length || !names.pages.length}\n onAdd={() => edit((m) => ({ ...m, foreground: [...(m.foreground ?? []), { file: names.files[0]!, page: names.pages[0]! }] }))}\n extra={\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={!names.files.length} onClick={() => {\n const r = matchAudio(names, anchorIds);\n setMatched(r);\n if (r.foreground.length) edit((m) => ({ ...m, foreground: r.foreground }));\n }}>\n {t(\"readerManifest.match\")}\n </Button>\n }>\n {matched && <MatchReport match={matched} />}\n {(meta.foreground ?? []).map((c, i) => (\n <ForegroundRow key={i} index={i} cue={c} names={names} media={media} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, foreground: replace(m.foreground, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, foreground: drop(m.foreground, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.background\")} add={t(\"readerManifest.addTrack\")} disabled={!names.files.length}\n onAdd={() => edit((m) => ({ ...m, background: [...(m.background ?? []), { file: names.files[0]! }] }))}>\n {(meta.background ?? []).map((c, i) => (\n <BackgroundRow key={i} index={i} cue={c} names={names} media={media} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, background: replace(m.background, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, background: drop(m.background, i) }))} />\n ))}\n </Group>\n {problems.length > 0 && <Problems problems={problems} />}\n <ErrorLine error={save.error} />\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button type=\"submit\" size=\"sm\" disabled={save.pending || !dirty || problems.length > 0}>\n {save.pending ? t(\"common.saving\") : t(\"common.save\")}\n </Button>\n {dirty && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => {\n setDirty(false);\n setBasis(null);\n setMatched(null);\n }}>\n {t(\"readerManifest.revert\")}\n </Button>\n )}\n {saved && <span role=\"status\" className=\"text-muted-foreground\">{t(\"readerManifest.saved\")}</span>}\n </div>\n </form>\n );\n })();\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 text-sm\", className)} data-ckui=\"reader-manifest\">\n <h4 className=\"font-medium\">{t(\"readerManifest.title\")}</h4>\n {body}\n </ContentKitUiRoot>\n );\n}\n\nfunction nextId(anchors: readonly Anchor[]): string {\n const taken = new Set(anchors.map((a) => a.id));\n let n = anchors.length + 1;\n while (taken.has(`a${n}`)) n++;\n return `a${n}`;\n}\n\nconst replace = <T,>(list: T[] | undefined, i: number, next: T): T[] => (list ?? []).map((x, j) => (j === i ? next : x));\nconst drop = <T,>(list: T[] | undefined, i: number): T[] => (list ?? []).filter((_, j) => j !== i);\n\nfunction Group({ title, add, disabled, onAdd, extra, children }: { title: string; add: string; disabled: boolean; onAdd: () => void; extra?: ReactNode; children: ReactNode }) {\n return (\n <fieldset className=\"grid gap-2 rounded-md border border-border p-3\">\n <legend className=\"px-1 font-medium\">{title}</legend>\n {children}\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={disabled} onClick={onAdd}>\n {add}\n </Button>\n {extra}\n </div>\n </fieldset>\n );\n}\n\nfunction Defaults({ meta, edit }: { meta: ReaderMeta; edit: (fn: (m: ReaderMeta) => ReaderMeta) => void }) {\n const { t } = useMessages();\n const id = useId();\n const auto = meta.defaults?.auto_advance_seconds;\n return (\n <div className=\"grid max-w-xs gap-1.5\">\n <Label htmlFor={`${id}-auto`}>{t(\"readerManifest.autoAdvance\")}</Label>\n <Input id={`${id}-auto`} className=\"h-8\" type=\"number\" min={0} step=\"any\" value={shown(auto)}\n onChange={(e) => edit((m) => ({ ...m, defaults: e.target.value.trim() === \"\" ? undefined : { auto_advance_seconds: num(e.target.value) } }))} />\n </div>\n );\n}\n\nfunction NameSelect({ label, value, names, onChange }: { label: string; value: string; names: readonly string[]; onChange: (v: string) => void }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {names.map((n) => (\n <SelectItem key={n} value={n}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction Place({ label, value, media, anchors, none, onChange }: { label: string; value: string; media: PlanMedia; anchors: readonly string[]; none?: string; onChange: (v: string) => void }) {\n const { t } = useMessages();\n const extras = [...(none !== undefined ? [{ value: NONE, label: none }] : []), ...anchors.map((a) => ({ value: `anchor:${a}`, label: t(\"readerManifest.anchorNamed\", { id: a }) }))];\n return <PagePicker label={label} pages={media.pages} value={value} extras={extras} onChange={onChange} />;\n}\n\nfunction Remove({ label, onRemove }: { label: string; onRemove: () => void }) {\n const { t } = useMessages();\n return (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" aria-label={label} onClick={onRemove}>\n {t(\"common.remove\")}\n </Button>\n );\n}\n\nfunction AnchorRow({ index, anchor: a, media, onChange, onRemove }: { index: number; anchor: Anchor; media: PlanMedia; onChange: (a: Anchor) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-anchor\">\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.anchorId\", { n })} value={a.id} onChange={(e) => onChange({ ...a, id: e.target.value })} />\n <PagePicker label={t(\"readerManifest.anchorPage\", { n })} pages={media.pages} value={a.page} onChange={(page) => onChange({ ...a, page })} />\n <Input className=\"h-8 w-24\" type=\"number\" min={0} max={100} step=\"any\" aria-label={t(\"readerManifest.offset\", { n })} value={shown(a.offset_percent)}\n onChange={(e) => onChange({ ...a, offset_percent: num(e.target.value) })} />\n <Input className=\"h-8 w-28\" type=\"number\" min={0} step=\"any\" aria-label={t(\"readerManifest.duration\", { n })} placeholder={t(\"readerManifest.durationPlaceholder\")}\n value={shown(a.duration_seconds)} onChange={(e) => onChange({ ...a, duration_seconds: e.target.value.trim() === \"\" ? undefined : num(e.target.value) })} />\n <Remove label={t(\"readerManifest.removeAnchor\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction ForegroundRow({ index, cue: c, names, media, anchors, onChange, onRemove }: { index: number; cue: ForegroundCue; names: ReaderNames; media: PlanMedia; anchors: readonly string[]; onChange: (c: ForegroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-foreground\">\n <NameSelect label={t(\"readerManifest.cueFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <AudioPreview url={media.audio.get(c.file)} label={c.file} />\n <Place label={t(\"readerManifest.cueAt\", { n })} value={placeOf(c.page, c.anchor)} media={media} anchors={anchors} onChange={(v) => onChange({ file: c.file, ...fromPlace(v) })} />\n <Remove label={t(\"readerManifest.removeCue\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction BackgroundRow({ index, cue: c, names, media, anchors, onChange, onRemove }: { index: number; cue: BackgroundCue; names: ReaderNames; media: PlanMedia; anchors: readonly string[]; onChange: (c: BackgroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n const from = fromPlace(placeOf(c.from_page, c.from_anchor));\n const to = fromPlace(placeOf(c.to_page, c.to_anchor));\n const set = (f: typeof from, tt: typeof to) => onChange({ file: c.file, loop: c.loop, layer: c.layer, from_page: f.page, from_anchor: f.anchor, to_page: tt.page, to_anchor: tt.anchor });\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-background\">\n <NameSelect label={t(\"readerManifest.trackFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <AudioPreview url={media.audio.get(c.file)} label={c.file} />\n <Place label={t(\"readerManifest.trackFrom\", { n })} none={t(\"readerManifest.start\")} value={placeOf(c.from_page, c.from_anchor)} media={media} anchors={anchors}\n onChange={(v) => set(fromPlace(v), to)} />\n <Place label={t(\"readerManifest.trackTo\", { n })} none={t(\"readerManifest.end\")} value={placeOf(c.to_page, c.to_anchor)} media={media} anchors={anchors}\n onChange={(v) => set(from, fromPlace(v))} />\n <label className=\"flex items-center gap-1.5\">\n <Switch size=\"sm\" checked={!!c.loop} onCheckedChange={(on) => onChange({ ...c, loop: on || undefined })} aria-label={t(\"readerManifest.loopN\", { n })} />\n {t(\"readerManifest.loop\")}\n </label>\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.layer\", { n })} placeholder={t(\"readerManifest.layerPlaceholder\")} value={c.layer ?? \"\"}\n onChange={(e) => onChange({ ...c, layer: e.target.value || undefined })} />\n <Remove label={t(\"readerManifest.removeTrack\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction MatchReport({ match }: { match: AudioMatch }) {\n const { t } = useMessages();\n return (\n <div className=\"grid gap-1 rounded-md bg-muted p-2 text-xs\" role=\"status\">\n <p>{match.foreground.length ? t(\"readerManifest.matched\", { count: match.foreground.length }) : t(\"readerManifest.matchedNone\")}</p>\n {match.unmatchedPages.length > 0 && <p>{t(\"readerManifest.pagesWithoutAudio\", { names: match.unmatchedPages.join(\", \") })}</p>}\n {match.unmatchedFiles.length > 0 && <p>{t(\"readerManifest.audioWithoutPlace\", { names: match.unmatchedFiles.join(\", \") })}</p>}\n </div>\n );\n}\n\nfunction Problems({ problems }: { problems: ReaderMetaProblem[] }) {\n const { t } = useMessages();\n const where = (p: ReaderMetaProblem) => t(`readerManifest.at_${p.at}`, { n: p.index + 1 });\n return (\n <ul className=\"grid gap-0.5 text-destructive\" role=\"alert\" data-ckui=\"reader-problems\">\n {problems.map((p, i) => (\n <li key={i}>{t(`readerManifest.problem_${p.problem}`, { where: where(p) })}</li>\n ))}\n </ul>\n );\n}\n"],"mappings":"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"}
@@ -51,12 +51,20 @@ export declare function LoadFailed({ error, message, retry }: {
51
51
  retry: () => void;
52
52
  }): import('react').JSX.Element;
53
53
  /** Previous and next around the current page number, announced as it changes. */
54
- export declare function Pager({ page, hasNext, onPage, disabled }: {
54
+ export interface PagerProps {
55
55
  page: number;
56
56
  hasNext: boolean;
57
57
  onPage: (page: number) => void;
58
58
  disabled?: boolean;
59
- }): import('react').JSX.Element | null;
59
+ /** Matching items; with truncated, a lower bound. Adds the range, page count and first/last. */
60
+ total?: number;
61
+ truncated?: boolean;
62
+ pageSize?: number;
63
+ /** Offers these page sizes when set. */
64
+ onPageSize?: (size: number) => void;
65
+ pageSizes?: readonly number[];
66
+ }
67
+ export declare function Pager({ page, hasNext, onPage, disabled, total, truncated, pageSize, onPageSize, pageSizes }: PagerProps): import('react').JSX.Element | null;
60
68
  /** A positive page number from the query; 1 otherwise. */
61
69
  export declare function pageOf(q: URLSearchParams): number;
62
70
  export interface ConfirmOptions {
@@ -4,26 +4,27 @@ import { AppearanceContext as i, ContentKitUiRoot as a } from "../../scope.js";
4
4
  import { useTranslator as o } from "../../provider.js";
5
5
  import { GuardContext as s, useGuarded as c } from "../../react/context.js";
6
6
  import { Button as l } from "../../ui/button.js";
7
- import { Skeleton as u } from "../../ui/skeleton.js";
8
- import { ConfirmDialog as d } from "../social/parts.js";
9
- import { useCallback as f, useContext as p, useMemo as m, useState as h } from "react";
10
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
- import { cn as v } from "cn";
7
+ import { Select as u, SelectContent as d, SelectItem as f, SelectTrigger as p, SelectValue as m } from "../../ui/select.js";
8
+ import { Skeleton as h } from "../../ui/skeleton.js";
9
+ import { ConfirmDialog as g } from "../social/parts.js";
10
+ import { useCallback as _, useContext as v, useMemo as y, useState as b } from "react";
11
+ import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
12
+ import { cn as w } from "cn";
12
13
  //#region src/components/admin/shell.tsx
13
- function y({ language: i, messages: a, children: s }) {
14
- let c = r(), l = o(i, a), u = m(() => i ? l : e(t([c.messages, ...a ? Array.isArray(a) ? a : [a] : []]), void 0, c.language), [
14
+ function T({ language: i, messages: a, children: s }) {
15
+ let c = r(), l = o(i, a), u = y(() => i ? l : e(t([c.messages, ...a ? Array.isArray(a) ? a : [a] : []]), void 0, c.language), [
15
16
  i,
16
17
  l,
17
18
  c,
18
19
  a
19
20
  ]);
20
- return /* @__PURE__ */ g(n.Provider, {
21
+ return /* @__PURE__ */ S(n.Provider, {
21
22
  value: u,
22
23
  children: s
23
24
  });
24
25
  }
25
- function b({ appearance: e, language: t, messages: n, guard: r, className: o, flow: c, children: l }) {
26
- let u = p(i), d = m(() => ({
26
+ function E({ appearance: e, language: t, messages: n, guard: r, className: o, flow: c, children: l }) {
27
+ let u = v(i), d = y(() => ({
27
28
  ...u,
28
29
  ...e,
29
30
  theme: e?.theme ?? u?.theme ?? "inherit",
@@ -31,55 +32,55 @@ function b({ appearance: e, language: t, messages: n, guard: r, className: o, fl
31
32
  ...u?.variables,
32
33
  ...e?.variables
33
34
  }
34
- }), [u, e]), f = /* @__PURE__ */ g(i.Provider, {
35
+ }), [u, e]), f = /* @__PURE__ */ S(i.Provider, {
35
36
  value: d,
36
- children: /* @__PURE__ */ g(s.Provider, {
37
+ children: /* @__PURE__ */ S(s.Provider, {
37
38
  value: r,
38
- children: /* @__PURE__ */ g(a, {
39
+ children: /* @__PURE__ */ S(a, {
39
40
  appearance: d,
40
- className: v("grid gap-4 text-sm text-foreground", o),
41
+ className: w("grid gap-4 text-sm text-foreground", o),
41
42
  "data-ckui": c,
42
43
  children: l
43
44
  })
44
45
  })
45
46
  });
46
- return t || n ? /* @__PURE__ */ g(y, {
47
+ return t || n ? /* @__PURE__ */ S(T, {
47
48
  language: t,
48
49
  messages: n,
49
50
  children: f
50
51
  }) : f;
51
52
  }
52
- function x({ children: e }) {
53
+ function D({ children: e }) {
53
54
  let { t } = r();
54
- return /* @__PURE__ */ _("div", {
55
+ return /* @__PURE__ */ C("div", {
55
56
  className: "grid gap-1 rounded-lg border border-dashed border-border p-6 text-center",
56
57
  "data-ckui": "no-access",
57
58
  role: "status",
58
- children: [/* @__PURE__ */ g("p", {
59
+ children: [/* @__PURE__ */ S("p", {
59
60
  className: "font-medium",
60
61
  children: t("admin.noAccessTitle")
61
- }), /* @__PURE__ */ g("p", {
62
+ }), /* @__PURE__ */ S("p", {
62
63
  className: "text-muted-foreground",
63
64
  children: e ?? t("admin.noAccess")
64
65
  })]
65
66
  });
66
67
  }
67
- function S({ count: e = 3 }) {
68
- return /* @__PURE__ */ g("div", {
68
+ function O({ count: e = 3 }) {
69
+ return /* @__PURE__ */ S("div", {
69
70
  className: "grid gap-3",
70
71
  "aria-busy": !0,
71
- children: Array.from({ length: e }, (e, t) => /* @__PURE__ */ g(u, { className: "h-14 w-full" }, t))
72
+ children: Array.from({ length: e }, (e, t) => /* @__PURE__ */ S(h, { className: "h-14 w-full" }, t))
72
73
  });
73
74
  }
74
- function C({ error: e, message: t, retry: n }) {
75
+ function k({ error: e, message: t, retry: n }) {
75
76
  let { t: i } = r(), a = e?.code === "forbidden" || e?.code === "unauthorized";
76
- return /* @__PURE__ */ _("div", {
77
+ return /* @__PURE__ */ C("div", {
77
78
  className: "grid justify-items-start gap-2",
78
79
  role: "alert",
79
- children: [/* @__PURE__ */ g("p", {
80
+ children: [/* @__PURE__ */ S("p", {
80
81
  className: "text-destructive",
81
82
  children: a ? i(e.code === "forbidden" ? "contentErrors.forbidden" : "contentErrors.unauthorized") : t
82
- }), /* @__PURE__ */ g(l, {
83
+ }), /* @__PURE__ */ S(l, {
83
84
  size: "sm",
84
85
  variant: "outline",
85
86
  onClick: n,
@@ -87,47 +88,118 @@ function C({ error: e, message: t, retry: n }) {
87
88
  })]
88
89
  });
89
90
  }
90
- function w({ page: e, hasNext: t, onPage: n, disabled: i }) {
91
- let { t: a } = r();
92
- return e <= 1 && !t ? null : /* @__PURE__ */ _("nav", {
91
+ function A({ page: e, hasNext: t, onPage: n, disabled: i, total: a, truncated: o, pageSize: s = 20, onPageSize: c, pageSizes: h = [
92
+ 20,
93
+ 50,
94
+ 100
95
+ ] }) {
96
+ let { t: g } = r();
97
+ if (a === void 0) return e <= 1 && !t ? null : /* @__PURE__ */ C("nav", {
93
98
  className: "flex items-center justify-center gap-3",
94
- "aria-label": a("admin.pages"),
99
+ "aria-label": g("admin.pages"),
95
100
  "data-ckui": "pager",
96
101
  children: [
97
- /* @__PURE__ */ g(l, {
102
+ /* @__PURE__ */ S(l, {
98
103
  size: "sm",
99
104
  variant: "outline",
100
105
  disabled: i || e <= 1,
101
106
  onClick: () => n(e - 1),
102
- children: a("admin.previous")
107
+ children: g("admin.previous")
103
108
  }),
104
- /* @__PURE__ */ g("span", {
109
+ /* @__PURE__ */ S("span", {
105
110
  className: "text-muted-foreground",
106
111
  "aria-live": "polite",
107
- children: a("admin.page", { page: e })
112
+ children: g("admin.page", { page: e })
108
113
  }),
109
- /* @__PURE__ */ g(l, {
114
+ /* @__PURE__ */ S(l, {
110
115
  size: "sm",
111
116
  variant: "outline",
112
117
  disabled: i || !t,
113
118
  onClick: () => n(e + 1),
114
- children: a("admin.next")
119
+ children: g("admin.next")
115
120
  })
116
121
  ]
117
122
  });
123
+ if (!a) return null;
124
+ let _ = Math.max(1, Math.ceil(a / s)), v = (e - 1) * s + 1, y = Math.min(e * s, a);
125
+ return /* @__PURE__ */ C("nav", {
126
+ className: "flex flex-wrap items-center justify-between gap-3 text-sm",
127
+ "aria-label": g("admin.pages"),
128
+ "data-ckui": "pager",
129
+ children: [/* @__PURE__ */ S("span", {
130
+ className: "text-muted-foreground",
131
+ "aria-live": "polite",
132
+ children: v <= y && g(o ? "admin.showingMore" : "admin.showing", {
133
+ from: v,
134
+ to: y,
135
+ total: a
136
+ })
137
+ }), /* @__PURE__ */ C("div", {
138
+ className: "flex flex-wrap items-center gap-2",
139
+ children: [c && /* @__PURE__ */ C(u, {
140
+ value: String(s),
141
+ disabled: i,
142
+ onValueChange: (e) => e && c(Number(e)),
143
+ children: [/* @__PURE__ */ S(p, {
144
+ size: "sm",
145
+ "aria-label": g("admin.perPage"),
146
+ children: /* @__PURE__ */ S(m, { children: (e) => `${g("admin.perPage")}: ${e}` })
147
+ }), /* @__PURE__ */ S(d, { children: h.map((e) => /* @__PURE__ */ S(f, {
148
+ value: String(e),
149
+ children: e
150
+ }, e)) })]
151
+ }), (_ > 1 || e > 1) && /* @__PURE__ */ C(x, { children: [
152
+ /* @__PURE__ */ S(l, {
153
+ size: "sm",
154
+ variant: "outline",
155
+ disabled: i || e <= 1,
156
+ onClick: () => n(1),
157
+ children: g("admin.first")
158
+ }),
159
+ /* @__PURE__ */ S(l, {
160
+ size: "sm",
161
+ variant: "outline",
162
+ disabled: i || e <= 1,
163
+ onClick: () => n(e - 1),
164
+ children: g("admin.previous")
165
+ }),
166
+ /* @__PURE__ */ S("span", {
167
+ className: "text-muted-foreground",
168
+ children: g(o ? "admin.pageOfMore" : "admin.pageOf", {
169
+ page: e,
170
+ pages: _
171
+ })
172
+ }),
173
+ /* @__PURE__ */ S(l, {
174
+ size: "sm",
175
+ variant: "outline",
176
+ disabled: i || !t,
177
+ onClick: () => n(e + 1),
178
+ children: g("admin.next")
179
+ }),
180
+ /* @__PURE__ */ S(l, {
181
+ size: "sm",
182
+ variant: "outline",
183
+ disabled: i || o || e >= _,
184
+ onClick: () => n(_),
185
+ children: g("admin.last")
186
+ })
187
+ ] })]
188
+ })]
189
+ });
118
190
  }
119
- function T(e) {
191
+ function j(e) {
120
192
  let t = Number(e.get("page"));
121
193
  return Number.isInteger(t) && t > 1 ? t : 1;
122
194
  }
123
- function E() {
124
- let [e, t] = h(null);
195
+ function M() {
196
+ let [e, t] = b(null);
125
197
  return {
126
- confirm: f((e) => new Promise((n) => t({
198
+ confirm: _((e) => new Promise((n) => t({
127
199
  ...e,
128
200
  resolve: n
129
201
  })), []),
130
- dialog: /* @__PURE__ */ g(d, {
202
+ dialog: /* @__PURE__ */ S(g, {
131
203
  open: !!e,
132
204
  onOpenChange: (n) => {
133
205
  !n && e && (e.resolve(!1), t(null));
@@ -143,6 +215,6 @@ function E() {
143
215
  };
144
216
  }
145
217
  //#endregion
146
- export { b as AdminRoot, C as LoadFailed, x as NoAccess, w as Pager, S as Rows, T as pageOf, E as useConfirm, c as useGuarded };
218
+ export { E as AdminRoot, k as LoadFailed, D as NoAccess, A as Pager, O as Rows, j as pageOf, M as useConfirm, c as useGuarded };
147
219
 
148
220
  //# sourceMappingURL=shell.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","names":[],"sources":["../../../src/components/admin/shell.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport { MessagesContext, useMessages } from \"../../i18n/context.js\";\nimport { createTranslator, resolveMessages, type ContentKitUiMessageBundle } from \"../../i18n/messages.js\";\nimport { useTranslator } from \"../../provider.js\";\nimport type { AdminNavigation, ContentKitAuth } from \"../../react/admin.js\";\nimport { GuardContext, useGuarded, type ContentKitErrorHandler, type ContentKitGuard } from \"../../react/context.js\";\nimport { AppearanceContext, ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { ConfirmDialog } from \"../social/parts.js\";\n\nexport { useGuarded, type ContentKitGuard };\n\n/** What every drop-in admin flow takes. */\nexport interface AdminFlowProps {\n /** The ContentKit client; or give baseUrl and auth. Without either, the provider's. */\n client?: ContentKitClient;\n /** Where the host mounts ContentKit, with auth. */\n baseUrl?: string;\n /** The host's auth (auth-ui's AuthClient or IssuerClient): its authFetch carries the session. */\n auth?: ContentKitAuth;\n /** Runs every write (auth-ui's useStepUpGuard()). */\n guard?: ContentKitGuard;\n /** Default theme \"inherit\": the host's shadcn variables. */\n appearance?: ContentKitUiAppearance;\n className?: string;\n /** BCP 47; its built-in bundle loads. Default the provider's. */\n language?: string;\n messages?: ContentKitUiMessageBundle | readonly ContentKitUiMessageBundle[];\n onError?: ContentKitErrorHandler;\n}\n\n/** A multi-view flow's place: views and filters in the URL under basePath, through the host router when given. */\nexport interface AdminRouteProps {\n /** Where the host mounts the flow, e.g. \"/admin/comments\". Without it, views live in memory. */\n basePath?: string;\n /** The host router; without it the history API. */\n navigation?: AdminNavigation;\n}\n\nfunction Words({ language, messages, children }: { language?: string; messages?: AdminFlowProps[\"messages\"]; children: ReactNode }) {\n const parent = useMessages();\n const own = useTranslator(language, messages);\n const layered = useMemo(\n () => (language ? own : createTranslator(resolveMessages([parent.messages as ContentKitUiMessageBundle, ...(messages ? (Array.isArray(messages) ? messages : [messages]) : [])]), undefined, parent.language)),\n [language, own, parent, messages],\n );\n return <MessagesContext.Provider value={layered}>{children}</MessagesContext.Provider>;\n}\n\n/** A flow's root: its words, its look (the host's theme unless set), the guard for writes, and the `.ckui` scope. */\nexport function AdminRoot({\n appearance,\n language,\n messages,\n guard,\n className,\n flow,\n children,\n}: Pick<AdminFlowProps, \"appearance\" | \"language\" | \"messages\" | \"guard\" | \"className\"> & { flow: string; children: ReactNode }) {\n const parent = useContext(AppearanceContext);\n const look = useMemo<ContentKitUiAppearance>(\n () => ({ ...parent, ...appearance, theme: appearance?.theme ?? parent?.theme ?? \"inherit\", variables: { ...parent?.variables, ...appearance?.variables } }),\n [parent, appearance],\n );\n const body = (\n <AppearanceContext.Provider value={look}>\n <GuardContext.Provider value={guard}>\n <ContentKitUiRoot appearance={look} className={cn(\"grid gap-4 text-sm text-foreground\", className)} data-ckui={flow}>\n {children}\n </ContentKitUiRoot>\n </GuardContext.Provider>\n </AppearanceContext.Provider>\n );\n return language || messages ? (\n <Words language={language} messages={messages}>\n {body}\n </Words>\n ) : (\n body\n );\n}\n\n/** Said when the caller may do nothing here. */\nexport function NoAccess({ children }: { children?: ReactNode }) {\n const { t } = useMessages();\n return (\n <div className=\"grid gap-1 rounded-lg border border-dashed border-border p-6 text-center\" data-ckui=\"no-access\" role=\"status\">\n <p className=\"font-medium\">{t(\"admin.noAccessTitle\")}</p>\n <p className=\"text-muted-foreground\">{children ?? t(\"admin.noAccess\")}</p>\n </div>\n );\n}\n\n/** Rows loading. */\nexport function Rows({ count = 3 }: { count?: number }) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n {Array.from({ length: count }, (_, i) => (\n <Skeleton key={i} className=\"h-14 w-full\" />\n ))}\n </div>\n );\n}\n\n/** A failed read with a retry; a refusal says so. */\nexport function LoadFailed({ error, message, retry }: { error?: ContentKitError; message: string; retry: () => void }) {\n const { t } = useMessages();\n const refused = error?.code === \"forbidden\" || error?.code === \"unauthorized\";\n return (\n <div className=\"grid justify-items-start gap-2\" role=\"alert\">\n <p className=\"text-destructive\">{refused ? t(error.code === \"forbidden\" ? \"contentErrors.forbidden\" : \"contentErrors.unauthorized\") : message}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={retry}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n}\n\n/** Previous and next around the current page number, announced as it changes. */\nexport function Pager({ page, hasNext, onPage, disabled }: { page: number; hasNext: boolean; onPage: (page: number) => void; disabled?: boolean }) {\n const { t } = useMessages();\n if (page <= 1 && !hasNext) return null;\n return (\n <nav className=\"flex items-center justify-center gap-3\" aria-label={t(\"admin.pages\")} data-ckui=\"pager\">\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || page <= 1} onClick={() => onPage(page - 1)}>\n {t(\"admin.previous\")}\n </Button>\n <span className=\"text-muted-foreground\" aria-live=\"polite\">\n {t(\"admin.page\", { page })}\n </span>\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || !hasNext} onClick={() => onPage(page + 1)}>\n {t(\"admin.next\")}\n </Button>\n </nav>\n );\n}\n\n/** A positive page number from the query; 1 otherwise. */\nexport function pageOf(q: URLSearchParams): number {\n const n = Number(q.get(\"page\"));\n return Number.isInteger(n) && n > 1 ? n : 1;\n}\n\nexport interface ConfirmOptions {\n title: ReactNode;\n description?: ReactNode;\n confirm: ReactNode;\n /** A word to type first: for acts that cannot be undone. */\n typed?: string;\n}\n\n/** A promise-based confirmation: `if (await confirm({ … })) …`; render `dialog` once. */\nexport function useConfirm(): { confirm: (o: ConfirmOptions) => Promise<boolean>; dialog: ReactNode } {\n const [asking, setAsking] = useState<(ConfirmOptions & { resolve: (ok: boolean) => void }) | null>(null);\n const confirm = useCallback((o: ConfirmOptions) => new Promise<boolean>((resolve) => setAsking({ ...o, resolve })), []);\n const dialog = (\n <ConfirmDialog\n open={!!asking}\n onOpenChange={(open) => {\n if (!open && asking) {\n asking.resolve(false);\n setAsking(null);\n }\n }}\n title={asking?.title}\n description={asking?.description}\n confirm={asking?.confirm}\n typed={asking?.typed}\n onConfirm={() => {\n asking?.resolve(true);\n setAsking(null);\n }}\n />\n );\n return { confirm, dialog };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shell.js","names":[],"sources":["../../../src/components/admin/shell.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport { MessagesContext, useMessages } from \"../../i18n/context.js\";\nimport { createTranslator, resolveMessages, type ContentKitUiMessageBundle } from \"../../i18n/messages.js\";\nimport { useTranslator } from \"../../provider.js\";\nimport type { AdminNavigation, ContentKitAuth } from \"../../react/admin.js\";\nimport { GuardContext, useGuarded, type ContentKitErrorHandler, type ContentKitGuard } from \"../../react/context.js\";\nimport { AppearanceContext, ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { ConfirmDialog } from \"../social/parts.js\";\n\nexport { useGuarded, type ContentKitGuard };\n\n/** What every drop-in admin flow takes. */\nexport interface AdminFlowProps {\n /** The ContentKit client; or give baseUrl and auth. Without either, the provider's. */\n client?: ContentKitClient;\n /** Where the host mounts ContentKit, with auth. */\n baseUrl?: string;\n /** The host's auth (auth-ui's AuthClient or IssuerClient): its authFetch carries the session. */\n auth?: ContentKitAuth;\n /** Runs every write (auth-ui's useStepUpGuard()). */\n guard?: ContentKitGuard;\n /** Default theme \"inherit\": the host's shadcn variables. */\n appearance?: ContentKitUiAppearance;\n className?: string;\n /** BCP 47; its built-in bundle loads. Default the provider's. */\n language?: string;\n messages?: ContentKitUiMessageBundle | readonly ContentKitUiMessageBundle[];\n onError?: ContentKitErrorHandler;\n}\n\n/** A multi-view flow's place: views and filters in the URL under basePath, through the host router when given. */\nexport interface AdminRouteProps {\n /** Where the host mounts the flow, e.g. \"/admin/comments\". Without it, views live in memory. */\n basePath?: string;\n /** The host router; without it the history API. */\n navigation?: AdminNavigation;\n}\n\nfunction Words({ language, messages, children }: { language?: string; messages?: AdminFlowProps[\"messages\"]; children: ReactNode }) {\n const parent = useMessages();\n const own = useTranslator(language, messages);\n const layered = useMemo(\n () => (language ? own : createTranslator(resolveMessages([parent.messages as ContentKitUiMessageBundle, ...(messages ? (Array.isArray(messages) ? messages : [messages]) : [])]), undefined, parent.language)),\n [language, own, parent, messages],\n );\n return <MessagesContext.Provider value={layered}>{children}</MessagesContext.Provider>;\n}\n\n/** A flow's root: its words, its look (the host's theme unless set), the guard for writes, and the `.ckui` scope. */\nexport function AdminRoot({\n appearance,\n language,\n messages,\n guard,\n className,\n flow,\n children,\n}: Pick<AdminFlowProps, \"appearance\" | \"language\" | \"messages\" | \"guard\" | \"className\"> & { flow: string; children: ReactNode }) {\n const parent = useContext(AppearanceContext);\n const look = useMemo<ContentKitUiAppearance>(\n () => ({ ...parent, ...appearance, theme: appearance?.theme ?? parent?.theme ?? \"inherit\", variables: { ...parent?.variables, ...appearance?.variables } }),\n [parent, appearance],\n );\n const body = (\n <AppearanceContext.Provider value={look}>\n <GuardContext.Provider value={guard}>\n <ContentKitUiRoot appearance={look} className={cn(\"grid gap-4 text-sm text-foreground\", className)} data-ckui={flow}>\n {children}\n </ContentKitUiRoot>\n </GuardContext.Provider>\n </AppearanceContext.Provider>\n );\n return language || messages ? (\n <Words language={language} messages={messages}>\n {body}\n </Words>\n ) : (\n body\n );\n}\n\n/** Said when the caller may do nothing here. */\nexport function NoAccess({ children }: { children?: ReactNode }) {\n const { t } = useMessages();\n return (\n <div className=\"grid gap-1 rounded-lg border border-dashed border-border p-6 text-center\" data-ckui=\"no-access\" role=\"status\">\n <p className=\"font-medium\">{t(\"admin.noAccessTitle\")}</p>\n <p className=\"text-muted-foreground\">{children ?? t(\"admin.noAccess\")}</p>\n </div>\n );\n}\n\n/** Rows loading. */\nexport function Rows({ count = 3 }: { count?: number }) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n {Array.from({ length: count }, (_, i) => (\n <Skeleton key={i} className=\"h-14 w-full\" />\n ))}\n </div>\n );\n}\n\n/** A failed read with a retry; a refusal says so. */\nexport function LoadFailed({ error, message, retry }: { error?: ContentKitError; message: string; retry: () => void }) {\n const { t } = useMessages();\n const refused = error?.code === \"forbidden\" || error?.code === \"unauthorized\";\n return (\n <div className=\"grid justify-items-start gap-2\" role=\"alert\">\n <p className=\"text-destructive\">{refused ? t(error.code === \"forbidden\" ? \"contentErrors.forbidden\" : \"contentErrors.unauthorized\") : message}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={retry}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n}\n\n/** Previous and next around the current page number, announced as it changes. */\nexport interface PagerProps {\n page: number;\n hasNext: boolean;\n onPage: (page: number) => void;\n disabled?: boolean;\n /** Matching items; with truncated, a lower bound. Adds the range, page count and first/last. */\n total?: number;\n truncated?: boolean;\n pageSize?: number;\n /** Offers these page sizes when set. */\n onPageSize?: (size: number) => void;\n pageSizes?: readonly number[];\n}\n\nexport function Pager({ page, hasNext, onPage, disabled, total, truncated, pageSize = 20, onPageSize, pageSizes = [20, 50, 100] }: PagerProps) {\n const { t } = useMessages();\n if (total === undefined) {\n if (page <= 1 && !hasNext) return null;\n return (\n <nav className=\"flex items-center justify-center gap-3\" aria-label={t(\"admin.pages\")} data-ckui=\"pager\">\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || page <= 1} onClick={() => onPage(page - 1)}>\n {t(\"admin.previous\")}\n </Button>\n <span className=\"text-muted-foreground\" aria-live=\"polite\">\n {t(\"admin.page\", { page })}\n </span>\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || !hasNext} onClick={() => onPage(page + 1)}>\n {t(\"admin.next\")}\n </Button>\n </nav>\n );\n }\n if (!total) return null;\n const pages = Math.max(1, Math.ceil(total / pageSize));\n const from = (page - 1) * pageSize + 1;\n const to = Math.min(page * pageSize, total);\n return (\n <nav className=\"flex flex-wrap items-center justify-between gap-3 text-sm\" aria-label={t(\"admin.pages\")} data-ckui=\"pager\">\n <span className=\"text-muted-foreground\" aria-live=\"polite\">\n {from <= to && t(truncated ? \"admin.showingMore\" : \"admin.showing\", { from, to, total })}\n </span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {onPageSize && (\n <Select value={String(pageSize)} disabled={disabled} onValueChange={(v) => v && onPageSize(Number(v))}>\n <SelectTrigger size=\"sm\" aria-label={t(\"admin.perPage\")}>\n <SelectValue>{(v: string) => `${t(\"admin.perPage\")}: ${v}`}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {pageSizes.map((n) => (\n <SelectItem key={n} value={String(n)}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {(pages > 1 || page > 1) && (\n <>\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || page <= 1} onClick={() => onPage(1)}>\n {t(\"admin.first\")}\n </Button>\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || page <= 1} onClick={() => onPage(page - 1)}>\n {t(\"admin.previous\")}\n </Button>\n <span className=\"text-muted-foreground\">{t(truncated ? \"admin.pageOfMore\" : \"admin.pageOf\", { page, pages })}</span>\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || !hasNext} onClick={() => onPage(page + 1)}>\n {t(\"admin.next\")}\n </Button>\n {/* A truncated total has no known last page. */}\n <Button size=\"sm\" variant=\"outline\" disabled={disabled || truncated || page >= pages} onClick={() => onPage(pages)}>\n {t(\"admin.last\")}\n </Button>\n </>\n )}\n </div>\n </nav>\n );\n}\n\n/** A positive page number from the query; 1 otherwise. */\nexport function pageOf(q: URLSearchParams): number {\n const n = Number(q.get(\"page\"));\n return Number.isInteger(n) && n > 1 ? n : 1;\n}\n\nexport interface ConfirmOptions {\n title: ReactNode;\n description?: ReactNode;\n confirm: ReactNode;\n /** A word to type first: for acts that cannot be undone. */\n typed?: string;\n}\n\n/** A promise-based confirmation: `if (await confirm({ … })) …`; render `dialog` once. */\nexport function useConfirm(): { confirm: (o: ConfirmOptions) => Promise<boolean>; dialog: ReactNode } {\n const [asking, setAsking] = useState<(ConfirmOptions & { resolve: (ok: boolean) => void }) | null>(null);\n const confirm = useCallback((o: ConfirmOptions) => new Promise<boolean>((resolve) => setAsking({ ...o, resolve })), []);\n const dialog = (\n <ConfirmDialog\n open={!!asking}\n onOpenChange={(open) => {\n if (!open && asking) {\n asking.resolve(false);\n setAsking(null);\n }\n }}\n title={asking?.title}\n description={asking?.description}\n confirm={asking?.confirm}\n typed={asking?.typed}\n onConfirm={() => {\n asking?.resolve(true);\n setAsking(null);\n }}\n />\n );\n return { confirm, dialog };\n}\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { RankedContent } from '../../client/generated/wire.js';
2
1
  import { AdminFlowProps } from './shell.js';
3
2
  type Choice = string | {
4
3
  value: string;
@@ -7,16 +6,12 @@ type Choice = string | {
7
6
  export interface StatsDashboardProps extends AdminFlowProps {
8
7
  /** Content kinds offered: codes, or codes with labels; the first shows first. */
9
8
  kinds: readonly Choice[];
10
- /** A ranked item's link; default its path. */
11
- contentHref?: (content: RankedContent) => string | undefined;
12
- /** Rows in the ranking. Default 10. */
13
- limit?: number;
14
9
  }
15
10
  /**
16
- * Staff statistics from measured attention: daily distinct people and focused
17
- * active time, plus content ranked by distinct engaged people or comments.
18
- * Daily people are never summed into a misleading period-unique total.
19
- * Comments remain available without the signal plane.
11
+ * Staff statistics from measured attention: distinct people per UTC day who
12
+ * read or played at least one item, by audience (anonymous, free, premium).
13
+ * People are distinct within a day only, so days are never summed into a
14
+ * period-unique total.
20
15
  */
21
16
  export declare function StatsDashboard({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: StatsDashboardProps): import('react').JSX.Element;
22
17
  export {};