@glw907/cairn-cms 0.5.0 → 0.5.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/dist/adapter.d.ts +24 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/auth/capabilities.d.ts +7 -0
- package/dist/auth/capabilities.d.ts.map +1 -0
- package/dist/auth/capabilities.js +26 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/components/AdminLayout.svelte +72 -16
- package/dist/components/AdminLayout.svelte.d.ts +9 -0
- package/dist/components/AdminLayout.svelte.d.ts.map +1 -1
- package/dist/components/CollectionList.svelte +96 -0
- package/dist/components/CollectionList.svelte.d.ts +8 -0
- package/dist/components/CollectionList.svelte.d.ts.map +1 -0
- package/dist/components/ComponentPalette.svelte +34 -0
- package/dist/components/ComponentPalette.svelte.d.ts +9 -0
- package/dist/components/ComponentPalette.svelte.d.ts.map +1 -0
- package/dist/components/EditPage.svelte +66 -28
- package/dist/components/EditPage.svelte.d.ts +2 -0
- package/dist/components/EditPage.svelte.d.ts.map +1 -1
- package/dist/components/NavTree.svelte +128 -0
- package/dist/components/NavTree.svelte.d.ts +8 -0
- package/dist/components/NavTree.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -1
- package/dist/editor.d.ts +25 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +20 -0
- package/dist/frontmatter.d.ts +3 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/nav.d.ts +58 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +86 -0
- package/dist/slug.d.ts +7 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +15 -0
- package/dist/sveltekit/index.d.ts +102 -12
- package/dist/sveltekit/index.d.ts.map +1 -1
- package/dist/sveltekit/index.js +219 -20
- package/package.json +7 -2
- package/src/lib/adapter.ts +25 -0
- package/src/lib/auth/capabilities.ts +35 -0
- package/src/lib/auth/index.ts +1 -0
- package/src/lib/components/AdminLayout.svelte +72 -16
- package/src/lib/components/CollectionList.svelte +96 -0
- package/src/lib/components/ComponentPalette.svelte +34 -0
- package/src/lib/components/EditPage.svelte +66 -28
- package/src/lib/components/NavTree.svelte +128 -0
- package/src/lib/components/index.ts +3 -1
- package/src/lib/editor.ts +38 -0
- package/src/lib/frontmatter.ts +17 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/nav.ts +117 -0
- package/src/lib/slug.ts +16 -0
- package/src/lib/sveltekit/index.ts +303 -26
- package/dist/components/AdminList.svelte +0 -33
- package/dist/components/AdminList.svelte.d.ts +0 -10
- package/dist/components/AdminList.svelte.d.ts.map +0 -1
- package/src/lib/components/AdminList.svelte +0 -33
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The navigation tree editor (Pass L). Edits a local copy of the menu tree and posts the whole
|
|
3
|
+
// tree as JSON to the `save` action. DaisyUI primitives under the Warm Stone admin theme. Drag a
|
|
4
|
+
// row up or down to reorder within its level; use Indent/Outdent to nest under the previous
|
|
5
|
+
// sibling or promote a level (capped at the menu's maxDepth). The engine validates on save.
|
|
6
|
+
import { untrack } from 'svelte';
|
|
7
|
+
import type { NavLoadData } from '../sveltekit';
|
|
8
|
+
import type { NavNode } from '../nav';
|
|
9
|
+
|
|
10
|
+
let { data }: { data: NavLoadData } = $props();
|
|
11
|
+
|
|
12
|
+
// A flat, ordered working model is far simpler to drag-edit than a recursive one: each row
|
|
13
|
+
// carries an explicit depth, and the tree is rebuilt from (order + depth) only at submit time.
|
|
14
|
+
interface Row {
|
|
15
|
+
id: number;
|
|
16
|
+
depth: number;
|
|
17
|
+
label: string;
|
|
18
|
+
url: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let nextId = 1;
|
|
22
|
+
function flatten(nodes: NavNode[], depth: number, out: Row[]): Row[] {
|
|
23
|
+
for (const n of nodes) {
|
|
24
|
+
out.push({ id: nextId++, depth, label: n.label, url: n.url ?? '' });
|
|
25
|
+
if (n.children?.length) flatten(n.children, depth + 1, out);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let rows = $state<Row[]>(untrack(() => flatten(data.tree, 0, [])));
|
|
31
|
+
const maxDepthIndex = $derived(data.menu.maxDepth - 1); // depth is 0-based here
|
|
32
|
+
|
|
33
|
+
// Rebuild the nested tree from the flat rows by depth, then serialize for the hidden field.
|
|
34
|
+
function toTree(list: Row[]): NavNode[] {
|
|
35
|
+
const root: NavNode[] = [];
|
|
36
|
+
const stack: { depth: number; node: NavNode }[] = [];
|
|
37
|
+
for (const r of list) {
|
|
38
|
+
const node: NavNode = { label: r.label.trim() };
|
|
39
|
+
if (r.url.trim()) node.url = r.url.trim();
|
|
40
|
+
while (stack.length && stack[stack.length - 1].depth >= r.depth) stack.pop();
|
|
41
|
+
if (stack.length) (stack[stack.length - 1].node.children ??= []).push(node);
|
|
42
|
+
else root.push(node);
|
|
43
|
+
stack.push({ depth: r.depth, node });
|
|
44
|
+
}
|
|
45
|
+
return root;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const treeJson = $derived(JSON.stringify(toTree(rows)));
|
|
49
|
+
|
|
50
|
+
function addRow() {
|
|
51
|
+
rows = [...rows, { id: nextId++, depth: 0, label: 'New item', url: '' }];
|
|
52
|
+
}
|
|
53
|
+
function removeRow(id: number) {
|
|
54
|
+
rows = rows.filter((r) => r.id !== id);
|
|
55
|
+
}
|
|
56
|
+
function indent(i: number) {
|
|
57
|
+
// A row may nest at most one level deeper than the row above it, and never past the cap.
|
|
58
|
+
if (i === 0) return;
|
|
59
|
+
const ceiling = Math.min(rows[i - 1].depth + 1, maxDepthIndex);
|
|
60
|
+
if (rows[i].depth < ceiling) rows[i].depth += 1;
|
|
61
|
+
}
|
|
62
|
+
function outdent(i: number) {
|
|
63
|
+
if (rows[i].depth > 0) rows[i].depth -= 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let dragFrom = $state<number | null>(null);
|
|
67
|
+
function onDrop(to: number) {
|
|
68
|
+
if (dragFrom === null || dragFrom === to) return;
|
|
69
|
+
const next = [...rows];
|
|
70
|
+
const [moved] = next.splice(dragFrom, 1);
|
|
71
|
+
next.splice(to, 0, moved);
|
|
72
|
+
rows = next;
|
|
73
|
+
dragFrom = null;
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<div class="cairn-admin">
|
|
78
|
+
<div class="flex items-center justify-between">
|
|
79
|
+
<h1 class="text-xl font-semibold">{data.menu.label}</h1>
|
|
80
|
+
<button type="button" class="btn btn-sm" onclick={addRow}>Add item</button>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{#if data.saved}
|
|
84
|
+
<div class="alert alert-success mt-3">Navigation saved.</div>
|
|
85
|
+
{/if}
|
|
86
|
+
{#if data.error}
|
|
87
|
+
<div class="alert alert-error mt-3">{data.error}</div>
|
|
88
|
+
{/if}
|
|
89
|
+
|
|
90
|
+
<form method="POST" action="?/save" class="mt-4">
|
|
91
|
+
<input type="hidden" name="tree" value={treeJson} />
|
|
92
|
+
<ul class="menu w-full gap-1">
|
|
93
|
+
{#each rows as row, i (row.id)}
|
|
94
|
+
<li
|
|
95
|
+
draggable="true"
|
|
96
|
+
ondragstart={() => (dragFrom = i)}
|
|
97
|
+
ondragover={(e) => e.preventDefault()}
|
|
98
|
+
ondrop={() => onDrop(i)}
|
|
99
|
+
style={`margin-left:${row.depth * 1.5}rem`}
|
|
100
|
+
>
|
|
101
|
+
<div class="flex items-center gap-2 p-2">
|
|
102
|
+
<span class="cursor-grab opacity-40" aria-hidden="true">⠿</span>
|
|
103
|
+
<input class="input input-sm input-bordered flex-1" placeholder="Label" bind:value={row.label} />
|
|
104
|
+
<input
|
|
105
|
+
class="input input-sm input-bordered flex-1"
|
|
106
|
+
placeholder="/path or https://…"
|
|
107
|
+
list="cairn-nav-pages"
|
|
108
|
+
bind:value={row.url}
|
|
109
|
+
/>
|
|
110
|
+
<button type="button" class="btn btn-xs btn-ghost" onclick={() => outdent(i)} aria-label="Outdent">←</button>
|
|
111
|
+
<button type="button" class="btn btn-xs btn-ghost" onclick={() => indent(i)} aria-label="Indent">→</button>
|
|
112
|
+
<button type="button" class="btn btn-xs btn-ghost text-error" onclick={() => removeRow(row.id)} aria-label="Remove">×</button>
|
|
113
|
+
</div>
|
|
114
|
+
</li>
|
|
115
|
+
{/each}
|
|
116
|
+
</ul>
|
|
117
|
+
|
|
118
|
+
<datalist id="cairn-nav-pages">
|
|
119
|
+
{#each data.pages as p (p.url)}
|
|
120
|
+
<option value={p.url}>{p.label}</option>
|
|
121
|
+
{/each}
|
|
122
|
+
</datalist>
|
|
123
|
+
|
|
124
|
+
<div class="mt-4">
|
|
125
|
+
<button type="submit" class="btn btn-primary btn-sm">Save navigation</button>
|
|
126
|
+
</div>
|
|
127
|
+
</form>
|
|
128
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NavLoadData } from '../sveltekit';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: NavLoadData;
|
|
4
|
+
};
|
|
5
|
+
declare const NavTree: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type NavTree = ReturnType<typeof NavTree>;
|
|
7
|
+
export default NavTree;
|
|
8
|
+
//# sourceMappingURL=NavTree.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavTree.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/NavTree.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAY/C,KAAK,gBAAgB,GAAI;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAiHhD,QAAA,MAAM,OAAO,sDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { default as AdminLayout } from './AdminLayout.svelte';
|
|
2
|
-
export { default as
|
|
2
|
+
export { default as CollectionList } from './CollectionList.svelte';
|
|
3
3
|
export { default as LoginPage } from './LoginPage.svelte';
|
|
4
4
|
export { default as ConfirmPage } from './ConfirmPage.svelte';
|
|
5
5
|
export { default as EditPage } from './EditPage.svelte';
|
|
6
6
|
export { default as ManageAdmins } from './ManageAdmins.svelte';
|
|
7
|
+
export { default as ComponentPalette } from './ComponentPalette.svelte';
|
|
8
|
+
export { default as NavTree } from './NavTree.svelte';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/components/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// cairn-cms admin UI shell. Consumers import from 'cairn-cms/components'; each site's
|
|
2
2
|
// admin route `.svelte` files are one-line shims around these.
|
|
3
3
|
export { default as AdminLayout } from './AdminLayout.svelte';
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as CollectionList } from './CollectionList.svelte';
|
|
5
5
|
export { default as LoginPage } from './LoginPage.svelte';
|
|
6
6
|
export { default as ConfirmPage } from './ConfirmPage.svelte';
|
|
7
7
|
export { default as EditPage } from './EditPage.svelte';
|
|
8
8
|
export { default as ManageAdmins } from './ManageAdmins.svelte';
|
|
9
|
+
export { default as ComponentPalette } from './ComponentPalette.svelte';
|
|
10
|
+
export { default as NavTree } from './NavTree.svelte';
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface CartaInput {
|
|
2
|
+
getSelection(): {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
direction: string;
|
|
6
|
+
slice: string;
|
|
7
|
+
};
|
|
8
|
+
insertAt(position: number, text: string): void;
|
|
9
|
+
}
|
|
10
|
+
interface CartaLike {
|
|
11
|
+
input?: CartaInput;
|
|
12
|
+
}
|
|
13
|
+
/** The programmatic editing surface the admin relies on. */
|
|
14
|
+
export interface MarkdownEditor {
|
|
15
|
+
/** Insert a component or template at the current cursor position. */
|
|
16
|
+
insertComponent(template: string): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wrap a Carta instance as a MarkdownEditor. Takes a getter (not the instance) because the
|
|
20
|
+
* EditPage component creates the Carta instance once and `carta.input` is only populated after
|
|
21
|
+
* the editor mounts; reading it lazily at call time avoids capturing an undefined `input`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function cartaEditor(getCarta: () => CartaLike): MarkdownEditor;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/lib/editor.ts"],"names":[],"mappings":"AAQA,UAAU,UAAU;IAClB,YAAY,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAChD;AAED,UAAU,SAAS;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,SAAS,GAAG,cAAc,CASrE"}
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// cairn-core: the editor cursor seam (decision P3). The component palette and any later insert
|
|
2
|
+
// control talk to MarkdownEditor, never to Carta directly, so a swap to a different editing
|
|
3
|
+
// engine is contained to this file. Verified against carta-md@4.11: `input.getSelection()` and
|
|
4
|
+
// `input.insertAt(pos, text)` are public on the InputEnhancer.
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a Carta instance as a MarkdownEditor. Takes a getter (not the instance) because the
|
|
7
|
+
* EditPage component creates the Carta instance once and `carta.input` is only populated after
|
|
8
|
+
* the editor mounts; reading it lazily at call time avoids capturing an undefined `input`.
|
|
9
|
+
*/
|
|
10
|
+
export function cartaEditor(getCarta) {
|
|
11
|
+
return {
|
|
12
|
+
insertComponent(template) {
|
|
13
|
+
const input = getCarta().input;
|
|
14
|
+
if (!input)
|
|
15
|
+
return; // editor not mounted yet; nothing to insert into
|
|
16
|
+
const { start } = input.getSelection();
|
|
17
|
+
input.insertAt(start, template);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/lib/frontmatter.ts"],"names":[],"mappings":"AAMA,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASrD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// cairn-core: coerce a frontmatter value to the YYYY-MM-DD string an <input type="date"> wants.
|
|
2
|
+
// gray-matter parses an unquoted YAML date (date: 2026-05-14) into a JS Date, so a string-only
|
|
3
|
+
// read leaves the date input empty and drops the date on save. This normalizes a Date or an
|
|
4
|
+
// ISO-ish string to YYYY-MM-DD. A parsed YAML date is UTC midnight, so slicing the ISO string
|
|
5
|
+
// avoids a local-timezone shift. Internal (not re-exported from the barrel), like utils.ts.
|
|
6
|
+
/** A frontmatter date value (Date or string) to the `YYYY-MM-DD` an `<input type="date">` expects. */
|
|
7
|
+
export function dateInputValue(value) {
|
|
8
|
+
if (value instanceof Date) {
|
|
9
|
+
return Number.isNaN(value.getTime()) ? '' : value.toISOString().slice(0, 10);
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
const match = value.match(/^\d{4}-\d{2}-\d{2}/);
|
|
13
|
+
return match ? match[0] : '';
|
|
14
|
+
}
|
|
15
|
+
return '';
|
|
16
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/nav.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** One navigation node. `url` omitted/empty is a label-only grouping header; `children` omitted is a leaf. */
|
|
2
|
+
export interface NavNode {
|
|
3
|
+
label: string;
|
|
4
|
+
url?: string;
|
|
5
|
+
children?: NavNode[];
|
|
6
|
+
}
|
|
7
|
+
/** Total node cap across the whole tree, a guard against a runaway payload. */
|
|
8
|
+
export declare const MAX_NAV_NODES = 200;
|
|
9
|
+
export declare class NavValidationError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validate and normalize an untrusted value into a NavNode[]: arrays only, non-empty labels,
|
|
14
|
+
* depth within `maxDepth` (1 = flat), bounded node count, and only the three known keys kept.
|
|
15
|
+
* Throws NavValidationError on any violation. Used by `navSave` before writing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateNavTree(value: unknown, maxDepth: number): NavNode[];
|
|
18
|
+
/**
|
|
19
|
+
* Shape of the YAML site-config file. Unknown keys are ignored so the file can grow without
|
|
20
|
+
* an engine change. Read at build time by the public site.
|
|
21
|
+
*/
|
|
22
|
+
export interface SiteConfig {
|
|
23
|
+
siteName: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
author?: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
locale?: string;
|
|
28
|
+
/** Named navigation menus, each a NavNode[] (normalized by extractMenu). */
|
|
29
|
+
menus?: Record<string, unknown>;
|
|
30
|
+
email?: {
|
|
31
|
+
sender?: string;
|
|
32
|
+
senderName?: string;
|
|
33
|
+
};
|
|
34
|
+
footer?: {
|
|
35
|
+
copyrightName?: string;
|
|
36
|
+
};
|
|
37
|
+
settings?: {
|
|
38
|
+
feedMaxItems?: number;
|
|
39
|
+
homepageFeaturedCount?: number;
|
|
40
|
+
postTags?: string[];
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export declare class SiteConfigError extends Error {
|
|
45
|
+
constructor(message: string);
|
|
46
|
+
}
|
|
47
|
+
/** Parse the YAML site-config text into a typed object. Throws SiteConfigError on a malformed root. */
|
|
48
|
+
export declare function parseSiteConfig(raw: string): SiteConfig;
|
|
49
|
+
/** Extract one named menu from a parsed config and validate it. Returns [] when the menu is absent. */
|
|
50
|
+
export declare function extractMenu(config: SiteConfig, name: string, maxDepth: number): NavNode[];
|
|
51
|
+
/**
|
|
52
|
+
* Replace one named menu in the YAML site-config text and re-serialize, preserving every other
|
|
53
|
+
* top-level key (siteName, other menus, settings, ...). The `/admin/nav` editor commits the result.
|
|
54
|
+
* Parses into a Document so the rest of the file round-trips; YAML comments are not preserved
|
|
55
|
+
* (an accepted trade), but data keys are. A leaf node serializes without `url`/`children` keys.
|
|
56
|
+
*/
|
|
57
|
+
export declare function setMenu(raw: string, name: string, tree: NavNode[]): string;
|
|
58
|
+
//# sourceMappingURL=nav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/lib/nav.ts"],"names":[],"mappings":"AAOA,8GAA8G;AAC9G,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,+EAA+E;AAC/E,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAuB3E;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,MAAM,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,QAAQ,CAAC,EAAE;QACT,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAUvD;AAED,uGAAuG;AACvG,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAIzF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAO1E"}
|
package/dist/nav.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// cairn-core: the navigation tree. A menu lives in the site's git-committed `site.config.yaml`
|
|
2
|
+
// under `menus.<name>`, read at build time by the public layout and edited from `/admin/nav`,
|
|
3
|
+
// which commits the file back through the GitHub-App pipeline. The engine returns data only; each
|
|
4
|
+
// site renders the tree with its own header markup.
|
|
5
|
+
import { parse as parseYaml, parseDocument } from 'yaml';
|
|
6
|
+
/** Total node cap across the whole tree, a guard against a runaway payload. */
|
|
7
|
+
export const MAX_NAV_NODES = 200;
|
|
8
|
+
export class NavValidationError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'NavValidationError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate and normalize an untrusted value into a NavNode[]: arrays only, non-empty labels,
|
|
16
|
+
* depth within `maxDepth` (1 = flat), bounded node count, and only the three known keys kept.
|
|
17
|
+
* Throws NavValidationError on any violation. Used by `navSave` before writing.
|
|
18
|
+
*/
|
|
19
|
+
export function validateNavTree(value, maxDepth) {
|
|
20
|
+
let count = 0;
|
|
21
|
+
function walk(nodes, depth) {
|
|
22
|
+
if (!Array.isArray(nodes))
|
|
23
|
+
throw new NavValidationError('Navigation must be a list of items');
|
|
24
|
+
if (depth > maxDepth)
|
|
25
|
+
throw new NavValidationError(`Navigation is nested deeper than ${maxDepth} levels`);
|
|
26
|
+
return nodes.map((raw) => {
|
|
27
|
+
if (typeof raw !== 'object' || raw === null)
|
|
28
|
+
throw new NavValidationError('Each item must be an object');
|
|
29
|
+
const item = raw;
|
|
30
|
+
const label = typeof item.label === 'string' ? item.label.trim() : '';
|
|
31
|
+
if (!label)
|
|
32
|
+
throw new NavValidationError('Each item needs a label');
|
|
33
|
+
if (++count > MAX_NAV_NODES)
|
|
34
|
+
throw new NavValidationError('Too many navigation items');
|
|
35
|
+
const node = { label };
|
|
36
|
+
if (typeof item.url === 'string' && item.url.trim())
|
|
37
|
+
node.url = item.url.trim();
|
|
38
|
+
if (item.children !== undefined) {
|
|
39
|
+
const children = walk(item.children, depth + 1);
|
|
40
|
+
if (children.length)
|
|
41
|
+
node.children = children;
|
|
42
|
+
}
|
|
43
|
+
return node;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return walk(value, 1);
|
|
47
|
+
}
|
|
48
|
+
export class SiteConfigError extends Error {
|
|
49
|
+
constructor(message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = 'SiteConfigError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Parse the YAML site-config text into a typed object. Throws SiteConfigError on a malformed root. */
|
|
55
|
+
export function parseSiteConfig(raw) {
|
|
56
|
+
const parsed = parseYaml(raw);
|
|
57
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
58
|
+
throw new SiteConfigError('Site config must be a YAML mapping');
|
|
59
|
+
}
|
|
60
|
+
const { siteName } = parsed;
|
|
61
|
+
if (typeof siteName !== 'string' || !siteName.trim()) {
|
|
62
|
+
throw new SiteConfigError('Site config needs a siteName');
|
|
63
|
+
}
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
/** Extract one named menu from a parsed config and validate it. Returns [] when the menu is absent. */
|
|
67
|
+
export function extractMenu(config, name, maxDepth) {
|
|
68
|
+
const menu = config.menus?.[name];
|
|
69
|
+
if (menu === undefined)
|
|
70
|
+
return [];
|
|
71
|
+
return validateNavTree(menu, maxDepth);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Replace one named menu in the YAML site-config text and re-serialize, preserving every other
|
|
75
|
+
* top-level key (siteName, other menus, settings, ...). The `/admin/nav` editor commits the result.
|
|
76
|
+
* Parses into a Document so the rest of the file round-trips; YAML comments are not preserved
|
|
77
|
+
* (an accepted trade), but data keys are. A leaf node serializes without `url`/`children` keys.
|
|
78
|
+
*/
|
|
79
|
+
export function setMenu(raw, name, tree) {
|
|
80
|
+
const doc = parseDocument(raw);
|
|
81
|
+
if (doc.get('siteName') === undefined) {
|
|
82
|
+
throw new SiteConfigError('Site config must be a mapping with a siteName');
|
|
83
|
+
}
|
|
84
|
+
doc.setIn(['menus', name], tree);
|
|
85
|
+
return doc.toString();
|
|
86
|
+
}
|
package/dist/slug.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lowercase a title into a filename-safe slug stem.
|
|
3
|
+
* Apostrophes are dropped so "Geoff's" becomes "geoffs" (no spurious hyphen).
|
|
4
|
+
* All other non-alphanumeric runs become a single hyphen; leading/trailing hyphens are trimmed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function slugify(title: string): string;
|
|
7
|
+
//# sourceMappingURL=slug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../src/lib/slug.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C"}
|
package/dist/slug.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// cairn-core: derive a filename-safe slug stem from a human title, for the create-entry form.
|
|
2
|
+
// The admin is filename-based (Pass E): this produces the editable stem an author can adjust,
|
|
3
|
+
// matching the server-side SLUG_RE (lowercase alphanumerics and internal hyphens). Pure.
|
|
4
|
+
/**
|
|
5
|
+
* Lowercase a title into a filename-safe slug stem.
|
|
6
|
+
* Apostrophes are dropped so "Geoff's" becomes "geoffs" (no spurious hyphen).
|
|
7
|
+
* All other non-alphanumeric runs become a single hyphen; leading/trailing hyphens are trimmed.
|
|
8
|
+
*/
|
|
9
|
+
export function slugify(title) {
|
|
10
|
+
return title
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/'/g, '')
|
|
13
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
14
|
+
.replace(/^-+|-+$/g, '');
|
|
15
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CairnUser } from '../auth/guard';
|
|
2
|
-
import { type RepoFile } from '../github';
|
|
3
2
|
import { type CairnAdapter, type CairnField } from '../adapter';
|
|
3
|
+
import { type NavNode } from '../nav';
|
|
4
4
|
/** The `platform.env` bindings the content routes read. All optional; the handlers guard. */
|
|
5
5
|
export interface AdminEnv {
|
|
6
6
|
GITHUB_APP_ID?: string;
|
|
@@ -12,17 +12,31 @@ interface PlatformEvent {
|
|
|
12
12
|
env?: AdminEnv;
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
/** A collection reduced to what the sidebar nav needs (no plugin graph crosses to the client). */
|
|
16
|
+
export interface NavCollection {
|
|
17
|
+
type: string;
|
|
18
|
+
label: string;
|
|
19
|
+
}
|
|
15
20
|
export interface AdminLayoutData {
|
|
16
21
|
user: CairnUser | null;
|
|
17
22
|
siteName: string;
|
|
18
23
|
pathname: string;
|
|
24
|
+
collections: NavCollection[];
|
|
25
|
+
/** Managed menus (name+label only) so the shell can show a Navigation entry. */
|
|
26
|
+
navMenus: {
|
|
27
|
+
name: string;
|
|
28
|
+
label: string;
|
|
29
|
+
}[];
|
|
30
|
+
/** Whether the viewer may manage navigation (gates the Navigation nav entry). */
|
|
31
|
+
canManageNav: boolean;
|
|
19
32
|
}
|
|
20
33
|
/**
|
|
21
|
-
* Branding
|
|
22
|
-
* its plugin graph into client bundles
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
34
|
+
* Branding, session, and collection nav for every admin page. `siteName` and the collection
|
|
35
|
+
* list flow from the adapter without pulling its plugin graph into client bundles (the import
|
|
36
|
+
* stays server-side in the layout load; only `{type,label}` crosses). `pathname` lets the
|
|
37
|
+
* shared shell highlight the active nav item without a `$app/*` import (those kit virtual
|
|
38
|
+
* modules have no types outside a kit app); reading `event.url` also opts the layout load into
|
|
39
|
+
* rerunning on navigation, keeping the active class correct.
|
|
26
40
|
*/
|
|
27
41
|
export declare function adminLayoutLoad(event: {
|
|
28
42
|
locals: {
|
|
@@ -30,20 +44,66 @@ export declare function adminLayoutLoad(event: {
|
|
|
30
44
|
};
|
|
31
45
|
url: URL;
|
|
32
46
|
}, adapter: CairnAdapter): AdminLayoutData;
|
|
33
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The `/admin` index has no content of its own now that each collection is its own page; send
|
|
49
|
+
* the editor straight to the first collection's entries list (a Sveltia-style landing).
|
|
50
|
+
*/
|
|
51
|
+
export declare function adminIndexRedirect(adapter: CairnAdapter): never;
|
|
52
|
+
/** One entry row: id (filename stem), display title, optional date, draft flag. */
|
|
53
|
+
export interface CollectionEntry {
|
|
54
|
+
id: string;
|
|
55
|
+
path: string;
|
|
56
|
+
title: string;
|
|
57
|
+
date: string | null;
|
|
58
|
+
draft: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface CollectionListData {
|
|
34
61
|
type: string;
|
|
35
62
|
label: string;
|
|
36
|
-
|
|
63
|
+
kind: 'page' | 'story';
|
|
64
|
+
entries: CollectionEntry[];
|
|
65
|
+
/** Set when the directory listing itself failed (rate limit, network). */
|
|
37
66
|
error?: string;
|
|
67
|
+
/** A create-flow error bounced back via `?error=` (an invalid or taken slug). */
|
|
68
|
+
formError: string | null;
|
|
69
|
+
/** Whether the viewer may create an entry in this collection (page-create is owner-only). */
|
|
70
|
+
canCreate: boolean;
|
|
38
71
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
/**
|
|
73
|
+
* List one collection's entries, reading each file's frontmatter for the display title, date,
|
|
74
|
+
* and draft badge. Reads run in parallel; a single failed read degrades that row to the slug
|
|
75
|
+
* (rather than failing the page), and a failed directory listing returns an inline `error`.
|
|
76
|
+
* Collections are small here; the 1,000-entry / Git-Trees sharding concern is risk #11, deferred.
|
|
77
|
+
*/
|
|
78
|
+
export declare function collectionListLoad(event: PlatformEvent & {
|
|
79
|
+
params: {
|
|
80
|
+
collection: string;
|
|
81
|
+
};
|
|
82
|
+
url: URL;
|
|
83
|
+
locals: {
|
|
84
|
+
user: CairnUser | null;
|
|
85
|
+
};
|
|
86
|
+
}, adapter: CairnAdapter): Promise<CollectionListData>;
|
|
87
|
+
/**
|
|
88
|
+
* The "New entry" form action. Validates the requested slug, rejects one that already exists,
|
|
89
|
+
* then redirects into the editor in create mode (`?new=1`, where `editLoad` serves a blank
|
|
90
|
+
* document and `saveCommit`'s create path commits a new file). cairn is filename-based, so the
|
|
91
|
+
* slug is the filename stem the author types; a title-driven auto-slug is a later (Pass K) concern.
|
|
92
|
+
*/
|
|
93
|
+
export declare function createEntry(event: PlatformEvent & {
|
|
94
|
+
params: {
|
|
95
|
+
collection: string;
|
|
96
|
+
};
|
|
97
|
+
locals: {
|
|
98
|
+
user: CairnUser | null;
|
|
99
|
+
};
|
|
100
|
+
request: Request;
|
|
101
|
+
}, adapter: CairnAdapter): Promise<never>;
|
|
43
102
|
export interface EditData {
|
|
44
103
|
type: string;
|
|
45
104
|
id: string;
|
|
46
105
|
label: string;
|
|
106
|
+
kind: 'page' | 'story';
|
|
47
107
|
fields: CairnField[];
|
|
48
108
|
path: string;
|
|
49
109
|
body: string;
|
|
@@ -51,6 +111,8 @@ export interface EditData {
|
|
|
51
111
|
title: string;
|
|
52
112
|
saved: boolean;
|
|
53
113
|
error: string | null;
|
|
114
|
+
/** True when editing a not-yet-committed new entry (reached via `?new=1`). */
|
|
115
|
+
isNew: boolean;
|
|
54
116
|
}
|
|
55
117
|
export declare function editLoad(event: PlatformEvent & {
|
|
56
118
|
params: {
|
|
@@ -65,6 +127,34 @@ export declare function saveCommit(event: PlatformEvent & {
|
|
|
65
127
|
user: CairnUser | null;
|
|
66
128
|
};
|
|
67
129
|
}, adapter: CairnAdapter): Promise<never>;
|
|
130
|
+
/** A page the picker can insert: its display label and the URL the nav item points at. */
|
|
131
|
+
export interface NavPageOption {
|
|
132
|
+
label: string;
|
|
133
|
+
url: string;
|
|
134
|
+
}
|
|
135
|
+
export interface NavLoadData {
|
|
136
|
+
menu: {
|
|
137
|
+
name: string;
|
|
138
|
+
label: string;
|
|
139
|
+
maxDepth: number;
|
|
140
|
+
};
|
|
141
|
+
tree: NavNode[];
|
|
142
|
+
pages: NavPageOption[];
|
|
143
|
+
saved: boolean;
|
|
144
|
+
error: string | null;
|
|
145
|
+
}
|
|
146
|
+
export declare function navLoad(event: PlatformEvent & {
|
|
147
|
+
locals: {
|
|
148
|
+
user: CairnUser | null;
|
|
149
|
+
};
|
|
150
|
+
url: URL;
|
|
151
|
+
}, adapter: CairnAdapter): Promise<NavLoadData>;
|
|
152
|
+
export declare function navSave(event: PlatformEvent & {
|
|
153
|
+
locals: {
|
|
154
|
+
user: CairnUser | null;
|
|
155
|
+
};
|
|
156
|
+
request: Request;
|
|
157
|
+
}, adapter: CairnAdapter): Promise<never>;
|
|
68
158
|
export interface HealthData {
|
|
69
159
|
ok: boolean;
|
|
70
160
|
checks: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"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"}
|