@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
|
@@ -17,256 +17,148 @@ the model made and never a count of the author's own usage. A normalization name
|
|
|
17
17
|
setting that authorized it; counting the author's own habit is the harmonize-to-author judgment cairn
|
|
18
18
|
must never make, so no such count exists.
|
|
19
19
|
-->
|
|
20
|
-
<script lang="ts">
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const keptCount = $derived(hunks.filter((h) => dispositions[h.index] === 'kept').length);
|
|
155
|
-
const reviewCount = $derived(hunks.filter((h) => dispositions[h.index] === 'undecided').length);
|
|
156
|
-
const skipCount = $derived(hunks.filter((h) => dispositions[h.index] === 'rejected').length);
|
|
157
|
-
|
|
158
|
-
let dialog = $state<HTMLDialogElement | null>(null);
|
|
159
|
-
|
|
160
|
-
$effect(() => {
|
|
161
|
-
// Open the dialog once on mount; showModal supplies the focus trap and Escape.
|
|
162
|
-
dialog?.showModal();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
function setDisposition(index: number, next: Disposition) {
|
|
166
|
-
overrides = { ...overrides, [index]: next };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Narrate one hunk in the polite region. The verb says what just happened to it ("Kept", "Skipped",
|
|
170
|
-
// or "Focused" as the cursor lands on it). The sentence carries the kind and the before/after text,
|
|
171
|
-
// and for a normalization appends the config-named rationale (never a usage count). The trailing
|
|
172
|
-
// nonce keeps a repeated identical action audible.
|
|
173
|
-
function narrate(h: Hunk, verb: string) {
|
|
174
|
-
const where = `Hunk ${hunks.indexOf(h) + 1} of ${hunks.length}`;
|
|
175
|
-
const what = h.delRun.mid && h.addRun.mid ? `${h.delRun.mid.trim()} becomes ${h.addRun.mid.trim()}` : h.label;
|
|
176
|
-
const why = h.because ? `, your ${h.because.label} setting is ${h.because.variant}` : '';
|
|
177
|
-
actionMessage = `${where}. ${h.label}. ${what}${why}. ${verb}.${nonce()}`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function acceptHunk(h: Hunk) {
|
|
181
|
-
setDisposition(h.index, 'kept');
|
|
182
|
-
narrate(h, 'Kept');
|
|
183
|
-
}
|
|
184
|
-
function rejectHunk(h: Hunk) {
|
|
185
|
-
setDisposition(h.index, 'rejected');
|
|
186
|
-
narrate(h, 'Skipped');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Accept fixes (the bulk action): mark EVERY OBJECTIVE hunk kept and nothing else. A judgment hunk is
|
|
190
|
-
// never touched here, so it stays undecided and is never swept. The tally region announces the result.
|
|
191
|
-
function acceptFixes() {
|
|
192
|
-
const next = { ...overrides };
|
|
193
|
-
for (const h of hunks) if (h.objective) next[h.index] = 'kept';
|
|
194
|
-
overrides = next;
|
|
195
|
-
const n = hunks.filter((h) => h.objective).length;
|
|
196
|
-
const stillReview = hunks.filter((h) => effectiveDisposition(h, next) === 'undecided').length;
|
|
197
|
-
tallyMessage = `${n} fixes kept. ${stillReview} still to review.${nonce()}`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Reject all: mark every hunk rejected; no text is written. The tally region announces it.
|
|
201
|
-
function rejectAll() {
|
|
202
|
-
overrides = Object.fromEntries(hunks.map((h) => [h.index, 'rejected'] as const));
|
|
203
|
-
tallyMessage = `All ${hunks.length} changes skipping.${nonce()}`;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Apply: write the kept hunks in ONE batched transaction through the apply seam, then close. The
|
|
207
|
-
// seam's acceptMany dispatches a single view.dispatch({ changes }), so the whole tidy is one undoable
|
|
208
|
-
// step. ONLY the kept indexes are passed, so an undecided judgment hunk is never written.
|
|
209
|
-
function apply() {
|
|
210
|
-
const keptIndexes = hunks.filter((h) => dispositions[h.index] === 'kept').map((h) => h.index);
|
|
211
|
-
api.acceptMany(keptIndexes);
|
|
212
|
-
api.exit();
|
|
213
|
-
dialog?.close();
|
|
214
|
-
onclose(true);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Cancel: write nothing, clear the decorations, leave the document byte-identical.
|
|
218
|
-
function cancel() {
|
|
219
|
-
api.exit();
|
|
220
|
-
dialog?.close();
|
|
221
|
-
onclose(false);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function showInText(h: Hunk) {
|
|
225
|
-
const c = changes.find((ch) => ch.index === h.index);
|
|
226
|
-
if (c) onshow(c.from, c.to);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Move the step-through cursor and announce the hunk it lands on. A screen-reader user pressing j/k
|
|
230
|
-
// hears the newly-focused hunk (kind plus before/after text, plus the because-line for a judgment
|
|
231
|
-
// hunk), the same spec invariant the per-hunk action narration holds. Without this a move was silent.
|
|
232
|
-
function moveFocus(next: number) {
|
|
233
|
-
focusedPos = next;
|
|
234
|
-
const h = hunks[focusedPos];
|
|
235
|
-
if (h) narrate(h, 'Focused');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Keyboard step-through on the hunk list (graft 3): j/k or n/p move; a/r accept/reject the focused
|
|
239
|
-
// hunk; A accepts all objective; Escape cancels (the native dialog supplies Escape, handled below).
|
|
240
|
-
function onListKeydown(e: KeyboardEvent) {
|
|
241
|
-
const h = hunks[focusedPos];
|
|
242
|
-
if (e.key === 'j' || e.key === 'n') {
|
|
243
|
-
moveFocus(Math.min(focusedPos + 1, hunks.length - 1));
|
|
244
|
-
e.preventDefault();
|
|
245
|
-
} else if (e.key === 'k' || e.key === 'p') {
|
|
246
|
-
moveFocus(Math.max(focusedPos - 1, 0));
|
|
247
|
-
e.preventDefault();
|
|
248
|
-
} else if (e.key === 'a' && !e.shiftKey) {
|
|
249
|
-
if (h) acceptHunk(h);
|
|
250
|
-
e.preventDefault();
|
|
251
|
-
} else if (e.key === 'r' && !e.shiftKey) {
|
|
252
|
-
if (h) rejectHunk(h);
|
|
253
|
-
e.preventDefault();
|
|
254
|
-
} else if (e.key === 'A' || (e.key === 'a' && e.shiftKey)) {
|
|
255
|
-
acceptFixes();
|
|
256
|
-
e.preventDefault();
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// The native dialog raises a cancel event on Escape; map it to the surface's cancel so the buffer is
|
|
261
|
-
// left untouched and the host clears tidy mode.
|
|
262
|
-
function onDialogCancel(e: Event) {
|
|
20
|
+
<script lang="ts">import SparklesIcon from "@lucide/svelte/icons/sparkles";
|
|
21
|
+
import CheckIcon from "@lucide/svelte/icons/check";
|
|
22
|
+
import XIcon from "@lucide/svelte/icons/x";
|
|
23
|
+
import TriangleAlertIcon from "@lucide/svelte/icons/triangle-alert";
|
|
24
|
+
import LightbulbIcon from "@lucide/svelte/icons/lightbulb";
|
|
25
|
+
import EyeIcon from "@lucide/svelte/icons/eye";
|
|
26
|
+
import { lineLabel } from "./tidy-diff.js";
|
|
27
|
+
import {
|
|
28
|
+
categorize,
|
|
29
|
+
isObjective,
|
|
30
|
+
buildBecause,
|
|
31
|
+
categoryLabel
|
|
32
|
+
} from "./tidy-categorize.js";
|
|
33
|
+
let { changes, original, conventions, model, title, api, onclose, onshow } = $props();
|
|
34
|
+
const hunks = $derived(changes.map((c) => {
|
|
35
|
+
const category = categorize(c, original, conventions);
|
|
36
|
+
const objective = isObjective(category);
|
|
37
|
+
const removed = original.slice(c.from, c.to);
|
|
38
|
+
const added = c.replacement;
|
|
39
|
+
const line = lineLabel(original, c.from);
|
|
40
|
+
const lines = original.split("\n");
|
|
41
|
+
const contextBefore = line >= 2 ? lines[line - 2] ?? "" : "";
|
|
42
|
+
const contextAfter = line < lines.length ? lines[line] ?? "" : "";
|
|
43
|
+
const lineStart = original.lastIndexOf("\n", c.from - 1) + 1;
|
|
44
|
+
const nextNewline = original.indexOf("\n", c.from);
|
|
45
|
+
const lineEnd = nextNewline === -1 ? original.length : nextNewline;
|
|
46
|
+
const fullLine = original.slice(lineStart, lineEnd);
|
|
47
|
+
const pre = original.slice(lineStart, c.from);
|
|
48
|
+
const post = original.slice(c.to, lineEnd);
|
|
49
|
+
const because = category.kind === "normalization" ? buildBecause(category.convention, conventions) : null;
|
|
50
|
+
return {
|
|
51
|
+
index: c.index,
|
|
52
|
+
category,
|
|
53
|
+
objective,
|
|
54
|
+
line,
|
|
55
|
+
contextBefore,
|
|
56
|
+
contextAfter,
|
|
57
|
+
delText: fullLine,
|
|
58
|
+
addText: pre + added + post,
|
|
59
|
+
delRun: { pre, mid: removed, post },
|
|
60
|
+
addRun: { pre, mid: added, post },
|
|
61
|
+
because,
|
|
62
|
+
label: categoryLabel(category)
|
|
63
|
+
};
|
|
64
|
+
}));
|
|
65
|
+
let overrides = $state({});
|
|
66
|
+
function effectiveDisposition(h, map) {
|
|
67
|
+
return map[h.index] ?? (h.objective ? "kept" : "undecided");
|
|
68
|
+
}
|
|
69
|
+
const dispositions = $derived(
|
|
70
|
+
Object.fromEntries(hunks.map((h) => [h.index, effectiveDisposition(h, overrides)]))
|
|
71
|
+
);
|
|
72
|
+
let focusedPos = $state(0);
|
|
73
|
+
let tallyMessage = $state("");
|
|
74
|
+
let actionMessage = $state("");
|
|
75
|
+
let announceNonce = 0;
|
|
76
|
+
function nonce() {
|
|
77
|
+
return announceNonce++ % 2 === 0 ? "" : "";
|
|
78
|
+
}
|
|
79
|
+
const keptCount = $derived(hunks.filter((h) => dispositions[h.index] === "kept").length);
|
|
80
|
+
const reviewCount = $derived(hunks.filter((h) => dispositions[h.index] === "undecided").length);
|
|
81
|
+
const skipCount = $derived(hunks.filter((h) => dispositions[h.index] === "rejected").length);
|
|
82
|
+
let dialog = $state(null);
|
|
83
|
+
$effect(() => {
|
|
84
|
+
dialog?.showModal();
|
|
85
|
+
});
|
|
86
|
+
function setDisposition(index, next) {
|
|
87
|
+
overrides = { ...overrides, [index]: next };
|
|
88
|
+
}
|
|
89
|
+
function narrate(h, verb) {
|
|
90
|
+
const where = `Hunk ${hunks.indexOf(h) + 1} of ${hunks.length}`;
|
|
91
|
+
const what = h.delRun.mid && h.addRun.mid ? `${h.delRun.mid.trim()} becomes ${h.addRun.mid.trim()}` : h.label;
|
|
92
|
+
const why = h.because ? `, your ${h.because.label} setting is ${h.because.variant}` : "";
|
|
93
|
+
actionMessage = `${where}. ${h.label}. ${what}${why}. ${verb}.${nonce()}`;
|
|
94
|
+
}
|
|
95
|
+
function acceptHunk(h) {
|
|
96
|
+
setDisposition(h.index, "kept");
|
|
97
|
+
narrate(h, "Kept");
|
|
98
|
+
}
|
|
99
|
+
function rejectHunk(h) {
|
|
100
|
+
setDisposition(h.index, "rejected");
|
|
101
|
+
narrate(h, "Skipped");
|
|
102
|
+
}
|
|
103
|
+
function acceptFixes() {
|
|
104
|
+
const next = { ...overrides };
|
|
105
|
+
for (const h of hunks) if (h.objective) next[h.index] = "kept";
|
|
106
|
+
overrides = next;
|
|
107
|
+
const n = hunks.filter((h) => h.objective).length;
|
|
108
|
+
const stillReview = hunks.filter((h) => effectiveDisposition(h, next) === "undecided").length;
|
|
109
|
+
tallyMessage = `${n} fixes kept. ${stillReview} still to review.${nonce()}`;
|
|
110
|
+
}
|
|
111
|
+
function rejectAll() {
|
|
112
|
+
overrides = Object.fromEntries(hunks.map((h) => [h.index, "rejected"]));
|
|
113
|
+
tallyMessage = `All ${hunks.length} changes skipping.${nonce()}`;
|
|
114
|
+
}
|
|
115
|
+
function apply() {
|
|
116
|
+
const keptIndexes = hunks.filter((h) => dispositions[h.index] === "kept").map((h) => h.index);
|
|
117
|
+
api.acceptMany(keptIndexes);
|
|
118
|
+
api.exit();
|
|
119
|
+
dialog?.close();
|
|
120
|
+
onclose(true);
|
|
121
|
+
}
|
|
122
|
+
function cancel() {
|
|
123
|
+
api.exit();
|
|
124
|
+
dialog?.close();
|
|
125
|
+
onclose(false);
|
|
126
|
+
}
|
|
127
|
+
function showInText(h) {
|
|
128
|
+
const c = changes.find((ch) => ch.index === h.index);
|
|
129
|
+
if (c) onshow(c.from, c.to);
|
|
130
|
+
}
|
|
131
|
+
function moveFocus(next) {
|
|
132
|
+
focusedPos = next;
|
|
133
|
+
const h = hunks[focusedPos];
|
|
134
|
+
if (h) narrate(h, "Focused");
|
|
135
|
+
}
|
|
136
|
+
function onListKeydown(e) {
|
|
137
|
+
const h = hunks[focusedPos];
|
|
138
|
+
if (e.key === "j" || e.key === "n") {
|
|
139
|
+
moveFocus(Math.min(focusedPos + 1, hunks.length - 1));
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
} else if (e.key === "k" || e.key === "p") {
|
|
142
|
+
moveFocus(Math.max(focusedPos - 1, 0));
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
} else if (e.key === "a" && !e.shiftKey) {
|
|
145
|
+
if (h) acceptHunk(h);
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
} else if (e.key === "r" && !e.shiftKey) {
|
|
148
|
+
if (h) rejectHunk(h);
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
} else if (e.key === "A" || e.key === "a" && e.shiftKey) {
|
|
151
|
+
acceptFixes();
|
|
263
152
|
e.preventDefault();
|
|
264
|
-
cancel();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function actsLabel(h: Hunk): string {
|
|
268
|
-
return h.objective ? 'Accept or reject this fix' : 'Accept or reject this change';
|
|
269
153
|
}
|
|
154
|
+
}
|
|
155
|
+
function onDialogCancel(e) {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
cancel();
|
|
158
|
+
}
|
|
159
|
+
function actsLabel(h) {
|
|
160
|
+
return h.objective ? "Accept or reject this fix" : "Accept or reject this change";
|
|
161
|
+
}
|
|
270
162
|
</script>
|
|
271
163
|
|
|
272
164
|
<dialog
|
|
@@ -6,46 +6,25 @@ text; when the editor holds a selection it arrives as the default text, and the
|
|
|
6
6
|
that selection either way. Built on a native <dialog>, following the LinkPicker a11y conventions,
|
|
7
7
|
and opened by the host's Ctrl/Cmd+K shortcut through the exported open().
|
|
8
8
|
-->
|
|
9
|
-
<script lang="ts">
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
/** Open the dialog with fresh fields; the edit page's Ctrl/Cmd+K shortcut calls it too. */
|
|
30
|
-
export function open() {
|
|
31
|
-
href = '';
|
|
32
|
-
text = selection?.() ?? '';
|
|
33
|
-
dialog?.showModal();
|
|
34
|
-
// showModal() lands focus on the first focusable element (the header Close button), so move
|
|
35
|
-
// it to the address input the dialog exists for (WCAG 2.4.3). A microtask defers past the
|
|
36
|
-
// dialog's own focus handling, the RenameDialog recipe.
|
|
37
|
-
queueMicrotask(() => hrefInput?.focus());
|
|
38
|
-
}
|
|
39
|
-
function close() {
|
|
40
|
-
dialog?.close();
|
|
41
|
-
}
|
|
42
|
-
function submit(e: SubmitEvent) {
|
|
43
|
-
e.preventDefault();
|
|
44
|
-
// With no text and no selection the address itself becomes the display text, so the link
|
|
45
|
-
// never renders as an invisible pair of brackets.
|
|
46
|
-
insert(href, text.trim() || href);
|
|
47
|
-
close();
|
|
48
|
-
}
|
|
9
|
+
<script lang="ts">let { insert, selection, disabled = false, trigger = true } = $props();
|
|
10
|
+
let dialog = $state(null);
|
|
11
|
+
let hrefInput = $state(null);
|
|
12
|
+
let href = $state("");
|
|
13
|
+
let text = $state("");
|
|
14
|
+
export function open() {
|
|
15
|
+
href = "";
|
|
16
|
+
text = selection?.() ?? "";
|
|
17
|
+
dialog?.showModal();
|
|
18
|
+
queueMicrotask(() => hrefInput?.focus());
|
|
19
|
+
}
|
|
20
|
+
function close() {
|
|
21
|
+
dialog?.close();
|
|
22
|
+
}
|
|
23
|
+
function submit(e) {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
insert(href, text.trim() || href);
|
|
26
|
+
close();
|
|
27
|
+
}
|
|
49
28
|
</script>
|
|
50
29
|
|
|
51
30
|
{#if trigger}
|
|
@@ -7135,6 +7135,10 @@
|
|
|
7135
7135
|
user-select: none;
|
|
7136
7136
|
}
|
|
7137
7137
|
|
|
7138
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .\[builtin\:vite-dynamic-import-vars\] {
|
|
7139
|
+
builtin: vite-dynamic-import-vars;
|
|
7140
|
+
}
|
|
7141
|
+
|
|
7138
7142
|
@media (hover: hover) {
|
|
7139
7143
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .group-hover\/sec\:opacity-90:is(:where(.group\/sec):hover *) {
|
|
7140
7144
|
opacity: .9;
|
|
@@ -44,11 +44,15 @@ export declare function firstImageFile(dt: {
|
|
|
44
44
|
files?: ArrayLike<File>;
|
|
45
45
|
items?: ArrayLike<DataTransferItem>;
|
|
46
46
|
}): File | null;
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
47
|
+
/**
|
|
48
|
+
* The conservative canvas area budget, about 16.7M px (4096 x 4096). A source over this is scaled
|
|
49
|
+
* down before any `drawImage`, never clipped.
|
|
50
|
+
*/
|
|
49
51
|
export declare const MAX_AREA = 16777216;
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
+
/**
|
|
53
|
+
* The conservative short-side budget. A source whose smaller dimension exceeds this is scaled down so
|
|
54
|
+
* the short side lands at the cap, even when its area is within MAX_AREA.
|
|
55
|
+
*/
|
|
52
56
|
export declare const MAX_SHORT_SIDE = 4096;
|
|
53
57
|
/**
|
|
54
58
|
* The canvas budget for a source of the given dimensions. When the source area exceeds MAX_AREA or its
|
|
@@ -60,9 +64,11 @@ export declare function budgetForDimensions(width: number, height: number): {
|
|
|
60
64
|
width: number;
|
|
61
65
|
height: number;
|
|
62
66
|
};
|
|
63
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* The ingest failure taxonomy. `decode-unsupported` is a format the browser and the HEIC decoder both
|
|
64
69
|
* refuse; `transcode-failed` is a HEIC decode or a canvas re-encode that threw; `too-large` is a
|
|
65
|
-
* source still over budget after a transcode; `network` is the upload fetch rejecting.
|
|
70
|
+
* source still over budget after a transcode; `network` is the upload fetch rejecting.
|
|
71
|
+
*/
|
|
66
72
|
export type IngestFailureKind = 'decode-unsupported' | 'transcode-failed' | 'too-large' | 'network';
|
|
67
73
|
/** A failed ingest card: a stable discriminant plus a human message the capture card renders. */
|
|
68
74
|
export interface IngestFailureCard {
|
|
@@ -136,7 +142,9 @@ export declare function ingestFailureKind(error: unknown): IngestFailureKind;
|
|
|
136
142
|
* `fetch`.
|
|
137
143
|
*/
|
|
138
144
|
export declare function sendUpload(url: string, init: RequestInit): Promise<Response>;
|
|
139
|
-
/**
|
|
140
|
-
*
|
|
145
|
+
/**
|
|
146
|
+
* Guard a drop target: cancel the browser's default open-the-file behavior on `dragover` and `drop`
|
|
147
|
+
* so a dropped image stays inside the editor rather than navigating the page to the file.
|
|
148
|
+
*/
|
|
141
149
|
export declare function guardDropTarget(event: DragEvent): void;
|
|
142
150
|
export {};
|
|
@@ -109,11 +109,15 @@ export function normalizeDataTransfer(dt) {
|
|
|
109
109
|
export function firstImageFile(dt) {
|
|
110
110
|
return normalizeDataTransfer(dt)[0] ?? null;
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
113
|
-
*
|
|
112
|
+
/**
|
|
113
|
+
* The conservative canvas area budget, about 16.7M px (4096 x 4096). A source over this is scaled
|
|
114
|
+
* down before any `drawImage`, never clipped.
|
|
115
|
+
*/
|
|
114
116
|
export const MAX_AREA = 16_777_216;
|
|
115
|
-
/**
|
|
116
|
-
*
|
|
117
|
+
/**
|
|
118
|
+
* The conservative short-side budget. A source whose smaller dimension exceeds this is scaled down so
|
|
119
|
+
* the short side lands at the cap, even when its area is within MAX_AREA.
|
|
120
|
+
*/
|
|
117
121
|
export const MAX_SHORT_SIDE = 4096;
|
|
118
122
|
/**
|
|
119
123
|
* The canvas budget for a source of the given dimensions. When the source area exceeds MAX_AREA or its
|
|
@@ -290,8 +294,10 @@ export async function sendUpload(url, init) {
|
|
|
290
294
|
throw new IngestError('network');
|
|
291
295
|
}
|
|
292
296
|
}
|
|
293
|
-
/**
|
|
294
|
-
*
|
|
297
|
+
/**
|
|
298
|
+
* Guard a drop target: cancel the browser's default open-the-file behavior on `dragover` and `drop`
|
|
299
|
+
* so a dropped image stays inside the editor rather than navigating the page to the file.
|
|
300
|
+
*/
|
|
295
301
|
export function guardDropTarget(event) {
|
|
296
302
|
event.preventDefault();
|
|
297
303
|
}
|
|
@@ -103,8 +103,10 @@ class MediaChipWidget extends WidgetType {
|
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
106
|
+
/**
|
|
107
|
+
* Scan one line's text for media image tokens, mapping each to its document offsets and resolving its
|
|
108
|
+
* library entry. lineFrom is the line's document start, so the match offsets become absolute.
|
|
109
|
+
*/
|
|
108
110
|
function matchesInLine(text, lineFrom, library) {
|
|
109
111
|
const out = [];
|
|
110
112
|
MEDIA_IMAGE.lastIndex = 0;
|
|
@@ -133,10 +135,12 @@ function matchesInLine(text, lineFrom, library) {
|
|
|
133
135
|
}
|
|
134
136
|
return out;
|
|
135
137
|
}
|
|
136
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Every media image match across the editor's visible ranges, in document order, each carrying its
|
|
137
140
|
* enclosing figure role. One {@link fenceScan} over the whole document feeds the cheap per-token
|
|
138
141
|
* figure detection (no remark parse on the per-rebuild chip path); the visible lines are scanned
|
|
139
|
-
* for tokens, then each token's line index drives {@link figureRoleAtLine}.
|
|
142
|
+
* for tokens, then each token's line index drives {@link figureRoleAtLine}.
|
|
143
|
+
*/
|
|
140
144
|
function visibleMatches(view, library) {
|
|
141
145
|
const lines = view.state.doc.toString().split('\n');
|
|
142
146
|
const scan = fenceScan(lines);
|
|
@@ -153,8 +157,10 @@ function visibleMatches(view, library) {
|
|
|
153
157
|
}
|
|
154
158
|
return out;
|
|
155
159
|
}
|
|
156
|
-
/**
|
|
157
|
-
*
|
|
160
|
+
/**
|
|
161
|
+
* Replace decorations for each visible media image's reference token: the chip widget over the URL
|
|
162
|
+
* token, the alt left untouched. The same spans seed the atomic-range set.
|
|
163
|
+
*/
|
|
158
164
|
function buildMediaDecorations(view, library) {
|
|
159
165
|
const builder = new RangeSetBuilder();
|
|
160
166
|
for (const match of visibleMatches(view, library)) {
|
|
@@ -162,9 +168,11 @@ function buildMediaDecorations(view, library) {
|
|
|
162
168
|
}
|
|
163
169
|
return builder.finish();
|
|
164
170
|
}
|
|
165
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* The atomic ranges for the visible media reference tokens: a caret or selection edit treats each
|
|
166
173
|
* token as one unit, so a stray keystroke replaces the whole reference rather than corrupting a hex
|
|
167
|
-
* digit. Built from the same matches the decorations use, so the two never disagree.
|
|
174
|
+
* digit. Built from the same matches the decorations use, so the two never disagree.
|
|
175
|
+
*/
|
|
168
176
|
function buildAtomicRanges(view, library) {
|
|
169
177
|
const ranges = [];
|
|
170
178
|
for (const match of visibleMatches(view, library)) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { EditorView } from '@codemirror/view';
|
|
2
2
|
import { type Extension } from '@codemirror/state';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* The seam the host drives: begin lands a placeholder and returns its id; progress moves its bar;
|
|
4
5
|
* resolveTo swaps it for the committed image text; cancel removes it leaving the source untouched.
|
|
5
|
-
* Mirrors the register-callback idiom MarkdownEditor uses for its other editor ops.
|
|
6
|
+
* Mirrors the register-callback idiom MarkdownEditor uses for its other editor ops.
|
|
7
|
+
*/
|
|
6
8
|
export interface ImagePlaceholderApi {
|
|
7
9
|
/** Land an optimistic placeholder at the current caret from a local object URL; returns its id. */
|
|
8
10
|
begin(objectUrl: string): number;
|