nimbus-docs 0.0.3 → 0.1.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/cli/index.js +31 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -3
- package/dist/client.js.map +1 -1
- package/dist/content.d.ts +36 -1
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +19 -2
- package/dist/content.js.map +1 -1
- package/dist/index.d.ts +289 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1103 -52
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +24 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +17 -2
- package/dist/schemas.js.map +1 -1
- package/dist/types.d.ts +118 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/components/NimbusHead.astro +81 -2
package/dist/cli/index.js
CHANGED
|
@@ -131,6 +131,12 @@ const BUNDLED_INDEX = {
|
|
|
131
131
|
"title": "PackageManagers",
|
|
132
132
|
"description": "Tabbed install command block translated across npm / pnpm / yarn / bun."
|
|
133
133
|
},
|
|
134
|
+
"page-actions": {
|
|
135
|
+
"name": "page-actions",
|
|
136
|
+
"type": "registry:ui",
|
|
137
|
+
"title": "PageActions",
|
|
138
|
+
"description": "Inline page-header actions: copy the page as markdown, open the raw .md."
|
|
139
|
+
},
|
|
134
140
|
"pagination": {
|
|
135
141
|
"name": "pagination",
|
|
136
142
|
"type": "registry:ui",
|
|
@@ -179,6 +185,12 @@ const BUNDLED_INDEX = {
|
|
|
179
185
|
"title": "TOC",
|
|
180
186
|
"description": "On-page table of contents with active-heading tracking."
|
|
181
187
|
},
|
|
188
|
+
"version-switcher": {
|
|
189
|
+
"name": "version-switcher",
|
|
190
|
+
"type": "registry:ui",
|
|
191
|
+
"title": "VersionPicker",
|
|
192
|
+
"description": "Header dropdown for switching between docs versions. Reads `versions` from nimbus.config.ts, uses the build-time alternates table to land readers on the same logical page in the target version. Includes deprecation badge and hidden-version exclusion. Renders nothing when versioning is off or only one version is configured."
|
|
193
|
+
},
|
|
182
194
|
"404-page": {
|
|
183
195
|
"name": "404-page",
|
|
184
196
|
"type": "registry:feature",
|
|
@@ -191,6 +203,24 @@ const BUNDLED_INDEX = {
|
|
|
191
203
|
"title": "AI-native static surface",
|
|
192
204
|
"description": "Add llms.txt, markdown variants, robots.txt, and an AgentDirective to a Nimbus docs site."
|
|
193
205
|
},
|
|
206
|
+
"component-showcase": {
|
|
207
|
+
"name": "component-showcase",
|
|
208
|
+
"type": "registry:feature",
|
|
209
|
+
"title": "Component showcase",
|
|
210
|
+
"description": "Add a /components grid landing plus per-component showcase pages at /components/<slug>, driven by a dedicated content collection. For sites documenting their own UI library."
|
|
211
|
+
},
|
|
212
|
+
"new-collection": {
|
|
213
|
+
"name": "new-collection",
|
|
214
|
+
"type": "registry:feature",
|
|
215
|
+
"title": "Add a new collection",
|
|
216
|
+
"description": "End-to-end setup for a non-version content tree on a Nimbus docs site — blog, API reference, changelog, glossary. Creates the folder, registers the collection, scaffolds routes. For docs versions, use `nimbus-docs add new-version`."
|
|
217
|
+
},
|
|
218
|
+
"new-version": {
|
|
219
|
+
"name": "new-version",
|
|
220
|
+
"type": "registry:feature",
|
|
221
|
+
"title": "Add a docs version",
|
|
222
|
+
"description": "End-to-end setup for adding a docs version to a Nimbus site. Creates the content directory, registers the collection, scaffolds routes, declares the manifest, installs the version-switcher picker, and wires it into your Header and DocsLayout. One command for one mental task."
|
|
223
|
+
},
|
|
194
224
|
"pagefind-search": {
|
|
195
225
|
"name": "pagefind-search",
|
|
196
226
|
"type": "registry:feature",
|
|
@@ -596,7 +626,7 @@ async function main() {
|
|
|
596
626
|
return;
|
|
597
627
|
}
|
|
598
628
|
if (args.version) {
|
|
599
|
-
process.stdout.write(`0.
|
|
629
|
+
process.stdout.write(`0.1.1\n`);
|
|
600
630
|
return;
|
|
601
631
|
}
|
|
602
632
|
const [command, slug] = args._;
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["addCommand","addCommand"],"sources":["../../src/cli/_registry.generated.ts","../../src/cli/pm.ts","../../src/cli/component.ts","../../src/cli/dotenv.ts","../../src/cli/resolver.ts","../../src/cli/feature.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Bundled registry index — auto-generated by\n * apps/www/scripts/generate-registry.ts. Do not edit by hand.\n *\n * Re-run via: pnpm --filter @nimbus/www generate-registry\n */\n\nexport type RegistryEntryType =\n | \"registry:ui\"\n | \"registry:lib\"\n | \"registry:feature\";\n\nexport interface RegistryIndexEntry {\n name: string;\n type: RegistryEntryType;\n title: string;\n description: string;\n}\n\nexport interface BundledIndex {\n version: 1;\n items: Record<string, RegistryIndexEntry>;\n}\n\nexport const REGISTRY_BASE_URL = \"https://nimbus-docs.com/registry\";\n\nexport const BUNDLED_INDEX: BundledIndex = {\n \"version\": 1,\n \"items\": {\n \"cn\": {\n \"name\": \"cn\",\n \"type\": \"registry:lib\",\n \"title\": \"cn\",\n \"description\": \"Tailwind-aware className merger built on clsx + tailwind-merge.\"\n },\n \"accordion\": {\n \"name\": \"accordion\",\n \"type\": \"registry:ui\",\n \"title\": \"Accordion\",\n \"description\": \"Vertically stacked collapsible sections.\"\n },\n \"aside\": {\n \"name\": \"aside\",\n \"type\": \"registry:ui\",\n \"title\": \"Aside\",\n \"description\": \"Generic boxed callout. Building block for Callout, Note, Warning.\"\n },\n \"badge\": {\n \"name\": \"badge\",\n \"type\": \"registry:ui\",\n \"title\": \"Badge\",\n \"description\": \"Small status / category pill.\"\n },\n \"banner\": {\n \"name\": \"banner\",\n \"type\": \"registry:ui\",\n \"title\": \"Banner\",\n \"description\": \"Site-wide dismissible announcement bar.\"\n },\n \"breadcrumbs\": {\n \"name\": \"breadcrumbs\",\n \"type\": \"registry:ui\",\n \"title\": \"Breadcrumbs\",\n \"description\": \"Page-context navigation crumbs.\"\n },\n \"callout\": {\n \"name\": \"callout\",\n \"type\": \"registry:ui\",\n \"title\": \"Callout\",\n \"description\": \"Inline note / tip / warning / danger / info card.\"\n },\n \"card\": {\n \"name\": \"card\",\n \"type\": \"registry:ui\",\n \"title\": \"Card\",\n \"description\": \"Generic content card with optional title and footer.\"\n },\n \"card-grid\": {\n \"name\": \"card-grid\",\n \"type\": \"registry:ui\",\n \"title\": \"CardGrid\",\n \"description\": \"Responsive grid layout for cards.\"\n },\n \"code\": {\n \"name\": \"code\",\n \"type\": \"registry:ui\",\n \"title\": \"Code\",\n \"description\": \"Inline / block code wrapper. Re-exports Astro's built-in <Code> (Shiki).\"\n },\n \"code-group\": {\n \"name\": \"code-group\",\n \"type\": \"registry:ui\",\n \"title\": \"CodeGroup\",\n \"description\": \"Tabbed group of code blocks.\"\n },\n \"collapsible\": {\n \"name\": \"collapsible\",\n \"type\": \"registry:ui\",\n \"title\": \"Collapsible\",\n \"description\": \"Headless show/hide primitive — building block for Accordion and Sidebar groups.\"\n },\n \"dialog\": {\n \"name\": \"dialog\",\n \"type\": \"registry:ui\",\n \"title\": \"Dialog\",\n \"description\": \"Modal dialog with focus-trap and body-scroll lock.\"\n },\n \"embed\": {\n \"name\": \"embed\",\n \"type\": \"registry:ui\",\n \"title\": \"Embed\",\n \"description\": \"Responsive iframe / video / external content wrapper.\"\n },\n \"file-tree\": {\n \"name\": \"file-tree\",\n \"type\": \"registry:ui\",\n \"title\": \"FileTree\",\n \"description\": \"Render a directory tree as nested markup.\"\n },\n \"frame\": {\n \"name\": \"frame\",\n \"type\": \"registry:ui\",\n \"title\": \"Frame\",\n \"description\": \"Decorative outer frame for screenshots and demos.\"\n },\n \"layer-card\": {\n \"name\": \"layer-card\",\n \"type\": \"registry:ui\",\n \"title\": \"LayerCard\",\n \"description\": \"Stacked-card container with sticky header. Base for CodeGroup and PackageManagers.\"\n },\n \"link-button\": {\n \"name\": \"link-button\",\n \"type\": \"registry:ui\",\n \"title\": \"LinkButton\",\n \"description\": \"Anchor styled as a button.\"\n },\n \"link-card\": {\n \"name\": \"link-card\",\n \"type\": \"registry:ui\",\n \"title\": \"LinkCard\",\n \"description\": \"Card whose entire surface is a link.\"\n },\n \"package-managers\": {\n \"name\": \"package-managers\",\n \"type\": \"registry:ui\",\n \"title\": \"PackageManagers\",\n \"description\": \"Tabbed install command block translated across npm / pnpm / yarn / bun.\"\n },\n \"pagination\": {\n \"name\": \"pagination\",\n \"type\": \"registry:ui\",\n \"title\": \"Pagination\",\n \"description\": \"Prev / next page navigation.\"\n },\n \"popover\": {\n \"name\": \"popover\",\n \"type\": \"registry:ui\",\n \"title\": \"Popover\",\n \"description\": \"Floating panel anchored to a trigger element.\"\n },\n \"search\": {\n \"name\": \"search\",\n \"type\": \"registry:ui\",\n \"title\": \"Search\",\n \"description\": \"Command-palette search dialog with a provider seam. Defaults to Pagefind.\"\n },\n \"sidebar\": {\n \"name\": \"sidebar\",\n \"type\": \"registry:ui\",\n \"title\": \"Sidebar\",\n \"description\": \"Docs sidebar with nested groups and active-link tracking.\"\n },\n \"steps\": {\n \"name\": \"steps\",\n \"type\": \"registry:ui\",\n \"title\": \"Steps\",\n \"description\": \"Numbered ordered-list with vertical connectors.\"\n },\n \"tabs\": {\n \"name\": \"tabs\",\n \"type\": \"registry:ui\",\n \"title\": \"Tabs\",\n \"description\": \"Tabbed content panels (manual + Starlight-compatible modes).\"\n },\n \"theme-toggle\": {\n \"name\": \"theme-toggle\",\n \"type\": \"registry:ui\",\n \"title\": \"ThemeToggle\",\n \"description\": \"Light / dark theme switcher button.\"\n },\n \"toc\": {\n \"name\": \"toc\",\n \"type\": \"registry:ui\",\n \"title\": \"TOC\",\n \"description\": \"On-page table of contents with active-heading tracking.\"\n },\n \"404-page\": {\n \"name\": \"404-page\",\n \"type\": \"registry:feature\",\n \"title\": \"Custom 404 page\",\n \"description\": \"Generate a brand-matched 404 page for the docs site.\"\n },\n \"ai-native\": {\n \"name\": \"ai-native\",\n \"type\": \"registry:feature\",\n \"title\": \"AI-native static surface\",\n \"description\": \"Add llms.txt, markdown variants, robots.txt, and an AgentDirective to a Nimbus docs site.\"\n },\n \"pagefind-search\": {\n \"name\": \"pagefind-search\",\n \"type\": \"registry:feature\",\n \"title\": \"Pagefind search\",\n \"description\": \"Add static Pagefind indexing and the Nimbus search dialog to an existing docs site.\"\n }\n }\n};\n","/**\n * Package-manager detection + install command helpers.\n *\n * Detection prefers lockfile presence in the user's cwd, then falls back\n * to the `npm_config_user_agent` env var the active package manager sets\n * when invoking the CLI. Finally falls back to `npm`.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nconst LOCKFILES: ReadonlyArray<readonly [string, PackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"yarn.lock\", \"yarn\"],\n [\"bun.lockb\", \"bun\"],\n [\"bun.lock\", \"bun\"],\n [\"package-lock.json\", \"npm\"],\n];\n\nexport function detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of LOCKFILES) {\n if (existsSync(join(cwd, lockfile))) return pm;\n }\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.startsWith(\"pnpm\")) return \"pnpm\";\n if (ua.startsWith(\"yarn\")) return \"yarn\";\n if (ua.startsWith(\"bun\")) return \"bun\";\n return \"npm\";\n}\n\n/**\n * Command + args to install one or more new npm deps. Each PM picks the\n * verb that both adds to package.json AND installs:\n *\n * npm install <deps...>\n * pnpm add <deps...>\n * yarn add <deps...>\n * bun add <deps...>\n */\nexport function addCommand(\n pm: PackageManager,\n deps: string[],\n): { bin: string; args: string[] } {\n if (deps.length === 0) {\n throw new Error(\"addCommand called with empty deps\");\n }\n switch (pm) {\n case \"npm\":\n return { bin: \"npm\", args: [\"install\", ...deps] };\n case \"pnpm\":\n return { bin: \"pnpm\", args: [\"add\", ...deps] };\n case \"yarn\":\n return { bin: \"yarn\", args: [\"add\", ...deps] };\n case \"bun\":\n return { bin: \"bun\", args: [\"add\", ...deps] };\n }\n}\n","/**\n * Component / utility installer.\n *\n * Walks the resolved list of items, writes each file (per-file overwrite\n * prompt on conflict), then collects all npm `dependencies` across the\n * tree and runs `<pm> add` once for the dedup'd set.\n *\n * File destination: `<cwd>/src/<path>`. The `path` field already encodes\n * the directory layout (e.g. `components/ui/dialog/Dialog.astro`).\n */\n\nimport { spawn } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nimport * as p from \"@clack/prompts\";\n\nimport { addCommand, detectPackageManager } from \"./pm.js\";\nimport type { ComponentItem } from \"./resolver.js\";\n\nexport interface InstallOptions {\n /** User's project root. */\n cwd: string;\n /** Skip overwrite prompts; assume \"overwrite\" on every conflict. */\n yes: boolean;\n}\n\nexport interface InstallReport {\n /** Individual files actually written. */\n written: string[];\n /** Registry slugs skipped wholesale by the user. */\n skipped: string[];\n npmDepsInstalled: string[];\n}\n\nexport async function installComponents(\n items: ComponentItem[],\n options: InstallOptions,\n): Promise<InstallReport> {\n const report: InstallReport = {\n written: [],\n skipped: [],\n npmDepsInstalled: [],\n };\n\n // ---- 1. Write files — atomic per registry item -------------------------\n //\n // Each item (e.g. `dialog`) is treated as an indivisible unit: when any\n // of its files conflict, we prompt once for the whole slug. Letting users\n // overwrite Dialog.astro while keeping DialogContent.astro is a footgun\n // — components are cohesive and meant to evolve together.\n const srcDir = join(options.cwd, \"src\");\n\n for (const item of items) {\n const filePlans = item.files.map((file) => {\n const targetAbs = join(srcDir, file.path);\n return {\n targetAbs,\n targetRel: relative(options.cwd, targetAbs),\n content: file.content,\n exists: existsSync(targetAbs),\n };\n });\n\n const conflicts = filePlans.filter((f) => f.exists);\n\n // Utilities (registry:lib) are transitive dependencies of UI\n // components — install silently when missing, skip silently when\n // present. Never prompt or overwrite: users may have customized\n // them (e.g. cn) and being asked about `cn` every time you `add` a\n // component is noise.\n if (item.type === \"registry:lib\") {\n if (conflicts.length > 0) {\n report.skipped.push(item.name);\n continue;\n }\n } else if (conflicts.length > 0 && !options.yes) {\n const total = filePlans.length;\n const message =\n conflicts.length === total\n ? `${item.name} is already installed (${total} file${total === 1 ? \"\" : \"s\"}). Overwrite?`\n : `${item.name} is partially installed (${conflicts.length} of ${total} file${total === 1 ? \"\" : \"s\"} present). Overwrite all?`;\n\n const choice = await p.select({\n message,\n options: [\n { value: \"overwrite\", label: \"Overwrite — replace existing files\" },\n { value: \"skip\", label: \"Skip — leave files as-is\" },\n { value: \"cancel\", label: \"Cancel install\" },\n ],\n initialValue: \"overwrite\",\n });\n\n if (p.isCancel(choice) || choice === \"cancel\") {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n }\n if (choice === \"skip\") {\n report.skipped.push(item.name);\n continue;\n }\n }\n\n // Either no conflicts, --yes, or user chose overwrite. Write every file.\n for (const plan of filePlans) {\n mkdirSync(dirname(plan.targetAbs), { recursive: true });\n writeFileSync(plan.targetAbs, plan.content);\n report.written.push(plan.targetRel);\n }\n }\n\n // ---- 2. Install missing npm deps ---------------------------------------\n const allDeps = new Set<string>();\n for (const item of items) {\n for (const dep of item.dependencies) allDeps.add(dep);\n }\n\n if (allDeps.size > 0) {\n const newDeps = filterAlreadyInstalled(options.cwd, [...allDeps]);\n if (newDeps.length > 0) {\n const pm = detectPackageManager(options.cwd);\n const { bin, args } = addCommand(pm, newDeps);\n const spinner = p.spinner();\n spinner.start(`${pm} add ${newDeps.join(\" \")}`);\n try {\n await runCommand(bin, args, options.cwd);\n spinner.stop(\n `Installed ${newDeps.length} dep${newDeps.length === 1 ? \"\" : \"s\"}.`,\n );\n report.npmDepsInstalled = newDeps;\n } catch (err) {\n spinner.stop(\"Dependency install failed.\");\n p.log.warn(\n `Could not install ${newDeps.join(\", \")}. Run \\`${bin} ${args.join(\" \")}\\` manually.`,\n );\n }\n }\n }\n\n return report;\n}\n\n/**\n * Filter out deps already present in `dependencies` or `devDependencies`\n * of the user's package.json. If package.json is missing, returns all.\n */\nfunction filterAlreadyInstalled(cwd: string, deps: string[]): string[] {\n const pkgPath = join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return deps;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const installed = new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n return deps.filter((d) => !installed.has(d));\n } catch {\n return deps;\n }\n}\n\nfunction runCommand(\n bin: string,\n args: string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolveP, rejectP) => {\n const child = spawn(bin, args, {\n cwd,\n stdio: [\"ignore\", \"ignore\", \"inherit\"],\n });\n child.on(\"close\", (code) =>\n code === 0\n ? resolveP()\n : rejectP(new Error(`${bin} ${args.join(\" \")} exited ${code}`)),\n );\n child.on(\"error\", rejectP);\n });\n}\n","/**\n * Tiny .env loader — no dependency.\n *\n * Reads `.env` from the user's cwd at CLI startup and sets any KEY=VALUE\n * pairs into `process.env` IF the variable isn't already set (so a shell-\n * provided env always wins over the file). Supports the basic cases:\n *\n * KEY=value\n * KEY=\"quoted value\"\n * KEY='quoted value'\n * # comments\n *\n * Used so `examples/local/.env` can carry `NIMBUS_REGISTRY_URL=...` without\n * the user having to prefix every CLI invocation.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function loadDotenv(cwd: string): void {\n const path = join(cwd, \".env\");\n if (!existsSync(path)) return;\n\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return;\n }\n\n for (const rawLine of raw.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n\n const key = line.slice(0, eq).trim();\n if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) continue;\n\n let value = line.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n","/**\n * Registry resolver.\n *\n * Two entry points:\n *\n * - `resolveComponentTree(slug)` walks `registryDependencies` transitively\n * and returns a flat ordered list of components/utilities to install\n * (dependencies first, root last). Cycles are detected as repeated\n * visits and skipped.\n *\n * - `fetchFeatureMarkdown(slug)` returns the raw markdown for an\n * agent-handoff feature; the caller decides what to do with it.\n *\n * The base URL for hosted artifacts is read from the bundled index, with\n * an `NIMBUS_REGISTRY_URL` env override for local development.\n */\n\nimport {\n BUNDLED_INDEX,\n REGISTRY_BASE_URL,\n type RegistryIndexEntry,\n} from \"./_registry.generated.js\";\n\nexport interface RegistryFile {\n path: string;\n content: string;\n}\n\nexport interface ComponentItem {\n name: string;\n type: \"registry:ui\" | \"registry:lib\";\n title: string;\n description: string;\n dependencies: string[];\n registryDependencies: string[];\n files: RegistryFile[];\n}\n\n/**\n * Read the registry base URL on every call so `.env` files loaded after\n * module-import time (see cli/dotenv.ts) are picked up. The cost is\n * negligible — string interpolation of an env var.\n */\nfunction getBaseUrl(): string {\n return (process.env.NIMBUS_REGISTRY_URL ?? REGISTRY_BASE_URL).replace(\n /\\/$/,\n \"\",\n );\n}\n\n// ---------------------------------------------------------------------------\n// Index lookup (offline — no network)\n// ---------------------------------------------------------------------------\n\nexport function getIndexEntry(slug: string): RegistryIndexEntry | undefined {\n return BUNDLED_INDEX.items[slug];\n}\n\nexport function listEntries(filter?: {\n type?: RegistryIndexEntry[\"type\"];\n}): RegistryIndexEntry[] {\n const all = Object.values(BUNDLED_INDEX.items);\n if (!filter?.type) return all;\n return all.filter((e) => e.type === filter.type);\n}\n\n// ---------------------------------------------------------------------------\n// Network: component JSON + feature markdown\n// ---------------------------------------------------------------------------\n\nasync function httpGet(url: string): Promise<Response> {\n let res: Response;\n try {\n res = await fetch(url);\n } catch (err) {\n const cause = (err as Error).message;\n throw new Error(\n `Could not reach the registry at ${url}.\\n` +\n ` Underlying error: ${cause}\\n\\n` +\n ` Things to try:\\n` +\n ` - Is the registry server running? Start it with \\`pnpm local\\` (in the monorepo root).\\n` +\n ` - Override the URL: NIMBUS_REGISTRY_URL=https://example.com nimbus-docs add ...\\n` +\n ` - Check the value in your project's .env file.`,\n );\n }\n if (!res.ok) {\n throw new Error(\n `Registry returned ${res.status} ${res.statusText} for ${url}. ` +\n `The server is up but doesn't know about this slug — check \\`nimbus-docs list\\` for valid names.`,\n );\n }\n return res;\n}\n\nexport async function fetchComponent(slug: string): Promise<ComponentItem> {\n const res = await httpGet(`${getBaseUrl()}/components/${slug}.json`);\n return (await res.json()) as ComponentItem;\n}\n\nexport async function fetchFeatureMarkdown(slug: string): Promise<string> {\n const res = await httpGet(`${getBaseUrl()}/features/${slug}.md`);\n return await res.text();\n}\n\n// ---------------------------------------------------------------------------\n// Transitive dep resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Depth-first walk of registryDependencies. Returns items in install order\n * (deps before dependents), deduplicated by slug.\n */\nexport async function resolveComponentTree(\n rootSlug: string,\n): Promise<ComponentItem[]> {\n const visited = new Set<string>();\n const ordered: ComponentItem[] = [];\n\n async function visit(slug: string): Promise<void> {\n if (visited.has(slug)) return;\n visited.add(slug);\n\n const item = await fetchComponent(slug);\n\n // Walk deps first so they're earlier in the install order.\n for (const dep of item.registryDependencies) {\n await visit(dep);\n }\n\n ordered.push(item);\n }\n\n await visit(rootSlug);\n return ordered;\n}\n","/**\n * Feature installer — Flue-style agent-handoff.\n *\n * Same rule Flue uses for `flue add`: if `--print` is set OR\n * `determineAgent()` says the CLI is running inside a known coding agent,\n * the markdown is piped to stdout for the agent to consume. Otherwise we\n * print human-friendly instructions on stderr telling the user exactly\n * how to pipe the output to their agent of choice.\n *\n * No picker, no clipboard mode — the printed pipe commands cover both.\n */\n\nimport { determineAgent } from \"@vercel/detect-agent\";\n\nimport { fetchFeatureMarkdown } from \"./resolver.js\";\n\nexport interface FeatureInstallOptions {\n /** Force markdown to stdout regardless of agent detection. */\n print: boolean;\n}\n\nexport async function installFeature(\n slug: string,\n options: FeatureInstallOptions,\n): Promise<void> {\n const markdown = await fetchFeatureMarkdown(slug);\n\n // Same predicate as Flue: explicit --print, or detection says we're\n // running inside a known agent (which captures our stdout).\n const detected = await determineAgent().catch(() => ({\n isAgent: false as const,\n }));\n const isAgentMode = options.print || detected.isAgent === true;\n\n if (isAgentMode) {\n process.stdout.write(markdown);\n if (!markdown.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n return;\n }\n\n printHumanInstructions(slug);\n}\n\n/**\n * Stderr-only. We don't put this on stdout because if the user pipes our\n * output anywhere by accident, only the markdown should reach the agent.\n *\n * Formatting mirrors Flue's `printHumanInstructions` 1:1 — agents listed\n * with a blank line between the \"first-tier\" CLIs (claude/codex/cursor-agent)\n * and the rest (opencode/pi).\n */\nfunction printHumanInstructions(slug: string): void {\n const cmd = `nimbus-docs add ${slug}`;\n const stream = process.stderr;\n stream.write(`${cmd}\\n\\n`);\n stream.write(\"To install this feature, pipe it to your coding agent:\\n\\n\");\n stream.write(` ${cmd} --print | claude\\n`);\n stream.write(` ${cmd} --print | codex\\n`);\n stream.write(` ${cmd} --print | cursor-agent\\n\\n`);\n stream.write(` ${cmd} --print | opencode\\n`);\n stream.write(` ${cmd} --print | pi\\n`);\n stream.write(\"Or paste this prompt into any agent:\\n\\n\");\n stream.write(` Run \"${cmd} --print\" and follow the instructions.\\n`);\n}\n","#!/usr/bin/env node\n\n/**\n * `nimbus-docs` CLI entry.\n *\n * Surface:\n *\n * nimbus → list (table of installable items)\n * nimbus-docs list → list\n * nimbus-docs list --type ui|lib|feature\n * nimbus-docs add → list\n * nimbus-docs add <slug> → install (component path or feature path)\n * nimbus-docs add <slug> --yes → component: skip overwrite prompts\n * nimbus-docs add <slug> --print → feature: print markdown to stdout (skip detect)\n *\n * Feature behavior mirrors Flue's `add` command: print markdown to stdout\n * iff `--print` OR an agent is detected; otherwise print human-friendly\n * pipe instructions to stderr.\n *\n * The bundled index makes `list` (and `add` with no slug) work offline.\n * Per-item content is fetched from `REGISTRY_BASE_URL` only when actually\n * installing a slug — override via `NIMBUS_REGISTRY_URL` for local dev.\n */\n\nimport mri from \"mri\";\nimport * as p from \"@clack/prompts\";\n\nimport { BUNDLED_INDEX } from \"./_registry.generated.js\";\nimport { installComponents } from \"./component.js\";\nimport { loadDotenv } from \"./dotenv.js\";\nimport { installFeature } from \"./feature.js\";\nimport {\n getIndexEntry,\n listEntries,\n resolveComponentTree,\n} from \"./resolver.js\";\n\n// Load .env from the user's cwd so per-project NIMBUS_REGISTRY_URL (and\n// any future env vars) work without shell prefixes. Shell-provided vars\n// always win (loadDotenv only sets undefined keys).\nloadDotenv(process.cwd());\n\ndeclare const __APP_VERSION__: string;\n\ninterface CliArgs {\n _: string[];\n yes: boolean;\n print: boolean;\n help: boolean;\n version: boolean;\n type?: string;\n}\n\nconst HELP = `\n Usage: nimbus-docs <command> [args]\n\n Commands:\n list [--type ui|lib|feature] List available registry items\n add Same as \\`list\\`\n add <slug> Install a component or hand off a feature\n\n Flags:\n --yes, -y Component: overwrite conflicts without prompting\n --print Feature: print markdown to stdout (skip agent detect)\n --type <ui|lib|feature> \\`list\\`: filter by type\n --help, -h\n --version, -v\n\n Examples:\n nimbus-docs add dialog # component: resolve + install\n nimbus-docs add 404-page # feature: detect agent or print\n # pipe instructions for humans\n nimbus-docs add 404-page --print | claude # explicit pipe to claude\n nimbus-docs add 404-page --print | codex # …or any other agent\n`;\n\nasync function main(): Promise<void> {\n const args = mri(process.argv.slice(2), {\n boolean: [\"yes\", \"print\", \"help\", \"version\"],\n string: [\"type\"],\n alias: { y: \"yes\", h: \"help\", v: \"version\" },\n }) as unknown as CliArgs;\n\n if (args.help) {\n process.stdout.write(HELP);\n return;\n }\n if (args.version) {\n process.stdout.write(`${__APP_VERSION__}\\n`);\n return;\n }\n\n const [command, slug] = args._;\n\n if (command === \"list\" || (command === \"add\" && !slug) || !command) {\n listCommand(args.type);\n return;\n }\n\n if (command === \"add\") {\n await addCommand(slug!, {\n yes: args.yes,\n print: args.print,\n });\n return;\n }\n\n p.log.error(`Unknown command: \\`${command}\\`. Try \\`nimbus-docs --help\\`.`);\n process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// `nimbus-docs list`\n// ---------------------------------------------------------------------------\n\nfunction listCommand(typeFilter: string | undefined): void {\n const typeMap: Record<string, \"registry:ui\" | \"registry:lib\" | \"registry:feature\"> = {\n ui: \"registry:ui\",\n lib: \"registry:lib\",\n feature: \"registry:feature\",\n };\n\n const filter =\n typeFilter && typeFilter in typeMap\n ? { type: typeMap[typeFilter] }\n : undefined;\n\n if (typeFilter && !(typeFilter in typeMap)) {\n p.log.error(\n `Unknown --type \"${typeFilter}\". Valid: ui, lib, feature.`,\n );\n process.exit(1);\n }\n\n const items = listEntries(filter);\n if (items.length === 0) {\n p.log.info(\"No items match the filter.\");\n return;\n }\n\n // Group by type for readability.\n const grouped: Record<string, typeof items> = {\n \"registry:ui\": [],\n \"registry:lib\": [],\n \"registry:feature\": [],\n };\n for (const item of items) grouped[item.type]!.push(item);\n\n const labels: Record<string, string> = {\n \"registry:ui\": \"Components\",\n \"registry:lib\": \"Utilities\",\n \"registry:feature\": \"Features\",\n };\n const widths = items.reduce(\n (m, i) => Math.max(m, i.name.length),\n 0,\n );\n\n process.stdout.write(\"\\n\");\n for (const [type, label] of Object.entries(labels)) {\n const group = grouped[type];\n if (!group || group.length === 0) continue;\n process.stdout.write(` ${label}\\n`);\n for (const item of group) {\n process.stdout.write(\n ` ${item.name.padEnd(widths + 2)}${item.description}\\n`,\n );\n }\n process.stdout.write(\"\\n\");\n }\n process.stdout.write(\n ` Install: nimbus-docs add <name> · ${items.length} item${items.length === 1 ? \"\" : \"s\"}\\n\\n`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `nimbus-docs add <slug>`\n// ---------------------------------------------------------------------------\n\nasync function addCommand(\n slug: string,\n flags: { yes: boolean; print: boolean },\n): Promise<void> {\n const entry = getIndexEntry(slug);\n if (!entry) {\n p.log.error(\n `Unknown registry item: \\`${slug}\\`. Try \\`nimbus-docs list\\` to see what's available.`,\n );\n process.exit(1);\n }\n\n if (entry.type === \"registry:feature\") {\n await installFeature(slug, { print: flags.print });\n return;\n }\n\n // Component / utility path.\n p.intro(`nimbus-docs add ${slug}`);\n p.log.info(`${entry.title} — ${entry.description}`);\n\n const spinner = p.spinner();\n spinner.start(\"Resolving dependencies\");\n let items;\n try {\n items = await resolveComponentTree(slug);\n spinner.stop(\n `Resolved ${items.length} item${items.length === 1 ? \"\" : \"s\"}.`,\n );\n } catch (err) {\n spinner.stop(\"Failed to resolve.\");\n p.log.error((err as Error).message);\n process.exit(1);\n }\n\n if (items.length > 1) {\n p.log.message(\n \"Install order:\\n \" + items.map((i) => i.name).join(\" → \"),\n );\n }\n\n const report = await installComponents(items, {\n cwd: process.cwd(),\n yes: flags.yes,\n });\n\n const lines: string[] = [];\n if (report.written.length > 0) {\n lines.push(`✓ Wrote ${report.written.length} file${report.written.length === 1 ? \"\" : \"s\"}`);\n }\n if (report.skipped.length > 0) {\n lines.push(`↷ Skipped: ${report.skipped.join(\", \")}`);\n }\n if (report.npmDepsInstalled.length > 0) {\n lines.push(\n `+ Installed ${report.npmDepsInstalled.length} npm dep${report.npmDepsInstalled.length === 1 ? \"\" : \"s\"}: ${report.npmDepsInstalled.join(\", \")}`,\n );\n }\n\n if (lines.length === 0) {\n p.outro(\"Nothing to do.\");\n } else {\n p.outro(lines.join(\"\\n\"));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Entrypoint\n// ---------------------------------------------------------------------------\n\nmain().catch((err) => {\n p.log.error(`${(err as Error).message}`);\n process.exit(1);\n});\n\n// Tell TS BUNDLED_INDEX is used (so no `verbatimModuleSyntax` warning).\nvoid BUNDLED_INDEX;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["addCommand","addCommand"],"sources":["../../src/cli/_registry.generated.ts","../../src/cli/pm.ts","../../src/cli/component.ts","../../src/cli/dotenv.ts","../../src/cli/resolver.ts","../../src/cli/feature.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Bundled registry index — auto-generated by\n * apps/www/scripts/generate-registry.ts. Do not edit by hand.\n *\n * Re-run via: pnpm --filter @nimbus/www generate-registry\n */\n\nexport type RegistryEntryType =\n | \"registry:ui\"\n | \"registry:lib\"\n | \"registry:feature\";\n\nexport interface RegistryIndexEntry {\n name: string;\n type: RegistryEntryType;\n title: string;\n description: string;\n}\n\nexport interface BundledIndex {\n version: 1;\n items: Record<string, RegistryIndexEntry>;\n}\n\nexport const REGISTRY_BASE_URL = \"https://nimbus-docs.com/registry\";\n\nexport const BUNDLED_INDEX: BundledIndex = {\n \"version\": 1,\n \"items\": {\n \"cn\": {\n \"name\": \"cn\",\n \"type\": \"registry:lib\",\n \"title\": \"cn\",\n \"description\": \"Tailwind-aware className merger built on clsx + tailwind-merge.\"\n },\n \"accordion\": {\n \"name\": \"accordion\",\n \"type\": \"registry:ui\",\n \"title\": \"Accordion\",\n \"description\": \"Vertically stacked collapsible sections.\"\n },\n \"aside\": {\n \"name\": \"aside\",\n \"type\": \"registry:ui\",\n \"title\": \"Aside\",\n \"description\": \"Generic boxed callout. Building block for Callout, Note, Warning.\"\n },\n \"badge\": {\n \"name\": \"badge\",\n \"type\": \"registry:ui\",\n \"title\": \"Badge\",\n \"description\": \"Small status / category pill.\"\n },\n \"banner\": {\n \"name\": \"banner\",\n \"type\": \"registry:ui\",\n \"title\": \"Banner\",\n \"description\": \"Site-wide dismissible announcement bar.\"\n },\n \"breadcrumbs\": {\n \"name\": \"breadcrumbs\",\n \"type\": \"registry:ui\",\n \"title\": \"Breadcrumbs\",\n \"description\": \"Page-context navigation crumbs.\"\n },\n \"callout\": {\n \"name\": \"callout\",\n \"type\": \"registry:ui\",\n \"title\": \"Callout\",\n \"description\": \"Inline note / tip / warning / danger / info card.\"\n },\n \"card\": {\n \"name\": \"card\",\n \"type\": \"registry:ui\",\n \"title\": \"Card\",\n \"description\": \"Generic content card with optional title and footer.\"\n },\n \"card-grid\": {\n \"name\": \"card-grid\",\n \"type\": \"registry:ui\",\n \"title\": \"CardGrid\",\n \"description\": \"Responsive grid layout for cards.\"\n },\n \"code\": {\n \"name\": \"code\",\n \"type\": \"registry:ui\",\n \"title\": \"Code\",\n \"description\": \"Inline / block code wrapper. Re-exports Astro's built-in <Code> (Shiki).\"\n },\n \"code-group\": {\n \"name\": \"code-group\",\n \"type\": \"registry:ui\",\n \"title\": \"CodeGroup\",\n \"description\": \"Tabbed group of code blocks.\"\n },\n \"collapsible\": {\n \"name\": \"collapsible\",\n \"type\": \"registry:ui\",\n \"title\": \"Collapsible\",\n \"description\": \"Headless show/hide primitive — building block for Accordion and Sidebar groups.\"\n },\n \"dialog\": {\n \"name\": \"dialog\",\n \"type\": \"registry:ui\",\n \"title\": \"Dialog\",\n \"description\": \"Modal dialog with focus-trap and body-scroll lock.\"\n },\n \"embed\": {\n \"name\": \"embed\",\n \"type\": \"registry:ui\",\n \"title\": \"Embed\",\n \"description\": \"Responsive iframe / video / external content wrapper.\"\n },\n \"file-tree\": {\n \"name\": \"file-tree\",\n \"type\": \"registry:ui\",\n \"title\": \"FileTree\",\n \"description\": \"Render a directory tree as nested markup.\"\n },\n \"frame\": {\n \"name\": \"frame\",\n \"type\": \"registry:ui\",\n \"title\": \"Frame\",\n \"description\": \"Decorative outer frame for screenshots and demos.\"\n },\n \"layer-card\": {\n \"name\": \"layer-card\",\n \"type\": \"registry:ui\",\n \"title\": \"LayerCard\",\n \"description\": \"Stacked-card container with sticky header. Base for CodeGroup and PackageManagers.\"\n },\n \"link-button\": {\n \"name\": \"link-button\",\n \"type\": \"registry:ui\",\n \"title\": \"LinkButton\",\n \"description\": \"Anchor styled as a button.\"\n },\n \"link-card\": {\n \"name\": \"link-card\",\n \"type\": \"registry:ui\",\n \"title\": \"LinkCard\",\n \"description\": \"Card whose entire surface is a link.\"\n },\n \"package-managers\": {\n \"name\": \"package-managers\",\n \"type\": \"registry:ui\",\n \"title\": \"PackageManagers\",\n \"description\": \"Tabbed install command block translated across npm / pnpm / yarn / bun.\"\n },\n \"page-actions\": {\n \"name\": \"page-actions\",\n \"type\": \"registry:ui\",\n \"title\": \"PageActions\",\n \"description\": \"Inline page-header actions: copy the page as markdown, open the raw .md.\"\n },\n \"pagination\": {\n \"name\": \"pagination\",\n \"type\": \"registry:ui\",\n \"title\": \"Pagination\",\n \"description\": \"Prev / next page navigation.\"\n },\n \"popover\": {\n \"name\": \"popover\",\n \"type\": \"registry:ui\",\n \"title\": \"Popover\",\n \"description\": \"Floating panel anchored to a trigger element.\"\n },\n \"search\": {\n \"name\": \"search\",\n \"type\": \"registry:ui\",\n \"title\": \"Search\",\n \"description\": \"Command-palette search dialog with a provider seam. Defaults to Pagefind.\"\n },\n \"sidebar\": {\n \"name\": \"sidebar\",\n \"type\": \"registry:ui\",\n \"title\": \"Sidebar\",\n \"description\": \"Docs sidebar with nested groups and active-link tracking.\"\n },\n \"steps\": {\n \"name\": \"steps\",\n \"type\": \"registry:ui\",\n \"title\": \"Steps\",\n \"description\": \"Numbered ordered-list with vertical connectors.\"\n },\n \"tabs\": {\n \"name\": \"tabs\",\n \"type\": \"registry:ui\",\n \"title\": \"Tabs\",\n \"description\": \"Tabbed content panels (manual + Starlight-compatible modes).\"\n },\n \"theme-toggle\": {\n \"name\": \"theme-toggle\",\n \"type\": \"registry:ui\",\n \"title\": \"ThemeToggle\",\n \"description\": \"Light / dark theme switcher button.\"\n },\n \"toc\": {\n \"name\": \"toc\",\n \"type\": \"registry:ui\",\n \"title\": \"TOC\",\n \"description\": \"On-page table of contents with active-heading tracking.\"\n },\n \"version-switcher\": {\n \"name\": \"version-switcher\",\n \"type\": \"registry:ui\",\n \"title\": \"VersionPicker\",\n \"description\": \"Header dropdown for switching between docs versions. Reads `versions` from nimbus.config.ts, uses the build-time alternates table to land readers on the same logical page in the target version. Includes deprecation badge and hidden-version exclusion. Renders nothing when versioning is off or only one version is configured.\"\n },\n \"404-page\": {\n \"name\": \"404-page\",\n \"type\": \"registry:feature\",\n \"title\": \"Custom 404 page\",\n \"description\": \"Generate a brand-matched 404 page for the docs site.\"\n },\n \"ai-native\": {\n \"name\": \"ai-native\",\n \"type\": \"registry:feature\",\n \"title\": \"AI-native static surface\",\n \"description\": \"Add llms.txt, markdown variants, robots.txt, and an AgentDirective to a Nimbus docs site.\"\n },\n \"component-showcase\": {\n \"name\": \"component-showcase\",\n \"type\": \"registry:feature\",\n \"title\": \"Component showcase\",\n \"description\": \"Add a /components grid landing plus per-component showcase pages at /components/<slug>, driven by a dedicated content collection. For sites documenting their own UI library.\"\n },\n \"new-collection\": {\n \"name\": \"new-collection\",\n \"type\": \"registry:feature\",\n \"title\": \"Add a new collection\",\n \"description\": \"End-to-end setup for a non-version content tree on a Nimbus docs site — blog, API reference, changelog, glossary. Creates the folder, registers the collection, scaffolds routes. For docs versions, use `nimbus-docs add new-version`.\"\n },\n \"new-version\": {\n \"name\": \"new-version\",\n \"type\": \"registry:feature\",\n \"title\": \"Add a docs version\",\n \"description\": \"End-to-end setup for adding a docs version to a Nimbus site. Creates the content directory, registers the collection, scaffolds routes, declares the manifest, installs the version-switcher picker, and wires it into your Header and DocsLayout. One command for one mental task.\"\n },\n \"pagefind-search\": {\n \"name\": \"pagefind-search\",\n \"type\": \"registry:feature\",\n \"title\": \"Pagefind search\",\n \"description\": \"Add static Pagefind indexing and the Nimbus search dialog to an existing docs site.\"\n }\n }\n};\n","/**\n * Package-manager detection + install command helpers.\n *\n * Detection prefers lockfile presence in the user's cwd, then falls back\n * to the `npm_config_user_agent` env var the active package manager sets\n * when invoking the CLI. Finally falls back to `npm`.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nconst LOCKFILES: ReadonlyArray<readonly [string, PackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"yarn.lock\", \"yarn\"],\n [\"bun.lockb\", \"bun\"],\n [\"bun.lock\", \"bun\"],\n [\"package-lock.json\", \"npm\"],\n];\n\nexport function detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of LOCKFILES) {\n if (existsSync(join(cwd, lockfile))) return pm;\n }\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.startsWith(\"pnpm\")) return \"pnpm\";\n if (ua.startsWith(\"yarn\")) return \"yarn\";\n if (ua.startsWith(\"bun\")) return \"bun\";\n return \"npm\";\n}\n\n/**\n * Command + args to install one or more new npm deps. Each PM picks the\n * verb that both adds to package.json AND installs:\n *\n * npm install <deps...>\n * pnpm add <deps...>\n * yarn add <deps...>\n * bun add <deps...>\n */\nexport function addCommand(\n pm: PackageManager,\n deps: string[],\n): { bin: string; args: string[] } {\n if (deps.length === 0) {\n throw new Error(\"addCommand called with empty deps\");\n }\n switch (pm) {\n case \"npm\":\n return { bin: \"npm\", args: [\"install\", ...deps] };\n case \"pnpm\":\n return { bin: \"pnpm\", args: [\"add\", ...deps] };\n case \"yarn\":\n return { bin: \"yarn\", args: [\"add\", ...deps] };\n case \"bun\":\n return { bin: \"bun\", args: [\"add\", ...deps] };\n }\n}\n","/**\n * Component / utility installer.\n *\n * Walks the resolved list of items, writes each file (per-file overwrite\n * prompt on conflict), then collects all npm `dependencies` across the\n * tree and runs `<pm> add` once for the dedup'd set.\n *\n * File destination: `<cwd>/src/<path>`. The `path` field already encodes\n * the directory layout (e.g. `components/ui/dialog/Dialog.astro`).\n */\n\nimport { spawn } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nimport * as p from \"@clack/prompts\";\n\nimport { addCommand, detectPackageManager } from \"./pm.js\";\nimport type { ComponentItem } from \"./resolver.js\";\n\nexport interface InstallOptions {\n /** User's project root. */\n cwd: string;\n /** Skip overwrite prompts; assume \"overwrite\" on every conflict. */\n yes: boolean;\n}\n\nexport interface InstallReport {\n /** Individual files actually written. */\n written: string[];\n /** Registry slugs skipped wholesale by the user. */\n skipped: string[];\n npmDepsInstalled: string[];\n}\n\nexport async function installComponents(\n items: ComponentItem[],\n options: InstallOptions,\n): Promise<InstallReport> {\n const report: InstallReport = {\n written: [],\n skipped: [],\n npmDepsInstalled: [],\n };\n\n // ---- 1. Write files — atomic per registry item -------------------------\n //\n // Each item (e.g. `dialog`) is treated as an indivisible unit: when any\n // of its files conflict, we prompt once for the whole slug. Letting users\n // overwrite Dialog.astro while keeping DialogContent.astro is a footgun\n // — components are cohesive and meant to evolve together.\n const srcDir = join(options.cwd, \"src\");\n\n for (const item of items) {\n const filePlans = item.files.map((file) => {\n const targetAbs = join(srcDir, file.path);\n return {\n targetAbs,\n targetRel: relative(options.cwd, targetAbs),\n content: file.content,\n exists: existsSync(targetAbs),\n };\n });\n\n const conflicts = filePlans.filter((f) => f.exists);\n\n // Utilities (registry:lib) are transitive dependencies of UI\n // components — install silently when missing, skip silently when\n // present. Never prompt or overwrite: users may have customized\n // them (e.g. cn) and being asked about `cn` every time you `add` a\n // component is noise.\n if (item.type === \"registry:lib\") {\n if (conflicts.length > 0) {\n report.skipped.push(item.name);\n continue;\n }\n } else if (conflicts.length > 0 && !options.yes) {\n const total = filePlans.length;\n const message =\n conflicts.length === total\n ? `${item.name} is already installed (${total} file${total === 1 ? \"\" : \"s\"}). Overwrite?`\n : `${item.name} is partially installed (${conflicts.length} of ${total} file${total === 1 ? \"\" : \"s\"} present). Overwrite all?`;\n\n const choice = await p.select({\n message,\n options: [\n { value: \"overwrite\", label: \"Overwrite — replace existing files\" },\n { value: \"skip\", label: \"Skip — leave files as-is\" },\n { value: \"cancel\", label: \"Cancel install\" },\n ],\n initialValue: \"overwrite\",\n });\n\n if (p.isCancel(choice) || choice === \"cancel\") {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n }\n if (choice === \"skip\") {\n report.skipped.push(item.name);\n continue;\n }\n }\n\n // Either no conflicts, --yes, or user chose overwrite. Write every file.\n for (const plan of filePlans) {\n mkdirSync(dirname(plan.targetAbs), { recursive: true });\n writeFileSync(plan.targetAbs, plan.content);\n report.written.push(plan.targetRel);\n }\n }\n\n // ---- 2. Install missing npm deps ---------------------------------------\n const allDeps = new Set<string>();\n for (const item of items) {\n for (const dep of item.dependencies) allDeps.add(dep);\n }\n\n if (allDeps.size > 0) {\n const newDeps = filterAlreadyInstalled(options.cwd, [...allDeps]);\n if (newDeps.length > 0) {\n const pm = detectPackageManager(options.cwd);\n const { bin, args } = addCommand(pm, newDeps);\n const spinner = p.spinner();\n spinner.start(`${pm} add ${newDeps.join(\" \")}`);\n try {\n await runCommand(bin, args, options.cwd);\n spinner.stop(\n `Installed ${newDeps.length} dep${newDeps.length === 1 ? \"\" : \"s\"}.`,\n );\n report.npmDepsInstalled = newDeps;\n } catch (err) {\n spinner.stop(\"Dependency install failed.\");\n p.log.warn(\n `Could not install ${newDeps.join(\", \")}. Run \\`${bin} ${args.join(\" \")}\\` manually.`,\n );\n }\n }\n }\n\n return report;\n}\n\n/**\n * Filter out deps already present in `dependencies` or `devDependencies`\n * of the user's package.json. If package.json is missing, returns all.\n */\nfunction filterAlreadyInstalled(cwd: string, deps: string[]): string[] {\n const pkgPath = join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return deps;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const installed = new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n return deps.filter((d) => !installed.has(d));\n } catch {\n return deps;\n }\n}\n\nfunction runCommand(\n bin: string,\n args: string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolveP, rejectP) => {\n const child = spawn(bin, args, {\n cwd,\n stdio: [\"ignore\", \"ignore\", \"inherit\"],\n });\n child.on(\"close\", (code) =>\n code === 0\n ? resolveP()\n : rejectP(new Error(`${bin} ${args.join(\" \")} exited ${code}`)),\n );\n child.on(\"error\", rejectP);\n });\n}\n","/**\n * Tiny .env loader — no dependency.\n *\n * Reads `.env` from the user's cwd at CLI startup and sets any KEY=VALUE\n * pairs into `process.env` IF the variable isn't already set (so a shell-\n * provided env always wins over the file). Supports the basic cases:\n *\n * KEY=value\n * KEY=\"quoted value\"\n * KEY='quoted value'\n * # comments\n *\n * Used so `examples/local/.env` can carry `NIMBUS_REGISTRY_URL=...` without\n * the user having to prefix every CLI invocation.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function loadDotenv(cwd: string): void {\n const path = join(cwd, \".env\");\n if (!existsSync(path)) return;\n\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return;\n }\n\n for (const rawLine of raw.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n\n const key = line.slice(0, eq).trim();\n if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) continue;\n\n let value = line.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n","/**\n * Registry resolver.\n *\n * Two entry points:\n *\n * - `resolveComponentTree(slug)` walks `registryDependencies` transitively\n * and returns a flat ordered list of components/utilities to install\n * (dependencies first, root last). Cycles are detected as repeated\n * visits and skipped.\n *\n * - `fetchFeatureMarkdown(slug)` returns the raw markdown for an\n * agent-handoff feature; the caller decides what to do with it.\n *\n * The base URL for hosted artifacts is read from the bundled index, with\n * an `NIMBUS_REGISTRY_URL` env override for local development.\n */\n\nimport {\n BUNDLED_INDEX,\n REGISTRY_BASE_URL,\n type RegistryIndexEntry,\n} from \"./_registry.generated.js\";\n\nexport interface RegistryFile {\n path: string;\n content: string;\n}\n\nexport interface ComponentItem {\n name: string;\n type: \"registry:ui\" | \"registry:lib\";\n title: string;\n description: string;\n dependencies: string[];\n registryDependencies: string[];\n files: RegistryFile[];\n}\n\n/**\n * Read the registry base URL on every call so `.env` files loaded after\n * module-import time (see cli/dotenv.ts) are picked up. The cost is\n * negligible — string interpolation of an env var.\n */\nfunction getBaseUrl(): string {\n return (process.env.NIMBUS_REGISTRY_URL ?? REGISTRY_BASE_URL).replace(\n /\\/$/,\n \"\",\n );\n}\n\n// ---------------------------------------------------------------------------\n// Index lookup (offline — no network)\n// ---------------------------------------------------------------------------\n\nexport function getIndexEntry(slug: string): RegistryIndexEntry | undefined {\n return BUNDLED_INDEX.items[slug];\n}\n\nexport function listEntries(filter?: {\n type?: RegistryIndexEntry[\"type\"];\n}): RegistryIndexEntry[] {\n const all = Object.values(BUNDLED_INDEX.items);\n if (!filter?.type) return all;\n return all.filter((e) => e.type === filter.type);\n}\n\n// ---------------------------------------------------------------------------\n// Network: component JSON + feature markdown\n// ---------------------------------------------------------------------------\n\nasync function httpGet(url: string): Promise<Response> {\n let res: Response;\n try {\n res = await fetch(url);\n } catch (err) {\n const cause = (err as Error).message;\n throw new Error(\n `Could not reach the registry at ${url}.\\n` +\n ` Underlying error: ${cause}\\n\\n` +\n ` Things to try:\\n` +\n ` - Is the registry server running? Start it with \\`pnpm local\\` (in the monorepo root).\\n` +\n ` - Override the URL: NIMBUS_REGISTRY_URL=https://example.com nimbus-docs add ...\\n` +\n ` - Check the value in your project's .env file.`,\n );\n }\n if (!res.ok) {\n throw new Error(\n `Registry returned ${res.status} ${res.statusText} for ${url}. ` +\n `The server is up but doesn't know about this slug — check \\`nimbus-docs list\\` for valid names.`,\n );\n }\n return res;\n}\n\nexport async function fetchComponent(slug: string): Promise<ComponentItem> {\n const res = await httpGet(`${getBaseUrl()}/components/${slug}.json`);\n return (await res.json()) as ComponentItem;\n}\n\nexport async function fetchFeatureMarkdown(slug: string): Promise<string> {\n const res = await httpGet(`${getBaseUrl()}/features/${slug}.md`);\n return await res.text();\n}\n\n// ---------------------------------------------------------------------------\n// Transitive dep resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Depth-first walk of registryDependencies. Returns items in install order\n * (deps before dependents), deduplicated by slug.\n */\nexport async function resolveComponentTree(\n rootSlug: string,\n): Promise<ComponentItem[]> {\n const visited = new Set<string>();\n const ordered: ComponentItem[] = [];\n\n async function visit(slug: string): Promise<void> {\n if (visited.has(slug)) return;\n visited.add(slug);\n\n const item = await fetchComponent(slug);\n\n // Walk deps first so they're earlier in the install order.\n for (const dep of item.registryDependencies) {\n await visit(dep);\n }\n\n ordered.push(item);\n }\n\n await visit(rootSlug);\n return ordered;\n}\n","/**\n * Feature installer — Flue-style agent-handoff.\n *\n * Same rule Flue uses for `flue add`: if `--print` is set OR\n * `determineAgent()` says the CLI is running inside a known coding agent,\n * the markdown is piped to stdout for the agent to consume. Otherwise we\n * print human-friendly instructions on stderr telling the user exactly\n * how to pipe the output to their agent of choice.\n *\n * No picker, no clipboard mode — the printed pipe commands cover both.\n */\n\nimport { determineAgent } from \"@vercel/detect-agent\";\n\nimport { fetchFeatureMarkdown } from \"./resolver.js\";\n\nexport interface FeatureInstallOptions {\n /** Force markdown to stdout regardless of agent detection. */\n print: boolean;\n}\n\nexport async function installFeature(\n slug: string,\n options: FeatureInstallOptions,\n): Promise<void> {\n const markdown = await fetchFeatureMarkdown(slug);\n\n // Same predicate as Flue: explicit --print, or detection says we're\n // running inside a known agent (which captures our stdout).\n const detected = await determineAgent().catch(() => ({\n isAgent: false as const,\n }));\n const isAgentMode = options.print || detected.isAgent === true;\n\n if (isAgentMode) {\n process.stdout.write(markdown);\n if (!markdown.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n return;\n }\n\n printHumanInstructions(slug);\n}\n\n/**\n * Stderr-only. We don't put this on stdout because if the user pipes our\n * output anywhere by accident, only the markdown should reach the agent.\n *\n * Formatting mirrors Flue's `printHumanInstructions` 1:1 — agents listed\n * with a blank line between the \"first-tier\" CLIs (claude/codex/cursor-agent)\n * and the rest (opencode/pi).\n */\nfunction printHumanInstructions(slug: string): void {\n const cmd = `nimbus-docs add ${slug}`;\n const stream = process.stderr;\n stream.write(`${cmd}\\n\\n`);\n stream.write(\"To install this feature, pipe it to your coding agent:\\n\\n\");\n stream.write(` ${cmd} --print | claude\\n`);\n stream.write(` ${cmd} --print | codex\\n`);\n stream.write(` ${cmd} --print | cursor-agent\\n\\n`);\n stream.write(` ${cmd} --print | opencode\\n`);\n stream.write(` ${cmd} --print | pi\\n`);\n stream.write(\"Or paste this prompt into any agent:\\n\\n\");\n stream.write(` Run \"${cmd} --print\" and follow the instructions.\\n`);\n}\n","#!/usr/bin/env node\n\n/**\n * `nimbus-docs` CLI entry.\n *\n * Surface:\n *\n * nimbus → list (table of installable items)\n * nimbus-docs list → list\n * nimbus-docs list --type ui|lib|feature\n * nimbus-docs add → list\n * nimbus-docs add <slug> → install (component path or feature path)\n * nimbus-docs add <slug> --yes → component: skip overwrite prompts\n * nimbus-docs add <slug> --print → feature: print markdown to stdout (skip detect)\n *\n * Feature behavior mirrors Flue's `add` command: print markdown to stdout\n * iff `--print` OR an agent is detected; otherwise print human-friendly\n * pipe instructions to stderr.\n *\n * The bundled index makes `list` (and `add` with no slug) work offline.\n * Per-item content is fetched from `REGISTRY_BASE_URL` only when actually\n * installing a slug — override via `NIMBUS_REGISTRY_URL` for local dev.\n */\n\nimport mri from \"mri\";\nimport * as p from \"@clack/prompts\";\n\nimport { BUNDLED_INDEX } from \"./_registry.generated.js\";\nimport { installComponents } from \"./component.js\";\nimport { loadDotenv } from \"./dotenv.js\";\nimport { installFeature } from \"./feature.js\";\nimport {\n getIndexEntry,\n listEntries,\n resolveComponentTree,\n} from \"./resolver.js\";\n\n// Load .env from the user's cwd so per-project NIMBUS_REGISTRY_URL (and\n// any future env vars) work without shell prefixes. Shell-provided vars\n// always win (loadDotenv only sets undefined keys).\nloadDotenv(process.cwd());\n\ndeclare const __APP_VERSION__: string;\n\ninterface CliArgs {\n _: string[];\n yes: boolean;\n print: boolean;\n help: boolean;\n version: boolean;\n type?: string;\n}\n\nconst HELP = `\n Usage: nimbus-docs <command> [args]\n\n Commands:\n list [--type ui|lib|feature] List available registry items\n add Same as \\`list\\`\n add <slug> Install a component or hand off a feature\n\n Flags:\n --yes, -y Component: overwrite conflicts without prompting\n --print Feature: print markdown to stdout (skip agent detect)\n --type <ui|lib|feature> \\`list\\`: filter by type\n --help, -h\n --version, -v\n\n Examples:\n nimbus-docs add dialog # component: resolve + install\n nimbus-docs add 404-page # feature: detect agent or print\n # pipe instructions for humans\n nimbus-docs add 404-page --print | claude # explicit pipe to claude\n nimbus-docs add 404-page --print | codex # …or any other agent\n`;\n\nasync function main(): Promise<void> {\n const args = mri(process.argv.slice(2), {\n boolean: [\"yes\", \"print\", \"help\", \"version\"],\n string: [\"type\"],\n alias: { y: \"yes\", h: \"help\", v: \"version\" },\n }) as unknown as CliArgs;\n\n if (args.help) {\n process.stdout.write(HELP);\n return;\n }\n if (args.version) {\n process.stdout.write(`${__APP_VERSION__}\\n`);\n return;\n }\n\n const [command, slug] = args._;\n\n if (command === \"list\" || (command === \"add\" && !slug) || !command) {\n listCommand(args.type);\n return;\n }\n\n if (command === \"add\") {\n await addCommand(slug!, {\n yes: args.yes,\n print: args.print,\n });\n return;\n }\n\n p.log.error(`Unknown command: \\`${command}\\`. Try \\`nimbus-docs --help\\`.`);\n process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// `nimbus-docs list`\n// ---------------------------------------------------------------------------\n\nfunction listCommand(typeFilter: string | undefined): void {\n const typeMap: Record<string, \"registry:ui\" | \"registry:lib\" | \"registry:feature\"> = {\n ui: \"registry:ui\",\n lib: \"registry:lib\",\n feature: \"registry:feature\",\n };\n\n const filter =\n typeFilter && typeFilter in typeMap\n ? { type: typeMap[typeFilter] }\n : undefined;\n\n if (typeFilter && !(typeFilter in typeMap)) {\n p.log.error(\n `Unknown --type \"${typeFilter}\". Valid: ui, lib, feature.`,\n );\n process.exit(1);\n }\n\n const items = listEntries(filter);\n if (items.length === 0) {\n p.log.info(\"No items match the filter.\");\n return;\n }\n\n // Group by type for readability.\n const grouped: Record<string, typeof items> = {\n \"registry:ui\": [],\n \"registry:lib\": [],\n \"registry:feature\": [],\n };\n for (const item of items) grouped[item.type]!.push(item);\n\n const labels: Record<string, string> = {\n \"registry:ui\": \"Components\",\n \"registry:lib\": \"Utilities\",\n \"registry:feature\": \"Features\",\n };\n const widths = items.reduce(\n (m, i) => Math.max(m, i.name.length),\n 0,\n );\n\n process.stdout.write(\"\\n\");\n for (const [type, label] of Object.entries(labels)) {\n const group = grouped[type];\n if (!group || group.length === 0) continue;\n process.stdout.write(` ${label}\\n`);\n for (const item of group) {\n process.stdout.write(\n ` ${item.name.padEnd(widths + 2)}${item.description}\\n`,\n );\n }\n process.stdout.write(\"\\n\");\n }\n process.stdout.write(\n ` Install: nimbus-docs add <name> · ${items.length} item${items.length === 1 ? \"\" : \"s\"}\\n\\n`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `nimbus-docs add <slug>`\n// ---------------------------------------------------------------------------\n\nasync function addCommand(\n slug: string,\n flags: { yes: boolean; print: boolean },\n): Promise<void> {\n const entry = getIndexEntry(slug);\n if (!entry) {\n p.log.error(\n `Unknown registry item: \\`${slug}\\`. Try \\`nimbus-docs list\\` to see what's available.`,\n );\n process.exit(1);\n }\n\n if (entry.type === \"registry:feature\") {\n await installFeature(slug, { print: flags.print });\n return;\n }\n\n // Component / utility path.\n p.intro(`nimbus-docs add ${slug}`);\n p.log.info(`${entry.title} — ${entry.description}`);\n\n const spinner = p.spinner();\n spinner.start(\"Resolving dependencies\");\n let items;\n try {\n items = await resolveComponentTree(slug);\n spinner.stop(\n `Resolved ${items.length} item${items.length === 1 ? \"\" : \"s\"}.`,\n );\n } catch (err) {\n spinner.stop(\"Failed to resolve.\");\n p.log.error((err as Error).message);\n process.exit(1);\n }\n\n if (items.length > 1) {\n p.log.message(\n \"Install order:\\n \" + items.map((i) => i.name).join(\" → \"),\n );\n }\n\n const report = await installComponents(items, {\n cwd: process.cwd(),\n yes: flags.yes,\n });\n\n const lines: string[] = [];\n if (report.written.length > 0) {\n lines.push(`✓ Wrote ${report.written.length} file${report.written.length === 1 ? \"\" : \"s\"}`);\n }\n if (report.skipped.length > 0) {\n lines.push(`↷ Skipped: ${report.skipped.join(\", \")}`);\n }\n if (report.npmDepsInstalled.length > 0) {\n lines.push(\n `+ Installed ${report.npmDepsInstalled.length} npm dep${report.npmDepsInstalled.length === 1 ? \"\" : \"s\"}: ${report.npmDepsInstalled.join(\", \")}`,\n );\n }\n\n if (lines.length === 0) {\n p.outro(\"Nothing to do.\");\n } else {\n p.outro(lines.join(\"\\n\"));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Entrypoint\n// ---------------------------------------------------------------------------\n\nmain().catch((err) => {\n p.log.error(`${(err as Error).message}`);\n process.exit(1);\n});\n\n// Tell TS BUNDLED_INDEX is used (so no `verbatimModuleSyntax` warning).\nvoid BUNDLED_INDEX;\n"],"mappings":"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"}
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","names":[],"sources":["../src/client/mount.ts","../src/client/disclosure.ts","../src/client/tabs-controller.ts","../src/client/scroll-lock.ts","../src/client/ids.ts","../src/client/dom.ts","../src/client/code-copy.ts","../src/client/heading-anchors.ts"],"mappings":";;;;;;;AA0BA;;;;;;;;;;;;ACNA;;;;;KDIK,IAAA,IAAQ,IAAA,EAAM,WAAA;AAAA,iBAEH,KAAA,CAAM,QAAA,UAAkB,IAAA,EAAM,IAAA;;;;;;;;;AAA9C;;;;;;;;;;;UCNiB,iBAAA;EAAA;EAEf,OAAA,EAAS,WAAA;;EAET,OAAA,EAAS,WAAA;EAFT;EAIA,WAAA;EAFA;EAIA,YAAA,IAAgB,IAAA;AAAA;AAAA,UAGD,kBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,iBAGc,cAAA,CAAe,IAAA,EAAM,iBAAA,GAAoB,kBAAA;;;;;;;;;ADbzD;;UEfiB,UAAA;EFeiC;EEbhD,SAAA,EAAW,OAAA;EFaiC;EEX5C,WAAA;EFWgD;EEThD,aAAA;;EAEA,SAAA,GAAY,WAAA;;EAEZ,cAAA;EDDgC;ECGhC,IAAA;IACE,GAAA;IACA,OAAA,wBDHO;ICKP,QAAA,IAAY,GAAA,EAAK,WAAA;EAAA;EDDnB;ECIA,UAAA,IAAc,KAAA;AAAA;AAAA,UAGC,YAAA;EACf,QAAA,CAAS,KAAA,UAAe,OAAA;IAAY,QAAA;EAAA;EAAA,SAC3B,YAAA;EACT,OAAA;AAAA;AAAA,iBAGc,QAAA,CAAS,MAAA,EAAQ,UAAA,GAAa,YAAA;;;;;;;;;AFb9C;;;;;;iBGRgB,UAAA,CAAA;AAAA,iBAaA,YAAA,CAAA;;;;;;;;;iBCtBA,UAAA,CAAW,MAAA;;;;;;;cCJd,SAAA;;;;;;;;;ALqBb;;;;;;;;;;;;ACNA;AAAA,
|
|
1
|
+
{"version":3,"file":"client.d.ts","names":[],"sources":["../src/client/mount.ts","../src/client/disclosure.ts","../src/client/tabs-controller.ts","../src/client/scroll-lock.ts","../src/client/ids.ts","../src/client/dom.ts","../src/client/code-copy.ts","../src/client/heading-anchors.ts"],"mappings":";;;;;;;AA0BA;;;;;;;;;;;;ACNA;;;;;KDIK,IAAA,IAAQ,IAAA,EAAM,WAAA;AAAA,iBAEH,KAAA,CAAM,QAAA,UAAkB,IAAA,EAAM,IAAA;;;;;;;;;AAA9C;;;;;;;;;;;UCNiB,iBAAA;EAAA;EAEf,OAAA,EAAS,WAAA;;EAET,OAAA,EAAS,WAAA;EAFT;EAIA,WAAA;EAFA;EAIA,YAAA,IAAgB,IAAA;AAAA;AAAA,UAGD,kBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,iBAGc,cAAA,CAAe,IAAA,EAAM,iBAAA,GAAoB,kBAAA;;;;;;;;;ADbzD;;UEfiB,UAAA;EFeiC;EEbhD,SAAA,EAAW,OAAA;EFaiC;EEX5C,WAAA;EFWgD;EEThD,aAAA;;EAEA,SAAA,GAAY,WAAA;;EAEZ,cAAA;EDDgC;ECGhC,IAAA;IACE,GAAA;IACA,OAAA,wBDHO;ICKP,QAAA,IAAY,GAAA,EAAK,WAAA;EAAA;EDDnB;ECIA,UAAA,IAAc,KAAA;AAAA;AAAA,UAGC,YAAA;EACf,QAAA,CAAS,KAAA,UAAe,OAAA;IAAY,QAAA;EAAA;EAAA,SAC3B,YAAA;EACT,OAAA;AAAA;AAAA,iBAGc,QAAA,CAAS,MAAA,EAAQ,UAAA,GAAa,YAAA;;;;;;;;;AFb9C;;;;;;iBGRgB,UAAA,CAAA;AAAA,iBAaA,YAAA,CAAA;;;;;;;;;iBCtBA,UAAA,CAAW,MAAA;;;;;;;cCJd,SAAA;;;;;;;;;ALqBb;;;;;;;;;;;;ACNA;AAAA,iBKkEgB,QAAA,CAAA;;;;;;;;;iBCjEA,cAAA,CAAA"}
|
package/dist/client.js
CHANGED
|
@@ -298,7 +298,8 @@ function getCodeText(pre) {
|
|
|
298
298
|
return pre.querySelector("code")?.textContent ?? pre.textContent ?? "";
|
|
299
299
|
}
|
|
300
300
|
function initCodeCopy(pre) {
|
|
301
|
-
if (pre.closest("[data-cg-row]")) return () => {};
|
|
301
|
+
if (pre.closest("[data-cg-panels-raw], [data-cg-row]")) return () => {};
|
|
302
|
+
const host = pre.closest(".nb-code-figure") ?? pre;
|
|
302
303
|
const btn = document.createElement("button");
|
|
303
304
|
btn.type = "button";
|
|
304
305
|
btn.className = "nb-code-copy";
|
|
@@ -324,8 +325,8 @@ function initCodeCopy(pre) {
|
|
|
324
325
|
}, 1500);
|
|
325
326
|
}
|
|
326
327
|
btn.addEventListener("click", handleClick);
|
|
327
|
-
if (getComputedStyle(
|
|
328
|
-
|
|
328
|
+
if (getComputedStyle(host).position === "static") host.style.position = "relative";
|
|
329
|
+
host.appendChild(btn);
|
|
329
330
|
return () => {
|
|
330
331
|
if (resetTimer) window.clearTimeout(resetTimer);
|
|
331
332
|
btn.removeEventListener("click", handleClick);
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../src/client/mount.ts","../src/client/ids.ts","../src/client/disclosure.ts","../src/client/dom.ts","../src/client/tabs-controller.ts","../src/client/scroll-lock.ts","../src/client/code-copy.ts","../src/client/heading-anchors.ts"],"sourcesContent":["/**\n * mount.ts — Discover, mount, and unmount component instances.\n *\n * The single entry point used by every `*.client.ts` to wire its component.\n * Handles three concerns:\n *\n * 1. Initial discovery — finds elements matching `selector` and calls `init`\n * on each, storing the returned teardown.\n * 2. View transitions — on `astro:before-swap`, runs every teardown so\n * document/window listeners come down before the DOM is replaced.\n * 3. Re-mount — on `astro:page-load`, re-runs discovery against the new DOM.\n *\n * The init function receives the root element and returns a `destroy()`\n * function. The root element is the keying mechanism — calling mount again\n * against an already-tracked element is a no-op.\n *\n * Usage:\n *\n * mount(\"[data-nb-collapsible]\", (root) => {\n * const disclosure = makeDisclosure({ ... });\n * return () => disclosure.destroy();\n * });\n */\n\ntype Init = (root: HTMLElement) => () => void;\n\nexport function mount(selector: string, init: Init): void {\n const instances = new Map<HTMLElement, () => void>();\n\n function setup() {\n document.querySelectorAll<HTMLElement>(selector).forEach((el) => {\n if (instances.has(el)) return;\n instances.set(el, init(el));\n });\n }\n\n function teardown() {\n instances.forEach((destroy) => destroy());\n instances.clear();\n }\n\n // Module scripts are deferred, so DOM is parsed by the time this runs.\n // Belt-and-braces check anyway.\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setup, { once: true });\n } else {\n setup();\n }\n\n document.addEventListener(\"astro:before-swap\", teardown);\n document.addEventListener(\"astro:page-load\", setup);\n}\n","/**\n * Generate a unique ID with a prefix, scoped to the page.\n *\n * Used to build ARIA relationships (`aria-controls` / `aria-labelledby`)\n * between elements that don't have a stable author-provided id.\n */\n\nlet counter = 0;\n\nexport function generateId(prefix: string): string {\n counter += 1;\n return `${prefix}-${counter}`;\n}\n","/**\n * disclosure.ts — Open/close state with ARIA wiring.\n *\n * The shared module for any \"click trigger, reveals content\" pattern.\n * Owns:\n * - open/closed state (in-memory + reflected as `data-nb-state` on both\n * trigger and content)\n * - ARIA: `aria-expanded` on trigger, `aria-controls` linking to content\n * - Click handler on the trigger\n *\n * Animation is intentionally out of scope — CSS targets the `data-nb-state`\n * attribute and runs whatever transition the component wants. Returning\n * a teardown means the caller can clean up on unmount.\n *\n * Used by: Collapsible, and any future Accordion / Sidebar group /\n * dismissable Banner that wants the standard disclosure semantics.\n */\n\nimport { generateId } from \"./ids\";\n\nexport interface DisclosureOptions {\n /** The element users click to toggle. Usually a `<button>`. */\n trigger: HTMLElement;\n /** The element that's shown/hidden. Gets `id` + `data-nb-state`. */\n content: HTMLElement;\n /** Initial open state. Default `false`. */\n defaultOpen?: boolean;\n /** Called whenever open changes. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface DisclosureInstance {\n open(): void;\n close(): void;\n toggle(): void;\n isOpen(): boolean;\n destroy(): void;\n}\n\nexport function makeDisclosure(opts: DisclosureOptions): DisclosureInstance {\n const { trigger, content, defaultOpen = false, onOpenChange } = opts;\n\n let open = defaultOpen;\n\n // Ensure ARIA relationship exists.\n if (!content.id) {\n content.id = generateId(\"nb-disclosure\");\n }\n trigger.setAttribute(\"aria-controls\", content.id);\n\n function syncState() {\n const state = open ? \"open\" : \"closed\";\n trigger.setAttribute(\"data-nb-state\", state);\n content.setAttribute(\"data-nb-state\", state);\n trigger.setAttribute(\"aria-expanded\", String(open));\n }\n\n function setOpen(value: boolean) {\n if (open === value) return;\n open = value;\n syncState();\n onOpenChange?.(value);\n }\n\n function handleClick(e: MouseEvent) {\n e.preventDefault();\n setOpen(!open);\n }\n\n syncState();\n trigger.addEventListener(\"click\", handleClick);\n\n return {\n open() {\n setOpen(true);\n },\n close() {\n setOpen(false);\n },\n toggle() {\n setOpen(!open);\n },\n isOpen() {\n return open;\n },\n destroy() {\n trigger.removeEventListener(\"click\", handleClick);\n },\n };\n}\n","/**\n * Shared DOM constants for client-side interaction primitives.\n */\n\n/** CSS selector for focusable elements within a container. */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([disabled]):not([tabindex=\"-1\"])';\n","/**\n * Shared tab activation primitive.\n *\n * Handles aria-selected, panel visibility, roving tabindex,\n * keyboard navigation, sliding indicator, and cross-instance sync.\n *\n * Used by: Tabs.astro, PackageManagers.astro\n */\n\nimport { FOCUSABLE } from \"./dom\";\n\nexport interface TabsConfig {\n /** Root container holding tabs + panels */\n container: Element;\n /** CSS selector for tab trigger buttons */\n tabSelector: string;\n /** CSS selector for tab panels */\n panelSelector: string;\n /** Optional sliding indicator element */\n indicator?: HTMLElement | null;\n /** Enable roving tabindex + arrow key navigation (default: true) */\n rovingTabindex?: boolean;\n /** Cross-instance persistence config */\n sync?: {\n key: string;\n storage?: \"local\" | \"session\";\n /** Extract sync label from a tab element. Default: textContent.trim() */\n getLabel?: (tab: HTMLElement) => string;\n };\n /** Called after a tab is activated */\n onActivate?: (index: number) => void;\n}\n\nexport interface TabsInstance {\n activate(index: number, options?: { emitSync?: boolean }): void;\n readonly currentIndex: number;\n destroy(): void;\n}\n\nexport function initTabs(config: TabsConfig): TabsInstance {\n const { container, tabSelector, panelSelector, indicator = null, rovingTabindex = true, sync, onActivate } = config;\n\n const tabs = Array.from(container.querySelectorAll<HTMLElement>(tabSelector));\n const panels = Array.from(container.querySelectorAll<HTMLElement>(panelSelector));\n const tablist = container.querySelector(\"[role=tablist]\") ?? container;\n\n let currentIndex = -1;\n let isInitialActivation = true;\n\n function getStorage(kind: \"local\" | \"session\"): Storage | null {\n try {\n return kind === \"session\" ? sessionStorage : localStorage;\n } catch {\n return null;\n }\n }\n\n function getLabel(tab: HTMLElement): string {\n return sync?.getLabel?.(tab) ?? tab.textContent?.trim() ?? \"\";\n }\n\n function updateIndicator(index: number) {\n if (!indicator || !tabs[index]) return;\n indicator.style.left = `${tabs[index].offsetLeft}px`;\n indicator.style.width = `${tabs[index].offsetWidth}px`;\n }\n\n function activate(index: number, options?: { emitSync?: boolean }) {\n const emitSync = options?.emitSync ?? true;\n if (index === currentIndex) return;\n\n // Capture scroll position before DOM changes to prevent layout jump\n const rect = container.getBoundingClientRect();\n const scrollBefore = rect.top;\n\n currentIndex = index;\n\n tabs.forEach((tab, i) => {\n const active = i === index;\n tab.setAttribute(\"aria-selected\", String(active));\n if (rovingTabindex) {\n tab.setAttribute(\"tabindex\", active ? \"0\" : \"-1\");\n }\n });\n\n panels.forEach((panel, i) => {\n const visible = i === index;\n panel.hidden = !visible;\n // Panels with no focusable children need tabindex=\"0\" so keyboard\n // users can Tab into the content (WAI-ARIA Tabs pattern).\n if (visible) {\n const hasFocusable = panel.querySelector(FOCUSABLE) !== null;\n if (!hasFocusable) {\n panel.setAttribute(\"tabindex\", \"0\");\n } else {\n panel.removeAttribute(\"tabindex\");\n }\n }\n });\n\n updateIndicator(index);\n onActivate?.(index);\n\n // Compensate scroll position after panel height change (skip on first paint)\n if (emitSync && !isInitialActivation) {\n const scrollAfter = container.getBoundingClientRect().top;\n const delta = scrollAfter - scrollBefore;\n if (Math.abs(delta) > 1) {\n window.scrollTo({\n top: window.scrollY + delta,\n behavior: \"instant\",\n });\n }\n }\n isInitialActivation = false;\n\n if (sync && emitSync) {\n const label = getLabel(tabs[index]);\n const store = getStorage(sync.storage === \"session\" ? \"session\" : \"local\");\n store?.setItem(sync.key, label);\n window.dispatchEvent(\n new CustomEvent(\"ui-tab-sync\", {\n detail: { key: sync.key, label, origin: container },\n }),\n );\n }\n }\n\n // Resolve initial index from sync storage\n let initialIndex = 0;\n if (sync) {\n const store = getStorage(sync.storage === \"session\" ? \"session\" : \"local\");\n const saved = store?.getItem(sync.key);\n if (saved) {\n const idx = tabs.findIndex((t) => getLabel(t) === saved);\n if (idx >= 0) initialIndex = idx;\n }\n }\n\n // Click delegation on tablist\n function handleClick(e: Event) {\n const target = (e.target as HTMLElement).closest(tabSelector);\n if (!target) return;\n const idx = tabs.indexOf(target as HTMLElement);\n if (idx >= 0) {\n activate(idx);\n if (rovingTabindex) (target as HTMLElement).focus();\n }\n }\n\n // Keyboard navigation (roving tabindex)\n function handleKeydown(e: KeyboardEvent) {\n if (!rovingTabindex) return;\n const ci = tabs.indexOf(e.target as HTMLElement);\n if (ci < 0) return;\n\n let next: number;\n switch (e.key) {\n case \"ArrowRight\":\n next = ci + 1;\n break;\n case \"ArrowLeft\":\n next = ci - 1;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = tabs.length - 1;\n break;\n default:\n return;\n }\n // No-wrap: ignore if out of bounds\n if (!tabs[next]) return;\n e.preventDefault();\n activate(next);\n tabs[next].focus();\n }\n\n // Cross-instance sync via CustomEvent\n function handleSync(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (detail.key === sync?.key && detail.origin !== container) {\n const idx = tabs.findIndex((t) => getLabel(t) === detail.label);\n if (idx >= 0) activate(idx, { emitSync: false });\n }\n }\n\n // Wire events\n tablist.addEventListener(\"click\", handleClick);\n tablist.addEventListener(\"keydown\", handleKeydown as EventListener);\n if (sync) window.addEventListener(\"ui-tab-sync\", handleSync);\n\n // Initial activation — skip indicator transition for first paint\n if (indicator) indicator.style.transition = \"none\";\n activate(initialIndex);\n if (indicator) {\n void indicator.offsetHeight; // force reflow\n indicator.style.transition = \"\";\n }\n\n return {\n activate,\n get currentIndex() {\n return currentIndex;\n },\n destroy() {\n tablist.removeEventListener(\"click\", handleClick);\n tablist.removeEventListener(\"keydown\", handleKeydown as EventListener);\n if (sync) window.removeEventListener(\"ui-tab-sync\", handleSync);\n },\n };\n}\n","/**\n * scroll-lock.ts — Body scroll lock with scrollbar-width compensation.\n *\n * Prevents background scrolling when a modal/overlay is open.\n * Compensates for the scrollbar disappearing to avoid layout shift\n * (visible on Windows/Linux where scrollbars have width).\n *\n * Uses a data attribute + CSS for the overflow lock, and inline\n * paddingRight for the scrollbar compensation.\n *\n * Used by: Dialog (and any future overlay primitive).\n */\n\nconst ATTR = \"data-scroll-locked\";\n\nlet lockCount = 0;\nlet savedPaddingRight = \"\";\n\nexport function lockScroll(): void {\n lockCount++;\n if (lockCount > 1) return;\n\n const scrollbarW = window.innerWidth - document.documentElement.clientWidth;\n savedPaddingRight = document.body.style.paddingRight;\n\n document.body.setAttribute(ATTR, \"\");\n if (scrollbarW > 0) {\n document.body.style.paddingRight = `${scrollbarW}px`;\n }\n}\n\nexport function unlockScroll(): void {\n if (lockCount === 0) return;\n lockCount--;\n if (lockCount > 0) return;\n\n document.body.removeAttribute(ATTR);\n document.body.style.paddingRight = savedPaddingRight;\n}\n","/**\n * code-copy.ts — Injects a Nimbus-styled copy button into every Shiki\n * code block rendered by Astro's built-in `<Code>` and fenced\n * code blocks in MDX.\n *\n * Astro emits `<pre class=\"astro-code shiki ...\">` for syntax-highlighted\n * blocks. We attach a copy button positioned in the top-right corner.\n *\n * Page-wide enhancement, not tied to a single component. Call `codeCopy()`\n * once (e.g. from BaseLayout). Uses `mount` for lifecycle so the buttons\n * are torn down on view transitions and re-added against the new DOM.\n *\n * The original code text comes from the `<code>` element's textContent —\n * Shiki's wrapper spans flatten down to the raw source.\n *\n * Code blocks rendered inside [data-cg-row] (CodeGroup) are skipped\n * because CodeGroup brings its own copy button per panel.\n */\n\nimport { mount } from \"./mount\";\n\nconst COPY_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z\"/></svg>`;\nconst CHECK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34Z\"/></svg>`;\n\nfunction getCodeText(pre: HTMLElement): string {\n const codeEl = pre.querySelector<HTMLElement>(\"code\");\n return codeEl?.textContent ?? pre.textContent ?? \"\";\n}\n\nfunction initCodeCopy(pre: HTMLElement): () => void {\n // Skip code blocks owned by CodeGroup — that component renders its own\n // copy button per panel.\n if (pre.closest(\"[data-cg-row]\")) return () => {};\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"nb-code-copy\";\n btn.setAttribute(\"aria-label\", \"Copy code to clipboard\");\n btn.innerHTML = COPY_ICON;\n\n let resetTimer: number | undefined;\n\n async function handleClick() {\n const text = getCodeText(pre);\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return;\n }\n\n btn.innerHTML = CHECK_ICON;\n btn.dataset.state = \"copied\";\n btn.setAttribute(\"aria-label\", \"Copied!\");\n\n if (resetTimer) window.clearTimeout(resetTimer);\n resetTimer = window.setTimeout(() => {\n btn.innerHTML = COPY_ICON;\n delete btn.dataset.state;\n btn.setAttribute(\"aria-label\", \"Copy code to clipboard\");\n }, 1500);\n }\n\n btn.addEventListener(\"click\", handleClick);\n\n // The button positions absolutely against the <pre>; make sure <pre> is\n // a positioning context.\n if (getComputedStyle(pre).position === \"static\") {\n pre.style.position = \"relative\";\n }\n pre.appendChild(btn);\n\n return () => {\n if (resetTimer) window.clearTimeout(resetTimer);\n btn.removeEventListener(\"click\", handleClick);\n btn.remove();\n };\n}\n\n/** Wire copy buttons onto all Shiki code blocks on the page. Call once. */\nexport function codeCopy(): void {\n mount(\"pre.astro-code\", initCodeCopy);\n}\n","/** Add hoverable self-links to markdown headings with ids. */\n\nfunction applyHeadingAnchors() {\n document.querySelectorAll<HTMLElement>(\".docs-content :is(h2, h3, h4)[id]\").forEach((heading) => {\n if (heading.hasAttribute(\"data-heading-anchor-ready\")) return;\n heading.setAttribute(\"data-heading-anchor-ready\", \"true\");\n\n const link = document.createElement(\"a\");\n link.href = `#${heading.id}`;\n link.className = \"heading-anchor\";\n link.setAttribute(\"aria-label\", `Link to ${heading.textContent?.trim() ?? \"section\"}`);\n link.textContent = \"#\";\n heading.appendChild(link);\n });\n}\n\n/**\n * Add hoverable `#` self-links to all `h2`–`h4` headings in `.docs-content`.\n * Re-runs on `astro:page-load` for View Transitions. Call once (e.g. from\n * BaseLayout).\n */\nexport function headingAnchors(): void {\n applyHeadingAnchors();\n document.addEventListener(\"astro:page-load\", applyHeadingAnchors);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../src/client/mount.ts","../src/client/ids.ts","../src/client/disclosure.ts","../src/client/dom.ts","../src/client/tabs-controller.ts","../src/client/scroll-lock.ts","../src/client/code-copy.ts","../src/client/heading-anchors.ts"],"sourcesContent":["/**\n * mount.ts — Discover, mount, and unmount component instances.\n *\n * The single entry point used by every `*.client.ts` to wire its component.\n * Handles three concerns:\n *\n * 1. Initial discovery — finds elements matching `selector` and calls `init`\n * on each, storing the returned teardown.\n * 2. View transitions — on `astro:before-swap`, runs every teardown so\n * document/window listeners come down before the DOM is replaced.\n * 3. Re-mount — on `astro:page-load`, re-runs discovery against the new DOM.\n *\n * The init function receives the root element and returns a `destroy()`\n * function. The root element is the keying mechanism — calling mount again\n * against an already-tracked element is a no-op.\n *\n * Usage:\n *\n * mount(\"[data-nb-collapsible]\", (root) => {\n * const disclosure = makeDisclosure({ ... });\n * return () => disclosure.destroy();\n * });\n */\n\ntype Init = (root: HTMLElement) => () => void;\n\nexport function mount(selector: string, init: Init): void {\n const instances = new Map<HTMLElement, () => void>();\n\n function setup() {\n document.querySelectorAll<HTMLElement>(selector).forEach((el) => {\n if (instances.has(el)) return;\n instances.set(el, init(el));\n });\n }\n\n function teardown() {\n instances.forEach((destroy) => destroy());\n instances.clear();\n }\n\n // Module scripts are deferred, so DOM is parsed by the time this runs.\n // Belt-and-braces check anyway.\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setup, { once: true });\n } else {\n setup();\n }\n\n document.addEventListener(\"astro:before-swap\", teardown);\n document.addEventListener(\"astro:page-load\", setup);\n}\n","/**\n * Generate a unique ID with a prefix, scoped to the page.\n *\n * Used to build ARIA relationships (`aria-controls` / `aria-labelledby`)\n * between elements that don't have a stable author-provided id.\n */\n\nlet counter = 0;\n\nexport function generateId(prefix: string): string {\n counter += 1;\n return `${prefix}-${counter}`;\n}\n","/**\n * disclosure.ts — Open/close state with ARIA wiring.\n *\n * The shared module for any \"click trigger, reveals content\" pattern.\n * Owns:\n * - open/closed state (in-memory + reflected as `data-nb-state` on both\n * trigger and content)\n * - ARIA: `aria-expanded` on trigger, `aria-controls` linking to content\n * - Click handler on the trigger\n *\n * Animation is intentionally out of scope — CSS targets the `data-nb-state`\n * attribute and runs whatever transition the component wants. Returning\n * a teardown means the caller can clean up on unmount.\n *\n * Used by: Collapsible, and any future Accordion / Sidebar group /\n * dismissable Banner that wants the standard disclosure semantics.\n */\n\nimport { generateId } from \"./ids\";\n\nexport interface DisclosureOptions {\n /** The element users click to toggle. Usually a `<button>`. */\n trigger: HTMLElement;\n /** The element that's shown/hidden. Gets `id` + `data-nb-state`. */\n content: HTMLElement;\n /** Initial open state. Default `false`. */\n defaultOpen?: boolean;\n /** Called whenever open changes. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface DisclosureInstance {\n open(): void;\n close(): void;\n toggle(): void;\n isOpen(): boolean;\n destroy(): void;\n}\n\nexport function makeDisclosure(opts: DisclosureOptions): DisclosureInstance {\n const { trigger, content, defaultOpen = false, onOpenChange } = opts;\n\n let open = defaultOpen;\n\n // Ensure ARIA relationship exists.\n if (!content.id) {\n content.id = generateId(\"nb-disclosure\");\n }\n trigger.setAttribute(\"aria-controls\", content.id);\n\n function syncState() {\n const state = open ? \"open\" : \"closed\";\n trigger.setAttribute(\"data-nb-state\", state);\n content.setAttribute(\"data-nb-state\", state);\n trigger.setAttribute(\"aria-expanded\", String(open));\n }\n\n function setOpen(value: boolean) {\n if (open === value) return;\n open = value;\n syncState();\n onOpenChange?.(value);\n }\n\n function handleClick(e: MouseEvent) {\n e.preventDefault();\n setOpen(!open);\n }\n\n syncState();\n trigger.addEventListener(\"click\", handleClick);\n\n return {\n open() {\n setOpen(true);\n },\n close() {\n setOpen(false);\n },\n toggle() {\n setOpen(!open);\n },\n isOpen() {\n return open;\n },\n destroy() {\n trigger.removeEventListener(\"click\", handleClick);\n },\n };\n}\n","/**\n * Shared DOM constants for client-side interaction primitives.\n */\n\n/** CSS selector for focusable elements within a container. */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([disabled]):not([tabindex=\"-1\"])';\n","/**\n * Shared tab activation primitive.\n *\n * Handles aria-selected, panel visibility, roving tabindex,\n * keyboard navigation, sliding indicator, and cross-instance sync.\n *\n * Used by: Tabs.astro, PackageManagers.astro\n */\n\nimport { FOCUSABLE } from \"./dom\";\n\nexport interface TabsConfig {\n /** Root container holding tabs + panels */\n container: Element;\n /** CSS selector for tab trigger buttons */\n tabSelector: string;\n /** CSS selector for tab panels */\n panelSelector: string;\n /** Optional sliding indicator element */\n indicator?: HTMLElement | null;\n /** Enable roving tabindex + arrow key navigation (default: true) */\n rovingTabindex?: boolean;\n /** Cross-instance persistence config */\n sync?: {\n key: string;\n storage?: \"local\" | \"session\";\n /** Extract sync label from a tab element. Default: textContent.trim() */\n getLabel?: (tab: HTMLElement) => string;\n };\n /** Called after a tab is activated */\n onActivate?: (index: number) => void;\n}\n\nexport interface TabsInstance {\n activate(index: number, options?: { emitSync?: boolean }): void;\n readonly currentIndex: number;\n destroy(): void;\n}\n\nexport function initTabs(config: TabsConfig): TabsInstance {\n const { container, tabSelector, panelSelector, indicator = null, rovingTabindex = true, sync, onActivate } = config;\n\n const tabs = Array.from(container.querySelectorAll<HTMLElement>(tabSelector));\n const panels = Array.from(container.querySelectorAll<HTMLElement>(panelSelector));\n const tablist = container.querySelector(\"[role=tablist]\") ?? container;\n\n let currentIndex = -1;\n let isInitialActivation = true;\n\n function getStorage(kind: \"local\" | \"session\"): Storage | null {\n try {\n return kind === \"session\" ? sessionStorage : localStorage;\n } catch {\n return null;\n }\n }\n\n function getLabel(tab: HTMLElement): string {\n return sync?.getLabel?.(tab) ?? tab.textContent?.trim() ?? \"\";\n }\n\n function updateIndicator(index: number) {\n if (!indicator || !tabs[index]) return;\n indicator.style.left = `${tabs[index].offsetLeft}px`;\n indicator.style.width = `${tabs[index].offsetWidth}px`;\n }\n\n function activate(index: number, options?: { emitSync?: boolean }) {\n const emitSync = options?.emitSync ?? true;\n if (index === currentIndex) return;\n\n // Capture scroll position before DOM changes to prevent layout jump\n const rect = container.getBoundingClientRect();\n const scrollBefore = rect.top;\n\n currentIndex = index;\n\n tabs.forEach((tab, i) => {\n const active = i === index;\n tab.setAttribute(\"aria-selected\", String(active));\n if (rovingTabindex) {\n tab.setAttribute(\"tabindex\", active ? \"0\" : \"-1\");\n }\n });\n\n panels.forEach((panel, i) => {\n const visible = i === index;\n panel.hidden = !visible;\n // Panels with no focusable children need tabindex=\"0\" so keyboard\n // users can Tab into the content (WAI-ARIA Tabs pattern).\n if (visible) {\n const hasFocusable = panel.querySelector(FOCUSABLE) !== null;\n if (!hasFocusable) {\n panel.setAttribute(\"tabindex\", \"0\");\n } else {\n panel.removeAttribute(\"tabindex\");\n }\n }\n });\n\n updateIndicator(index);\n onActivate?.(index);\n\n // Compensate scroll position after panel height change (skip on first paint)\n if (emitSync && !isInitialActivation) {\n const scrollAfter = container.getBoundingClientRect().top;\n const delta = scrollAfter - scrollBefore;\n if (Math.abs(delta) > 1) {\n window.scrollTo({\n top: window.scrollY + delta,\n behavior: \"instant\",\n });\n }\n }\n isInitialActivation = false;\n\n if (sync && emitSync) {\n const label = getLabel(tabs[index]);\n const store = getStorage(sync.storage === \"session\" ? \"session\" : \"local\");\n store?.setItem(sync.key, label);\n window.dispatchEvent(\n new CustomEvent(\"ui-tab-sync\", {\n detail: { key: sync.key, label, origin: container },\n }),\n );\n }\n }\n\n // Resolve initial index from sync storage\n let initialIndex = 0;\n if (sync) {\n const store = getStorage(sync.storage === \"session\" ? \"session\" : \"local\");\n const saved = store?.getItem(sync.key);\n if (saved) {\n const idx = tabs.findIndex((t) => getLabel(t) === saved);\n if (idx >= 0) initialIndex = idx;\n }\n }\n\n // Click delegation on tablist\n function handleClick(e: Event) {\n const target = (e.target as HTMLElement).closest(tabSelector);\n if (!target) return;\n const idx = tabs.indexOf(target as HTMLElement);\n if (idx >= 0) {\n activate(idx);\n if (rovingTabindex) (target as HTMLElement).focus();\n }\n }\n\n // Keyboard navigation (roving tabindex)\n function handleKeydown(e: KeyboardEvent) {\n if (!rovingTabindex) return;\n const ci = tabs.indexOf(e.target as HTMLElement);\n if (ci < 0) return;\n\n let next: number;\n switch (e.key) {\n case \"ArrowRight\":\n next = ci + 1;\n break;\n case \"ArrowLeft\":\n next = ci - 1;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = tabs.length - 1;\n break;\n default:\n return;\n }\n // No-wrap: ignore if out of bounds\n if (!tabs[next]) return;\n e.preventDefault();\n activate(next);\n tabs[next].focus();\n }\n\n // Cross-instance sync via CustomEvent\n function handleSync(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (detail.key === sync?.key && detail.origin !== container) {\n const idx = tabs.findIndex((t) => getLabel(t) === detail.label);\n if (idx >= 0) activate(idx, { emitSync: false });\n }\n }\n\n // Wire events\n tablist.addEventListener(\"click\", handleClick);\n tablist.addEventListener(\"keydown\", handleKeydown as EventListener);\n if (sync) window.addEventListener(\"ui-tab-sync\", handleSync);\n\n // Initial activation — skip indicator transition for first paint\n if (indicator) indicator.style.transition = \"none\";\n activate(initialIndex);\n if (indicator) {\n void indicator.offsetHeight; // force reflow\n indicator.style.transition = \"\";\n }\n\n return {\n activate,\n get currentIndex() {\n return currentIndex;\n },\n destroy() {\n tablist.removeEventListener(\"click\", handleClick);\n tablist.removeEventListener(\"keydown\", handleKeydown as EventListener);\n if (sync) window.removeEventListener(\"ui-tab-sync\", handleSync);\n },\n };\n}\n","/**\n * scroll-lock.ts — Body scroll lock with scrollbar-width compensation.\n *\n * Prevents background scrolling when a modal/overlay is open.\n * Compensates for the scrollbar disappearing to avoid layout shift\n * (visible on Windows/Linux where scrollbars have width).\n *\n * Uses a data attribute + CSS for the overflow lock, and inline\n * paddingRight for the scrollbar compensation.\n *\n * Used by: Dialog (and any future overlay primitive).\n */\n\nconst ATTR = \"data-scroll-locked\";\n\nlet lockCount = 0;\nlet savedPaddingRight = \"\";\n\nexport function lockScroll(): void {\n lockCount++;\n if (lockCount > 1) return;\n\n const scrollbarW = window.innerWidth - document.documentElement.clientWidth;\n savedPaddingRight = document.body.style.paddingRight;\n\n document.body.setAttribute(ATTR, \"\");\n if (scrollbarW > 0) {\n document.body.style.paddingRight = `${scrollbarW}px`;\n }\n}\n\nexport function unlockScroll(): void {\n if (lockCount === 0) return;\n lockCount--;\n if (lockCount > 0) return;\n\n document.body.removeAttribute(ATTR);\n document.body.style.paddingRight = savedPaddingRight;\n}\n","/**\n * code-copy.ts — Injects a Nimbus-styled copy button into every Shiki\n * code block rendered by Astro's built-in `<Code>` and fenced\n * code blocks in MDX.\n *\n * Astro emits `<pre class=\"astro-code shiki ...\">` for syntax-highlighted\n * blocks. We attach a copy button positioned in the top-right corner.\n *\n * Page-wide enhancement, not tied to a single component. Call `codeCopy()`\n * once (e.g. from BaseLayout). Uses `mount` for lifecycle so the buttons\n * are torn down on view transitions and re-added against the new DOM.\n *\n * The original code text comes from the `<code>` element's textContent —\n * Shiki's wrapper spans flatten down to the raw source.\n *\n * Code blocks rendered inside [data-cg-row] (CodeGroup) are skipped\n * because CodeGroup brings its own copy button per panel.\n */\n\nimport { mount } from \"./mount\";\n\nconst COPY_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z\"/></svg>`;\nconst CHECK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34Z\"/></svg>`;\n\nfunction getCodeText(pre: HTMLElement): string {\n const codeEl = pre.querySelector<HTMLElement>(\"code\");\n return codeEl?.textContent ?? pre.textContent ?? \"\";\n}\n\nfunction initCodeCopy(pre: HTMLElement): () => void {\n // Skip code blocks owned by CodeGroup. `data-cg-panels-raw` is SSR'd\n // (catches pres before CodeGroup's client script reparents them);\n // `data-cg-row` catches them after reparenting.\n if (pre.closest(\"[data-cg-panels-raw], [data-cg-row]\")) return () => {};\n\n // Append the button to the figure wrapper, not the pre. The pre has\n // `overflow-x: auto`, and absolutely-positioned children of overflow:auto\n // containers slide along with horizontal scroll on iOS Safari. The figure\n // is the non-scrolling wrapper emitted by titleAndLangTransformer.\n const host = (pre.closest(\".nb-code-figure\") as HTMLElement | null) ?? pre;\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"nb-code-copy\";\n btn.setAttribute(\"aria-label\", \"Copy code to clipboard\");\n btn.innerHTML = COPY_ICON;\n\n let resetTimer: number | undefined;\n\n async function handleClick() {\n const text = getCodeText(pre);\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return;\n }\n\n btn.innerHTML = CHECK_ICON;\n btn.dataset.state = \"copied\";\n btn.setAttribute(\"aria-label\", \"Copied!\");\n\n if (resetTimer) window.clearTimeout(resetTimer);\n resetTimer = window.setTimeout(() => {\n btn.innerHTML = COPY_ICON;\n delete btn.dataset.state;\n btn.setAttribute(\"aria-label\", \"Copy code to clipboard\");\n }, 1500);\n }\n\n btn.addEventListener(\"click\", handleClick);\n\n if (getComputedStyle(host).position === \"static\") {\n host.style.position = \"relative\";\n }\n host.appendChild(btn);\n\n return () => {\n if (resetTimer) window.clearTimeout(resetTimer);\n btn.removeEventListener(\"click\", handleClick);\n btn.remove();\n };\n}\n\n/** Wire copy buttons onto all Shiki code blocks on the page. Call once. */\nexport function codeCopy(): void {\n mount(\"pre.astro-code\", initCodeCopy);\n}\n","/** Add hoverable self-links to markdown headings with ids. */\n\nfunction applyHeadingAnchors() {\n document.querySelectorAll<HTMLElement>(\".docs-content :is(h2, h3, h4)[id]\").forEach((heading) => {\n if (heading.hasAttribute(\"data-heading-anchor-ready\")) return;\n heading.setAttribute(\"data-heading-anchor-ready\", \"true\");\n\n const link = document.createElement(\"a\");\n link.href = `#${heading.id}`;\n link.className = \"heading-anchor\";\n link.setAttribute(\"aria-label\", `Link to ${heading.textContent?.trim() ?? \"section\"}`);\n link.textContent = \"#\";\n heading.appendChild(link);\n });\n}\n\n/**\n * Add hoverable `#` self-links to all `h2`–`h4` headings in `.docs-content`.\n * Re-runs on `astro:page-load` for View Transitions. Call once (e.g. from\n * BaseLayout).\n */\nexport function headingAnchors(): void {\n applyHeadingAnchors();\n document.addEventListener(\"astro:page-load\", applyHeadingAnchors);\n}\n"],"mappings":"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"}
|
package/dist/content.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ declare function docsCollection(options?: DocsCollectionOptions): {
|
|
|
107
107
|
link: z.ZodOptional<z.ZodString>;
|
|
108
108
|
label: z.ZodOptional<z.ZodString>;
|
|
109
109
|
}, z.core.$strip>, z.ZodLiteral<false>]>>;
|
|
110
|
+
previousSlug: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
110
111
|
}, z.core.$strip> | z.ZodObject<{
|
|
111
112
|
[x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
112
113
|
}, z.core.$strip>;
|
|
@@ -121,6 +122,40 @@ declare function partialsCollection(options?: PartialsCollectionOptions): {
|
|
|
121
122
|
params: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
122
123
|
}, z.core.$strip>>;
|
|
123
124
|
};
|
|
125
|
+
interface ComponentsCollectionOptions {
|
|
126
|
+
/**
|
|
127
|
+
* Directory under `src/content/` to load component entries from.
|
|
128
|
+
* Default: `"components"`.
|
|
129
|
+
*/
|
|
130
|
+
base?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Glob pattern relative to `base`.
|
|
133
|
+
* Default: `"**\/*.{md,mdx}"`.
|
|
134
|
+
*/
|
|
135
|
+
pattern?: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns an Astro content-collection config (`{ loader, schema }`) for the
|
|
139
|
+
* components collection — for sites documenting their own UI components.
|
|
140
|
+
*
|
|
141
|
+
* Pairs with the `component-showcase` registry recipe, which installs the
|
|
142
|
+
* matching `<Showcase>` / `<Example>` MDX wrappers and the `/components`
|
|
143
|
+
* route. Frontmatter shape: `{ title, tagline, props }`.
|
|
144
|
+
*/
|
|
145
|
+
declare function componentsCollection(options?: ComponentsCollectionOptions): {
|
|
146
|
+
loader: astro_loaders0.Loader;
|
|
147
|
+
schema: z.ZodObject<{
|
|
148
|
+
title: z.ZodString;
|
|
149
|
+
tagline: z.ZodString;
|
|
150
|
+
props: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
151
|
+
name: z.ZodString;
|
|
152
|
+
type: z.ZodString;
|
|
153
|
+
defaultValue: z.ZodOptional<z.ZodString>;
|
|
154
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
155
|
+
description: z.ZodString;
|
|
156
|
+
}, z.core.$strip>>>;
|
|
157
|
+
}, z.core.$strip>;
|
|
158
|
+
};
|
|
124
159
|
//#endregion
|
|
125
|
-
export { DocsCollectionOptions, PartialsCollectionOptions, docsCollection, partialsCollection };
|
|
160
|
+
export { ComponentsCollectionOptions, DocsCollectionOptions, PartialsCollectionOptions, componentsCollection, docsCollection, partialsCollection };
|
|
126
161
|
//# sourceMappingURL=content.d.ts.map
|
package/dist/content.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","names":[],"sources":["../src/content.ts"],"mappings":";;;;UAyBiB,qBAAA;EAsCD;;;;EAjCd,IAAA;;;;;EAKA,OAAA;;;;;;EAMA,YAAA,GAAe,MAAA,SAAe,CAAA,CAAE,UAAA;AAAA;AAAA,UAGjB,yBAAA;;;;;EAKf,IAAA;;;;;EAKA,OAAA;AAAA;;;;;iBASc,cAAA,CAAe,OAAA,GAAS,qBAAA;UAA0B,cAAA,CAAA,MAAA
|
|
1
|
+
{"version":3,"file":"content.d.ts","names":[],"sources":["../src/content.ts"],"mappings":";;;;UAyBiB,qBAAA;EAsCD;;;;EAjCd,IAAA;;;;;EAKA,OAAA;;;;;;EAMA,YAAA,GAAe,MAAA,SAAe,CAAA,CAAE,UAAA;AAAA;AAAA,UAGjB,yBAAA;;;;;EAKf,IAAA;;;;;EAKA,OAAA;AAAA;;;;;iBASc,cAAA,CAAe,OAAA,GAAS,qBAAA;UAA0B,cAAA,CAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAelD,kBAAA,CAAmB,OAAA,GAAS,yBAAA;UAA8B,cAAA,CAAA,MAAA;;;;;UAUzD,2BAAA;;;;;EAKf,IAAA;;;;;EAKA,OAAA;AAAA;;;;;;;;;iBAWc,oBAAA,CAAqB,OAAA,GAAS,2BAAA;UAAgC,cAAA,CAAA,MAAA"}
|
package/dist/content.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineDocSchema, partialsSchema } from "./schemas.js";
|
|
1
|
+
import { componentsSchema, defineDocSchema, partialsSchema } from "./schemas.js";
|
|
2
2
|
import { glob } from "astro/loaders";
|
|
3
3
|
|
|
4
4
|
//#region src/content.ts
|
|
@@ -51,7 +51,24 @@ function partialsCollection(options = {}) {
|
|
|
51
51
|
schema: partialsSchema
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns an Astro content-collection config (`{ loader, schema }`) for the
|
|
56
|
+
* components collection — for sites documenting their own UI components.
|
|
57
|
+
*
|
|
58
|
+
* Pairs with the `component-showcase` registry recipe, which installs the
|
|
59
|
+
* matching `<Showcase>` / `<Example>` MDX wrappers and the `/components`
|
|
60
|
+
* route. Frontmatter shape: `{ title, tagline, props }`.
|
|
61
|
+
*/
|
|
62
|
+
function componentsCollection(options = {}) {
|
|
63
|
+
return {
|
|
64
|
+
loader: glob({
|
|
65
|
+
base: `./src/content/${options.base ?? "components"}`,
|
|
66
|
+
pattern: options.pattern ?? DEFAULT_PATTERN
|
|
67
|
+
}),
|
|
68
|
+
schema: componentsSchema
|
|
69
|
+
};
|
|
70
|
+
}
|
|
54
71
|
|
|
55
72
|
//#endregion
|
|
56
|
-
export { docsCollection, partialsCollection };
|
|
73
|
+
export { componentsCollection, docsCollection, partialsCollection };
|
|
57
74
|
//# sourceMappingURL=content.js.map
|
package/dist/content.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","names":[],"sources":["../src/content.ts"],"sourcesContent":["/**\n * Content collection helpers for `nimbus-docs/content`.\n *\n * Users plug these into their `src/content.config.ts`:\n *\n * import { defineCollection } from \"astro:content\";\n * import { docsCollection, partialsCollection } from \"nimbus-docs/content\";\n *\n * export const collections = {\n * docs: defineCollection(docsCollection()),\n * partials: defineCollection(partialsCollection()),\n * };\n *\n * Extend the docs schema with extra frontmatter fields:\n *\n * docs: defineCollection(docsCollection({\n * schemaFields: { author: z.string(), tags: z.array(z.string()) },\n * })),\n */\n\nimport { glob } from \"astro/loaders\";\nimport type { z } from \"astro/zod\";\n\nimport { defineDocSchema, partialsSchema } from \"./schemas.js\";\n\nexport interface DocsCollectionOptions {\n /**\n * Directory under `src/content/` to load docs from.\n * Default: `\"docs\"`.\n */\n base?: string;\n /**\n * Glob pattern relative to `base`.\n * Default: `\"** /*.{md,mdx}\"` (space added to avoid breaking this comment).\n */\n pattern?: string;\n /**\n * Extra fields merged into the default docs schema. Lets users add\n * project-specific frontmatter (author, tags, etc.) without rebuilding\n * the whole schema.\n */\n schemaFields?: Record<string, z.ZodTypeAny>;\n}\n\nexport interface PartialsCollectionOptions {\n /**\n * Directory under `src/content/` to load partials from.\n * Default: `\"partials\"`.\n */\n base?: string;\n /**\n * Glob pattern relative to `base`.\n * Default: `\"** /*.{md,mdx}\"`.\n */\n pattern?: string;\n}\n\nconst DEFAULT_PATTERN = \"**/*.{md,mdx}\";\n\n/**\n * Returns an Astro content-collection config (`{ loader, schema }`) for the\n * docs collection. Pass to `defineCollection()`.\n */\nexport function docsCollection(options: DocsCollectionOptions = {}) {\n const base = `./src/content/${options.base ?? \"docs\"}`;\n const pattern = options.pattern ?? DEFAULT_PATTERN;\n const schema = defineDocSchema({ fields: options.schemaFields });\n\n return {\n loader: glob({ base, pattern }),\n schema,\n };\n}\n\n/**\n * Returns an Astro content-collection config (`{ loader, schema }`) for the\n * partials collection. Pass to `defineCollection()`.\n */\nexport function partialsCollection(options: PartialsCollectionOptions = {}) {\n const base = `./src/content/${options.base ?? \"partials\"}`;\n const pattern = options.pattern ?? DEFAULT_PATTERN;\n\n return {\n loader: glob({ base, pattern }),\n schema: partialsSchema,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyDA,MAAM,kBAAkB;;;;;AAMxB,SAAgB,eAAe,UAAiC,EAAE,EAAE;CAClE,MAAM,OAAO,iBAAiB,QAAQ,QAAQ;CAC9C,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,gBAAgB,EAAE,QAAQ,QAAQ,cAAc,CAAC;AAEhE,QAAO;EACL,QAAQ,KAAK;GAAE;GAAM;GAAS,CAAC;EAC/B;EACD;;;;;;AAOH,SAAgB,mBAAmB,UAAqC,EAAE,EAAE;AAI1E,QAAO;EACL,QAAQ,KAAK;GAAE,MAJJ,iBAAiB,QAAQ,QAAQ;GAIvB,SAHP,QAAQ,WAAW;GAGH,CAAC;EAC/B,QAAQ;EACT"}
|
|
1
|
+
{"version":3,"file":"content.js","names":[],"sources":["../src/content.ts"],"sourcesContent":["/**\n * Content collection helpers for `nimbus-docs/content`.\n *\n * Users plug these into their `src/content.config.ts`:\n *\n * import { defineCollection } from \"astro:content\";\n * import { docsCollection, partialsCollection } from \"nimbus-docs/content\";\n *\n * export const collections = {\n * docs: defineCollection(docsCollection()),\n * partials: defineCollection(partialsCollection()),\n * };\n *\n * Extend the docs schema with extra frontmatter fields:\n *\n * docs: defineCollection(docsCollection({\n * schemaFields: { author: z.string(), tags: z.array(z.string()) },\n * })),\n */\n\nimport { glob } from \"astro/loaders\";\nimport type { z } from \"astro/zod\";\n\nimport { componentsSchema, defineDocSchema, partialsSchema } from \"./schemas.js\";\n\nexport interface DocsCollectionOptions {\n /**\n * Directory under `src/content/` to load docs from.\n * Default: `\"docs\"`.\n */\n base?: string;\n /**\n * Glob pattern relative to `base`.\n * Default: `\"** /*.{md,mdx}\"` (space added to avoid breaking this comment).\n */\n pattern?: string;\n /**\n * Extra fields merged into the default docs schema. Lets users add\n * project-specific frontmatter (author, tags, etc.) without rebuilding\n * the whole schema.\n */\n schemaFields?: Record<string, z.ZodTypeAny>;\n}\n\nexport interface PartialsCollectionOptions {\n /**\n * Directory under `src/content/` to load partials from.\n * Default: `\"partials\"`.\n */\n base?: string;\n /**\n * Glob pattern relative to `base`.\n * Default: `\"** /*.{md,mdx}\"`.\n */\n pattern?: string;\n}\n\nconst DEFAULT_PATTERN = \"**/*.{md,mdx}\";\n\n/**\n * Returns an Astro content-collection config (`{ loader, schema }`) for the\n * docs collection. Pass to `defineCollection()`.\n */\nexport function docsCollection(options: DocsCollectionOptions = {}) {\n const base = `./src/content/${options.base ?? \"docs\"}`;\n const pattern = options.pattern ?? DEFAULT_PATTERN;\n const schema = defineDocSchema({ fields: options.schemaFields });\n\n return {\n loader: glob({ base, pattern }),\n schema,\n };\n}\n\n/**\n * Returns an Astro content-collection config (`{ loader, schema }`) for the\n * partials collection. Pass to `defineCollection()`.\n */\nexport function partialsCollection(options: PartialsCollectionOptions = {}) {\n const base = `./src/content/${options.base ?? \"partials\"}`;\n const pattern = options.pattern ?? DEFAULT_PATTERN;\n\n return {\n loader: glob({ base, pattern }),\n schema: partialsSchema,\n };\n}\n\nexport interface ComponentsCollectionOptions {\n /**\n * Directory under `src/content/` to load component entries from.\n * Default: `\"components\"`.\n */\n base?: string;\n /**\n * Glob pattern relative to `base`.\n * Default: `\"**\\/*.{md,mdx}\"`.\n */\n pattern?: string;\n}\n\n/**\n * Returns an Astro content-collection config (`{ loader, schema }`) for the\n * components collection — for sites documenting their own UI components.\n *\n * Pairs with the `component-showcase` registry recipe, which installs the\n * matching `<Showcase>` / `<Example>` MDX wrappers and the `/components`\n * route. Frontmatter shape: `{ title, tagline, props }`.\n */\nexport function componentsCollection(options: ComponentsCollectionOptions = {}) {\n const base = `./src/content/${options.base ?? \"components\"}`;\n const pattern = options.pattern ?? DEFAULT_PATTERN;\n\n return {\n loader: glob({ base, pattern }),\n schema: componentsSchema,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyDA,MAAM,kBAAkB;;;;;AAMxB,SAAgB,eAAe,UAAiC,EAAE,EAAE;CAClE,MAAM,OAAO,iBAAiB,QAAQ,QAAQ;CAC9C,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,gBAAgB,EAAE,QAAQ,QAAQ,cAAc,CAAC;AAEhE,QAAO;EACL,QAAQ,KAAK;GAAE;GAAM;GAAS,CAAC;EAC/B;EACD;;;;;;AAOH,SAAgB,mBAAmB,UAAqC,EAAE,EAAE;AAI1E,QAAO;EACL,QAAQ,KAAK;GAAE,MAJJ,iBAAiB,QAAQ,QAAQ;GAIvB,SAHP,QAAQ,WAAW;GAGH,CAAC;EAC/B,QAAQ;EACT;;;;;;;;;;AAwBH,SAAgB,qBAAqB,UAAuC,EAAE,EAAE;AAI9E,QAAO;EACL,QAAQ,KAAK;GAAE,MAJJ,iBAAiB,QAAQ,QAAQ;GAIvB,SAHP,QAAQ,WAAW;GAGH,CAAC;EAC/B,QAAQ;EACT"}
|