@abstractdata/create-docs 0.2.3

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.
Files changed (33) hide show
  1. package/README.md +36 -0
  2. package/bin/.fuse_hidden0000000a00000002 +194 -0
  3. package/bin/.fuse_hidden0000000d00000003 +194 -0
  4. package/bin/cli.js +194 -0
  5. package/package.json +38 -0
  6. package/template/.claude/skills/abstract-data-docs-author/SKILL.md +305 -0
  7. package/template/.claude/skills/abstract-data-setup/SKILL.md +555 -0
  8. package/template/.cursor/rules/abstract-data-docs-author.mdc +311 -0
  9. package/template/.cursor/rules/abstract-data-setup.mdc +561 -0
  10. package/template/.cursor/rules/welcome.mdc +29 -0
  11. package/template/.fuse_hidden0000001e00000008 +75 -0
  12. package/template/.fuse_hidden0000002100000009 +46 -0
  13. package/template/.fuse_hidden000000210000000a +75 -0
  14. package/template/.fuse_hidden000000240000000b +46 -0
  15. package/template/.github/copilot-instructions.md +893 -0
  16. package/template/.github/workflows/deploy-cloudflare.yml +50 -0
  17. package/template/.github/workflows/deploy-vercel.yml +47 -0
  18. package/template/.github/workflows/deploy.yml +55 -0
  19. package/template/CLAUDE.md +46 -0
  20. package/template/README.md +75 -0
  21. package/template/astro.config.mjs +69 -0
  22. package/template/package.json +25 -0
  23. package/template/public/favicon.svg +26 -0
  24. package/template/scripts/build-python-docs.mjs +385 -0
  25. package/template/scripts/build-ts-docs.mjs +349 -0
  26. package/template/scripts/python-autodoc.json +10 -0
  27. package/template/scripts/ts-autodoc.json +10 -0
  28. package/template/src/assets/README.md +1 -0
  29. package/template/src/content/docs/index.mdx +81 -0
  30. package/template/src/content/docs/quickstart.md +162 -0
  31. package/template/src/content.config.ts +46 -0
  32. package/template/src/env.d.ts +2 -0
  33. package/template/tsconfig.json +5 -0
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * build-ts-docs.mjs
4
+ *
5
+ * Generates Starlight-compatible Markdown API reference from a
6
+ * TypeScript project's source via TypeDoc + typedoc-plugin-markdown.
7
+ *
8
+ * Reads `scripts/ts-autodoc.json` for configuration:
9
+ * - entryPoints: array of TS entry files (e.g. ["../../my-lib/src/index.ts"])
10
+ * - tsconfig: path to the project's tsconfig.json
11
+ * - outputDir: where the generated .md pages land
12
+ * - githubPages: pass through to TypeDoc
13
+ * - skipErrorChecking: pass through to TypeDoc
14
+ * - repoUrl: (optional) base URL for "View on GitHub" footer
15
+ * - repoBranch: (optional) default 'main'
16
+ * - versions: (optional) array of { tag, label, default } for versioned docs.
17
+ * When present, each tag triggers an independent build
18
+ * from a `git worktree` checkout into <outputDir>/<safeTag>/.
19
+ * The default version is also aliased at the un-versioned
20
+ * URL so existing links keep resolving.
21
+ *
22
+ * For each module, TypeDoc emits a markdown file. The orchestrator
23
+ * post-processes each one to add Starlight `title:` frontmatter, a `version:`
24
+ * field when versioned, and a stale-version banner on non-default builds.
25
+ *
26
+ * Run:
27
+ * bun run docs:ts
28
+ *
29
+ * Requires:
30
+ * bun add -d typedoc typedoc-plugin-markdown
31
+ */
32
+ import { execSync } from 'node:child_process';
33
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
34
+ import { dirname, join, resolve, relative } from 'node:path';
35
+ import { fileURLToPath } from 'node:url';
36
+ import { tmpdir } from 'node:os';
37
+
38
+ const __dirname = dirname(fileURLToPath(import.meta.url));
39
+ const PROJECT_ROOT = resolve(__dirname, '..');
40
+ const CONFIG_PATH = resolve(__dirname, 'ts-autodoc.json');
41
+
42
+ const c = {
43
+ reset: '\x1b[0m', dim: '\x1b[2m', cyan: '\x1b[36m', gold: '\x1b[33m',
44
+ red: '\x1b[31m', green: '\x1b[32m',
45
+ };
46
+ const log = (...a) => console.log(...a);
47
+ const die = (msg) => { console.error(`${c.red}error${c.reset} ${msg}`); process.exit(1); };
48
+
49
+ // ─── Load config ──────────────────────────────────────────────────────
50
+ if (!existsSync(CONFIG_PATH)) die(`Missing config: ${CONFIG_PATH}`);
51
+ const cfg = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
52
+ if (!Array.isArray(cfg.entryPoints) || cfg.entryPoints.length === 0) {
53
+ die('ts-autodoc.json: `entryPoints` must be a non-empty array.');
54
+ }
55
+
56
+ const ROOT_OUTPUT = resolve(PROJECT_ROOT, cfg.outputDir ?? 'src/content/docs/api/ts');
57
+ const ROOT_TSCONFIG = cfg.tsconfig ? resolve(PROJECT_ROOT, cfg.tsconfig) : null;
58
+
59
+ // ─── Verify TypeDoc available ─────────────────────────────────────────
60
+ log(`${c.dim}→ checking typedoc${c.reset}`);
61
+ try {
62
+ execSync('npx --no-install typedoc --version', { cwd: PROJECT_ROOT, stdio: 'ignore' });
63
+ } catch {
64
+ die(`typedoc not found. Install it as a dev dep:
65
+ bun add -d typedoc typedoc-plugin-markdown
66
+ Then re-run this script.`);
67
+ }
68
+
69
+ // ─── Versioning setup ────────────────────────────────────────────────
70
+ function findGitRoot(start) {
71
+ let dir = start;
72
+ while (true) {
73
+ if (existsSync(join(dir, '.git'))) return dir;
74
+ const parent = dirname(dir);
75
+ if (parent === dir) return null;
76
+ dir = parent;
77
+ }
78
+ }
79
+
80
+ const versions = Array.isArray(cfg.versions) ? cfg.versions : null;
81
+ let SOURCE_REPO_ROOT = null;
82
+ let ENTRY_POINTS_REL = null;
83
+ let TSCONFIG_REL = null;
84
+ if (versions) {
85
+ if (versions.length === 0) die('`versions` is an empty array — set it to null/omit, or list at least one version.');
86
+ if (!versions.some((v) => v.tag)) die('`versions[].tag` is required on every entry.');
87
+ if (versions.filter((v) => v.default).length > 1) die('Only one `versions[].default: true` allowed.');
88
+ if (!versions.some((v) => v.default)) {
89
+ log(`${c.gold}warn${c.reset} no version marked default; treating the first one (${versions[0].tag}) as default`);
90
+ versions[0].default = true;
91
+ }
92
+ // Use the first entry point to find the source git repo. All entries
93
+ // must live under the same repo for versioned builds to make sense.
94
+ const firstEntry = resolve(PROJECT_ROOT, cfg.entryPoints[0]);
95
+ SOURCE_REPO_ROOT = findGitRoot(firstEntry);
96
+ if (!SOURCE_REPO_ROOT) {
97
+ die(`versions[] is configured but no .git directory was found above ${firstEntry}.\n Versioned builds require the source to be a git checkout.`);
98
+ }
99
+ ENTRY_POINTS_REL = cfg.entryPoints.map((e) =>
100
+ relative(SOURCE_REPO_ROOT, resolve(PROJECT_ROOT, e)),
101
+ );
102
+ TSCONFIG_REL = ROOT_TSCONFIG ? relative(SOURCE_REPO_ROOT, ROOT_TSCONFIG) : null;
103
+ log(`${c.dim}→ source repo: ${SOURCE_REPO_ROOT}${c.reset}`);
104
+ log(`${c.dim}→ entryPoints (rel): ${ENTRY_POINTS_REL.join(', ')}${c.reset}`);
105
+ }
106
+
107
+ // Make a tag filesystem-safe for use as a directory name. We have to be
108
+ // strict here: Astro's slug normalizer strips dots from URL segments
109
+ // (`0.1.0` → `010`), so if our directory names contain dots the URL the
110
+ // VersionPicker constructs won't match the rendered URL. Convert dots
111
+ // to dashes (and any other non-alphanumeric to dashes).
112
+ // v0.3.0 → 0-3-0
113
+ // v1.0.0-rc.1 → 1-0-0-rc-1
114
+ function safeTag(tag) {
115
+ return tag.replace(/^v/, '').replace(/[^a-zA-Z0-9_-]/g, '-');
116
+ }
117
+
118
+ // ─── Per-build pipeline ──────────────────────────────────────────────
119
+ function buildOnce({ entryPoints, tsconfig, version }) {
120
+ const outDir = version ? join(ROOT_OUTPUT, safeTag(version.tag)) : ROOT_OUTPUT;
121
+ mkdirSync(outDir, { recursive: true });
122
+
123
+ const tagPrefix = version ? `${c.cyan}[${version.label ?? version.tag}]${c.reset} ` : '';
124
+ log(`${c.dim}→ ${tagPrefix}generating TypeScript API pages${c.reset}`);
125
+
126
+ const args = [
127
+ '--plugin', 'typedoc-plugin-markdown',
128
+ '--out', outDir,
129
+ '--readme', 'none',
130
+ '--hideBreadcrumbs', 'true',
131
+ '--hidePageHeader', 'true',
132
+ ];
133
+ if (tsconfig) args.push('--tsconfig', tsconfig);
134
+ if (cfg.skipErrorChecking) args.push('--skipErrorChecking');
135
+ for (const entry of entryPoints) args.push(entry);
136
+
137
+ try {
138
+ execSync(`npx typedoc ${args.map((a) => `"${a}"`).join(' ')}`,
139
+ { cwd: PROJECT_ROOT, stdio: 'inherit' });
140
+ } catch {
141
+ log(`${c.red}error${c.reset} ${tagPrefix}typedoc failed; skipping this build`);
142
+ return;
143
+ }
144
+
145
+ // Post-process every emitted .md
146
+ log(`${c.dim}→ ${tagPrefix}adding Starlight frontmatter${c.reset}`);
147
+
148
+ function walk(dir) {
149
+ const out = [];
150
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
151
+ const full = join(dir, entry.name);
152
+ if (entry.isDirectory()) out.push(...walk(full));
153
+ else if (entry.name.endsWith('.md')) out.push(full);
154
+ }
155
+ return out;
156
+ }
157
+
158
+ const pages = [];
159
+ for (const file of walk(outDir)) {
160
+ let content = readFileSync(file, 'utf8');
161
+ if (content.startsWith('---\n')) continue;
162
+
163
+ const h1 = content.match(/^# (.+?)$/m);
164
+ const fallbackTitle = relative(outDir, file).replace(/\.md$/, '').replace(/[\\/_]/g, ' ');
165
+ const title = (h1?.[1] ?? fallbackTitle).trim().replace(/\\_/g, '_');
166
+ const body = h1 ? content.replace(h1[0] + '\n', '') : content;
167
+
168
+ const desc = body.split('\n').find((l) => {
169
+ const t = l.trim();
170
+ if (!t) return false;
171
+ if (/^#{1,6} /.test(t)) return false;
172
+ if (t.startsWith('```')) return false;
173
+ if (t.startsWith('|')) return false;
174
+ if (/^[-*+] /.test(t)) return false;
175
+ if (/^<[^>]+>/.test(t)) return false;
176
+ return true;
177
+ });
178
+ const description = (desc ?? `API reference for \`${title}\`.`)
179
+ .trim().replace(/`/g, '').replace(/"/g, "'").slice(0, 160);
180
+
181
+ const fmLines = ['---', `title: ${title}`, `description: "${description}"`];
182
+ if (version) {
183
+ // Emit version metadata. `versionDefault: true` lets the bundled
184
+ // <VersionPicker> auto-discover which version to pre-select without
185
+ // duplicating the canonical list outside the autodoc JSON config.
186
+ fmLines.push(`version: "${version.tag}"`);
187
+ if (version.label) fmLines.push(`versionLabel: "${version.label}"`);
188
+ if (version.default) fmLines.push(`versionDefault: true`);
189
+ }
190
+ fmLines.push('---', '');
191
+ const frontmatter = fmLines.join('\n');
192
+ pages.push({ file, title, description, body, frontmatter });
193
+ }
194
+
195
+ // Thin-page post-processor (TS)
196
+ let bannered = 0, enriched = 0;
197
+ const landingPage = pages.find((p) => /^(index|readme|globals|modules)\.md$/i.test(relative(outDir, p.file)));
198
+
199
+ for (const page of pages) {
200
+ const proseLines = page.body.split('\n').filter((l) => {
201
+ const t = l.trim();
202
+ if (!t) return false;
203
+ if (/^#{1,6} /.test(t)) return false;
204
+ if (t.startsWith('```')) return false;
205
+ if (t.startsWith('|') || /^[-=]{3,}/.test(t)) return false;
206
+ if (/^[-*+] /.test(t)) return false;
207
+ if (/^<[^>]+>/.test(t)) return false;
208
+ return true;
209
+ }).length;
210
+ const bodyChars = page.body.replace(/\s+/g, '').length;
211
+ const isThin = bodyChars < 150 && proseLines < 1;
212
+
213
+ let newBody = page.body;
214
+ let touched = false;
215
+
216
+ if (version && !version.default) {
217
+ const defaultV = (cfg.versions ?? []).find((v) => v.default);
218
+ const latestLabel = defaultV ? (defaultV.label ?? defaultV.tag) : 'latest';
219
+ const apiBase = `/${cfg.outputDir.replace(/^src\/content\/docs\/?/, '').replace(/\/$/, '')}`;
220
+ const sameRel = relative(outDir, page.file).replace(/\.md$/, '');
221
+ const latestPath = defaultV
222
+ ? `${apiBase}/${safeTag(defaultV.tag)}/${sameRel}/`
223
+ : null;
224
+ const link = latestPath ? `[${latestLabel} →](${latestPath})` : latestLabel;
225
+ const stale = [
226
+ '', `:::caution[Older version]`,
227
+ `You're viewing **${version.label ?? version.tag}**. Latest is ${link}.`,
228
+ ':::', '',
229
+ ].join('\n');
230
+ newBody = stale + newBody;
231
+ touched = true;
232
+ }
233
+
234
+ if (page === landingPage) {
235
+ const others = pages.filter((p) => p !== landingPage);
236
+ if (others.length > 0) {
237
+ const lines = ['', '## Submodules', ''];
238
+ for (const sib of others) {
239
+ const rel = relative(outDir, sib.file).replace(/\.md$/, '');
240
+ const summary = sib.description && !sib.description.startsWith('API reference for')
241
+ ? ` — ${sib.description}` : '';
242
+ lines.push(`- [\`${sib.title}\`](./${rel}.md)${summary}`);
243
+ }
244
+ lines.push('');
245
+ const submodulesSection = lines.join('\n');
246
+ if (isThin) {
247
+ newBody = newBody + `\nTop-level entry — see modules below for the full API surface.\n${submodulesSection}`;
248
+ } else {
249
+ newBody = newBody.replace(/\s+$/, '') + '\n' + submodulesSection;
250
+ }
251
+ enriched += 1;
252
+ touched = true;
253
+ log(`${c.green} ✓${c.reset} ${tagPrefix}added Submodules to ${relative(outDir, page.file)}`);
254
+ }
255
+ } else if (isThin) {
256
+ const noteBlock = [
257
+ '', ':::note[This page is sparse]',
258
+ `The auto-generated reference for \`${page.title}\` is short. Expanding the leading \`/** ... */\` TSDoc comment in the source (purpose, when to use it, a tiny example) would populate this page with real context.`,
259
+ ':::', '',
260
+ ].join('\n');
261
+ newBody = noteBlock + newBody;
262
+ bannered += 1;
263
+ touched = true;
264
+ log(`${c.gold} ⚠${c.reset} ${tagPrefix}thin-page banner on ${relative(outDir, page.file)}`);
265
+ }
266
+
267
+ if (touched && cfg.repoUrl) {
268
+ const branch = version ? version.tag : (cfg.repoBranch ?? 'main');
269
+ const repo = cfg.repoUrl.replace(/\/$/, '');
270
+ newBody = newBody.replace(/\s+$/, '') +
271
+ `\n\n## See also\n\n- [View on GitHub](${repo}/tree/${branch})\n`;
272
+ }
273
+
274
+ writeFileSync(page.file, page.frontmatter + newBody);
275
+ }
276
+
277
+ log('');
278
+ log(`${c.green}✓${c.reset} ${tagPrefix}Generated ${c.gold}${pages.length}${c.reset} TS API page${pages.length === 1 ? '' : 's'} in ${c.cyan}${relative(PROJECT_ROOT, outDir)}${c.reset}/`);
279
+ if (enriched || bannered) log(`${c.dim} ${enriched} landing page${enriched === 1 ? '' : 's'} enriched, ${bannered} thin page${bannered === 1 ? '' : 's'} flagged${c.reset}`);
280
+ }
281
+
282
+ // ─── Run: single-build or per-version with worktrees ──────────────────
283
+ const createdWorktrees = [];
284
+
285
+ function cleanup() {
286
+ for (const wt of createdWorktrees) {
287
+ try {
288
+ execSync(`git -C "${SOURCE_REPO_ROOT}" worktree remove --force "${wt}"`, { stdio: 'ignore' });
289
+ } catch {
290
+ try { rmSync(wt, { recursive: true, force: true }); } catch {}
291
+ }
292
+ }
293
+ }
294
+ process.on('exit', cleanup);
295
+ process.on('SIGINT', () => { cleanup(); process.exit(130); });
296
+
297
+ try {
298
+ if (!versions) {
299
+ buildOnce({
300
+ entryPoints: cfg.entryPoints.map((e) => resolve(PROJECT_ROOT, e)),
301
+ tsconfig: ROOT_TSCONFIG,
302
+ version: null,
303
+ });
304
+ } else {
305
+ for (const v of versions) {
306
+ const wt = mkdtempSync(join(tmpdir(), `tsdoc-${safeTag(v.tag)}-`));
307
+ createdWorktrees.push(wt);
308
+ log(`${c.dim}→ git worktree add ${wt} ${v.tag}${c.reset}`);
309
+ try {
310
+ execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${v.tag}"`, { stdio: 'inherit' });
311
+ } catch {
312
+ log(`${c.red}error${c.reset} failed to create worktree for ${v.tag}; skipping`);
313
+ continue;
314
+ }
315
+ const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
316
+ const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
317
+ const missing = wtEntries.find((p) => !existsSync(p));
318
+ if (missing) {
319
+ log(`${c.gold}warn${c.reset} ${v.tag}: entry point ${missing} not found; skipping`);
320
+ continue;
321
+ }
322
+ buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: v });
323
+ }
324
+
325
+ // Alias the default version at un-versioned URLs
326
+ const defaultV = versions.find((v) => v.default);
327
+ if (defaultV) {
328
+ log(`${c.dim}→ aliasing ${defaultV.tag} as the default (un-versioned) build${c.reset}`);
329
+ const wt = mkdtempSync(join(tmpdir(), `tsdoc-default-`));
330
+ createdWorktrees.push(wt);
331
+ try {
332
+ execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${defaultV.tag}"`, { stdio: 'ignore' });
333
+ const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
334
+ const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
335
+ buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: null });
336
+ } catch (err) {
337
+ log(`${c.gold}warn${c.reset} default-alias build failed: ${err.message}`);
338
+ }
339
+ }
340
+ }
341
+ } finally {
342
+ cleanup();
343
+ }
344
+
345
+ log('');
346
+ log(`${c.dim}Sidebar wiring (astro.config.mjs):${c.reset}`);
347
+ log(`${c.dim} { label: 'TS API', autogenerate: { directory: '${cfg.outputDir.replace(/^src\/content\/docs\/?/, '')}' } }${c.reset}`);
348
+ if (versions) log(`${c.dim} → with ${versions.length} version${versions.length === 1 ? '' : 's'}, the sidebar auto-groups by version subdirectory.${c.reset}`);
349
+ log('');
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "Configuration for scripts/build-python-docs.mjs. Adjust to point at your Python source.",
3
+ "searchPath": "../../python-example/src",
4
+ "modules": [
5
+ "example_module"
6
+ ],
7
+ "outputDir": "src/content/docs/api",
8
+ "repoUrl": "",
9
+ "repoBranch": "main"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "Configuration for scripts/build-ts-docs.mjs. Adjust to point at your TypeScript project.",
3
+ "entryPoints": ["../../packages/your-lib/src/index.ts"],
4
+ "tsconfig": "../../packages/your-lib/tsconfig.json",
5
+ "outputDir": "src/content/docs/api/ts",
6
+ "githubPages": false,
7
+ "skipErrorChecking": true,
8
+ "repoUrl": "",
9
+ "repoBranch": "main"
10
+ }
@@ -0,0 +1 @@
1
+ # Place your project's logo (PNG or SVG) in this folder, then reference it from astro.config.mjs and src/content/docs/index.mdx.
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: Your Project Docs
3
+ description: Replace this with your project's elevator pitch — one sentence that tells visitors what you do.
4
+ template: splash
5
+ hero:
6
+ tagline: One-sentence tagline. Drop your hook here. The skill will offer to update this on first interaction.
7
+ actions:
8
+ - text: Get Started
9
+ link: /quickstart/
10
+ icon: right-arrow
11
+ variant: primary
12
+ - text: View Source
13
+ link: https://github.com/your-org/your-repo
14
+ icon: external
15
+ variant: secondary
16
+ ---
17
+
18
+ import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
19
+ import Glitch from '@abstractdata/starlight-theme/components/Glitch.astro';
20
+
21
+ ## <Glitch text="WHAT'S INSIDE" />
22
+
23
+ Four cards is a starting point — replace, reorder, add more. Each `Card` accepts a `title`, an `icon` (any [Lucide icon](https://lucide.dev) name), and Markdown body content.
24
+
25
+ <CardGrid>
26
+ <Card title="Quickstart" icon="rocket">
27
+ The five-minute install + first deploy walkthrough. [Read it →](/quickstart/)
28
+ </Card>
29
+ <Card title="API Reference" icon="open-book">
30
+ Auto-generated from your source's docstrings (Python today; TypeScript and OpenAPI coming). Run `bun run docs:python`.
31
+ </Card>
32
+ <Card title="Brand toggle" icon="setting">
33
+ Two motion modes: `full` (HUD) for marketing-y docs, `calm` for prose-heavy client docs. Light + dark in both. Configure in `astro.config.mjs`.
34
+ </Card>
35
+ <Card title="Branded components" icon="puzzle">
36
+ `<Glitch>`, `<Hero>`, branded callouts, and a Shiki theme that paints code in cyan / gold / burgundy. Import from `@abstractdata/starlight-theme/components/*`.
37
+ </Card>
38
+ </CardGrid>
39
+
40
+ ## Make it yours
41
+
42
+ Open Claude Code (or Cursor) in this folder — it'll greet you and offer to wire up Python autodoc, your sidebar, and your plugin config in one conversation. Or do it manually:
43
+
44
+ <CardGrid>
45
+ <LinkCard
46
+ title="Edit astro.config.mjs"
47
+ description="Set your project title, sidebar structure, social links, and which motion mode the brand runs in."
48
+ href="/quickstart/#configure"
49
+ />
50
+ <LinkCard
51
+ title="Drop your logo"
52
+ description="Place it in src/assets/, then point logo.src in astro.config.mjs at it."
53
+ href="/quickstart/#logo"
54
+ />
55
+ <LinkCard
56
+ title="Write content"
57
+ description="Markdown and MDX in src/content/docs/. Sub-folders become sidebar groups."
58
+ href="/quickstart/#content"
59
+ />
60
+ <LinkCard
61
+ title="Deploy"
62
+ description="GitHub Pages workflow is preconfigured. Vercel and Cloudflare workflow variants ship alongside."
63
+ href="/quickstart/#deploy"
64
+ />
65
+ </CardGrid>
66
+
67
+ ## Branded callouts
68
+
69
+ The four standard Starlight callouts (`note`, `tip`, `caution`, `danger`) are styled with the brand palette — colored left border, matching glow in HUD mode, branded label typography.
70
+
71
+ :::tip[Pro tip]
72
+ Use `:::caution` for footguns, `:::danger` for destructive operations, `:::note` for context, `:::tip` for opinion. Your readers will thank you.
73
+ :::
74
+
75
+ :::caution[Heads up]
76
+ Update the placeholder values in `astro.config.mjs` (title, site URL, social links) before deploying. The skill's first-interaction handshake catches these — open your AI assistant if you'd like it to do the rewrite for you.
77
+ :::
78
+
79
+ ---
80
+
81
+ > Replace this whole page once your real content lands. The `template: splash` frontmatter is what gives this hero its layout; switch to `template: doc` (or remove the line) on inner pages so they get a sidebar and table of contents.
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Quickstart
3
+ description: Five minutes from clean checkout to a deployed branded docs site.
4
+ ---
5
+
6
+ This template gives you a Starlight documentation site pre-wired with the [Abstract Data Documentation Theme](https://github.com/Abstract-Data/abstract-data-doc-theme). This page is the hands-on walkthrough — copy it, replace the placeholders, ship it.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ bun install
12
+ bun dev
13
+ ```
14
+
15
+ That boots the dev server at `http://localhost:4321` with hot module reload. Edit any `.md`/`.mdx` file under `src/content/docs/` and the page updates instantly.
16
+
17
+ :::tip
18
+ If you don't have Bun yet: `curl -fsSL https://bun.sh/install | bash`. The theme is designed for Bun (faster installs, native TypeScript) but works on `npm` and `pnpm` too if your team prefers.
19
+ :::
20
+
21
+ ## Configure
22
+
23
+ Open `astro.config.mjs` and replace the placeholders:
24
+
25
+ ```js {3-5,12,18-19}
26
+ starlight({
27
+ // ⬇️ project metadata
28
+ title: 'Your Project Docs',
29
+ description: 'One-sentence elevator pitch.',
30
+ site: 'https://your-deployed-url.example.com',
31
+
32
+ // ⬇️ uncomment for a logo
33
+ // logo: { src: './src/assets/your-logo.png', replacesTitle: true },
34
+
35
+ social: [
36
+ {
37
+ icon: 'github',
38
+ href: 'https://github.com/your-org/your-repo',
39
+ label: 'GitHub',
40
+ },
41
+ ],
42
+
43
+ plugins: [
44
+ abstractData({
45
+ motion: 'calm', // 'full' for HUD, 'calm' for client docs
46
+ credit: 'auto', // 'hide' for white-label
47
+ }),
48
+ ],
49
+ });
50
+ ```
51
+
52
+ The three knobs that matter most:
53
+
54
+ - **`motion`** — `full` is the loud HUD look (marketing-y, animated hex grid, glitch on the version chip); `calm` strips animations while keeping palette and fonts. Default: `calm` for client work.
55
+ - **`credit`** — `auto` shows "Built by Abstract Data" in the footer; `hide` removes it for white-label projects.
56
+ - **`version`** *(optional)* — set to a string like `'v1.0.0'` to display a version chip in the header next to the social icons.
57
+
58
+ ## Add your logo
59
+
60
+ Drop a PNG or SVG in `src/assets/` (e.g. `your-logo.png`), then in `astro.config.mjs`:
61
+
62
+ ```js
63
+ logo: {
64
+ src: './src/assets/your-logo.png',
65
+ replacesTitle: true, // hide the text title when logo is present
66
+ }
67
+ ```
68
+
69
+ The theme's `<Hero>` component uses Astro's `<Image>` for optimized output (lazy loading, AVIF/WebP, srcset). Drop a `400×400` or larger source — Astro handles the rest.
70
+
71
+ ## Write content
72
+
73
+ Pages live under `src/content/docs/`:
74
+
75
+ ```
76
+ src/content/docs/
77
+ ├── index.mdx # the splash page (this file is its sibling)
78
+ ├── quickstart.md # this page
79
+ └── guides/ # subfolders become sidebar groups (with autogenerate)
80
+ ├── deployment.md
81
+ └── customization.md
82
+ ```
83
+
84
+ Frontmatter sets the page title and description. Body is Markdown or MDX (Markdown + components):
85
+
86
+ ```md
87
+ ---
88
+ title: My Page
89
+ description: Short SEO description.
90
+ ---
91
+
92
+ # Heading auto-rendered by Starlight
93
+
94
+ Markdown content with **bold**, *italic*, [links](/), and `inline code`.
95
+ ```
96
+
97
+ The theme's branded components are importable in any `.mdx` file:
98
+
99
+ ```mdx
100
+ import Glitch from '@abstractdata/starlight-theme/components/Glitch.astro';
101
+
102
+ # <Glitch text="404" /> Not Found
103
+ ```
104
+
105
+ ## Add API reference
106
+
107
+ The template ships two autodoc pipelines — pick the one(s) that match your source. The `abstract-data-setup` workflow can detect your stack and prune this section automatically; if you'd rather drive it manually, follow whichever subsection applies.
108
+
109
+ <!-- abstract-data-setup:python-autodoc -->
110
+
111
+ ### Python source
112
+
113
+ ```bash
114
+ bun run docs:python
115
+ ```
116
+
117
+ Reads `scripts/python-autodoc.json`, generates Markdown pages under `src/content/docs/api/` from your module docstrings via `pydoc-markdown`. Edit the config to point `searchPath` at your Python source root and list the `modules` you want documented.
118
+
119
+ :::caution
120
+ You need `pydoc-markdown` installed in your local Python environment: `pipx install pydoc-markdown` or `pip install --user pydoc-markdown`.
121
+ :::
122
+
123
+ <!-- /abstract-data-setup:python-autodoc -->
124
+
125
+ <!-- abstract-data-setup:ts-autodoc -->
126
+
127
+ ### TypeScript source
128
+
129
+ ```bash
130
+ bun run docs:ts
131
+ ```
132
+
133
+ Reads `scripts/ts-autodoc.json`, generates Markdown pages under `src/content/docs/api/ts/` from your TSDoc comments via TypeDoc + `typedoc-plugin-markdown`. Point `entryPoints` at the entry TS files and `tsconfig` at the matching tsconfig.
134
+
135
+ :::caution
136
+ Install once as dev deps: `bun add -d typedoc typedoc-plugin-markdown`.
137
+ :::
138
+
139
+ <!-- /abstract-data-setup:ts-autodoc -->
140
+
141
+ :::tip[Guided setup]
142
+ Open Claude Code or Cursor in this folder and say *"set up docs"* — the bundled `abstract-data-setup` workflow detects your stack(s), audits docstring coverage, asks which modules to document, writes the configs, and tailors this very page so only the relevant autodoc subsection remains.
143
+ :::
144
+
145
+ ## Deploy
146
+
147
+ Three workflow files ship in `.github/workflows/`:
148
+
149
+ | File | Target | When to use |
150
+ |---|---|---|
151
+ | `deploy.yml` | GitHub Pages | Default. Just enable Pages → "GitHub Actions" in repo settings and push. |
152
+ | `deploy-vercel.yml` | Vercel | Opt-in. Add `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` secrets; run once. |
153
+ | `deploy-cloudflare.yml` | Cloudflare Pages | Opt-in. Add `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`. |
154
+
155
+ For Vercel and Cloudflare, the simpler path is connecting the repo through their respective dashboards (no workflow file needed) — they auto-detect Astro and deploy on push. Use the workflow files only when you want CI to control deploys.
156
+
157
+ ## What's next
158
+
159
+ - Iterate on `index.mdx` — the splash page is your shop window.
160
+ - Add per-section guides under `src/content/docs/guides/`.
161
+ - Run `bun run build && bun run preview` before deploying to catch errors with production assets.
162
+ - Open Claude Code or Cursor in this folder if you want a guided setup — the bundled `abstract-data-setup` workflow handles the boilerplate.
@@ -0,0 +1,46 @@
1
+ import { defineCollection, z } from 'astro:content';
2
+ import { docsLoader } from '@astrojs/starlight/loaders';
3
+ import { docsSchema } from '@astrojs/starlight/schema';
4
+
5
+ /**
6
+ * Type-safe frontmatter for the docs collection.
7
+ *
8
+ * `docsSchema({ extend })` lets you add custom fields on top of Starlight's
9
+ * built-in ones (`title`, `description`, `sidebar`, `draft`, `pagefind`, …).
10
+ * The fields below are scaffolding — keep, replace, or extend them to match
11
+ * how your team actually tags content. Errors in frontmatter fail the build
12
+ * with helpful Zod messages, so this catches issues at PR time instead of
13
+ * after deploy.
14
+ *
15
+ * Common fields teams add:
16
+ * - `category`: a grouping label for filterable views or future analytics
17
+ * - `audience`: who this page is written for
18
+ * - `lastReviewed`: when a maintainer last vetted the content (good for
19
+ * surfacing "this page is over a year old" warnings)
20
+ */
21
+ export const collections = {
22
+ docs: defineCollection({
23
+ loader: docsLoader(),
24
+ schema: docsSchema({
25
+ extend: z.object({
26
+ // ── User-authored fields ───────────────────────────────────
27
+ category: z.string().optional(),
28
+ audience: z
29
+ .enum(['user', 'contributor', 'maintainer'])
30
+ .optional(),
31
+ lastReviewed: z.coerce.date().optional(),
32
+
33
+ // ── Theme-managed fields ───────────────────────────────────
34
+ // Written by the autodoc orchestrators (build-python-docs.mjs /
35
+ // build-ts-docs.mjs) onto every page emitted under a `versions[]`
36
+ // tag. The bundled <VersionPicker> reads them via
37
+ // getCollection('docs') so the autodoc JSON stays the canonical
38
+ // source of truth — no duplicating the version list in your
39
+ // override component. Don't hand-edit these.
40
+ version: z.string().optional(),
41
+ versionLabel: z.string().optional(),
42
+ versionDefault: z.boolean().optional(),
43
+ }),
44
+ }),
45
+ }),
46
+ };
@@ -0,0 +1,2 @@
1
+ /// <reference path="../.astro/types.d.ts" />
2
+ /// <reference types="@abstractdata/starlight-theme/types" />
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist"]
5
+ }