@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.
- package/README.md +6 -6
- package/dist/client/beacons.js +4 -1
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/contents.d.ts +9 -1
- package/dist/client/contents.js +73 -55
- package/dist/client/contents.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -2
- package/dist/client/discovery.js +1 -1
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +4 -0
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +43 -30
- package/dist/client/generated/routes.js +70 -63
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +36 -74
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/media/windows.d.ts +2 -0
- package/dist/client/media/windows.js +4 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +4 -20
- package/dist/client/stats.js +0 -11
- package/dist/client/stats.js.map +1 -1
- package/dist/client/versions.d.ts +8 -6
- package/dist/client/versions.js +15 -10
- package/dist/client/versions.js.map +1 -1
- package/dist/components/admin/bulk-drafts.d.ts +17 -0
- package/dist/components/admin/bulk-drafts.js +41 -0
- package/dist/components/admin/bulk-drafts.js.map +1 -0
- package/dist/components/admin/content-admin.d.ts +1 -1
- package/dist/components/admin/content-admin.js.map +1 -1
- package/dist/components/admin/content-manager.d.ts +11 -3
- package/dist/components/admin/content-manager.js +739 -380
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.d.ts +8 -3
- package/dist/components/admin/group-manager.js +218 -146
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/page-notices.d.ts +16 -0
- package/dist/components/admin/page-notices.js +113 -0
- package/dist/components/admin/page-notices.js.map +1 -0
- package/dist/components/admin/page-picker.d.ts +38 -0
- package/dist/components/admin/page-picker.js +159 -0
- package/dist/components/admin/page-picker.js.map +1 -0
- package/dist/components/admin/reader-content-editor.js +537 -500
- package/dist/components/admin/reader-content-editor.js.map +1 -1
- package/dist/components/admin/reader-manifest.js +181 -179
- package/dist/components/admin/reader-manifest.js.map +1 -1
- package/dist/components/admin/shell.d.ts +10 -2
- package/dist/components/admin/shell.js +114 -42
- package/dist/components/admin/shell.js.map +1 -1
- package/dist/components/admin/stats-dashboard.d.ts +4 -9
- package/dist/components/admin/stats-dashboard.js +300 -202
- package/dist/components/admin/stats-dashboard.js.map +1 -1
- package/dist/components/admin/upload-queue.d.ts +14 -0
- package/dist/components/admin/upload-queue.js +115 -98
- package/dist/components/admin/upload-queue.js.map +1 -1
- package/dist/components/content/card.d.ts +31 -3
- package/dist/components/content/card.js +324 -158
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.js +14 -12
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editor-save.js +27 -25
- package/dist/components/content/editor-save.js.map +1 -1
- package/dist/components/content/editors.js +274 -253
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/favorites.d.ts +1 -1
- package/dist/components/content/favorites.js +36 -22
- package/dist/components/content/favorites.js.map +1 -1
- package/dist/components/content/grid.d.ts +11 -4
- package/dist/components/content/grid.js +52 -43
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.js +83 -63
- package/dist/components/content/history.js.map +1 -1
- package/dist/components/content/index.d.ts +5 -3
- package/dist/components/content/listing.d.ts +14 -3
- package/dist/components/content/listing.js +65 -56
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/offer-fields.js +6 -6
- package/dist/components/content/paged-grid.d.ts +10 -0
- package/dist/components/content/paged-grid.js +116 -0
- package/dist/components/content/paged-grid.js.map +1 -0
- package/dist/components/content/pagination.d.ts +19 -0
- package/dist/components/content/pagination.js +97 -0
- package/dist/components/content/pagination.js.map +1 -0
- package/dist/components/content/paywall.d.ts +9 -0
- package/dist/components/content/paywall.js +27 -3
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/post-list.d.ts +37 -0
- package/dist/components/content/post-list.js +203 -0
- package/dist/components/content/post-list.js.map +1 -0
- package/dist/components/content/reader-settings.d.ts +2 -0
- package/dist/components/content/reader-settings.js +1 -1
- package/dist/components/content/reader-settings.js.map +1 -1
- package/dist/components/groups/banner.d.ts +6 -2
- package/dist/components/groups/banner.js +79 -61
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.d.ts +12 -2
- package/dist/components/groups/directory.js +168 -105
- package/dist/components/groups/directory.js.map +1 -1
- package/dist/components/groups/editor.js +10 -10
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/entity-chips.d.ts +61 -0
- package/dist/components/groups/entity-chips.js +382 -0
- package/dist/components/groups/entity-chips.js.map +1 -0
- package/dist/components/groups/group-members.d.ts +29 -0
- package/dist/components/groups/group-members.js +474 -0
- package/dist/components/groups/group-members.js.map +1 -0
- package/dist/components/groups/header.js +53 -45
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/index.d.ts +3 -1
- package/dist/components/groups/memberships.d.ts +3 -36
- package/dist/components/groups/memberships.js +105 -480
- package/dist/components/groups/memberships.js.map +1 -1
- package/dist/components/groups/scoped.d.ts +23 -0
- package/dist/components/groups/scoped.js +92 -0
- package/dist/components/groups/scoped.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +5 -0
- package/dist/components/media-folder-editor.js +418 -230
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/social/poll.js +57 -48
- package/dist/components/social/poll.js.map +1 -1
- package/dist/components/sortable-list.d.ts +5 -1
- package/dist/components/sortable-list.js +97 -81
- package/dist/components/sortable-list.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -37
- package/dist/locales/admin/de.js +78 -45
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +74 -41
- package/dist/locales/admin/en.js +78 -45
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +78 -45
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +78 -45
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +78 -45
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +78 -45
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +2 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +25 -1
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +24 -0
- package/dist/locales/discovery/en.js +25 -1
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +25 -1
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +25 -1
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +25 -1
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +25 -1
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +120 -43
- package/dist/locales/en.js +2 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +2 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/groups/de.js +22 -4
- package/dist/locales/groups/de.js.map +1 -1
- package/dist/locales/groups/en.d.ts +20 -2
- package/dist/locales/groups/en.js +22 -4
- package/dist/locales/groups/en.js.map +1 -1
- package/dist/locales/groups/es.js +22 -4
- package/dist/locales/groups/es.js.map +1 -1
- package/dist/locales/groups/ja.js +22 -4
- package/dist/locales/groups/ja.js.map +1 -1
- package/dist/locales/groups/ko.js +22 -4
- package/dist/locales/groups/ko.js.map +1 -1
- package/dist/locales/groups/zh.js +22 -4
- package/dist/locales/groups/zh.js.map +1 -1
- package/dist/locales/ja.js +2 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +2 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +2 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/contents.js +1 -1
- package/dist/react/contents.js.map +1 -1
- package/dist/react/discovery.d.ts +16 -0
- package/dist/react/discovery.js +69 -37
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/groups.d.ts +19 -3
- package/dist/react/groups.js +49 -22
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +6 -4
- package/dist/react/page-notices.d.ts +13 -0
- package/dist/react/page-notices.js +38 -0
- package/dist/react/page-notices.js.map +1 -0
- package/dist/react/post-reads.d.ts +23 -0
- package/dist/react/post-reads.js +90 -0
- package/dist/react/post-reads.js.map +1 -0
- package/dist/react/remembered-sort.d.ts +5 -0
- package/dist/react/remembered-sort.js +29 -0
- package/dist/react/remembered-sort.js.map +1 -0
- package/dist/react/resources.js +2 -2
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +3 -7
- package/dist/react/stats.js +2 -6
- package/dist/react/stats.js.map +1 -1
- package/dist/react/use-engagement.d.ts +2 -0
- package/dist/react/use-engagement.js +40 -34
- package/dist/react/use-engagement.js.map +1 -1
- package/dist/react.js +29 -27
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +1 -1
- package/dist/urls.js +2 -2
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/admin/page-review.d.ts +0 -13
- package/dist/components/admin/page-review.js +0 -225
- package/dist/components/admin/page-review.js.map +0 -1
- package/dist/react/page-review.d.ts +0 -14
- package/dist/react/page-review.js +0 -37
- 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
|
|
54
|
+
export interface PagerProps {
|
|
55
55
|
page: number;
|
|
56
56
|
hasNext: boolean;
|
|
57
57
|
onPage: (page: number) => void;
|
|
58
58
|
disabled?: boolean;
|
|
59
|
-
|
|
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 {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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
|
|
14
|
-
let c = r(), l = o(i, a), u =
|
|
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__ */
|
|
21
|
+
return /* @__PURE__ */ S(n.Provider, {
|
|
21
22
|
value: u,
|
|
22
23
|
children: s
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
|
-
function
|
|
26
|
-
let u =
|
|
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__ */
|
|
35
|
+
}), [u, e]), f = /* @__PURE__ */ S(i.Provider, {
|
|
35
36
|
value: d,
|
|
36
|
-
children: /* @__PURE__ */
|
|
37
|
+
children: /* @__PURE__ */ S(s.Provider, {
|
|
37
38
|
value: r,
|
|
38
|
-
children: /* @__PURE__ */
|
|
39
|
+
children: /* @__PURE__ */ S(a, {
|
|
39
40
|
appearance: d,
|
|
40
|
-
className:
|
|
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__ */
|
|
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
|
|
53
|
+
function D({ children: e }) {
|
|
53
54
|
let { t } = r();
|
|
54
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
59
|
+
children: [/* @__PURE__ */ S("p", {
|
|
59
60
|
className: "font-medium",
|
|
60
61
|
children: t("admin.noAccessTitle")
|
|
61
|
-
}), /* @__PURE__ */
|
|
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
|
|
68
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
72
|
+
children: Array.from({ length: e }, (e, t) => /* @__PURE__ */ S(h, { className: "h-14 w-full" }, t))
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
|
-
function
|
|
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__ */
|
|
77
|
+
return /* @__PURE__ */ C("div", {
|
|
77
78
|
className: "grid justify-items-start gap-2",
|
|
78
79
|
role: "alert",
|
|
79
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
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":
|
|
99
|
+
"aria-label": g("admin.pages"),
|
|
95
100
|
"data-ckui": "pager",
|
|
96
101
|
children: [
|
|
97
|
-
/* @__PURE__ */
|
|
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:
|
|
107
|
+
children: g("admin.previous")
|
|
103
108
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ S("span", {
|
|
105
110
|
className: "text-muted-foreground",
|
|
106
111
|
"aria-live": "polite",
|
|
107
|
-
children:
|
|
112
|
+
children: g("admin.page", { page: e })
|
|
108
113
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
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:
|
|
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
|
|
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
|
|
124
|
-
let [e, t] =
|
|
195
|
+
function M() {
|
|
196
|
+
let [e, t] = b(null);
|
|
125
197
|
return {
|
|
126
|
-
confirm:
|
|
198
|
+
confirm: _((e) => new Promise((n) => t({
|
|
127
199
|
...e,
|
|
128
200
|
resolve: n
|
|
129
201
|
})), []),
|
|
130
|
-
dialog: /* @__PURE__ */ g
|
|
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 {
|
|
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":";;;;;;;;;;;;AA4CA,SAAS,EAAM,EAAE,aAAU,aAAU,eAA+F;CAClI,IAAM,IAAS,EAAY,GACrB,IAAM,EAAc,GAAU,CAAQ,GACtC,IAAU,QACP,IAAW,IAAM,EAAiB,EAAgB,CAAC,EAAO,UAAuC,GAAI,IAAY,MAAM,QAAQ,CAAQ,IAAI,IAAW,CAAC,CAAQ,IAAK,CAAC,CAAE,CAAC,GAAG,KAAA,GAAW,EAAO,QAAQ,GAC5M;EAAC;EAAU;EAAK;EAAQ;CAAQ,CAClC;CACA,OAAO,kBAAC,EAAgB,UAAjB;EAA0B,OAAO;EAAU;CAAmC,CAAA;AACvF;AAGA,SAAgB,EAAU,EACxB,eACA,aACA,aACA,UACA,cACA,SACA,eAC+H;CAC/H,IAAM,IAAS,EAAW,CAAiB,GACrC,IAAO,SACJ;EAAE,GAAG;EAAQ,GAAG;EAAY,OAAO,GAAY,SAAS,GAAQ,SAAS;EAAW,WAAW;GAAE,GAAG,GAAQ;GAAW,GAAG,GAAY;EAAU;CAAE,IACzJ,CAAC,GAAQ,CAAU,CACrB,GACM,IACJ,kBAAC,EAAkB,UAAnB;EAA4B,OAAO;EACjC,UAAA,kBAAC,EAAa,UAAd;GAAuB,OAAO;GAC5B,UAAA,kBAAC,GAAD;IAAkB,YAAY;IAAM,WAAW,EAAG,sCAAsC,CAAS;IAAG,aAAW;IAC5G;GACe,CAAA;EACG,CAAA;CACG,CAAA;CAE9B,OAAO,KAAY,IACjB,kBAAC,GAAD;EAAiB;EAAoB;EAClC,UAAA;CACI,CAAA,IAEP;AAEJ;AAGA,SAAgB,EAAS,EAAE,eAAsC;CAC/D,IAAM,EAAE,MAAM,EAAY;CAC1B,OACE,kBAAC,OAAD;EAAK,WAAU;EAA2E,aAAU;EAAY,MAAK;EAArH,UAAA,CACE,kBAAC,KAAD;GAAG,WAAU;GAAe,UAAA,EAAE,qBAAqB;EAAK,CAAA,GACxD,kBAAC,KAAD;GAAG,WAAU;GAAyB,UAAA,KAAY,EAAE,gBAAgB;EAAK,CAAA,CACtE;;AAET;AAGA,SAAgB,EAAK,EAAE,WAAQ,KAAyB;CACtD,OACE,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAA;EACzB,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAM,IAAI,GAAG,MACjC,kBAAC,GAAD,EAAkB,WAAU,cAAe,GAA5B,CAA4B,CAC5C;CACE,CAAA;AAET;AAGA,SAAgB,EAAW,EAAE,UAAO,YAAS,YAA0E;CACrH,IAAM,EAAE,SAAM,EAAY,GACpB,IAAU,GAAO,SAAS,eAAe,GAAO,SAAS;CAC/D,OACE,kBAAC,OAAD;EAAK,WAAU;EAAiC,MAAK;EAArD,UAAA,CACE,kBAAC,KAAD;GAAG,WAAU;GAAoB,UAAA,IAAU,EAAE,EAAM,SAAS,cAAc,4BAA4B,4BAA4B,IAAI;EAAW,CAAA,GACjJ,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAQ;GAAU,SAAS;GAC1C,UAAA,EAAE,cAAc;EACX,CAAA,CACL;;AAET;AAGA,SAAgB,EAAM,EAAE,SAAM,YAAS,WAAQ,eAAoG;CACjJ,IAAM,EAAE,SAAM,EAAY;CAE1B,OADI,KAAQ,KAAK,CAAC,IAAgB,OAEhC,kBAAC,OAAD;EAAK,WAAU;EAAyC,cAAY,EAAE,aAAa;EAAG,aAAU;EAAhG,UAAA;GACE,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,UAAU,KAAY,KAAQ;IAAG,eAAe,EAAO,IAAO,CAAC;IAChG,UAAA,EAAE,gBAAgB;GACb,CAAA;GACR,kBAAC,QAAD;IAAM,WAAU;IAAwB,aAAU;IAC/C,UAAA,EAAE,cAAc,EAAE,QAAK,CAAC;GACrB,CAAA;GACN,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,UAAU,KAAY,CAAC;IAAS,eAAe,EAAO,IAAO,CAAC;IAC/F,UAAA,EAAE,YAAY;GACT,CAAA;EACL;;AAET;AAGA,SAAgB,EAAO,GAA4B;CACjD,IAAM,IAAI,OAAO,EAAE,IAAI,MAAM,CAAC;CAC9B,OAAO,OAAO,UAAU,CAAC,KAAK,IAAI,IAAI,IAAI;AAC5C;AAWA,SAAgB,IAAsF;CACpG,IAAM,CAAC,GAAQ,KAAa,EAAuE,IAAI;CAqBvG,OAAO;EAAE,SApBO,GAAa,MAAsB,IAAI,SAAkB,MAAY,EAAU;GAAE,GAAG;GAAG;EAAQ,CAAC,CAAC,GAAG,CAAC,CAoB5G;EAAS,QAlBhB,kBAAC,GAAD;GACE,MAAM,CAAC,CAAC;GACR,eAAe,MAAS;IACtB,AAAI,CAAC,KAAQ,MACX,EAAO,QAAQ,EAAK,GACpB,EAAU,IAAI;GAElB;GACA,OAAO,GAAQ;GACf,aAAa,GAAQ;GACrB,SAAS,GAAQ;GACjB,OAAO,GAAQ;GACf,iBAAiB;IAEf,AADA,GAAQ,QAAQ,EAAI,GACpB,EAAU,IAAI;GAChB;EACD,CAEe;CAAO;AAC3B"}
|
|
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:
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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 {};
|