@glw907/cairn-cms 0.60.1 → 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 (254) hide show
  1. package/CHANGELOG.md +69 -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 +51 -0
  39. package/dist/content/advisories.js +79 -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 +295 -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 +141 -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 +492 -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
@@ -27,8 +27,10 @@ const LINE = {
27
27
  task: { prefix: () => '- [ ] ', exact: /^- \[[ xX]\] /, strip: /^- \[[ xX]\] / },
28
28
  };
29
29
  const TABLE_GRID = '| Column 1 | Column 2 |\n| -------- | -------- |\n| | |\n| | |';
30
- /** Wrap the selection in `marker`, or unwrap when the markers are already there (inside or just
31
- * outside the selection). The returned range covers the text without its markers either way. */
30
+ /**
31
+ * Wrap the selection in `marker`, or unwrap when the markers are already there (inside or just
32
+ * outside the selection). The returned range covers the text without its markers either way.
33
+ */
32
34
  function toggleWrap(doc, from, to, marker) {
33
35
  const m = marker.length;
34
36
  const sel = doc.slice(from, to);
@@ -42,10 +44,12 @@ function toggleWrap(doc, from, to, marker) {
42
44
  const next = doc.slice(0, from) + marker + sel + marker + doc.slice(to);
43
45
  return { doc: next, from: from + m, to: to + m };
44
46
  }
45
- /** Apply a line-prefix kind to every selected line. When the kind toggles and every line already
47
+ /**
48
+ * Apply a line-prefix kind to every selected line. When the kind toggles and every line already
46
49
  * carries its marker, the markers come off; otherwise competing markers are replaced and each
47
50
  * line gains the kind's prefix. The selection shifts with the first line's edit and stretches
48
- * by the total length change, the same mechanics the original single-prefix version had. */
51
+ * by the total length change, the same mechanics the original single-prefix version had.
52
+ */
49
53
  function applyLinePrefix(doc, from, to, kind) {
50
54
  const { prefix, exact, strip } = LINE[kind];
51
55
  const lineStart = doc.lastIndexOf('\n', from - 1) + 1; // 0 when the selection is on the first line
@@ -62,8 +66,10 @@ function applyLinePrefix(doc, from, to, kind) {
62
66
  to: to + totalDelta,
63
67
  };
64
68
  }
65
- /** Fence the selected lines in triple backticks on their own lines, or remove the fences when the
66
- * lines just above and below the selection already are fences. */
69
+ /**
70
+ * Fence the selected lines in triple backticks on their own lines, or remove the fences when the
71
+ * lines just above and below the selection already are fences.
72
+ */
67
73
  function toggleCodeFence(doc, from, to) {
68
74
  const lineStart = doc.lastIndexOf('\n', from - 1) + 1;
69
75
  const lineEndRaw = doc.indexOf('\n', to);
@@ -82,6 +88,9 @@ function toggleCodeFence(doc, from, to) {
82
88
  const next = doc.slice(0, lineStart) + open + doc.slice(lineStart, lineEnd) + '\n```' + doc.slice(lineEnd);
83
89
  return { doc: next, from: from + open.length, to: to + open.length };
84
90
  }
91
+ /**
92
+ *
93
+ */
85
94
  export function applyMarkdownFormat(doc, from, to, kind) {
86
95
  if (kind === 'bold' || kind === 'italic' || kind === 'code' || kind === 'strike') {
87
96
  return toggleWrap(doc, from, to, WRAP[kind]);
@@ -165,10 +174,12 @@ export function findMediaImagesNeedingAlt(doc) {
165
174
  hits.sort((a, b) => a.from - b.from);
166
175
  return hits;
167
176
  }
168
- /** Concatenate a link node's text-child values. The parser has already unescaped them, so a source
177
+ /**
178
+ * Concatenate a link node's text-child values. The parser has already unescaped them, so a source
169
179
  * `Notes \[draft\]` yields `Notes [draft]`. Used instead of mdast-util-to-string, which is not a
170
180
  * direct dependency. Non-text children (a nested emphasis, say) contribute no value, which is fine
171
- * for the picker-produced links this fix targets. */
181
+ * for the picker-produced links this fix targets.
182
+ */
172
183
  function linkText(node) {
173
184
  return node.children.map((c) => ('value' in c ? c.value : '')).join('');
174
185
  }
@@ -201,14 +212,18 @@ export function unwrapCairnLink(doc, href) {
201
212
  return out;
202
213
  }
203
214
  const FIGURE_ROLES = new Set(['center', 'wide', 'full']);
204
- /** Parse a doc with the figure-aware pipeline (the render step's grammar), so the editor transforms
205
- * agree with what renders. Container directives need remark-directive on top of the markdown base. */
215
+ /**
216
+ * Parse a doc with the figure-aware pipeline (the render step's grammar), so the editor transforms
217
+ * agree with what renders. Container directives need remark-directive on top of the markdown base.
218
+ */
206
219
  function parseFigureDoc(doc) {
207
220
  return unified().use(remarkParse).use(remarkGfm).use(remarkDirective).parse(doc);
208
221
  }
209
- /** Find the media `image` node whose source range contains `pos`, or whose enclosing figure contains
222
+ /**
223
+ * Find the media `image` node whose source range contains `pos`, or whose enclosing figure contains
210
224
  * `pos`, along with its enclosing `figure` directive when there is one. Returns null when `pos` is
211
- * not on a media image nor inside a figure that wraps one. */
225
+ * not on a media image nor inside a figure that wraps one.
226
+ */
212
227
  function locateMediaImage(tree, pos) {
213
228
  let bareHit = null;
214
229
  let figureHit = null;
@@ -241,8 +256,10 @@ function locateMediaImage(tree, pos) {
241
256
  // A figure hit (the caret on the image or anywhere in its block) wins over a bare hit.
242
257
  return figureHit ?? bareHit;
243
258
  }
244
- /** The `figure`-named container directive that encloses `node`, or null. Walks the tree to find the
245
- * ancestor, since unist-util-visit's per-call ancestors are not retained across the traversal. */
259
+ /**
260
+ * The `figure`-named container directive that encloses `node`, or null. Walks the tree to find the
261
+ * ancestor, since unist-util-visit's per-call ancestors are not retained across the traversal.
262
+ */
246
263
  function enclosingFigure(tree, target) {
247
264
  let found = null;
248
265
  visit(tree, 'containerDirective', (dir) => {
@@ -258,24 +275,30 @@ function enclosingFigure(tree, target) {
258
275
  });
259
276
  return found;
260
277
  }
261
- /** Strip one leading backslash sitting immediately before a colon, the inverse of the fence-escape
278
+ /**
279
+ * Strip one leading backslash sitting immediately before a colon, the inverse of the fence-escape
262
280
  * wrapImageInFigure/updateFigure apply, so a caption that began with a directive-opening colon run
263
- * round-trips to the author's original text. */
281
+ * round-trips to the author's original text.
282
+ */
264
283
  function unescapeCaption(raw) {
265
284
  return raw.replace(/^\\(?=:)/, '');
266
285
  }
267
- /** Collapse a raw caption source span to the single-line value the control edits: internal newlines
268
- * to single spaces, trimmed, with the leading-colon fence escape stripped. */
286
+ /**
287
+ * Collapse a raw caption source span to the single-line value the control edits: internal newlines
288
+ * to single spaces, trimmed, with the leading-colon fence escape stripped.
289
+ */
269
290
  function finishCaption(raw) {
270
291
  return unescapeCaption(raw.replace(/\s*\n\s*/g, ' ').trim());
271
292
  }
272
- /** Read the raw caption source from a figure directive, mirroring the render step's caption: the first
293
+ /**
294
+ * Read the raw caption source from a figure directive, mirroring the render step's caption: the first
273
295
  * text-bearing content after the image. The render step (remark-figure.ts) handles both caption
274
296
  * forms, so the read must too. In the no-blank-line form the caption shares the image's paragraph,
275
297
  * trailing the token, so it is read from the token end to that block's end; in the blank-line form it
276
298
  * is the first text-bearing block after the image's paragraph. Only the first such content is the
277
299
  * caption (a later block is a stray paragraph the render leaves outside the figcaption). Empty when
278
- * the figure has no caption. */
300
+ * the figure has no caption.
301
+ */
279
302
  function readCaption(doc, figure, image) {
280
303
  const imageStart = image.position?.start?.offset;
281
304
  const imageEnd = image.position?.end?.offset;
@@ -307,8 +330,10 @@ function readCaption(doc, figure, image) {
307
330
  }
308
331
  return '';
309
332
  }
310
- /** Whether a block's subtree carries any non-whitespace text, the caption-candidate test the render
311
- * step uses (a bare image paragraph has no text node, so it is never read as a caption). */
333
+ /**
334
+ * Whether a block's subtree carries any non-whitespace text, the caption-candidate test the render
335
+ * step uses (a bare image paragraph has no text node, so it is never read as a caption).
336
+ */
312
337
  function blockHasText(node) {
313
338
  let found = false;
314
339
  visit(node, 'text', (text) => {
@@ -343,18 +368,22 @@ export function figureAtImage(doc, pos) {
343
368
  const role = className && FIGURE_ROLES.has(className) ? className : null;
344
369
  return { imageFrom, imageTo, figure: { from, to, caption: readCaption(doc, dir, hit.image), role } };
345
370
  }
346
- /** Sanitize a caption into a single safe body line: collapse internal newlines to single spaces,
371
+ /**
372
+ * Sanitize a caption into a single safe body line: collapse internal newlines to single spaces,
347
373
  * trim, and neutralize ONLY the directive-fence hazard (a leading colon would open a directive at
348
374
  * line start) by prefixing one backslash. The author's inline markdown is preserved otherwise, so
349
- * emphasis and links survive. figureAtImage strips the backslash on read for a clean round-trip. */
375
+ * emphasis and links survive. figureAtImage strips the backslash on read for a clean round-trip.
376
+ */
350
377
  function sanitizeCaption(caption) {
351
378
  const line = caption.replace(/\s*\n\s*/g, ' ').trim();
352
379
  return line.startsWith(':') ? '\\' + line : line;
353
380
  }
354
- /** Build the canonical figure block source: the opener (with the role brace only for a non-null
381
+ /**
382
+ * Build the canonical figure block source: the opener (with the role brace only for a non-null
355
383
  * role), the image token verbatim on its own line, then a blank line and the sanitized caption when
356
384
  * the caption is non-empty, and the closing fence. This is the blank-line form remarkFigure reads as
357
- * its primary path, and it reads cleanly when hand-edited. */
385
+ * its primary path, and it reads cleanly when hand-edited.
386
+ */
358
387
  function buildFigureBlock(imageSrc, caption, role) {
359
388
  const opener = role ? `:::figure{.${role}}` : ':::figure';
360
389
  const cap = sanitizeCaption(caption);
@@ -382,9 +411,11 @@ export function wrapImageInFigure(doc, imageFrom, imageTo, caption, role) {
382
411
  const end = blockStart + block.length;
383
412
  return { doc: before + inserted + after, from: end, to: end };
384
413
  }
385
- /** The inner image token of the figure at `figureRange.from`, sliced verbatim from the source so it
414
+ /**
415
+ * The inner image token of the figure at `figureRange.from`, sliced verbatim from the source so it
386
416
  * is reused byte-for-byte (open risk 3). Empty when no media image is found there, which leaves the
387
- * rebuild image-less rather than throwing. Shared by updateFigure and unwrapFigure. */
417
+ * rebuild image-less rather than throwing. Shared by updateFigure and unwrapFigure.
418
+ */
388
419
  function figureImageSrc(doc, figureRange) {
389
420
  const info = figureAtImage(doc, figureRange.from);
390
421
  return info ? doc.slice(info.imageFrom, info.imageTo) : '';
@@ -0,0 +1,8 @@
1
+ /** One cheat-sheet row: the literal syntax, a plain gloss, and the group it belongs to. */
2
+ export interface MarkdownReferenceRow {
3
+ syntax: string;
4
+ makes: string;
5
+ group: 'text' | 'links' | 'blocks';
6
+ }
7
+ /** The cheat-sheet vocabulary, everyday rows first: the five text, the four links, then the blocks. */
8
+ export declare const markdownReference: MarkdownReferenceRow[];
@@ -0,0 +1,22 @@
1
+ // The one Markdown cheat-sheet table, the single source the editor's Markdown help dialog and the
2
+ // Help home both render, so the two surfaces cannot drift. Each row pairs the literal syntax an
3
+ // author types with a plain gloss of what it makes, grouped so the Help home can show the everyday
4
+ // rows (text and links) and the dialog can show every group. Mirrors editor-shortcuts.ts.
5
+ /** The cheat-sheet vocabulary, everyday rows first: the five text, the four links, then the blocks. */
6
+ export const markdownReference = [
7
+ { syntax: '## Heading', makes: 'A heading', group: 'text' },
8
+ { syntax: '**bold**', makes: 'Bold text', group: 'text' },
9
+ { syntax: '*italic*', makes: 'Italic text', group: 'text' },
10
+ { syntax: '> quote', makes: 'A quote', group: 'text' },
11
+ { syntax: '`code`', makes: 'Inline code', group: 'text' },
12
+ { syntax: '[text](url)', makes: 'A link', group: 'links' },
13
+ { syntax: '[[page-name]]', makes: 'A link to one of your pages', group: 'links' },
14
+ { syntax: '- item', makes: 'A bulleted list', group: 'links' },
15
+ { syntax: '1. item', makes: 'A numbered list', group: 'links' },
16
+ { syntax: '### Heading', makes: 'A smaller heading', group: 'blocks' },
17
+ { syntax: '#### Heading', makes: 'A fourth-level heading', group: 'blocks' },
18
+ { syntax: '~~text~~', makes: 'Crossed-out text', group: 'blocks' },
19
+ { syntax: '- [ ] item', makes: 'A checklist', group: 'blocks' },
20
+ { syntax: 'Table', makes: 'The Table button in the toolbar inserts one', group: 'blocks' },
21
+ { syntax: '---', makes: 'A horizontal rule', group: 'blocks' },
22
+ ];
@@ -1,12 +1,16 @@
1
1
  import type { MediaEntry } from '../media/manifest.js';
2
2
  import type { UploadResult } from '../sveltekit/content-routes.js';
3
3
  import type { IngestFailureKind } from './client-ingest.js';
4
- /** A failure the card surfaces. The ingest taxonomy plus a `generic` catch-all for a refuse reason
5
- * with no specific author-facing card (a binding-missing, a length-required, a parse miss). */
4
+ /**
5
+ * A failure the card surfaces. The ingest taxonomy plus a `generic` catch-all for a refuse reason
6
+ * with no specific author-facing card (a binding-missing, a length-required, a parse miss).
7
+ */
6
8
  export type UploadFailureKind = IngestFailureKind | 'generic';
7
- /** The outcome the popover acts on. `inserted` swaps the placeholder for the reference and records
9
+ /**
10
+ * The outcome the popover acts on. `inserted` swaps the placeholder for the reference and records
8
11
  * the entry; `failed` cancels the placeholder and shows the typed card; `session-expired` cancels
9
- * the placeholder and tells the author to sign in again. */
12
+ * the placeholder and tells the author to sign in again.
13
+ */
10
14
  export type UploadOutcome = {
11
15
  kind: 'inserted';
12
16
  reference: string;
@@ -18,10 +22,12 @@ export type UploadOutcome = {
18
22
  } | {
19
23
  kind: 'session-expired';
20
24
  };
21
- /** The shape the popover hands in: either a parsed SvelteKit action result (success or failure) or a
25
+ /**
26
+ * The shape the popover hands in: either a parsed SvelteKit action result (success or failure) or a
22
27
  * bare response signal for the redirect and network-error cases. The popover deserializes the body
23
28
  * for the success and failure cases and passes the raw `response.type`/`response.status` for the
24
- * redirect case, so this one mapper covers every branch. */
29
+ * redirect case, so this one mapper covers every branch.
30
+ */
25
31
  export type UploadEnvelope = {
26
32
  type: 'success';
27
33
  status?: number;
@@ -1,15 +1,19 @@
1
1
  import type { Range } from './spellcheck.js';
2
2
  /** The three objective-error kinds, each its own check. */
3
3
  export type ObjectiveErrorKind = 'doubled-word' | 'double-space' | 'repeated-punct';
4
- /** A single deterministic edit that resolves one finding: replace [from, to) with `insert`. The lint
5
- * source turns this into the diagnostic's quick-fix action. */
4
+ /**
5
+ * A single deterministic edit that resolves one finding: replace [from, to) with `insert`. The lint
6
+ * source turns this into the diagnostic's quick-fix action.
7
+ */
6
8
  export interface ObjectiveFix {
7
9
  from: number;
8
10
  to: number;
9
11
  insert: string;
10
12
  }
11
- /** One objective-error finding: the flagged range a reader sees underlined, the error kind, a plain
12
- * message, and the one-edit fix. */
13
+ /**
14
+ * One objective-error finding: the flagged range a reader sees underlined, the error kind, a plain
15
+ * message, and the one-edit fix.
16
+ */
13
17
  export interface ObjectiveError {
14
18
  kind: ObjectiveErrorKind;
15
19
  /** The flagged range (absolute document offsets), the span the underline covers. */
@@ -20,14 +20,18 @@ const DOUBLE_SPACE = /[^\s] ( +)/g;
20
20
  // a single mark is correct, two or more of these three is plainly a typo. A mixed run ("?!") is not
21
21
  // flagged because it is a legitimate construction; only a run of one identical mark counts.
22
22
  const REPEATED_PUNCT = /([!?,])\1+/g;
23
- /** Whether two matched word strings are the same word, case-insensitively. Both are already plain
24
- * word runs from the same WORD pattern, so a locale-insensitive lowercase compare is enough. */
23
+ /**
24
+ * Whether two matched word strings are the same word, case-insensitively. Both are already plain
25
+ * word runs from the same WORD pattern, so a locale-insensitive lowercase compare is enough.
26
+ */
25
27
  function sameWord(a, b) {
26
28
  return a.toLowerCase() === b.toLowerCase();
27
29
  }
28
- /** Run the three objective checks over one prose span [from, to), returning every finding with an
30
+ /**
31
+ * Run the three objective checks over one prose span [from, to), returning every finding with an
29
32
  * absolute range and fix. The doubled-word check is bounded to this span so a repeat that straddles
30
- * a skipped region is never matched. */
33
+ * a skipped region is never matched.
34
+ */
31
35
  function checkSpan(text, from, to) {
32
36
  const out = [];
33
37
  const slice = text.slice(from, to);
@@ -13,8 +13,10 @@ export type PreviewDeviceId = PreviewDevice['id'];
13
13
  export declare const previewDevices: PreviewDevice[];
14
14
  /** The table row for a device id. The id type makes a miss impossible; the fallback satisfies find. */
15
15
  export declare function previewDevice(id: PreviewDeviceId): PreviewDevice;
16
- /** A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
17
- * label with its width when one is fixed, so the value reaches assistive tech at pick time. */
16
+ /**
17
+ * A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
18
+ * label with its width when one is fixed, so the value reaches assistive tech at pick time.
19
+ */
18
20
  export declare function deviceLabel(d: PreviewDevice): string;
19
21
  /**
20
22
  * Build the preview iframe's srcdoc: a complete document linking the site's stylesheets around
@@ -15,8 +15,10 @@ export const previewDevices = [
15
15
  export function previewDevice(id) {
16
16
  return previewDevices.find((d) => d.id === id) ?? previewDevices[0];
17
17
  }
18
- /** A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
19
- * label with its width when one is fixed, so the value reaches assistive tech at pick time. */
18
+ /**
19
+ * A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
20
+ * label with its width when one is fixed, so the value reaches assistive tech at pick time.
21
+ */
20
22
  export function deviceLabel(d) {
21
23
  return d.width === null ? d.label : `${d.label} · ${d.width} px`;
22
24
  }
@@ -7,8 +7,10 @@ export interface Range {
7
7
  from: number;
8
8
  to: number;
9
9
  }
10
- /** A word extracted for lookup: the lowercased form the Worker checks, and its absolute range so a
11
- * verdict maps straight back to an underline. */
10
+ /**
11
+ * A word extracted for lookup: the lowercased form the Worker checks, and its absolute range so a
12
+ * verdict maps straight back to an underline.
13
+ */
12
14
  export interface ExtractedWord {
13
15
  /** The lowercased word, as the engine's case-insensitive lookup expects. */
14
16
  text: string;
@@ -22,8 +24,10 @@ export interface ExtractedWord {
22
24
  * precedence live in {@link skipRanges}.
23
25
  */
24
26
  export declare function classifyProse(text: string, tree: Tree, from: number, to: number): Range[];
25
- /** The prose ranges worth checking across the whole document. The lint source narrows this to the
26
- * visible window; the unit test reads the whole-document set. */
27
+ /**
28
+ * The prose ranges worth checking across the whole document. The lint source narrows this to the
29
+ * visible window; the unit test reads the whole-document set.
30
+ */
27
31
  export declare function spellcheckRanges(text: string, tree: Tree): Range[];
28
32
  /**
29
33
  * The checkable words inside [from, to), each lowercased for lookup with its absolute range recorded
@@ -31,8 +35,10 @@ export declare function spellcheckRanges(text: string, tree: Tree): Range[];
31
35
  * all-caps tokens are dropped.
32
36
  */
33
37
  export declare function extractWords(text: string, from: number, to: number): ExtractedWord[];
34
- /** The callbacks the management actions invoke. The lint source supplies these so the pure builder
35
- * never touches the Worker or the re-lint mechanism: it only wires the buttons to these handlers. */
38
+ /**
39
+ * The callbacks the management actions invoke. The lint source supplies these so the pure builder
40
+ * never touches the Worker or the re-lint mechanism: it only wires the buttons to these handlers.
41
+ */
36
42
  export interface SpellDiagnosticActions {
37
43
  /** Add the word to the personal dictionary (posts addWord, records the pending addition, re-lints). */
38
44
  onAddWord(word: string): void;
@@ -40,7 +46,7 @@ export interface SpellDiagnosticActions {
40
46
  onIgnoreWord(word: string): void;
41
47
  }
42
48
  /**
43
- * Build the correction popover for one misspelled word, as a @codemirror/lint Diagnostic whose
49
+ * Build the correction popover for one misspelled word, as a `@codemirror/lint` Diagnostic whose
44
50
  * `actions` CodeMirror renders as tooltip buttons (no custom popover code). The actions, in order:
45
51
  * up to five ranked suggestions (each replaces the word's range with one transaction), then "Add to
46
52
  * dictionary", then "Ignore". The severity is `info` so the underline is quiet, and the message names
@@ -64,7 +70,7 @@ export interface SeqArbiter {
64
70
  /** Build a fresh {@link SeqArbiter}. */
65
71
  export declare function arbitrateChecked(): SeqArbiter;
66
72
  /**
67
- * Build the quick-fix popover for one objective-error finding, as a @codemirror/lint Diagnostic whose
73
+ * Build the quick-fix popover for one objective-error finding, as a `@codemirror/lint` Diagnostic whose
68
74
  * one `actions` entry applies the finding's deterministic fix. The severity is `info` so the underline
69
75
  * shares the spellcheck surface and the locked amber color (an editor reads spelling and these
70
76
  * mechanical errors as one "spellcheck" layer). The fix range is recomputed from the live diagnostic
@@ -73,60 +79,80 @@ export declare function arbitrateChecked(): SeqArbiter;
73
79
  * diagnostic without a browser.
74
80
  */
75
81
  export declare function buildObjectiveDiagnostic(error: ObjectiveError): Diagnostic;
76
- /** The narrow Worker surface the lint source drives: it posts check, suggest, addWord, and ignoreWord
82
+ /**
83
+ * The narrow Worker surface the lint source drives: it posts check, suggest, addWord, and ignoreWord
77
84
  * messages and listens for the answers. A `suggest` answer is a one-shot, so the source removes its
78
- * own listener once it lands. A test injects a fake; production injects a real Worker. */
85
+ * own listener once it lands. A test injects a fake; production injects a real Worker.
86
+ */
79
87
  export interface SpellWorker {
80
88
  postMessage(message: unknown): void;
81
89
  addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
82
90
  removeEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
83
91
  }
84
- /** Construct the real spellcheck Worker, the spike's delivery shape. Kept behind the seam so the
85
- * lint source never references `Worker` at module scope and a test can swap it. */
92
+ /**
93
+ * Construct the real spellcheck Worker, the spike's delivery shape. Kept behind the seam so the
94
+ * lint source never references `Worker` at module scope and a test can swap it.
95
+ */
86
96
  export declare function createSpellWorker(): SpellWorker;
87
97
  /** The real wasm asset URL, resolved module-relative the same way the worker is. */
88
98
  export declare function resolveWasmUrl(): string;
89
- /** The real dictionary asset URL for a dictionary filename, resolved module-relative. The caller
99
+ /**
100
+ * The real dictionary asset URL for a dictionary filename, resolved module-relative. The caller
90
101
  * passes the dialect-resolved filename (default `dictionary-en-us.txt`). `dictionaryFileForDialect`
91
102
  * already collapses an unknown dialect to the default, so an unmapped name falls back the same way
92
- * rather than pointing at an asset that does not ship. */
103
+ * rather than pointing at an asset that does not ship.
104
+ */
93
105
  export declare function resolveDictionaryUrl(dictionaryFile: string): string;
94
- /** Options for {@link cairnSpellcheck}, so the unit and component layers can inject a fake Worker
95
- * factory in place of the real `new Worker(...)`. */
106
+ /**
107
+ * Options for {@link cairnSpellcheck}, so the unit and component layers can inject a fake Worker
108
+ * factory in place of the real `new Worker(...)`.
109
+ */
96
110
  export interface SpellcheckOptions {
97
111
  /** The Worker factory; defaults to {@link createSpellWorker}. Created lazily on the first lint. */
98
112
  createWorker?: () => SpellWorker;
99
- /** The pending personal-dictionary additions, owned by the caller. When an author chooses "Add to
113
+ /**
114
+ * The pending personal-dictionary additions, owned by the caller. When an author chooses "Add to
100
115
  * dictionary" the source posts addWord to the Worker (the underline clears at once) and records the
101
116
  * word here. The set is the seam Task 9 commits to the git-backed dictionary file; this source only
102
- * fills it and never persists. A caller that does not pass one gets a fresh internal set. */
117
+ * fills it and never persists. A caller that does not pass one gets a fresh internal set.
118
+ */
103
119
  pendingAdditions?: Set<string>;
104
- /** The committed personal-dictionary words (spec 1.6) the source seeds the Worker's personal layer
120
+ /**
121
+ * The committed personal-dictionary words (spec 1.6) the source seeds the Worker's personal layer
105
122
  * with, posted as one batch `addWord` right after `init`. The git-backed site dictionary is the
106
123
  * durable layer; the editor reads it at load (EditData.siteDictionary) and hands it here, so a word
107
- * another editor committed answers correct from the first lint. Empty by default (dialect-only). */
124
+ * another editor committed answers correct from the first lint. Empty by default (dialect-only).
125
+ */
108
126
  siteWords?: ReadonlyArray<string>;
109
- /** The dialect-resolved dictionary filename, e.g. "dictionary-en-us.txt". The source resolves it to
110
- * a real asset URL and posts it in the Worker's `init`. Defaults to US English. */
127
+ /**
128
+ * The dialect-resolved dictionary filename, e.g. "dictionary-en-us.txt". The source resolves it to
129
+ * a real asset URL and posts it in the Worker's `init`. Defaults to US English.
130
+ */
111
131
  dictionaryFile?: string;
112
- /** Override the resolved wasm and dictionary URLs the source posts in `init`. The real resolution
132
+ /**
133
+ * Override the resolved wasm and dictionary URLs the source posts in `init`. The real resolution
113
134
  * uses {@link resolveWasmUrl}/{@link resolveDictionaryUrl} (module-relative `import.meta.url`); a
114
135
  * component test that injects a fake Worker can pass canned URLs so it never touches the asset
115
- * resolver. */
136
+ * resolver.
137
+ */
116
138
  assetUrls?: {
117
139
  wasmUrl: string;
118
140
  dictionaryUrl: string;
119
141
  };
120
- /** Treat the Worker as ready without waiting for a `ready` message. The production path is strict
142
+ /**
143
+ * Treat the Worker as ready without waiting for a `ready` message. The production path is strict
121
144
  * (it posts `init` and waits for `ready` before painting); a fake Worker in a test that does not
122
- * answer `ready` can set this so a lint run is not held back. Defaults to false. */
145
+ * answer `ready` can set this so a lint run is not held back. Defaults to false.
146
+ */
123
147
  assumeReady?: boolean;
124
- /** The already-loaded CodeMirror modules to reuse instead of importing them again. The editor
148
+ /**
149
+ * The already-loaded CodeMirror modules to reuse instead of importing them again. The editor
125
150
  * component loads `@codemirror/view`/`@codemirror/language` for its own extensions, so passing them
126
151
  * here keeps the lint source on the SAME module instances; a second dynamic import can resolve to a
127
152
  * separate copy (the test bundler's dedup quirk), and CodeMirror's instanceof checks then reject the
128
153
  * extension. When omitted, the source imports them itself (the standalone path). `@codemirror/lint`
129
- * is loaded here when not supplied, since the editor does not otherwise need it. */
154
+ * is loaded here when not supplied, since the editor does not otherwise need it.
155
+ */
130
156
  modules?: {
131
157
  lint?: typeof import('@codemirror/lint');
132
158
  language?: typeof import('@codemirror/language');
@@ -135,7 +161,7 @@ export interface SpellcheckOptions {
135
161
  };
136
162
  }
137
163
  /**
138
- * The @codemirror/lint linter() source, made markdown-aware by the Lezer tree. It runs over the
164
+ * The `@codemirror/lint` linter() source, made markdown-aware by the Lezer tree. It runs over the
139
165
  * visible viewport plus a margin (not the whole document), extracts the checkable words via the pure
140
166
  * classifier, posts them to the Worker keyed by a monotonic latest-wins seq, and maps the
141
167
  * `correct: false` answers back to ranges. Each wrong word becomes a correction popover: the source