nimbus-docs 0.1.4 → 0.1.6

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.
@@ -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 \"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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"}
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/lint/disables.ts","../../src/lint/fix.ts","../../src/lint/engine.ts","../../src/lint/discover.ts","../../src/lint/format.ts","../../src/cli/lint.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 \"lint-prose-textlint\": {\n \"name\": \"lint-prose-textlint\",\n \"type\": \"registry:feature\",\n \"title\": \"Prose linting with textlint\",\n \"description\": \"Add textlint with write-good, alex, and terminology rules — npm-native prose linting that runs alongside nimbus-docs lint.\"\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","/**\n * Per-file and per-line disable directives — both designed so the disable\n * itself is greppable:\n *\n * - Frontmatter `nimbusDisableRules: [\"nimbus/internal-link\"]` disables\n * the listed codes for the whole file.\n * - An inline `{/* nimbus-rule-disable-next-line nimbus/bare-url *​/}`\n * comment disables the named code on the next non-blank line.\n *\n * Both require a rule code: an empty `nimbusDisableRules` array is a\n * reported error, so the reason for a disable is always visible.\n */\n\nimport { isRuleCode } from \"./diagnostic.js\";\nimport type { RuleReport } from \"./rule.js\";\n\nexport interface DisableInfo {\n /** Rule codes disabled for the entire file. */\n fileDisabled: Set<string>;\n /** 1-based source line → rule codes disabled on that line. */\n lineDisabled: Map<number, Set<string>>;\n /** Malformed-directive findings (e.g. an empty disable array). */\n problems: RuleReport[];\n}\n\nconst INLINE_DISABLE =\n /\\{\\/\\*\\s*nimbus-rule-disable-next-line\\s+(\\S+)\\s*\\*\\/\\}/;\n\nexport function collectDisables(\n frontmatter: Record<string, unknown> | null,\n frontmatterRaw: string | null,\n frontmatterStartLine: number,\n lines: string[],\n): DisableInfo {\n const fileDisabled = new Set<string>();\n const lineDisabled = new Map<number, Set<string>>();\n const problems: RuleReport[] = [];\n\n // ----- Frontmatter file-level disables.\n if (frontmatter && \"nimbusDisableRules\" in frontmatter) {\n const raw = frontmatter.nimbusDisableRules;\n const at = locateFrontmatterKey(\n frontmatterRaw,\n \"nimbusDisableRules\",\n frontmatterStartLine,\n );\n if (!Array.isArray(raw)) {\n problems.push({\n message:\n '\"nimbusDisableRules\" must be an array of rule codes, e.g. [\"nimbus/internal-link\"].',\n line: at.line,\n column: at.column,\n });\n } else if (raw.length === 0) {\n problems.push({\n message:\n '\"nimbusDisableRules\" is empty — remove it, or name the rule code(s) you mean to disable so the reason stays greppable.',\n line: at.line,\n column: at.column,\n });\n } else {\n for (const entry of raw) {\n if (typeof entry !== \"string\") {\n problems.push({\n message: `\"nimbusDisableRules\" entry ${JSON.stringify(entry)} must be a string rule code.`,\n line: at.line,\n column: at.column,\n });\n continue;\n }\n if (!isRuleCode(entry)) {\n problems.push({\n message: `\"nimbusDisableRules\" lists \"${entry}\", which is not a known rule code — typos here silently no-op, so we surface them.`,\n line: at.line,\n column: at.column,\n });\n continue;\n }\n fileDisabled.add(entry);\n }\n }\n }\n\n // ----- Inline next-line disables.\n for (let i = 0; i < lines.length; i++) {\n const match = lines[i]!.match(INLINE_DISABLE);\n if (!match) continue;\n const code = match[1]!;\n if (!isRuleCode(code)) {\n problems.push({\n message: `inline disable references \"${code}\", which is not a known rule code — typos here silently no-op, so we surface them.`,\n line: i + 1,\n column: (match.index ?? 0) + 1,\n });\n continue;\n }\n // Target the next non-blank line (1-based).\n let target = -1;\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j]!.trim() !== \"\") {\n target = j + 1;\n break;\n }\n }\n if (target === -1) continue;\n const set = lineDisabled.get(target) ?? new Set<string>();\n set.add(code);\n lineDisabled.set(target, set);\n }\n\n return { fileDisabled, lineDisabled, problems };\n}\n\n/** Is a diagnostic for `code` on `line` suppressed by a disable directive? */\nexport function isDisabled(\n info: DisableInfo,\n code: string,\n line: number,\n): boolean {\n if (info.fileDisabled.has(code)) return true;\n return info.lineDisabled.get(line)?.has(code) ?? false;\n}\n\nfunction locateFrontmatterKey(\n frontmatterRaw: string | null,\n key: string,\n startLine: number,\n): { line: number; column: number } {\n if (frontmatterRaw) {\n const rawLines = frontmatterRaw.split(\"\\n\");\n for (let i = 0; i < rawLines.length; i++) {\n const m = rawLines[i]!.match(new RegExp(`^(\\\\s*)${key}\\\\s*:`));\n if (m) return { line: startLine + i, column: m[1]!.length + 1 };\n }\n }\n return { line: startLine, column: 1 };\n}\n","/**\n * Apply rule `fix` edits to source. Diagnostic-level atomic: each\n * diagnostic's edits are applied together or not at all, and a diagnostic\n * whose span overlaps one already applied is skipped (a second pass picks\n * it up). Edits are character offsets into the source — the same unist\n * offsets the parser reports — applied right-to-left so earlier offsets\n * stay valid.\n *\n * No \"smart\" fixes: this only applies edits a rule already declared. The\n * change is plain and reviewable in `git diff`.\n */\n\nimport type { Diagnostic } from \"./diagnostic.js\";\n\nexport interface FixResult {\n output: string;\n /** Number of diagnostics whose fix was applied. */\n fixed: number;\n /**\n * The exact diagnostic objects whose fixes were applied. The caller\n * uses this (by identity, not by index) to know which diagnostics to\n * suppress from the post-fix report — the rest stay in the output\n * because their `fix` field was advisory-only (no edits) or was\n * skipped due to an overlap with another applied fix.\n */\n applied: Set<Diagnostic>;\n}\n\nexport function applyFixes(source: string, diagnostics: Diagnostic[]): FixResult {\n const items = diagnostics\n .filter(\n (d): d is Diagnostic & { fix: NonNullable<Diagnostic[\"fix\"]> } =>\n d.fix !== undefined && d.fix.edits.length > 0,\n )\n .map((d) => {\n const edits = d.fix.edits;\n return {\n diagnostic: d,\n edits,\n start: Math.min(...edits.map((e) => e.range[0])),\n end: Math.max(...edits.map((e) => e.range[1])),\n };\n })\n // Apply from the end of the file backwards.\n .sort((a, b) => b.start - a.start);\n\n let output = source;\n let frontier = Number.POSITIVE_INFINITY;\n const applied = new Set<Diagnostic>();\n\n for (const item of items) {\n if (item.end > frontier) continue; // overlaps an already-applied span\n const ordered = [...item.edits].sort((a, b) => b.range[0] - a.range[0]);\n for (const edit of ordered) {\n output =\n output.slice(0, edit.range[0]) + edit.text + output.slice(edit.range[1]);\n }\n frontier = item.start;\n applied.add(item.diagnostic);\n }\n\n return { output, fixed: applied.size, applied };\n}\n","/**\n * The lint engine. Runs the registered rules over parsed files, resolves\n * each rule's severity from config, applies per-file and per-line disables,\n * and collects everything into the one `Diagnostic` envelope.\n *\n * Pure and synchronous: `lintFile` takes a `ParsedFile` and returns\n * `Diagnostic[]`, which is what the test harness drives directly. The\n * disk-walking entry points (`lintPaths`) sit on top.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport {\n resolveRuleForCollection,\n type CollectionsConfig,\n type RulesConfig,\n} from \"./config.js\";\nimport type { Diagnostic, RuleCode, Severity } from \"./diagnostic.js\";\nimport { collectDisables, isDisabled } from \"./disables.js\";\nimport { applyFixes } from \"./fix.js\";\nimport { parseSource, type ParsedFile } from \"./parse.js\";\nimport type { RuleReport } from \"./rule.js\";\nimport { RULES } from \"./rules/index.js\";\nimport type { AuthoringRuleCode } from \"./diagnostic.js\";\n\nexport interface LintOptions {\n /** Severity overrides. Empty/omitted = every authoring rule on at error. */\n rules?: RulesConfig;\n /**\n * Per-collection overrides. Each entry's `rules` block shallow-merges\n * over the top-level `rules` for files in that collection. Resolution\n * precedence: top-level → per-collection → per-file `nimbusDisableRules`\n * → per-line inline disables.\n */\n collections?: CollectionsConfig;\n /**\n * Restrict the run to a single rule (CLI `--rule`). Authoring-only —\n * build validators don't have a severity knob, so `--rule=mdx-syntax`\n * is rejected at the CLI before reaching the engine.\n */\n only?: AuthoringRuleCode;\n /**\n * The project's canonical site URL (from `nimbusConfig.site`), threaded\n * into each rule's `ctx.site`. Lets site-aware rules (e.g.\n * `no-self-host-url`) catch the deploy host without making the user\n * duplicate it in their lint config.\n */\n site?: string;\n /**\n * Cancellation signal — checked between files in `lintPaths`/`fixPaths`.\n * The CLI wires this to SIGINT so Ctrl-C stops the run after the\n * in-progress file finishes (its write is already atomic), instead of\n * killing the process mid-rename.\n */\n signal?: AbortSignal;\n}\n\nexport interface LintSummary {\n errors: number;\n warnings: number;\n total: number;\n files: number;\n}\n\n/** Lint one already-parsed file. */\nexport function lintFile(file: ParsedFile, opts: LintOptions = {}): Diagnostic[] {\n // `opts.only` targets one rule via `--rule=<code>`. Since authoring rules\n // default to \"off\", a bare `--rule=foo` against the framework default\n // would resolve to off and print nothing — confusing UX for a flag the\n // user explicitly asked for. Force-enable it at \"error\" *unless* the\n // user already wrote an explicit top-level setting (including \"off\" —\n // explicit top-level intent wins over a CLI shortcut). When the\n // force-enable is active we also strip per-collection overrides for that\n // same code, so a `collections.<name>.rules: { \"foo\": \"off\" }` block\n // doesn't silently re-shadow the rule for whole subtrees (the exact\n // \"silent zero coverage\" failure mode the --rule flag exists to prevent).\n const forceEnable =\n opts.only !== undefined && opts.rules?.[opts.only] === undefined;\n const rules = forceEnable\n ? { ...(opts.rules ?? {}), [opts.only!]: \"error\" as const }\n : opts.rules ?? {};\n const collections = forceEnable\n ? stripCodeFromCollections(opts.collections ?? {}, opts.only!)\n : opts.collections ?? {};\n const out: Diagnostic[] = [];\n\n // A file that didn't parse won't render — emit the build-level syntax\n // error and stop; there's no tree to run authoring rules against.\n if (file.parseError) {\n return [\n {\n code: \"nimbus/mdx-syntax\",\n severity: \"error\",\n source: \"docs-compiler\",\n file: file.path,\n message: `MDX failed to parse: ${file.parseError.message}`,\n line: file.parseError.line,\n column: file.parseError.column,\n },\n ];\n }\n\n const disables = collectDisables(\n file.frontmatter,\n file.frontmatterRaw,\n file.frontmatterStartLine,\n file.lines,\n );\n\n // Malformed disable directives are themselves frontmatter problems, and\n // always errors — a disable you can't read is worse than no disable.\n for (const problem of disables.problems) {\n out.push({\n code: \"nimbus/frontmatter-shape\",\n severity: \"error\",\n source: \"docs-compiler\",\n file: file.path,\n message: problem.message,\n line: problem.line,\n column: problem.column,\n });\n }\n\n for (const rule of RULES) {\n if (opts.only && rule.code !== opts.only) continue;\n const resolved = resolveRuleForCollection(\n rule.code,\n rules,\n collections,\n file.collection,\n );\n if (resolved.severity === \"off\") continue;\n const severity = resolved.severity as Severity;\n\n const reports: RuleReport[] = [];\n try {\n rule.run({\n file,\n options: resolved.options,\n site: opts.site,\n report: (report) => reports.push(report),\n });\n } catch (err) {\n // A rule that throws is a bug in the rule, not user content. Skip it\n // and keep linting — one bad rule shouldn't blind the user to the\n // other thirteen.\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `nimbus-docs: rule \\`${rule.code}\\` threw on ${file.path}: ${detail}\\n`,\n );\n continue;\n }\n\n for (const report of reports) {\n if (isDisabled(disables, rule.code, report.line)) continue;\n out.push({\n code: rule.code,\n severity,\n source: \"docs-compiler\",\n file: file.path,\n message: report.message,\n line: report.line,\n column: report.column,\n endLine: report.endLine,\n endColumn: report.endColumn,\n fix: report.fix,\n });\n }\n }\n\n out.sort(\n (a, b) =>\n a.line - b.line ||\n a.column - b.column ||\n a.code.localeCompare(b.code),\n );\n return out;\n}\n\n/** Lint a set of absolute file paths, reading + parsing each one. */\nexport function lintPaths(\n absPaths: string[],\n projectRoot: string,\n opts: LintOptions = {},\n): Diagnostic[] {\n const out: Diagnostic[] = [];\n for (const abs of absPaths) {\n if (opts.signal?.aborted) break;\n const rel = path.relative(projectRoot, abs);\n try {\n const source = fs.readFileSync(abs, \"utf8\");\n const parsed = parseSource(source, {\n path: rel,\n absPath: abs,\n collection: inferCollection(rel),\n });\n out.push(...lintFile(parsed, opts));\n } catch (err) {\n // I/O errors (file vanished mid-run, permission denied) shouldn't kill\n // the whole lint — skip the file with a clear message and continue.\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`nimbus-docs: skipped ${rel}: ${detail}\\n`);\n }\n }\n return out;\n}\n\nexport interface FixRunResult {\n /** Diagnostics that remain after fixing (i.e. those with no auto-fix). */\n diagnostics: Diagnostic[];\n /** Count of diagnostics whose fix was applied. */\n fixed: number;\n /** Count of files actually rewritten. */\n filesChanged: number;\n}\n\n/**\n * Hard cap on per-file fix passes — a runaway rule that keeps emitting\n * convergence-breaking fixes won't hang the CLI. 10 is well above any\n * realistic chain (the longest in practice is 2–3: a fix unmasks a\n * second-tier finding the first pass shadowed).\n */\nconst MAX_FIX_PASSES = 10;\n\n/**\n * Lint + apply auto-fixes in place. Each file is read, linted, fixed, and\n * (when content changed) atomically rewritten via tmp-file + rename — so a\n * crash or SIGINT mid-write can't truncate the user's content. We iterate\n * each file until the output stabilizes or `MAX_FIX_PASSES` is hit; this\n * picks up diagnostics that were skipped on pass 1 due to overlap with\n * another applied fix, plus diagnostics a fix on pass 1 unmasked.\n *\n * A diagnostic stays in the report when it wasn't *actually* applied —\n * which includes the advisory-only case (a rule emits a `fix` with no\n * `edits`, like the did-you-mean hint on `internal-link`) and the\n * skipped-overlap case after the convergence cap. Both are real,\n * unresolved issues; suppressing them just because the diagnostic carries\n * a `fix` field would silently hide broken links and other\n * un-auto-fixable problems.\n *\n * Files that fail to read, parse, or write are skipped with a stderr\n * message and the run continues — one bad file shouldn't leave the rest\n * of the working tree half-fixed.\n */\nexport function fixPaths(\n absPaths: string[],\n projectRoot: string,\n opts: LintOptions = {},\n): FixRunResult {\n let fixed = 0;\n let filesChanged = 0;\n const remaining: Diagnostic[] = [];\n\n for (const abs of absPaths) {\n if (opts.signal?.aborted) break;\n const rel = path.relative(projectRoot, abs);\n try {\n const original = fs.readFileSync(abs, \"utf8\");\n let current = original;\n let lastDiagnostics: Diagnostic[] = [];\n let lastApplied = new Set<Diagnostic>();\n\n for (let pass = 0; pass < MAX_FIX_PASSES; pass++) {\n const parsed = parseSource(current, {\n path: rel,\n absPath: abs,\n collection: inferCollection(rel),\n });\n const diagnostics = lintFile(parsed, opts);\n const result = applyFixes(current, diagnostics);\n fixed += result.fixed;\n lastDiagnostics = diagnostics;\n lastApplied = result.applied;\n if (result.output === current) break;\n current = result.output;\n }\n\n if (current !== original) {\n writeFileAtomicSync(abs, current);\n filesChanged++;\n }\n for (const d of lastDiagnostics) {\n if (!lastApplied.has(d)) remaining.push(d);\n }\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`nimbus-docs: skipped ${rel}: ${detail}\\n`);\n }\n }\n\n return { diagnostics: remaining, fixed, filesChanged };\n}\n\n/**\n * Write atomically: serialize to a sibling tmp file, fsync, rename over the\n * target. A crash mid-write leaves the original intact instead of a\n * truncated .mdx. The tmp file lives next to the target so the rename is\n * a same-filesystem atomic op.\n */\nfunction writeFileAtomicSync(abs: string, content: string): void {\n const tmp = `${abs}.nimbus-tmp-${process.pid}`;\n const fd = fs.openSync(tmp, \"w\");\n try {\n fs.writeFileSync(fd, content, \"utf8\");\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n try {\n fs.renameSync(tmp, abs);\n } catch (err) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // best-effort cleanup\n }\n throw err;\n }\n}\n\nexport function summarize(diagnostics: Diagnostic[], files: number): LintSummary {\n let errors = 0;\n let warnings = 0;\n for (const d of diagnostics) {\n if (d.severity === \"error\") errors++;\n else warnings++;\n }\n return { errors, warnings, total: diagnostics.length, files };\n}\n\n/** Infer the collection name from a `src/content/<name>/…` path. */\nfunction inferCollection(relPath: string): string | null {\n const match = relPath\n .replace(/\\\\/g, \"/\")\n .match(/(?:^|\\/)src\\/content\\/([^/]+)\\//);\n return match ? match[1]! : null;\n}\n\n/**\n * Return a new collections config with `code` removed from every per-\n * collection `rules` block. Used by `lintFile`'s `--rule` force-enable so\n * a per-collection \"off\" doesn't silently shadow the CLI flag — the\n * user explicitly asked to see this rule's findings.\n */\nfunction stripCodeFromCollections(\n collections: CollectionsConfig,\n code: AuthoringRuleCode,\n): CollectionsConfig {\n const out: CollectionsConfig = {};\n for (const [name, cfg] of Object.entries(collections)) {\n if (!cfg.rules || !(code in cfg.rules)) {\n out[name] = cfg;\n continue;\n }\n const { [code]: _stripped, ...remaining } = cfg.rules;\n out[name] = { ...cfg, rules: remaining };\n }\n return out;\n}\n","/**\n * File discovery for the CLI — walk the configured content directories for\n * `.mdx` files, skipping `node_modules` and dotfolders. Mirrors the walk\n * the MDX validator already uses, kept here so the lint CLI doesn't depend\n * on integration internals.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport function findMdxFiles(dirs: string[]): string[] {\n const out: string[] = [];\n for (const dir of dirs) walk(dir, out);\n out.sort();\n return out;\n}\n\nfunction walk(dir: string, out: string[]): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n walk(full, out);\n } else if (entry.isFile() && entry.name.endsWith(\".mdx\")) {\n out.push(full);\n }\n }\n}\n","/**\n * Diagnostic formatters: a pretty terminal form and a machine-readable\n * JSON form. The pretty form mirrors the spec —\n *\n * src/content/docs/getting-started.mdx:42:18 error nimbus/single-h1\n * more than one top-level \"#\" heading …\n *\n * The JSON form is the agent-shaped envelope, validated against the\n * committed `diagnostic.schema.json`.\n */\n\nimport type { Diagnostic } from \"./diagnostic.js\";\nimport type { LintSummary } from \"./engine.js\";\n\nconst COLORS = {\n reset: \"\u001b[0m\",\n dim: \"\u001b[2m\",\n red: \"\u001b[31m\",\n yellow: \"\u001b[33m\",\n green: \"\u001b[32m\",\n bold: \"\u001b[1m\",\n};\n\nexport interface FormatOptions {\n color: boolean;\n /** Suppress `warn`-severity diagnostics from the output. */\n quiet?: boolean;\n}\n\nexport function formatPretty(\n diagnostics: Diagnostic[],\n summary: LintSummary,\n opts: FormatOptions,\n): string {\n const paint = (code: string, text: string) =>\n opts.color ? `${code}${text}${COLORS.reset}` : text;\n\n const shown = opts.quiet\n ? diagnostics.filter((d) => d.severity === \"error\")\n : diagnostics;\n\n const lines: string[] = [];\n for (const d of shown) {\n const sev =\n d.severity === \"error\"\n ? paint(COLORS.red, \"error\")\n : paint(COLORS.yellow, \"warn\");\n const loc = paint(COLORS.dim, `${d.file}:${d.line}:${d.column}`);\n lines.push(`${loc} ${sev} ${paint(COLORS.dim, d.code)}`);\n lines.push(` ${d.message}`);\n if (d.fix && d.fix.description) {\n lines.push(` ${paint(COLORS.dim, `fix: ${d.fix.description}`)}`);\n }\n }\n\n if (summary.total === 0) {\n return paint(COLORS.green, `✓ ${summary.files} file(s) lint clean.`);\n }\n\n const tally = `${summary.errors} error(s), ${summary.warnings} warning(s) across ${summary.files} file(s)`;\n lines.push(\"\");\n lines.push(\n summary.errors > 0\n ? paint(COLORS.red, `✗ ${tally}`)\n : paint(COLORS.yellow, `${tally}`),\n );\n return lines.join(\"\\n\");\n}\n\nexport function formatJson(\n diagnostics: Diagnostic[],\n summary: LintSummary,\n): string {\n return JSON.stringify(\n {\n version: 1,\n summary: {\n errors: summary.errors,\n warnings: summary.warnings,\n total: summary.total,\n files: summary.files,\n },\n diagnostics,\n },\n null,\n 2,\n );\n}\n","/**\n * `nimbus-docs lint` — the authoring-quality verdict for MDX content.\n *\n * Walks the content directories, runs the registered rules, prints\n * diagnostics, and exits non-zero when any `error`-severity finding\n * survives. The build is never gated by this command — drafts that fail\n * lint still render under `astro dev`.\n *\n * Severity overrides live with the integration\n * (`nimbus(config, { rules })`), which materializes them to\n * `.nimbus/lint.json` at build/dev time; this command reads that file when\n * present and otherwise runs every authoring rule at its default. In-file\n * disables (`nimbusDisableRules`, inline comments) work with no config.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport {\n findMdxFiles,\n fixPaths,\n formatJson,\n formatPretty,\n IMPLEMENTED_CODES,\n isRuleCode,\n lintPaths,\n summarize,\n validateLintOptions,\n type CollectionsConfig,\n type Diagnostic,\n type RuleCode,\n type RulesConfig,\n} from \"../lint/index.js\";\n\nexport interface LintCliFlags {\n format?: string;\n quiet?: boolean;\n rule?: string;\n color?: boolean;\n fix?: boolean;\n}\n\nexport async function lintCommand(flags: LintCliFlags): Promise<void> {\n const cwd = process.cwd();\n const contentDir = path.join(cwd, \"src\", \"content\");\n\n if (flags.rule) {\n if (!isRuleCode(flags.rule)) {\n process.stderr.write(\n `Unknown rule code: \\`${flags.rule}\\`. See https://nimbus-docs.com/lint for the rule list.\\n`,\n );\n process.exit(1);\n }\n // `isRuleCode` accepts every registered code, including build validators\n // (which run inside `astro build`, not here) and planned codes that\n // don't have a rule module yet. Either case would silently exit clean\n // with zero coverage — a worse outcome than \"unknown rule\" for a\n // command users invoke specifically because they trust it to enforce\n // something.\n if (!IMPLEMENTED_CODES.has(flags.rule)) {\n process.stderr.write(\n `Rule \\`${flags.rule}\\` is not implemented by \\`nimbus-docs lint\\`. ` +\n `Build validators run inside \\`astro build\\`, not here; planned rules haven't shipped yet. ` +\n `Implemented authoring rules: ${[...IMPLEMENTED_CODES].sort().join(\", \")}.\\n`,\n );\n process.exit(1);\n }\n }\n\n const files = findMdxFiles([contentDir]);\n if (files.length === 0) {\n process.stderr.write(\n `No .mdx files found under ${path.relative(cwd, contentDir) || \".\"}. ` +\n \"Run from your project root.\\n\",\n );\n process.exit(0);\n }\n\n const { rules, collections, site } = loadMaterializedConfig(cwd);\n const opts = {\n rules,\n collections,\n site,\n // `flags.rule` already passed through validation that rejects build\n // validators; the cast here is the type-level reflection of that runtime\n // narrowing (LintOptions.only is `AuthoringRuleCode` because `--rule`\n // can't force-enable a build validator).\n only: flags.rule as import(\"../lint/diagnostic.js\").AuthoringRuleCode | undefined,\n };\n\n let diagnostics: Diagnostic[];\n let interrupted = false;\n if (flags.fix) {\n // Atomic writes already protect each file from SIGINT corruption — the\n // .tmp+rename in `fixPaths` either finishes or doesn't. The handler\n // here just stops the iteration after the in-progress file finishes\n // and gives the user a clean \"stopped after N files\" report instead\n // of a default Ctrl-C abort with partial output.\n const ac = new AbortController();\n const onSigint = () => {\n if (interrupted) {\n process.stderr.write(\"\\nnimbus-docs: forced exit.\\n\");\n process.exit(130);\n }\n interrupted = true;\n process.stderr.write(\n \"\\nnimbus-docs: interrupted — finishing current file, then stopping. Press Ctrl-C again to force.\\n\",\n );\n ac.abort();\n };\n process.on(\"SIGINT\", onSigint);\n try {\n const result = fixPaths(files, cwd, { ...opts, signal: ac.signal });\n if (result.fixed > 0) {\n process.stderr.write(\n `Fixed ${result.fixed} issue(s) across ${result.filesChanged} file(s).\\n`,\n );\n }\n if (interrupted) {\n process.stderr.write(\n `nimbus-docs: stopped early — ${result.filesChanged} file(s) changed before interrupt.\\n`,\n );\n }\n diagnostics = result.diagnostics.sort(\n (a, b) =>\n a.file.localeCompare(b.file) ||\n a.line - b.line ||\n a.column - b.column,\n );\n } finally {\n process.off(\"SIGINT\", onSigint);\n }\n } else {\n diagnostics = lintPaths(files, cwd, opts);\n }\n\n const summary = summarize(diagnostics, files.length);\n\n if (flags.format === \"json\") {\n process.stdout.write(formatJson(diagnostics, summary) + \"\\n\");\n } else {\n process.stdout.write(\n formatPretty(diagnostics, summary, {\n color: shouldUseColor(flags.color),\n quiet: flags.quiet,\n }) + \"\\n\",\n );\n }\n\n // Only `error`-severity findings fail the command. Warnings are advisory.\n // SIGINT during --fix exits 130 (standard interrupted exit code) so CI\n // can distinguish a clean run from a partial one.\n if (interrupted) process.exit(130);\n process.exit(summary.errors > 0 ? 1 : 0);\n}\n\n/**\n * Resolve whether to emit ANSI escapes, in standard CLI precedence:\n * 1. Explicit `--color` / `--no-color` flag.\n * 2. `FORCE_COLOR` env (Node ecosystem convention) — any non-empty,\n * non-zero value forces color on.\n * 3. `NO_COLOR` env (no-color.org) — any non-empty value forces color off.\n * 4. Auto-detect via `process.stdout.isTTY`.\n */\nfunction shouldUseColor(explicit: boolean | undefined): boolean {\n if (explicit !== undefined) return explicit;\n const force = process.env.FORCE_COLOR;\n if (force !== undefined && force !== \"\" && force !== \"0\") return true;\n if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== \"\") {\n return false;\n }\n return process.stdout.isTTY === true;\n}\n\ninterface MaterializedConfig {\n rules: RulesConfig;\n collections: CollectionsConfig;\n site?: string;\n}\n\n/**\n * Read the integration's materialized lint config from `.nimbus/lint.json`.\n * Returns empty defaults when the file is absent or unreadable (lint must\n * work before the first build).\n *\n * **Re-validates the parsed config** against `validateLintOptions`, the\n * same validator the integration ran at config-setup time. The materialized\n * file is normally machine-written, so failures here typically mean a\n * hand-edit or a stale schema. The CLI surfaces the validation error and\n * exits — silently ignoring a typo'd rule code in `lint.json` contradicts\n * the anti-silent-typo invariant the rest of the codebase enforces.\n */\nfunction loadMaterializedConfig(cwd: string): MaterializedConfig {\n const file = path.join(cwd, \".nimbus\", \"lint.json\");\n let raw: string;\n try {\n raw = fs.readFileSync(file, \"utf8\");\n } catch {\n return { rules: {}, collections: {} };\n }\n\n let parsed: { rules?: unknown; collections?: unknown; site?: unknown };\n try {\n parsed = JSON.parse(raw) as typeof parsed;\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `nimbus-docs: ${path.relative(cwd, file) || file} is not valid JSON — ${detail}. ` +\n \"Delete the file (it'll be regenerated by `astro build`) or fix the syntax.\\n\",\n );\n process.exit(1);\n }\n\n let validated;\n try {\n validated = validateLintOptions(\n { rules: parsed.rules, collections: parsed.collections },\n IMPLEMENTED_CODES,\n );\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `${detail}\\n\\n` +\n `This shape lives in ${path.relative(cwd, file) || file} — usually machine-written by the Nimbus integration at \\`astro build\\`. ` +\n \"If you've hand-edited it, fix or delete the file. Otherwise, re-run `astro build` to regenerate it.\\n\",\n );\n process.exit(1);\n }\n\n const site = typeof parsed.site === \"string\" ? parsed.site : undefined;\n return { rules: validated.rules, collections: validated.collections, site };\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 { lintCommand } from \"./lint.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 quiet: boolean;\n fix: boolean;\n type?: string;\n format?: string;\n rule?: string;\n color?: boolean;\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 lint Lint .mdx content for authoring-quality issues\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 --format <json> \\`lint\\`: machine-readable output\n --rule <nimbus/...> \\`lint\\`: run a single rule\n --fix \\`lint\\`: apply auto-fixes in place\n --quiet \\`lint\\`: errors only, suppress warnings\n --help, -h\n --version, -v\n\n Examples:\n nimbus-docs add dialog # component: resolve + install\n nimbus-docs add 404-page --print | claude # explicit pipe to claude\n nimbus-docs lint # pretty output, exit non-zero on error\n nimbus-docs lint --format=json # agent-readable diagnostics\n nimbus-docs lint --rule=nimbus/single-h1 # one rule\n`;\n\nasync function main(): Promise<void> {\n const args = mri(process.argv.slice(2), {\n boolean: [\"yes\", \"print\", \"help\", \"version\", \"quiet\", \"color\", \"fix\"],\n string: [\"type\", \"format\", \"rule\"],\n default: { color: undefined },\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 === \"lint\") {\n await lintCommand({\n format: args.format,\n quiet: args.quiet,\n rule: args.rule,\n color: args.color,\n fix: args.fix,\n });\n return;\n }\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 +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. `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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"}
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 // `tabs[index]!`: `activate(index)` is only called with validated indices.\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]!`: guard above proves the assertion.\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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"}