@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
@@ -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,58 +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
90
- * passes the dialect-resolved filename (default `dictionary-en-us.txt`). */
99
+ /**
100
+ * The real dictionary asset URL for a dictionary filename, resolved module-relative. The caller
101
+ * passes the dialect-resolved filename (default `dictionary-en-us.txt`). `dictionaryFileForDialect`
102
+ * already collapses an unknown dialect to the default, so an unmapped name falls back the same way
103
+ * rather than pointing at an asset that does not ship.
104
+ */
91
105
  export declare function resolveDictionaryUrl(dictionaryFile: string): string;
92
- /** Options for {@link cairnSpellcheck}, so the unit and component layers can inject a fake Worker
93
- * 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
+ */
94
110
  export interface SpellcheckOptions {
95
111
  /** The Worker factory; defaults to {@link createSpellWorker}. Created lazily on the first lint. */
96
112
  createWorker?: () => SpellWorker;
97
- /** 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
98
115
  * dictionary" the source posts addWord to the Worker (the underline clears at once) and records the
99
116
  * word here. The set is the seam Task 9 commits to the git-backed dictionary file; this source only
100
- * 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
+ */
101
119
  pendingAdditions?: Set<string>;
102
- /** 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
103
122
  * with, posted as one batch `addWord` right after `init`. The git-backed site dictionary is the
104
123
  * durable layer; the editor reads it at load (EditData.siteDictionary) and hands it here, so a word
105
- * 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
+ */
106
126
  siteWords?: ReadonlyArray<string>;
107
- /** The dialect-resolved dictionary filename, e.g. "dictionary-en-us.txt". The source resolves it to
108
- * 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
+ */
109
131
  dictionaryFile?: string;
110
- /** 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
111
134
  * uses {@link resolveWasmUrl}/{@link resolveDictionaryUrl} (module-relative `import.meta.url`); a
112
135
  * component test that injects a fake Worker can pass canned URLs so it never touches the asset
113
- * resolver. */
136
+ * resolver.
137
+ */
114
138
  assetUrls?: {
115
139
  wasmUrl: string;
116
140
  dictionaryUrl: string;
117
141
  };
118
- /** 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
119
144
  * (it posts `init` and waits for `ready` before painting); a fake Worker in a test that does not
120
- * 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
+ */
121
147
  assumeReady?: boolean;
122
- /** 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
123
150
  * component loads `@codemirror/view`/`@codemirror/language` for its own extensions, so passing them
124
151
  * here keeps the lint source on the SAME module instances; a second dynamic import can resolve to a
125
152
  * separate copy (the test bundler's dedup quirk), and CodeMirror's instanceof checks then reject the
126
153
  * extension. When omitted, the source imports them itself (the standalone path). `@codemirror/lint`
127
- * 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
+ */
128
156
  modules?: {
129
157
  lint?: typeof import('@codemirror/lint');
130
158
  language?: typeof import('@codemirror/language');
@@ -133,7 +161,7 @@ export interface SpellcheckOptions {
133
161
  };
134
162
  }
135
163
  /**
136
- * 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
137
165
  * visible viewport plus a margin (not the whole document), extracts the checkable words via the pure
138
166
  * classifier, posts them to the Worker keyed by a monotonic latest-wins seq, and maps the
139
167
  * `correct: false` answers back to ranges. Each wrong word becomes a correction popover: the source
@@ -40,8 +40,10 @@ const SKIP_NODES = new Set([
40
40
  // surrounding whitespace). A token inside an image is already caught by the URL skip; this catches
41
41
  // the form authors type directly in prose, so it is never split into "media" plus a flagged hash.
42
42
  const MEDIA_TOKEN = /media:[\w.-]+/g;
43
- /** Merge overlapping or touching ranges into a sorted, disjoint set, so the keep-span computation
44
- * subtracts one clean list of skip regions. */
43
+ /**
44
+ * Merge overlapping or touching ranges into a sorted, disjoint set, so the keep-span computation
45
+ * subtracts one clean list of skip regions.
46
+ */
45
47
  function mergeRanges(ranges) {
46
48
  if (ranges.length === 0)
47
49
  return [];
@@ -57,9 +59,11 @@ function mergeRanges(ranges) {
57
59
  }
58
60
  return out;
59
61
  }
60
- /** Every absolute skip range in the document, from all three mechanisms, merged. This is the single
62
+ /**
63
+ * Every absolute skip range in the document, from all three mechanisms, merged. This is the single
61
64
  * skip authority the spec calls for: the tree decides node kind, frontmatterSpan covers the `---`
62
- * region, and fenceTokens covers the directive machinery the tree parses as plain text. */
65
+ * region, and fenceTokens covers the directive machinery the tree parses as plain text.
66
+ */
63
67
  function skipRanges(text, tree) {
64
68
  const skips = [];
65
69
  // 1. The Lezer tree: the single authority for node-kind skips.
@@ -131,8 +135,10 @@ export function classifyProse(text, tree, from, to) {
131
135
  out.push({ from: cursor, to });
132
136
  return out;
133
137
  }
134
- /** The prose ranges worth checking across the whole document. The lint source narrows this to the
135
- * visible window; the unit test reads the whole-document set. */
138
+ /**
139
+ * The prose ranges worth checking across the whole document. The lint source narrows this to the
140
+ * visible window; the unit test reads the whole-document set.
141
+ */
136
142
  export function spellcheckRanges(text, tree) {
137
143
  return classifyProse(text, tree, 0, text.length);
138
144
  }
@@ -141,8 +147,10 @@ export function spellcheckRanges(text, tree) {
141
147
  // with the inner apostrophe (straight or curly) and hyphen allowed only between word characters.
142
148
  const WORD = /[\p{L}\p{N}]+(?:[-'’][\p{L}\p{N}]+)*/gu;
143
149
  const ALL_DIGITS = /^\p{N}+$/u;
144
- /** Whether a word is worth a lookup. Words under three characters, pure numbers, and all-caps tokens
145
- * are skipped to cut false positives (the conservative posture VSCode's spell checker takes). */
150
+ /**
151
+ * Whether a word is worth a lookup. Words under three characters, pure numbers, and all-caps tokens
152
+ * are skipped to cut false positives (the conservative posture VSCode's spell checker takes).
153
+ */
146
154
  function isCheckable(word) {
147
155
  if (word.length < 3)
148
156
  return false;
@@ -175,7 +183,7 @@ export function extractWords(text, from, to) {
175
183
  // wall of near-ties.
176
184
  const MAX_SUGGESTIONS = 5;
177
185
  /**
178
- * Build the correction popover for one misspelled word, as a @codemirror/lint Diagnostic whose
186
+ * Build the correction popover for one misspelled word, as a `@codemirror/lint` Diagnostic whose
179
187
  * `actions` CodeMirror renders as tooltip buttons (no custom popover code). The actions, in order:
180
188
  * up to five ranked suggestions (each replaces the word's range with one transaction), then "Add to
181
189
  * dictionary", then "Ignore". The severity is `info` so the underline is quiet, and the message names
@@ -227,7 +235,7 @@ export function arbitrateChecked() {
227
235
  };
228
236
  }
229
237
  /**
230
- * Build the quick-fix popover for one objective-error finding, as a @codemirror/lint Diagnostic whose
238
+ * Build the quick-fix popover for one objective-error finding, as a `@codemirror/lint` Diagnostic whose
231
239
  * one `actions` entry applies the finding's deterministic fix. The severity is `info` so the underline
232
240
  * shares the spellcheck surface and the locked amber color (an editor reads spelling and these
233
241
  * mechanical errors as one "spellcheck" layer). The fix range is recomputed from the live diagnostic
@@ -271,8 +279,10 @@ let lintMod = null;
271
279
  let langMod = null;
272
280
  let viewMod = null;
273
281
  let stateMod = null;
274
- /** Construct the real spellcheck Worker, the spike's delivery shape. Kept behind the seam so the
275
- * lint source never references `Worker` at module scope and a test can swap it. */
282
+ /**
283
+ * Construct the real spellcheck Worker, the spike's delivery shape. Kept behind the seam so the
284
+ * lint source never references `Worker` at module scope and a test can swap it.
285
+ */
276
286
  export function createSpellWorker() {
277
287
  return new Worker(new URL('./spellcheck-worker.js', import.meta.url), {
278
288
  type: 'module',
@@ -290,10 +300,26 @@ export function createSpellWorker() {
290
300
  export function resolveWasmUrl() {
291
301
  return new URL('./spellcheck-assets/spellchecker-wasm.wasm', import.meta.url).href;
292
302
  }
293
- /** The real dictionary asset URL for a dictionary filename, resolved module-relative. The caller
294
- * passes the dialect-resolved filename (default `dictionary-en-us.txt`). */
303
+ /**
304
+ * Each shipped dictionary, mapped to a resolver that builds its asset URL with a LITERAL
305
+ * `new URL(..., import.meta.url)`. The literal path is load-bearing. A templated `new URL` makes Vite
306
+ * and rolldown treat the directory as a glob and parse every sibling module to build it, including the
307
+ * `.svelte` components that still carry `lang="ts"` in `dist`, and the glob parser chokes on the TS
308
+ * syntax and breaks the consumer build. This set mirrors the dialect map in `nav/site-config.ts`; add
309
+ * one line per new shipped dialect dictionary.
310
+ */
311
+ const DICTIONARY_URLS = {
312
+ 'dictionary-en-us.txt': () => new URL('./spellcheck-assets/dictionary-en-us.txt', import.meta.url).href,
313
+ };
314
+ /**
315
+ * The real dictionary asset URL for a dictionary filename, resolved module-relative. The caller
316
+ * passes the dialect-resolved filename (default `dictionary-en-us.txt`). `dictionaryFileForDialect`
317
+ * already collapses an unknown dialect to the default, so an unmapped name falls back the same way
318
+ * rather than pointing at an asset that does not ship.
319
+ */
295
320
  export function resolveDictionaryUrl(dictionaryFile) {
296
- return new URL(`./spellcheck-assets/${dictionaryFile}`, import.meta.url).href;
321
+ const resolve = DICTIONARY_URLS[dictionaryFile] ?? DICTIONARY_URLS['dictionary-en-us.txt'];
322
+ return resolve();
297
323
  }
298
324
  /** How far past the visible viewport to lint, so a small scroll does not re-lint from scratch. */
299
325
  const VIEWPORT_MARGIN = 1000;
@@ -333,7 +359,7 @@ function lockedUnderlineTheme(EditorViewMod) {
333
359
  });
334
360
  }
335
361
  /**
336
- * The @codemirror/lint linter() source, made markdown-aware by the Lezer tree. It runs over the
362
+ * The `@codemirror/lint` linter() source, made markdown-aware by the Lezer tree. It runs over the
337
363
  * visible viewport plus a margin (not the whole document), extracts the checkable words via the pure
338
364
  * classifier, posts them to the Worker keyed by a monotonic latest-wins seq, and maps the
339
365
  * `correct: false` answers back to ranges. Each wrong word becomes a correction popover: the source
@@ -445,10 +471,12 @@ export async function cairnSpellcheck(options = {}) {
445
471
  worker.postMessage({ type: 'addWord', words: siteWords });
446
472
  return worker;
447
473
  }
448
- /** Fetch a single word's ranked suggestions over the Worker, a one-shot listener removed on the
474
+ /**
475
+ * Fetch a single word's ranked suggestions over the Worker, a one-shot listener removed on the
449
476
  * answer. The suggest path is independent of the check seq, so a slow suggest never blocks a fresh
450
477
  * check; an empty list (the engine returned nothing) still yields a popover with the two
451
- * management actions. */
478
+ * management actions.
479
+ */
452
480
  function fetchSuggestions(w, word) {
453
481
  suggestSeq += 1;
454
482
  const seq = suggestSeq;
@@ -464,8 +492,10 @@ export async function cairnSpellcheck(options = {}) {
464
492
  w.postMessage({ type: 'suggest', seq, word });
465
493
  });
466
494
  }
467
- /** Turn the wrong words into correction popovers, each carrying its ranked suggestions and the two
468
- * management actions. */
495
+ /**
496
+ * Turn the wrong words into correction popovers, each carrying its ranked suggestions and the two
497
+ * management actions.
498
+ */
469
499
  async function buildDiagnostics(wrong) {
470
500
  const w = ensureWorker();
471
501
  const callbacks = {
@@ -1,8 +1,10 @@
1
1
  import type { Change } from './tidy-diff.js';
2
2
  import type { TidyConventions } from '../nav/site-config.js';
3
- /** A change's locally-inferred category. The first four are objective (safe to sweep); `normalization`
3
+ /**
4
+ * A change's locally-inferred category. The first four are objective (safe to sweep); `normalization`
4
5
  * and `grammar` are judgment (held undecided, never swept by Accept-fixes). `normalization` carries
5
- * the convention key that authorized it, so the surface can name the setting and label the badge. */
6
+ * the convention key that authorized it, so the surface can name the setting and label the badge.
7
+ */
6
8
  export type TidyCategory = {
7
9
  kind: 'spelling';
8
10
  } | {
@@ -17,13 +19,17 @@ export type TidyCategory = {
17
19
  } | {
18
20
  kind: 'grammar';
19
21
  };
20
- /** True for the objective categories: the safe, pre-kept, Accept-fixes-swept rank. A judgment
22
+ /**
23
+ * True for the objective categories: the safe, pre-kept, Accept-fixes-swept rank. A judgment
21
24
  * category (`normalization` or `grammar`) returns false. The bulk action and the surface both read
22
- * this, so the safety rank is one source of truth. */
25
+ * this, so the safety rank is one source of truth.
26
+ */
23
27
  export declare function isObjective(category: TidyCategory): boolean;
24
- /** The enabled-convention keys a normalization can be attributed to. Each maps to one config field on
28
+ /**
29
+ * The enabled-convention keys a normalization can be attributed to. Each maps to one config field on
25
30
  * TidyConventions and to a because-line. A change is only ever labelled a normalization when it matches
26
- * one of these AND the config has the matching variant enabled; otherwise it is never a normalization. */
31
+ * one of these AND the config has the matching variant enabled; otherwise it is never a normalization.
32
+ */
27
33
  export type NormalizationKey = 'oxfordComma' | 'numberStyle' | 'measurements' | 'percent' | 'emDash' | 'enDashRanges' | 'ellipsis' | 'timeFormat' | 'smartQuotes';
28
34
  /**
29
35
  * Categorize one change against the captured original and the resolved conventions. The rules are
@@ -39,11 +45,13 @@ export type NormalizationKey = 'oxfordComma' | 'numberStyle' | 'measurements' |
39
45
  * offers a normalization that cannot cite an enabled setting.
40
46
  */
41
47
  export declare function categorize(change: Change, original: string, conventions: TidyConventions): TidyCategory;
42
- /** The because-line data for a hunk: the convention's display name and the variant phrasing, both pure
48
+ /**
49
+ * The because-line data for a hunk: the convention's display name and the variant phrasing, both pure
43
50
  * strings derived from the config. The surface renders "Your <label> setting is <variant>, ..." from
44
51
  * these. Only a normalization carries a because-line; an objective or grammar hunk returns null (a
45
52
  * grammar hunk's rationale, when shown, is the local subject-verb note the surface composes, not a
46
- * config citation). */
53
+ * config citation).
54
+ */
47
55
  export interface BecauseLine {
48
56
  /** The convention's display label, e.g. "Oxford-comma". */
49
57
  label: string;
@@ -61,7 +69,9 @@ export interface BecauseLine {
61
69
  * null when the convention is somehow not enabled (defensive: categorize never produces such a hunk).
62
70
  */
63
71
  export declare function buildBecause(key: NormalizationKey, conventions: TidyConventions): BecauseLine | null;
64
- /** The human badge label for a category, the word shown in the hunk's category pill. A normalization's
72
+ /**
73
+ * The human badge label for a category, the word shown in the hunk's category pill. A normalization's
65
74
  * label is the convention's display name (its comma style, its time format), never "consistency" and
66
- * never a count. */
75
+ * never a count.
76
+ */
67
77
  export declare function categoryLabel(category: TidyCategory): string;
@@ -9,9 +9,11 @@
9
9
  // quiet and are swept by Accept-fixes. Judgment categories (a declared normalization, or a grammar fix
10
10
  // that reworded more than one token) carry the review-this treatment and are never swept until the
11
11
  // author confirms each. The category alone decides the rank, so the surface and the bulk action agree.
12
- /** True for the objective categories: the safe, pre-kept, Accept-fixes-swept rank. A judgment
12
+ /**
13
+ * True for the objective categories: the safe, pre-kept, Accept-fixes-swept rank. A judgment
13
14
  * category (`normalization` or `grammar`) returns false. The bulk action and the surface both read
14
- * this, so the safety rank is one source of truth. */
15
+ * this, so the safety rank is one source of truth.
16
+ */
15
17
  export function isObjective(category) {
16
18
  return (category.kind === 'spelling' ||
17
19
  category.kind === 'typo' ||
@@ -94,9 +96,11 @@ function isWhitespaceOnly(text) {
94
96
  function isPunctuationOnly(text) {
95
97
  return text.length > 0 && /^[^A-Za-z0-9_\s]+$/.test(text);
96
98
  }
97
- /** The word ending immediately before `offset` in `text`, skipping any whitespace just before the
99
+ /**
100
+ * The word ending immediately before `offset` in `text`, skipping any whitespace just before the
98
101
  * offset, or null when none. The doubled-word rule reads it to confirm the deleted word repeats the
99
- * one before it. Pure text inspection, never a count. */
102
+ * one before it. Pure text inspection, never a count.
103
+ */
100
104
  function precedingWord(text, offset) {
101
105
  let i = offset;
102
106
  while (i > 0 && /\s/.test(text[i - 1]))
@@ -106,8 +110,10 @@ function precedingWord(text, offset) {
106
110
  j--;
107
111
  return j < i ? text.slice(j, i) : null;
108
112
  }
109
- /** The word starting immediately after `offset` in `text`, skipping any whitespace just after the
110
- * offset, or null when none. The doubled-word rule reads it as the other half of the look-around. */
113
+ /**
114
+ * The word starting immediately after `offset` in `text`, skipping any whitespace just after the
115
+ * offset, or null when none. The doubled-word rule reads it as the other half of the look-around.
116
+ */
111
117
  function followingWord(text, offset) {
112
118
  let i = offset;
113
119
  while (i < text.length && /\s/.test(text[i]))
@@ -349,9 +355,11 @@ export function buildBecause(key, conventions) {
349
355
  }
350
356
  }
351
357
  }
352
- /** The human badge label for a category, the word shown in the hunk's category pill. A normalization's
358
+ /**
359
+ * The human badge label for a category, the word shown in the hunk's category pill. A normalization's
353
360
  * label is the convention's display name (its comma style, its time format), never "consistency" and
354
- * never a count. */
361
+ * never a count.
362
+ */
355
363
  export function categoryLabel(category) {
356
364
  switch (category.kind) {
357
365
  case 'spelling':
@@ -1,19 +1,25 @@
1
1
  import type { Change } from './tidy-diff.js';
2
- /** The reason a tidy result was rejected. Task 14 branches on this; every value maps to the one
2
+ /**
3
+ * The reason a tidy result was rejected. Task 14 branches on this; every value maps to the one
3
4
  * honest author-facing message, so the reason is for logging and tests, not the user surface.
4
5
  * - `structure`: a directive opener/closer sequence, a heading count or level, or a fenced-code
5
6
  * count diverged (the result restructured the document).
6
7
  * - `frontmatter`: the frontmatter block is not byte-for-byte equal.
7
8
  * - `media`: the multiset of `media:` hashes differs (a hash was altered, dropped, or invented).
8
9
  * - `code`: a code span or fenced code block was edited.
9
- * - `divergence`: the changed-token amount exceeds the length-aware bound (a wholesale rewrite). */
10
+ * - `divergence`: the changed-token amount exceeds the length-aware bound (a wholesale rewrite).
11
+ */
10
12
  export type TidyRejectionReason = 'structure' | 'frontmatter' | 'media' | 'code' | 'divergence';
11
- /** The honest author-facing message a rejection maps to. The same message for every reason, by
13
+ /**
14
+ * The honest author-facing message a rejection maps to. The same message for every reason, by
12
15
  * design: an author does not need the validator's internal taxonomy, only that the result was
13
- * discarded and their text is safe. */
16
+ * discarded and their text is safe.
17
+ */
14
18
  export declare const TIDY_REJECTION_MESSAGE = "Tidy returned a result that changed more than the wording, so it was discarded. Your text is unchanged.";
15
- /** The outcome of validating a tidy result. On success it carries the Task 12 change set the review
16
- * surface accepts and rejects against; on failure it carries the typed reason and the message. */
19
+ /**
20
+ * The outcome of validating a tidy result. On success it carries the Task 12 change set the review
21
+ * surface accepts and rejects against; on failure it carries the typed reason and the message.
22
+ */
17
23
  export type TidyValidation = {
18
24
  ok: true;
19
25
  changes: Change[];
@@ -16,9 +16,11 @@ import { visit } from 'unist-util-visit';
16
16
  import { fenceScan, frontmatterSpan } from './markdown-directives.js';
17
17
  import { parseMediaToken } from '../media/reference.js';
18
18
  import { diffTokens, diffChanges } from './tidy-diff.js';
19
- /** The honest author-facing message a rejection maps to. The same message for every reason, by
19
+ /**
20
+ * The honest author-facing message a rejection maps to. The same message for every reason, by
20
21
  * design: an author does not need the validator's internal taxonomy, only that the result was
21
- * discarded and their text is safe. */
22
+ * discarded and their text is safe.
23
+ */
22
24
  export const TIDY_REJECTION_MESSAGE = 'Tidy returned a result that changed more than the wording, so it was discarded. Your text is unchanged.';
23
25
  // The divergence bound. The floor allows a fixed number of changed tokens regardless of fraction so
24
26
  // a legitimate heavy proofread of a SHORT input is not penalized: a short paragraph with a typo in
@@ -38,10 +40,12 @@ const DIVERGENCE_FRACTION = 0.5;
38
40
  // site, which the validator otherwise has no reason to know. A token mangled inside a code fence is
39
41
  // caught here too, redundantly with the code check, which is the right posture for a backstop.
40
42
  const MEDIA_TOKEN = /media:[A-Za-z0-9.-]+/g;
41
- /** The sorted multiset of valid media hashes in the text. Each `media:` occurrence is parsed; a
43
+ /**
44
+ * The sorted multiset of valid media hashes in the text. Each `media:` occurrence is parsed; a
42
45
  * malformed token (a broken hash, an illegal slug) parses to null and is dropped, so a tidy that
43
46
  * CORRUPTED a hash drops it from the multiset and the comparison fails. Sorted so two multisets
44
- * compare by value, order-independent. */
47
+ * compare by value, order-independent.
48
+ */
45
49
  function mediaHashes(text) {
46
50
  const hashes = [];
47
51
  for (const m of text.matchAll(MEDIA_TOKEN)) {
@@ -51,11 +55,13 @@ function mediaHashes(text) {
51
55
  }
52
56
  return hashes.sort();
53
57
  }
54
- /** The directive structure signature: each opener or closer in document order, paired with the depth
58
+ /**
59
+ * The directive structure signature: each opener or closer in document order, paired with the depth
55
60
  * the fence scan assigned it. Two texts share a directive structure when these signatures are equal,
56
61
  * so an added, removed, or relevelled container fails the comparison. A fence-shaped line inside a
57
62
  * code block is already disowned by the scan (its role is null), so a documented `:::` example does
58
- * not enter the signature. */
63
+ * not enter the signature.
64
+ */
59
65
  function directiveSignature(text) {
60
66
  const { depths, roles } = fenceScan(text.split('\n'));
61
67
  const parts = [];
@@ -65,10 +71,12 @@ function directiveSignature(text) {
65
71
  }
66
72
  return parts.join(',');
67
73
  }
68
- /** The heading signature: every ATX heading's level in document order. Parsed as mdast so a `#`
74
+ /**
75
+ * The heading signature: every ATX heading's level in document order. Parsed as mdast so a `#`
69
76
  * inside a code block or an escaped one is never counted, and the level is the parser's own depth.
70
77
  * Two texts share a heading structure when these are equal, so an added, removed, or relevelled
71
- * heading fails the comparison. */
78
+ * heading fails the comparison.
79
+ */
72
80
  function headingSignature(text) {
73
81
  const tree = unified().use(remarkParse).use(remarkGfm).parse(text);
74
82
  const levels = [];
@@ -78,11 +86,13 @@ function headingSignature(text) {
78
86
  });
79
87
  return levels.join(',');
80
88
  }
81
- /** Every code span and fenced or indented code block in the text, as a sorted multiset of values.
89
+ /**
90
+ * Every code span and fenced or indented code block in the text, as a sorted multiset of values.
82
91
  * Parsed as mdast so the comparison sees exactly what the parser treats as code, the same authority
83
92
  * the media body scan uses. Sorted so the comparison is order-independent: the divergence and
84
93
  * structure checks own ordering, this check owns the contents. A `code` node is a block, an
85
- * `inlineCode` node is a span. */
94
+ * `inlineCode` node is a span.
95
+ */
86
96
  function codeContents(text) {
87
97
  const tree = unified().use(remarkParse).use(remarkGfm).parse(text);
88
98
  const values = [];
@@ -2,9 +2,11 @@ import { type Snippet } from 'svelte';
2
2
  /** The shared holder: the desk snippet a document registers, or null on the office routes. */
3
3
  export interface TopbarHolder {
4
4
  desk: Snippet | null;
5
- /** True while the document is in zen: AdminLayout drops the whole topbar element so the band
5
+ /**
6
+ * True while the document is in zen: AdminLayout drops the whole topbar element so the band
6
7
  * slides away (the desk's three clusters include AdminLayout-owned chrome, the drawer toggle and
7
- * breadcrumb, that must vanish with it). EditPage sets this; the office routes leave it false. */
8
+ * breadcrumb, that must vanish with it). EditPage sets this; the office routes leave it false.
9
+ */
8
10
  zen: boolean;
9
11
  }
10
12
  /** Called by AdminLayout once: creates the holder, provides it on context, returns it to render. */
@@ -0,0 +1,51 @@
1
+ import type { ConceptDescriptor } from './types.js';
2
+ import type { RepoRef } from '../github/types.js';
3
+ import type { Manifest } from './manifest.js';
4
+ /** One action an advisory offers, as a label and an optional link target. */
5
+ export interface AdvisoryAction {
6
+ /** The action's button or link label. */
7
+ label: string;
8
+ /** The link target, when the action navigates. */
9
+ href?: string;
10
+ }
11
+ /** A non-blocking editor notice, serializable so it can ride EditData across the SSR boundary. */
12
+ export interface AdvisoryNotice {
13
+ /** The notice kind, e.g. "address-collision". */
14
+ kind: string;
15
+ /** The advisory severity; warn-and-allow, never a gate. */
16
+ severity: 'warn';
17
+ /** The notice text shown to the editor. */
18
+ message: string;
19
+ /** The notice's offered actions, when any. */
20
+ actions?: AdvisoryAction[];
21
+ }
22
+ /** One entry that resolves to an address, in a shape the collision check and the message read. */
23
+ export interface AddressEntry {
24
+ /** The concept id, e.g. "pages". */
25
+ concept: string;
26
+ /** The entry id (its filename stem). */
27
+ id: string;
28
+ /** The entry title for display, from the manifest (main) or frontmatter (branch). */
29
+ title: string;
30
+ /** The published corpus on main, or an open cairn/* edit branch. */
31
+ source: 'main' | 'branch';
32
+ }
33
+ /** Permalink to the distinct entries that resolve to it, across main and every open branch. */
34
+ export type AddressIndex = Map<string, AddressEntry[]>;
35
+ /**
36
+ * Build the permalink-keyed address index over main (from each manifest entry's resolved permalink)
37
+ * plus every open cairn/* branch (resolved from its edited markdown).
38
+ *
39
+ * The build fails open: a branch read that throws and a permalink that cannot resolve are both caught
40
+ * and skipped, so a transient failure degrades to a thinner index, never a thrown editor or a blocked
41
+ * publish. The branches are read in one Promise.all, the way buildUsageIndex reads them.
42
+ */
43
+ export declare function buildAddressIndex(repo: RepoRef, token: string, concepts: ConceptDescriptor[], manifest: Manifest): Promise<AddressIndex>;
44
+ /**
45
+ * Find the first other entry that already resolves to an address, or null when the address is free
46
+ * or holds only the entry itself. The self entry is identified by its concept and id together.
47
+ */
48
+ export declare function addressCollision(index: AddressIndex, self: {
49
+ concept: string;
50
+ id: string;
51
+ }, address: string): AddressEntry | null;