@zebradil/docs-kit 0.0.0-stage → 0.11.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.
@@ -0,0 +1,372 @@
1
+ ---
2
+ import { readFileSync } from 'node:fs';
3
+ import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
4
+ import { Code, LinkCard, TabItem, Tabs } from '@astrojs/starlight/components';
5
+ import site from 'virtual:docs-kit/site';
6
+ import { escape, inline } from './text.mjs';
7
+ import Cascade from './Cascade.astro';
8
+ import Diff from './Diff.astro';
9
+ import Tree from './Tree.astro';
10
+
11
+ // The hero's text column is Starlight's: the eyebrow rides in the tagline slot and CSS moves it above the title.
12
+ const lede = `<span class="kit-lede">${inline(site.description ?? site.tagline)}</span>`;
13
+ const eyebrow = site.eyebrow ? `<span class="kit-eyebrow">${escape(site.eyebrow)}</span>` : '';
14
+
15
+ // Illustrations are project content, inlined so they can follow the color scheme and animate.
16
+ // Paths are relative to the docs directory, the working directory of a build.
17
+ const svg = (path: string) => readFileSync(path, 'utf8');
18
+ const art = site.art && `<div class="kit-art" aria-hidden="true">${svg(site.art)}</div>`;
19
+
20
+ // The flow panel fills Starlight's hero image slot, which only takes an HTML string, unless `art` takes it;
21
+ // then the panel opens a section below the hero. With `flow.art` the rows stay for screen readers only.
22
+ const arrows = { forward: '→', back: '←', both: '↔' };
23
+ const node = (name: string) =>
24
+ `<span class="kit-flow-node${name === site.flow?.highlight ? ' kit-flow-node-hl' : ''}">${escape(name)}</span>`;
25
+ const flow =
26
+ site.flow &&
27
+ [
28
+ `<div class="kit-flow">`,
29
+ art ? '' : `<p class="kit-flow-title">${escape(site.flow.title ?? 'How it fits')}</p>`,
30
+ site.flow.art ? `<div class="kit-flow-art" aria-hidden="true">${svg(site.flow.art)}</div><div class="sr-only">` : '',
31
+ ...site.flow.steps.map(
32
+ (s) =>
33
+ `<div class="kit-flow-row">${node(s.from)}` +
34
+ `<span class="kit-flow-edge" data-arrow="${s.arrow ?? 'forward'}"><span class="kit-flow-label">${escape(s.label)}</span>` +
35
+ `<span class="sr-only">${arrows[s.arrow ?? 'forward']}</span></span>${node(s.to)}</div>`,
36
+ ),
37
+ site.flow.art ? '</div>' : '',
38
+ site.flow.note && !art ? `<p class="kit-flow-note">${inline(site.flow.note)}</p>` : '',
39
+ '</div>',
40
+ ].join('');
41
+
42
+ const frontmatter = {
43
+ title: site.name,
44
+ description: site.tagline,
45
+ template: 'splash' as const,
46
+ hero: {
47
+ title: escape(site.headline ?? site.name),
48
+ tagline: eyebrow + lede,
49
+ ...((art || flow) && { image: { html: art || flow } }),
50
+ actions: [
51
+ { text: 'Get started', link: `${import.meta.env.BASE_URL.replace(/\/$/, '')}/getting-started/`, icon: 'right-arrow' },
52
+ { text: 'GitHub', link: site.repo, icon: 'github', variant: 'minimal' },
53
+ ],
54
+ },
55
+ };
56
+ const number = (i: number) => String(i + 1).padStart(2, '0');
57
+ ---
58
+
59
+ <StarlightPage frontmatter={frontmatter}>
60
+ <div class="kit-landing not-content">
61
+ <section class="kit-install" aria-label="Install">
62
+ <Tabs>
63
+ {site.install.map((m) => <TabItem label={m.label}><Code code={m.code} lang="sh" /></TabItem>)}
64
+ </Tabs>
65
+ </section>
66
+
67
+ {site.diff && <Diff />}
68
+
69
+ {
70
+ art && flow && (
71
+ <section class="kit-flow-section">
72
+ <div>
73
+ <h2>{site.flow.title ?? 'How it fits'}</h2>
74
+ {site.flow.note && <p set:html={inline(site.flow.note)} />}
75
+ </div>
76
+ <Fragment set:html={flow} />
77
+ </section>
78
+ )
79
+ }
80
+
81
+ {site.tree && <Tree />}
82
+
83
+ {site.cascade && <Cascade />}
84
+
85
+ <section>
86
+ <h2>Features</h2>
87
+ <div class="kit-features">
88
+ {
89
+ site.features.map((f, i) => (
90
+ <article class="kit-feature">
91
+ <span class="kit-number">{number(i)}</span>
92
+ <h3 set:html={escape(f.title)} />
93
+ <p set:html={inline(f.body)} />
94
+ </article>
95
+ ))
96
+ }
97
+ </div>
98
+ </section>
99
+
100
+ {
101
+ site.components && (
102
+ <section class="kit-components">
103
+ <div>
104
+ <h2>{site.components.title ?? 'Components'}</h2>
105
+ {site.components.intro && <p set:html={inline(site.components.intro)} />}
106
+ </div>
107
+ <dl>
108
+ {site.components.items.map((c) => (
109
+ <div>
110
+ <dt>
111
+ <code>{c.name}</code>
112
+ </dt>
113
+ <dd set:html={inline(c.body)} />
114
+ </div>
115
+ ))}
116
+ </dl>
117
+ </section>
118
+ )
119
+ }
120
+
121
+ {
122
+ !!site.links?.length && (
123
+ <section>
124
+ <h2>Links</h2>
125
+ <div class="kit-links">
126
+ {site.links.map((l) => <LinkCard title={l.label} href={l.href} />)}
127
+ </div>
128
+ </section>
129
+ )
130
+ }
131
+ </div>
132
+ </StarlightPage>
133
+
134
+ <style is:global>
135
+ /* Hero: Starlight's markup in the kit's layout, scoped to the landing page with :has(). */
136
+ :root:has(.kit-landing) {
137
+ --kit-display: var(--kit-font-display, var(--sl-font));
138
+ }
139
+ :root:has(.kit-landing) .hero .tagline {
140
+ display: contents;
141
+ }
142
+ :root:has(.kit-landing) .hero h1 {
143
+ font-family: var(--kit-display);
144
+ letter-spacing: -0.03em;
145
+ line-height: 1.05;
146
+ }
147
+ .kit-eyebrow {
148
+ order: -1;
149
+ font-family: var(--sl-font-mono);
150
+ font-size: var(--sl-text-xs);
151
+ letter-spacing: 0.06em;
152
+ text-transform: uppercase;
153
+ color: var(--sl-color-text-accent);
154
+ }
155
+ .kit-lede {
156
+ font-size: clamp(var(--sl-text-base), calc(0.0625rem + 2vw), var(--sl-text-xl));
157
+ line-height: 1.55;
158
+ color: var(--sl-color-gray-2);
159
+ }
160
+ :root:has(.kit-landing) .hero > .hero-html {
161
+ order: 2;
162
+ width: 100%;
163
+ }
164
+ @media (min-width: 50rem) {
165
+ :root:has(.kit-landing) .hero:has(> .hero-html) {
166
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
167
+ gap: 4rem;
168
+ }
169
+ }
170
+
171
+ /* Illustrations: project SVGs scaled to their column. */
172
+ .kit-art {
173
+ display: grid;
174
+ place-items: center;
175
+ width: 100%;
176
+ }
177
+ .kit-art svg {
178
+ width: min(100%, 26rem);
179
+ height: auto;
180
+ }
181
+ .kit-flow-art svg {
182
+ display: block;
183
+ width: 100%;
184
+ height: auto;
185
+ }
186
+ .kit-flow-section {
187
+ display: grid;
188
+ grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
189
+ gap: 2rem 3rem;
190
+ align-items: center;
191
+ }
192
+ .kit-flow-section > div > p {
193
+ margin: 0;
194
+ line-height: 1.55;
195
+ color: var(--sl-color-gray-2);
196
+ }
197
+ @media (max-width: 50rem) {
198
+ .kit-flow-section {
199
+ grid-template-columns: minmax(0, 1fr);
200
+ }
201
+ }
202
+
203
+ /* Flow panel */
204
+ .kit-flow {
205
+ width: 100%;
206
+ display: flex;
207
+ flex-direction: column;
208
+ gap: 1.125rem;
209
+ padding: 1.75rem;
210
+ border: 1px solid var(--sl-color-hairline-light);
211
+ border-radius: 1.25rem;
212
+ background: var(--sl-color-bg-nav);
213
+ text-align: start;
214
+ }
215
+ .kit-flow-title {
216
+ margin: 0;
217
+ font-family: var(--sl-font-mono);
218
+ font-size: var(--sl-text-2xs);
219
+ letter-spacing: 0.08em;
220
+ text-transform: uppercase;
221
+ color: var(--sl-color-gray-3);
222
+ }
223
+ .kit-flow-row {
224
+ display: grid;
225
+ grid-template-columns: minmax(0, 9rem) minmax(4rem, 1fr) minmax(0, 9rem);
226
+ align-items: center;
227
+ gap: 0.75rem;
228
+ }
229
+ .kit-flow-node {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ min-height: 3.25rem;
234
+ padding: 0.25rem 0.75rem;
235
+ border: 1.5px solid var(--sl-color-gray-5);
236
+ border-radius: 0.75rem;
237
+ font-size: var(--sl-text-sm);
238
+ font-weight: 600;
239
+ line-height: 1.25;
240
+ text-align: center;
241
+ color: var(--sl-color-white);
242
+ }
243
+ .kit-flow-node-hl {
244
+ border-color: var(--sl-color-white);
245
+ background: var(--sl-color-white);
246
+ color: var(--sl-color-black);
247
+ }
248
+ .kit-flow-edge {
249
+ position: relative;
250
+ display: flex;
251
+ justify-content: center;
252
+ padding-bottom: 0.625rem;
253
+ border-bottom: 2px solid var(--sl-color-gray-3);
254
+ }
255
+ .kit-flow-edge::before,
256
+ .kit-flow-edge::after {
257
+ position: absolute;
258
+ bottom: -7px;
259
+ border-block: 6px solid transparent;
260
+ }
261
+ .kit-flow-edge:is([data-arrow='forward'], [data-arrow='both'])::after {
262
+ content: '';
263
+ right: -2px;
264
+ border-left: 9px solid var(--sl-color-gray-3);
265
+ }
266
+ .kit-flow-edge:is([data-arrow='back'], [data-arrow='both'])::before {
267
+ content: '';
268
+ left: -2px;
269
+ border-right: 9px solid var(--sl-color-gray-3);
270
+ }
271
+ .kit-flow-label {
272
+ font-family: var(--sl-font-mono);
273
+ font-size: var(--sl-text-2xs);
274
+ line-height: 1.3;
275
+ text-align: center;
276
+ color: var(--sl-color-gray-2);
277
+ }
278
+ .kit-flow-note {
279
+ margin: 0;
280
+ padding-top: 0.875rem;
281
+ border-top: 1px dashed var(--sl-color-hairline-light);
282
+ font-size: var(--sl-text-sm);
283
+ color: var(--sl-color-gray-3);
284
+ }
285
+
286
+ /* Sections below the hero */
287
+ .kit-landing {
288
+ display: flex;
289
+ flex-direction: column;
290
+ gap: 4.5rem;
291
+ padding-bottom: 3rem;
292
+ }
293
+ .kit-landing h2 {
294
+ margin: 0 0 1.5rem;
295
+ font-family: var(--kit-display);
296
+ font-size: clamp(var(--sl-text-3xl), calc(1rem + 2.5vw), var(--sl-text-5xl));
297
+ letter-spacing: -0.025em;
298
+ line-height: 1.1;
299
+ color: var(--sl-color-white);
300
+ }
301
+ .kit-features {
302
+ display: grid;
303
+ grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
304
+ gap: 1.25rem;
305
+ }
306
+ .kit-feature {
307
+ display: flex;
308
+ flex-direction: column;
309
+ gap: 0.75rem;
310
+ padding: 1.75rem;
311
+ border: 1px solid var(--sl-color-hairline-light);
312
+ border-radius: 1rem;
313
+ background: var(--sl-color-bg-nav);
314
+ }
315
+ .kit-feature h3 {
316
+ margin: 0;
317
+ font-family: var(--kit-display);
318
+ font-size: var(--sl-text-xl);
319
+ letter-spacing: -0.015em;
320
+ color: var(--sl-color-white);
321
+ }
322
+ .kit-feature p {
323
+ margin: 0;
324
+ line-height: 1.55;
325
+ color: var(--sl-color-gray-2);
326
+ }
327
+ .kit-number {
328
+ font-family: var(--sl-font-mono);
329
+ font-size: var(--sl-text-xs);
330
+ color: var(--sl-color-text-accent);
331
+ }
332
+ .kit-components {
333
+ display: grid;
334
+ grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
335
+ gap: 2rem 3rem;
336
+ padding: clamp(1.75rem, 4vw, 3.5rem);
337
+ border-radius: 1.5rem;
338
+ background: var(--sl-color-gray-6);
339
+ }
340
+ .kit-components p {
341
+ margin: 0;
342
+ line-height: 1.55;
343
+ color: var(--sl-color-gray-2);
344
+ }
345
+ .kit-components dl {
346
+ display: grid;
347
+ grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
348
+ gap: 1.25rem 3rem;
349
+ margin: 0;
350
+ }
351
+ @media (max-width: 50rem) {
352
+ .kit-components {
353
+ grid-template-columns: minmax(0, 1fr);
354
+ }
355
+ }
356
+ .kit-components dt code {
357
+ padding: 0;
358
+ background: none;
359
+ font-size: var(--sl-text-sm);
360
+ font-weight: 600;
361
+ color: var(--sl-color-white);
362
+ }
363
+ .kit-components dd {
364
+ margin: 0.25rem 0 0;
365
+ font-size: var(--sl-text-sm);
366
+ color: var(--sl-color-gray-2);
367
+ }
368
+ .kit-links {
369
+ display: grid;
370
+ gap: 1rem;
371
+ }
372
+ </style>
@@ -0,0 +1,45 @@
1
+ ---
2
+ import Default from '@astrojs/starlight/components/SiteTitle.astro';
3
+ import Select from '@astrojs/starlight/components/Select.astro';
4
+ import { root, version } from 'virtual:docs-kit/site';
5
+ ---
6
+
7
+ <Default />
8
+ {/* Filled from versions.json at runtime, so the docs of a past release list releases made after it. */}
9
+ <kit-versions data-root={root} data-version={version} hidden>
10
+ <Select icon="open-book" label="Docs version" options={[]} width="auto" />
11
+ </kit-versions>
12
+
13
+ <script>
14
+ customElements.define(
15
+ 'kit-versions',
16
+ class extends HTMLElement {
17
+ async connectedCallback() {
18
+ const { root, version } = this.dataset as { root: string; version: string };
19
+ const select = this.querySelector('select')!;
20
+ const response = await fetch(`${root}versions.json`).catch(() => undefined);
21
+ if (!response?.ok) return;
22
+ const versions: { name: string; path: string; latest?: true }[] = await response.json();
23
+ const current = version && `${version}/`;
24
+ // The same page in another version: the URL path below the current version's base.
25
+ const page = location.pathname.slice(root.length + current.length);
26
+ for (const { name, path, latest } of versions) {
27
+ select.add(new Option(latest ? `${name} (latest release)` : name, path, false, path === current));
28
+ }
29
+ select.addEventListener('change', async () => {
30
+ const target = root + select.value;
31
+ const exists = await fetch(target + page, { method: 'HEAD' }).then((r) => r.ok, () => false);
32
+ location.href = exists ? target + page : target;
33
+ });
34
+ this.hidden = false;
35
+ }
36
+ },
37
+ );
38
+ </script>
39
+
40
+ <style>
41
+ kit-versions:not([hidden]) {
42
+ display: flex;
43
+ align-items: center;
44
+ }
45
+ </style>
@@ -0,0 +1,139 @@
1
+ ---
2
+ // The inheritance tree: one tab per node with an `id`, numbering the nodes it inherits from, root first.
3
+ import { TabItem, Tabs } from '@astrojs/starlight/components';
4
+ import site from 'virtual:docs-kit/site';
5
+ import { escape, inline } from './text.mjs';
6
+
7
+ const tree = site.tree;
8
+ const chains = [];
9
+ const walk = (node, chain) => {
10
+ const here = [...chain, node];
11
+ if (node.id) chains.push(here);
12
+ node.children?.forEach((child) => walk(child, here));
13
+ };
14
+ walk(tree.root, []);
15
+
16
+ const render = (node, chain) => {
17
+ const step = chain.indexOf(node) + 1;
18
+ const row =
19
+ `<div class="kit-tree-row"${step ? ` data-step="${step}"` : ''}>` +
20
+ `<span class="kit-tree-step">${step || ''}</span>` +
21
+ `<code>${escape(node.name)}</code>` +
22
+ (node.file ? `<span class="kit-tree-file">${escape(node.file)}</span>` : '') +
23
+ (node.note ? `<span class="kit-tree-note">${inline(node.note)}</span>` : '') +
24
+ '</div>';
25
+ const children = node.children ? `<ul>${node.children.map((c) => render(c, chain)).join('')}</ul>` : '';
26
+ return `<li>${row}${children}</li>`;
27
+ };
28
+ ---
29
+
30
+ <section class="kit-tree" aria-labelledby="kit-tree-title">
31
+ <div>
32
+ <h2 id="kit-tree-title">{tree.title ?? 'How settings inherit'}</h2>
33
+ {tree.intro && <p set:html={inline(tree.intro)} />}
34
+ </div>
35
+ <div class="kit-tree-panel">
36
+ <Tabs>
37
+ {
38
+ chains.map((chain) => (
39
+ <TabItem label={chain.at(-1).id}>
40
+ <ul class="kit-tree-list" set:html={render(tree.root, chain)} />
41
+ </TabItem>
42
+ ))
43
+ }
44
+ </Tabs>
45
+ </div>
46
+ </section>
47
+
48
+ <style is:global>
49
+ .kit-tree {
50
+ display: grid;
51
+ grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
52
+ gap: 2rem 3rem;
53
+ align-items: start;
54
+ }
55
+ @media (max-width: 50rem) {
56
+ .kit-tree {
57
+ grid-template-columns: minmax(0, 1fr);
58
+ }
59
+ }
60
+ .kit-tree > div > p {
61
+ margin: 0;
62
+ line-height: 1.55;
63
+ color: var(--sl-color-gray-2);
64
+ }
65
+ .kit-tree-panel {
66
+ min-width: 0;
67
+ padding: 0.5rem 1.25rem 1.25rem;
68
+ border: 1px solid var(--sl-color-hairline-light);
69
+ border-radius: 1rem;
70
+ background: var(--sl-color-bg-nav);
71
+ }
72
+ .kit-tree starlight-tabs {
73
+ display: block;
74
+ }
75
+ .kit-tree-list,
76
+ .kit-tree-list ul {
77
+ margin: 0;
78
+ padding: 0;
79
+ list-style: none;
80
+ }
81
+ .kit-tree-list {
82
+ margin-top: 1rem;
83
+ }
84
+ .kit-tree-list ul {
85
+ margin-left: 0.875rem;
86
+ padding-left: 1rem;
87
+ border-left: 1px solid var(--sl-color-gray-5);
88
+ }
89
+ .kit-tree-list li {
90
+ margin: 0;
91
+ }
92
+ .kit-tree-row {
93
+ display: flex;
94
+ flex-wrap: wrap;
95
+ align-items: center;
96
+ gap: 0.25rem 0.625rem;
97
+ padding: 0.375rem 0;
98
+ font-size: var(--sl-text-sm);
99
+ color: var(--sl-color-gray-3);
100
+ }
101
+ .kit-tree-row > code {
102
+ padding: 0;
103
+ background: none;
104
+ font-size: var(--sl-text-sm);
105
+ color: var(--sl-color-gray-3);
106
+ }
107
+ .kit-tree-step {
108
+ display: inline-grid;
109
+ place-items: center;
110
+ width: 1.5rem;
111
+ height: 1.5rem;
112
+ border: 1px dashed var(--sl-color-gray-5);
113
+ border-radius: 50%;
114
+ font-family: var(--sl-font-mono);
115
+ font-size: var(--sl-text-xs);
116
+ }
117
+ .kit-tree-file {
118
+ font-family: var(--sl-font-mono);
119
+ font-size: var(--sl-text-xs);
120
+ }
121
+ .kit-tree-note {
122
+ flex-basis: 100%;
123
+ padding-left: 2.125rem;
124
+ font-size: var(--sl-text-xs);
125
+ }
126
+ .kit-tree-row[data-step] {
127
+ color: var(--sl-color-gray-2);
128
+ }
129
+ .kit-tree-row[data-step] > code {
130
+ font-weight: 600;
131
+ color: var(--sl-color-white);
132
+ }
133
+ .kit-tree-row[data-step] .kit-tree-step {
134
+ border: 0;
135
+ background: var(--sl-color-text-accent);
136
+ color: var(--sl-color-black);
137
+ font-weight: 600;
138
+ }
139
+ </style>
@@ -0,0 +1,5 @@
1
+ import { docsLoader } from '@astrojs/starlight/loaders';
2
+ import { docsSchema } from '@astrojs/starlight/schema';
3
+ import { defineCollection } from 'astro:content';
4
+
5
+ export const collections = { docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }) };
@@ -0,0 +1,94 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import starlight from '@astrojs/starlight';
4
+ import { defineConfig } from 'astro/config';
5
+ import { parse } from 'yaml';
6
+ import { validateSite } from '../../schema/validate.mjs';
7
+
8
+ const kitPath = (path) => fileURLToPath(new URL(path, import.meta.url));
9
+
10
+ /** Builds the whole Astro config of a consuming project from its site.yaml (path relative to the working directory). */
11
+ export default function docsKit({ site: sitePath = 'site.yaml' } = {}) {
12
+ const site = parse(readFileSync(sitePath, 'utf8'));
13
+ const errors = validateSite(site);
14
+ if (errors.length) throw new Error(`${sitePath} is invalid:\n ${errors.join('\n ')}`);
15
+
16
+ // Hosting is GitHub Pages per repo: https://github.com/<owner>/<repo> is served at https://<owner>.github.io/<repo>,
17
+ // or at the root of its custom domain.
18
+ const [owner, repo] = new URL(site.repo).pathname.split('/').filter(Boolean);
19
+ const root = site.domain ? '' : `/${repo}`;
20
+ // Set by docs-kit-versions when it builds a release's docs into <root>/<version>/.
21
+ const version = process.env.DOCS_KIT_VERSION ?? '';
22
+ if (!/^[\w.-]*$/.test(version)) throw new Error(`DOCS_KIT_VERSION '${version}' is not a path segment`);
23
+ const base = version ? `${root}/${version}` : root;
24
+
25
+ // Starlight links public/favicon.svg itself; these are linked only when the project ships them.
26
+ const icons = [
27
+ ['favicon.ico', { rel: 'icon', sizes: '32x32' }],
28
+ ['apple-touch-icon.png', { rel: 'apple-touch-icon' }],
29
+ ]
30
+ .filter(([file]) => existsSync(`public/${file}`))
31
+ .map(([file, attrs]) => ({ tag: 'link', attrs: { ...attrs, href: `${base}/${file}` } }));
32
+ // A release's docs stay readable but out of search results, which should point at the current docs.
33
+ const noindex = version ? [{ tag: 'meta', attrs: { name: 'robots', content: 'noindex' } }] : [];
34
+
35
+ return defineConfig({
36
+ site: site.domain ? `https://${site.domain}` : `https://${owner.toLowerCase()}.github.io`,
37
+ base: base || '/',
38
+ vite: { plugins: [siteModule(site, { version, root: `${root}/` })] },
39
+ integrations: [
40
+ starlight({
41
+ title: site.name,
42
+ description: site.tagline,
43
+ logo: site.logo && (site.logo.dark ? { light: site.logo.light, dark: site.logo.dark } : { src: site.logo.light }),
44
+ head: [
45
+ ...icons,
46
+ ...noindex,
47
+ ...(site.analytics
48
+ ? [{ tag: 'script', attrs: { src: site.analytics.script, defer: true, ...site.analytics.attributes } }]
49
+ : []),
50
+ ],
51
+ social: [{ icon: 'github', label: 'GitHub', href: site.repo }],
52
+ // A release's pages live in a tag, which GitHub cannot edit.
53
+ editLink: version ? {} : { baseUrl: `${site.repo}/edit/main/docs/` },
54
+ components: site.versions ? { SiteTitle: kitPath('SiteTitle.astro'), Banner: kitPath('Banner.astro') } : {},
55
+ customCss: [kitPath('layout.css'), kitPath(`themes/${site.theme ?? 'default'}.css`)],
56
+ sidebar: [
57
+ { label: 'Getting started', slug: 'getting-started' },
58
+ ...['Guides', 'Concepts', 'Reference'].map((label) => ({
59
+ label,
60
+ items: [{ autogenerate: { directory: label.toLowerCase() } }],
61
+ })),
62
+ ],
63
+ }),
64
+ {
65
+ name: 'docs-kit',
66
+ hooks: {
67
+ 'astro:config:setup': ({ injectRoute }) => {
68
+ injectRoute({ pattern: '/', entrypoint: kitPath('Landing.astro') });
69
+ injectRoute({ pattern: '/[...slug].md', entrypoint: kitPath('page.md.mjs') });
70
+ injectRoute({ pattern: '/[name].txt', entrypoint: kitPath('llms.txt.mjs') });
71
+ },
72
+ },
73
+ },
74
+ ],
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Exposes the validated site.yaml to the kit's components as `virtual:docs-kit/site`, with the docs version being built
80
+ * ('' for the default branch) and the site root URL path, where versions.json lives.
81
+ */
82
+ function siteModule(site, { version, root }) {
83
+ const id = 'virtual:docs-kit/site';
84
+ const code = [
85
+ `export default ${JSON.stringify(site)};`,
86
+ `export const version = ${JSON.stringify(version)};`,
87
+ `export const root = ${JSON.stringify(root)};`,
88
+ ].join('\n');
89
+ return {
90
+ name: 'docs-kit-site',
91
+ resolveId: (source) => (source === id ? `\0${id}` : undefined),
92
+ load: (resolved) => (resolved === `\0${id}` ? code : undefined),
93
+ };
94
+ }