@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.
- package/CHANGELOG.md +82 -0
- package/dist/components/AdminLayout.svelte +152 -229
- package/dist/components/CairnAdmin.svelte +13 -42
- package/dist/components/CairnLogo.svelte +1 -6
- package/dist/components/CairnMediaLibrary.svelte +821 -1210
- package/dist/components/CairnTidySettings.svelte +194 -261
- package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
- package/dist/components/ComponentForm.svelte +110 -185
- package/dist/components/ComponentInsertDialog.svelte +163 -283
- package/dist/components/ConceptList.svelte +111 -191
- package/dist/components/ConfirmPage.svelte +5 -12
- package/dist/components/CsrfField.svelte +5 -11
- package/dist/components/DeleteDialog.svelte +15 -42
- package/dist/components/EditPage.svelte +781 -1205
- package/dist/components/EditorToolbar.svelte +108 -170
- package/dist/components/HelpHome.svelte +824 -0
- package/dist/components/HelpHome.svelte.d.ts +22 -0
- package/dist/components/IconPicker.svelte +23 -53
- package/dist/components/LinkPicker.svelte +34 -58
- package/dist/components/LoginPage.svelte +14 -27
- package/dist/components/ManageEditors.svelte +3 -15
- package/dist/components/MarkdownEditor.svelte +689 -957
- package/dist/components/MarkdownHelpDialog.svelte +12 -27
- package/dist/components/MediaCaptureCard.svelte +18 -57
- package/dist/components/MediaFigureControl.svelte +32 -71
- package/dist/components/MediaHeroField.svelte +210 -329
- package/dist/components/MediaInsertPopover.svelte +156 -283
- package/dist/components/MediaPicker.svelte +67 -131
- package/dist/components/NavTree.svelte +46 -78
- package/dist/components/RenameDialog.svelte +16 -43
- package/dist/components/ShortcutsDialog.svelte +9 -13
- package/dist/components/ShortcutsGrid.svelte +1 -2
- package/dist/components/TidyReview.svelte +140 -248
- package/dist/components/WebLinkDialog.svelte +19 -40
- package/dist/components/cairn-admin.css +4 -0
- package/dist/components/client-ingest.d.ts +16 -8
- package/dist/components/client-ingest.js +12 -6
- package/dist/components/editor-media.js +16 -8
- package/dist/components/editor-placeholder.d.ts +4 -2
- package/dist/components/editor-tidy.d.ts +24 -12
- package/dist/components/editor-tidy.js +8 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/link-completion.d.ts +12 -6
- package/dist/components/link-completion.js +12 -6
- package/dist/components/markdown-directives.d.ts +9 -6
- package/dist/components/markdown-directives.js +9 -6
- package/dist/components/markdown-format.d.ts +7 -2
- package/dist/components/markdown-format.js +59 -28
- package/dist/components/markdown-reference.d.ts +8 -0
- package/dist/components/markdown-reference.js +22 -0
- package/dist/components/media-upload-outcome.d.ts +12 -6
- package/dist/components/objective-errors.d.ts +8 -4
- package/dist/components/objective-errors.js +8 -4
- package/dist/components/preview-doc.d.ts +4 -2
- package/dist/components/preview-doc.js +4 -2
- package/dist/components/spellcheck.d.ts +57 -29
- package/dist/components/spellcheck.js +50 -20
- package/dist/components/tidy-categorize.d.ts +20 -10
- package/dist/components/tidy-categorize.js +16 -8
- package/dist/components/tidy-validate.d.ts +12 -6
- package/dist/components/tidy-validate.js +20 -10
- package/dist/components/topbar-context.d.ts +4 -2
- package/dist/content/advisories.d.ts +51 -0
- package/dist/content/advisories.js +79 -0
- package/dist/content/compose.d.ts +4 -2
- package/dist/content/compose.js +1 -0
- package/dist/content/excerpt.js +4 -2
- package/dist/content/getting-started.d.ts +18 -0
- package/dist/content/getting-started.js +12 -0
- package/dist/content/links.d.ts +16 -8
- package/dist/content/links.js +12 -6
- package/dist/content/manifest.d.ts +36 -18
- package/dist/content/manifest.js +32 -16
- package/dist/content/media-refs.d.ts +4 -2
- package/dist/content/media-refs.js +4 -2
- package/dist/content/media-rewrite.d.ts +8 -4
- package/dist/content/media-rewrite.js +76 -38
- package/dist/content/schema.d.ts +20 -10
- package/dist/content/site-dictionary.d.ts +4 -2
- package/dist/content/site-dictionary.js +8 -4
- package/dist/content/types.d.ts +97 -42
- package/dist/delivery/CairnHead.svelte +8 -11
- package/dist/delivery/content-index.d.ts +16 -8
- package/dist/delivery/feeds.js +4 -2
- package/dist/delivery/json-ld.d.ts +3 -0
- package/dist/delivery/json-ld.js +3 -0
- package/dist/delivery/manifest.d.ts +4 -2
- package/dist/delivery/manifest.js +4 -2
- package/dist/delivery/public-routes.d.ts +12 -6
- package/dist/delivery/public-routes.js +4 -2
- package/dist/delivery/seo-fields.d.ts +12 -6
- package/dist/delivery/seo-fields.js +8 -4
- package/dist/delivery/site-indexes.d.ts +4 -2
- package/dist/delivery/site-resolver.d.ts +4 -2
- package/dist/delivery/site-resolver.js +4 -2
- package/dist/doctor/cloudflare-api.d.ts +6 -0
- package/dist/doctor/cloudflare-api.js +6 -0
- package/dist/doctor/index.d.ts +12 -6
- package/dist/doctor/report.d.ts +3 -0
- package/dist/doctor/report.js +3 -0
- package/dist/doctor/run.d.ts +3 -0
- package/dist/doctor/run.js +3 -0
- package/dist/doctor/types.d.ts +10 -2
- package/dist/doctor/types.js +6 -0
- package/dist/doctor/wrangler-config.d.ts +7 -2
- package/dist/doctor/wrangler-config.js +3 -0
- package/dist/email.d.ts +4 -2
- package/dist/env.d.ts +0 -3
- package/dist/env.js +0 -3
- package/dist/github/branches.d.ts +4 -2
- package/dist/github/branches.js +4 -2
- package/dist/github/signing.d.ts +1 -1
- package/dist/github/signing.js +2 -2
- package/dist/log/events.d.ts +1 -1
- package/dist/media/bulk-delete-plan.d.ts +8 -4
- package/dist/media/config.d.ts +12 -6
- package/dist/media/config.js +16 -8
- package/dist/media/delivery-bucket.d.ts +4 -2
- package/dist/media/library-entry.d.ts +4 -2
- package/dist/media/library-entry.js +4 -2
- package/dist/media/manifest.d.ts +29 -15
- package/dist/media/manifest.js +29 -16
- package/dist/media/naming.d.ts +12 -6
- package/dist/media/naming.js +24 -12
- package/dist/media/orphan-scan.d.ts +4 -2
- package/dist/media/reconcile.d.ts +21 -11
- package/dist/media/reconcile.js +12 -6
- package/dist/media/reference.d.ts +8 -4
- package/dist/media/reference.js +12 -6
- package/dist/media/rewrite-plan.d.ts +12 -6
- package/dist/media/sniff.d.ts +4 -2
- package/dist/media/sniff.js +28 -14
- package/dist/media/store.d.ts +16 -8
- package/dist/media/store.js +4 -2
- package/dist/media/transform-url.d.ts +12 -6
- package/dist/media/transform-url.js +8 -4
- package/dist/media/usage.d.ts +8 -4
- package/dist/nav/site-config.d.ts +16 -8
- package/dist/render/component-grammar.d.ts +23 -10
- package/dist/render/component-grammar.js +19 -8
- package/dist/render/component-insert.d.ts +8 -4
- package/dist/render/component-insert.js +4 -2
- package/dist/render/component-reference.d.ts +4 -2
- package/dist/render/component-reference.js +4 -2
- package/dist/render/component-validate.d.ts +3 -0
- package/dist/render/component-validate.js +3 -0
- package/dist/render/glyph.d.ts +4 -2
- package/dist/render/glyph.js +4 -2
- package/dist/render/pipeline.d.ts +20 -10
- package/dist/render/pipeline.js +4 -2
- package/dist/render/registry.d.ts +40 -20
- package/dist/render/registry.js +16 -8
- package/dist/render/rehype-dispatch.d.ts +22 -8
- package/dist/render/rehype-dispatch.js +22 -8
- package/dist/render/remark-directives.d.ts +3 -0
- package/dist/render/remark-directives.js +3 -0
- package/dist/render/remark-figure.d.ts +4 -2
- package/dist/render/remark-figure.js +4 -2
- package/dist/render/resolve-links.d.ts +4 -2
- package/dist/render/resolve-links.js +4 -2
- package/dist/render/resolve-media.d.ts +16 -8
- package/dist/render/resolve-media.js +12 -6
- package/dist/sveltekit/admin-dispatch.d.ts +2 -0
- package/dist/sveltekit/admin-dispatch.js +9 -3
- package/dist/sveltekit/auth-routes.d.ts +3 -0
- package/dist/sveltekit/auth-routes.js +3 -0
- package/dist/sveltekit/cairn-admin.d.ts +16 -5
- package/dist/sveltekit/cairn-admin.js +26 -10
- package/dist/sveltekit/content-routes.d.ts +191 -86
- package/dist/sveltekit/content-routes.js +295 -107
- package/dist/sveltekit/editors-routes.d.ts +3 -0
- package/dist/sveltekit/editors-routes.js +3 -0
- package/dist/sveltekit/guard.d.ts +4 -2
- package/dist/sveltekit/guard.js +4 -2
- package/dist/sveltekit/https-required-page.d.ts +1 -1
- package/dist/sveltekit/https-required-page.js +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/media-route.d.ts +1 -2
- package/dist/sveltekit/media-route.js +13 -8
- package/dist/sveltekit/nav-routes.d.ts +7 -2
- package/dist/sveltekit/nav-routes.js +3 -0
- package/dist/sveltekit/types.d.ts +4 -2
- package/dist/vite/index.d.ts +32 -16
- package/dist/vite/index.js +52 -26
- package/dist/vite/resolve-root.d.ts +8 -4
- package/dist/vite/resolve-root.js +4 -2
- package/package.json +8 -2
- package/src/lib/components/AdminLayout.svelte +22 -0
- package/src/lib/components/CairnAdmin.svelte +3 -0
- package/src/lib/components/CairnTidySettings.svelte +2 -2
- package/src/lib/components/ComponentForm.svelte +0 -1
- package/src/lib/components/EditPage.svelte +133 -41
- package/src/lib/components/HelpHome.svelte +850 -0
- package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
- package/src/lib/components/client-ingest.ts +20 -10
- package/src/lib/components/editor-media.ts +20 -10
- package/src/lib/components/editor-placeholder.ts +12 -6
- package/src/lib/components/editor-tidy.ts +28 -14
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/link-completion.ts +12 -6
- package/src/lib/components/markdown-directives.ts +13 -8
- package/src/lib/components/markdown-format.ts +63 -30
- package/src/lib/components/markdown-reference.ts +30 -0
- package/src/lib/components/media-upload-outcome.ts +12 -6
- package/src/lib/components/objective-errors.ts +16 -8
- package/src/lib/components/preview-doc.ts +4 -2
- package/src/lib/components/spellcheck.ts +92 -40
- package/src/lib/components/tidy-categorize.ts +28 -14
- package/src/lib/components/tidy-validate.ts +28 -14
- package/src/lib/components/topbar-context.ts +4 -2
- package/src/lib/content/advisories.ts +141 -0
- package/src/lib/content/compose.ts +5 -2
- package/src/lib/content/excerpt.ts +4 -2
- package/src/lib/content/getting-started.ts +31 -0
- package/src/lib/content/links.ts +16 -8
- package/src/lib/content/manifest.ts +36 -18
- package/src/lib/content/media-refs.ts +4 -2
- package/src/lib/content/media-rewrite.ts +100 -50
- package/src/lib/content/schema.ts +20 -10
- package/src/lib/content/site-dictionary.ts +8 -4
- package/src/lib/content/types.ts +97 -42
- package/src/lib/delivery/content-index.ts +16 -8
- package/src/lib/delivery/feeds.ts +4 -2
- package/src/lib/delivery/json-ld.ts +3 -0
- package/src/lib/delivery/manifest.ts +4 -2
- package/src/lib/delivery/public-routes.ts +16 -8
- package/src/lib/delivery/seo-fields.ts +12 -6
- package/src/lib/delivery/site-indexes.ts +4 -2
- package/src/lib/delivery/site-resolver.ts +4 -2
- package/src/lib/doctor/cloudflare-api.ts +6 -0
- package/src/lib/doctor/index.ts +12 -6
- package/src/lib/doctor/report.ts +3 -0
- package/src/lib/doctor/run.ts +3 -0
- package/src/lib/doctor/types.ts +10 -2
- package/src/lib/doctor/wrangler-config.ts +7 -2
- package/src/lib/email.ts +4 -2
- package/src/lib/env.ts +0 -3
- package/src/lib/github/branches.ts +4 -2
- package/src/lib/github/signing.ts +2 -2
- package/src/lib/log/events.ts +1 -0
- package/src/lib/media/bulk-delete-plan.ts +8 -4
- package/src/lib/media/config.ts +24 -12
- package/src/lib/media/delivery-bucket.ts +4 -2
- package/src/lib/media/library-entry.ts +4 -2
- package/src/lib/media/manifest.ts +33 -18
- package/src/lib/media/naming.ts +24 -12
- package/src/lib/media/orphan-scan.ts +4 -2
- package/src/lib/media/reconcile.ts +21 -11
- package/src/lib/media/reference.ts +12 -6
- package/src/lib/media/rewrite-plan.ts +12 -6
- package/src/lib/media/sniff.ts +28 -14
- package/src/lib/media/store.ts +16 -8
- package/src/lib/media/transform-url.ts +12 -6
- package/src/lib/media/usage.ts +8 -4
- package/src/lib/nav/site-config.ts +16 -8
- package/src/lib/render/component-grammar.ts +23 -10
- package/src/lib/render/component-insert.ts +8 -4
- package/src/lib/render/component-reference.ts +4 -2
- package/src/lib/render/component-validate.ts +3 -0
- package/src/lib/render/glyph.ts +4 -2
- package/src/lib/render/pipeline.ts +20 -10
- package/src/lib/render/registry.ts +44 -22
- package/src/lib/render/rehype-dispatch.ts +22 -8
- package/src/lib/render/remark-directives.ts +3 -0
- package/src/lib/render/remark-figure.ts +4 -2
- package/src/lib/render/resolve-links.ts +4 -2
- package/src/lib/render/resolve-media.ts +16 -8
- package/src/lib/sveltekit/admin-dispatch.ts +10 -4
- package/src/lib/sveltekit/auth-routes.ts +3 -0
- package/src/lib/sveltekit/cairn-admin.ts +37 -15
- package/src/lib/sveltekit/content-routes.ts +492 -197
- package/src/lib/sveltekit/editors-routes.ts +3 -0
- package/src/lib/sveltekit/guard.ts +4 -2
- package/src/lib/sveltekit/https-required-page.ts +1 -1
- package/src/lib/sveltekit/index.ts +3 -0
- package/src/lib/sveltekit/media-route.ts +13 -8
- package/src/lib/sveltekit/nav-routes.ts +7 -2
- package/src/lib/sveltekit/types.ts +4 -2
- package/src/lib/vite/index.ts +60 -30
- package/src/lib/vite/resolve-root.ts +8 -4
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
import { EditorView } from '@codemirror/view';
|
|
2
2
|
import { type Extension } from '@codemirror/state';
|
|
3
3
|
import type { Change } from './tidy-diff.js';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The api the host drives over one editor view (spec 2.5). Mirrors imagePlaceholderApi: the host
|
|
5
6
|
* registers it through registerTidy, and the review surface calls it as the author works the list.
|
|
6
|
-
* Every accept lands as a CodeMirror transaction; reject and reject-all write no text.
|
|
7
|
+
* Every accept lands as a CodeMirror transaction; reject and reject-all write no text.
|
|
8
|
+
*/
|
|
7
9
|
export interface TidyApi {
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
+
/**
|
|
11
|
+
* Open tidy with the validated change set: seed the field, show the decorations. The buffer is
|
|
12
|
+
* untouched; the originals stay until an accept writes.
|
|
13
|
+
*/
|
|
10
14
|
enter(changes: Change[]): void;
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
15
|
+
/**
|
|
16
|
+
* Accept one change: dispatch its replacement over its current span in one transaction and mark it
|
|
17
|
+
* accepted. The other pending changes map across the edit.
|
|
18
|
+
*/
|
|
13
19
|
acceptOne(index: number): void;
|
|
14
20
|
/** Reject one change: mark it rejected so its decorations clear, leaving the original untouched. */
|
|
15
21
|
rejectOne(index: number): void;
|
|
16
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Accept many changes (the bulk action) in ONE transaction: the whole edit is one undoable step.
|
|
17
24
|
* The caller passes ONLY the indexes it has decided to keep; this never sweeps an index the caller
|
|
18
|
-
* did not name, which is how Accept-fixes confines itself to objective hunks.
|
|
25
|
+
* did not name, which is how Accept-fixes confines itself to objective hunks.
|
|
26
|
+
*/
|
|
19
27
|
acceptMany(indexes: number[]): void;
|
|
20
28
|
/** Reject every remaining pending change, leaving the document byte-identical. */
|
|
21
29
|
rejectAll(): void;
|
|
22
30
|
/** Close tidy: clear the field and the decorations. The buffer holds whatever the accepts wrote. */
|
|
23
31
|
exit(): void;
|
|
24
32
|
}
|
|
25
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The tidy extension: the StateField holding the change set and its decorations. The host adds it to
|
|
26
35
|
* the initial editor state (in its own compartment beside media and folding), then builds the driving
|
|
27
|
-
* api with tidyApi once the view exists.
|
|
36
|
+
* api with tidyApi once the view exists.
|
|
37
|
+
*/
|
|
28
38
|
export declare function cairnTidy(): Extension;
|
|
29
|
-
/**
|
|
30
|
-
*
|
|
39
|
+
/**
|
|
40
|
+
* Build the api that drives tidy against one editor view. The host registers it through registerTidy;
|
|
41
|
+
* the review surface calls enter, the per-hunk and bulk accept/reject, and exit.
|
|
42
|
+
*/
|
|
31
43
|
export declare function tidyApi(view: EditorView): TidyApi;
|
|
@@ -152,14 +152,18 @@ const tidyField = StateField.define({
|
|
|
152
152
|
},
|
|
153
153
|
provide: (f) => EditorView.decorations.from(f, (v) => buildDecorations(v)),
|
|
154
154
|
});
|
|
155
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* The tidy extension: the StateField holding the change set and its decorations. The host adds it to
|
|
156
157
|
* the initial editor state (in its own compartment beside media and folding), then builds the driving
|
|
157
|
-
* api with tidyApi once the view exists.
|
|
158
|
+
* api with tidyApi once the view exists.
|
|
159
|
+
*/
|
|
158
160
|
export function cairnTidy() {
|
|
159
161
|
return tidyField;
|
|
160
162
|
}
|
|
161
|
-
/**
|
|
162
|
-
*
|
|
163
|
+
/**
|
|
164
|
+
* Build the api that drives tidy against one editor view. The host registers it through registerTidy;
|
|
165
|
+
* the review surface calls enter, the per-hunk and bulk accept/reject, and exit.
|
|
166
|
+
*/
|
|
163
167
|
export function tidyApi(view) {
|
|
164
168
|
// Dispatch the named changes' replacements over their CURRENT mapped spans in one transaction, mark
|
|
165
169
|
// them accepted, and let the field map any remaining pending entries. The changes are read from the
|
|
@@ -6,6 +6,7 @@ export { default as CsrfField } from './CsrfField.svelte';
|
|
|
6
6
|
export { default as ConceptList } from './ConceptList.svelte';
|
|
7
7
|
export { default as CairnMediaLibrary } from './CairnMediaLibrary.svelte';
|
|
8
8
|
export { default as CairnTidySettings } from './CairnTidySettings.svelte';
|
|
9
|
+
export { default as HelpHome } from './HelpHome.svelte';
|
|
9
10
|
export { default as EditPage } from './EditPage.svelte';
|
|
10
11
|
export { default as ManageEditors } from './ManageEditors.svelte';
|
|
11
12
|
export { default as MarkdownEditor } from './MarkdownEditor.svelte';
|
package/dist/components/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { default as CsrfField } from './CsrfField.svelte';
|
|
|
8
8
|
export { default as ConceptList } from './ConceptList.svelte';
|
|
9
9
|
export { default as CairnMediaLibrary } from './CairnMediaLibrary.svelte';
|
|
10
10
|
export { default as CairnTidySettings } from './CairnTidySettings.svelte';
|
|
11
|
+
export { default as HelpHome } from './HelpHome.svelte';
|
|
11
12
|
export { default as EditPage } from './EditPage.svelte';
|
|
12
13
|
export { default as ManageEditors } from './ManageEditors.svelte';
|
|
13
14
|
export { default as MarkdownEditor } from './MarkdownEditor.svelte';
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import type { Completion, CompletionSource } from '@codemirror/autocomplete';
|
|
2
2
|
import type { LinkTarget } from '../content/manifest.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/**
|
|
4
|
+
* The open `[[query` before the cursor, or null. The query stops at a closing bracket or a newline,
|
|
5
|
+
* so a finished `[[x]]` link and ordinary prose never trigger. `from` is the index of the `[[`.
|
|
6
|
+
*/
|
|
5
7
|
export declare function matchCairnTrigger(before: string): {
|
|
6
8
|
query: string;
|
|
7
9
|
from: number;
|
|
8
10
|
} | null;
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
+
/**
|
|
12
|
+
* The completion options for a query: a case-insensitive title substring match, each option grouped
|
|
13
|
+
* by concept, a draft marked and a post date shown in the detail, and the apply text the full link.
|
|
14
|
+
*/
|
|
11
15
|
export declare function linkCompletions(targets: LinkTarget[], query: string): Completion[];
|
|
12
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* A CodeMirror CompletionSource over the site's link targets, triggered by `[[`. It replaces the
|
|
13
18
|
* whole `[[query` with the chosen link, and sets filter:false because linkCompletions already
|
|
14
|
-
* filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`).
|
|
19
|
+
* filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`).
|
|
20
|
+
*/
|
|
15
21
|
export declare function cairnLinkCompletionSource(targets: LinkTarget[]): CompletionSource;
|
|
@@ -11,14 +11,18 @@ const CONCEPT_SECTIONS = {
|
|
|
11
11
|
function sectionFor(concept) {
|
|
12
12
|
return CONCEPT_SECTIONS[concept] ?? { name: concept.charAt(0).toUpperCase() + concept.slice(1), rank: 2 };
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
14
|
+
/**
|
|
15
|
+
* The open `[[query` before the cursor, or null. The query stops at a closing bracket or a newline,
|
|
16
|
+
* so a finished `[[x]]` link and ordinary prose never trigger. `from` is the index of the `[[`.
|
|
17
|
+
*/
|
|
16
18
|
export function matchCairnTrigger(before) {
|
|
17
19
|
const match = /\[\[([^[\]\n]*)$/.exec(before);
|
|
18
20
|
return match ? { query: match[1], from: match.index } : null;
|
|
19
21
|
}
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
+
/**
|
|
23
|
+
* The completion options for a query: a case-insensitive title substring match, each option grouped
|
|
24
|
+
* by concept, a draft marked and a post date shown in the detail, and the apply text the full link.
|
|
25
|
+
*/
|
|
22
26
|
export function linkCompletions(targets, query) {
|
|
23
27
|
const q = query.trim().toLowerCase();
|
|
24
28
|
const matched = q ? targets.filter((t) => t.title.toLowerCase().includes(q)) : targets;
|
|
@@ -29,9 +33,11 @@ export function linkCompletions(targets, query) {
|
|
|
29
33
|
apply: `[${escapeLinkText(t.title)}](${formatCairnToken(t)})`,
|
|
30
34
|
}));
|
|
31
35
|
}
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* A CodeMirror CompletionSource over the site's link targets, triggered by `[[`. It replaces the
|
|
33
38
|
* whole `[[query` with the chosen link, and sets filter:false because linkCompletions already
|
|
34
|
-
* filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`).
|
|
39
|
+
* filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`).
|
|
40
|
+
*/
|
|
35
41
|
export function cairnLinkCompletionSource(targets) {
|
|
36
42
|
return async (context) => {
|
|
37
43
|
const line = context.state.doc.lineAt(context.pos);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The directive name from a container opener line (`:::callout{...}`
|
|
3
|
-
* `::::cta[Book]`
|
|
2
|
+
* The directive name from a container opener line (`:::callout{...}` gives `callout`,
|
|
3
|
+
* `::::cta[Book]` gives `cta`), or null when the line is not a named container opener. Reads the
|
|
4
4
|
* same FENCE match the scan uses: group 2 is the name, empty on a bare closer, so a closer or a
|
|
5
5
|
* non-fence line returns null.
|
|
6
6
|
*/
|
|
@@ -11,9 +11,11 @@ export declare function directiveLineKind(line: string): 'fence' | 'leaf' | null
|
|
|
11
11
|
export interface FenceScan {
|
|
12
12
|
/** The 1-based container depth per line, or null outside any container. */
|
|
13
13
|
depths: (number | null)[];
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Whether a line opened or closed a container, or null for everything else. A fence-shaped
|
|
15
16
|
* line the code-block tracking disowned is null too, so the role array is the one source of
|
|
16
|
-
* truth for pairing and no caller re-parses a line the scan already judged.
|
|
17
|
+
* truth for pairing and no caller re-parses a line the scan already judged.
|
|
18
|
+
*/
|
|
17
19
|
roles: ('opener' | 'closer' | null)[];
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
@@ -56,7 +58,8 @@ export declare function caretContainerRange(scan: FenceScan, caretLine: number):
|
|
|
56
58
|
* example can neither open nor close a range. This is the sole source of fold ranges.
|
|
57
59
|
*/
|
|
58
60
|
export declare function containerRanges(scan: FenceScan): ContainerRange[];
|
|
59
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* The figure placement role for a media token sitting on `lineIndex`, derived from the editor's
|
|
60
63
|
* line scan without a remark parse (the chip rebuild runs on every doc and viewport change).
|
|
61
64
|
*
|
|
62
65
|
* Returns the closed-set role (`center`/`wide`/`full`) when the innermost container holding the
|
|
@@ -76,7 +79,7 @@ export interface FenceToken {
|
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
81
|
* Split a fence line into machinery and meaning. The colon run, the label's brackets, and the
|
|
79
|
-
* whole {attrs} group are machinery; the directive name and the label text are meaning, the
|
|
82
|
+
* whole `{attrs}` group are machinery; the directive name and the label text are meaning, the
|
|
80
83
|
* parts an editor reads. A bare closer is a single machinery span, and a non-fence line yields
|
|
81
84
|
* no spans at all.
|
|
82
85
|
*/
|
|
@@ -15,8 +15,8 @@ const INLINE = /(?<![:\w]):[\w-]+\[[^\]]*\](\{[^}]*\})?/g;
|
|
|
15
15
|
// never opens a real container.
|
|
16
16
|
const CODE_FENCE = /^\s{0,3}(`{3,}|~{3,})/;
|
|
17
17
|
/**
|
|
18
|
-
* The directive name from a container opener line (`:::callout{...}`
|
|
19
|
-
* `::::cta[Book]`
|
|
18
|
+
* The directive name from a container opener line (`:::callout{...}` gives `callout`,
|
|
19
|
+
* `::::cta[Book]` gives `cta`), or null when the line is not a named container opener. Reads the
|
|
20
20
|
* same FENCE match the scan uses: group 2 is the name, empty on a bare closer, so a closer or a
|
|
21
21
|
* non-fence line returns null.
|
|
22
22
|
*/
|
|
@@ -145,8 +145,10 @@ export function containerRanges(scan) {
|
|
|
145
145
|
}
|
|
146
146
|
return out;
|
|
147
147
|
}
|
|
148
|
-
/**
|
|
149
|
-
*
|
|
148
|
+
/**
|
|
149
|
+
* The closed placement-role set for the reserved `figure` directive, mirroring remark-figure.ts.
|
|
150
|
+
* A class outside this set is the measure default, never passed through as a role.
|
|
151
|
+
*/
|
|
150
152
|
const FIGURE_ROLES = new Set(['center', 'wide', 'full']);
|
|
151
153
|
// The directive `{attrs}` brace, and the `.class` shorthands inside it. mdast-util-directive folds
|
|
152
154
|
// every `.class` into a space-joined node.attributes.class, and remark-figure honors a role only when
|
|
@@ -156,7 +158,8 @@ const FIGURE_ROLES = new Set(['center', 'wide', 'full']);
|
|
|
156
158
|
const ATTR_BRACE = /\{([^}]*)\}/;
|
|
157
159
|
const CLASS_SHORTHAND = /\.([\w-]+)/g;
|
|
158
160
|
const CLASS_ATTR = /class\s*=\s*"([^"]*)"/;
|
|
159
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* The figure placement role for a media token sitting on `lineIndex`, derived from the editor's
|
|
160
163
|
* line scan without a remark parse (the chip rebuild runs on every doc and viewport change).
|
|
161
164
|
*
|
|
162
165
|
* Returns the closed-set role (`center`/`wide`/`full`) when the innermost container holding the
|
|
@@ -189,7 +192,7 @@ export function figureRoleAtLine(scan, lines, lineIndex) {
|
|
|
189
192
|
}
|
|
190
193
|
/**
|
|
191
194
|
* Split a fence line into machinery and meaning. The colon run, the label's brackets, and the
|
|
192
|
-
* whole {attrs} group are machinery; the directive name and the label text are meaning, the
|
|
195
|
+
* whole `{attrs}` group are machinery; the directive name and the label text are meaning, the
|
|
193
196
|
* parts an editor reads. A bare closer is a single machinery span, and a non-fence line yields
|
|
194
197
|
* no spans at all.
|
|
195
198
|
*/
|
|
@@ -4,6 +4,9 @@ export interface FormatResult {
|
|
|
4
4
|
from: number;
|
|
5
5
|
to: number;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
7
10
|
export declare function applyMarkdownFormat(doc: string, from: number, to: number, kind: FormatKind): FormatResult;
|
|
8
11
|
/**
|
|
9
12
|
* Insert an inline markdown link at the selection. With a non-empty selection the selected text
|
|
@@ -50,8 +53,10 @@ export declare function findMediaImagesNeedingAlt(doc: string): MediaImageNeedin
|
|
|
50
53
|
* is left in place.
|
|
51
54
|
*/
|
|
52
55
|
export declare function unwrapCairnLink(doc: string, href: string): string;
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
56
|
+
/**
|
|
57
|
+
* The closed placement role set the figure render step honors. A role outside it is the measure
|
|
58
|
+
* default (null), so the control never writes one. Mirrors the set in render/remark-figure.ts.
|
|
59
|
+
*/
|
|
55
60
|
export type FigureRole = 'center' | 'wide' | 'full';
|
|
56
61
|
/**
|
|
57
62
|
* The media image at a caret, with the enclosing `:::figure` block when there is one. `imageFrom`
|
|
@@ -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
|
-
/**
|
|
31
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
66
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
205
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
245
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
268
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
311
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
5
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
5
|
-
*
|
|
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
|
-
/**
|
|
12
|
-
*
|
|
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
|
-
/**
|
|
24
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
17
|
-
*
|
|
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
|
-
/**
|
|
19
|
-
*
|
|
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
|
}
|