@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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HelpData } from '../sveltekit/content-routes.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: HelpData;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* The Help home admin screen, the standing place an author comes to get their bearings and look up
|
|
7
|
+
* how things work. It renders inside `AdminLayout` (the office shell), so it carries no `data-theme`
|
|
8
|
+
* wrapper and imports no CSS: the layout owns the theme and `cairn-admin.css`, and this component
|
|
9
|
+
* consumes the Warm Stone tokens through its scoped `<style>` block.
|
|
10
|
+
*
|
|
11
|
+
* The content is one calm column: a masthead, then three co-equal eyebrow-plus-display sections.
|
|
12
|
+
*
|
|
13
|
+
* - Getting started reads `data.gettingStarted`, the count derived from the committed manifest and
|
|
14
|
+
* the open edit branches (never a stored count). At 3 of 3 the whole section is omitted, never
|
|
15
|
+
* shown as a done checklist. A per-device localStorage flag hides it on request.
|
|
16
|
+
* - Formatting renders `data.reference` (the everyday text and links rows) as a real semantic table.
|
|
17
|
+
* - Get help reads the optional `data.supportContact`. Unset is the canonical self-serve default,
|
|
18
|
+
* with no control; set renders the hand-off shaped by the contact (email, URL, or a note).
|
|
19
|
+
*/
|
|
20
|
+
declare const HelpHome: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
21
|
+
type HelpHome = ReturnType<typeof HelpHome>;
|
|
22
|
+
export default HelpHome;
|
|
@@ -6,60 +6,30 @@ field is optional, a None radio clears the value. A roving tabindex keeps a sing
|
|
|
6
6
|
keys move the selection, the standard radiogroup keyboard model. The glyph renders inline from the
|
|
7
7
|
IconSet path data, matching the renderer's 256-unit viewBox.
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
const names = $derived(Object.keys(icons));
|
|
32
|
-
// The selectable keys in DOM order: the optional None choice ('') first, then each glyph name.
|
|
33
|
-
// Arrow-key navigation walks this list, and the roving tabindex marks the selected key (or the
|
|
34
|
-
// first key when nothing is selected) as the single tab stop.
|
|
35
|
-
const choices = $derived(required ? names : ['', ...names]);
|
|
36
|
-
const tabStop = $derived(choices.includes(value) ? value : choices[0]);
|
|
37
|
-
|
|
38
|
-
function move(delta: number): void {
|
|
39
|
-
// Navigate relative to the focused element (the current tab stop), not the bound value. In a
|
|
40
|
-
// required group with no value, tabStop is the first radio while value is '', so a value-based
|
|
41
|
-
// origin would skip the first step.
|
|
42
|
-
const from = Math.max(0, choices.indexOf(tabStop));
|
|
43
|
-
const next = (from + delta + choices.length) % choices.length;
|
|
44
|
-
onChange(choices[next]);
|
|
45
|
-
// The roving tabindex updates reactively, so wait for the DOM then move focus onto the new tab
|
|
46
|
-
// stop. The keydown handler runs only when focus is already inside the group, so this never
|
|
47
|
-
// steals focus on mount.
|
|
48
|
-
void tick().then(() => {
|
|
49
|
-
const target = group?.querySelector<HTMLElement>('[tabindex="0"]');
|
|
50
|
-
target?.focus();
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function onKeydown(e: KeyboardEvent): void {
|
|
55
|
-
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
56
|
-
e.preventDefault();
|
|
57
|
-
move(1);
|
|
58
|
-
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
move(-1);
|
|
61
|
-
}
|
|
9
|
+
<script lang="ts">import { tick } from "svelte";
|
|
10
|
+
let { icons, value, required, onChange, label = "Icon" } = $props();
|
|
11
|
+
let group;
|
|
12
|
+
const names = $derived(Object.keys(icons));
|
|
13
|
+
const choices = $derived(required ? names : ["", ...names]);
|
|
14
|
+
const tabStop = $derived(choices.includes(value) ? value : choices[0]);
|
|
15
|
+
function move(delta) {
|
|
16
|
+
const from = Math.max(0, choices.indexOf(tabStop));
|
|
17
|
+
const next = (from + delta + choices.length) % choices.length;
|
|
18
|
+
onChange(choices[next]);
|
|
19
|
+
void tick().then(() => {
|
|
20
|
+
const target = group?.querySelector('[tabindex="0"]');
|
|
21
|
+
target?.focus();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function onKeydown(e) {
|
|
25
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
move(1);
|
|
28
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
move(-1);
|
|
62
31
|
}
|
|
32
|
+
}
|
|
63
33
|
</script>
|
|
64
34
|
|
|
65
35
|
<div class="flex flex-wrap gap-2" role="radiogroup" aria-label={label} bind:this={group}>
|
|
@@ -6,65 +6,41 @@ showing its date and each draft marked. Picking a target inserts a cairn: intern
|
|
|
6
6
|
editor's registerInsertLink seam. Built on a native <dialog>, following the component dialog's a11y
|
|
7
7
|
conventions. The plain-URL link is the toolbar's Web link dialog; this is for an internal target.
|
|
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
|
-
|
|
30
|
-
// Group filtered targets by concept, Pages first then Posts then any other concept, so the list
|
|
31
|
-
// reads in a stable order. The filter is a case-insensitive title substring.
|
|
32
|
-
const ORDER: Record<string, number> = { pages: 0, posts: 1 };
|
|
33
|
-
function rank(concept: string): number {
|
|
34
|
-
return ORDER[concept] ?? 2;
|
|
35
|
-
}
|
|
36
|
-
function heading(concept: string): string {
|
|
37
|
-
if (concept === 'pages') return 'Pages';
|
|
38
|
-
if (concept === 'posts') return 'Posts';
|
|
39
|
-
return concept.charAt(0).toUpperCase() + concept.slice(1);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const groups = $derived.by(() => {
|
|
43
|
-
const q = query.trim().toLowerCase();
|
|
44
|
-
const matched = q ? linkTargets.filter((t) => t.title.toLowerCase().includes(q)) : linkTargets;
|
|
45
|
-
const byConcept = new Map<string, LinkTarget[]>();
|
|
46
|
-
for (const t of matched) {
|
|
47
|
-
const list = byConcept.get(t.concept) ?? [];
|
|
48
|
-
list.push(t);
|
|
49
|
-
byConcept.set(t.concept, list);
|
|
50
|
-
}
|
|
51
|
-
return [...byConcept.entries()]
|
|
52
|
-
.map(([concept, items]) => ({ concept, heading: heading(concept), items }))
|
|
53
|
-
.sort((a, b) => rank(a.concept) - rank(b.concept) || a.heading.localeCompare(b.heading));
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
/** Open the picker programmatically, for a host that drives it without the trigger. */
|
|
57
|
-
export function open() {
|
|
58
|
-
query = '';
|
|
59
|
-
dialog?.showModal();
|
|
60
|
-
}
|
|
61
|
-
function close() {
|
|
62
|
-
dialog?.close();
|
|
63
|
-
}
|
|
64
|
-
function choose(target: LinkTarget) {
|
|
65
|
-
insert(formatCairnToken(target), target.title);
|
|
66
|
-
close();
|
|
9
|
+
<script lang="ts">import { formatCairnToken } from "../content/links.js";
|
|
10
|
+
let { linkTargets, insert, disabled = false, trigger = true } = $props();
|
|
11
|
+
let dialog = $state(null);
|
|
12
|
+
let query = $state("");
|
|
13
|
+
const ORDER = { pages: 0, posts: 1 };
|
|
14
|
+
function rank(concept) {
|
|
15
|
+
return ORDER[concept] ?? 2;
|
|
16
|
+
}
|
|
17
|
+
function heading(concept) {
|
|
18
|
+
if (concept === "pages") return "Pages";
|
|
19
|
+
if (concept === "posts") return "Posts";
|
|
20
|
+
return concept.charAt(0).toUpperCase() + concept.slice(1);
|
|
21
|
+
}
|
|
22
|
+
const groups = $derived.by(() => {
|
|
23
|
+
const q = query.trim().toLowerCase();
|
|
24
|
+
const matched = q ? linkTargets.filter((t) => t.title.toLowerCase().includes(q)) : linkTargets;
|
|
25
|
+
const byConcept = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const t of matched) {
|
|
27
|
+
const list = byConcept.get(t.concept) ?? [];
|
|
28
|
+
list.push(t);
|
|
29
|
+
byConcept.set(t.concept, list);
|
|
67
30
|
}
|
|
31
|
+
return [...byConcept.entries()].map(([concept, items]) => ({ concept, heading: heading(concept), items })).sort((a, b) => rank(a.concept) - rank(b.concept) || a.heading.localeCompare(b.heading));
|
|
32
|
+
});
|
|
33
|
+
export function open() {
|
|
34
|
+
query = "";
|
|
35
|
+
dialog?.showModal();
|
|
36
|
+
}
|
|
37
|
+
function close() {
|
|
38
|
+
dialog?.close();
|
|
39
|
+
}
|
|
40
|
+
function choose(target) {
|
|
41
|
+
insert(formatCairnToken(target), target.title);
|
|
42
|
+
close();
|
|
43
|
+
}
|
|
68
44
|
</script>
|
|
69
45
|
|
|
70
46
|
{#if trigger}
|
|
@@ -4,33 +4,20 @@ The magic-link sign-in page. A plain form POST to the named `?/request` action (
|
|
|
4
4
|
`requestAction`); no client SDK. The success message is identical whether or not the email is on
|
|
5
5
|
the allowlist, so the page never leaks membership (spec §7.1).
|
|
6
6
|
-->
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* neutral, send-error, and throttled outcomes. */
|
|
22
|
-
form: { sent?: boolean; status?: 'sent' | 'send_error' | 'throttled' } | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let { data, form }: Props = $props();
|
|
26
|
-
|
|
27
|
-
let rootEl = $state<HTMLElement>();
|
|
28
|
-
// Lets a mistyped address go back to the form without a reload, even though the server still
|
|
29
|
-
// reports `sent`. The success copy never reveals whether the email was on the allowlist.
|
|
30
|
-
let dismissed = $state(false);
|
|
31
|
-
onMount(() => {
|
|
32
|
-
if (rootEl) warnIfChromeWrapped(rootEl);
|
|
33
|
-
});
|
|
7
|
+
<script lang="ts">import "./cairn-admin.css";
|
|
8
|
+
import { onMount } from "svelte";
|
|
9
|
+
import MailCheckIcon from "@lucide/svelte/icons/mail-check";
|
|
10
|
+
import InfoIcon from "@lucide/svelte/icons/info";
|
|
11
|
+
import CairnLogo from "./CairnLogo.svelte";
|
|
12
|
+
import CsrfField from "./CsrfField.svelte";
|
|
13
|
+
import { cairnFaviconHref } from "./cairn-favicon.js";
|
|
14
|
+
import { warnIfChromeWrapped } from "./chrome-guard.js";
|
|
15
|
+
let { data, form } = $props();
|
|
16
|
+
let rootEl = $state();
|
|
17
|
+
let dismissed = $state(false);
|
|
18
|
+
onMount(() => {
|
|
19
|
+
if (rootEl) warnIfChromeWrapped(rootEl);
|
|
20
|
+
});
|
|
34
21
|
</script>
|
|
35
22
|
|
|
36
23
|
<svelte:head>
|
|
@@ -6,21 +6,9 @@ last-owner anti-lockout rule itself is enforced server-side (editors-routes). Ac
|
|
|
6
6
|
named `?/setRole`, `?/removeEditor`, and `?/addEditor` actions, the names the single-mount
|
|
7
7
|
dispatcher defines.
|
|
8
8
|
-->
|
|
9
|
-
<script lang="ts">
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
interface Props {
|
|
14
|
-
/** The editors load's data: the allowlist and the acting owner's email. */
|
|
15
|
-
data: { editors: Editor[]; self: string };
|
|
16
|
-
/** The last action's result (an error message when it failed). */
|
|
17
|
-
form: { error?: string; ok?: boolean } | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let { data, form }: Props = $props();
|
|
21
|
-
|
|
22
|
-
// Eyebrow styling for the table column headers, matching the concept list.
|
|
23
|
-
const col = 'text-[0.6875rem] font-semibold uppercase tracking-[0.08em] text-[var(--color-muted)]';
|
|
9
|
+
<script lang="ts">import CsrfField from "./CsrfField.svelte";
|
|
10
|
+
let { data, form } = $props();
|
|
11
|
+
const col = "text-[0.6875rem] font-semibold uppercase tracking-[0.08em] text-[var(--color-muted)]";
|
|
24
12
|
</script>
|
|
25
13
|
|
|
26
14
|
<header class="mb-6">
|