@glw907/cairn-cms 0.60.1 → 0.62.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/components/AdminLayout.svelte +22 -0
  3. package/dist/components/CairnAdmin.svelte +3 -0
  4. package/dist/components/CairnTidySettings.svelte +2 -2
  5. package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
  6. package/dist/components/EditPage.svelte +116 -39
  7. package/dist/components/HelpHome.svelte +824 -0
  8. package/dist/components/HelpHome.svelte.d.ts +22 -0
  9. package/dist/components/MarkdownHelpDialog.svelte +4 -15
  10. package/dist/components/client-ingest.d.ts +16 -8
  11. package/dist/components/client-ingest.js +12 -6
  12. package/dist/components/editor-media.js +16 -8
  13. package/dist/components/editor-placeholder.d.ts +4 -2
  14. package/dist/components/editor-tidy.d.ts +24 -12
  15. package/dist/components/editor-tidy.js +8 -4
  16. package/dist/components/index.d.ts +1 -0
  17. package/dist/components/index.js +1 -0
  18. package/dist/components/link-completion.d.ts +12 -6
  19. package/dist/components/link-completion.js +12 -6
  20. package/dist/components/markdown-directives.d.ts +9 -6
  21. package/dist/components/markdown-directives.js +9 -6
  22. package/dist/components/markdown-format.d.ts +7 -2
  23. package/dist/components/markdown-format.js +59 -28
  24. package/dist/components/markdown-reference.d.ts +8 -0
  25. package/dist/components/markdown-reference.js +22 -0
  26. package/dist/components/media-upload-outcome.d.ts +12 -6
  27. package/dist/components/objective-errors.d.ts +8 -4
  28. package/dist/components/objective-errors.js +8 -4
  29. package/dist/components/preview-doc.d.ts +4 -2
  30. package/dist/components/preview-doc.js +4 -2
  31. package/dist/components/spellcheck.d.ts +55 -29
  32. package/dist/components/spellcheck.js +39 -21
  33. package/dist/components/tidy-categorize.d.ts +20 -10
  34. package/dist/components/tidy-categorize.js +16 -8
  35. package/dist/components/tidy-validate.d.ts +12 -6
  36. package/dist/components/tidy-validate.js +20 -10
  37. package/dist/components/topbar-context.d.ts +4 -2
  38. package/dist/content/advisories.d.ts +56 -0
  39. package/dist/content/advisories.js +87 -0
  40. package/dist/content/compose.d.ts +4 -2
  41. package/dist/content/compose.js +1 -0
  42. package/dist/content/excerpt.js +4 -2
  43. package/dist/content/getting-started.d.ts +18 -0
  44. package/dist/content/getting-started.js +12 -0
  45. package/dist/content/links.d.ts +16 -8
  46. package/dist/content/links.js +12 -6
  47. package/dist/content/manifest.d.ts +36 -18
  48. package/dist/content/manifest.js +32 -16
  49. package/dist/content/media-refs.d.ts +4 -2
  50. package/dist/content/media-refs.js +4 -2
  51. package/dist/content/media-rewrite.d.ts +8 -4
  52. package/dist/content/media-rewrite.js +76 -38
  53. package/dist/content/schema.d.ts +20 -10
  54. package/dist/content/site-dictionary.d.ts +4 -2
  55. package/dist/content/site-dictionary.js +8 -4
  56. package/dist/content/types.d.ts +97 -42
  57. package/dist/delivery/content-index.d.ts +16 -8
  58. package/dist/delivery/feeds.js +4 -2
  59. package/dist/delivery/json-ld.d.ts +3 -0
  60. package/dist/delivery/json-ld.js +3 -0
  61. package/dist/delivery/manifest.d.ts +4 -2
  62. package/dist/delivery/manifest.js +4 -2
  63. package/dist/delivery/public-routes.d.ts +12 -6
  64. package/dist/delivery/public-routes.js +4 -2
  65. package/dist/delivery/seo-fields.d.ts +12 -6
  66. package/dist/delivery/seo-fields.js +8 -4
  67. package/dist/delivery/site-indexes.d.ts +4 -2
  68. package/dist/delivery/site-resolver.d.ts +4 -2
  69. package/dist/delivery/site-resolver.js +4 -2
  70. package/dist/doctor/cloudflare-api.d.ts +6 -0
  71. package/dist/doctor/cloudflare-api.js +6 -0
  72. package/dist/doctor/index.d.ts +12 -6
  73. package/dist/doctor/report.d.ts +3 -0
  74. package/dist/doctor/report.js +3 -0
  75. package/dist/doctor/run.d.ts +3 -0
  76. package/dist/doctor/run.js +3 -0
  77. package/dist/doctor/types.d.ts +10 -2
  78. package/dist/doctor/types.js +6 -0
  79. package/dist/doctor/wrangler-config.d.ts +7 -2
  80. package/dist/doctor/wrangler-config.js +3 -0
  81. package/dist/email.d.ts +4 -2
  82. package/dist/env.d.ts +0 -3
  83. package/dist/env.js +0 -3
  84. package/dist/github/branches.d.ts +4 -2
  85. package/dist/github/branches.js +4 -2
  86. package/dist/github/signing.d.ts +1 -1
  87. package/dist/github/signing.js +2 -2
  88. package/dist/log/events.d.ts +1 -1
  89. package/dist/media/bulk-delete-plan.d.ts +8 -4
  90. package/dist/media/config.d.ts +12 -6
  91. package/dist/media/config.js +16 -8
  92. package/dist/media/delivery-bucket.d.ts +4 -2
  93. package/dist/media/library-entry.d.ts +4 -2
  94. package/dist/media/library-entry.js +4 -2
  95. package/dist/media/manifest.d.ts +29 -15
  96. package/dist/media/manifest.js +29 -16
  97. package/dist/media/naming.d.ts +12 -6
  98. package/dist/media/naming.js +24 -12
  99. package/dist/media/orphan-scan.d.ts +4 -2
  100. package/dist/media/reconcile.d.ts +21 -11
  101. package/dist/media/reconcile.js +12 -6
  102. package/dist/media/reference.d.ts +8 -4
  103. package/dist/media/reference.js +12 -6
  104. package/dist/media/rewrite-plan.d.ts +12 -6
  105. package/dist/media/sniff.d.ts +4 -2
  106. package/dist/media/sniff.js +28 -14
  107. package/dist/media/store.d.ts +16 -8
  108. package/dist/media/store.js +4 -2
  109. package/dist/media/transform-url.d.ts +12 -6
  110. package/dist/media/transform-url.js +8 -4
  111. package/dist/media/usage.d.ts +8 -4
  112. package/dist/nav/site-config.d.ts +16 -8
  113. package/dist/render/component-grammar.d.ts +23 -10
  114. package/dist/render/component-grammar.js +19 -8
  115. package/dist/render/component-insert.d.ts +8 -4
  116. package/dist/render/component-insert.js +4 -2
  117. package/dist/render/component-reference.d.ts +4 -2
  118. package/dist/render/component-reference.js +4 -2
  119. package/dist/render/component-validate.d.ts +3 -0
  120. package/dist/render/component-validate.js +3 -0
  121. package/dist/render/glyph.d.ts +4 -2
  122. package/dist/render/glyph.js +4 -2
  123. package/dist/render/pipeline.d.ts +20 -10
  124. package/dist/render/pipeline.js +4 -2
  125. package/dist/render/registry.d.ts +40 -20
  126. package/dist/render/registry.js +16 -8
  127. package/dist/render/rehype-dispatch.d.ts +22 -8
  128. package/dist/render/rehype-dispatch.js +22 -8
  129. package/dist/render/remark-directives.d.ts +3 -0
  130. package/dist/render/remark-directives.js +3 -0
  131. package/dist/render/remark-figure.d.ts +4 -2
  132. package/dist/render/remark-figure.js +4 -2
  133. package/dist/render/resolve-links.d.ts +4 -2
  134. package/dist/render/resolve-links.js +4 -2
  135. package/dist/render/resolve-media.d.ts +16 -8
  136. package/dist/render/resolve-media.js +12 -6
  137. package/dist/sveltekit/admin-dispatch.d.ts +2 -0
  138. package/dist/sveltekit/admin-dispatch.js +9 -3
  139. package/dist/sveltekit/auth-routes.d.ts +3 -0
  140. package/dist/sveltekit/auth-routes.js +3 -0
  141. package/dist/sveltekit/cairn-admin.d.ts +16 -5
  142. package/dist/sveltekit/cairn-admin.js +26 -10
  143. package/dist/sveltekit/content-routes.d.ts +191 -86
  144. package/dist/sveltekit/content-routes.js +297 -107
  145. package/dist/sveltekit/editors-routes.d.ts +3 -0
  146. package/dist/sveltekit/editors-routes.js +3 -0
  147. package/dist/sveltekit/guard.d.ts +4 -2
  148. package/dist/sveltekit/guard.js +4 -2
  149. package/dist/sveltekit/https-required-page.d.ts +1 -1
  150. package/dist/sveltekit/https-required-page.js +1 -1
  151. package/dist/sveltekit/index.d.ts +1 -1
  152. package/dist/sveltekit/media-route.d.ts +1 -2
  153. package/dist/sveltekit/media-route.js +13 -8
  154. package/dist/sveltekit/nav-routes.d.ts +7 -2
  155. package/dist/sveltekit/nav-routes.js +3 -0
  156. package/dist/sveltekit/types.d.ts +4 -2
  157. package/dist/vite/index.d.ts +32 -16
  158. package/dist/vite/index.js +52 -26
  159. package/dist/vite/resolve-root.d.ts +8 -4
  160. package/dist/vite/resolve-root.js +4 -2
  161. package/package.json +7 -1
  162. package/src/lib/components/AdminLayout.svelte +22 -0
  163. package/src/lib/components/CairnAdmin.svelte +3 -0
  164. package/src/lib/components/CairnTidySettings.svelte +2 -2
  165. package/src/lib/components/ComponentForm.svelte +0 -1
  166. package/src/lib/components/EditPage.svelte +133 -41
  167. package/src/lib/components/HelpHome.svelte +850 -0
  168. package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
  169. package/src/lib/components/client-ingest.ts +20 -10
  170. package/src/lib/components/editor-media.ts +20 -10
  171. package/src/lib/components/editor-placeholder.ts +12 -6
  172. package/src/lib/components/editor-tidy.ts +28 -14
  173. package/src/lib/components/index.ts +1 -0
  174. package/src/lib/components/link-completion.ts +12 -6
  175. package/src/lib/components/markdown-directives.ts +13 -8
  176. package/src/lib/components/markdown-format.ts +63 -30
  177. package/src/lib/components/markdown-reference.ts +30 -0
  178. package/src/lib/components/media-upload-outcome.ts +12 -6
  179. package/src/lib/components/objective-errors.ts +16 -8
  180. package/src/lib/components/preview-doc.ts +4 -2
  181. package/src/lib/components/spellcheck.ts +79 -41
  182. package/src/lib/components/tidy-categorize.ts +28 -14
  183. package/src/lib/components/tidy-validate.ts +28 -14
  184. package/src/lib/components/topbar-context.ts +4 -2
  185. package/src/lib/content/advisories.ts +150 -0
  186. package/src/lib/content/compose.ts +5 -2
  187. package/src/lib/content/excerpt.ts +4 -2
  188. package/src/lib/content/getting-started.ts +31 -0
  189. package/src/lib/content/links.ts +16 -8
  190. package/src/lib/content/manifest.ts +36 -18
  191. package/src/lib/content/media-refs.ts +4 -2
  192. package/src/lib/content/media-rewrite.ts +100 -50
  193. package/src/lib/content/schema.ts +20 -10
  194. package/src/lib/content/site-dictionary.ts +8 -4
  195. package/src/lib/content/types.ts +97 -42
  196. package/src/lib/delivery/content-index.ts +16 -8
  197. package/src/lib/delivery/feeds.ts +4 -2
  198. package/src/lib/delivery/json-ld.ts +3 -0
  199. package/src/lib/delivery/manifest.ts +4 -2
  200. package/src/lib/delivery/public-routes.ts +16 -8
  201. package/src/lib/delivery/seo-fields.ts +12 -6
  202. package/src/lib/delivery/site-indexes.ts +4 -2
  203. package/src/lib/delivery/site-resolver.ts +4 -2
  204. package/src/lib/doctor/cloudflare-api.ts +6 -0
  205. package/src/lib/doctor/index.ts +12 -6
  206. package/src/lib/doctor/report.ts +3 -0
  207. package/src/lib/doctor/run.ts +3 -0
  208. package/src/lib/doctor/types.ts +10 -2
  209. package/src/lib/doctor/wrangler-config.ts +7 -2
  210. package/src/lib/email.ts +4 -2
  211. package/src/lib/env.ts +0 -3
  212. package/src/lib/github/branches.ts +4 -2
  213. package/src/lib/github/signing.ts +2 -2
  214. package/src/lib/log/events.ts +1 -0
  215. package/src/lib/media/bulk-delete-plan.ts +8 -4
  216. package/src/lib/media/config.ts +24 -12
  217. package/src/lib/media/delivery-bucket.ts +4 -2
  218. package/src/lib/media/library-entry.ts +4 -2
  219. package/src/lib/media/manifest.ts +33 -18
  220. package/src/lib/media/naming.ts +24 -12
  221. package/src/lib/media/orphan-scan.ts +4 -2
  222. package/src/lib/media/reconcile.ts +21 -11
  223. package/src/lib/media/reference.ts +12 -6
  224. package/src/lib/media/rewrite-plan.ts +12 -6
  225. package/src/lib/media/sniff.ts +28 -14
  226. package/src/lib/media/store.ts +16 -8
  227. package/src/lib/media/transform-url.ts +12 -6
  228. package/src/lib/media/usage.ts +8 -4
  229. package/src/lib/nav/site-config.ts +16 -8
  230. package/src/lib/render/component-grammar.ts +23 -10
  231. package/src/lib/render/component-insert.ts +8 -4
  232. package/src/lib/render/component-reference.ts +4 -2
  233. package/src/lib/render/component-validate.ts +3 -0
  234. package/src/lib/render/glyph.ts +4 -2
  235. package/src/lib/render/pipeline.ts +20 -10
  236. package/src/lib/render/registry.ts +44 -22
  237. package/src/lib/render/rehype-dispatch.ts +22 -8
  238. package/src/lib/render/remark-directives.ts +3 -0
  239. package/src/lib/render/remark-figure.ts +4 -2
  240. package/src/lib/render/resolve-links.ts +4 -2
  241. package/src/lib/render/resolve-media.ts +16 -8
  242. package/src/lib/sveltekit/admin-dispatch.ts +10 -4
  243. package/src/lib/sveltekit/auth-routes.ts +3 -0
  244. package/src/lib/sveltekit/cairn-admin.ts +37 -15
  245. package/src/lib/sveltekit/content-routes.ts +494 -197
  246. package/src/lib/sveltekit/editors-routes.ts +3 -0
  247. package/src/lib/sveltekit/guard.ts +4 -2
  248. package/src/lib/sveltekit/https-required-page.ts +1 -1
  249. package/src/lib/sveltekit/index.ts +3 -0
  250. package/src/lib/sveltekit/media-route.ts +13 -8
  251. package/src/lib/sveltekit/nav-routes.ts +7 -2
  252. package/src/lib/sveltekit/types.ts +4 -2
  253. package/src/lib/vite/index.ts +60 -30
  254. package/src/lib/vite/resolve-root.ts +8 -4
@@ -15,18 +15,22 @@ import { log } from '../log/index.js';
15
15
  /** The VFile data key the renderer sets the per-call media resolver under. */
16
16
  export const MEDIA_RESOLVE = 'mediaResolve';
17
17
 
18
- /** Resolve a media reference to its delivery URL. `undefined` is a preview miss (the plugin marks
18
+ /**
19
+ * Resolve a media reference to its delivery URL. `undefined` is a preview miss (the plugin marks
19
20
  * the image broken); a resolver that throws is the build backstop (the error propagates out of
20
- * render and fails the build), exactly like LinkResolve. */
21
+ * render and fails the build), exactly like LinkResolve.
22
+ */
21
23
  export type MediaResolve = (ref: MediaRef) => string | undefined;
22
24
 
23
- /** Build the per-call media resolver, closing over the manifest and the resolved config. The
25
+ /**
26
+ * Build the per-call media resolver, closing over the manifest and the resolved config. The
24
27
  * returned resolver looks a ref's content hash up in the manifest and builds the canonical delivery
25
28
  * path from the manifest entry's slug and ext, not the token's, so a rename never breaks the
26
29
  * reference. With a preset and zone transformations on it returns the variant URL; without a preset,
27
30
  * or when transformations are off, it returns the bare full-size path so a fresh zone with Image
28
31
  * Transformations disabled serves correct thumbnails rather than dead /cdn-cgi/image URLs. It returns
29
- * undefined when media is off or no entry carries the hash (the preview-miss backstop). */
32
+ * undefined when media is off or no entry carries the hash (the preview-miss backstop).
33
+ */
30
34
  export function makeMediaResolver(
31
35
  manifest: MediaManifest,
32
36
  resolved: ResolvedAssetConfig,
@@ -49,11 +53,13 @@ export function makeMediaResolver(
49
53
  };
50
54
  }
51
55
 
52
- /** A resolver backed by the lean `mediaTargets` projection, for the admin preview. It mirrors
56
+ /**
57
+ * A resolver backed by the lean `mediaTargets` projection, for the admin preview. It mirrors
53
58
  * manifestLinkResolver: a hash present in the projection builds the slug delivery path
54
59
  * (`/media/<slug>.<hash>.<ext>`); a miss returns undefined, so the render step marks the image
55
60
  * broken rather than throwing. Pure over the projection, with no manifest and no config, so the
56
- * edit page reaches it with the data it actually has. */
61
+ * edit page reaches it with the data it actually has.
62
+ */
57
63
  export function manifestMediaResolver(
58
64
  targets: Record<string, { slug: string; ext: string; contentType: string }>,
59
65
  ): MediaResolve {
@@ -69,9 +75,11 @@ interface ImageNode {
69
75
  data?: { hProperties?: Record<string, unknown> };
70
76
  }
71
77
 
72
- /** Resolve media: image nodes against the VFile's resolver. A non-media src and a malformed token
78
+ /**
79
+ * Resolve media: image nodes against the VFile's resolver. A non-media src and a malformed token
73
80
  * pass through. A missing target is marked with the cairn-broken-media class (the resolver returns
74
- * undefined) or, when the resolver throws, the error propagates and fails the build. */
81
+ * undefined) or, when the resolver throws, the error propagates and fails the build.
82
+ */
75
83
  export function remarkResolveMedia() {
76
84
  return (tree: unknown, file: VFile): void => {
77
85
  const resolve = file.data[MEDIA_RESOLVE] as MediaResolve | undefined;
@@ -17,15 +17,18 @@ export type AdminView =
17
17
  | { view: 'editors' }
18
18
  | { view: 'nav' }
19
19
  | { view: 'media' }
20
- | { view: 'settings' };
20
+ | { view: 'settings' }
21
+ | { view: 'help' };
21
22
 
22
23
  /**
23
24
  * Fixed first segments that never resolve as concepts. The engine only allows posts and pages
24
25
  * today, so no collision is possible, but the parser does not depend on that: a reserved
25
- * segment wins before concept lookup. `settings`, `nav`, and `media` are decided as views below,
26
- * so they are not in this no-view set.
26
+ * segment wins before concept lookup. `nav` and `media` are decided as views below, so they are
27
+ * not in this no-view set. `settings` and `help` are decided as views below AND kept here, so a
28
+ * deeper path (or a future concept claiming the segment) can never reach the two-segment edit
29
+ * branch through them.
27
30
  */
28
- const RESERVED_SEGMENTS = new Set(['login', 'auth', 'editors', 'nav', 'settings']);
31
+ const RESERVED_SEGMENTS = new Set(['login', 'auth', 'editors', 'nav', 'settings', 'help']);
29
32
 
30
33
  /**
31
34
  * Parse a raw `URL.pathname` (the caller passes `event.url.pathname`, never a SvelteKit rest
@@ -66,6 +69,9 @@ export function parseAdminPath(
66
69
  // settings is its own view, a peer of editors and nav. /admin/settings/<anything> 404s naturally
67
70
  // (the two-segment branch never matches settings), which is the correct shape.
68
71
  if (head === 'settings') return { view: 'settings' };
72
+ // help is its own view (the Help home, editor-help Pass 2), decided here like settings. It is
73
+ // also in the reserved set so /admin/help/<anything> 404s and no concept can claim the segment.
74
+ if (head === 'help') return { view: 'help' };
69
75
  if (RESERVED_SEGMENTS.has(head)) return null;
70
76
  const concept = findConcept(concepts, head);
71
77
  return concept ? { view: 'list', concept } : null;
@@ -45,6 +45,9 @@ function scrubSendError(err: unknown): string {
45
45
  .slice(0, 300);
46
46
  }
47
47
 
48
+ /**
49
+ *
50
+ */
48
51
  export function createAuthRoutes(config: AuthRoutesConfig) {
49
52
  const send = config.send ?? cloudflareSend;
50
53
 
@@ -16,6 +16,7 @@ import {
16
16
  type EditData,
17
17
  type MediaLibraryData,
18
18
  type SettingsData,
19
+ type HelpData,
19
20
  } from './content-routes.js';
20
21
  import { createEditorRoutes } from './editors-routes.js';
21
22
  import { createNavRoutes, type NavLoadData } from './nav-routes.js';
@@ -35,15 +36,19 @@ export interface AdminEvent extends EventBase<GithubKeyEnv & AuthEnv> {
35
36
  setHeaders(headers: Record<string, string>): void;
36
37
  }
37
38
 
38
- /** Injectable dependencies. Branding defaults from the runtime's siteName and sender, so a
39
+ /**
40
+ * Injectable dependencies. Branding defaults from the runtime's siteName and sender, so a
39
41
  * site overrides it only to change the magic-link email identity; `send` and `mintToken`
40
- * are the same seams the underlying factories take. */
42
+ * are the same seams the underlying factories take.
43
+ */
41
44
  export interface CairnAdminDeps {
42
45
  branding?: AuthBranding;
43
46
  send?: SendMagicLink;
44
47
  mintToken?: ContentRoutesDeps['mintToken'];
45
- /** Build the Anthropic client for the tidy action. Forwarded to the content routes; a site that
46
- * enables tidy injects a stub here to avoid a real network call. Defaults to the real SDK client. */
48
+ /**
49
+ * Build the Anthropic client for the tidy action. Forwarded to the content routes; a site that
50
+ * enables tidy injects a stub here to avoid a real network call. Defaults to the real SDK client.
51
+ */
47
52
  anthropic?: ContentRoutesDeps['anthropic'];
48
53
  /** The tidy action's own request deadline in milliseconds. Forwarded to the content routes. */
49
54
  tidyTimeoutMs?: ContentRoutesDeps['tidyTimeoutMs'];
@@ -62,8 +67,12 @@ export type AdminData =
62
67
  | { view: 'editors'; layout: LayoutData; page: { editors: Editor[]; self: string } }
63
68
  | { view: 'nav'; layout: LayoutData; page: NavLoadData }
64
69
  | { view: 'media'; layout: LayoutData; page: MediaLibraryData }
65
- | { view: 'settings'; layout: LayoutData; page: SettingsData };
70
+ | { view: 'settings'; layout: LayoutData; page: SettingsData }
71
+ | { view: 'help'; layout: LayoutData; page: HelpData };
66
72
 
73
+ /**
74
+ *
75
+ */
67
76
  export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {}) {
68
77
  // The runtime already composes the site name and the sender identity, so the magic-link
69
78
  // branding needs no second copy of either unless a site overrides it.
@@ -82,11 +91,13 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
82
91
  // The nav surface exists only when the site configures a menu; without one its view is a 404.
83
92
  const nav = runtime.navMenu ? createNavRoutes(runtime, { mintToken: deps.mintToken }) : null;
84
93
 
85
- /** Build the event a wrapped content load reads. The catch-all route carries only a rest
94
+ /**
95
+ * Build the event a wrapped content load reads. The catch-all route carries only a rest
86
96
  * param, so `concept` and `id` are synthesized from the parsed view. The override names
87
97
  * each field explicitly rather than spreading: a real RequestEvent's fields can sit behind
88
98
  * getters a bare spread copies poorly, and the structural ContentEvent contract needs only
89
- * these. */
99
+ * these.
100
+ */
90
101
  function contentEvent(event: AdminEvent, params: Record<string, string>): ContentEvent {
91
102
  return {
92
103
  url: event.url,
@@ -98,9 +109,11 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
98
109
  };
99
110
  }
100
111
 
101
- /** Serve the admin view the pathname names, or a 404 for any shape the parser refuses.
112
+ /**
113
+ * Serve the admin view the pathname names, or a 404 for any shape the parser refuses.
102
114
  * The authed views run the layout load and the view load concurrently; both mint a GitHub
103
- * token, and the installation-token cache coalesces the mints into one signing. */
115
+ * token, and the installation-token cache coalesces the mints into one signing.
116
+ */
104
117
  async function load(event: AdminEvent): Promise<AdminData> {
105
118
  const view = parseAdminPath(event.url.pathname, runtime.concepts);
106
119
  if (!view) throw error(404, 'Not found');
@@ -145,12 +158,19 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
145
158
  const [layout, page] = await Promise.all([content.layoutLoad(delegated), content.settingsLoad(delegated)]);
146
159
  return { view: 'settings', layout, page };
147
160
  }
161
+ case 'help': {
162
+ const delegated = contentEvent(event, {});
163
+ const [layout, page] = await Promise.all([content.layoutLoad(delegated), content.helpLoad(delegated)]);
164
+ return { view: 'help', layout, page };
165
+ }
148
166
  }
149
167
  }
150
168
 
151
- /** Wrap a delegate in the parse-and-check every action shares: parse the pathname exactly
169
+ /**
170
+ * Wrap a delegate in the parse-and-check every action shares: parse the pathname exactly
152
171
  * as load does, 404 on a null parse or a view outside the allowed set, then hand the
153
- * narrowed view to the delegate. */
172
+ * narrowed view to the delegate.
173
+ */
154
174
  function viewAction<V extends AdminView['view'], R>(
155
175
  allowed: readonly V[],
156
176
  delegate: (event: AdminEvent, view: Extract<AdminView, { view: V }>) => Promise<R>,
@@ -164,14 +184,16 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
164
184
  }
165
185
 
166
186
  // The topbar posts publishAll from every authed admin page; login and confirm may not.
167
- const authedViews = ['list', 'edit', 'editors', 'nav', 'media', 'settings'] as const;
187
+ const authedViews = ['list', 'edit', 'editors', 'nav', 'media', 'settings', 'help'] as const;
168
188
  // An editor signs out from wherever they are, so logout accepts any parsed view.
169
- const anyView = ['index', 'login', 'confirm', 'list', 'edit', 'editors', 'nav', 'media', 'settings'] as const;
189
+ const anyView = ['index', 'login', 'confirm', 'list', 'edit', 'editors', 'nav', 'media', 'settings', 'help'] as const;
170
190
 
171
- /** The full admin action vocabulary, one named async function per action, so a site's
191
+ /**
192
+ * The full admin action vocabulary, one named async function per action, so a site's
172
193
  * catch-all route exports `admin.actions` directly. Each wrapper stays thin: parse,
173
194
  * validate the view, synthesize the params the wrapped action reads, delegate. The
174
- * editor actions gate themselves with requireOwner, so no second gate is added here. */
195
+ * editor actions gate themselves with requireOwner, so no second gate is added here.
196
+ */
175
197
  const actions = {
176
198
  request: viewAction(['login'], (event) => auth.requestAction(event)),
177
199
  confirm: viewAction(['confirm'], (event) => auth.confirmAction(event)),