@glw907/cairn-cms 0.62.2 → 0.76.0
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 +216 -0
- package/dist/ambient.d.ts +2 -0
- package/dist/auth/types.d.ts +7 -0
- package/dist/components/CairnAdmin.svelte.d.ts +2 -7
- package/dist/components/ComponentForm.svelte +44 -27
- package/dist/components/ComponentInsertDialog.svelte +22 -11
- package/dist/components/ComponentInsertDialog.svelte.d.ts +2 -6
- package/dist/components/ConceptList.svelte +25 -4
- package/dist/components/EditPage.svelte +29 -107
- package/dist/components/EditPage.svelte.d.ts +2 -7
- package/dist/components/EntryPicker.svelte +117 -0
- package/dist/components/EntryPicker.svelte.d.ts +35 -0
- package/dist/components/FieldInput.svelte +218 -0
- package/dist/components/FieldInput.svelte.d.ts +51 -0
- package/dist/components/IconPicker.svelte +2 -2
- package/dist/components/IconPicker.svelte.d.ts +2 -0
- package/dist/components/LinkPicker.svelte +8 -75
- package/dist/components/LinkPicker.svelte.d.ts +4 -5
- package/dist/components/MediaHeroField.svelte +8 -5
- package/dist/components/MediaHeroField.svelte.d.ts +4 -0
- package/dist/components/ObjectGroupField.svelte +54 -0
- package/dist/components/ObjectGroupField.svelte.d.ts +47 -0
- package/dist/components/ReferenceField.svelte +94 -0
- package/dist/components/ReferenceField.svelte.d.ts +27 -0
- package/dist/components/RepeatableField.svelte +221 -0
- package/dist/components/RepeatableField.svelte.d.ts +53 -0
- package/dist/components/cairn-admin.css +179 -2
- package/dist/components/preview-doc.js +5 -1
- package/dist/components/tidy-validate.js +1 -1
- package/dist/content/adapter.js +18 -0
- package/dist/content/advisories.d.ts +2 -2
- package/dist/content/advisories.js +3 -5
- package/dist/content/compose.d.ts +7 -6
- package/dist/content/compose.js +26 -20
- package/dist/content/concepts.d.ts +21 -15
- package/dist/content/concepts.js +55 -32
- package/dist/content/field-rules.d.ts +15 -0
- package/dist/content/field-rules.js +38 -0
- package/dist/content/fields.d.ts +169 -0
- package/dist/content/fields.js +41 -0
- package/dist/content/fieldset.d.ts +107 -0
- package/dist/content/fieldset.js +386 -0
- package/dist/content/frontmatter-region.d.ts +38 -0
- package/dist/content/frontmatter-region.js +75 -0
- package/dist/content/frontmatter.d.ts +35 -2
- package/dist/content/frontmatter.js +232 -11
- package/dist/content/manifest.d.ts +34 -0
- package/dist/content/manifest.js +80 -4
- package/dist/content/media-refs.d.ts +2 -2
- package/dist/content/media-rewrite.js +1 -69
- package/dist/content/reference-index.d.ts +56 -0
- package/dist/content/reference-index.js +95 -0
- package/dist/content/references.d.ts +40 -0
- package/dist/content/references.js +0 -0
- package/dist/content/standard-schema.d.ts +30 -0
- package/dist/content/standard-schema.js +4 -0
- package/dist/content/types.d.ts +127 -178
- package/dist/delivery/data.d.ts +2 -2
- package/dist/delivery/data.js +1 -1
- package/dist/delivery/public-routes.d.ts +10 -5
- package/dist/delivery/public-routes.js +25 -2
- package/dist/delivery/site-descriptors.d.ts +5 -1
- package/dist/delivery/site-descriptors.js +8 -3
- package/dist/delivery/site-indexes.d.ts +2 -2
- package/dist/delivery/site-resolver.d.ts +25 -0
- package/dist/delivery/site-resolver.js +49 -0
- package/dist/doctor/checks-local.js +6 -11
- package/dist/github/backend.d.ts +83 -0
- package/dist/github/backend.js +76 -0
- package/dist/github/credentials.d.ts +11 -5
- package/dist/github/credentials.js +3 -3
- package/dist/github/repo.d.ts +8 -19
- package/dist/github/repo.js +69 -80
- package/dist/github/types.d.ts +1 -1
- package/dist/github/types.js +4 -4
- package/dist/index.d.ts +18 -10
- package/dist/index.js +9 -5
- package/dist/islands/index.d.ts +12 -0
- package/dist/islands/index.js +83 -0
- package/dist/islands/types.d.ts +7 -0
- package/dist/islands/types.js +1 -0
- package/dist/log/events.d.ts +1 -1
- package/dist/media/index.d.ts +1 -1
- package/dist/media/index.js +1 -1
- package/dist/media/manifest.d.ts +11 -0
- package/dist/media/manifest.js +13 -0
- package/dist/media/rewrite-plan.d.ts +2 -3
- package/dist/media/rewrite-plan.js +2 -3
- package/dist/media/usage.d.ts +2 -2
- package/dist/media/usage.js +3 -5
- package/dist/nav/site-config.d.ts +0 -6
- package/dist/nav/site-config.js +6 -4
- package/dist/render/component-grammar.js +11 -11
- package/dist/render/component-reference.js +5 -3
- package/dist/render/component-validate.d.ts +4 -1
- package/dist/render/component-validate.js +10 -35
- package/dist/render/highlight.d.ts +9 -0
- package/dist/render/highlight.js +206 -0
- package/dist/render/pipeline.d.ts +0 -6
- package/dist/render/pipeline.js +13 -2
- package/dist/render/registry.d.ts +44 -36
- package/dist/render/registry.js +47 -6
- package/dist/render/rehype-dispatch.d.ts +6 -10
- package/dist/render/rehype-dispatch.js +38 -17
- package/dist/render/remark-directives.js +4 -5
- package/dist/render/sanitize-schema.d.ts +10 -0
- package/dist/render/sanitize-schema.js +30 -1
- package/dist/sveltekit/cairn-admin.d.ts +5 -5
- package/dist/sveltekit/cairn-admin.js +3 -4
- package/dist/sveltekit/content-routes.d.ts +10 -8
- package/dist/sveltekit/content-routes.js +269 -181
- package/dist/sveltekit/guard.js +10 -0
- package/dist/sveltekit/health.d.ts +7 -3
- package/dist/sveltekit/health.js +9 -3
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/nav-routes.d.ts +6 -5
- package/dist/sveltekit/nav-routes.js +22 -20
- package/dist/sveltekit/types.d.ts +2 -0
- package/dist/vite/index.d.ts +3 -3
- package/dist/vite/index.js +17 -8
- package/package.json +17 -2
- package/src/lib/ambient.ts +7 -0
- package/src/lib/auth/types.ts +7 -0
- package/src/lib/components/CairnAdmin.svelte +2 -6
- package/src/lib/components/ComponentForm.svelte +48 -27
- package/src/lib/components/ComponentInsertDialog.svelte +26 -14
- package/src/lib/components/ConceptList.svelte +41 -4
- package/src/lib/components/EditPage.svelte +43 -119
- package/src/lib/components/EntryPicker.svelte +154 -0
- package/src/lib/components/FieldInput.svelte +262 -0
- package/src/lib/components/IconPicker.svelte +4 -2
- package/src/lib/components/LinkPicker.svelte +10 -81
- package/src/lib/components/MediaHeroField.svelte +12 -5
- package/src/lib/components/ObjectGroupField.svelte +97 -0
- package/src/lib/components/ReferenceField.svelte +126 -0
- package/src/lib/components/RepeatableField.svelte +310 -0
- package/src/lib/components/preview-doc.ts +5 -1
- package/src/lib/components/tidy-validate.ts +1 -1
- package/src/lib/content/adapter.ts +21 -0
- package/src/lib/content/advisories.ts +4 -7
- package/src/lib/content/compose.ts +30 -23
- package/src/lib/content/concepts.ts +68 -40
- package/src/lib/content/field-rules.ts +39 -0
- package/src/lib/content/fields.ts +178 -0
- package/src/lib/content/fieldset.ts +470 -0
- package/src/lib/content/frontmatter-region.ts +90 -0
- package/src/lib/content/frontmatter.ts +231 -15
- package/src/lib/content/manifest.ts +101 -4
- package/src/lib/content/media-refs.ts +2 -2
- package/src/lib/content/media-rewrite.ts +7 -80
- package/src/lib/content/reference-index.ts +159 -0
- package/src/lib/content/references.ts +0 -0
- package/src/lib/content/standard-schema.ts +25 -0
- package/src/lib/content/types.ts +128 -195
- package/src/lib/delivery/data.ts +2 -2
- package/src/lib/delivery/public-routes.ts +36 -4
- package/src/lib/delivery/site-descriptors.ts +8 -3
- package/src/lib/delivery/site-indexes.ts +2 -2
- package/src/lib/delivery/site-resolver.ts +64 -0
- package/src/lib/doctor/checks-local.ts +6 -14
- package/src/lib/github/backend.ts +161 -0
- package/src/lib/github/credentials.ts +10 -7
- package/src/lib/github/repo.ts +79 -83
- package/src/lib/github/types.ts +5 -5
- package/src/lib/index.ts +40 -18
- package/src/lib/islands/index.ts +84 -0
- package/src/lib/islands/types.ts +11 -0
- package/src/lib/log/events.ts +1 -0
- package/src/lib/media/index.ts +1 -0
- package/src/lib/media/manifest.ts +14 -0
- package/src/lib/media/rewrite-plan.ts +4 -6
- package/src/lib/media/usage.ts +4 -7
- package/src/lib/nav/site-config.ts +8 -9
- package/src/lib/render/component-grammar.ts +10 -10
- package/src/lib/render/component-reference.ts +4 -3
- package/src/lib/render/component-validate.ts +10 -35
- package/src/lib/render/highlight.ts +259 -0
- package/src/lib/render/pipeline.ts +13 -8
- package/src/lib/render/registry.ts +88 -42
- package/src/lib/render/rehype-dispatch.ts +47 -16
- package/src/lib/render/remark-directives.ts +4 -5
- package/src/lib/render/sanitize-schema.ts +32 -1
- package/src/lib/sveltekit/cairn-admin.ts +8 -9
- package/src/lib/sveltekit/content-routes.ts +330 -221
- package/src/lib/sveltekit/guard.ts +15 -0
- package/src/lib/sveltekit/health.ts +13 -6
- package/src/lib/sveltekit/index.ts +2 -2
- package/src/lib/sveltekit/nav-routes.ts +33 -29
- package/src/lib/sveltekit/types.ts +5 -1
- package/src/lib/vite/index.ts +20 -11
- package/dist/content/schema.d.ts +0 -87
- package/dist/content/schema.js +0 -89
- package/dist/content/validate.d.ts +0 -17
- package/dist/content/validate.js +0 -93
- package/src/lib/content/schema.ts +0 -167
- package/src/lib/content/validate.ts +0 -90
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { NamedField } from '../content/types.js';
|
|
2
|
+
import type { ArrayField } from '../content/fields.js';
|
|
3
|
+
import type { LinkTarget } from '../content/manifest.js';
|
|
4
|
+
import type { MediaEntry } from '../media/manifest.js';
|
|
5
|
+
import type { MediaLibraryEntry } from '../media/library-entry.js';
|
|
6
|
+
import type { IconSet } from '../render/glyph.js';
|
|
7
|
+
import type MediaHeroField from './MediaHeroField.svelte';
|
|
8
|
+
interface Props {
|
|
9
|
+
/** The array descriptor to render; its `item` is the per-row leaf or flat object. */
|
|
10
|
+
field: NamedField & ArrayField;
|
|
11
|
+
/** The form name prefix for this list; each row renders at `${name}.${i}`. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** The seed rows: a list of leaf values, or a list of object slices for an object item. */
|
|
14
|
+
rows: unknown[];
|
|
15
|
+
/** The site link targets the reference arm offers (threaded through to each row). */
|
|
16
|
+
targets: LinkTarget[];
|
|
17
|
+
/** Mark the edit form dirty; called on add, remove, and reorder (these skip the form's oninput). */
|
|
18
|
+
markFieldsDirty: () => void;
|
|
19
|
+
/** The merged committed-plus-uploaded media library, keyed by content hash. */
|
|
20
|
+
mediaLibrary: Record<string, MediaLibraryEntry>;
|
|
21
|
+
/** The concept the entry belongs to (the upload action's route param). */
|
|
22
|
+
conceptId: string;
|
|
23
|
+
/** The entry id (the upload action's route param). */
|
|
24
|
+
id: string;
|
|
25
|
+
/** The host's hero-field refs, keyed by the prefixed `name` so two rows do not collide. */
|
|
26
|
+
heroFieldRefs: Record<string, MediaHeroField>;
|
|
27
|
+
/** Called with the server-owned record on a successful upload, so the host merges it. */
|
|
28
|
+
onuploaded: (record: MediaEntry) => void;
|
|
29
|
+
/** Called when a hero's needs-alt status changes, keyed by the prefixed `name`. */
|
|
30
|
+
onheroneedsalt: (name: string, needsAlt: boolean) => void;
|
|
31
|
+
/** The site's icon set, forwarded to each row's icon arm. */
|
|
32
|
+
icons?: IconSet;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The repeatable-row editor, the arm for a non-reference `array` container. It renders a list of rows,
|
|
36
|
+
* each row either a single leaf (`array(text)`, `array(image)`) or a flat object group
|
|
37
|
+
* (`array(object({...}))`), with keyboard-operable add, remove, and reorder. Each row collapses to its
|
|
38
|
+
* `itemLabel` summary and expands to edit, the same buries-fewer-fields move the Details panel makes.
|
|
39
|
+
*
|
|
40
|
+
* Rows are wrapped in a `{ id, value }` envelope so node identity follows a row through a reorder or a
|
|
41
|
+
* remove and an in-progress edit (or the keyboard focus) never jumps to the wrong row. The id is a
|
|
42
|
+
* seed-time counter, not a random uuid, so the server and client agree at hydration. The envelope is
|
|
43
|
+
* UI-only; the form names derive from each row's CURRENT position (`${name}.${i}`), so the Task 3
|
|
44
|
+
* decoder reads a compact, ordered set. The component seeds once from `rows`; the `{#key entryKey}`
|
|
45
|
+
* wrapper in EditPage remounts it on an entry change, so it adds no re-seed effect.
|
|
46
|
+
*
|
|
47
|
+
* A structural mutation (add, remove, reorder) marks the form dirty, because those do not fire the
|
|
48
|
+
* form's `oninput`; a leaf edit inside a row does not, because the row inputs sit inside the edit form
|
|
49
|
+
* whose `oninput` bubbles. An always-mounted polite live region announces add and remove.
|
|
50
|
+
*/
|
|
51
|
+
declare const RepeatableField: import("svelte").Component<Props, {}, "">;
|
|
52
|
+
type RepeatableField = ReturnType<typeof RepeatableField>;
|
|
53
|
+
export default RepeatableField;
|
|
@@ -3361,6 +3361,10 @@
|
|
|
3361
3361
|
top: calc(var(--spacing) * 16);
|
|
3362
3362
|
}
|
|
3363
3363
|
|
|
3364
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .top-\[-3rem\] {
|
|
3365
|
+
top: -3rem;
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3364
3368
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .top-\[0\.875rem\] {
|
|
3365
3369
|
top: .875rem;
|
|
3366
3370
|
}
|
|
@@ -3389,6 +3393,10 @@
|
|
|
3389
3393
|
left: calc(var(--spacing) * 2);
|
|
3390
3394
|
}
|
|
3391
3395
|
|
|
3396
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .left-\[var\(--cairn-space-s\)\] {
|
|
3397
|
+
left: var(--cairn-space-s);
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3392
3400
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .join {
|
|
3393
3401
|
--join-ss: 0;
|
|
3394
3402
|
--join-se: 0;
|
|
@@ -3703,6 +3711,10 @@
|
|
|
3703
3711
|
z-index: 40;
|
|
3704
3712
|
}
|
|
3705
3713
|
|
|
3714
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .z-50 {
|
|
3715
|
+
z-index: 50;
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3706
3718
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .order-1 {
|
|
3707
3719
|
order: 1;
|
|
3708
3720
|
}
|
|
@@ -4075,6 +4087,10 @@
|
|
|
4075
4087
|
margin-top: calc(var(--spacing) * 16);
|
|
4076
4088
|
}
|
|
4077
4089
|
|
|
4090
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mt-\[var\(--cairn-space-2xl\)\] {
|
|
4091
|
+
margin-top: var(--cairn-space-2xl);
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4078
4094
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mt-auto {
|
|
4079
4095
|
margin-top: auto;
|
|
4080
4096
|
}
|
|
@@ -4083,6 +4099,10 @@
|
|
|
4083
4099
|
margin-top: 1px;
|
|
4084
4100
|
}
|
|
4085
4101
|
|
|
4102
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .-mr-1 {
|
|
4103
|
+
margin-right: calc(var(--spacing) * -1);
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4086
4106
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mr-0\.5 {
|
|
4087
4107
|
margin-right: calc(var(--spacing) * .5);
|
|
4088
4108
|
}
|
|
@@ -4144,6 +4164,10 @@
|
|
|
4144
4164
|
margin-bottom: calc(var(--spacing) * 7);
|
|
4145
4165
|
}
|
|
4146
4166
|
|
|
4167
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mb-\[var\(--cairn-space-s\)\] {
|
|
4168
|
+
margin-bottom: var(--cairn-space-s);
|
|
4169
|
+
}
|
|
4170
|
+
|
|
4147
4171
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .-ml-px {
|
|
4148
4172
|
margin-left: -1px;
|
|
4149
4173
|
}
|
|
@@ -4702,6 +4726,14 @@
|
|
|
4702
4726
|
height: .6875rem;
|
|
4703
4727
|
}
|
|
4704
4728
|
|
|
4729
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .h-\[1\.4rem\] {
|
|
4730
|
+
height: 1.4rem;
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .h-\[1\.55rem\] {
|
|
4734
|
+
height: 1.55rem;
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4705
4737
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .h-\[26px\] {
|
|
4706
4738
|
height: 26px;
|
|
4707
4739
|
}
|
|
@@ -4758,6 +4790,10 @@
|
|
|
4758
4790
|
max-height: 100%;
|
|
4759
4791
|
}
|
|
4760
4792
|
|
|
4793
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .min-h-0 {
|
|
4794
|
+
min-height: calc(var(--spacing) * 0);
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4761
4797
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .min-h-6 {
|
|
4762
4798
|
min-height: calc(var(--spacing) * 6);
|
|
4763
4799
|
}
|
|
@@ -4916,6 +4952,14 @@
|
|
|
4916
4952
|
width: .6875rem;
|
|
4917
4953
|
}
|
|
4918
4954
|
|
|
4955
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .w-\[1\.4rem\] {
|
|
4956
|
+
width: 1.4rem;
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .w-\[1\.55rem\] {
|
|
4960
|
+
width: 1.55rem;
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4919
4963
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .w-\[3\.25rem\] {
|
|
4920
4964
|
width: 3.25rem;
|
|
4921
4965
|
}
|
|
@@ -4968,6 +5012,10 @@
|
|
|
4968
5012
|
max-width: 30%;
|
|
4969
5013
|
}
|
|
4970
5014
|
|
|
5015
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[38rem\] {
|
|
5016
|
+
max-width: 38rem;
|
|
5017
|
+
}
|
|
5018
|
+
|
|
4971
5019
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[40ch\] {
|
|
4972
5020
|
max-width: 40ch;
|
|
4973
5021
|
}
|
|
@@ -4992,6 +5040,14 @@
|
|
|
4992
5040
|
max-width: 640px;
|
|
4993
5041
|
}
|
|
4994
5042
|
|
|
5043
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[var\(--cairn-measure\)\] {
|
|
5044
|
+
max-width: var(--cairn-measure);
|
|
5045
|
+
}
|
|
5046
|
+
|
|
5047
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[var\(--cairn-measure-wide\)\] {
|
|
5048
|
+
max-width: var(--cairn-measure-wide);
|
|
5049
|
+
}
|
|
5050
|
+
|
|
4995
5051
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-full {
|
|
4996
5052
|
max-width: 100%;
|
|
4997
5053
|
}
|
|
@@ -5040,7 +5096,7 @@
|
|
|
5040
5096
|
flex: none;
|
|
5041
5097
|
}
|
|
5042
5098
|
|
|
5043
|
-
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .shrink {
|
|
5099
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .flex-shrink, :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .shrink {
|
|
5044
5100
|
flex-shrink: 1;
|
|
5045
5101
|
}
|
|
5046
5102
|
|
|
@@ -5250,6 +5306,18 @@
|
|
|
5250
5306
|
gap: calc(var(--spacing) * 6);
|
|
5251
5307
|
}
|
|
5252
5308
|
|
|
5309
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .gap-\[0\.55rem\] {
|
|
5310
|
+
gap: .55rem;
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5313
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .gap-\[var\(--cairn-space-m\)\] {
|
|
5314
|
+
gap: var(--cairn-space-m);
|
|
5315
|
+
}
|
|
5316
|
+
|
|
5317
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .gap-\[var\(--cairn-space-s\)\] {
|
|
5318
|
+
gap: var(--cairn-space-s);
|
|
5319
|
+
}
|
|
5320
|
+
|
|
5253
5321
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .gap-px {
|
|
5254
5322
|
gap: 1px;
|
|
5255
5323
|
}
|
|
@@ -5476,6 +5544,15 @@
|
|
|
5476
5544
|
}
|
|
5477
5545
|
}
|
|
5478
5546
|
|
|
5547
|
+
@layer daisyui.l1.l2 {
|
|
5548
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .badge-outline {
|
|
5549
|
+
color: var(--badge-color);
|
|
5550
|
+
--badge-bg: #0000;
|
|
5551
|
+
background-image: none;
|
|
5552
|
+
border-color: currentColor;
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
|
|
5479
5556
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[color-mix\(in_oklab\,var\(--cairn-card-border\)_70\%\,transparent\)\] {
|
|
5480
5557
|
border-color: var(--cairn-card-border);
|
|
5481
5558
|
}
|
|
@@ -5546,7 +5623,7 @@
|
|
|
5546
5623
|
}
|
|
5547
5624
|
}
|
|
5548
5625
|
|
|
5549
|
-
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[var\(--cairn-card-border\)\] {
|
|
5626
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[color\:var\(--cairn-card-border\)\], :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[var\(--cairn-card-border\)\] {
|
|
5550
5627
|
border-color: var(--cairn-card-border);
|
|
5551
5628
|
}
|
|
5552
5629
|
|
|
@@ -5564,6 +5641,10 @@
|
|
|
5564
5641
|
border-color: var(--cairn-error-border);
|
|
5565
5642
|
}
|
|
5566
5643
|
|
|
5644
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[var\(--color-base-300\)\] {
|
|
5645
|
+
border-color: var(--color-base-300);
|
|
5646
|
+
}
|
|
5647
|
+
|
|
5567
5648
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .border-\[var\(--color-positive-ink\)\]\/\[0\.22\] {
|
|
5568
5649
|
border-color: var(--color-positive-ink);
|
|
5569
5650
|
}
|
|
@@ -6245,6 +6326,18 @@
|
|
|
6245
6326
|
padding-inline: calc(var(--spacing) * 7);
|
|
6246
6327
|
}
|
|
6247
6328
|
|
|
6329
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .px-\[0\.1rem\] {
|
|
6330
|
+
padding-inline: .1rem;
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .px-\[0\.9rem\] {
|
|
6334
|
+
padding-inline: .9rem;
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6337
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .px-\[var\(--cairn-space-m\)\] {
|
|
6338
|
+
padding-inline: var(--cairn-space-m);
|
|
6339
|
+
}
|
|
6340
|
+
|
|
6248
6341
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .px-px {
|
|
6249
6342
|
padding-inline: 1px;
|
|
6250
6343
|
}
|
|
@@ -6313,10 +6406,26 @@
|
|
|
6313
6406
|
padding-block: calc(var(--spacing) * 16);
|
|
6314
6407
|
}
|
|
6315
6408
|
|
|
6409
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-\[0\.3rem\] {
|
|
6410
|
+
padding-block: .3rem;
|
|
6411
|
+
}
|
|
6412
|
+
|
|
6413
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-\[0\.5rem\] {
|
|
6414
|
+
padding-block: .5rem;
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6316
6417
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-\[5px\] {
|
|
6317
6418
|
padding-block: 5px;
|
|
6318
6419
|
}
|
|
6319
6420
|
|
|
6421
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-\[var\(--cairn-space-xl\)\] {
|
|
6422
|
+
padding-block: var(--cairn-space-xl);
|
|
6423
|
+
}
|
|
6424
|
+
|
|
6425
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-\[var\(--cairn-space-xs\)\] {
|
|
6426
|
+
padding-block: var(--cairn-space-xs);
|
|
6427
|
+
}
|
|
6428
|
+
|
|
6320
6429
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-px {
|
|
6321
6430
|
padding-block: 1px;
|
|
6322
6431
|
}
|
|
@@ -6341,6 +6450,18 @@
|
|
|
6341
6450
|
padding-top: calc(var(--spacing) * 4);
|
|
6342
6451
|
}
|
|
6343
6452
|
|
|
6453
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pt-\[var\(--cairn-space-l\)\] {
|
|
6454
|
+
padding-top: var(--cairn-space-l);
|
|
6455
|
+
}
|
|
6456
|
+
|
|
6457
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pt-\[var\(--cairn-space-s\)\] {
|
|
6458
|
+
padding-top: var(--cairn-space-s);
|
|
6459
|
+
}
|
|
6460
|
+
|
|
6461
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pr-1 {
|
|
6462
|
+
padding-right: calc(var(--spacing) * 1);
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6344
6465
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pr-3 {
|
|
6345
6466
|
padding-right: calc(var(--spacing) * 3);
|
|
6346
6467
|
}
|
|
@@ -6353,6 +6474,10 @@
|
|
|
6353
6474
|
padding-bottom: calc(var(--spacing) * 1.5);
|
|
6354
6475
|
}
|
|
6355
6476
|
|
|
6477
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pb-\[var\(--cairn-space-xl\)\] {
|
|
6478
|
+
padding-bottom: var(--cairn-space-xl);
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6356
6481
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .pl-3 {
|
|
6357
6482
|
padding-left: calc(var(--spacing) * 3);
|
|
6358
6483
|
}
|
|
@@ -6377,6 +6502,10 @@
|
|
|
6377
6502
|
text-align: right;
|
|
6378
6503
|
}
|
|
6379
6504
|
|
|
6505
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .font-\[family-name\:var\(--font-body\)\] {
|
|
6506
|
+
font-family: var(--font-body);
|
|
6507
|
+
}
|
|
6508
|
+
|
|
6380
6509
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .font-\[family-name\:var\(--font-display\)\] {
|
|
6381
6510
|
font-family: var(--font-display);
|
|
6382
6511
|
}
|
|
@@ -6482,12 +6611,34 @@
|
|
|
6482
6611
|
font-size: 1.0625rem;
|
|
6483
6612
|
}
|
|
6484
6613
|
|
|
6614
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[length\:var\(--cairn-step--1\)\] {
|
|
6615
|
+
font-size: var(--cairn-step--1);
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[length\:var\(--cairn-step-1\)\] {
|
|
6619
|
+
font-size: var(--cairn-step-1);
|
|
6620
|
+
}
|
|
6621
|
+
|
|
6622
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[length\:var\(--cairn-step-5\)\] {
|
|
6623
|
+
font-size: var(--cairn-step-5);
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6485
6626
|
@layer daisyui.l1.l2 {
|
|
6486
6627
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .textarea-sm {
|
|
6487
6628
|
font-size: max(var(--font-size, .75rem), .75rem);
|
|
6488
6629
|
}
|
|
6489
6630
|
}
|
|
6490
6631
|
|
|
6632
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .leading-\[var\(--cairn-leading-snug\)\] {
|
|
6633
|
+
--tw-leading: var(--cairn-leading-snug);
|
|
6634
|
+
line-height: var(--cairn-leading-snug);
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6637
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .leading-\[var\(--cairn-leading-tight\)\] {
|
|
6638
|
+
--tw-leading: var(--cairn-leading-tight);
|
|
6639
|
+
line-height: var(--cairn-leading-tight);
|
|
6640
|
+
}
|
|
6641
|
+
|
|
6491
6642
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .leading-relaxed {
|
|
6492
6643
|
--tw-leading: var(--leading-relaxed);
|
|
6493
6644
|
line-height: var(--leading-relaxed);
|
|
@@ -6548,6 +6699,16 @@
|
|
|
6548
6699
|
letter-spacing: .12em;
|
|
6549
6700
|
}
|
|
6550
6701
|
|
|
6702
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .tracking-\[var\(--cairn-tracking-eyebrow\)\] {
|
|
6703
|
+
--tw-tracking: var(--cairn-tracking-eyebrow);
|
|
6704
|
+
letter-spacing: var(--cairn-tracking-eyebrow);
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .tracking-\[var\(--cairn-tracking-tight\)\] {
|
|
6708
|
+
--tw-tracking: var(--cairn-tracking-tight);
|
|
6709
|
+
letter-spacing: var(--cairn-tracking-tight);
|
|
6710
|
+
}
|
|
6711
|
+
|
|
6551
6712
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .tracking-tight {
|
|
6552
6713
|
--tw-tracking: var(--tracking-tight);
|
|
6553
6714
|
letter-spacing: var(--tracking-tight);
|
|
@@ -6598,6 +6759,18 @@
|
|
|
6598
6759
|
}
|
|
6599
6760
|
}
|
|
6600
6761
|
|
|
6762
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[Npx\] {
|
|
6763
|
+
color: Npx;
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[Nrem\] {
|
|
6767
|
+
color: Nrem;
|
|
6768
|
+
}
|
|
6769
|
+
|
|
6770
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[color\:var\(--cairn-muted\)\] {
|
|
6771
|
+
color: var(--cairn-muted);
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6601
6774
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[var\(--cairn-error-ink\)\] {
|
|
6602
6775
|
color: var(--cairn-error-ink);
|
|
6603
6776
|
}
|
|
@@ -7358,6 +7531,10 @@
|
|
|
7358
7531
|
}
|
|
7359
7532
|
}
|
|
7360
7533
|
|
|
7534
|
+
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .focus\:top-\[var\(--cairn-space-s\)\]:focus {
|
|
7535
|
+
top: var(--cairn-space-s);
|
|
7536
|
+
}
|
|
7537
|
+
|
|
7361
7538
|
:where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .focus-visible\:border-\[color-mix\(in_oklab\,var\(--color-primary\)_70\%\,transparent\)\]:focus-visible {
|
|
7362
7539
|
border-color: var(--color-primary);
|
|
7363
7540
|
}
|
|
@@ -46,9 +46,13 @@ export function buildPreviewDoc(html, preview) {
|
|
|
46
46
|
// parent's base URL, so a clicked fragment or root link could render the admin login inside
|
|
47
47
|
// the frame. Targeting every link at a new tab turns each click into a popup, and the sandbox
|
|
48
48
|
// (which grants no allow-popups) blocks it, so a proofing click goes nowhere.
|
|
49
|
+
// The marker on the root lets a site scope an entrance animation (driven off [data-rise]) away
|
|
50
|
+
// from the preview, which shows the resting state of content and runs the same pipeline; without
|
|
51
|
+
// it, content would re-animate on every debounced render. cairn provides the hook; the site owns
|
|
52
|
+
// its animation and decides what to suppress under [data-cairn-preview].
|
|
49
53
|
return [
|
|
50
54
|
'<!doctype html>',
|
|
51
|
-
'<html>',
|
|
55
|
+
'<html data-cairn-preview>',
|
|
52
56
|
'<head>',
|
|
53
57
|
'<meta charset="utf-8">',
|
|
54
58
|
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
@@ -36,7 +36,7 @@ const DIVERGENCE_FRACTION = 0.5;
|
|
|
36
36
|
// text rather than going through extractMediaRefs for two reasons. First, a true MULTISET is the
|
|
37
37
|
// invariant a backstop wants: extractMediaRefs dedups by hash, so a doubled token collapsing to one
|
|
38
38
|
// would read as equal, and the validator must catch a dropped duplicate. Second, the raw scan covers
|
|
39
|
-
// the whole text including frontmatter without threading the concept's
|
|
39
|
+
// the whole text including frontmatter without threading the concept's NamedField[] to the call
|
|
40
40
|
// site, which the validator otherwise has no reason to know. A token mangled inside a code fence is
|
|
41
41
|
// caught here too, redundantly with the code check, which is the right posture for a backstop.
|
|
42
42
|
const MEDIA_TOKEN = /media:[A-Za-z0-9.-]+/g;
|
package/dist/content/adapter.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
// Fail closed on an inconsistent island registry: a hydrate component with no live component, or a
|
|
2
|
+
// registered island with no hydrate component. Either is a wiring mistake the site author should see at
|
|
3
|
+
// build time, not a silent forever-fallback. Read-only over the rendering group; imports no runtime.
|
|
4
|
+
function assertIslandsConsistent(rendering) {
|
|
5
|
+
const islands = rendering.islands ?? {};
|
|
6
|
+
const hydrated = new Set((rendering.components?.defs ?? []).filter((d) => d.hydrate).map((d) => d.name));
|
|
7
|
+
for (const name of hydrated) {
|
|
8
|
+
if (!(name in islands)) {
|
|
9
|
+
throw new Error(`cairn: component '${name}' declares hydrate but rendering.islands has no entry for it.`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
for (const name of Object.keys(islands)) {
|
|
13
|
+
if (!hydrated.has(name)) {
|
|
14
|
+
throw new Error(`cairn: rendering.islands has '${name}' but no component declares hydrate for it.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
1
18
|
/** Declare a site's adapter while preserving each concept's concrete schema type for typed reads. */
|
|
2
19
|
export function defineAdapter(adapter) {
|
|
20
|
+
assertIslandsConsistent(adapter.rendering);
|
|
3
21
|
return adapter;
|
|
4
22
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ConceptDescriptor } from './types.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Backend } from '../github/backend.js';
|
|
3
3
|
import type { Manifest } from './manifest.js';
|
|
4
4
|
/** One action an advisory offers, as a label and an optional link target. */
|
|
5
5
|
export interface AdvisoryAction {
|
|
@@ -45,7 +45,7 @@ export declare function mainAddressIndex(manifest: Manifest): AddressIndex;
|
|
|
45
45
|
* and skipped, so a transient failure degrades to a thinner index, never a thrown editor or a blocked
|
|
46
46
|
* publish. The branches are read in one Promise.all, the way buildUsageIndex reads them.
|
|
47
47
|
*/
|
|
48
|
-
export declare function buildAddressIndex(
|
|
48
|
+
export declare function buildAddressIndex(backend: Backend, concepts: ConceptDescriptor[], manifest: Manifest): Promise<AddressIndex>;
|
|
49
49
|
/**
|
|
50
50
|
* Find the first other entry that already resolves to an address, or null when the address is free
|
|
51
51
|
* or holds only the entry itself. The self entry is identified by its concept and id together.
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { listBranches } from '../github/branches.js';
|
|
2
|
-
import { readRaw } from '../github/repo.js';
|
|
3
1
|
import { PENDING_PREFIX, parsePendingBranch } from './pending.js';
|
|
4
2
|
import { findConcept } from './concepts.js';
|
|
5
3
|
import { isValidId, filenameFromId } from './ids.js';
|
|
@@ -37,13 +35,13 @@ export function mainAddressIndex(manifest) {
|
|
|
37
35
|
* and skipped, so a transient failure degrades to a thinner index, never a thrown editor or a blocked
|
|
38
36
|
* publish. The branches are read in one Promise.all, the way buildUsageIndex reads them.
|
|
39
37
|
*/
|
|
40
|
-
export async function buildAddressIndex(
|
|
38
|
+
export async function buildAddressIndex(backend, concepts, manifest) {
|
|
41
39
|
// The main arm: the manifest already carries each entry's resolved permalink, so seed from the
|
|
42
40
|
// synchronous main-only index and union the branch arm on top.
|
|
43
41
|
const index = mainAddressIndex(manifest);
|
|
44
42
|
// The branch arm: read each open cairn/* branch's one edited file and resolve its permalink. The
|
|
45
43
|
// path is derivable from the branch name, so no tree-listing is needed.
|
|
46
|
-
const names = await listBranches(
|
|
44
|
+
const names = await backend.listBranches(PENDING_PREFIX);
|
|
47
45
|
const perBranch = await Promise.all(names.map(async (name) => {
|
|
48
46
|
// Resolve the branch name with the branch tooling's guard: a malformed name, an id that fails
|
|
49
47
|
// the slug rule, or an unconfigured concept is skipped with no read attempted.
|
|
@@ -55,7 +53,7 @@ export async function buildAddressIndex(repo, token, concepts, manifest) {
|
|
|
55
53
|
return null;
|
|
56
54
|
const path = `${concept.dir}/${filenameFromId(ref.id)}`;
|
|
57
55
|
try {
|
|
58
|
-
const raw = await
|
|
56
|
+
const raw = await backend.readFile(path, name);
|
|
59
57
|
if (raw === null)
|
|
60
58
|
return null; // The file is absent on the branch: nothing to resolve.
|
|
61
59
|
const { frontmatter } = parseMarkdown(raw);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { CairnAdapter, CairnExtension, CairnRuntime } from './types.js';
|
|
2
2
|
import { type SiteConfig } from '../nav/site-config.js';
|
|
3
3
|
/**
|
|
4
|
-
* The input to {@link composeRuntime}. `siteConfig` is required
|
|
5
|
-
*
|
|
6
|
-
* adapter's concepts.
|
|
4
|
+
* The input to {@link composeRuntime}. `siteConfig` is required: it is the canonical home for the
|
|
5
|
+
* site name, the spellcheck dialect, and the tidy block, so they can never be silently dropped.
|
|
6
|
+
* `extensions` fold in after the adapter's concepts.
|
|
7
7
|
*/
|
|
8
8
|
export interface ComposeInput {
|
|
9
9
|
adapter: CairnAdapter;
|
|
@@ -11,9 +11,10 @@ export interface ComposeInput {
|
|
|
11
11
|
extensions?: CairnExtension[];
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Fold an adapter and any extensions into the composed runtime (seam 2).
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Fold an adapter and any extensions into the composed runtime (seam 2). This is the one place the
|
|
15
|
+
* grouped adapter maps onto the flat runtime, and the one place the internal manifest and dictionary
|
|
16
|
+
* paths default by convention. Each concept declares its own routing and URL policy, so the runtime
|
|
17
|
+
* and delivery permalinks cannot diverge. Extension concepts merge after the adapter's. The media slot
|
|
17
18
|
* (seam 4) passes through untouched.
|
|
18
19
|
*/
|
|
19
20
|
export declare function composeRuntime({ adapter, siteConfig, extensions }: ComposeInput): CairnRuntime;
|
package/dist/content/compose.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { resolveConcepts } from './concepts.js';
|
|
2
2
|
import { normalizeAssets } from '../media/config.js';
|
|
3
3
|
import { dictionaryFileForDialect } from '../nav/site-config.js';
|
|
4
|
+
// The internal artifact paths the adapter does not carry. They share the `.cairn/` content root the
|
|
5
|
+
// manifests use, so `composeRuntime` defaults them by convention rather than reading them off config.
|
|
6
|
+
// The personal dictionary sits beside the manifests, so the spec's `content/.cairn/dictionary.txt`
|
|
7
|
+
// resolves the same configurable way the manifest paths do.
|
|
8
|
+
const CONTENT_MANIFEST_PATH = 'src/content/.cairn/index.json';
|
|
9
|
+
const MEDIA_MANIFEST_PATH = 'src/content/.cairn/media.json';
|
|
10
|
+
const DICTIONARY_PATH = 'src/content/.cairn/dictionary.txt';
|
|
4
11
|
/**
|
|
5
|
-
* Fold an adapter and any extensions into the composed runtime (seam 2).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
12
|
+
* Fold an adapter and any extensions into the composed runtime (seam 2). This is the one place the
|
|
13
|
+
* grouped adapter maps onto the flat runtime, and the one place the internal manifest and dictionary
|
|
14
|
+
* paths default by convention. Each concept declares its own routing and URL policy, so the runtime
|
|
15
|
+
* and delivery permalinks cannot diverge. Extension concepts merge after the adapter's. The media slot
|
|
8
16
|
* (seam 4) passes through untouched.
|
|
9
17
|
*/
|
|
10
18
|
export function composeRuntime({ adapter, siteConfig, extensions = [] }) {
|
|
11
19
|
if (!siteConfig)
|
|
12
|
-
throw new Error('composeRuntime needs a site config
|
|
20
|
+
throw new Error('composeRuntime needs a site config for the site name and editor settings');
|
|
13
21
|
const content = { ...adapter.content };
|
|
14
22
|
const adminPanels = [];
|
|
15
23
|
const fieldTypes = [];
|
|
@@ -24,23 +32,21 @@ export function composeRuntime({ adapter, siteConfig, extensions = [] }) {
|
|
|
24
32
|
fieldTypes.push(...extension.fieldTypes);
|
|
25
33
|
}
|
|
26
34
|
return {
|
|
27
|
-
siteName:
|
|
28
|
-
concepts: resolveConcepts(content
|
|
35
|
+
siteName: siteConfig.siteName,
|
|
36
|
+
concepts: resolveConcepts(content),
|
|
29
37
|
backend: adapter.backend,
|
|
30
|
-
sender: adapter.
|
|
31
|
-
supportContact: adapter.supportContact,
|
|
32
|
-
render: adapter.render,
|
|
33
|
-
manifestPath:
|
|
34
|
-
registry: adapter.
|
|
35
|
-
icons: adapter.icons,
|
|
36
|
-
navMenu: adapter.
|
|
37
|
-
preview: adapter.preview,
|
|
38
|
-
assets: adapter.
|
|
39
|
-
resolvedAssets: normalizeAssets(adapter.
|
|
40
|
-
mediaManifestPath:
|
|
41
|
-
|
|
42
|
-
// spec's `content/.cairn/dictionary.txt` resolves the same configurable way the manifest paths do.
|
|
43
|
-
dictionaryPath: adapter.dictionaryPath ?? 'src/content/.cairn/dictionary.txt',
|
|
38
|
+
sender: adapter.email,
|
|
39
|
+
supportContact: adapter.editor?.supportContact,
|
|
40
|
+
render: adapter.rendering.render,
|
|
41
|
+
manifestPath: CONTENT_MANIFEST_PATH,
|
|
42
|
+
registry: adapter.rendering.components,
|
|
43
|
+
icons: adapter.rendering.icons,
|
|
44
|
+
navMenu: adapter.editor?.nav,
|
|
45
|
+
preview: adapter.editor?.preview,
|
|
46
|
+
assets: adapter.media,
|
|
47
|
+
resolvedAssets: normalizeAssets(adapter.media),
|
|
48
|
+
mediaManifestPath: MEDIA_MANIFEST_PATH,
|
|
49
|
+
dictionaryPath: DICTIONARY_PATH,
|
|
44
50
|
// The spellcheck dictionary is resolved once here from the site config's dialect (default US),
|
|
45
51
|
// so the runtime and the editor never re-derive it. The site config is the one home for the
|
|
46
52
|
// dialect; the editor resolves this filename to a real asset URL on the main thread.
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import type { ConceptConfig, ConceptDescriptor, ConceptUrlPolicy, RoutingRule } from './types.js';
|
|
2
|
-
|
|
2
|
+
/** Expand a concept's routing shorthand to a concrete rule. The single resolution point: omitted is `page`. */
|
|
3
|
+
export declare function resolveRouting(routing: ConceptConfig['routing']): RoutingRule;
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Declare a concept while preserving its fieldset type for typed reads, and validate its URL policy at
|
|
6
|
+
* declaration so a bad permalink or datePrefix fails at module load rather than at a defaulted render.
|
|
7
|
+
* Mirrors {@link defineAdapter}; the validation is the build-independent net for a concept with no entries.
|
|
7
8
|
*/
|
|
8
|
-
export declare const
|
|
9
|
+
export declare function defineConcept<const C extends ConceptConfig>(concept: C): C;
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* otherwise; `datePrefix` defaults to `day`). `routing` is injectable so a contract test can prove
|
|
14
|
-
* a new concept attaches additively; production passes the default `CONCEPT_ROUTING`.
|
|
11
|
+
* Validate one concept's URL policy at build, so a misconfigured permalink or datePrefix fails loudly
|
|
12
|
+
* here rather than emitting a wrong or defaulted URL at render. The permalink must be root-relative and
|
|
13
|
+
* use only known tokens, a date token requires a dated concept, and the datePrefix must be in range.
|
|
15
14
|
*/
|
|
16
|
-
export declare function
|
|
15
|
+
export declare function validateUrlPolicy(id: string, policy: ConceptUrlPolicy, dated: boolean): void;
|
|
17
16
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Normalize an adapter's declared concepts into uniform descriptors (seam 1). Each concept declares its
|
|
18
|
+
* own routing (a shorthand or an explicit rule, resolved by `resolveRouting`) and URL policy
|
|
19
|
+
* (`permalink`, `datePrefix`) on the config; both default when omitted (`/:slug` for Pages, `/<id>/:slug`
|
|
20
|
+
* otherwise; `datePrefix` defaults to `day`). A new concept attaches by adding one key under `content`.
|
|
21
21
|
*/
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function normalizeConcepts(content: Record<string, ConceptConfig | undefined>): ConceptDescriptor[];
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a site's concept descriptors from its content map. The admin runtime (composeRuntime) and the
|
|
25
|
+
* delivery layer (siteDescriptors) both call this, so the per-concept routing and URL policy are derived
|
|
26
|
+
* once from the concept declarations and the runtime and delivery permalinks cannot diverge.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveConcepts(content: Record<string, ConceptConfig | undefined>): ConceptDescriptor[];
|
|
23
29
|
/** Look up a normalized concept by id, or undefined when the site does not enable it. */
|
|
24
30
|
export declare function findConcept(concepts: ConceptDescriptor[], id: string): ConceptDescriptor | undefined;
|