@glw907/cairn-cms 0.60.0 → 0.62.1

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 (281) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/components/AdminLayout.svelte +152 -229
  3. package/dist/components/CairnAdmin.svelte +13 -42
  4. package/dist/components/CairnLogo.svelte +1 -6
  5. package/dist/components/CairnMediaLibrary.svelte +821 -1210
  6. package/dist/components/CairnTidySettings.svelte +194 -261
  7. package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
  8. package/dist/components/ComponentForm.svelte +110 -185
  9. package/dist/components/ComponentInsertDialog.svelte +163 -283
  10. package/dist/components/ConceptList.svelte +111 -191
  11. package/dist/components/ConfirmPage.svelte +5 -12
  12. package/dist/components/CsrfField.svelte +5 -11
  13. package/dist/components/DeleteDialog.svelte +15 -42
  14. package/dist/components/EditPage.svelte +781 -1205
  15. package/dist/components/EditorToolbar.svelte +108 -170
  16. package/dist/components/HelpHome.svelte +824 -0
  17. package/dist/components/HelpHome.svelte.d.ts +22 -0
  18. package/dist/components/IconPicker.svelte +23 -53
  19. package/dist/components/LinkPicker.svelte +34 -58
  20. package/dist/components/LoginPage.svelte +14 -27
  21. package/dist/components/ManageEditors.svelte +3 -15
  22. package/dist/components/MarkdownEditor.svelte +689 -957
  23. package/dist/components/MarkdownHelpDialog.svelte +12 -27
  24. package/dist/components/MediaCaptureCard.svelte +18 -57
  25. package/dist/components/MediaFigureControl.svelte +32 -71
  26. package/dist/components/MediaHeroField.svelte +210 -329
  27. package/dist/components/MediaInsertPopover.svelte +156 -283
  28. package/dist/components/MediaPicker.svelte +67 -131
  29. package/dist/components/NavTree.svelte +46 -78
  30. package/dist/components/RenameDialog.svelte +16 -43
  31. package/dist/components/ShortcutsDialog.svelte +9 -13
  32. package/dist/components/ShortcutsGrid.svelte +1 -2
  33. package/dist/components/TidyReview.svelte +140 -248
  34. package/dist/components/WebLinkDialog.svelte +19 -40
  35. package/dist/components/cairn-admin.css +4 -0
  36. package/dist/components/client-ingest.d.ts +16 -8
  37. package/dist/components/client-ingest.js +12 -6
  38. package/dist/components/editor-media.js +16 -8
  39. package/dist/components/editor-placeholder.d.ts +4 -2
  40. package/dist/components/editor-tidy.d.ts +24 -12
  41. package/dist/components/editor-tidy.js +8 -4
  42. package/dist/components/index.d.ts +1 -0
  43. package/dist/components/index.js +1 -0
  44. package/dist/components/link-completion.d.ts +12 -6
  45. package/dist/components/link-completion.js +12 -6
  46. package/dist/components/markdown-directives.d.ts +9 -6
  47. package/dist/components/markdown-directives.js +9 -6
  48. package/dist/components/markdown-format.d.ts +7 -2
  49. package/dist/components/markdown-format.js +59 -28
  50. package/dist/components/markdown-reference.d.ts +8 -0
  51. package/dist/components/markdown-reference.js +22 -0
  52. package/dist/components/media-upload-outcome.d.ts +12 -6
  53. package/dist/components/objective-errors.d.ts +8 -4
  54. package/dist/components/objective-errors.js +8 -4
  55. package/dist/components/preview-doc.d.ts +4 -2
  56. package/dist/components/preview-doc.js +4 -2
  57. package/dist/components/spellcheck.d.ts +57 -29
  58. package/dist/components/spellcheck.js +50 -20
  59. package/dist/components/tidy-categorize.d.ts +20 -10
  60. package/dist/components/tidy-categorize.js +16 -8
  61. package/dist/components/tidy-validate.d.ts +12 -6
  62. package/dist/components/tidy-validate.js +20 -10
  63. package/dist/components/topbar-context.d.ts +4 -2
  64. package/dist/content/advisories.d.ts +51 -0
  65. package/dist/content/advisories.js +79 -0
  66. package/dist/content/compose.d.ts +4 -2
  67. package/dist/content/compose.js +1 -0
  68. package/dist/content/excerpt.js +4 -2
  69. package/dist/content/getting-started.d.ts +18 -0
  70. package/dist/content/getting-started.js +12 -0
  71. package/dist/content/links.d.ts +16 -8
  72. package/dist/content/links.js +12 -6
  73. package/dist/content/manifest.d.ts +36 -18
  74. package/dist/content/manifest.js +32 -16
  75. package/dist/content/media-refs.d.ts +4 -2
  76. package/dist/content/media-refs.js +4 -2
  77. package/dist/content/media-rewrite.d.ts +8 -4
  78. package/dist/content/media-rewrite.js +76 -38
  79. package/dist/content/schema.d.ts +20 -10
  80. package/dist/content/site-dictionary.d.ts +4 -2
  81. package/dist/content/site-dictionary.js +8 -4
  82. package/dist/content/types.d.ts +97 -42
  83. package/dist/delivery/CairnHead.svelte +8 -11
  84. package/dist/delivery/content-index.d.ts +16 -8
  85. package/dist/delivery/feeds.js +4 -2
  86. package/dist/delivery/json-ld.d.ts +3 -0
  87. package/dist/delivery/json-ld.js +3 -0
  88. package/dist/delivery/manifest.d.ts +4 -2
  89. package/dist/delivery/manifest.js +4 -2
  90. package/dist/delivery/public-routes.d.ts +12 -6
  91. package/dist/delivery/public-routes.js +4 -2
  92. package/dist/delivery/seo-fields.d.ts +12 -6
  93. package/dist/delivery/seo-fields.js +8 -4
  94. package/dist/delivery/site-indexes.d.ts +4 -2
  95. package/dist/delivery/site-resolver.d.ts +4 -2
  96. package/dist/delivery/site-resolver.js +4 -2
  97. package/dist/doctor/cloudflare-api.d.ts +6 -0
  98. package/dist/doctor/cloudflare-api.js +6 -0
  99. package/dist/doctor/index.d.ts +12 -6
  100. package/dist/doctor/report.d.ts +3 -0
  101. package/dist/doctor/report.js +3 -0
  102. package/dist/doctor/run.d.ts +3 -0
  103. package/dist/doctor/run.js +3 -0
  104. package/dist/doctor/types.d.ts +10 -2
  105. package/dist/doctor/types.js +6 -0
  106. package/dist/doctor/wrangler-config.d.ts +7 -2
  107. package/dist/doctor/wrangler-config.js +3 -0
  108. package/dist/email.d.ts +4 -2
  109. package/dist/env.d.ts +0 -3
  110. package/dist/env.js +0 -3
  111. package/dist/github/branches.d.ts +4 -2
  112. package/dist/github/branches.js +4 -2
  113. package/dist/github/signing.d.ts +1 -1
  114. package/dist/github/signing.js +2 -2
  115. package/dist/log/events.d.ts +1 -1
  116. package/dist/media/bulk-delete-plan.d.ts +8 -4
  117. package/dist/media/config.d.ts +12 -6
  118. package/dist/media/config.js +16 -8
  119. package/dist/media/delivery-bucket.d.ts +4 -2
  120. package/dist/media/library-entry.d.ts +4 -2
  121. package/dist/media/library-entry.js +4 -2
  122. package/dist/media/manifest.d.ts +29 -15
  123. package/dist/media/manifest.js +29 -16
  124. package/dist/media/naming.d.ts +12 -6
  125. package/dist/media/naming.js +24 -12
  126. package/dist/media/orphan-scan.d.ts +4 -2
  127. package/dist/media/reconcile.d.ts +21 -11
  128. package/dist/media/reconcile.js +12 -6
  129. package/dist/media/reference.d.ts +8 -4
  130. package/dist/media/reference.js +12 -6
  131. package/dist/media/rewrite-plan.d.ts +12 -6
  132. package/dist/media/sniff.d.ts +4 -2
  133. package/dist/media/sniff.js +28 -14
  134. package/dist/media/store.d.ts +16 -8
  135. package/dist/media/store.js +4 -2
  136. package/dist/media/transform-url.d.ts +12 -6
  137. package/dist/media/transform-url.js +8 -4
  138. package/dist/media/usage.d.ts +8 -4
  139. package/dist/nav/site-config.d.ts +16 -8
  140. package/dist/render/component-grammar.d.ts +23 -10
  141. package/dist/render/component-grammar.js +19 -8
  142. package/dist/render/component-insert.d.ts +8 -4
  143. package/dist/render/component-insert.js +4 -2
  144. package/dist/render/component-reference.d.ts +4 -2
  145. package/dist/render/component-reference.js +4 -2
  146. package/dist/render/component-validate.d.ts +3 -0
  147. package/dist/render/component-validate.js +3 -0
  148. package/dist/render/glyph.d.ts +4 -2
  149. package/dist/render/glyph.js +4 -2
  150. package/dist/render/pipeline.d.ts +20 -10
  151. package/dist/render/pipeline.js +4 -2
  152. package/dist/render/registry.d.ts +40 -20
  153. package/dist/render/registry.js +16 -8
  154. package/dist/render/rehype-dispatch.d.ts +22 -8
  155. package/dist/render/rehype-dispatch.js +22 -8
  156. package/dist/render/remark-directives.d.ts +3 -0
  157. package/dist/render/remark-directives.js +3 -0
  158. package/dist/render/remark-figure.d.ts +4 -2
  159. package/dist/render/remark-figure.js +4 -2
  160. package/dist/render/resolve-links.d.ts +4 -2
  161. package/dist/render/resolve-links.js +4 -2
  162. package/dist/render/resolve-media.d.ts +16 -8
  163. package/dist/render/resolve-media.js +12 -6
  164. package/dist/sveltekit/admin-dispatch.d.ts +2 -0
  165. package/dist/sveltekit/admin-dispatch.js +9 -3
  166. package/dist/sveltekit/auth-routes.d.ts +3 -0
  167. package/dist/sveltekit/auth-routes.js +3 -0
  168. package/dist/sveltekit/cairn-admin.d.ts +16 -5
  169. package/dist/sveltekit/cairn-admin.js +26 -10
  170. package/dist/sveltekit/content-routes.d.ts +191 -86
  171. package/dist/sveltekit/content-routes.js +295 -107
  172. package/dist/sveltekit/editors-routes.d.ts +3 -0
  173. package/dist/sveltekit/editors-routes.js +3 -0
  174. package/dist/sveltekit/guard.d.ts +4 -2
  175. package/dist/sveltekit/guard.js +4 -2
  176. package/dist/sveltekit/https-required-page.d.ts +1 -1
  177. package/dist/sveltekit/https-required-page.js +1 -1
  178. package/dist/sveltekit/index.d.ts +1 -1
  179. package/dist/sveltekit/media-route.d.ts +1 -2
  180. package/dist/sveltekit/media-route.js +13 -8
  181. package/dist/sveltekit/nav-routes.d.ts +7 -2
  182. package/dist/sveltekit/nav-routes.js +3 -0
  183. package/dist/sveltekit/types.d.ts +4 -2
  184. package/dist/vite/index.d.ts +32 -16
  185. package/dist/vite/index.js +52 -26
  186. package/dist/vite/resolve-root.d.ts +8 -4
  187. package/dist/vite/resolve-root.js +4 -2
  188. package/package.json +8 -2
  189. package/src/lib/components/AdminLayout.svelte +22 -0
  190. package/src/lib/components/CairnAdmin.svelte +3 -0
  191. package/src/lib/components/CairnTidySettings.svelte +2 -2
  192. package/src/lib/components/ComponentForm.svelte +0 -1
  193. package/src/lib/components/EditPage.svelte +133 -41
  194. package/src/lib/components/HelpHome.svelte +850 -0
  195. package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
  196. package/src/lib/components/client-ingest.ts +20 -10
  197. package/src/lib/components/editor-media.ts +20 -10
  198. package/src/lib/components/editor-placeholder.ts +12 -6
  199. package/src/lib/components/editor-tidy.ts +28 -14
  200. package/src/lib/components/index.ts +1 -0
  201. package/src/lib/components/link-completion.ts +12 -6
  202. package/src/lib/components/markdown-directives.ts +13 -8
  203. package/src/lib/components/markdown-format.ts +63 -30
  204. package/src/lib/components/markdown-reference.ts +30 -0
  205. package/src/lib/components/media-upload-outcome.ts +12 -6
  206. package/src/lib/components/objective-errors.ts +16 -8
  207. package/src/lib/components/preview-doc.ts +4 -2
  208. package/src/lib/components/spellcheck.ts +92 -40
  209. package/src/lib/components/tidy-categorize.ts +28 -14
  210. package/src/lib/components/tidy-validate.ts +28 -14
  211. package/src/lib/components/topbar-context.ts +4 -2
  212. package/src/lib/content/advisories.ts +141 -0
  213. package/src/lib/content/compose.ts +5 -2
  214. package/src/lib/content/excerpt.ts +4 -2
  215. package/src/lib/content/getting-started.ts +31 -0
  216. package/src/lib/content/links.ts +16 -8
  217. package/src/lib/content/manifest.ts +36 -18
  218. package/src/lib/content/media-refs.ts +4 -2
  219. package/src/lib/content/media-rewrite.ts +100 -50
  220. package/src/lib/content/schema.ts +20 -10
  221. package/src/lib/content/site-dictionary.ts +8 -4
  222. package/src/lib/content/types.ts +97 -42
  223. package/src/lib/delivery/content-index.ts +16 -8
  224. package/src/lib/delivery/feeds.ts +4 -2
  225. package/src/lib/delivery/json-ld.ts +3 -0
  226. package/src/lib/delivery/manifest.ts +4 -2
  227. package/src/lib/delivery/public-routes.ts +16 -8
  228. package/src/lib/delivery/seo-fields.ts +12 -6
  229. package/src/lib/delivery/site-indexes.ts +4 -2
  230. package/src/lib/delivery/site-resolver.ts +4 -2
  231. package/src/lib/doctor/cloudflare-api.ts +6 -0
  232. package/src/lib/doctor/index.ts +12 -6
  233. package/src/lib/doctor/report.ts +3 -0
  234. package/src/lib/doctor/run.ts +3 -0
  235. package/src/lib/doctor/types.ts +10 -2
  236. package/src/lib/doctor/wrangler-config.ts +7 -2
  237. package/src/lib/email.ts +4 -2
  238. package/src/lib/env.ts +0 -3
  239. package/src/lib/github/branches.ts +4 -2
  240. package/src/lib/github/signing.ts +2 -2
  241. package/src/lib/log/events.ts +1 -0
  242. package/src/lib/media/bulk-delete-plan.ts +8 -4
  243. package/src/lib/media/config.ts +24 -12
  244. package/src/lib/media/delivery-bucket.ts +4 -2
  245. package/src/lib/media/library-entry.ts +4 -2
  246. package/src/lib/media/manifest.ts +33 -18
  247. package/src/lib/media/naming.ts +24 -12
  248. package/src/lib/media/orphan-scan.ts +4 -2
  249. package/src/lib/media/reconcile.ts +21 -11
  250. package/src/lib/media/reference.ts +12 -6
  251. package/src/lib/media/rewrite-plan.ts +12 -6
  252. package/src/lib/media/sniff.ts +28 -14
  253. package/src/lib/media/store.ts +16 -8
  254. package/src/lib/media/transform-url.ts +12 -6
  255. package/src/lib/media/usage.ts +8 -4
  256. package/src/lib/nav/site-config.ts +16 -8
  257. package/src/lib/render/component-grammar.ts +23 -10
  258. package/src/lib/render/component-insert.ts +8 -4
  259. package/src/lib/render/component-reference.ts +4 -2
  260. package/src/lib/render/component-validate.ts +3 -0
  261. package/src/lib/render/glyph.ts +4 -2
  262. package/src/lib/render/pipeline.ts +20 -10
  263. package/src/lib/render/registry.ts +44 -22
  264. package/src/lib/render/rehype-dispatch.ts +22 -8
  265. package/src/lib/render/remark-directives.ts +3 -0
  266. package/src/lib/render/remark-figure.ts +4 -2
  267. package/src/lib/render/resolve-links.ts +4 -2
  268. package/src/lib/render/resolve-media.ts +16 -8
  269. package/src/lib/sveltekit/admin-dispatch.ts +10 -4
  270. package/src/lib/sveltekit/auth-routes.ts +3 -0
  271. package/src/lib/sveltekit/cairn-admin.ts +37 -15
  272. package/src/lib/sveltekit/content-routes.ts +492 -197
  273. package/src/lib/sveltekit/editors-routes.ts +3 -0
  274. package/src/lib/sveltekit/guard.ts +4 -2
  275. package/src/lib/sveltekit/https-required-page.ts +1 -1
  276. package/src/lib/sveltekit/index.ts +3 -0
  277. package/src/lib/sveltekit/media-route.ts +13 -8
  278. package/src/lib/sveltekit/nav-routes.ts +7 -2
  279. package/src/lib/sveltekit/types.ts +4 -2
  280. package/src/lib/vite/index.ts +60 -30
  281. package/src/lib/vite/resolve-root.ts +8 -4
@@ -12,12 +12,16 @@ import type {
12
12
  R2Range,
13
13
  } from '@cloudflare/workers-types';
14
14
 
15
- /** The narrow R2 surface the media pipeline uses. The engine depends on this, not on R2Bucket, so the
16
- * multipart, list, and conditional-read surface R2 also carries never leaks into the media code. */
15
+ /**
16
+ * The narrow R2 surface the media pipeline uses. The engine depends on this, not on R2Bucket, so the
17
+ * multipart, list, and conditional-read surface R2 also carries never leaks into the media code.
18
+ */
17
19
  export interface MediaStore {
18
- /** Store bytes under a content-addressed key, with the response HTTP metadata (the content type)
20
+ /**
21
+ * Store bytes under a content-addressed key, with the response HTTP metadata (the content type)
19
22
  * and optional custom metadata (the upload stores the full sha256 here, so a short-hash collision
20
- * is detectable on a later dedup probe). */
23
+ * is detectable on a later dedup probe).
24
+ */
21
25
  put(
22
26
  key: string,
23
27
  bytes: ArrayBuffer | Uint8Array,
@@ -26,10 +30,12 @@ export interface MediaStore {
26
30
  ): Promise<void>;
27
31
  /** The object's metadata, or null when no object lives at the key (the dedup probe). */
28
32
  head(key: string): Promise<R2Object | null>;
29
- /** The object body for streaming to a delivery response, or null when the key is absent. The
33
+ /**
34
+ * The object body for streaming to a delivery response, or null when the key is absent. The
30
35
  * delivery route passes `onlyIf` and `range` through for conditional and partial reads: an
31
36
  * `onlyIf` etag match returns a body-less R2Object (the 304 shape), so the return widens to
32
- * `R2Object` alongside `R2ObjectBody`. */
37
+ * `R2Object` alongside `R2ObjectBody`.
38
+ */
33
39
  get(
34
40
  key: string,
35
41
  opts?: { range?: R2Range; onlyIf?: R2Conditional },
@@ -38,9 +44,11 @@ export interface MediaStore {
38
44
  delete(key: string): Promise<void>;
39
45
  }
40
46
 
41
- /** Wrap an R2 bucket binding as a MediaStore. Each method delegates to the binding; put folds the
47
+ /**
48
+ * Wrap an R2 bucket binding as a MediaStore. Each method delegates to the binding; put folds the
42
49
  * HTTP and custom metadata into R2's options shape and drops the returned R2Object the pipeline does
43
- * not read. */
50
+ * not read.
51
+ */
44
52
  export function r2Store(bucket: R2Bucket): MediaStore {
45
53
  return {
46
54
  async put(key, bytes, httpMetadata, customMetadata) {
@@ -5,9 +5,11 @@
5
5
  // a CDN cache keys on it cleanly. The delivery path is appended unaltered, since it already carries
6
6
  // its own leading slash.
7
7
 
8
- /** A single image variant: the resize and format directives Cloudflare Images applies to the
8
+ /**
9
+ * A single image variant: the resize and format directives Cloudflare Images applies to the
9
10
  * original bytes. Every field is optional. width, height, quality, and fit are emitted only when
10
- * set; format and gravity always appear, defaulting to auto. */
11
+ * set; format and gravity always appear, defaulting to auto.
12
+ */
11
13
  export interface VariantSpec {
12
14
  /** Target width in pixels. */
13
15
  width?: number;
@@ -23,10 +25,12 @@ export interface VariantSpec {
23
25
  format?: 'auto' | 'webp' | 'avif' | string;
24
26
  }
25
27
 
26
- /** Build the on-demand Cloudflare Images transform URL for a delivery path. The options are
28
+ /**
29
+ * Build the on-demand Cloudflare Images transform URL for a delivery path. The options are
27
30
  * comma-joined in the stable order width, height, quality, fit, format, gravity, with width through
28
31
  * fit emitted only when the spec sets them and format and gravity always present (defaulting to
29
- * auto). The publicPath is appended unaltered, so the result is `/cdn-cgi/image/<options><publicPath>`. */
32
+ * auto). The publicPath is appended unaltered, so the result is `/cdn-cgi/image/<options><publicPath>`.
33
+ */
30
34
  export function variantUrl(publicPath: string, spec: VariantSpec): string {
31
35
  const options: string[] = [];
32
36
  if (spec.width !== undefined) options.push(`width=${spec.width}`);
@@ -42,9 +46,11 @@ export function variantUrl(publicPath: string, spec: VariantSpec): string {
42
46
  return `/cdn-cgi/image/${options.join(',')}${source}`;
43
47
  }
44
48
 
45
- /** Build a variant URL from a named preset. Looks up presetName in variants and builds its spec with
49
+ /**
50
+ * Build a variant URL from a named preset. Looks up presetName in variants and builds its spec with
46
51
  * variantUrl. Throws a cairn:-prefixed error naming the unknown preset when the name is absent, so a
47
- * typo in a preset name fails loudly rather than silently rendering an unsized image. */
52
+ * typo in a preset name fails loudly rather than silently rendering an unsized image.
53
+ */
48
54
  export function presetUrl(
49
55
  publicPath: string,
50
56
  presetName: string,
@@ -46,14 +46,18 @@ export interface UsageEntry {
46
46
  origin: UsageOrigin;
47
47
  }
48
48
 
49
- /** Content hash to the distinct entries that reference it. A hash with no row is "no references
50
- * found" (see the raw-HTML caveat above), never a proven orphan. */
49
+ /**
50
+ * Content hash to the distinct entries that reference it. A hash with no row is "no references
51
+ * found" (see the raw-HTML caveat above), never a proven orphan.
52
+ */
51
53
  export type UsageIndex = Map<string, UsageEntry[]>;
52
54
 
53
- /** Build options. `branches` lets a caller that already listed the open cairn/* branches pass them
55
+ /**
56
+ * Build options. `branches` lets a caller that already listed the open cairn/* branches pass them
54
57
  * in so the index does not list them a second time (the load path lists once for the media-union).
55
58
  * `strict` flips the per-branch read from degrade-and-skip to fail-closed: a delete gate must not
56
- * treat a transient branch-read failure as an absent reference, so it rethrows instead. */
59
+ * treat a transient branch-read failure as an absent reference, so it rethrows instead.
60
+ */
57
61
  export interface BuildUsageOptions {
58
62
  /** The open cairn/* branch names, already listed. When present the index skips its own listing. */
59
63
  branches?: string[];
@@ -81,14 +81,18 @@ export interface SiteConfig {
81
81
  menus?: Record<string, unknown>;
82
82
  /** Per-concept URL policy: the permalink pattern and date-prefix granularity, keyed by concept id. */
83
83
  content?: Record<string, ConceptUrlPolicy>;
84
- /** The editor spellcheck settings. The dialect is declared once per site (spec 1.2), so a British
84
+ /**
85
+ * The editor spellcheck settings. The dialect is declared once per site (spec 1.2), so a British
85
86
  * site loads the British word list and "colour" reads as correct. Today only US English ships, so an
86
- * unset or unknown dialect resolves to it. */
87
+ * unset or unknown dialect resolves to it.
88
+ */
87
89
  spellcheck?: { dialect?: string };
88
- /** The editor tidy (LLM copy-edit) settings. Opt-in at the site level (spec 2.8): tidy is a remote,
90
+ /**
91
+ * The editor tidy (LLM copy-edit) settings. Opt-in at the site level (spec 2.8): tidy is a remote,
89
92
  * costly model call, so the whole block is optional and `enabled` defaults false. The model is a
90
93
  * developer-tier fact; the `conventions` block is the editor-tier per-convention config that builds
91
- * the prompt's CONVENTIONS section. The Anthropic API key is a Worker secret, never config. */
94
+ * the prompt's CONVENTIONS section. The Anthropic API key is a Worker secret, never config.
95
+ */
92
96
  tidy?: TidyConfig;
93
97
  [key: string]: unknown;
94
98
  }
@@ -117,14 +121,18 @@ export const DEFAULT_TIDY_MODEL = 'claude-sonnet-4-6';
117
121
  * Sentence spacing is dropped on purpose and regional spelling is `spellcheck.dialect`, not a toggle.
118
122
  */
119
123
  export interface TidyConventions {
120
- /** The objective Fixes group (spelling, grammar, doubled words, whitespace, capitals, terminal
124
+ /**
125
+ * The objective Fixes group (spelling, grammar, doubled words, whitespace, capitals, terminal
121
126
  * punctuation). Default on. The always-on core governs it; this toggle lets the screen turn the
122
- * group off. */
127
+ * group off.
128
+ */
123
129
  fixes: boolean;
124
130
  /** Oxford comma position. Off when undefined; `always` | `complex-only` (AP) | `never`. */
125
131
  oxfordComma?: 'always' | 'complex-only' | 'never';
126
- /** Number style threshold. Off when undefined; the always-numeral exception sets (ages, dates,
127
- * measurements, percentages) apply at any threshold. */
132
+ /**
133
+ * Number style threshold. Off when undefined; the always-numeral exception sets (ages, dates,
134
+ * measurements, percentages) apply at any threshold.
135
+ */
128
136
  numberStyle?: 'under-ten' | 'under-hundred' | 'always-numerals';
129
137
  /** Measurement notation only (never the system, never the number). Off when undefined. */
130
138
  measurements?: 'abbreviate' | 'spell-out';
@@ -33,6 +33,9 @@ function nestedSlots(def: ComponentDef): SlotDef[] {
33
33
  return (def.slots ?? []).filter((s) => s.name !== 'title' && s.name !== 'body');
34
34
  }
35
35
 
36
+ /**
37
+ *
38
+ */
36
39
  export function serializeComponent(def: ComponentDef, values: ComponentValues): string {
37
40
  const fence = COLON.repeat(nestedSlots(def).length > 0 ? 4 : 3);
38
41
 
@@ -136,26 +139,33 @@ function rawKeysFromRoot(root: (RootContent & DirectiveNode) | undefined): strin
136
139
  return Object.keys(root?.attributes ?? {});
137
140
  }
138
141
 
139
- /** Parse a serialized component directive back into guided-form values, the inverse of
142
+ /**
143
+ * Parse a serialized component directive back into guided-form values, the inverse of
140
144
  * {@link serializeComponent}. The grammar is reversible, so the editor can round-trip a
141
- * saved directive through the form. */
145
+ * saved directive through the form.
146
+ */
142
147
  export async function parseComponent(markdown: string, def: ComponentDef): Promise<ComponentValues> {
143
148
  return valuesFromRoot(findComponentRoot(markdown, def), def);
144
149
  }
145
150
 
146
- /** The raw attribute keys present on the component's opening directive, read from the parsed tree
147
- * (quote-aware, unlike a regex over the source). Used by validation to flag unknown keys. */
151
+ /**
152
+ * The raw attribute keys present on the component's opening directive, read from the parsed tree
153
+ * (quote-aware, unlike a regex over the source). Used by validation to flag unknown keys.
154
+ */
148
155
  export function parseRawAttributeKeys(markdown: string, def: ComponentDef): string[] {
149
156
  return rawKeysFromRoot(findComponentRoot(markdown, def));
150
157
  }
151
158
 
152
- /** The result of {@link componentRoundTripSafety}: whether re-opening a placed block into the
153
- * guided form and re-serializing it is provably lossless. */
159
+ /**
160
+ * The result of {@link componentRoundTripSafety}: whether re-opening a placed block into the
161
+ * guided form and re-serializing it is provably lossless.
162
+ */
154
163
  export type RoundTripSafety =
155
164
  | { safe: true }
156
165
  | { safe: false; reason: 'unknown-attribute' | 'undeclared-child' | 'not-idempotent' | 'not-a-component' };
157
166
 
158
- /** Decide whether guided edit of this placed block is provably lossless. A block a person typed by
167
+ /**
168
+ * Decide whether guided edit of this placed block is provably lossless. A block a person typed by
159
169
  * hand can carry more than the schema models (an attribute the def does not list, a child container
160
170
  * the def does not declare, slot content the form cannot represent stably), and parsing such a block
161
171
  * into the form then re-serializing would silently drop it. The edit affordance is offered only when
@@ -165,7 +175,8 @@ export type RoundTripSafety =
165
175
  * 2. `unknown-attribute`: the block carries an attribute key the def does not declare.
166
176
  * 3. `undeclared-child`: the root has a direct child container directive that is not a declared
167
177
  * nested slot. Such a child would otherwise fold into the body slot and move on re-serialize.
168
- * 4. `not-idempotent`: `parse -> serialize -> parse` does not recover the same values. */
178
+ * 4. `not-idempotent`: `parse -> serialize -> parse` does not recover the same values.
179
+ */
169
180
  export async function componentRoundTripSafety(markdown: string, def: ComponentDef): Promise<RoundTripSafety> {
170
181
  const root = findComponentRoot(markdown, def);
171
182
  if (!root) return { safe: false, reason: 'not-a-component' };
@@ -191,9 +202,11 @@ export async function componentRoundTripSafety(markdown: string, def: ComponentD
191
202
  return { safe: true };
192
203
  }
193
204
 
194
- /** Parse the component once and derive both the guided-form values and the raw attribute keys.
205
+ /**
206
+ * Parse the component once and derive both the guided-form values and the raw attribute keys.
195
207
  * Validation needs both, so this seam spares it the double parse that calling
196
- * {@link parseComponent} and {@link parseRawAttributeKeys} separately would cost. */
208
+ * {@link parseComponent} and {@link parseRawAttributeKeys} separately would cost.
209
+ */
197
210
  export async function parseComponentWithRawKeys(
198
211
  markdown: string,
199
212
  def: ComponentDef,
@@ -2,12 +2,16 @@ import { serializeComponent } from './component-grammar.js';
2
2
  import { validateComponent } from './component-validate.js';
3
3
  import type { ComponentDef, ComponentValues } from './registry.js';
4
4
 
5
- /** The outcome of preparing a guided-form component for insertion: the markdown to insert, or the
6
- * field-keyed errors to show on the form. */
5
+ /**
6
+ * The outcome of preparing a guided-form component for insertion: the markdown to insert, or the
7
+ * field-keyed errors to show on the form.
8
+ */
7
9
  export type ComponentInsert = { ok: true; markdown: string } | { ok: false; errors: Record<string, string> };
8
10
 
9
- /** Serialize a component's form values, then validate the result against its schema. Returns the
10
- * markdown to insert at the cursor, or the field errors keyed by attribute key or slot name. */
11
+ /**
12
+ * Serialize a component's form values, then validate the result against its schema. Returns the
13
+ * markdown to insert at the cursor, or the field errors keyed by attribute key or slot name.
14
+ */
11
15
  export async function buildComponentInsert(def: ComponentDef, values: ComponentValues): Promise<ComponentInsert> {
12
16
  const markdown = serializeComponent(def, values);
13
17
  const verdict = await validateComponent(markdown, def);
@@ -8,8 +8,10 @@ export interface ReferenceOptions {
8
8
  summary: string;
9
9
  }
10
10
 
11
- /** Build a self-contained markdown reference (the llms-full.txt shape) for a component registry, for
12
- * authors and for pointing an LLM at one curated file. */
11
+ /**
12
+ * Build a self-contained markdown reference (the llms-full.txt shape) for a component registry, for
13
+ * authors and for pointing an LLM at one curated file.
14
+ */
13
15
  export function generateComponentReference(registry: ComponentRegistry, opts: ReferenceOptions): string {
14
16
  const sections = registry.defs.map((def) => componentSection(def));
15
17
  return `# ${opts.title}\n\n> ${opts.summary}\n\n${sections.join('\n\n')}\n`;
@@ -4,6 +4,9 @@ import type { ComponentDef, ComponentValues } from './registry.js';
4
4
  /** A validation verdict: ok, or field-keyed error messages. */
5
5
  export type ComponentValidation = { ok: true } | { ok: false; errors: Record<string, string> };
6
6
 
7
+ /**
8
+ *
9
+ */
7
10
  export async function validateComponent(markdown: string, def: ComponentDef): Promise<ComponentValidation> {
8
11
  const { values, rawKeys } = await parseComponentWithRawKeys(markdown, def);
9
12
  const errors: Record<string, string> = {};
@@ -4,10 +4,12 @@ import type { Element } from 'hast';
4
4
  /** A glyph name to SVG path-data map (the site owns the icon set). */
5
5
  export type IconSet = Record<string, string>;
6
6
 
7
- /** Inline SVG glyph as a real hast node: class ec-glyph, 256 viewBox, currentColor fill.
7
+ /**
8
+ * Inline SVG glyph as a real hast node: class ec-glyph, 256 viewBox, currentColor fill.
8
9
  * An unknown icon name yields the bare svg shell with no path child, so it never serializes
9
10
  * a stray empty (or undefined) path. Callers always wrap the returned element, so the shell
10
- * keeps them safe. */
11
+ * keeps them safe.
12
+ */
11
13
  export function glyph(name: string, icons: IconSet): Element {
12
14
  const d = icons[name];
13
15
  return s(
@@ -19,28 +19,38 @@ import { defineRegistry, type ComponentRegistry } from './registry.js';
19
19
  import type { LinkResolve } from '../content/links.js';
20
20
 
21
21
  export interface RendererOptions {
22
- /** Stamp a `data-rise` ordinal (0, 1, 2, …) on each top-level component so a site's
22
+ /**
23
+ * Stamp a `data-rise` ordinal (0, 1, 2, …) on each top-level component so a site's
23
24
  * CSS can drive an entrance-cascade delay off it. Omit for no stagger. The ordinal
24
- * is inert, so a consumer's sanitize floor can keep `data-rise` and drop `style`. */
25
+ * is inert, so a consumer's sanitize floor can keep `data-rise` and drop `style`.
26
+ */
25
27
  stagger?: boolean;
26
- /** Extend the sanitize allowlist. Receives cairn's default schema (defaultSchema plus the
28
+ /**
29
+ * Extend the sanitize allowlist. Receives cairn's default schema (defaultSchema plus the
27
30
  * directive markers and the common benign tags) and returns the schema to use. Add to the
28
31
  * allowlist for the benign HTML a site's content needs; start from the argument so the
29
- * dangerous strip is preserved. */
32
+ * dangerous strip is preserved.
33
+ */
30
34
  sanitizeSchema?: (defaults: Schema) => Schema;
31
- /** Developer-only escape hatch: disable the sanitize floor entirely. This reintroduces the XSS
35
+ /**
36
+ * Developer-only escape hatch: disable the sanitize floor entirely. This reintroduces the XSS
32
37
  * vector the floor closes, so it is only for a site whose content is fully developer-controlled.
33
- * It is a code-level adapter decision, never an editor-facing setting. */
38
+ * It is a code-level adapter decision, never an editor-facing setting.
39
+ */
34
40
  unsafeDisableSanitize?: boolean;
35
- /** The `rel` value forced on every `target="_blank"` anchor, applied last so it also covers
41
+ /**
42
+ * The `rel` value forced on every `target="_blank"` anchor, applied last so it also covers
36
43
  * component-built anchors. Defaults to `'noopener noreferrer'`. Set a different string to change
37
- * it, or `false` to disable the injection (a site that owns its own anchor hardening). */
44
+ * it, or `false` to disable the injection (a site that owns its own anchor hardening).
45
+ */
38
46
  anchorRel?: string | false;
39
47
  }
40
48
 
41
- /** Compose a site's render pipeline from its component registry: directive syntax to
49
+ /**
50
+ * Compose a site's render pipeline from its component registry: directive syntax to
42
51
  * stamped markers to registry-built hast. Returns `renderMarkdown` plus the remark/
43
- * rehype plugin arrays (so the admin editor preview can reuse the exact same set). */
52
+ * rehype plugin arrays (so the admin editor preview can reuse the exact same set).
53
+ */
44
54
  export function createRenderer(
45
55
  registry: ComponentRegistry = defineRegistry({ components: [] }),
46
56
  options: RendererOptions = {},
@@ -24,16 +24,20 @@ export interface AttributeField {
24
24
  help?: string;
25
25
  /** A RegExp `source` to validate the value against, plus the message to show on a mismatch. */
26
26
  pattern?: { source: string; message: string };
27
- /** A pure, browser-safe cross-field validator. Returns an error string, or null when valid.
27
+ /**
28
+ * A pure, browser-safe cross-field validator. Returns an error string, or null when valid.
28
29
  * Receives the field's value and the full {@link ComponentValues} so a rule can read sibling
29
- * fields. The picker wraps the call in try/catch so an author's throw never crashes the form. */
30
+ * fields. The picker wraps the call in try/catch so an author's throw never crashes the form.
31
+ */
30
32
  validate?: (value: string | boolean, all: ComponentValues) => string | null;
31
33
  }
32
34
 
33
35
  export type SlotKind = 'markdown' | 'inline' | 'repeatable';
34
36
 
35
- /** One named content region of a component. The slots named `title` and `body` are special: `title`
36
- * serializes to the directive `[label]` and `body` to the unmarked content (see the canonical grammar). */
37
+ /**
38
+ * One named content region of a component. The slots named `title` and `body` are special: `title`
39
+ * serializes to the directive `[label]` and `body` to the unmarked content (see the canonical grammar).
40
+ */
37
41
  export interface SlotDef {
38
42
  name: string;
39
43
  label: string;
@@ -42,15 +46,19 @@ export interface SlotDef {
42
46
  help?: string;
43
47
  /** For `kind: 'repeatable'`: the fields composing each list item (v1 uses the first field). */
44
48
  itemFields?: AttributeField[];
45
- /** For `kind: 'repeatable'`: derives a row's label from its item values and zero-based index.
46
- * When it returns nothing, the picker falls back to `${label} ${index + 1}`. */
49
+ /**
50
+ * For `kind: 'repeatable'`: derives a row's label from its item values and zero-based index.
51
+ * When it returns nothing, the picker falls back to `${label} ${index + 1}`.
52
+ */
47
53
  itemLabel?: (item: Record<string, string | boolean>, index: number) => string;
48
54
  }
49
55
 
50
- /** The structured input a component's `build` receives. The engine stamps the component's
56
+ /**
57
+ * The structured input a component's `build` receives. The engine stamps the component's
51
58
  * attributes and partitions its slots from the rendered hast, so `build` arranges hast and
52
59
  * never walks the tree. `slot(name)` returns a slot's rendered children (title, body, or any
53
- * named slot); `items(name)` returns a repeatable slot's items, one child list per item. */
60
+ * named slot); `items(name)` returns a repeatable slot's items, one child list per item.
61
+ */
54
62
  export interface ComponentContext {
55
63
  /** Declared attribute values, keyed by attribute key. Booleans are real booleans. */
56
64
  attributes: Record<string, string | boolean>;
@@ -72,9 +80,11 @@ export interface ComponentDef {
72
80
  description: string;
73
81
  /** Markdown scaffold inserted at the cursor by the editor palette. */
74
82
  insertTemplate?: string;
75
- /** Build the final hast element from the component context (attributes plus partitioned
83
+ /**
84
+ * Build the final hast element from the component context (attributes plus partitioned
76
85
  * slots). The engine stamps the entrance-stagger ordinal (`data-rise`) on the top-level
77
- * result, so a build fn stays free of any motion concern. */
86
+ * result, so a build fn stays free of any motion concern.
87
+ */
78
88
  build: (ctx: ComponentContext) => Element;
79
89
  /** Optional role-to-default-icon, e.g. `{ caution: 'warning' }`. */
80
90
  defaultIconByRole?: Record<string, string>;
@@ -90,8 +100,10 @@ export interface ComponentDef {
90
100
  group?: string;
91
101
  /** Omit from the top-level picker (for a nested or round-trip-only component). */
92
102
  hidden?: boolean;
93
- /** A structured sample the picker seeds the form with and renders through the same path a real
94
- * insert takes. Declaring `preview` is what opts the component into the two-pane configure layout. */
103
+ /**
104
+ * A structured sample the picker seeds the form with and renders through the same path a real
105
+ * insert takes. Declaring `preview` is what opts the component into the two-pane configure layout.
106
+ */
95
107
  preview?: {
96
108
  attributes?: Record<string, string | boolean>;
97
109
  slots?: Record<string, string | string[]>;
@@ -107,16 +119,20 @@ export interface ComponentRegistry {
107
119
  iconField(name: string): AttributeField | undefined;
108
120
  }
109
121
 
110
- /** The hast property name carrying one declared attribute from stamp to dispatch, e.g. `tone`
122
+ /**
123
+ * The hast property name carrying one declared attribute from stamp to dispatch, e.g. `tone`
111
124
  * becomes `dataAttrTone`. The directive stamp writes it and the rehype dispatch reads it, so both
112
- * sides derive the name from this one helper rather than spelling the capitalize twice. */
125
+ * sides derive the name from this one helper rather than spelling the capitalize twice.
126
+ */
113
127
  export function dataAttrProp(key: string): string {
114
128
  return `dataAttr${key.charAt(0).toUpperCase()}${key.slice(1)}`;
115
129
  }
116
130
 
117
- /** A component's first `type:'icon'` attribute, or undefined when it declares none. Both the
131
+ /**
132
+ * A component's first `type:'icon'` attribute, or undefined when it declares none. Both the
118
133
  * construction-time guard and the registry's `iconField` derive the icon field from this one
119
- * predicate rather than spelling the `type === 'icon'` find twice. */
134
+ * predicate rather than spelling the `type === 'icon'` find twice.
135
+ */
120
136
  function findIconField(def: ComponentDef): AttributeField | undefined {
121
137
  return def.attributes?.find((field) => field.type === 'icon');
122
138
  }
@@ -151,15 +167,19 @@ export function defineRegistry({ components }: { components: ComponentDef[] }):
151
167
  };
152
168
  }
153
169
 
154
- /** Guided-form values for one component: attribute values keyed by attribute key, slot values keyed
155
- * by slot name (a string, or a string list for a repeatable slot). */
170
+ /**
171
+ * Guided-form values for one component: attribute values keyed by attribute key, slot values keyed
172
+ * by slot name (a string, or a string list for a repeatable slot).
173
+ */
156
174
  export interface ComponentValues {
157
175
  attributes: Record<string, string | boolean>;
158
176
  slots: Record<string, string | string[]>;
159
177
  }
160
178
 
161
- /** Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
162
- * and empty slot values ([] for repeatable, '' otherwise). */
179
+ /**
180
+ * Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
181
+ * and empty slot values ([] for repeatable, '' otherwise).
182
+ */
163
183
  export function emptyValues(def: ComponentDef): ComponentValues {
164
184
  const attributes: Record<string, string | boolean> = {};
165
185
  for (const field of def.attributes ?? []) {
@@ -172,9 +192,11 @@ export function emptyValues(def: ComponentDef): ComponentValues {
172
192
  return { attributes, slots };
173
193
  }
174
194
 
175
- /** Seed {@link ComponentValues} from a component's `preview` sample: the {@link emptyValues} base
195
+ /**
196
+ * Seed {@link ComponentValues} from a component's `preview` sample: the {@link emptyValues} base
176
197
  * with `def.preview.attributes` and `def.preview.slots` overlaid (a shallow merge per side). When
177
- * the def declares no `preview`, returns exactly the {@link emptyValues} output. */
198
+ * the def declares no `preview`, returns exactly the {@link emptyValues} output.
199
+ */
178
200
  export function previewValues(def: ComponentDef): ComponentValues {
179
201
  const base = emptyValues(def);
180
202
  if (!def.preview) return base;
@@ -2,12 +2,17 @@ import type { Root, Element, ElementContent } from 'hast';
2
2
  import { h } from 'hastscript';
3
3
  import { dataAttrProp, type ComponentContext, type ComponentDef, type ComponentRegistry } from './registry.js';
4
4
 
5
+ /**
6
+ *
7
+ */
5
8
  export function isElement(node: ElementContent | undefined): node is Element {
6
9
  return !!node && node.type === 'element';
7
10
  }
8
11
 
9
- /** Read a declared string attribute off the component context, returning undefined for a boolean or
10
- * absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats. */
12
+ /**
13
+ * Read a declared string attribute off the component context, returning undefined for a boolean or
14
+ * absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats.
15
+ */
11
16
  export function strAttr(ctx: ComponentContext, key: string): string | undefined {
12
17
  const value = ctx.attributes[key];
13
18
  return typeof value === 'string' ? value : undefined;
@@ -16,6 +21,9 @@ export function strAttr(ctx: ComponentContext, key: string): string | undefined
16
21
  // hast Properties values are PropertyValue (string | number | boolean | array | null).
17
22
  // Directive markers (dataPrimitive/dataRole/dataAttr<Key>) are always stamped as strings;
18
23
  // this reads them back with that guarantee instead of casting at each call site.
24
+ /**
25
+ *
26
+ */
19
27
  export function strProp(node: Element, name: string): string | undefined {
20
28
  const value = node.properties?.[name];
21
29
  return typeof value === 'string' ? value : undefined;
@@ -35,10 +43,12 @@ export function cardShell(classes: string[], body: ElementContent[]): Element {
35
43
  return h('section', { className: classes }, [h('div', { className: ['card-body'] }, body)]);
36
44
  }
37
45
 
38
- /** Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
46
+ /**
47
+ * Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
39
48
  * Pass the title's inline children, an optional pre-built icon element, and an optional heading
40
49
  * level (default 2). This factors the icon-plus-heading head that a titled component build would
41
- * otherwise rebuild by hand (the shape the removed `splitHead` produced). */
50
+ * otherwise rebuild by hand (the shape the removed `splitHead` produced).
51
+ */
42
52
  export function headRow(title: ElementContent[], icon?: Element, level: number = 2): Element {
43
53
  const children: ElementContent[] = [];
44
54
  if (icon) children.push(icon);
@@ -46,8 +56,10 @@ export function headRow(title: ElementContent[], icon?: Element, level: number =
46
56
  return h('div', { className: ['ec-head'] }, children);
47
57
  }
48
58
 
49
- /** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
50
- * text nodes so the bare list serializes without newlines. Returns that <ul>. */
59
+ /**
60
+ * Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
61
+ * text nodes so the bare list serializes without newlines. Returns that <ul>.
62
+ */
51
63
  export function markFirstList(children: ElementContent[]): Element | undefined {
52
64
  const ul = children.find((c) => isElement(c) && c.tagName === 'ul') as Element | undefined;
53
65
  if (ul) {
@@ -142,12 +154,14 @@ function transformNode(node: Element, registry: ComponentRegistry): Element {
142
154
  return def.build(ctx);
143
155
  }
144
156
 
145
- /** Rehype transformer: dispatch each stamped element through its registry `build`
157
+ /**
158
+ * Rehype transformer: dispatch each stamped element through its registry `build`
146
159
  * fn. When `stagger` is on, each top-level primitive gets a `data-rise` attribute
147
160
  * carrying its document-order index (0, 1, 2, …); the site's CSS maps that ordinal
148
161
  * to an entrance delay. The index is inert, so a consumer's sanitize floor can keep
149
162
  * `data-rise` while dropping `style`. Nested primitives never get it. Non-primitive
150
- * content (lede, intro paragraphs, the page-toc nav) passes through untouched. */
163
+ * content (lede, intro paragraphs, the page-toc nav) passes through untouched.
164
+ */
151
165
  export function rehypeDispatch(registry: ComponentRegistry, stagger?: boolean) {
152
166
  return (tree: Root) => {
153
167
  let idx = 0;
@@ -51,6 +51,9 @@ function restoreLiteral(node: TextDirective | LeafDirective): PhrasingContent[]
51
51
  // component name, icon, and role. No structure is built here; the rehype
52
52
  // dispatcher rewrites the marked elements once their children are hast.
53
53
  // Text and leaf directives are restored to literal text (accidental prose colons).
54
+ /**
55
+ *
56
+ */
54
57
  export function remarkDirectiveStamp(registry: ComponentRegistry) {
55
58
  const known = new Set(registry.names);
56
59
  return (tree: Root) => {
@@ -69,8 +69,10 @@ function trimLeadingNewline(children: PhrasingContent[]): PhrasingContent[] {
69
69
  return children;
70
70
  }
71
71
 
72
- /** Rewrite the reserved `figure` container directive into a placed <figure>. Every other directive
73
- * is left to remarkDirectiveStamp, which already skips unregistered names. */
72
+ /**
73
+ * Rewrite the reserved `figure` container directive into a placed <figure>. Every other directive
74
+ * is left to remarkDirectiveStamp, which already skips unregistered names.
75
+ */
74
76
  export function remarkFigure() {
75
77
  return (tree: Root): void => {
76
78
  visit(tree, 'containerDirective', (node: ContainerDirective) => {
@@ -15,9 +15,11 @@ interface LinkNode {
15
15
  data?: { hProperties?: Record<string, unknown> };
16
16
  }
17
17
 
18
- /** Resolve cairn: link nodes against the VFile's resolver. A non-cairn href and a malformed token
18
+ /**
19
+ * Resolve cairn: link nodes against the VFile's resolver. A non-cairn href and a malformed token
19
20
  * pass through. A missing target is marked with the cairn-broken-link class (the resolver returns
20
- * undefined) or, when the resolver throws, the error propagates and fails the build. */
21
+ * undefined) or, when the resolver throws, the error propagates and fails the build.
22
+ */
21
23
  export function remarkResolveCairnLinks() {
22
24
  return (tree: unknown, file: VFile): void => {
23
25
  const resolve = file.data[CAIRN_RESOLVE] as LinkResolve | undefined;