From ab588a2518e615050d7b5e0d80a28b8591abc4d8 Mon Sep 17 00:00:00 2001 From: Diogo Carneiro Date: Mon, 6 Jul 2026 11:01:44 +0100 Subject: [PATCH 1/4] feat: migrate from Starlight to Nimbus documentation framework Replace @astrojs/starlight with nimbus-docs as the documentation site framework. Add Tailwind CSS v4, Pagefind search, custom fonts (Inter, JetBrains Mono), and astro-icon/astro-og-canvas integrations. Convert splash page hero from Starlight components to custom Nimbus layout with Tailwind styling. Update content config to use Nimbus collections with custom schema fields. Add TypeScript path aliases and update all references from --- docs/.nimbus/lint.json | 9 + docs/.nimbus/routes.json | 50 + docs/astro.config.mjs | 136 -- docs/astro.config.ts | 94 ++ docs/package.json | 16 +- docs/postcss.config.mjs | 5 + docs/public/_headers | 4 +- docs/public/favicon.ico | Bin 0 -> 4286 bytes docs/public/fonts/Inter-Bold.ttf | Bin 0 -> 420428 bytes docs/src/components.ts | 25 + docs/src/components/AgentDirective.astro | 16 + docs/src/components/Header.astro | 101 ++ docs/src/components/Render.astro | 106 ++ docs/src/components/ui/aside/Aside.astro | 75 + docs/src/components/ui/aside/index.ts | 1 + docs/src/components/ui/badge/Badge.astro | 42 + docs/src/components/ui/badge/index.ts | 1 + docs/src/components/ui/banner/Banner.astro | 82 + .../src/components/ui/banner/banner.client.ts | 49 + docs/src/components/ui/banner/index.ts | 1 + .../ui/breadcrumbs/Breadcrumbs.astro | 84 + docs/src/components/ui/breadcrumbs/index.ts | 1 + docs/src/components/ui/button/Button.astro | 69 + docs/src/components/ui/button/index.ts | 13 + docs/src/components/ui/button/variants.ts | 65 + .../components/ui/card-grid/CardGrid.astro | 15 + docs/src/components/ui/card-grid/index.ts | 1 + docs/src/components/ui/card/Card.astro | 21 + docs/src/components/ui/card/index.ts | 1 + docs/src/components/ui/code/Code.astro | 38 + docs/src/components/ui/code/index.ts | 1 + .../ui/collapsible/Collapsible.astro | 25 + .../ui/collapsible/CollapsibleContent.astro | 27 + .../ui/collapsible/CollapsibleTrigger.astro | 21 + .../ui/collapsible/collapsible.client.ts | 22 + docs/src/components/ui/collapsible/index.ts | 3 + docs/src/components/ui/dialog/Dialog.astro | 53 + .../components/ui/dialog/DialogClose.astro | 35 + .../components/ui/dialog/DialogContent.astro | 21 + docs/src/components/ui/dialog/index.ts | 3 + .../components/ui/layer-card/LayerCard.astro | 23 + .../ui/layer-card/LayerCardContent.astro | 16 + .../ui/layer-card/LayerCardHeader.astro | 16 + docs/src/components/ui/layer-card/index.ts | 3 + .../ui/link-button/LinkButton.astro | 75 + docs/src/components/ui/link-button/index.ts | 1 + .../ui/package-managers/PackageManagers.astro | 123 ++ .../components/ui/package-managers/index.ts | 1 + .../package-managers.client.ts | 55 + .../ui/page-actions/PageActions.astro | 65 + docs/src/components/ui/page-actions/index.ts | 1 + .../ui/page-actions/page-actions.client.ts | 55 + .../components/ui/pagination/Pagination.astro | 36 + docs/src/components/ui/pagination/index.ts | 1 + .../components/ui/search/SearchDialog.astro | 112 ++ .../components/ui/search/SearchTrigger.astro | 40 + docs/src/components/ui/search/index.ts | 2 + .../ui/search/providers/pagefind.ts | 75 + .../src/components/ui/search/search.client.ts | 208 +++ docs/src/components/ui/sidebar/Sidebar.astro | 64 + .../components/ui/sidebar/SidebarFilter.astro | 20 + .../components/ui/sidebar/SidebarGroup.astro | 209 +++ .../components/ui/sidebar/SidebarLink.astro | 37 + docs/src/components/ui/sidebar/index.ts | 4 + .../components/ui/sidebar/sidebar.client.ts | 203 +++ docs/src/components/ui/steps/Step.astro | 26 + docs/src/components/ui/steps/Steps.astro | 115 ++ docs/src/components/ui/steps/index.ts | 2 + docs/src/components/ui/steps/steps.client.ts | 20 + docs/src/components/ui/tabs/TabItem.astro | 30 + docs/src/components/ui/tabs/Tabs.astro | 36 + docs/src/components/ui/tabs/TabsContent.astro | 24 + docs/src/components/ui/tabs/TabsList.astro | 26 + docs/src/components/ui/tabs/TabsTrigger.astro | 31 + docs/src/components/ui/tabs/index.ts | 5 + docs/src/components/ui/tabs/tabs.client.ts | 72 + .../ui/theme-toggle/ThemeToggle.astro | 32 + docs/src/components/ui/theme-toggle/index.ts | 1 + .../ui/theme-toggle/theme-toggle.client.ts | 31 + docs/src/components/ui/toc/TOC.astro | 95 ++ docs/src/components/ui/toc/index.ts | 1 + docs/src/components/ui/toc/toc.client.ts | 325 ++++ docs/src/content.config.ts | 19 +- docs/src/content/docs/cli/index.md | 2 + docs/src/content/docs/configuration/index.md | 2 + docs/src/content/docs/index.mdx | 51 +- docs/src/content/partials/example.mdx | 4 + docs/src/layouts/BaseLayout.astro | 118 ++ docs/src/layouts/DocsLayout.astro | 333 ++++ docs/src/lib/cn.ts | 7 + docs/src/pages/404.astro | 23 + docs/src/pages/[...slug].astro | 87 + docs/src/pages/[...slug]/index.md.ts | 70 + docs/src/pages/[section]/llms.txt.ts | 64 + docs/src/pages/llms.txt.ts | 41 + docs/src/pages/og.png.ts | 17 + docs/src/pages/og/[...slug].ts | 24 + docs/src/pages/og/_og-card-config.ts | 39 + docs/src/pages/robots.txt.ts | 11 + docs/src/styles/custom.css | 30 - docs/src/styles/globals.css | 489 ++++++ docs/src/styles/prose.css | 312 ++++ docs/tsconfig.json | 6 + docs/wrangler.jsonc | 2 +- pnpm-lock.yaml | 1425 ++++++++++++----- pnpm-workspace.yaml | 3 + 106 files changed, 6265 insertions(+), 563 deletions(-) create mode 100644 docs/.nimbus/lint.json create mode 100644 docs/.nimbus/routes.json delete mode 100644 docs/astro.config.mjs create mode 100644 docs/astro.config.ts create mode 100644 docs/postcss.config.mjs create mode 100644 docs/public/favicon.ico create mode 100644 docs/public/fonts/Inter-Bold.ttf create mode 100644 docs/src/components.ts create mode 100644 docs/src/components/AgentDirective.astro create mode 100644 docs/src/components/Header.astro create mode 100644 docs/src/components/Render.astro create mode 100644 docs/src/components/ui/aside/Aside.astro create mode 100644 docs/src/components/ui/aside/index.ts create mode 100644 docs/src/components/ui/badge/Badge.astro create mode 100644 docs/src/components/ui/badge/index.ts create mode 100644 docs/src/components/ui/banner/Banner.astro create mode 100644 docs/src/components/ui/banner/banner.client.ts create mode 100644 docs/src/components/ui/banner/index.ts create mode 100644 docs/src/components/ui/breadcrumbs/Breadcrumbs.astro create mode 100644 docs/src/components/ui/breadcrumbs/index.ts create mode 100644 docs/src/components/ui/button/Button.astro create mode 100644 docs/src/components/ui/button/index.ts create mode 100644 docs/src/components/ui/button/variants.ts create mode 100644 docs/src/components/ui/card-grid/CardGrid.astro create mode 100644 docs/src/components/ui/card-grid/index.ts create mode 100644 docs/src/components/ui/card/Card.astro create mode 100644 docs/src/components/ui/card/index.ts create mode 100644 docs/src/components/ui/code/Code.astro create mode 100644 docs/src/components/ui/code/index.ts create mode 100644 docs/src/components/ui/collapsible/Collapsible.astro create mode 100644 docs/src/components/ui/collapsible/CollapsibleContent.astro create mode 100644 docs/src/components/ui/collapsible/CollapsibleTrigger.astro create mode 100644 docs/src/components/ui/collapsible/collapsible.client.ts create mode 100644 docs/src/components/ui/collapsible/index.ts create mode 100644 docs/src/components/ui/dialog/Dialog.astro create mode 100644 docs/src/components/ui/dialog/DialogClose.astro create mode 100644 docs/src/components/ui/dialog/DialogContent.astro create mode 100644 docs/src/components/ui/dialog/index.ts create mode 100644 docs/src/components/ui/layer-card/LayerCard.astro create mode 100644 docs/src/components/ui/layer-card/LayerCardContent.astro create mode 100644 docs/src/components/ui/layer-card/LayerCardHeader.astro create mode 100644 docs/src/components/ui/layer-card/index.ts create mode 100644 docs/src/components/ui/link-button/LinkButton.astro create mode 100644 docs/src/components/ui/link-button/index.ts create mode 100644 docs/src/components/ui/package-managers/PackageManagers.astro create mode 100644 docs/src/components/ui/package-managers/index.ts create mode 100644 docs/src/components/ui/package-managers/package-managers.client.ts create mode 100644 docs/src/components/ui/page-actions/PageActions.astro create mode 100644 docs/src/components/ui/page-actions/index.ts create mode 100644 docs/src/components/ui/page-actions/page-actions.client.ts create mode 100644 docs/src/components/ui/pagination/Pagination.astro create mode 100644 docs/src/components/ui/pagination/index.ts create mode 100644 docs/src/components/ui/search/SearchDialog.astro create mode 100644 docs/src/components/ui/search/SearchTrigger.astro create mode 100644 docs/src/components/ui/search/index.ts create mode 100644 docs/src/components/ui/search/providers/pagefind.ts create mode 100644 docs/src/components/ui/search/search.client.ts create mode 100644 docs/src/components/ui/sidebar/Sidebar.astro create mode 100644 docs/src/components/ui/sidebar/SidebarFilter.astro create mode 100644 docs/src/components/ui/sidebar/SidebarGroup.astro create mode 100644 docs/src/components/ui/sidebar/SidebarLink.astro create mode 100644 docs/src/components/ui/sidebar/index.ts create mode 100644 docs/src/components/ui/sidebar/sidebar.client.ts create mode 100644 docs/src/components/ui/steps/Step.astro create mode 100644 docs/src/components/ui/steps/Steps.astro create mode 100644 docs/src/components/ui/steps/index.ts create mode 100644 docs/src/components/ui/steps/steps.client.ts create mode 100644 docs/src/components/ui/tabs/TabItem.astro create mode 100644 docs/src/components/ui/tabs/Tabs.astro create mode 100644 docs/src/components/ui/tabs/TabsContent.astro create mode 100644 docs/src/components/ui/tabs/TabsList.astro create mode 100644 docs/src/components/ui/tabs/TabsTrigger.astro create mode 100644 docs/src/components/ui/tabs/index.ts create mode 100644 docs/src/components/ui/tabs/tabs.client.ts create mode 100644 docs/src/components/ui/theme-toggle/ThemeToggle.astro create mode 100644 docs/src/components/ui/theme-toggle/index.ts create mode 100644 docs/src/components/ui/theme-toggle/theme-toggle.client.ts create mode 100644 docs/src/components/ui/toc/TOC.astro create mode 100644 docs/src/components/ui/toc/index.ts create mode 100644 docs/src/components/ui/toc/toc.client.ts create mode 100644 docs/src/content/partials/example.mdx create mode 100644 docs/src/layouts/BaseLayout.astro create mode 100644 docs/src/layouts/DocsLayout.astro create mode 100644 docs/src/lib/cn.ts create mode 100644 docs/src/pages/404.astro create mode 100644 docs/src/pages/[...slug].astro create mode 100644 docs/src/pages/[...slug]/index.md.ts create mode 100644 docs/src/pages/[section]/llms.txt.ts create mode 100644 docs/src/pages/llms.txt.ts create mode 100644 docs/src/pages/og.png.ts create mode 100644 docs/src/pages/og/[...slug].ts create mode 100644 docs/src/pages/og/_og-card-config.ts create mode 100644 docs/src/pages/robots.txt.ts delete mode 100644 docs/src/styles/custom.css create mode 100644 docs/src/styles/globals.css create mode 100644 docs/src/styles/prose.css diff --git a/docs/.nimbus/lint.json b/docs/.nimbus/lint.json new file mode 100644 index 0000000..e1f00b6 --- /dev/null +++ b/docs/.nimbus/lint.json @@ -0,0 +1,9 @@ +{ + "version": 1, + "rules": { + "nimbus/frontmatter-shape": "error", + "nimbus/internal-link": "error" + }, + "collections": {}, + "site": "https://polystella-docs.pcx-team.workers.dev" +} diff --git a/docs/.nimbus/routes.json b/docs/.nimbus/routes.json new file mode 100644 index 0000000..8471902 --- /dev/null +++ b/docs/.nimbus/routes.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "base": "/", + "knownRoutes": [ + "/", + "/404", + "/adapters/custom-loader", + "/adapters/markdown", + "/adapters/mdx", + "/adapters/toml", + "/cli", + "/cli/audit-mdx", + "/cli/check-ui", + "/cli/sync-ui", + "/cli/translate", + "/cli/translate-ui", + "/concepts/ai-marker", + "/concepts/glossaries", + "/concepts/how-it-works", + "/concepts/mode-boundary", + "/concepts/overrides", + "/concepts/r2-cache", + "/concepts/runtime-bridge", + "/configuration", + "/configuration/reference", + "/cookbook/locale-picker", + "/getting-started/install", + "/getting-started/mental-model", + "/getting-started/quick-start", + "/operations/branch-dispatch", + "/operations/ci", + "/operations/preview-isolation", + "/providers/anthropic", + "/providers/batching", + "/providers/model-selection", + "/providers/permanent-errors", + "/providers/workers-ai", + "/reference/breaking-changes", + "/reference/exports", + "/roadmap", + "/routing/configuration", + "/routing/shims", + "/runtime-api/explicit-imports", + "/runtime-api/locals", + "/runtime-api/middleware", + "/runtime-api/react-hooks", + "/troubleshooting/missing-translations" + ], + "opaqueNamespaces": [] +} diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs deleted file mode 100644 index bca382b..0000000 --- a/docs/astro.config.mjs +++ /dev/null @@ -1,136 +0,0 @@ -// @ts-check -import { defineConfig } from "astro/config"; -import starlight from "@astrojs/starlight"; - -/** - * Starlight configuration for the PolyStella docs site. - * - * Single-language (en-US) for v0.x. Dogfooding polystella's own - * translation pipeline on the docs is queued for a later release. - */ -export default defineConfig({ - site: "https://polystella-docs.pcx-team.workers.dev", - integrations: [ - starlight({ - title: "PolyStella", - description: "AI-driven content localization for Astro", - logo: { - src: "./src/assets/wordmark.svg", - replacesTitle: true, - }, - social: [ - { - icon: "github", - label: "GitHub", - href: "https://github.com/cloudflare/polystella", - }, - ], - editLink: { - baseUrl: "https://github.com/cloudflare/polystella/edit/main/docs/", - }, - sidebar: [ - { - label: "Getting started", - items: [ - { label: "Install", slug: "getting-started/install" }, - { label: "Quick start", slug: "getting-started/quick-start" }, - { label: "Mental model", slug: "getting-started/mental-model" }, - ], - }, - { - label: "Concepts", - items: [ - { label: "How it works", slug: "concepts/how-it-works" }, - { label: "R2 cache", slug: "concepts/r2-cache" }, - { label: "Glossaries", slug: "concepts/glossaries" }, - { label: "Overrides", slug: "concepts/overrides" }, - { label: "Mode boundary", slug: "concepts/mode-boundary" }, - { label: "Runtime bridge", slug: "concepts/runtime-bridge" }, - { label: "AI marker", slug: "concepts/ai-marker" }, - ], - }, - { - label: "Configuration", - items: [ - { label: "Overview", slug: "configuration" }, - { label: "Full reference", slug: "configuration/reference" }, - ], - }, - { - label: "Adapters", - items: [ - { label: "Markdown", slug: "adapters/markdown" }, - { label: "MDX", slug: "adapters/mdx" }, - { label: "TOML", slug: "adapters/toml" }, - { label: "Custom loader", slug: "adapters/custom-loader" }, - ], - }, - { - label: "Providers", - items: [ - { label: "Workers AI", slug: "providers/workers-ai" }, - { label: "Anthropic", slug: "providers/anthropic" }, - { label: "Model selection", slug: "providers/model-selection" }, - { label: "Batching", slug: "providers/batching" }, - { label: "Permanent errors", slug: "providers/permanent-errors" }, - ], - }, - { - label: "Routing", - items: [ - { label: "Standalone shims", slug: "routing/shims" }, - { label: "Route configuration", slug: "routing/configuration" }, - ], - }, - { - label: "Runtime API", - items: [ - { label: "Astro.locals", slug: "runtime-api/locals" }, - { label: "Middleware", slug: "runtime-api/middleware" }, - { label: "Explicit imports", slug: "runtime-api/explicit-imports" }, - { label: "React hooks", slug: "runtime-api/react-hooks" }, - ], - }, - { - label: "CLI", - items: [ - { label: "Overview", slug: "cli" }, - { label: "translate", slug: "cli/translate" }, - { label: "check-ui", slug: "cli/check-ui" }, - { label: "sync-ui", slug: "cli/sync-ui" }, - { label: "translate-ui", slug: "cli/translate-ui" }, - { label: "audit-mdx", slug: "cli/audit-mdx" }, - ], - }, - { - label: "Operations", - items: [ - { label: "CI / Workers Builds", slug: "operations/ci" }, - { label: "Branch dispatch", slug: "operations/branch-dispatch" }, - { label: "Preview isolation", slug: "operations/preview-isolation" }, - ], - }, - { - label: "Cookbook", - items: [{ autogenerate: { directory: "cookbook" } }], - }, - { - label: "Troubleshooting", - items: [{ autogenerate: { directory: "troubleshooting" } }], - }, - { - label: "Reference", - items: [ - { label: "Public exports", slug: "reference/exports" }, - { label: "Breaking changes", slug: "reference/breaking-changes" }, - ], - }, - { - label: "Roadmap", - slug: "roadmap", - }, - ], - customCss: ["./src/styles/custom.css"], - }), - ], -}); diff --git a/docs/astro.config.ts b/docs/astro.config.ts new file mode 100644 index 0000000..f6696c8 --- /dev/null +++ b/docs/astro.config.ts @@ -0,0 +1,94 @@ +import { defineConfig } from "astro/config"; +import icon from "astro-icon"; +import nimbus, { defineConfig as defineNimbusConfig } from "nimbus-docs"; + +const nimbusConfig = defineNimbusConfig({ + site: "https://polystella-docs.pcx-team.workers.dev", + title: "PolyStella", + description: "AI-driven content localization for Astro", + locale: "en", + github: "https://github.com/cloudflare/polystella", + editPattern: "https://github.com/cloudflare/polystella/edit/main/docs/{path}", + socialImageAlt: "PolyStella documentation preview", + sidebar: { + items: [ + { + label: "Getting started", + items: ["getting-started/install", "getting-started/quick-start", "getting-started/mental-model"], + }, + { + label: "Concepts", + items: [ + "concepts/how-it-works", + "concepts/r2-cache", + "concepts/glossaries", + "concepts/overrides", + "concepts/mode-boundary", + "concepts/runtime-bridge", + "concepts/ai-marker", + ], + }, + { + label: "Configuration", + items: ["configuration", "configuration/reference"], + }, + { + label: "Adapters", + items: ["adapters/markdown", "adapters/mdx", "adapters/toml", "adapters/custom-loader"], + }, + { + label: "Providers", + items: [ + "providers/workers-ai", + "providers/anthropic", + "providers/model-selection", + "providers/batching", + "providers/permanent-errors", + ], + }, + { + label: "Routing", + items: ["routing/shims", "routing/configuration"], + }, + { + label: "Runtime API", + items: ["runtime-api/locals", "runtime-api/middleware", "runtime-api/explicit-imports", "runtime-api/react-hooks"], + }, + { + label: "CLI", + items: ["cli", "cli/translate", "cli/check-ui", "cli/sync-ui", "cli/translate-ui", "cli/audit-mdx"], + }, + { + label: "Operations", + items: ["operations/ci", "operations/branch-dispatch", "operations/preview-isolation"], + }, + { label: "Cookbook", autogenerate: { directory: "cookbook" } }, + { + label: "Troubleshooting", + autogenerate: { directory: "troubleshooting" }, + }, + { + label: "Reference", + items: ["reference/exports", "reference/breaking-changes"], + }, + "roadmap", + ], + }, +}); + +export default defineConfig({ + output: "static", + prefetch: { + prefetchAll: true, + defaultStrategy: "hover", + }, + integrations: [ + icon(), + nimbus(nimbusConfig, { + rules: { + "nimbus/frontmatter-shape": "error", + "nimbus/internal-link": "error", + }, + }), + ], +}); diff --git a/docs/package.json b/docs/package.json index 73031e1..bb19bb2 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "type": "module", - "description": "Starlight-based documentation site for PolyStella.", + "description": "Nimbus-based documentation site for PolyStella.", "scripts": { "dev": "pnpm prebuild && astro dev", "start": "pnpm prebuild && astro dev", @@ -17,12 +17,24 @@ "deploy": "wrangler deploy" }, "dependencies": { - "@astrojs/starlight": "^0.41.3", + "@fontsource-variable/inter": "^5.2.8", + "@fontsource-variable/jetbrains-mono": "^5.2.8", + "@iconify-json/ph": "^1.2.0", "astro": "^7.0.0", + "astro-icon": "^1.1.0", + "astro-og-canvas": "^0.13.0", + "canvaskit-wasm": "^0.41.1", + "clsx": "^2.1.1", + "nimbus-docs": "^0.1.22", + "shiki": "^4.2.0", + "tailwind-merge": "^3.5.0", "sharp": "^0.35.3" }, "devDependencies": { "@astrojs/check": "^0.9.9", + "@tailwindcss/postcss": "^4.1.4", + "pagefind": "^1.5.2", + "tailwindcss": "^4.1.4", "@types/node": "^26.1.0", "tsx": "^4.19.0", "typescript": "^6.0.3", diff --git a/docs/postcss.config.mjs b/docs/postcss.config.mjs new file mode 100644 index 0000000..c2ddf74 --- /dev/null +++ b/docs/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/docs/public/_headers b/docs/public/_headers index a2fbdbb..ede26c5 100644 --- a/docs/public/_headers +++ b/docs/public/_headers @@ -4,8 +4,8 @@ # # Syntax: https://developers.cloudflare.com/workers/static-assets/headers/ # -# CSP is intentionally omitted: Starlight ships inline -# ` + + diff --git a/docs/src/components/ui/banner/banner.client.ts b/docs/src/components/ui/banner/banner.client.ts new file mode 100644 index 0000000..4ee2fda --- /dev/null +++ b/docs/src/components/ui/banner/banner.client.ts @@ -0,0 +1,49 @@ +/** + * Storage key: `nb-banner-dismissed-{id}`. Value is "0" for permanent, + * or a future timestamp (ms) for time-limited dismissal. + */ + +import { mount } from "nimbus-docs/client"; + +const KEY_PREFIX = "nb-banner-dismissed-"; + +function initBanner(banner: HTMLElement): () => void { + const id = banner.dataset.nbBannerDismiss; + if (!id) return () => {}; + + const key = `${KEY_PREFIX}${id}`; + + try { + const stored = localStorage.getItem(key); + if (stored) { + const expiry = Number(stored); + if (expiry === 0 || expiry > Date.now()) { + banner.remove(); + return () => {}; + } + localStorage.removeItem(key); + } + } catch { + // localStorage unavailable; show without persistence. + } + + const btn = banner.querySelector("[data-nb-banner-close]"); + if (!btn) return () => {}; + + function handleClick() { + const days = Number(banner.dataset.nbBannerDays) || 0; + const value = days > 0 ? String(Date.now() + days * 86400000) : "0"; + try { + localStorage.setItem(key, value); + } catch { + // localStorage unavailable; dismissal is session-only. + } + banner.remove(); + } + + btn.addEventListener("click", handleClick); + + return () => btn.removeEventListener("click", handleClick); +} + +mount("[data-nb-banner-dismiss]", initBanner); diff --git a/docs/src/components/ui/banner/index.ts b/docs/src/components/ui/banner/index.ts new file mode 100644 index 0000000..9b90dcf --- /dev/null +++ b/docs/src/components/ui/banner/index.ts @@ -0,0 +1 @@ +export { default as Banner } from "./Banner.astro"; diff --git a/docs/src/components/ui/breadcrumbs/Breadcrumbs.astro b/docs/src/components/ui/breadcrumbs/Breadcrumbs.astro new file mode 100644 index 0000000..53da5f3 --- /dev/null +++ b/docs/src/components/ui/breadcrumbs/Breadcrumbs.astro @@ -0,0 +1,84 @@ +--- +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; +import type { Breadcrumb } from "nimbus-docs/types"; + +interface Props extends HTMLAttributes<"nav"> { + items: Breadcrumb[]; + /** Max visible crumbs before collapsing middle items (default: 4) */ + maxVisible?: number; +} + +const { items, maxVisible = 4, class: className, ...attrs } = Astro.props; + +// Determine if we need to collapse the middle +const shouldCollapse = items.length > maxVisible; +// When collapsed: Home + first segment + ... + last 2 (parent + current) +const headCount = 2; // Home + first segment +const tailCount = 2; // parent + current page +const headItems = shouldCollapse ? items.slice(0, headCount) : items; +const collapsedItems = shouldCollapse ? items.slice(headCount, items.length - tailCount) : []; +const tailItems = shouldCollapse ? items.slice(items.length - tailCount) : []; +--- + +{items.length > 1 && ( + +)} diff --git a/docs/src/components/ui/breadcrumbs/index.ts b/docs/src/components/ui/breadcrumbs/index.ts new file mode 100644 index 0000000..734e208 --- /dev/null +++ b/docs/src/components/ui/breadcrumbs/index.ts @@ -0,0 +1 @@ +export { default as Breadcrumbs } from "./Breadcrumbs.astro"; diff --git a/docs/src/components/ui/button/Button.astro b/docs/src/components/ui/button/Button.astro new file mode 100644 index 0000000..4503785 --- /dev/null +++ b/docs/src/components/ui/button/Button.astro @@ -0,0 +1,69 @@ +--- +/** + * Button — primary action trigger. + * + * + * + * + * diff --git a/docs/src/components/ui/button/index.ts b/docs/src/components/ui/button/index.ts new file mode 100644 index 0000000..32440d6 --- /dev/null +++ b/docs/src/components/ui/button/index.ts @@ -0,0 +1,13 @@ +export { default as Button } from "./Button.astro"; +export { + buttonVariants, + buttonBase, + buttonVariantClasses, + buttonSizeText, + buttonSizeCompact, + buttonIconSize, + type ButtonVariant, + type ButtonSize, + type ButtonShape, + type ButtonVariantsOptions, +} from "./variants"; diff --git a/docs/src/components/ui/button/variants.ts b/docs/src/components/ui/button/variants.ts new file mode 100644 index 0000000..f98c1c6 --- /dev/null +++ b/docs/src/components/ui/button/variants.ts @@ -0,0 +1,65 @@ +/** + * Shared button styling — the single source of truth for both diff --git a/docs/src/components/ui/collapsible/collapsible.client.ts b/docs/src/components/ui/collapsible/collapsible.client.ts new file mode 100644 index 0000000..9e93213 --- /dev/null +++ b/docs/src/components/ui/collapsible/collapsible.client.ts @@ -0,0 +1,22 @@ +/** Wires Collapsible via the disclosure module. */ + +import { mount, makeDisclosure } from "nimbus-docs/client"; + +function initCollapsible(root: HTMLElement): () => void { + const trigger = root.querySelector("[data-nb-collapsible-trigger]"); + const content = root.querySelector("[data-nb-collapsible-content]"); + + if (!trigger || !content) return () => {}; + + const defaultOpen = root.dataset.nbDefaultOpen === "true"; + + const disclosure = makeDisclosure({ + trigger, + content, + defaultOpen, + }); + + return () => disclosure.destroy(); +} + +mount("[data-nb-collapsible]", initCollapsible); diff --git a/docs/src/components/ui/collapsible/index.ts b/docs/src/components/ui/collapsible/index.ts new file mode 100644 index 0000000..a4c4221 --- /dev/null +++ b/docs/src/components/ui/collapsible/index.ts @@ -0,0 +1,3 @@ +export { default as Collapsible } from "./Collapsible.astro"; +export { default as CollapsibleTrigger } from "./CollapsibleTrigger.astro"; +export { default as CollapsibleContent } from "./CollapsibleContent.astro"; diff --git a/docs/src/components/ui/dialog/Dialog.astro b/docs/src/components/ui/dialog/Dialog.astro new file mode 100644 index 0000000..24e3309 --- /dev/null +++ b/docs/src/components/ui/dialog/Dialog.astro @@ -0,0 +1,53 @@ +--- +/** + * Dialog — modal overlay built on the native . + * + * Open with `el.showModal()`, close with `el.close()` or Escape. + * Scroll-lock and backdrop-click-to-close are handled automatically. + * + * + * + *
Are you sure?
+ * Cancel + *
+ *
+ */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"dialog"> {} + +const { class: className, ...attrs } = Astro.props; +--- + + + + + + diff --git a/docs/src/components/ui/dialog/DialogClose.astro b/docs/src/components/ui/dialog/DialogClose.astro new file mode 100644 index 0000000..bc6e81f --- /dev/null +++ b/docs/src/components/ui/dialog/DialogClose.astro @@ -0,0 +1,35 @@ +--- +/** + * DialogClose — button that closes the nearest ancestor . + * Consumer provides the visual (icon, text, kbd hint) via default slot. + */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"button"> {} + +const { class: className, ...attrs } = Astro.props; +--- + + + + diff --git a/docs/src/components/ui/dialog/DialogContent.astro b/docs/src/components/ui/dialog/DialogContent.astro new file mode 100644 index 0000000..462448a --- /dev/null +++ b/docs/src/components/ui/dialog/DialogContent.astro @@ -0,0 +1,21 @@ +--- +/** + * DialogContent — inner frame of a Dialog. Centered, constrained, styled. + * Consumer controls max-width/height via class override. + */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> {} + +const { class: className, ...attrs } = Astro.props; +--- + +
+
+ +
+
diff --git a/docs/src/components/ui/dialog/index.ts b/docs/src/components/ui/dialog/index.ts new file mode 100644 index 0000000..fe7fb64 --- /dev/null +++ b/docs/src/components/ui/dialog/index.ts @@ -0,0 +1,3 @@ +export { default as Dialog } from "./Dialog.astro"; +export { default as DialogContent } from "./DialogContent.astro"; +export { default as DialogClose } from "./DialogClose.astro"; diff --git a/docs/src/components/ui/layer-card/LayerCard.astro b/docs/src/components/ui/layer-card/LayerCard.astro new file mode 100644 index 0000000..8473c7b --- /dev/null +++ b/docs/src/components/ui/layer-card/LayerCard.astro @@ -0,0 +1,23 @@ +--- +/** + * LayerCard — two-layer card (recessed header + raised content). + * + * + * Title or tabs + * Main content + * + */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> {} + +const { class: className, ...attrs } = Astro.props; +--- + +
+ +
diff --git a/docs/src/components/ui/layer-card/LayerCardContent.astro b/docs/src/components/ui/layer-card/LayerCardContent.astro new file mode 100644 index 0000000..12bdaea --- /dev/null +++ b/docs/src/components/ui/layer-card/LayerCardContent.astro @@ -0,0 +1,16 @@ +--- +/** LayerCardContent — raised content layer of a LayerCard. */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> {} + +const { class: className, ...attrs } = Astro.props; +--- + +
+ +
diff --git a/docs/src/components/ui/layer-card/LayerCardHeader.astro b/docs/src/components/ui/layer-card/LayerCardHeader.astro new file mode 100644 index 0000000..b9be404 --- /dev/null +++ b/docs/src/components/ui/layer-card/LayerCardHeader.astro @@ -0,0 +1,16 @@ +--- +/** LayerCardHeader — recessed header layer of a LayerCard. */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> {} + +const { class: className, ...attrs } = Astro.props; +--- + +
+ +
diff --git a/docs/src/components/ui/layer-card/index.ts b/docs/src/components/ui/layer-card/index.ts new file mode 100644 index 0000000..dd347fe --- /dev/null +++ b/docs/src/components/ui/layer-card/index.ts @@ -0,0 +1,3 @@ +export { default as LayerCard } from "./LayerCard.astro"; +export { default as LayerCardHeader } from "./LayerCardHeader.astro"; +export { default as LayerCardContent } from "./LayerCardContent.astro"; diff --git a/docs/src/components/ui/link-button/LinkButton.astro b/docs/src/components/ui/link-button/LinkButton.astro new file mode 100644 index 0000000..e1eb918 --- /dev/null +++ b/docs/src/components/ui/link-button/LinkButton.astro @@ -0,0 +1,75 @@ +--- +/** + * LinkButton — an anchor styled as a button. + * + * Get started + * Read the docs + * Learn more + * + * + * The `icon` prop appends a right-caret that nudges on hover. + * + * Styling is delegated to the shared `ui/button/variants`, so LinkButton and + * Button stay visually identical. The original props are preserved (used by + * MDX): the LinkButton-only aliases map onto the shared vocabulary — + * `minimal → ghost`, `md → base`. + */ +import { cn } from "@/lib/cn"; +import { Icon } from "astro-icon/components"; +import type { HTMLAttributes } from "astro/types"; +import { + buttonVariants, + buttonIconSize, + type ButtonVariant, + type ButtonSize, + type ButtonShape, +} from "../button/variants"; + +interface Props extends HTMLAttributes<"a"> { + href: string; + /** Original `primary | secondary | minimal`, plus Button's variants. */ + variant?: ButtonVariant | "minimal"; + /** Original `sm | md | lg`, plus Button's `xs | base`. */ + size?: ButtonSize | "md"; + /** `base` (default) · `square` · `circle` (icon-only — pass `aria-label`). */ + shape?: ButtonShape; + /** Append a caret-right that nudges on hover. */ + icon?: boolean; +} + +const { + href, + variant = "primary", + size = "md", + shape = "base", + icon = false, + class: className, + ...attrs +} = Astro.props; + +// Map the LinkButton-only aliases onto the shared Button vocabulary. +const resolvedVariant: ButtonVariant = variant === "minimal" ? "ghost" : variant; +const resolvedSize: ButtonSize = size === "md" ? "base" : size; +--- + + + + { + icon && ( + + ) + } + diff --git a/docs/src/components/ui/link-button/index.ts b/docs/src/components/ui/link-button/index.ts new file mode 100644 index 0000000..71411b5 --- /dev/null +++ b/docs/src/components/ui/link-button/index.ts @@ -0,0 +1 @@ +export { default as LinkButton } from "./LinkButton.astro"; diff --git a/docs/src/components/ui/package-managers/PackageManagers.astro b/docs/src/components/ui/package-managers/PackageManagers.astro new file mode 100644 index 0000000..4e203af --- /dev/null +++ b/docs/src/components/ui/package-managers/PackageManagers.astro @@ -0,0 +1,123 @@ +--- +/** + * PackageManagers — code block with a tab per package manager (npm, + * pnpm, yarn, bun). Selection syncs across instances via sessionStorage; + * an inline custom element restores the saved tab before paint. + */ +import { createHash } from "node:crypto"; +import { Icon } from "astro-icon/components"; +import { getTabs } from "nimbus-docs/lib/pkgm"; +import type { CommandType, CommandOptions } from "nimbus-docs/lib/pkgm"; +import { LayerCard, LayerCardHeader } from "@/components/ui/layer-card"; + +interface Props extends CommandOptions { + pkg?: string; + type?: CommandType; +} + +const { pkg, type = "add", args, dev, comment } = Astro.props; +const tabs = getTabs(type, pkg, { args, dev, comment }); +// Deterministic ID — hash the props so two builds with identical content +// produce identical IDs. Required for incremental builds: a per-render +// `crypto.randomUUID()` would make warm output differ from cold. +// Two instances with identical props on the same page would collide; in +// practice this is a rare authoring pattern (same package + type + args). +const uid = + "pm-" + + createHash("sha256") + .update(JSON.stringify({ pkg, type, args, dev, comment })) + .digest("hex") + .slice(0, 12); +--- + + + +
+ + + {tabs.map((tab, i) => ( + + ))} + + + {tabs.map((tab, i) => { + const cmdLines = tab.cmd.split("\n"); + const commentPrefix = cmdLines.length > 1 ? cmdLines.slice(0, -1).join("\n") + "\n" : ""; + const codeLine = cmdLines[cmdLines.length - 1]; + const spaceIdx = codeLine.indexOf(" "); + const codeFirst = spaceIdx === -1 ? codeLine : codeLine.slice(0, spaceIdx); + const codeRest = spaceIdx === -1 ? "" : codeLine.slice(spaceIdx); + return ( + + ); + })} + + + + +
+ + diff --git a/docs/src/components/ui/package-managers/index.ts b/docs/src/components/ui/package-managers/index.ts new file mode 100644 index 0000000..248021d --- /dev/null +++ b/docs/src/components/ui/package-managers/index.ts @@ -0,0 +1 @@ +export { default as PackageManagers } from "./PackageManagers.astro"; diff --git a/docs/src/components/ui/package-managers/package-managers.client.ts b/docs/src/components/ui/package-managers/package-managers.client.ts new file mode 100644 index 0000000..a7c1152 --- /dev/null +++ b/docs/src/components/ui/package-managers/package-managers.client.ts @@ -0,0 +1,55 @@ +/** + * Sync key `ui-pm-tab` (sessionStorage) is shared with the + * `` early-paint element to avoid flash across navigations. + */ + +import { mount, initTabs } from "nimbus-docs/client"; + +function cloneIcon(tpl: HTMLTemplateElement | null): Node { + return tpl ? tpl.content.cloneNode(true) : document.createTextNode(""); +} + +function initPackageManager(container: HTMLElement): () => void { + const copyTpl = container.querySelector("[data-nb-pm-icon-copy]"); + const checkTpl = container.querySelector("[data-nb-pm-icon-check]"); + + const tabs = initTabs({ + container, + tabSelector: "[data-nb-pm-tab]", + panelSelector: "[data-nb-pm-panel]", + rovingTabindex: true, + sync: { key: "ui-pm-tab", storage: "session" }, + }); + + const copyHandlers: Array<{ btn: HTMLButtonElement; handler: () => void; timer?: number }> = []; + + container.querySelectorAll("[data-nb-pm-copy]").forEach((btn) => { + const handlerInfo: { btn: HTMLButtonElement; handler: () => void; timer?: number } = { + btn, + handler: async () => { + try { + await navigator.clipboard.writeText(btn.dataset.nbCommand ?? ""); + } catch { + return; + } + btn.replaceChildren(cloneIcon(checkTpl)); + if (handlerInfo.timer) window.clearTimeout(handlerInfo.timer); + handlerInfo.timer = window.setTimeout(() => { + btn.replaceChildren(cloneIcon(copyTpl)); + }, 1500); + }, + }; + btn.addEventListener("click", handlerInfo.handler); + copyHandlers.push(handlerInfo); + }); + + return () => { + tabs.destroy(); + copyHandlers.forEach(({ btn, handler, timer }) => { + btn.removeEventListener("click", handler); + if (timer) window.clearTimeout(timer); + }); + }; +} + +mount("[data-nb-pm]", initPackageManager); diff --git a/docs/src/components/ui/page-actions/PageActions.astro b/docs/src/components/ui/page-actions/PageActions.astro new file mode 100644 index 0000000..5c9a2bf --- /dev/null +++ b/docs/src/components/ui/page-actions/PageActions.astro @@ -0,0 +1,65 @@ +--- +import { Icon } from "astro-icon/components"; +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> { + markdownUrl?: string; + lastUpdated?: Date; +} + +const { markdownUrl, lastUpdated, class: className, ...attrs } = Astro.props; + +const baseBtn = + "inline-flex cursor-pointer items-center gap-1.5 rounded-md bg-transparent px-2 py-1 text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2"; + +const formattedDate = lastUpdated + ? new Intl.DateTimeFormat(undefined, { year: "numeric", month: "short", day: "numeric" }).format(lastUpdated) + : null; +--- + +{(markdownUrl || lastUpdated) && ( +
+ {lastUpdated && ( + + + Updated + + )} + + {markdownUrl && lastUpdated && ( + + )} + + {markdownUrl && ( + <> + + + + + + + View as Markdown + + + )} +
+)} + + diff --git a/docs/src/components/ui/page-actions/index.ts b/docs/src/components/ui/page-actions/index.ts new file mode 100644 index 0000000..3d7bcd1 --- /dev/null +++ b/docs/src/components/ui/page-actions/index.ts @@ -0,0 +1 @@ +export { default as PageActions } from "./PageActions.astro"; diff --git a/docs/src/components/ui/page-actions/page-actions.client.ts b/docs/src/components/ui/page-actions/page-actions.client.ts new file mode 100644 index 0000000..56c9d59 --- /dev/null +++ b/docs/src/components/ui/page-actions/page-actions.client.ts @@ -0,0 +1,55 @@ +import { mount } from "nimbus-docs/client"; + +function initPageActions(root: HTMLElement): () => void { + const copyBtn = root.querySelector("[data-nb-page-actions-copy]"); + const copyIcon = root.querySelector("[data-nb-page-actions-copy-icon]"); + const checkIcon = root.querySelector("[data-nb-page-actions-check-icon]"); + const label = root.querySelector("[data-nb-page-actions-label]"); + const mdUrl = root.dataset.mdUrl; + + if (!copyBtn || !mdUrl) return () => {}; + const markdownUrl = mdUrl; + + let resetTimer: number | undefined; + + function showState(state: "copied" | "error") { + if (!copyIcon || !checkIcon || !label) return; + if (state === "copied") { + copyIcon.classList.add("hidden"); + checkIcon.classList.remove("hidden"); + label.textContent = "Copied"; + } else { + label.textContent = "Couldn't copy"; + } + if (resetTimer) window.clearTimeout(resetTimer); + resetTimer = window.setTimeout(() => { + copyIcon.classList.remove("hidden"); + checkIcon.classList.add("hidden"); + label.textContent = "Copy page"; + }, 1500); + } + + async function handleCopyPage() { + try { + const res = await fetch(markdownUrl); + if (!res.ok) { + showState("error"); + return; + } + const text = await res.text(); + await navigator.clipboard.writeText(text); + showState("copied"); + } catch { + showState("error"); + } + } + + copyBtn.addEventListener("click", handleCopyPage); + + return () => { + if (resetTimer) window.clearTimeout(resetTimer); + copyBtn.removeEventListener("click", handleCopyPage); + }; +} + +mount("[data-nb-page-actions]", initPageActions); diff --git a/docs/src/components/ui/pagination/Pagination.astro b/docs/src/components/ui/pagination/Pagination.astro new file mode 100644 index 0000000..6768078 --- /dev/null +++ b/docs/src/components/ui/pagination/Pagination.astro @@ -0,0 +1,36 @@ +--- +import { Icon } from "astro-icon/components"; +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; +import type { PrevNext } from "nimbus-docs/types"; + +interface Props extends HTMLAttributes<"nav"> { + prevNext: PrevNext; +} + +const { prevNext, class: className, ...attrs } = Astro.props; +const { prev, next } = prevNext; +--- + +{(prev || next) && ( + +)} diff --git a/docs/src/components/ui/pagination/index.ts b/docs/src/components/ui/pagination/index.ts new file mode 100644 index 0000000..8972ed7 --- /dev/null +++ b/docs/src/components/ui/pagination/index.ts @@ -0,0 +1 @@ +export { default as Pagination } from "./Pagination.astro"; diff --git a/docs/src/components/ui/search/SearchDialog.astro b/docs/src/components/ui/search/SearchDialog.astro new file mode 100644 index 0000000..1dd0847 --- /dev/null +++ b/docs/src/components/ui/search/SearchDialog.astro @@ -0,0 +1,112 @@ +--- +import { Icon } from "astro-icon/components"; +import { Dialog, DialogClose, DialogContent } from "@/components/ui/dialog"; +--- + + + +
+
+ + + + Esc + +
+ +
+

+ Type to search… +

+
+
+ +
+ ↑↓ navigate + ↵ select + Esc close +
+
+
+ + diff --git a/docs/src/components/ui/search/SearchTrigger.astro b/docs/src/components/ui/search/SearchTrigger.astro new file mode 100644 index 0000000..4126e75 --- /dev/null +++ b/docs/src/components/ui/search/SearchTrigger.astro @@ -0,0 +1,40 @@ +--- +/** Button that opens the search dialog. Cmd+K on macOS, Ctrl+K elsewhere. */ +import { Icon } from "astro-icon/components"; +--- + + + + diff --git a/docs/src/components/ui/search/index.ts b/docs/src/components/ui/search/index.ts new file mode 100644 index 0000000..9f7c86a --- /dev/null +++ b/docs/src/components/ui/search/index.ts @@ -0,0 +1,2 @@ +export { default as SearchDialog } from "./SearchDialog.astro"; +export { default as SearchTrigger } from "./SearchTrigger.astro"; diff --git a/docs/src/components/ui/search/providers/pagefind.ts b/docs/src/components/ui/search/providers/pagefind.ts new file mode 100644 index 0000000..6a8d8e9 --- /dev/null +++ b/docs/src/components/ui/search/providers/pagefind.ts @@ -0,0 +1,75 @@ +import type { SearchProvider, SearchResult } from "nimbus-docs/types"; +import { config } from "virtual:nimbus/config"; + +interface PagefindSubResult { + title?: string; + url?: string; +} + +interface PagefindResultData { + url: string; + excerpt?: string; + meta?: { title?: string }; + sub_results?: PagefindSubResult[]; +} + +interface PagefindSearchResponse { + results: Array<{ data(): Promise }>; +} + +interface PagefindFilters { + [key: string]: string | string[] | { none?: string | string[]; any?: string | string[] }; +} + +interface PagefindApi { + init(): Promise; + search(query: string, options?: { filters?: PagefindFilters }): Promise; +} + +let pagefind: PagefindApi | undefined; + +/** + * Default Pagefind filters applied to every search. + * + * Versioning: when the site has a `versions.deprecated` list, the + * layout emits `data-pagefind-filter="status:deprecated"` on every + * deprecated-version page. Search defaults to excluding those results + * (readers searching for "auth" want the current version's auth page, + * not the deprecated one). Future UI work can expose a "include + * deprecated" toggle; for now the default is current + non-deprecated. + * + * Versions are still searchable individually — readers on a v0 page + * who explicitly search from there can opt the UI into a version-scoped + * filter. The default exclusion is just for the top-level search. + * + * Computed at module-import time so we don't pay the config lookup on + * every keystroke. + */ +const defaultFilters: PagefindFilters | undefined = + config.versions && config.versions.deprecated && config.versions.deprecated.length > 0 ? { status: { none: "deprecated" } } : undefined; + +export const provider: SearchProvider = { + async init() { + if (pagefind) return; + const baseUrl = new URL(import.meta.env.BASE_URL ?? "/", window.location.origin); + const pagefindUrl = new URL("pagefind/pagefind.js", baseUrl); + pagefind = (await import(/* @vite-ignore */ pagefindUrl.href)) as PagefindApi; + await pagefind.init(); + }, + + async search(query) { + if (!pagefind) await this.init?.(); + if (!pagefind) return []; + + const search = await pagefind.search(query, defaultFilters ? { filters: defaultFilters } : undefined); + const results = await Promise.all(search.results.slice(0, 10).map((result) => result.data())); + return results.map((result): SearchResult => ({ + title: result.meta?.title ?? "Untitled", + url: result.url, + snippet: result.excerpt, + subResults: result.sub_results + ?.filter((sub): sub is Required => Boolean(sub.title && sub.url)) + .map((sub) => ({ title: sub.title, url: sub.url })), + })); + }, +}; diff --git a/docs/src/components/ui/search/search.client.ts b/docs/src/components/ui/search/search.client.ts new file mode 100644 index 0000000..96f76f5 --- /dev/null +++ b/docs/src/components/ui/search/search.client.ts @@ -0,0 +1,208 @@ +import type { SearchProvider, SearchResult } from "nimbus-docs/types"; + +export interface SearchConfig { + input: HTMLInputElement; + resultsContainer: HTMLElement; + emptyState: HTMLElement; + provider: SearchProvider; + onNavigate?: () => void; +} + +export interface SearchInstance { + reset(): Promise; + destroy(): void; +} + +export function initSearch(config: SearchConfig): SearchInstance { + const { input, resultsContainer, emptyState, provider, onNavigate } = config; + + let initialized = false; + let activeIndex = -1; + let resultIdCounter = 0; + let debounceTimer: ReturnType | undefined; + let activeController: AbortController | undefined; + + function getOptions(): HTMLElement[] { + return Array.from(resultsContainer.querySelectorAll("[role='option']")); + } + + function updateActive(newIndex: number): void { + const options = getOptions(); + if (options.length === 0) { + activeIndex = -1; + input.removeAttribute("aria-activedescendant"); + return; + } + activeIndex = Math.max(-1, Math.min(newIndex, options.length - 1)); + options.forEach((option, index) => { + if (index === activeIndex) { + option.setAttribute("data-highlighted", ""); + option.scrollIntoView({ block: "nearest" }); + input.setAttribute("aria-activedescendant", option.id); + } else { + option.removeAttribute("data-highlighted"); + } + }); + if (activeIndex < 0) input.removeAttribute("aria-activedescendant"); + } + + function clearResults(): void { + for (const result of resultsContainer.querySelectorAll("[role='option']")) result.remove(); + input.setAttribute("aria-expanded", "false"); + input.removeAttribute("aria-activedescendant"); + } + + function resultLink(title: string, href: string, className: string): HTMLAnchorElement { + const link = document.createElement("a"); + link.href = href; + link.className = className; + link.textContent = title; + link.addEventListener("click", () => onNavigate?.()); + return link; + } + + function buildResult(result: SearchResult): HTMLElement { + const option = document.createElement("div"); + option.id = `search-result-${resultIdCounter++}`; + option.setAttribute("role", "option"); + option.className = + "rounded-lg px-2 py-2 transition-colors cursor-pointer hover:bg-accent focus-within:bg-accent data-[highlighted]:bg-accent"; + + const link = resultLink( + result.title, + result.url, + "block truncate text-sm font-medium text-foreground no-underline focus-visible:outline-none", + ); + option.appendChild(link); + + if (result.snippet) { + const snippet = document.createElement("p"); + snippet.className = "mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground"; + snippet.innerHTML = result.snippet; + option.appendChild(snippet); + } + + if (result.subResults?.length) { + const subList = document.createElement("div"); + subList.className = "mt-2 border-l border-border pl-3"; + for (const sub of result.subResults.slice(0, 3)) { + subList.appendChild( + resultLink(sub.title, sub.url, "block truncate py-0.5 text-xs text-muted-foreground no-underline hover:text-foreground"), + ); + } + option.appendChild(subList); + } + + option.addEventListener("click", (event) => { + if ((event.target as Element | null)?.closest("a")) return; + link.click(); + }); + + return option; + } + + async function ensureInitialized(): Promise { + if (initialized) return true; + try { + await provider.init?.(); + initialized = true; + return true; + } catch { + emptyState.textContent = "Search is available after a production build."; + return false; + } + } + + async function runSearch(query: string): Promise { + activeController?.abort(); + activeController = new AbortController(); + const signal = activeController.signal; + + emptyState.style.display = ""; + emptyState.textContent = "Searching…"; + clearResults(); + + if (!(await ensureInitialized()) || signal.aborted) return; + + try { + const results = await provider.search(query, { signal }); + if (signal.aborted) return; + + clearResults(); + activeIndex = -1; + + if (results.length === 0) { + emptyState.style.display = ""; + emptyState.textContent = "No results found."; + return; + } + + emptyState.style.display = "none"; + input.setAttribute("aria-expanded", "true"); + for (const result of results) resultsContainer.appendChild(buildResult(result)); + } catch { + if (signal.aborted) return; + clearResults(); + emptyState.style.display = ""; + emptyState.textContent = "Search is temporarily unavailable."; + } + } + + function handleInput(): void { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + const query = input.value.trim(); + if (!query) { + activeController?.abort(); + clearResults(); + emptyState.style.display = ""; + emptyState.textContent = "Type to search…"; + return; + } + void runSearch(query); + }, 150); + } + + function handleKeydown(event: KeyboardEvent): void { + const options = getOptions(); + if (event.key === "ArrowDown") { + event.preventDefault(); + updateActive(activeIndex + 1); + } else if (event.key === "ArrowUp") { + event.preventDefault(); + updateActive(activeIndex - 1); + } else if (event.key === "Home") { + event.preventDefault(); + updateActive(0); + } else if (event.key === "End") { + event.preventDefault(); + updateActive(options.length - 1); + } else if (event.key === "Enter" && activeIndex >= 0) { + event.preventDefault(); + options[activeIndex]?.querySelector("a")?.click(); + } + } + + input.addEventListener("input", handleInput); + input.closest("dialog")?.addEventListener("keydown", handleKeydown); + + return { + async reset() { + activeController?.abort(); + if (debounceTimer) clearTimeout(debounceTimer); + input.value = ""; + input.focus(); + activeIndex = -1; + clearResults(); + emptyState.style.display = ""; + emptyState.textContent = "Type to search…"; + await ensureInitialized(); + }, + destroy() { + activeController?.abort(); + if (debounceTimer) clearTimeout(debounceTimer); + input.removeEventListener("input", handleInput); + input.closest("dialog")?.removeEventListener("keydown", handleKeydown); + }, + }; +} diff --git a/docs/src/components/ui/sidebar/Sidebar.astro b/docs/src/components/ui/sidebar/Sidebar.astro new file mode 100644 index 0000000..1ca6bcd --- /dev/null +++ b/docs/src/components/ui/sidebar/Sidebar.astro @@ -0,0 +1,64 @@ +--- +/** + * Sidebar — recursive navigation tree from a `SidebarItem[]`. Composes + * SidebarGroup + SidebarLink. Pass `persist` to opt into sessionStorage + * for open/scroll state (desktop only). + */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; +import SidebarGroup from "./SidebarGroup.astro"; +import SidebarLink from "./SidebarLink.astro"; +import type { SidebarItem } from "nimbus-docs/types"; +import { sidebarHash } from "nimbus-docs"; + +interface Props extends HTMLAttributes<"div"> { + items: SidebarItem[]; + /** Persist open/scroll state to sessionStorage. Desktop sidebar only. */ + persist?: boolean; +} + +const { items, persist = false, class: className, ...attrs } = Astro.props; +const hash = sidebarHash(items); +--- + +
+
    + {items.map((item) => + item.type === "group" ? ( +
  • + +
  • + ) : item.type === "external" ? ( +
  • + +
  • + ) : ( +
  • + +
  • + ), + )} +
+
+ + + + diff --git a/docs/src/components/ui/sidebar/SidebarFilter.astro b/docs/src/components/ui/sidebar/SidebarFilter.astro new file mode 100644 index 0000000..be9cb97 --- /dev/null +++ b/docs/src/components/ui/sidebar/SidebarFilter.astro @@ -0,0 +1,20 @@ +--- +/** SidebarFilter — text input that filters the adjacent Sidebar. Press "/" to focus. */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> {} + +const { class: className, ...attrs } = Astro.props; +--- + +
+ +
diff --git a/docs/src/components/ui/sidebar/SidebarGroup.astro b/docs/src/components/ui/sidebar/SidebarGroup.astro new file mode 100644 index 0000000..fa22b55 --- /dev/null +++ b/docs/src/components/ui/sidebar/SidebarGroup.astro @@ -0,0 +1,209 @@ +--- +/** + * SidebarGroup — autogenerated section header in the sidebar rail. + * + * Renders in one of two shapes depending on whether the group has a + * landing page (`indexHref`): + * + * - Has landing: the label is an `` linking to indexHref. The + * collapse caret sits next to it as a separate ` diff --git a/docs/src/components/ui/tabs/index.ts b/docs/src/components/ui/tabs/index.ts new file mode 100644 index 0000000..07214f6 --- /dev/null +++ b/docs/src/components/ui/tabs/index.ts @@ -0,0 +1,5 @@ +export { default as Tabs } from "./Tabs.astro"; +export { default as TabItem } from "./TabItem.astro"; +export { default as TabsList } from "./TabsList.astro"; +export { default as TabsTrigger } from "./TabsTrigger.astro"; +export { default as TabsContent } from "./TabsContent.astro"; diff --git a/docs/src/components/ui/tabs/tabs.client.ts b/docs/src/components/ui/tabs/tabs.client.ts new file mode 100644 index 0000000..4e963a9 --- /dev/null +++ b/docs/src/components/ui/tabs/tabs.client.ts @@ -0,0 +1,72 @@ +/** Wires ; auto-detects manual triggers vs. synthesized-from-TabItem mode. */ + +import { mount, initTabs } from "nimbus-docs/client"; + +const TRIGGER_CLASS = + "cursor-pointer px-4 py-2 text-sm font-medium leading-6 whitespace-nowrap text-muted-foreground transition-colors hover:text-foreground aria-selected:text-primary focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]"; + +let counter = 0; + +function initTabContainer(container: HTMLElement): () => void { + const id = `nb-tabs-${counter++}`; + const syncKey = container.dataset.nbSyncKey; + const tablist = container.querySelector("[role=tablist]"); + const indicator = container.querySelector("[data-nb-tabs-indicator]"); + + // Scope to this container so a nested 's triggers don't flip the + // parent into manual mode (or vice-versa), independent of mount order. + const existingTriggers = Array.from(container.querySelectorAll("[data-nb-tabs-trigger]")).filter( + (t) => (t as HTMLElement).closest("[data-nb-tabs]") === container, + ); + const synthesize = existingTriggers.length === 0; + + if (synthesize && tablist) { + // Only this container's own panels — exclude a nested 's panels, + // whose nearest [data-nb-tabs] ancestor is the inner container. + const panels = Array.from(container.querySelectorAll("[data-nb-tabs-content]")).filter( + (p) => p.closest("[data-nb-tabs]") === container, + ); + + panels.forEach((panel, i) => { + const label = panel.dataset.nbTabLabel ?? "Tab"; + const btn = document.createElement("button"); + btn.role = "tab"; + btn.type = "button"; + btn.className = TRIGGER_CLASS; + btn.textContent = label; + btn.setAttribute("data-nb-tabs-trigger", ""); + + const panelId = `${id}-panel-${i}`; + const tabId = `${id}-tab-${i}`; + btn.id = tabId; + btn.setAttribute("aria-controls", panelId); + panel.id = panelId; + panel.setAttribute("aria-labelledby", tabId); + + if (indicator) { + tablist.insertBefore(btn, indicator); + } else { + tablist.appendChild(btn); + } + }); + } + + const instance = initTabs({ + container, + tabSelector: "[data-nb-tabs-trigger]", + panelSelector: "[data-nb-tabs-content]", + boundarySelector: "[data-nb-tabs]", + indicator, + sync: syncKey ? { key: `ui-synced-tabs__${syncKey}` } : undefined, + }); + + return () => { + instance.destroy(); + // Remove synthesized triggers so re-mount doesn't double up. + if (synthesize && tablist) { + tablist.querySelectorAll("[data-nb-tabs-trigger]").forEach((b) => b.remove()); + } + }; +} + +mount("[data-nb-tabs]", initTabContainer); diff --git a/docs/src/components/ui/theme-toggle/ThemeToggle.astro b/docs/src/components/ui/theme-toggle/ThemeToggle.astro new file mode 100644 index 0000000..1aded43 --- /dev/null +++ b/docs/src/components/ui/theme-toggle/ThemeToggle.astro @@ -0,0 +1,32 @@ +--- +/** + * ThemeToggle — light/dark switcher. Persists to localStorage; theme + * applies to `` via the inline script in BaseLayout + * (no FOUC). + */ +import { Icon } from "astro-icon/components"; +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"button"> {} + +const { class: className, ...attrs } = Astro.props; +--- + + + + diff --git a/docs/src/components/ui/theme-toggle/index.ts b/docs/src/components/ui/theme-toggle/index.ts new file mode 100644 index 0000000..23685f5 --- /dev/null +++ b/docs/src/components/ui/theme-toggle/index.ts @@ -0,0 +1 @@ +export { default as ThemeToggle } from "./ThemeToggle.astro"; diff --git a/docs/src/components/ui/theme-toggle/theme-toggle.client.ts b/docs/src/components/ui/theme-toggle/theme-toggle.client.ts new file mode 100644 index 0000000..f9624f6 --- /dev/null +++ b/docs/src/components/ui/theme-toggle/theme-toggle.client.ts @@ -0,0 +1,31 @@ +/** + * theme-toggle.client.ts — light/dark toggle. Writes pref to localStorage + * ("ui-mode"); BaseLayout's pre-paint script owns DOM application so view + * transitions, OS changes, and cross-tab edits stay in sync. + */ + +import { mount } from "nimbus-docs/client"; + +declare global { + interface Window { + __nbApplyTheme?: () => void; + } +} + +function initThemeToggle(button: HTMLElement): () => void { + function handleClick() { + const isDark = document.documentElement.getAttribute("data-mode") === "dark"; + try { + localStorage.setItem("ui-mode", isDark ? "light" : "dark"); + } catch { + // Ignore storage errors (private mode / restricted contexts). + } + window.__nbApplyTheme?.(); + } + + window.__nbApplyTheme?.(); + button.addEventListener("click", handleClick); + return () => button.removeEventListener("click", handleClick); +} + +mount("[data-nb-theme-toggle]", initThemeToggle); diff --git a/docs/src/components/ui/toc/TOC.astro b/docs/src/components/ui/toc/TOC.astro new file mode 100644 index 0000000..084dfd7 --- /dev/null +++ b/docs/src/components/ui/toc/TOC.astro @@ -0,0 +1,95 @@ +--- +/** + * TOC — on-this-page nav with scroll-spy. Static rail rendered inline + * (border-l + S-curve SVGs at indent changes); animated active indicator + * lives in toc.client.ts. + */ +import { cn } from "@/lib/cn"; +import type { HTMLAttributes } from "astro/types"; +import type { TOCItem } from "nimbus-docs/types"; + +interface Props extends HTMLAttributes<"div"> { + headings: TOCItem[]; +} + +const { headings, class: className, ...attrs } = Astro.props; +--- + +{headings.length > 0 && ( +
+

On this page

+ +
+)} + + diff --git a/docs/src/components/ui/toc/index.ts b/docs/src/components/ui/toc/index.ts new file mode 100644 index 0000000..8df1055 --- /dev/null +++ b/docs/src/components/ui/toc/index.ts @@ -0,0 +1 @@ +export { default as TOC } from "./TOC.astro"; diff --git a/docs/src/components/ui/toc/toc.client.ts b/docs/src/components/ui/toc/toc.client.ts new file mode 100644 index 0000000..9bf1a24 --- /dev/null +++ b/docs/src/components/ui/toc/toc.client.ts @@ -0,0 +1,325 @@ +/** + * Scroll-spy + animated rail indicator. Active heading tracked via a single + * IntersectionObserver; the dash slides by arc-length so it weaves through the + * rail's curves instead of cutting across. + */ + +import { mount } from "nimbus-docs/client"; + +const READING_BAND = 0.25; +const BOTTOM_EPSILON = 2; +const REVEAL_PADDING = 12; + +function initToc(root: HTMLElement): () => void { + const nav = root.querySelector("nav"); + const activePath = root.querySelector("[data-nb-toc-rail-active]"); + const links = root.querySelectorAll("[data-nb-toc-link]"); + if (!nav || !activePath || links.length === 0) return () => {}; + const navElement = nav; + const activeRailPath = activePath; + + const scrollHost = root.closest("[data-nb-toc-scroll-host]") ?? root; + const slugs = Array.from(links) + .map((link) => link.dataset.nbSlug) + .filter((slug): slug is string => typeof slug === "string" && slug.length > 0); + if (slugs.length !== links.length) return () => {}; + + const headingEls = slugs.map((slug) => document.getElementById(slug)).filter((el): el is HTMLElement => el instanceof HTMLElement); + if (headingEls.length === 0) return () => {}; + + let segments: { start: number; length: number }[] = []; + let totalLength = 0; + let currentIndex = -1; + let currentLink: HTMLElement | null = null; + let hasApplied = false; + + // Measure the rail from the DOM so the path stays pixel-perfect over the + // static gray rail, capturing each link's arc-length range as we go. + function buildRail() { + const navRect = navElement.getBoundingClientRect(); + + const m = Array.from(links).map((link) => { + const r = link.getBoundingClientRect(); + return { + x: r.left - navRect.left + 1, + yTop: r.top - navRect.top, + yBot: r.top - navRect.top + r.height, + }; + }); + + let d = ""; + const newSegments: { start: number; length: number }[] = []; + + // Measure each command in isolation (O(1)) and accumulate, rather than + // re-measuring the whole cumulatively-growing path with getTotalLength() + // on every iteration — the latter is O(n^2) and blocks the main thread on + // pages with hundreds of headings. Arc length is additive across + // contiguous commands, so summing isolated sub-paths matches the total. + // activePath doubles as the scratch measurer here; the full `d` is written + // back once at the end. + const measure = (subPath: string) => { + activeRailPath.setAttribute("d", subPath); + return activeRailPath.getTotalLength(); + }; + + let cumulative = 0; + let prevX = 0; + let prevYBot = 0; + + for (let i = 0; i < m.length; i++) { + const cur = m[i]; + + if (i === 0) { + d += `M ${cur.x} ${cur.yTop} `; + } else { + const prev = m[i - 1]; + let connector: string; + if (Math.abs(cur.x - prev.x) < 0.5) { + connector = `L ${cur.x} ${cur.yTop} `; + } else { + // Indent change → S-curve matching the static gap SVG. + const midY = (prev.yBot + cur.yTop) / 2; + connector = `C ${prev.x} ${midY}, ${cur.x} ${midY}, ${cur.x} ${cur.yTop} `; + } + d += connector; + cumulative += measure(`M ${prevX} ${prevYBot} ${connector}`); + } + + const start = cumulative; + + const seg = `L ${cur.x} ${cur.yBot} `; + d += seg; + cumulative += measure(`M ${cur.x} ${cur.yTop} ${seg}`); + + newSegments.push({ start, length: cumulative - start }); + + prevX = cur.x; + prevYBot = cur.yBot; + } + + activeRailPath.setAttribute("d", d); + segments = newSegments; + totalLength = cumulative; + } + + function applyActive(index: number, instant: boolean) { + const seg = segments[index]; + if (!seg) return; + + if (instant) { + activeRailPath.setAttribute("data-initial", "true"); + // Force recalc so only opacity transitions on first paint (no dash sweep). + void activeRailPath.getBoundingClientRect(); + } + + activeRailPath.style.strokeDasharray = `${seg.length} ${totalLength + 1}`; + activeRailPath.style.strokeDashoffset = `${-seg.start}`; + + if (instant) { + requestAnimationFrame(() => { + activeRailPath.setAttribute("data-ready", "true"); + requestAnimationFrame(() => { + activeRailPath.removeAttribute("data-initial"); + }); + }); + } + } + + function revealActiveLink(link: HTMLElement) { + const hostRect = scrollHost.getBoundingClientRect(); + const linkRect = link.getBoundingClientRect(); + + if (linkRect.top < hostRect.top + REVEAL_PADDING) { + scrollHost.scrollTop += linkRect.top - hostRect.top - REVEAL_PADDING; + return; + } + + if (linkRect.bottom > hostRect.bottom - REVEAL_PADDING) { + scrollHost.scrollTop += linkRect.bottom - hostRect.bottom + REVEAL_PADDING; + } + } + + function setActive(index: number) { + if (index === currentIndex) return; + currentIndex = index; + + currentLink?.removeAttribute("aria-current"); + const activeLink = links[index] ?? null; + activeLink?.setAttribute("aria-current", "true"); + currentLink = activeLink; + if (activeLink) revealActiveLink(activeLink); + + applyActive(index, !hasApplied); + hasApplied = true; + } + + const inBand = new Set(); + let observedIndex = 0; + let atBottom = false; + let pinnedIndex: number | null = null; + let pinnedEnteredViewport = false; + + function resolve() { + if (pinnedIndex !== null) { + setActive(pinnedIndex); + return; + } + setActive(atBottom ? headingEls.length - 1 : observedIndex); + } + + // rootMargin collapses the root to the top band; deepest in-band heading wins. + const spy = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + const i = headingEls.indexOf(entry.target as HTMLElement); + if (i === -1) continue; + if (entry.isIntersecting) inBand.add(i); + else inBand.delete(i); + } + if (inBand.size > 0) observedIndex = Math.max(...inBand); + resolve(); + }, + { rootMargin: `0px 0px -${(1 - READING_BAND) * 100}% 0px`, threshold: 0 }, + ); + headingEls.forEach((h) => spy.observe(h)); + + function updateBottom() { + const scrollEl = document.scrollingElement ?? document.documentElement; + const maxScroll = scrollEl.scrollHeight - window.innerHeight; + const next = maxScroll > BOTTOM_EPSILON && scrollEl.scrollTop >= maxScroll - BOTTOM_EPSILON; + if (next !== atBottom) { + atBottom = next; + resolve(); + } + } + + function updateObservedIndex() { + const bandBottom = window.innerHeight * READING_BAND; + let nextIndex = 0; + for (let i = 0; i < headingEls.length; i++) { + if (headingEls[i].getBoundingClientRect().top <= bandBottom) nextIndex = i; + else break; + } + observedIndex = nextIndex; + } + + function releaseStalePin() { + if (pinnedIndex === null) return; + const pinnedSlug = slugs[pinnedIndex]; + const heading = pinnedSlug ? document.getElementById(pinnedSlug) : null; + if (!heading) { + pinnedIndex = null; + pinnedEnteredViewport = false; + return; + } + + const rect = heading.getBoundingClientRect(); + const inViewport = rect.bottom >= 0 && rect.top <= window.innerHeight; + if (inViewport) { + pinnedEnteredViewport = true; + return; + } + + if (pinnedEnteredViewport) { + pinnedIndex = null; + pinnedEnteredViewport = false; + } + } + + let ticking = false; + function onScroll() { + if (ticking) return; + ticking = true; + requestAnimationFrame(() => { + updateObservedIndex(); + updateBottom(); + releaseStalePin(); + resolve(); + ticking = false; + }); + } + + function onLayoutChange() { + buildRail(); + updateObservedIndex(); + updateBottom(); + releaseStalePin(); + resolve(); + if (currentIndex >= 0) { + applyActive(currentIndex, true); + const activeLink = links[currentIndex]; + if (activeLink) revealActiveLink(activeLink); + } + } + + const controller = new AbortController(); + + navElement.addEventListener( + "click", + (e) => { + if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; + const link = (e.target as Element).closest("[data-nb-toc-link]"); + if (!link) return; + const linkSlug = link.dataset.nbSlug; + if (!linkSlug) return; + const i = slugs.indexOf(linkSlug); + if (i === -1) return; + pinnedIndex = i; + const headingSlug = slugs[i]; + const heading = headingSlug ? document.getElementById(headingSlug) : null; + const rect = heading?.getBoundingClientRect(); + pinnedEnteredViewport = !!rect && rect.bottom >= 0 && rect.top <= window.innerHeight; + resolve(); + }, + { signal: controller.signal }, + ); + + // Hand-driven scrolling releases the pin and resumes auto-tracking. + function releasePin() { + if (pinnedIndex === null) return; + pinnedIndex = null; + pinnedEnteredViewport = false; + resolve(); + } + const NAV_KEYS = new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " ", "Spacebar"]); + window.addEventListener("wheel", releasePin, { + passive: true, + signal: controller.signal, + }); + window.addEventListener("touchmove", releasePin, { + passive: true, + signal: controller.signal, + }); + window.addEventListener( + "keydown", + (e) => { + if (NAV_KEYS.has(e.key)) releasePin(); + }, + { signal: controller.signal }, + ); + + window.addEventListener("scroll", onScroll, { + passive: true, + signal: controller.signal, + }); + window.addEventListener("resize", onLayoutChange, { + passive: true, + signal: controller.signal, + }); + + const ro = new ResizeObserver(onLayoutChange); + ro.observe(navElement); + + buildRail(); + updateObservedIndex(); + updateBottom(); + resolve(); + + return () => { + controller.abort(); + ro.disconnect(); + spy.disconnect(); + }; +} + +mount("[data-nb-toc]", initToc); diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts index 196db15..3b5e263 100644 --- a/docs/src/content.config.ts +++ b/docs/src/content.config.ts @@ -1,13 +1,14 @@ import { defineCollection } from "astro:content"; -import { docsLoader } from "@astrojs/starlight/loaders"; -import { docsSchema } from "@astrojs/starlight/schema"; +import { z } from "astro/zod"; +import { docsCollection, partialsCollection } from "nimbus-docs/content"; -/** - * Single docs collection, loaded from `src/content/docs/**` via - * Starlight's filesystem loader. Schema follows Starlight's - * canonical shape so every page gets `title`, `description`, and - * the standard sidebar / hero fields. - */ export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), + docs: defineCollection( + docsCollection({ + schemaFields: { + audience: z.literal("human").optional(), + }, + }), + ), + partials: defineCollection(partialsCollection()), }; diff --git a/docs/src/content/docs/cli/index.md b/docs/src/content/docs/cli/index.md index a25cf3f..1ecdfef 100644 --- a/docs/src/content/docs/cli/index.md +++ b/docs/src/content/docs/cli/index.md @@ -1,6 +1,8 @@ --- title: CLI overview description: The polystella binary and its verb-style subcommands. +sidebar: + label: Overview --- The package exposes a single `polystella` binary with verb-style diff --git a/docs/src/content/docs/configuration/index.md b/docs/src/content/docs/configuration/index.md index 491d2f1..585019d 100644 --- a/docs/src/content/docs/configuration/index.md +++ b/docs/src/content/docs/configuration/index.md @@ -1,6 +1,8 @@ --- title: Configuration overview description: Where polystella.config.mjs sits and how it relates to astro.config.mjs. +sidebar: + label: Overview --- PolyStella's configuration lives in two files. Knowing which goes diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index fed994d..9e9163d 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -1,44 +1,53 @@ --- title: PolyStella description: AI-driven content localization for Astro — build-time markdown translation, R2-cached. -template: splash -hero: - tagline: AI-driven content localization for Astro. Build-time translation, R2-cached, no runtime AI calls. - actions: - - text: Get started - link: /getting-started/install/ - icon: right-arrow - variant: primary - - text: View on GitHub - link: https://github.com/cloudflare/polystella - icon: external - variant: minimal +mode: custom --- -import { Card, CardGrid } from "@astrojs/starlight/components"; +
+
+

+ AI-driven localization for Astro +

+

+ Build-time translation, R2-cached, no runtime AI calls. +

+

+ PolyStella translates content into additional locales during your Astro build, caches the generated bytes in Cloudflare R2, and injects locale-prefixed routes for static delivery. +

+ +
- - + + Translates `.md`, `.mdx`, and `.toml` content into additional locales during `astro build`. Visitors get static bytes; no runtime AI calls. - + Translations are content-addressed by source bytes + glossary + model. Unchanged pages cost zero on rebuild. Translations are never committed to the repo. - + A YAML file per locale pins do-not-translate terms, preferred translations, and free-form translator notes. Edits re-translate only the pages that need it. - + Drop a markdown file under `i18n/overrides/{locale}/...` and it wins over AI output verbatim. The pipeline still locale-prefixes its internal links. - + Per-locale sibling content collections. Astro routes `/[lang]/...` through generated shims. `Astro.locals.t` and `Astro.locals.lhref` work in templates. - + Per-locale JSON files for chrome text. The CLI offers offline drift detection, key reconciliation, and AI-fill of empty placeholders. - + +
diff --git a/docs/src/content/partials/example.mdx b/docs/src/content/partials/example.mdx new file mode 100644 index 0000000..e326161 --- /dev/null +++ b/docs/src/content/partials/example.mdx @@ -0,0 +1,4 @@ +This text lives in `src/content/partials/example.mdx` and is pulled in with +``. Edit the partial once and every page that renders +it stays in sync — handy for install steps, support notes, or any boilerplate +you'd otherwise copy between pages. diff --git a/docs/src/layouts/BaseLayout.astro b/docs/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..40c2a9c --- /dev/null +++ b/docs/src/layouts/BaseLayout.astro @@ -0,0 +1,118 @@ +--- +import "@fontsource-variable/inter/index.css"; +import "@fontsource-variable/jetbrains-mono/index.css"; +import "../styles/globals.css"; +import "../styles/prose.css"; +import { config } from "virtual:nimbus/config"; +import { getCollectionLlmsUrl, getVersionStatus } from "nimbus-docs"; +import AgentDirective from "@/components/AgentDirective.astro"; +import { SearchDialog } from "@/components/ui/search"; +import NimbusHead from "nimbus-docs/components/NimbusHead.astro"; +import type { BasePageProps } from "nimbus-docs/types"; + +type Props = BasePageProps; + +const { + title, + description, + noindex, + markdownUrl, + socialImage, + lastUpdated, + head: pageHead = [], + collection, + entryId, +} = Astro.props; + +const lang = config.locale ?? "en"; +// Per-page agent-index pointer. For pages in non-primary or version +// collections, this resolves to `//llms.txt` (e.g. `/v0/llms.txt` +// for a v0 docs page, `/blog/llms.txt` for a blog post). Falls back to +// the root `/llms.txt` when no collection is provided. +// Hidden-version pages don't advertise an agent index (the per-version +// llms.txt isn't emitted for them); suppress the AgentDirective entirely. +const versionStatus = collection ? await getVersionStatus(collection) : null; +const isHiddenVersion = versionStatus?.isHidden === true; +const llmsIndexPath = collection + ? await getCollectionLlmsUrl(collection) + : "/llms.txt"; +const llmsUrl = Astro.site + ? new URL(llmsIndexPath, Astro.site).href + : llmsIndexPath; +--- + + + + + + + + + + + + + + Skip to content + + {markdownUrl && !isHiddenVersion && } + + {config.search !== false && } + + + + diff --git a/docs/src/layouts/DocsLayout.astro b/docs/src/layouts/DocsLayout.astro new file mode 100644 index 0000000..04c8347 --- /dev/null +++ b/docs/src/layouts/DocsLayout.astro @@ -0,0 +1,333 @@ +--- +/** + * DocsLayout — three-column docs layout. + * + * Named slots for overrides (all optional, defaults render otherwise): + * header, sidebar, toc, page-title, content-footer, pagination + */ +import { Icon } from "astro-icon/components"; +import BaseLayout from "./BaseLayout.astro"; +import Header from "@/components/Header.astro"; +import { Banner } from "@/components/ui/banner"; +import { Sidebar, SidebarFilter } from "@/components/ui/sidebar"; +import { TOC } from "@/components/ui/toc"; +import { Breadcrumbs } from "@/components/ui/breadcrumbs"; +import { Pagination } from "@/components/ui/pagination"; +import { PageActions } from "@/components/ui/page-actions"; +import { Badge } from "@/components/ui/badge"; +import type { DocsPageProps } from "nimbus-docs/types"; +import { getVersionStatus, getVersionAlternates } from "nimbus-docs"; + +type Props = DocsPageProps & { audience?: "human" }; + +const { title, description, sidebar, headings, breadcrumbs, prevNext, mode = "doc", banner, head = [], searchable, noindex, markdownUrl, socialImage, lastUpdated, editUrl, draft, audience, collection, entryId } = Astro.props; + +// `searchable` derives from `noindex` when omitted: a non-crawlable page is +// by default not in the site search either. Explicit `searchable: true` +// overrides for the edge case of "no search engines, yes internal search." +const effectiveSearchable = searchable ?? !noindex; +const isCustom = mode === "custom"; + +// `sidebar: false` / `tableOfContents: false` in frontmatter come through +// from the route as literal `false` (not coerced to `[]`). They drive +// per-column suppression — chrome, mobile dialog, header menu button. +const showSidebar = sidebar !== false; + +// Versioning: per-page status drives Pagefind faceting, deprecation +// banner, and the data-pagefind-ignore exclusion for hidden versions. +const versionStatus = collection ? await getVersionStatus(collection) : null; +const versionAlternates = collection && entryId + ? await getVersionAlternates(collection, entryId) + : null; +const currentSiblingUrl = versionAlternates?.canonical?.url ?? null; + +// Page-body attribute resolution: +// - If the user opted out via `searchable: false` (or `noindex: true` +// with no explicit override) → ignore. +// - If the page is in a hidden version → ignore unconditionally +// (hidden takes precedence; you can't index hidden content even +// if the page frontmatter says searchable=true). +// - Otherwise → mark as a Pagefind body, plus emit per-version and +// per-status filters so the search UI can scope by them. +const pagefindAttrs: Record = {}; +if (!effectiveSearchable || versionStatus?.isHidden) { + pagefindAttrs["data-pagefind-ignore"] = ""; +} else { + pagefindAttrs["data-pagefind-body"] = ""; + if (versionStatus) { + pagefindAttrs["data-pagefind-filter"] = `version:${versionStatus.version}`; + if (versionStatus.isDeprecated) { + // Pagefind supports multiple filter attributes on the same element + // via `data-pagefind-filter` repeated as a comma-separated list in + // the value. The status filter pairs with the version filter so a + // search UI can offer "exclude deprecated" as a default toggle. + pagefindAttrs["data-pagefind-filter"] += `, status:deprecated`; + } + } +} + +const hasHeader = Astro.slots.has("header"); +const hasSidebar = Astro.slots.has("sidebar"); +const hasToc = Astro.slots.has("toc"); +const hasPageTitle = Astro.slots.has("page-title"); +const hasContentFooter = Astro.slots.has("content-footer"); +const hasPagination = Astro.slots.has("pagination"); +--- + + +
+ {hasHeader ? :
} + + {isCustom ? ( +
+ +
+ ) : ( +
+
+ {showSidebar && ( + + )} + + {/* Sidebar state restore — pre-sets data attributes before the + disclosure module loads so collapsed/scrolled state survives nav. + Only emit when the sidebar is rendered; otherwise the script is + dead code (the `desktop-sidebar` element doesn't exist). */} + {showSidebar && ( + + )} + +
+
+ {versionStatus?.isDeprecated && ( + ${versionStatus.version} version of the docs and is no longer maintained. The latest version of this page is at ${currentSiblingUrl}.` + : `This is the ${versionStatus.version} version of the docs and is no longer maintained. See the current docs for up-to-date content.` + } + /> + )} + {banner && } + +
+ {hasPageTitle ? ( + + ) : ( + +
+

{title}

+ {draft && } +
+ {description && ( +

{description}

+ )} + + {(markdownUrl || audience === "human") && ( + audience === "human" ? ( +
+
+ +
+
+ ) : ( +
+ ) + )} + {!description && !markdownUrl && audience !== "human" &&
} + + )} +
+ +
+ {hasContentFooter ? ( + + ) : editUrl && ( + + )} +
+ {hasPagination ? : } +
+
+ + {headings !== false && (hasToc || headings.length > 0) && ( + + )} +
+
+ )} + +
+ + {showSidebar && ( + /* Mobile sidebar — native for free focus trap, escape, backdrop. */ + +
+
+ Navigation + +
+ +
+
+ )} + + + +
diff --git a/docs/src/lib/cn.ts b/docs/src/lib/cn.ts new file mode 100644 index 0000000..75424d6 --- /dev/null +++ b/docs/src/lib/cn.ts @@ -0,0 +1,7 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +/** Compose class names with Tailwind conflict resolution. Consumer class (last arg) wins. */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/docs/src/pages/404.astro b/docs/src/pages/404.astro new file mode 100644 index 0000000..ad970ae --- /dev/null +++ b/docs/src/pages/404.astro @@ -0,0 +1,23 @@ +--- +import Header from "../components/Header.astro"; +import BaseLayout from "../layouts/BaseLayout.astro"; +--- + + +
+
+
+

404

+

Page not found

+

+ This docs page may have moved or may not exist yet. +

+ + Back to the docs + +
+
+
diff --git a/docs/src/pages/[...slug].astro b/docs/src/pages/[...slug].astro new file mode 100644 index 0000000..88f70c0 --- /dev/null +++ b/docs/src/pages/[...slug].astro @@ -0,0 +1,87 @@ +--- +import DocsLayout from "../layouts/DocsLayout.astro"; +import { getCollection } from "astro:content"; +import { + getDocsPageProps, + getSidebar, + getPrevNext, + getBreadcrumbs, + getEditUrl, + getLastUpdated, + getTOC, +} from "nimbus-docs"; +import { config } from "virtual:nimbus/config"; +import { components } from "../components"; + +export const prerender = true; + +export async function getStaticPaths() { + const entries = await getCollection("docs", (entry) => import.meta.env.DEV || !entry.data.draft); + + return entries.map((entry) => ({ + params: { slug: entry.id === "index" ? undefined : entry.id }, + props: { entry }, + })); +} + +const { entry, Content, headings } = await getDocsPageProps(Astro); + +const currentSlug = Astro.url.pathname.replace(/\/$/, "") || "/"; + +// Column renders only when not custom mode AND site-wide flag on AND page hasn't opted out. +const isCustom = entry.data.mode === "custom"; +const sidebarOn = + !isCustom && + config.features?.sidebar !== false && + entry.data.sidebar !== false; +const tocOn = + !isCustom && + config.features?.tableOfContents !== false && + entry.data.tableOfContents !== false; + +const sidebar = sidebarOn + ? await getSidebar(currentSlug, { collection: entry.collection }) + : false; +const prevNext = await getPrevNext(currentSlug, { + sidebarTree: sidebar === false ? [] : sidebar, + overrides: { prev: entry.data.prev, next: entry.data.next }, +}); +// Pass `collection` so versioned pages (docs-) get version-prefixed +// breadcrumb hrefs — parity with the getSidebar call above. +const breadcrumbs = await getBreadcrumbs(currentSlug, { collection: entry.collection }); +const editUrl = await getEditUrl(entry); +// Frontmatter wins; git is the fallback. +const lastUpdated = entry.data.lastUpdated ?? await getLastUpdated(entry); +// `tocOn` already implies `tableOfContents !== false`, but TS can't carry +// that boolean narrowing to the value here — re-check it so `getTOC` only +// ever sees its options object (or undefined), never `false`. +const tocConfig = entry.data.tableOfContents; +const toc = tocOn && tocConfig !== false ? getTOC(headings, tocConfig) : false; +const markdownPath = entry.id === "index" ? "/index.md" : `/${entry.id}/index.md`; +const markdownUrl = Astro.site ? new URL(markdownPath, Astro.site).href : markdownPath; +const socialImage = entry.data.socialImage ?? `/og/${entry.id}.png`; +--- + + + + diff --git a/docs/src/pages/[...slug]/index.md.ts b/docs/src/pages/[...slug]/index.md.ts new file mode 100644 index 0000000..7dbfe23 --- /dev/null +++ b/docs/src/pages/[...slug]/index.md.ts @@ -0,0 +1,70 @@ +/** + * Per-page `//index.md` — the clean-markdown alternate for every + * indexable entry of the primary `docs` collection. + * + * Non-primary collections (`api`, `blog`, …) mount under their own + * URL namespace by convention; their `.md` alternates live at the + * sibling route `pages//[...slug]/index.md.ts`. This route + * filters to the primary collection so multi-collection sites don't + * generate conflicting `[...slug]` paths at root. + */ + +import { getIndexedEntries, renderEntryAsMarkdown, type IndexedEntry } from "nimbus-docs"; +import { config } from "virtual:nimbus/config"; + +export const prerender = true; + +const PRIMARY_COLLECTION = "docs"; + +interface SlugProps { + item: IndexedEntry; +} + +export async function getStaticPaths() { + const indexed = await getIndexedEntries(); + return indexed + .filter((item) => item.collection === PRIMARY_COLLECTION) + .map((item) => ({ + // Root index (`entry.id === "index"`) emits at `/index.md`; Astro's + // rest-segment treats `undefined` as "no segment" so the URL is + // `/index.md` rather than `/index/index.md`. Every other entry emits + // at `//index.md` — the convention `/index.md`. + params: { + slug: item.entry.id === "index" ? undefined : item.entry.id, + }, + props: { item } as SlugProps, + })); +} + +export async function GET({ props }: { props: SlugProps }) { + const { item } = props; + const { entry, title, description, markdownUrl } = item; + const data = (entry.data ?? {}) as Record; + const rawImage = data.socialImage; + const socialImage = typeof rawImage === "string" && rawImage.length > 0 ? rawImage : config.socialImage; + + const markdown = renderEntryAsMarkdown(entry); + + const body = [ + "---", + `title: ${JSON.stringify(title)}`, + ...(description ? [`description: ${JSON.stringify(description)}`] : []), + ...(socialImage ? [`image: ${JSON.stringify(new URL(socialImage, config.site).href)}`] : []), + "---", + "", + "> Documentation Index", + `> Fetch the complete documentation index at: ${new URL("/llms.txt", config.site).href}`, + "> Use this file to discover all available pages before exploring further.", + "", + `# ${title}`, + "", + markdown, + "", + `Source: ${new URL(markdownUrl, config.site).href}`, + "", + ].join("\n"); + + return new Response(body, { + headers: { "Content-Type": "text/markdown; charset=utf-8" }, + }); +} diff --git a/docs/src/pages/[section]/llms.txt.ts b/docs/src/pages/[section]/llms.txt.ts new file mode 100644 index 0000000..f511cc8 --- /dev/null +++ b/docs/src/pages/[section]/llms.txt.ts @@ -0,0 +1,64 @@ +/** + * Per-section /
/llms.txt — sub-index files that drill down + * from the root `/llms.txt` into a named slice of the site's docs. + * + * A "section" is one of two things: + * 1. A folder inside the primary `docs` collection with more than + * one page (e.g. `src/content/docs//*` → `//llms.txt`). + * 2. A whole non-primary collection — `api`, `blog`, etc. — which + * becomes a single section mounted at `//llms.txt`. + * + * Both cases produce the same shape at the same URL pattern, so + * agents follow one rule: every link in `/llms.txt` that ends in + * `.llms.txt` resolves here. + * + * `getIndexedTopLevel()` decides which sections exist and what they + * contain; this route just renders one file per section it returns. + */ + +import { getIndexedTopLevel, type IndexedEntry } from "nimbus-docs"; +import { config } from "virtual:nimbus/config"; + +export const prerender = true; + +interface SectionProps { + slug: string; + label: string; + members: IndexedEntry[]; +} + +export async function getStaticPaths() { + const { groups } = await getIndexedTopLevel(); + return ( + groups + // Versioning: hidden versions don't get a per-section llms.txt + // index. They're URL-reachable for direct navigation, but every + // agent-discovery surface should treat them as if they don't exist. + .filter((group) => !group.hidden) + .map((group) => ({ + params: { section: group.slug }, + props: { + slug: group.slug, + label: group.label, + members: group.members, + } as SectionProps, + })) + ); +} + +export async function GET({ props }: { props: SectionProps }) { + const { label, members } = props; + + const lines = [`# ${label}`, "", "## Pages", ""]; + + for (const item of members) { + const description = item.description ? ` — ${item.description}` : ""; + lines.push(`- [${item.title}](${new URL(item.markdownUrl, config.site).href})${description}`); + } + + lines.push(""); + + return new Response(lines.join("\n"), { + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }); +} diff --git a/docs/src/pages/llms.txt.ts b/docs/src/pages/llms.txt.ts new file mode 100644 index 0000000..bdc0593 --- /dev/null +++ b/docs/src/pages/llms.txt.ts @@ -0,0 +1,41 @@ +// Root /llms.txt — sectioned index for AI agents. +import { getIndexedTopLevel } from "nimbus-docs"; +import { config } from "virtual:nimbus/config"; + +export const prerender = true; + +export async function GET() { + const { leaves, groups } = await getIndexedTopLevel(); + + const lines = [`# ${config.title}`, "", config.description ?? "Documentation index for AI agents.", "", "## Pages", ""]; + + // Sort leaves + groups alphabetically into a single stable list. + type Row = { key: string; line: string }; + const rows: Row[] = []; + + for (const leaf of leaves) { + const description = leaf.description ? ` — ${leaf.description}` : ""; + rows.push({ + key: leaf.url, + line: `- [${leaf.title}](${new URL(leaf.markdownUrl, config.site).href})${description}`, + }); + } + + for (const group of groups) { + // Older doc versions have their own //llms.txt; don't list them here. + if (group.kind === "version") continue; + rows.push({ + key: `/${group.slug}`, + line: `- [${group.label}](${new URL(`/${group.slug}/llms.txt`, config.site).href})`, + }); + } + + rows.sort((a, b) => a.key.localeCompare(b.key)); + for (const row of rows) lines.push(row.line); + + lines.push(""); + + return new Response(lines.join("\n"), { + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }); +} diff --git a/docs/src/pages/og.png.ts b/docs/src/pages/og.png.ts new file mode 100644 index 0000000..e03cacb --- /dev/null +++ b/docs/src/pages/og.png.ts @@ -0,0 +1,17 @@ +import { generateOpenGraphImage } from "astro-og-canvas"; +import { config } from "virtual:nimbus/config"; +import { ogCardConfig } from "./og/_og-card-config"; + +export const prerender = true; + +export async function GET() { + const body = await generateOpenGraphImage({ + title: config.title, + description: config.description, + ...ogCardConfig, + }); + + return new Response(body, { + headers: { "Content-Type": "image/png" }, + }); +} diff --git a/docs/src/pages/og/[...slug].ts b/docs/src/pages/og/[...slug].ts new file mode 100644 index 0000000..9e16df1 --- /dev/null +++ b/docs/src/pages/og/[...slug].ts @@ -0,0 +1,24 @@ +import { getCollection } from "astro:content"; +import { OGImageRoute } from "astro-og-canvas"; +import { ogCardConfig } from "./_og-card-config"; + +const entries = await getCollection("docs", (entry) => !entry.data.draft); + +const pages = Object.fromEntries( + entries.map((entry) => [ + entry.id, + { + title: entry.data.title, + description: entry.data.description ?? "", + }, + ]), +); + +export const { getStaticPaths, GET } = await OGImageRoute({ + pages, + getImageOptions: (_path, page) => ({ + title: page.title, + description: page.description, + ...ogCardConfig, + }), +}); diff --git a/docs/src/pages/og/_og-card-config.ts b/docs/src/pages/og/_og-card-config.ts new file mode 100644 index 0000000..61f8f41 --- /dev/null +++ b/docs/src/pages/og/_og-card-config.ts @@ -0,0 +1,39 @@ +/** + * Shared visual config for build-time OG cards. + * + * Edit this file to retune generated card colors, spacing, and fonts. Both + * the per-page endpoint (`og/[...slug].ts`) and the homepage fallback + * (`og.png.ts`) spread this object into `astro-og-canvas`. + * + * Leading underscore tells Astro to skip routing for this file — it sits + * inside `src/pages/` to be next to its consumers, but it's not a route. + */ + +import type { OGImageOptions } from "astro-og-canvas"; + +export const ogCardConfig = { + bgGradient: [ + [11, 11, 12], + [26, 26, 28], + ], + border: { color: [39, 39, 42], width: 2, side: "inline-start" }, + padding: 96, + fonts: ["./public/fonts/Inter-Bold.ttf"], + font: { + title: { + color: [250, 250, 250], + size: 64, + weight: "Bold", + families: ["Inter"], + lineHeight: 1.1, + }, + description: { + color: [161, 161, 170], + size: 32, + weight: "Bold", + families: ["Inter"], + lineHeight: 1.3, + }, + }, + format: "PNG", +} satisfies Partial; diff --git a/docs/src/pages/robots.txt.ts b/docs/src/pages/robots.txt.ts new file mode 100644 index 0000000..6d1e804 --- /dev/null +++ b/docs/src/pages/robots.txt.ts @@ -0,0 +1,11 @@ +import { config } from "virtual:nimbus/config"; + +export const prerender = true; + +export function GET() { + const body = ["User-agent: *", "Allow: /", "", `Sitemap: ${new URL("/sitemap-index.xml", config.site).href}`, ""].join("\n"); + + return new Response(body, { + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }); +} diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css deleted file mode 100644 index b1723d1..0000000 --- a/docs/src/styles/custom.css +++ /dev/null @@ -1,30 +0,0 @@ -/* - * PolyStella docs — Starlight theme overrides. - * - * Single accent colour (Cloudflare orange #f6821f) wired into - * Starlight's CSS-variable system. Everything else stays at - * Starlight defaults so a future Starlight upgrade doesn't fight - * with our customisation. - */ - -:root { - /* Light mode: orange-tinted blue-ish gray as the accent base. - Starlight derives hover / focus / link colours from these. */ - --sl-color-accent-low: #fff1e6; - --sl-color-accent: #f6821f; - --sl-color-accent-high: #c1631a; -} - -:root[data-theme="dark"] { - --sl-color-accent-low: #3a1f08; - --sl-color-accent: #f6821f; - --sl-color-accent-high: #fcb15c; -} - -/* Wordmark SVG inherits currentColor so the "Poly" half follows - Starlight's foreground (white on dark, near-black on light) and - "Stella" stays Cloudflare orange across themes. */ -.site-title svg { - height: 1.6rem; - width: auto; -} diff --git a/docs/src/styles/globals.css b/docs/src/styles/globals.css new file mode 100644 index 0000000..70f5f70 --- /dev/null +++ b/docs/src/styles/globals.css @@ -0,0 +1,489 @@ +@import "tailwindcss/index.css"; + +/* ============================================================ + * Nimbus Theme — THE file you edit to customize your site. + * + * Architecture: + * 1. CSS custom properties (--nb-*) define the design tokens + * 2. `@theme` block exposes them to Tailwind so utilities like + * `bg-card`, `text-foreground`, `border-border` work + * 3. Dark mode uses the `[data-mode="dark"]` attribute + * 4. All colors use oklch for perceptual uniformity + * + * Naming convention: + * - Surfaces paired with foregrounds: `card` / `card-foreground` + * - Functional names: `primary` (action), `accent` (hover surface), + * `muted` (de-emphasized), not visual relationships + * - Reserved Tailwind keywords avoided (no `text-base`, `font-sans`, + * `shadow-lg`, etc. as color names — they collide with built-ins) + * ============================================================ */ + +/* ---- Light mode (default) ---- */ +:root { + color-scheme: light; + + /* Surfaces (paired with foregrounds for contrast) */ + --nb-background: oklch(0.99 0 0); + --nb-foreground: oklch(0.21 0 0); + + --nb-card: oklch(1 0 0); + --nb-card-foreground: var(--nb-foreground); + + --nb-muted: oklch(0.967 0 0); + --nb-muted-foreground: oklch(0.556 0 0); + + --nb-accent: #fff1e6; + --nb-accent-foreground: var(--nb-foreground); + + /* Primary action / brand */ + --nb-primary: #f6821f; + --nb-primary-foreground: #1f1308; + --nb-primary-hover: #c1631a; + + /* Borders / focus */ + --nb-border: oklch(0.935 0 0); + --nb-border-strong: oklch(0.87 0 0); + --nb-selected: oklch(0.89 0 0); + --nb-ring: rgb(246 130 31 / 0.35); + + /* Status colors — paired (saturated for text/border + muted for bg) */ + --nb-info: oklch(0.45 0.18 255); + --nb-info-foreground: oklch(0.45 0.18 255); + --nb-info-muted: oklch(0.965 0.015 255); + + --nb-success: oklch(0.42 0.14 152); + --nb-success-foreground: oklch(0.42 0.14 152); + --nb-success-muted: oklch(0.965 0.015 152); + + --nb-warning: oklch(0.48 0.15 65); + --nb-warning-foreground: oklch(0.48 0.15 65); + --nb-warning-muted: oklch(0.97 0.02 70); + + --nb-danger: oklch(0.47 0.18 25); + --nb-danger-foreground: oklch(0.47 0.18 25); + --nb-danger-muted: oklch(0.965 0.015 25); + + /* Layout */ + --nb-sidebar-width: 18.75rem; + --nb-toc-width: 18rem; + --nb-content-max: 43.5rem; + + /* Typography — self-hosted via @fontsource-variable in BaseLayout.astro */ + --nb-font-sans: "Inter Variable", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --nb-font-mono: "JetBrains Mono Variable", "JetBrains Mono", ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace; + --nb-h1-size: 2.1875rem; + --nb-h1-weight: 600; + --nb-h1-tracking: -0.025em; + --nb-h2-size: 1.3rem; + --nb-h2-weight: 650; + --nb-h2-tracking: -0.015em; + --nb-h3-size: 1.1rem; + --nb-h3-weight: 600; + --nb-h3-tracking: -0.01em; + --nb-h4-weight: 600; + + /* Shadows */ + --nb-shadow-sm: 0 1px 2px oklch(0 0 0 / 0.04); + --nb-shadow: 0 1px 3px oklch(0 0 0 / 0.06), 0 1px 2px oklch(0 0 0 / 0.04); + --nb-shadow-lg: 0 4px 12px oklch(0 0 0 / 0.08), 0 2px 4px oklch(0 0 0 / 0.04); +} + +/* Wider content area on large displays (e.g. MacBook Pro 16″) */ +@media (min-width: 1536px) { + :root { + --nb-content-max: 52rem; + } +} + +/* ---- Dark mode ---- */ +[data-mode="dark"] { + color-scheme: dark; + + --nb-background: oklch(0.085 0 0); + --nb-foreground: oklch(0.97 0 0); + + --nb-card: #0f0f0f; + --nb-card-foreground: var(--nb-foreground); + + --nb-muted: #060606; + --nb-muted-foreground: oklch(0.556 0 0); + + --nb-accent: #3a1f08; + --nb-accent-foreground: var(--nb-foreground); + + --nb-primary: #f6821f; + --nb-primary-foreground: #1f1308; + --nb-primary-hover: #fcb15c; + + --nb-border: oklch(0.269 0 0); + --nb-border-strong: oklch(0.371 0 0); + --nb-selected: oklch(0.371 0 0); + --nb-ring: rgb(246 130 31 / 0.35); + + --nb-info: oklch(0.75 0.12 255); + --nb-info-foreground: oklch(0.75 0.12 255); + --nb-info-muted: oklch(0.17 0.03 260); + + --nb-success: oklch(0.72 0.12 150); + --nb-success-foreground: oklch(0.72 0.12 150); + --nb-success-muted: oklch(0.17 0.025 153); + + --nb-warning: oklch(0.78 0.11 80); + --nb-warning-foreground: oklch(0.78 0.11 80); + --nb-warning-muted: oklch(0.19 0.03 70); + + --nb-danger: oklch(0.72 0.14 25); + --nb-danger-foreground: oklch(0.72 0.14 25); + --nb-danger-muted: oklch(0.17 0.03 25); + + --nb-shadow-sm: 0 1px 2px oklch(0 0 0 / 0.2); + --nb-shadow: 0 1px 3px oklch(0 0 0 / 0.3), 0 1px 2px oklch(0 0 0 / 0.2); + --nb-shadow-lg: 0 4px 12px oklch(0 0 0 / 0.4), 0 2px 4px oklch(0 0 0 / 0.2); +} + +/* ---- Tailwind theme mapping ---- + * These generate Tailwind utility classes: + * bg-card, text-foreground, border-border, ring-ring, etc. + */ +@theme { + --color-background: var(--nb-background); + --color-foreground: var(--nb-foreground); + + --color-card: var(--nb-card); + --color-card-foreground: var(--nb-card-foreground); + + --color-muted: var(--nb-muted); + --color-muted-foreground: var(--nb-muted-foreground); + + --color-accent: var(--nb-accent); + --color-accent-foreground: var(--nb-accent-foreground); + + --color-primary: var(--nb-primary); + --color-primary-foreground: var(--nb-primary-foreground); + --color-primary-hover: var(--nb-primary-hover); + + --color-border: var(--nb-border); + --color-border-strong: var(--nb-border-strong); + --color-selected: var(--nb-selected); + --color-ring: var(--nb-ring); + + --color-info: var(--nb-info); + --color-info-foreground: var(--nb-info-foreground); + --color-info-muted: var(--nb-info-muted); + + --color-success: var(--nb-success); + --color-success-foreground: var(--nb-success-foreground); + --color-success-muted: var(--nb-success-muted); + + --color-warning: var(--nb-warning); + --color-warning-foreground: var(--nb-warning-foreground); + --color-warning-muted: var(--nb-warning-muted); + + --color-danger: var(--nb-danger); + --color-danger-foreground: var(--nb-danger-foreground); + --color-danger-muted: var(--nb-danger-muted); + + --font-sans: var(--nb-font-sans); + --font-mono: var(--nb-font-mono); + + --shadow-sm: var(--nb-shadow-sm); + --shadow-default: var(--nb-shadow); + --shadow-lg: var(--nb-shadow-lg); +} + +/* ---- Minimal overrides the plugin doesn't cover ---- */ +html { + font-family: var(--nb-font-sans); + font-feature-settings: "cv02", "cv03", "cv04", "cv11"; +} + +body { + opacity: 1; +} + +@media (prefers-reduced-motion: no-preference) { + html:focus-within { + scroll-behavior: smooth; + } +} + +::selection { + background: color-mix(in oklch, var(--nb-primary) 10%, transparent); + color: var(--nb-foreground); +} + +/* ---- Focus indicators ---- */ +@layer base { + :focus-visible { + outline: 2px solid var(--nb-ring); + outline-offset: 2px; + } +} + +/* ---- Reduced motion ---- */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* ---- Scroll lock (used by native overlays) ---- */ +[data-scroll-locked] { + overflow: hidden; +} + +/* ---- Scrollbar ---- */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: color-mix(in oklch, var(--nb-foreground) 20%, transparent); + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--nb-muted-foreground); +} + +/* ---- Shiki code-block styling + copy button ---- */ + +pre.astro-code { + position: relative; + margin: 1rem 0; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + border: 1px solid var(--nb-border); + background-color: var(--nb-card); + overflow-x: auto; + font-family: var(--nb-font-mono); + font-size: 0.875rem; + line-height: 1.625; +} +pre.astro-code > code { + background: transparent; +} + +/* Classed Shiki tokens set --shiki-light/--shiki-dark once in _nimbus/shiki.css. */ +pre.astro-code span { + color: var(--shiki-light, inherit); +} +[data-mode="dark"] pre.astro-code span { + color: var(--shiki-dark, inherit); +} + +/* Copy button injected by initCodeCopy. */ +.nb-code-copy { + position: absolute; + top: 0.5rem; + right: 0.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.75rem; + height: 1.75rem; + margin: 0; + padding: 0; + border: 1px solid var(--nb-border); + border-radius: 0.375rem; + background: var(--nb-card); + color: var(--nb-muted-foreground); + cursor: pointer; + opacity: 0; + transition: + opacity 0.15s ease, + color 0.15s ease, + background 0.15s ease; + z-index: 2; +} +.nb-code-figure:hover .nb-code-copy, +.nb-code-copy:focus-visible { + opacity: 1; +} +.nb-code-copy:hover { + color: var(--nb-foreground); + background: var(--nb-muted); +} +.nb-code-copy svg { + width: 0.875rem; + height: 0.875rem; +} +.nb-code-copy[data-state="copied"] { + color: var(--nb-success); +} + +/* ---- Premium code chrome ---- */ + +/* Every Shiki block is wrapped in a .nb-code-figure (titled or not). + Chrome lives on the figure so the language badge + copy button can sit + on a non-scrolling ancestor — iOS Safari slides absolutely-positioned + children of overflow:auto containers. */ +.nb-code-figure { + position: relative; + margin: 1rem 0; + border: 1px solid var(--nb-border); + border-radius: 0.5rem; + background-color: var(--nb-card); + overflow: hidden; +} +.nb-code-figure > pre.astro-code { + margin: 0; + border: 0; + border-radius: 0; + background-color: transparent; +} +.nb-code-title { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 0.5rem 0.875rem; + border-bottom: 1px solid var(--nb-border); + background: color-mix(in oklch, var(--nb-muted) 50%, transparent); + font-family: var(--nb-font-mono); + font-size: 0.75rem; + line-height: 1.4; +} +.nb-code-title-name { + color: var(--nb-foreground); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.nb-code-title-lang { + flex-shrink: 0; + padding: 0.0625rem 0.375rem; + border-radius: 0.25rem; + background: var(--nb-muted); + color: var(--nb-muted-foreground); + font-size: 0.6875rem; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +/* Language badge — pinned to the figure (which doesn't scroll), so it + stays at top-right even when the pre overflows horizontally. Hidden + when a figcaption is already showing the language (titled blocks). */ +.nb-code-figure[data-nb-lang]:not(.nb-code-figure-titled)::before { + content: attr(data-nb-lang); + position: absolute; + top: 0.5rem; + right: 0.625rem; + padding: 0.0625rem 0.375rem; + border-radius: 0.25rem; + background: var(--nb-muted); + color: var(--nb-muted-foreground); + font-family: var(--nb-font-mono); + font-size: 0.6875rem; + letter-spacing: 0.02em; + text-transform: uppercase; + pointer-events: none; + opacity: 0.75; + transition: opacity 0.15s ease; + z-index: 1; +} +.nb-code-figure:hover::before { + opacity: 0; +} +/* Hide plaintext/text "language" — it's noise. */ +.nb-code-figure[data-nb-lang="text"]::before, +.nb-code-figure[data-nb-lang="plaintext"]::before, +.nb-code-figure[data-nb-lang="plain"]::before, +.nb-code-figure[data-nb-lang="txt"]::before { + display: none; +} + +/* Block-level lines so tints + gutters paint across the full row. */ +pre.astro-code code { + display: block; + width: max-content; + min-width: 100%; +} +pre.astro-code .line { + display: inline-block; + width: 100%; + padding-inline: 1rem; + margin-inline: -1rem; + box-sizing: content-box; +} + +/* Highlighted lines (`{1,3-5}` meta or `// [!code highlight]`). */ +pre.astro-code .line.highlighted { + background: color-mix(in oklch, var(--nb-info) 12%, transparent); + box-shadow: inset 2px 0 0 var(--nb-info); +} + +/* Diff lines (`// [!code ++]` / `// [!code --]`). */ +pre.astro-code .line.diff { + position: relative; +} +pre.astro-code .line.diff.add { + background: color-mix(in oklch, var(--nb-success) 10%, transparent); + box-shadow: inset 2px 0 0 var(--nb-success); +} +pre.astro-code .line.diff.remove { + background: color-mix(in oklch, var(--nb-danger) 10%, transparent); + box-shadow: inset 2px 0 0 var(--nb-danger); +} +pre.astro-code .line.diff.add::before, +pre.astro-code .line.diff.remove::before { + position: absolute; + left: 0.25rem; + color: var(--nb-muted-foreground); + font-weight: 600; +} +pre.astro-code .line.diff.add::before { + content: "+"; + color: var(--nb-success); +} +pre.astro-code .line.diff.remove::before { + content: "−"; + color: var(--nb-danger); +} + +/* Focus mode — dim non-focused lines; clears on hover. */ +pre.astro-code.has-focused .line:not(.focused) { + opacity: 0.45; + filter: blur(0.25px); + transition: + opacity 0.2s ease, + filter 0.2s ease; +} +pre.astro-code.has-focused:hover .line:not(.focused) { + opacity: 1; + filter: none; +} + +/* Error / warning lines — use muted tokens for theme-tuned tints. */ +pre.astro-code .line.highlighted.error { + background: var(--nb-danger-muted); + box-shadow: inset 2px 0 0 var(--nb-danger); +} +pre.astro-code .line.highlighted.warning { + background: var(--nb-warning-muted); + box-shadow: inset 2px 0 0 var(--nb-warning); +} + +/* Word highlight (`// [!code word:foo]` and meta `/foo/`). */ +pre.astro-code .highlighted-word { + padding: 0.0625rem 0.25rem; + margin: -0.0625rem -0.125rem; + border-radius: 0.25rem; + background: color-mix(in oklch, var(--nb-info) 18%, transparent); + box-shadow: 0 0 0 1px color-mix(in oklch, var(--nb-info) 30%, transparent); +} + +/* ---- Mobile sidebar toggle ---- */ + +/* Hide hamburger on pages without a sidebar dialog. */ +body:not(:has([data-mobile-sidebar])) [data-menu-btn] { + display: none; +} diff --git a/docs/src/styles/prose.css b/docs/src/styles/prose.css new file mode 100644 index 0000000..192cbec --- /dev/null +++ b/docs/src/styles/prose.css @@ -0,0 +1,312 @@ +/* Docs markdown styling scoped to docs content containers only. */ + +.docs-content { + font-size: 0.9375rem; + line-height: 1.75; + color: var(--nb-foreground); +} + +/* Vertical rhythm — every direct child gets spacing by default. + Components can override with their own margin utilities (my-4, mt-6, etc.) + because :where() keeps specificity at (0,1,0) and utilities come later. */ +.docs-content > :where(:not(:first-child)) { + margin-top: 1.25rem; +} + +.docs-content :where(ul:not([class])) { + padding-left: 1.25rem; + list-style-type: disc; +} + +.docs-content :where(ol:not([class])) { + padding-left: 1.25rem; + list-style-type: decimal; +} + +.docs-content :where(ul:not([class]) ul:not([class])) { + list-style-type: circle; +} + +.docs-content :where(ul:not([class]) ul:not([class]) ul:not([class])) { + list-style-type: square; +} + +.docs-content :where(li:not([class])) { + margin: 0.25rem 0; +} + +.docs-content :where(blockquote:not([class])) { + margin: 1.25rem 0; + padding-left: 1rem; + border-left: 2px solid var(--nb-border); + color: var(--nb-muted-foreground); +} + +.docs-content :where(hr:not([class])) { + border: 0; + border-top: 1px solid var(--nb-border); + margin: 1.5rem 0; +} + +/* Component boundary — elements with a class attribute are component-owned. + Markdown never adds classes to h1–h6 or p; components always do. */ +.docs-content :where(h1, h2, h3, h4, h5, h6, p):where([class]) { + margin: 0; + font-size: inherit; +} + +/* Markdown heading typography is token-driven from globals.css. */ +.docs-content :where(h1:not([class])) { + font-size: var(--nb-h1-size); + font-weight: var(--nb-h1-weight); + letter-spacing: var(--nb-h1-tracking); + margin-top: 0; +} + +.docs-content :where(h2:not([class])) { + font-size: var(--nb-h2-size); + font-weight: var(--nb-h2-weight); + letter-spacing: var(--nb-h2-tracking); + margin-top: 2.5rem; +} + +.docs-content :where(h3:not([class])) { + font-size: var(--nb-h3-size); + font-weight: var(--nb-h3-weight); + letter-spacing: var(--nb-h3-tracking); + margin-top: 2rem; +} + +.docs-content :where(h4:not([class]), h5:not([class]), h6:not([class])) { + font-weight: var(--nb-h4-weight); +} + +.docs-content :where(h1:not([class]), h2:not([class]), h3:not([class]), h4:not([class])) { + scroll-margin-top: 5rem; +} + +/* Typographic wrapping — balance multi-line headings (no orphan words), + pretty-wrap body copy (no single-word last lines). Progressive + enhancement; unsupported browsers ignore both. */ +.docs-content :where(h1:not([class]), h2:not([class]), h3:not([class]), h4:not([class])) { + text-wrap: balance; +} + +.docs-content :where(p:not([class]), li:not([class]), blockquote:not([class])) { + text-wrap: pretty; +} + +.docs-content :where(h2[id], h3[id], h4[id]) { + position: relative; +} + +.docs-content .heading-anchor { + margin-left: 0.5rem; + color: var(--nb-muted-foreground); + text-decoration: none; + opacity: 0; + transition: + opacity 0.15s, + color 0.15s; +} + +.docs-content :where(h2[id], h3[id], h4[id]):hover .heading-anchor, +.docs-content .heading-anchor:focus-visible { + opacity: 1; +} + +.docs-content .heading-anchor:hover { + color: var(--nb-foreground); +} + +.docs-content :where(code:not([class])):not(:where(pre *)) { + font-family: var(--nb-font-mono); + direction: ltr; + unicode-bidi: isolate; + background: var(--nb-muted); + border: 1px solid var(--nb-border); + border-radius: 0.375rem; + padding: 0.125rem 0.375rem; + font-size: 0.8125em; + font-weight: 450; +} + +.docs-content :where(code:not([class])):not(:where(pre *))::before, +.docs-content :where(code:not([class])):not(:where(pre *))::after { + content: none; +} + +.docs-content :where(pre:not([class])) { + font-family: var(--nb-font-mono); +} + +/* Shiki-rendered code blocks — wider vertical margin than base rhythm + (1.25rem) because code blocks are visually dense. Inline code-token + styling (background, border, padding) is reset because Shiki spans + already carry their own colors via inline styles. */ +.docs-content .nb-code-figure { + margin: 1.5rem 0; +} + +.docs-content pre.astro-code code { + direction: ltr; + unicode-bidi: isolate; + background: none; + border: none; + padding: 0; + font-size: inherit; + font-weight: inherit; + border-radius: 0; +} + +/* Rounded bordered table that fills its container on desktop and falls back to + horizontal scroll on narrow viewports. The earlier rule used `display:block` + unconditionally, which kept the outer border at 100% but let thead/tbody + shrink to content width — leaving dead space on the right for tables with + short cells. */ +.docs-content :where(table:not([class])) { + display: table; + width: 100%; + max-width: 100%; + text-align: left; + border-collapse: separate; + border-spacing: 0; + border-radius: 0.75rem; + border: 1px solid var(--nb-border); + table-layout: auto; +} + +@media (max-width: 640px) { + .docs-content :where(table:not([class])) { + display: block; + overflow-x: auto; + overflow-y: hidden; + overscroll-behavior-x: contain; + -webkit-overflow-scrolling: touch; + } +} + +.docs-content :where(th:not([class])), +.docs-content :where(td:not([class])) { + padding: 0.625rem 0.75rem; + text-align: left; + vertical-align: top; + border-bottom: 1px solid var(--nb-border); +} + +.docs-content :where(th:not([class])) { + background: var(--nb-muted); + font-weight: 600; + font-size: 0.8125rem; + white-space: nowrap; +} + +.docs-content :where(tbody:not([class]) tr:not([class]):last-child td:not([class])) { + border-bottom: none; +} + +.docs-content :where(a:not([class])) { + color: var(--nb-foreground); + text-decoration-line: underline; + text-decoration-color: var(--nb-border-strong); + text-underline-offset: 3px; + text-decoration-thickness: 1px; + font-weight: 500; + transition: text-decoration-color 0.15s; +} + +.docs-content :where(a:not([class]):hover) { + text-decoration-color: var(--nb-foreground); +} + +/* ---- Long-tail prose vocabulary ---- + Elements markdown/MDX authors can reach for that would otherwise + render with raw browser defaults. Same component boundary as the rest + of the file: `:not([class])` leaves component-owned markup alone. */ + +/* Keycaps. The thicker bottom border stands in for a pressed edge — + border darkening, not a drop shadow. */ +.docs-content :where(kbd:not([class])) { + display: inline-block; + font-family: var(--nb-font-mono); + font-size: 0.75em; + font-weight: 500; + line-height: 1; + color: var(--nb-foreground); + background: var(--nb-muted); + border: 1px solid var(--nb-border); + border-bottom-width: 2px; + border-radius: 0.375rem; + padding: 0.1875rem 0.375rem; + vertical-align: 0.0625rem; +} + +/* Inline highlight — harmonized with ::selection instead of the + browser's fluorescent yellow. `box-decoration-break` keeps the tint + coherent when the highlight wraps across lines. */ +.docs-content :where(mark:not([class])) { + background: color-mix(in oklch, var(--nb-primary) 12%, transparent); + color: var(--nb-foreground); + border-radius: 0.25rem; + padding: 0.0625rem 0.25rem; + -webkit-box-decoration-break: clone; + box-decoration-break: clone; +} + +/* Native disclosure. Scope guard: FileTree builds classless
+ internally (`.file-tree :global(details)`), so exclude its subtree — + without this the rows below would grow double carets and borders. */ +.docs-content :where(details:not([class]):not(.file-tree *)) { + border: 1px solid var(--nb-border); + border-radius: 0.5rem; + padding: 0.625rem 0.875rem; +} + +.docs-content :where(details:not([class]):not(.file-tree *) > summary) { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.625rem; + cursor: pointer; + font-weight: 500; + list-style: none; + user-select: none; +} + +.docs-content :where(details:not([class]):not(.file-tree *) > summary)::-webkit-details-marker { + display: none; +} + +/* Trailing chevron — label left, indicator right, matching Collapsible. */ +.docs-content :where(details:not([class]):not(.file-tree *) > summary)::after { + content: ""; + flex-shrink: 0; + width: 0.4375em; + height: 0.4375em; + border-right: 1.5px solid var(--nb-muted-foreground); + border-bottom: 1.5px solid var(--nb-muted-foreground); + transform: rotate(-45deg); + transition: transform 0.2s cubic-bezier(0.32, 0.72, 0, 1); +} + +.docs-content :where(details[open]:not([class]):not(.file-tree *) > summary)::after { + transform: rotate(45deg) translateY(-0.125em); +} + +.docs-content :where(details[open]:not([class]):not(.file-tree *) > summary) { + margin-bottom: 0.5rem; +} + +/* Definition lists — term/description pairs. */ +.docs-content :where(dt:not([class])) { + font-weight: 600; +} + +.docs-content :where(dt:not([class]):not(:first-child)) { + margin-top: 0.75rem; +} + +.docs-content :where(dd:not([class])) { + margin-left: 1rem; + color: var(--nb-muted-foreground); +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 8a81642..abc8919 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -1,5 +1,11 @@ { "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist", "scripts"] } diff --git a/docs/wrangler.jsonc b/docs/wrangler.jsonc index e08f3fa..6b313f6 100644 --- a/docs/wrangler.jsonc +++ b/docs/wrangler.jsonc @@ -19,7 +19,7 @@ /** * On unmatched routes, serve the Astro-generated `404.html` with * a 404 status. Single-page-application mode is wrong for - * Starlight (it's MPA); `none` would return a bare 404 with no + * Nimbus (it's MPA); `none` would return a bare 404 with no * branding. */ "not_found_handling": "404-page", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2bd80db..b33a00e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 8.17.0 astro: specifier: ^7.0.0 - version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) p-retry: specifier: ^8.0.0 version: 8.0.0 @@ -80,26 +80,62 @@ importers: version: 6.0.3 vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@26.1.0)(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.1.0)(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)) docs: dependencies: - '@astrojs/starlight': - specifier: ^0.41.3 - version: 0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))(typescript@6.0.3) + '@fontsource-variable/inter': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource-variable/jetbrains-mono': + specifier: ^5.2.8 + version: 5.2.8 + '@iconify-json/ph': + specifier: ^1.2.0 + version: 1.2.2 astro: specifier: ^7.0.0 - version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + astro-icon: + specifier: ^1.1.0 + version: 1.1.5 + astro-og-canvas: + specifier: ^0.13.0 + version: 0.13.0(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) + canvaskit-wasm: + specifier: ^0.41.1 + version: 0.41.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + nimbus-docs: + specifier: ^0.1.22 + version: 0.1.22(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))(react@19.2.6)(shiki@4.3.1) sharp: specifier: ^0.35.3 version: 0.35.3(@types/node@26.1.0) + shiki: + specifier: ^4.2.0 + version: 4.3.1 + tailwind-merge: + specifier: ^3.5.0 + version: 3.6.0 devDependencies: '@astrojs/check': specifier: ^0.9.9 version: 0.9.9(prettier@3.9.4)(typescript@6.0.3) + '@tailwindcss/postcss': + specifier: ^4.1.4 + version: 4.3.2 '@types/node': specifier: ^26.1.0 version: 26.1.0 + pagefind: + specifier: ^1.5.2 + version: 1.5.2 + tailwindcss: + specifier: ^4.1.4 + version: 4.3.2 tsx: specifier: ^4.19.0 version: 4.22.2 @@ -114,13 +150,13 @@ importers: dependencies: '@astrojs/mdx': specifier: ^7.0.0 - version: 7.0.2(@astrojs/markdown-satteri@0.3.3)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) + version: 7.0.2(@astrojs/markdown-satteri@0.3.3)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) '@cloudflare/polystella': specifier: workspace:* version: link:../.. astro: specifier: ^7.0.0 - version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + version: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) devDependencies: '@types/node': specifier: ^26.1.0 @@ -131,6 +167,16 @@ importers: packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@8.1.1': + resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + '@astrojs/check@0.9.9': resolution: {integrity: sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==} hasBin: true @@ -205,6 +251,9 @@ packages: '@astrojs/compiler@2.13.1': resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} + '@astrojs/internal-helpers@0.10.0': + resolution: {integrity: sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw==} + '@astrojs/internal-helpers@0.10.1': resolution: {integrity: sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q==} @@ -220,12 +269,28 @@ packages: prettier-plugin-astro: optional: true + '@astrojs/markdown-remark@7.2.0': + resolution: {integrity: sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw==} + '@astrojs/markdown-remark@7.2.1': resolution: {integrity: sha512-jPVNIqTvk+yKviikszv/Y1U4jGUSKpp/Nw48QZV4qjWgp70j4Lkq3lhSDRbWwCfgKvEyO9GHuVbV1dM2WYXy1w==} + '@astrojs/markdown-satteri@0.2.1': + resolution: {integrity: sha512-rsdT8ucMLmu1v/POYApJcfH/t30Yuae6pZdfFU0yG3Kya8pwNItcJ6TLqUACRlRV9W50g4gLIxCwwfWjKsJ67w==} + '@astrojs/markdown-satteri@0.3.3': resolution: {integrity: sha512-Lje33Ittd8UQGgbIIWQvhPkj5X5c4b1sZnZWX3JQV/AWpfbuQGxVi2ONt6+ScydcwfR4egilslEWyczMclrJ1g==} + '@astrojs/mdx@6.0.1': + resolution: {integrity: sha512-J5K8F7A1LMH+cj+dcxm+uAeIznkfwxMcRpG7DD6ABNDOt8da98ph6ie4TD+4FV/ojVOJK0PQGWY4hmxQORLv0w==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@astrojs/markdown-satteri': 0.2.1 + astro: ^6.4.0 + peerDependenciesMeta: + '@astrojs/markdown-satteri': + optional: true + '@astrojs/mdx@7.0.2': resolution: {integrity: sha512-l+sJY5U1KkGZUdr+bIL4Y6BefeS549qoSHVSkUSs6A9INwdCND+/0+vN0NroPBXwl5Vcg5u78t7VQRsJjePxbw==} engines: {node: '>=22.12.0'} @@ -243,15 +308,6 @@ packages: '@astrojs/sitemap@3.7.3': resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==} - '@astrojs/starlight@0.41.3': - resolution: {integrity: sha512-8xsG6UpK581TJmtOc7/pnxHKEbP16rV06VLWaVa7FOyE/VEwnaHOsLtL+miifCEfuiuv0Wn+j8u3JSluRQesMQ==} - peerDependencies: - '@astrojs/markdown-remark': ^7.2.0 - astro: ^7.0.2 - peerDependenciesMeta: - '@astrojs/markdown-remark': - optional: true - '@astrojs/telemetry@3.3.2': resolution: {integrity: sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} @@ -280,11 +336,21 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bruits/satteri-darwin-arm64@0.6.3': + resolution: {integrity: sha512-oKgMfpmNzQ8vaqmkE37PBu8tOyVjoOc4s+DV2tLpWvO6WO467qn/+Nbcirm/ceer7wUM8v4vMLGcZO0gkRzBEg==} + cpu: [arm64] + os: [darwin] + '@bruits/satteri-darwin-arm64@0.9.4': resolution: {integrity: sha512-W3MSUkr2mZRR8Stoe+lqNAyzQzRuFMU8WffV9IvFSxTok0LGWR0ZZQPLELU4QTRiUbhL2Y4VUP9vV7pj8rHjgg==} cpu: [arm64] os: [darwin] + '@bruits/satteri-darwin-x64@0.6.3': + resolution: {integrity: sha512-9I5pbwZRWH5LvhoCtwpRr4rYSDe43/dLvps6zO70ipVF2XbH4rJ20T+EfvPcmou5jsWMsq9Ybn5GX3PwlSrBaw==} + cpu: [x64] + os: [darwin] + '@bruits/satteri-darwin-x64@0.9.4': resolution: {integrity: sha512-DXOuuaE1lsv7mpk2mOvGrzqoEWEvOIZEO/fXVa7zfM23Iob+CBjBkRAMwpHA4pmZ3j6Gj7WJzPKw0kQ7w741AQ==} cpu: [x64] @@ -302,6 +368,12 @@ packages: os: [linux] libc: [musl] + '@bruits/satteri-linux-x64-gnu@0.6.3': + resolution: {integrity: sha512-aFfw2DL2HpIcAQ8I3ZEtKuz+/GoF0H0sq387jAlvr00Q7buiiFbvARFuQlvTk00N7u3SJIh/3+YkKssTJDT+yQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@bruits/satteri-linux-x64-gnu@0.9.4': resolution: {integrity: sha512-MR1Q+wMx65FQlbSV7cRqWW87Knp0zkoaIV55Dt+xZl028wJABXEPEEmG3670SLq7lVZvcGIDwCgSg2kCYxvRwA==} cpu: [x64] @@ -314,6 +386,11 @@ packages: os: [linux] libc: [musl] + '@bruits/satteri-wasm32-wasi@0.6.3': + resolution: {integrity: sha512-DdpfnJ+04Mb4YtHaxAeETUvhdxFKg3URnroGY39FvweJEgeXx8cFNutuF5w904BhqaiblhmF76AoBnJwNLxsXg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + '@bruits/satteri-wasm32-wasi@0.9.4': resolution: {integrity: sha512-/CEG8LUlpaBEnhFnYVn0UnlHFLs51UhrkJBUPDUXLzkadzAcnR88iRA/nOl7Zwhjb4WhfBV4p3P5qeOJMtH0iA==} engines: {node: '>=14.0.0'} @@ -324,6 +401,11 @@ packages: cpu: [arm64] os: [win32] + '@bruits/satteri-win32-x64-msvc@0.6.3': + resolution: {integrity: sha512-J/CZqACnBbv77eImx/JeO5RmCuCyliihiC81u3M4VobA8eupsygaPen3/UFFqf3yfeHvMlE3myilouh/2iHMOA==} + cpu: [x64] + os: [win32] + '@bruits/satteri-win32-x64-msvc@0.9.4': resolution: {integrity: sha512-5I7SiarsNdAUuhJb50CXJPTwr/ECVrBoU+fymoLjChK5fW//+srhY4lstcNTzgFRtQSYfVtm4OQZz16CVMeTeA==} cpu: [x64] @@ -394,10 +476,16 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@clack/core@0.4.1': + resolution: {integrity: sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==} + '@clack/core@1.3.1': resolution: {integrity: sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==} engines: {node: '>= 20.12.0'} + '@clack/prompts@0.9.1': + resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==} + '@clack/prompts@1.4.0': resolution: {integrity: sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==} engines: {node: '>= 20.12.0'} @@ -449,10 +537,6 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@ctrl/tinycolor@4.2.0': - resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} - engines: {node: '>=14'} - '@emmetio/abbreviation@2.3.3': resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} @@ -477,6 +561,9 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.9.1': + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} @@ -486,6 +573,12 @@ packages: '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + + '@emnapi/wasi-threads@1.2.0': + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} @@ -801,17 +894,23 @@ packages: cpu: [x64] os: [win32] - '@expressive-code/core@0.44.0': - resolution: {integrity: sha512-xgiF2P6tYUbrhi3+x0S8xHZWT1t3Bvb3U91tAtRbLb9HLejLvYc5GZUqKICKLaUN4iSGhhNJu2fM/aH8e5yCMg==} + '@fontsource-variable/inter@5.2.8': + resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} + + '@fontsource-variable/jetbrains-mono@5.2.8': + resolution: {integrity: sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==} - '@expressive-code/plugin-frames@0.44.0': - resolution: {integrity: sha512-V6M6+zVc1GzqCvXkQHc2m5rcFOIVzJgMq5gnfrMnVf2gwtj/sg4H93c1f/mGeqHycubwkHFUDyParAOiGeDZeA==} + '@iconify-json/ph@1.2.2': + resolution: {integrity: sha512-PgkEZNtqa8hBGjHXQa4pMwZa93hmfu8FUSjs/nv4oUU6yLsgv+gh9nu28Kqi8Fz9CCVu4hj1MZs9/60J57IzFw==} - '@expressive-code/plugin-shiki@0.44.0': - resolution: {integrity: sha512-RZsdaqlbGqyAQKuoX4myQXxjmiE2l5KBpJ/gKPh62tCdIdpWyjbzVqSo8+5XsezZxkfi8AJ/J6EUaBTPROFX/Q==} + '@iconify/tools@4.2.0': + resolution: {integrity: sha512-WRxPva/ipxYkqZd1+CkEAQmd86dQmrwH0vwK89gmp2Kh2WyyVw57XbPng0NehP3x4V1LzLsXUneP1uMfTMZmUA==} - '@expressive-code/plugin-text-markers@0.44.0': - resolution: {integrity: sha512-0/m3A5b+lz2upyNq+wzZ1S69HRoJmyFs5LsR42lVZ9pmGRlBiSBYQpvqlji4DBj1+Riamxc0AvcCr5kuzOQeWA==} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.3.0': + resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} @@ -1133,6 +1232,16 @@ packages: '@types/node': optional: true + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1140,6 +1249,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -1186,9 +1298,6 @@ packages: cpu: [x64] os: [darwin] - '@pagefind/default-ui@1.5.2': - resolution: {integrity: sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==} - '@pagefind/freebsd-x64@1.5.2': resolution: {integrity: sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==} cpu: [x64] @@ -1492,6 +1601,10 @@ packages: resolution: {integrity: sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==} engines: {node: '>=20'} + '@shikijs/transformers@4.3.1': + resolution: {integrity: sha512-z6ir0bGDgWcF2FduktEfPgIsdOtIlDiLAjFBgBzE42Q9xHbkkIXZtORHzlLVB71iZP9elEcqKg6keajvOUwE2A==} + engines: {node: '>=20'} + '@shikijs/types@4.3.1': resolution: {integrity: sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==} engines: {node: '>=20'} @@ -1509,6 +1622,98 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tailwindcss/node@4.3.2': + resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} + + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.2': + resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.2': + resolution: {integrity: sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==} + '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -1533,9 +1738,6 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/js-yaml@4.0.9': - resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} - '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1572,9 +1774,16 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@vercel/detect-agent@1.2.3': + resolution: {integrity: sha512-VYNCgUc0nOmC4WJmWw9GkrKdfr8Zl4/rxhC5SvgacBgxiW9W/9NRttUoHHXV8xdII3MaRgkZZVX8Ikzc/Jmjag==} + engines: {node: '>=14'} + '@vitest/expect@4.1.9': resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} @@ -1630,6 +1839,9 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + '@webgpu/types@0.1.21': + resolution: {integrity: sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1699,10 +1911,13 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - astro-expressive-code@0.44.0: - resolution: {integrity: sha512-b1wN/ZvbJprzxlGKIpIes2kQrCY5KRLwys2tWbZAZyjGZcW5ZtgneZnBwzNRiBna9/48d4mQl19KLjcRuhO1hw==} + astro-icon@1.1.5: + resolution: {integrity: sha512-CJYS5nWOw9jz4RpGWmzNQY7D0y2ZZacH7atL2K9DeJXJVaz7/5WrxeyIxO8KASk1jCM96Q4LjRx/F3R+InjJrw==} + + astro-og-canvas@0.13.0: + resolution: {integrity: sha512-jcIDhE9OGIbd7LstZQ4CyzOpbbEKAlxJqEgdkVu6r26m7yJKlQjKysszfDxlox717cnCLPXKivyFjsufcq8sYA==} peerDependencies: - astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0 + astro: ^5.0.0 || ^6.0.0 || ^7.0.0 astro@7.0.6: resolution: {integrity: sha512-Myw0sFia+zs/Y0yqfZEsUYXfDPh3ELcLf1f0Q/qQzVXBh/af1qO62WNT+P89DCcfGVV51nMoQhEfkBYqJmoUOQ==} @@ -1721,11 +1936,8 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - bcp-47-match@2.0.3: - resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} - - bcp-47@2.1.1: - resolution: {integrity: sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} @@ -1741,6 +1953,12 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + canvaskit-wasm@0.41.1: + resolution: {integrity: sha512-gO2rNMIE0lOQ8MaarM4qNeq2zZAEknAObKP2XAKidMSAY8P5sURGdFkrx1TSdHuBFqTf5w35JqjuUzjibqkD5g==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1763,6 +1981,13 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} + engines: {node: '>=20.18.1'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1771,6 +1996,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -1800,10 +2029,20 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + common-ancestor-path@2.0.0: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1824,13 +2063,14 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - css-selector-parser@3.3.0: - resolution: {integrity: sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==} - css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@3.2.1: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -1839,11 +2079,6 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -1884,6 +2119,10 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} @@ -1898,10 +2137,6 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - direction@2.0.1: - resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} - hasBin: true - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1929,6 +2164,16 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -1941,6 +2186,14 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} @@ -2007,8 +2260,8 @@ packages: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} - expressive-code@0.44.0: - resolution: {integrity: sha512-JXVWVNCKlLuZLMQH8cOiDUSosT0Bb+elwE/dbAkpwFwDFmyFyWlECoWZIohh2FkIF1iI67TQJ+Ts9k7oNDh2qA==} + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -2016,6 +2269,11 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2041,6 +2299,9 @@ packages: fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2090,6 +2351,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + get-tsconfig@5.0.0-beta.4: resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} engines: {node: '>=20.20.0'} @@ -2101,6 +2366,10 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2111,42 +2380,21 @@ packages: h3@1.15.11: resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} - hast-util-embedded@3.0.0: - resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} - - hast-util-format@1.1.0: - resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} - hast-util-from-html@2.0.3: resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} hast-util-from-parse5@8.0.3: resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - hast-util-has-property@3.0.0: - resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} - - hast-util-is-body-ok-link@3.0.1: - resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} - hast-util-is-element@3.0.0: resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - hast-util-minify-whitespace@1.0.1: - resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} - hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - hast-util-phrasing@3.0.1: - resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} - hast-util-raw@9.1.0: resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} - hast-util-select@6.0.4: - resolution: {integrity: sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==} - hast-util-to-estree@3.1.3: resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} @@ -2159,9 +2407,6 @@ packages: hast-util-to-parse5@8.0.1: resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} - hast-util-to-string@3.0.1: - resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} - hast-util-to-text@4.0.2: resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} @@ -2177,8 +2422,8 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - html-whitespace-sensitive-tag-names@3.0.1: - resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -2187,13 +2432,9 @@ packages: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true - i18next@26.3.4: - resolution: {integrity: sha512-pa7m0d7pBDqGHZxljT+WPFeyFgQ7P7SciPPo1tTqYuO0z4sqADYhwnBESmmGp/wEof1inwdls/k8ZgTg8rxFHA==} - peerDependencies: - typescript: ^5 || ^6 - peerDependenciesMeta: - typescript: - optional: true + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} @@ -2275,6 +2516,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-yaml@3.15.0: resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true @@ -2299,9 +2544,8 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - klona@2.0.6: - resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} - engines: {node: '>= 8'} + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} @@ -2377,6 +2621,10 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2407,9 +2655,6 @@ packages: mdast-util-definitions@6.0.0: resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} - mdast-util-directive@3.1.0: - resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} - mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -2464,6 +2709,9 @@ packages: mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -2474,9 +2722,6 @@ packages: micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - micromark-extension-directive@4.0.0: - resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} - micromark-extension-frontmatter@2.0.0: resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} @@ -2591,6 +2836,17 @@ packages: engines: {node: '>=22.0.0'} hasBin: true + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -2614,6 +2870,20 @@ packages: resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} engines: {node: '>= 10'} + nimbus-docs@0.1.22: + resolution: {integrity: sha512-XLtC4CXg9BM+nhNH0zmVwb6H9eXRPAH0UpbgbhtkKJapckDViKLhryW8WcNZa9KBnfnIIH9Eqxq65IzJQS7O+g==} + hasBin: true + peerDependencies: + astro: '>=6.4.0' + react: '>=19.0.0' + react-dom: '>=19.0.0' + shiki: ^4.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} @@ -2649,6 +2919,9 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + oniguruma-parser@0.12.2: resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} @@ -2710,6 +2983,12 @@ packages: parse-latin@7.0.0: resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -2734,6 +3013,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + piccolore@0.1.3: resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} @@ -2756,15 +3038,11 @@ packages: resolution: {integrity: sha512-u9mdErTewKSMsr+ceCt8VcNuNP0ro5AXiPXhUVApuEyqr2Zlvt+DdCFBcm+yGWN8mhOdZJ27meIDbnoZgfzpOw==} hasBin: true - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - postcss-selector-parser@6.1.4: - resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} - engines: {node: '>=4'} + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} @@ -2796,12 +3074,18 @@ packages: property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quotation@2.0.3: + resolution: {integrity: sha512-yEc24TEgCFLXx7D4JHJJkK4JFVtatO8fziwUxY4nB/Jbea9o9CVS3gt22mA0W7rPYAGW2fWzYDSOtD94PwOyqA==} + radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} @@ -2844,15 +3128,6 @@ packages: regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - rehype-expressive-code@0.44.0: - resolution: {integrity: sha512-5r74C5F2sMR3X+QJH8OKWgZBO/cqRw5W1fLT6GVlSfLqepk+4j8tGFkyPqZYWjwntsBHzKPDH2zI68sZ7ScLfA==} - - rehype-format@5.0.1: - resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} - - rehype-parse@9.0.1: - resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} - rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} @@ -2862,18 +3137,36 @@ packages: rehype-stringify@10.0.1: resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} - rehype@13.0.2: - resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} - - remark-directive@4.0.0: - resolution: {integrity: sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==} - remark-frontmatter@5.0.0: resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-lint-emphasis-marker@4.0.1: + resolution: {integrity: sha512-BF1WWsAxai3XoKk48sfiqT3L8m02AZLj3BnipWkHDRXuLfz6VwsHVaHWyNvvE0p6b2B3A5dSYbcfJu5RmPx4tQ==} + + remark-lint-fenced-code-flag@4.2.0: + resolution: {integrity: sha512-QWGTrnYbcopOFZR98djDREmKApLonJ7hmXE7pEcOGee9JY/EUIVS7Lq54Hy9CtU3cVIvQQmiMTxCwUhfddDJFA==} + + remark-lint-heading-increment@4.0.1: + resolution: {integrity: sha512-uat7RTQn0hGlMv62p7yjLlg3tO3RljFbH6C+0M+5BNEF+s3NrA8jJgqW0UwLLNdCd3EABCKaWloHumT57ND7PQ==} + + remark-lint-no-duplicate-headings@4.0.1: + resolution: {integrity: sha512-6lggqnpIe5FepikjYF2me3ovKV4oD/rAz8WmwVbLR2cLkce1iH+PB7jyxk/A2gQQqrDcIlRMA5Ct2Yj56cEwhQ==} + + remark-lint-no-heading-punctuation@4.0.1: + resolution: {integrity: sha512-lpSVFEHPDKGWi8YPeO51xmLNVON5A2cGz0Y8VRkW0f2l6LvEkPTMjQAvA84AQu/10TrxTbIzU/tQlRLpG96QUA==} + + remark-lint-no-literal-urls@4.0.1: + resolution: {integrity: sha512-RhTANFkFFXE6bM+WxWcPo2TTPEfkWG3lJZU50ycW7tJJmxUzDNzRed/z80EVJIdGwFa0NntVooLUJp3xrogalQ==} + + remark-lint-no-multiple-toplevel-headings@4.0.1: + resolution: {integrity: sha512-8sepobIOu3PlDOuMH7jtri+LH4tFNVQU+aqKSkrlNRdp831fYz9S+jA2crTVqWqxVbTwiF96uJWePv8/9qmHnA==} + + remark-lint-unordered-list-marker-style@4.0.1: + resolution: {integrity: sha512-HMrVQC0Qbr8ktSy+1lJGRGU10qecL3T14L6s/THEQXR5Tk0wcsLLG0auNvB4r2+H+ClhVO/Vnm1TEosh1OCsfw==} + remark-mdx@3.1.1: resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} @@ -2947,6 +3240,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + satteri@0.6.3: + resolution: {integrity: sha512-iY5xd2tBDQveYRFkL1F0cegkabWSVoXHi64e1p49SiCs1bZDaqTQPGQI+PqEQIaWkz0iqK80CuQQUYvhsssviw==} + satteri@0.9.4: resolution: {integrity: sha512-BKob126Tay84diOZsnVNH/Q/c+3njPJTCad3w5zLKa6j8bVjxskPNHDtxrMwYK4bN/RlqUSdMnPwKY4k65EMOQ==} @@ -3063,11 +3359,30 @@ packages: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} + svgo@3.3.3: + resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==} + engines: {node: '>=14.0.0'} + hasBin: true + svgo@4.0.1: resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} engines: {node: '>=16'} hasBin: true + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.2: + resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tar@7.5.19: + resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -3148,6 +3463,9 @@ packages: unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unified-lint-rule@3.0.1: + resolution: {integrity: sha512-HxIeQOmwL19DGsxHXbeyzKHBsoSCFO7UtRVUvT2v61ptw/G+GbysWcrpHdfs5jqbIFDA11MoKngIhQK0BeTVjA==} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -3250,13 +3568,6 @@ packages: uploadthing: optional: true - url-extras@0.1.0: - resolution: {integrity: sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==} - engines: {node: '>=20'} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -3456,6 +3767,15 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -3492,6 +3812,9 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -3511,6 +3834,10 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml-language-server@1.20.0: resolution: {integrity: sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA==} hasBin: true @@ -3537,6 +3864,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@1.2.2: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} @@ -3555,6 +3885,15 @@ packages: snapshots: + '@alloc/quick-lru@5.2.0': {} + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.2.4 + + '@antfu/utils@8.1.1': {} + '@astrojs/check@0.9.9(prettier@3.9.4)(typescript@6.0.3)': dependencies: '@astrojs/language-server': 2.16.9(prettier@3.9.4)(typescript@6.0.3) @@ -3622,6 +3961,17 @@ snapshots: '@astrojs/compiler@2.13.1': {} + '@astrojs/internal-helpers@0.10.0': + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + js-yaml: 4.3.0 + picomatch: 4.0.5 + retext-smartypants: 6.2.0 + shiki: 4.3.1 + smol-toml: 1.7.0 + unified: 11.0.5 + '@astrojs/internal-helpers@0.10.1': dependencies: '@types/hast': 3.0.4 @@ -3658,6 +4008,28 @@ snapshots: transitivePeerDependencies: - typescript + '@astrojs/markdown-remark@7.2.0': + dependencies: + '@astrojs/internal-helpers': 0.10.0 + '@astrojs/prism': 4.0.2 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + '@astrojs/markdown-remark@7.2.1': dependencies: '@astrojs/internal-helpers': 0.10.1 @@ -3680,6 +4052,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@astrojs/markdown-satteri@0.2.1': + dependencies: + '@astrojs/internal-helpers': 0.10.0 + github-slugger: 2.0.0 + satteri: 0.6.3 + '@astrojs/markdown-satteri@0.3.3': dependencies: '@astrojs/internal-helpers': 0.10.1 @@ -3687,13 +4065,13 @@ snapshots: github-slugger: 2.0.0 satteri: 0.9.4 - '@astrojs/mdx@7.0.2(@astrojs/markdown-satteri@0.3.3)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))': + '@astrojs/mdx@6.0.1(@astrojs/markdown-satteri@0.2.1)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))': dependencies: - '@astrojs/internal-helpers': 0.10.1 - '@astrojs/markdown-remark': 7.2.1 + '@astrojs/internal-helpers': 0.10.0 + '@astrojs/markdown-remark': 7.2.0 '@mdx-js/mdx': 3.1.1 acorn: 8.17.0 - astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) es-module-lexer: 2.3.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -3705,11 +4083,33 @@ snapshots: unist-util-visit: 5.1.0 vfile: 6.0.3 optionalDependencies: - '@astrojs/markdown-satteri': 0.3.3 + '@astrojs/markdown-satteri': 0.2.1 transitivePeerDependencies: - supports-color - '@astrojs/prism@4.0.2': + '@astrojs/mdx@7.0.2(@astrojs/markdown-satteri@0.3.3)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))': + dependencies: + '@astrojs/internal-helpers': 0.10.1 + '@astrojs/markdown-remark': 7.2.1 + '@mdx-js/mdx': 3.1.1 + acorn: 8.17.0 + astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + es-module-lexer: 2.3.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + piccolore: 0.1.3 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.6 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + optionalDependencies: + '@astrojs/markdown-satteri': 0.3.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@4.0.2': dependencies: prismjs: 1.30.0 @@ -3719,44 +4119,6 @@ snapshots: stream-replace-string: 2.0.0 zod: 4.4.3 - '@astrojs/starlight@0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))(typescript@6.0.3)': - dependencies: - '@astrojs/markdown-satteri': 0.3.3 - '@astrojs/mdx': 7.0.2(@astrojs/markdown-satteri@0.3.3)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) - '@astrojs/sitemap': 3.7.3 - '@pagefind/default-ui': 1.5.2 - '@types/hast': 3.0.4 - '@types/js-yaml': 4.0.9 - '@types/mdast': 4.0.4 - astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) - astro-expressive-code: 0.44.0(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) - bcp-47: 2.1.1 - hast-util-from-html: 2.0.3 - hast-util-select: 6.0.4 - hast-util-to-string: 3.0.1 - hastscript: 9.0.1 - i18next: 26.3.4(typescript@6.0.3) - js-yaml: 4.3.0 - klona: 2.0.6 - magic-string: 0.30.21 - mdast-util-directive: 3.1.0 - mdast-util-to-markdown: 2.1.2 - mdast-util-to-string: 4.0.0 - pagefind: 1.5.2 - rehype: 13.0.2 - rehype-format: 5.0.1 - remark-directive: 4.0.0 - satteri: 0.9.4 - ultrahtml: 1.6.0 - unified: 11.0.5 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - optionalDependencies: - '@astrojs/markdown-remark': 7.2.1 - transitivePeerDependencies: - - supports-color - - typescript - '@astrojs/telemetry@3.3.2': dependencies: ci-info: 4.4.0 @@ -3784,9 +4146,15 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bruits/satteri-darwin-arm64@0.6.3': + optional: true + '@bruits/satteri-darwin-arm64@0.9.4': optional: true + '@bruits/satteri-darwin-x64@0.6.3': + optional: true + '@bruits/satteri-darwin-x64@0.9.4': optional: true @@ -3796,12 +4164,22 @@ snapshots: '@bruits/satteri-linux-arm64-musl@0.9.4': optional: true + '@bruits/satteri-linux-x64-gnu@0.6.3': + optional: true + '@bruits/satteri-linux-x64-gnu@0.9.4': optional: true '@bruits/satteri-linux-x64-musl@0.9.4': optional: true + '@bruits/satteri-wasm32-wasi@0.6.3': + dependencies: + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + optional: true + '@bruits/satteri-wasm32-wasi@0.9.4': dependencies: '@emnapi/core': 1.11.1 @@ -3812,6 +4190,9 @@ snapshots: '@bruits/satteri-win32-arm64-msvc@0.9.4': optional: true + '@bruits/satteri-win32-x64-msvc@0.6.3': + optional: true + '@bruits/satteri-win32-x64-msvc@0.9.4': optional: true @@ -3977,11 +4358,22 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 + '@clack/core@0.4.1': + dependencies: + picocolors: 1.1.1 + sisteransi: 1.0.5 + '@clack/core@1.3.1': dependencies: fast-wrap-ansi: 0.2.0 sisteransi: 1.0.5 + '@clack/prompts@0.9.1': + dependencies: + '@clack/core': 0.4.1 + picocolors: 1.1.1 + sisteransi: 1.0.5 + '@clack/prompts@1.4.0': dependencies: '@clack/core': 1.3.1 @@ -4016,8 +4408,6 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@ctrl/tinycolor@4.2.0': {} - '@emmetio/abbreviation@2.3.3': dependencies: '@emmetio/scanner': 1.0.4 @@ -4047,6 +4437,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.9.1': + dependencies: + '@emnapi/wasi-threads': 1.2.0 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 @@ -4062,6 +4458,16 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.9.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.0': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 @@ -4223,30 +4629,42 @@ snapshots: '@esbuild/win32-x64@0.28.0': optional: true - '@expressive-code/core@0.44.0': - dependencies: - '@ctrl/tinycolor': 4.2.0 - hast-util-select: 6.0.4 - hast-util-to-html: 9.0.5 - hast-util-to-text: 4.0.2 - hastscript: 9.0.1 - postcss: 8.5.16 - postcss-nested: 6.2.0(postcss@8.5.16) - unist-util-visit: 5.1.0 - unist-util-visit-parents: 6.0.2 + '@fontsource-variable/inter@5.2.8': {} + + '@fontsource-variable/jetbrains-mono@5.2.8': {} - '@expressive-code/plugin-frames@0.44.0': + '@iconify-json/ph@1.2.2': dependencies: - '@expressive-code/core': 0.44.0 + '@iconify/types': 2.0.0 - '@expressive-code/plugin-shiki@0.44.0': + '@iconify/tools@4.2.0': dependencies: - '@expressive-code/core': 0.44.0 - shiki: 4.3.1 + '@iconify/types': 2.0.0 + '@iconify/utils': 2.3.0 + cheerio: 1.2.0 + domhandler: 5.0.3 + extract-zip: 2.0.1 + local-pkg: 1.2.1 + pathe: 2.0.3 + svgo: 3.3.3 + tar: 7.5.19 + transitivePeerDependencies: + - supports-color + + '@iconify/types@2.0.0': {} - '@expressive-code/plugin-text-markers@0.44.0': + '@iconify/utils@2.3.0': dependencies: - '@expressive-code/core': 0.44.0 + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 8.1.1 + '@iconify/types': 2.0.0 + debug: 4.4.3 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.2.1 + mlly: 1.8.2 + transitivePeerDependencies: + - supports-color '@img/colour@1.1.0': {} @@ -4455,10 +4873,29 @@ snapshots: optionalDependencies: '@types/node': 26.1.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -4524,6 +4961,13 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': + dependencies: + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4546,8 +4990,6 @@ snapshots: '@pagefind/darwin-x64@1.5.2': optional: true - '@pagefind/default-ui@1.5.2': {} - '@pagefind/freebsd-x64@1.5.2': optional: true @@ -4742,6 +5184,11 @@ snapshots: dependencies: '@shikijs/types': 4.3.1 + '@shikijs/transformers@4.3.1': + dependencies: + '@shikijs/core': 4.3.1 + '@shikijs/types': 4.3.1 + '@shikijs/types@4.3.1': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -4755,6 +5202,75 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tailwindcss/node@4.3.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.2 + + '@tailwindcss/oxide-android-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide@4.3.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-x64': 4.3.2 + '@tailwindcss/oxide-freebsd-x64': 4.3.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-x64-musl': 4.3.2 + '@tailwindcss/oxide-wasm32-wasi': 4.3.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 + + '@tailwindcss/postcss@4.3.2': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + postcss: 8.5.16 + tailwindcss: 4.3.2 + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 @@ -4784,8 +5300,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/js-yaml@4.0.9': {} - '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -4822,8 +5336,15 @@ snapshots: '@types/unist@3.0.3': {} + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 26.1.0 + optional: true + '@ungap/structured-clone@1.3.1': {} + '@vercel/detect-agent@1.2.3': {} + '@vitest/expect@4.1.9': dependencies: '@standard-schema/spec': 1.1.0 @@ -4833,13 +5354,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0))': + '@vitest/mocker@4.1.9(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0) '@vitest/pretty-format@4.1.9': dependencies: @@ -4915,6 +5436,8 @@ snapshots: '@vscode/l10n@0.0.18': {} + '@webgpu/types@0.1.21': {} + acorn-jsx@5.3.2(acorn@8.17.0): dependencies: acorn: 8.17.0 @@ -4967,13 +5490,22 @@ snapshots: astring@1.9.0: {} - astro-expressive-code@0.44.0(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)): + astro-icon@1.1.5: dependencies: - astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) - rehype-expressive-code: 0.44.0 - url-extras: 0.1.0 + '@iconify/tools': 4.2.0 + '@iconify/types': 2.0.0 + '@iconify/utils': 2.3.0 + transitivePeerDependencies: + - supports-color + + astro-og-canvas@0.13.0(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)): + dependencies: + astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + canvaskit-wasm: 0.41.1 + deterministic-object-hash: 2.0.2 + entities: 8.0.0 - astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0): + astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0): dependencies: '@astrojs/compiler-rs': 0.3.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2) '@astrojs/internal-helpers': 0.10.1 @@ -5023,8 +5555,8 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.4 unstorage: 1.17.5 - vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0)) + vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -5070,13 +5602,7 @@ snapshots: bail@2.0.2: {} - bcp-47-match@2.0.3: {} - - bcp-47@2.1.1: - dependencies: - is-alphabetical: 2.0.1 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 + base-64@1.0.0: {} better-path-resolve@1.0.0: dependencies: @@ -5090,6 +5616,12 @@ snapshots: dependencies: fill-range: 7.1.1 + buffer-crc32@0.2.13: {} + + canvaskit-wasm@0.41.1: + dependencies: + '@webgpu/types': 0.1.21 + ccount@2.0.1: {} chai@6.2.2: {} @@ -5104,6 +5636,29 @@ snapshots: chardet@2.1.1: {} + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.2.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.24.8 + whatwg-mimetype: 4.0.0 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -5112,6 +5667,8 @@ snapshots: dependencies: readdirp: 5.0.0 + chownr@3.0.0: {} + ci-info@4.4.0: {} cliui@8.0.1: @@ -5134,8 +5691,14 @@ snapshots: commander@11.1.0: {} + commander@7.2.0: {} + common-ancestor-path@2.0.0: {} + confbox@0.1.8: {} + + confbox@0.2.4: {} + convert-source-map@2.0.0: {} cookie-es@1.2.3: {} @@ -5160,13 +5723,16 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 - css-selector-parser@3.3.0: {} - css-tree@2.2.1: dependencies: mdn-data: 2.0.28 source-map-js: 1.2.1 + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + css-tree@3.2.1: dependencies: mdn-data: 2.27.1 @@ -5174,8 +5740,6 @@ snapshots: css-what@6.2.2: {} - cssesc@3.0.0: {} - csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -5202,6 +5766,10 @@ snapshots: detect-libc@2.1.2: {} + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + devalue@5.8.1: {} devlop@1.1.0: @@ -5214,8 +5782,6 @@ snapshots: dependencies: path-type: 4.0.0 - direction@2.0.1: {} - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -5245,6 +5811,20 @@ snapshots: emoji-regex@8.0.0: {} + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -5254,6 +5834,10 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + + entities@8.0.0: {} + error-stack-parser-es@1.0.5: {} es-module-lexer@2.3.0: {} @@ -5375,17 +5959,22 @@ snapshots: expect-type@1.4.0: {} - expressive-code@0.44.0: - dependencies: - '@expressive-code/core': 0.44.0 - '@expressive-code/plugin-frames': 0.44.0 - '@expressive-code/plugin-shiki': 0.44.0 - '@expressive-code/plugin-text-markers': 0.44.0 + exsolve@1.1.0: {} extend@3.0.2: {} extendable-error@0.1.7: {} + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -5416,6 +6005,10 @@ snapshots: dependencies: format: 0.2.2 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 @@ -5458,6 +6051,10 @@ snapshots: get-caller-file@2.0.5: {} + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + get-tsconfig@5.0.0-beta.4: dependencies: resolve-pkg-maps: 1.0.0 @@ -5468,6 +6065,8 @@ snapshots: dependencies: is-glob: 4.0.3 + globals@15.15.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -5491,21 +6090,6 @@ snapshots: ufo: 1.6.4 uncrypto: 0.1.3 - hast-util-embedded@3.0.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-is-element: 3.0.0 - - hast-util-format@1.1.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-embedded: 3.0.0 - hast-util-minify-whitespace: 1.0.1 - hast-util-phrasing: 3.0.1 - hast-util-whitespace: 3.0.0 - html-whitespace-sensitive-tag-names: 3.0.1 - unist-util-visit-parents: 6.0.2 - hast-util-from-html@2.0.3: dependencies: '@types/hast': 3.0.4 @@ -5526,38 +6110,14 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 - hast-util-has-property@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - hast-util-is-body-ok-link@3.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-is-element@3.0.0: dependencies: '@types/hast': 3.0.4 - hast-util-minify-whitespace@1.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-embedded: 3.0.0 - hast-util-is-element: 3.0.0 - hast-util-whitespace: 3.0.0 - unist-util-is: 6.0.1 - hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 - hast-util-phrasing@3.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-embedded: 3.0.0 - hast-util-has-property: 3.0.0 - hast-util-is-body-ok-link: 3.0.1 - hast-util-is-element: 3.0.0 - hast-util-raw@9.1.0: dependencies: '@types/hast': 3.0.4 @@ -5574,24 +6134,6 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 - hast-util-select@6.0.4: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - bcp-47-match: 2.0.3 - comma-separated-tokens: 2.0.3 - css-selector-parser: 3.3.0 - devlop: 1.1.0 - direction: 2.0.1 - hast-util-has-property: 3.0.0 - hast-util-to-string: 3.0.1 - hast-util-whitespace: 3.0.0 - nth-check: 2.1.1 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - unist-util-visit: 5.1.0 - zwitch: 2.0.4 - hast-util-to-estree@3.1.3: dependencies: '@types/estree': 1.0.9 @@ -5657,10 +6199,6 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 - hast-util-to-string@3.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-to-text@4.0.2: dependencies: '@types/hast': 3.0.4 @@ -5684,15 +6222,20 @@ snapshots: html-void-elements@3.0.0: {} - html-whitespace-sensitive-tag-names@3.0.1: {} + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 http-cache-semantics@4.2.0: {} human-id@4.1.3: {} - i18next@26.3.4(typescript@6.0.3): - optionalDependencies: - typescript: 6.0.3 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 iconv-lite@0.7.2: dependencies: @@ -5749,6 +6292,8 @@ snapshots: isexe@2.0.0: {} + jiti@2.7.0: {} + js-yaml@3.15.0: dependencies: argparse: 1.0.10 @@ -5770,7 +6315,7 @@ snapshots: kleur@4.1.5: {} - klona@2.0.6: {} + kolorist@1.8.0: {} lightningcss-android-arm64@1.32.0: optional: true @@ -5821,6 +6366,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -5851,20 +6402,6 @@ snapshots: '@types/unist': 3.0.3 unist-util-visit: 5.1.0 - mdast-util-directive@3.1.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-visit-parents: 6.0.2 - transitivePeerDependencies: - - supports-color - mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -6041,6 +6578,8 @@ snapshots: mdn-data@2.0.28: {} + mdn-data@2.0.30: {} + mdn-data@2.27.1: {} merge2@1.4.1: {} @@ -6064,16 +6603,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-directive@4.0.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - parse-entities: 4.0.2 - micromark-extension-frontmatter@2.0.0: dependencies: fault: 2.0.1 @@ -6343,6 +6872,19 @@ snapshots: - bufferutil - utf-8-validate + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mlly@1.8.2: + dependencies: + acorn: 8.17.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + mri@1.2.0: {} mrmime@2.0.1: {} @@ -6355,6 +6897,37 @@ snapshots: neotraverse@0.6.18: {} + nimbus-docs@0.1.22(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0))(react@19.2.6)(shiki@4.3.1): + dependencies: + '@astrojs/markdown-satteri': 0.2.1 + '@astrojs/mdx': 6.0.1(@astrojs/markdown-satteri@0.2.1)(astro@7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0)) + '@astrojs/sitemap': 3.7.3 + '@clack/prompts': 0.9.1 + '@shikijs/transformers': 4.3.1 + '@vercel/detect-agent': 1.2.3 + astro: 7.0.6(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(jiti@2.7.0)(rollup@4.60.4)(tsx@4.22.2)(yaml@2.9.0) + clsx: 2.1.1 + github-slugger: 2.0.0 + mri: 1.2.0 + remark-lint-emphasis-marker: 4.0.1 + remark-lint-fenced-code-flag: 4.2.0 + remark-lint-heading-increment: 4.0.1 + remark-lint-no-duplicate-headings: 4.0.1 + remark-lint-no-heading-punctuation: 4.0.1 + remark-lint-no-literal-urls: 4.0.1 + remark-lint-no-multiple-toplevel-headings: 4.0.1 + remark-lint-unordered-list-marker-style: 4.0.1 + satteri: 0.6.3 + shiki: 4.3.1 + tailwind-merge: 3.6.0 + unified: 11.0.5 + vfile: 6.0.3 + yaml: 2.9.0 + optionalDependencies: + react: 19.2.6 + transitivePeerDependencies: + - supports-color + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -6383,6 +6956,10 @@ snapshots: ohash@2.0.11: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + oniguruma-parser@0.12.2: {} oniguruma-to-es@4.3.6: @@ -6459,6 +7036,15 @@ snapshots: unist-util-visit-children: 3.0.0 vfile: 6.0.3 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -6475,6 +7061,8 @@ snapshots: pathe@2.0.3: {} + pend@1.2.0: {} + piccolore@0.1.3: {} picocolors@1.1.1: {} @@ -6487,15 +7075,17 @@ snapshots: pkg-pr-new@0.0.75: {} - postcss-nested@6.2.0(postcss@8.5.16): + pkg-types@1.3.1: dependencies: - postcss: 8.5.16 - postcss-selector-parser: 6.1.4 + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 - postcss-selector-parser@6.1.4: + pkg-types@2.3.1: dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 + confbox: 0.2.4 + exsolve: 1.1.0 + pathe: 2.0.3 postcss@8.5.16: dependencies: @@ -6515,10 +7105,17 @@ snapshots: property-information@7.2.0: {} + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + quansync@0.2.11: {} queue-microtask@1.2.3: {} + quotation@2.0.3: {} + radix3@1.1.2: {} react@19.2.6: {} @@ -6573,21 +7170,6 @@ snapshots: dependencies: regex-utilities: 2.3.0 - rehype-expressive-code@0.44.0: - dependencies: - expressive-code: 0.44.0 - - rehype-format@5.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-format: 1.1.0 - - rehype-parse@9.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-from-html: 2.0.3 - unified: 11.0.5 - rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -6608,22 +7190,6 @@ snapshots: hast-util-to-html: 9.0.5 unified: 11.0.5 - rehype@13.0.2: - dependencies: - '@types/hast': 3.0.4 - rehype-parse: 9.0.1 - rehype-stringify: 10.0.1 - unified: 11.0.5 - - remark-directive@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-directive: 3.1.0 - micromark-extension-directive: 4.0.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - remark-frontmatter@5.0.0: dependencies: '@types/mdast': 4.0.4 @@ -6644,6 +7210,85 @@ snapshots: transitivePeerDependencies: - supports-color + remark-lint-emphasis-marker@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + unified-lint-rule: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit-parents: 6.0.2 + vfile-message: 4.0.3 + + remark-lint-fenced-code-flag@4.2.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-phrasing: 4.1.0 + quotation: 2.0.3 + unified-lint-rule: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit-parents: 6.0.2 + + remark-lint-heading-increment@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-mdx: 3.0.0 + unified-lint-rule: 3.0.1 + unist-util-visit-parents: 6.0.2 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + remark-lint-no-duplicate-headings@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-mdx: 3.0.0 + mdast-util-to-string: 4.0.0 + unified-lint-rule: 3.0.1 + unist-util-visit-parents: 6.0.2 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + remark-lint-no-heading-punctuation@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-mdx: 3.0.0 + mdast-util-to-string: 4.0.0 + unified-lint-rule: 3.0.1 + unist-util-visit-parents: 6.0.2 + transitivePeerDependencies: + - supports-color + + remark-lint-no-literal-urls@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-string: 4.0.0 + micromark-util-character: 2.1.1 + unified-lint-rule: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit-parents: 6.0.2 + + remark-lint-no-multiple-toplevel-headings@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-mdx: 3.0.0 + unified-lint-rule: 3.0.1 + unist-util-visit-parents: 6.0.2 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + remark-lint-unordered-list-marker-style@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-phrasing: 4.1.0 + unified-lint-rule: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit-parents: 6.0.2 + vfile-message: 4.0.3 + remark-mdx@3.1.1: dependencies: mdast-util-mdx: 3.0.0 @@ -6781,6 +7426,19 @@ snapshots: safer-buffer@2.1.2: {} + satteri@0.6.3: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + optionalDependencies: + '@bruits/satteri-darwin-arm64': 0.6.3 + '@bruits/satteri-darwin-x64': 0.6.3 + '@bruits/satteri-linux-x64-gnu': 0.6.3 + '@bruits/satteri-wasm32-wasi': 0.6.3 + '@bruits/satteri-win32-x64-msvc': 0.6.3 + satteri@0.9.4: dependencies: '@types/estree-jsx': 1.0.5 @@ -6948,6 +7606,16 @@ snapshots: supports-color@10.2.2: {} + svgo@3.3.3: + dependencies: + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + svgo@4.0.1: dependencies: commander: 11.1.0 @@ -6958,6 +7626,20 @@ snapshots: picocolors: 1.1.1 sax: 1.6.0 + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.2: {} + + tapable@2.3.3: {} + + tar@7.5.19: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + term-size@2.2.1: {} tiny-inflate@1.0.3: {} @@ -7018,6 +7700,13 @@ snapshots: dependencies: pathe: 2.0.3 + unified-lint-rule@3.0.1: + dependencies: + '@types/unist': 3.0.3 + trough: 2.2.0 + unified: 11.0.5 + vfile: 6.0.3 + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -7093,10 +7782,6 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.4 - url-extras@0.1.0: {} - - util-deprecate@1.0.2: {} - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -7112,7 +7797,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0): + vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -7123,17 +7808,18 @@ snapshots: '@types/node': 26.1.0 esbuild: 0.28.0 fsevents: 2.3.3 + jiti: 2.7.0 tsx: 4.22.2 yaml: 2.9.0 - vitefu@1.1.3(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)): optionalDependencies: - vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0) - vitest@4.1.9(@types/node@26.1.0)(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0)): + vitest@4.1.9(@types/node@26.1.0)(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0)) + '@vitest/mocker': 4.1.9(vite@8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 @@ -7150,7 +7836,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.1.3(@types/node@26.1.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.1.0 @@ -7258,6 +7944,12 @@ snapshots: webidl-conversions@3.0.1: {} + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -7304,12 +7996,16 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrappy@1.0.2: {} + ws@8.18.0: {} xxhash-wasm@1.1.0: {} y18n@5.0.8: {} + yallist@5.0.0: {} + yaml-language-server@1.20.0: dependencies: '@vscode/l10n': 0.0.18 @@ -7342,6 +8038,11 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yocto-queue@1.2.2: {} youch-core@0.3.3: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7c106b8..00493df 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,3 +7,6 @@ allowBuilds: esbuild: true sharp: true workerd: true + +minimumReleaseAgeExclude: + - nimbus-docs@0.1.22 From 8120b2f354152b7577040f97f73542019e930439 Mon Sep 17 00:00:00 2001 From: Diogo Carneiro Date: Mon, 6 Jul 2026 11:06:49 +0100 Subject: [PATCH 2/4] docs: add Changesets workflow to contributor guidelines Add `pnpm changeset` command to scripts table and expand "Always" section with guidance on when to create Changesets entries vs empty changesets for non-release changes (docs-site-only, CI-only maintenance). --- .changeset/brave-nimbus-docs.md | 5 +++++ AGENTS.md | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/brave-nimbus-docs.md diff --git a/.changeset/brave-nimbus-docs.md b/.changeset/brave-nimbus-docs.md new file mode 100644 index 0000000..6b3632c --- /dev/null +++ b/.changeset/brave-nimbus-docs.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/polystella": patch +--- + +Document the requirement to add a Changesets entry for package-affecting changes and record the docs migration to Nimbus. diff --git a/AGENTS.md b/AGENTS.md index 47ebd37..d8f695b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,7 @@ section numbers. Inserting new sections never breaks links. | `pnpm test:watch` | Vitest in watch mode. | | `pnpm build` | Compile `src/` → `dist/` via `tsc -p tsconfig.build.json` (mirrored layout, `.js` + `.d.ts` + sourcemaps + declaration maps). Produces the standalone `polystella` CLI at `dist/cli.js` and library entries. | | `pnpm exec tsc --noEmit` | Typecheck against the root `tsconfig.json` (which includes tests). The build config (`tsconfig.build.json`) sets `noEmit: false` and narrows `include` to `src/**`. | +| `pnpm changeset` | Add a Changesets entry for package-affecting work. Use `pnpm changeset add --empty` only for changes that intentionally do not need a package release. | No lint step yet. @@ -85,6 +86,12 @@ to the explanatory section when adding code that touches one. ### Always - Run `pnpm test` before pushing. Tests must stay green. +- Add a Changesets entry for every change that affects the published + package, its documented behaviour, or release-facing contributor + guidance. Use `pnpm changeset` for release notes; use + `pnpm changeset add --empty` only when the change deliberately does + not require a package release (for example, docs-site-only or CI-only + maintenance). - Bump the package version in `package.json` only — `POLYSTELLA_VERSION` (in `src/version.ts`) reads it at module-load time via a JSON import attribute, so the constant flows automatically through to From 5b44e27848159ae353248b822b47b15ca635414c Mon Sep 17 00:00:00 2001 From: Diogo Carneiro Date: Mon, 6 Jul 2026 11:32:33 +0100 Subject: [PATCH 3/4] chore(docs): update references from Starlight to Nimbus docs site Replace remaining "Starlight" references with "Nimbus docs" in workflow job name, contributor guide, and README to reflect the documentation framework migration. --- .github/workflows/docs.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a14f3b4..c027d4c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -27,7 +27,7 @@ concurrency: jobs: build: - name: Build Starlight site + name: Build Nimbus docs site runs-on: ubuntu-latest timeout-minutes: 15 steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 583b476..f3b463e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ Required: | `pnpm exec tsc --noEmit` | Typecheck the package. | | `pnpm build` | Compile `src/` → `dist/` (library + CLI, JS + `.d.ts`). | | `pnpm build:llms` | Regenerate `llms-full.txt` from canonical agent docs. | -| `pnpm --filter polystella-docs dev` | Run the Starlight docs site locally. | +| `pnpm --filter polystella-docs dev` | Run the Nimbus docs site locally. | | `pnpm --filter polystella-docs build` | Build the docs site (includes auto-generated config reference). | | `pnpm --filter polystella-docs check` | Astro check over docs content. | diff --git a/README.md b/README.md index 7330f69..f2c5870 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ collection APIs. ## Documentation -Full documentation lives at the Starlight docs site (under `docs/` in this repo): +Full documentation lives at the Nimbus docs site (under `docs/` in this repo): - [Getting started](https://polystella-docs.pcx-team.workers.dev/getting-started/install/) — install, quick start, mental model - [Concepts](https://polystella-docs.pcx-team.workers.dev/concepts/how-it-works/) — pipeline, cache, overrides, runtime bridge From 8011fa251655e468e1be566dc157e681923f47e6 Mon Sep 17 00:00:00 2001 From: Diogo Carneiro Date: Mon, 6 Jul 2026 12:00:15 +0100 Subject: [PATCH 4/4] docs: mark AI-generated pages with aiGenerated frontmatter flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `aiGenerated: true` frontmatter field to all documentation pages drafted by AI agents. Update content schema to accept the boolean flag, add it to config reference generation script, and wire it into PageActions component to display "AI-generated · awaiting review" badge with sparkle icon when present. --- docs/scripts/generate-config-ref.ts | 1 + .../ui/page-actions/PageActions.astro | 21 +++++++++++++++++-- docs/src/content.config.ts | 1 + .../content/docs/adapters/custom-loader.md | 1 + docs/src/content/docs/adapters/markdown.md | 1 + docs/src/content/docs/adapters/mdx.md | 1 + docs/src/content/docs/adapters/toml.md | 1 + docs/src/content/docs/cli/audit-mdx.md | 1 + docs/src/content/docs/cli/check-ui.md | 1 + docs/src/content/docs/cli/index.md | 1 + docs/src/content/docs/cli/sync-ui.md | 1 + docs/src/content/docs/cli/translate-ui.md | 1 + docs/src/content/docs/cli/translate.md | 1 + docs/src/content/docs/concepts/ai-marker.md | 1 + docs/src/content/docs/concepts/glossaries.md | 1 + .../src/content/docs/concepts/how-it-works.md | 1 + .../content/docs/concepts/mode-boundary.md | 1 + docs/src/content/docs/concepts/overrides.md | 1 + docs/src/content/docs/concepts/r2-cache.md | 1 + .../content/docs/concepts/runtime-bridge.md | 1 + docs/src/content/docs/configuration/index.md | 1 + .../content/docs/configuration/reference.md | 1 + .../content/docs/cookbook/locale-picker.md | 1 + .../content/docs/getting-started/install.md | 1 + .../docs/getting-started/mental-model.md | 1 + .../docs/getting-started/quick-start.md | 1 + docs/src/content/docs/index.mdx | 1 + .../docs/operations/branch-dispatch.md | 1 + docs/src/content/docs/operations/ci.md | 1 + .../docs/operations/preview-isolation.md | 1 + docs/src/content/docs/providers/anthropic.md | 1 + docs/src/content/docs/providers/batching.md | 1 + .../content/docs/providers/model-selection.md | 1 + .../docs/providers/permanent-errors.md | 1 + docs/src/content/docs/providers/workers-ai.md | 1 + .../docs/reference/breaking-changes.md | 1 + docs/src/content/docs/reference/exports.md | 1 + docs/src/content/docs/roadmap.md | 1 + .../src/content/docs/routing/configuration.md | 1 + docs/src/content/docs/routing/shims.md | 1 + .../docs/runtime-api/explicit-imports.md | 1 + docs/src/content/docs/runtime-api/locals.md | 1 + .../content/docs/runtime-api/middleware.md | 1 + .../content/docs/runtime-api/react-hooks.md | 1 + .../troubleshooting/missing-translations.md | 1 + docs/src/layouts/DocsLayout.astro | 6 +++--- docs/src/pages/[...slug].astro | 1 + 47 files changed, 67 insertions(+), 5 deletions(-) diff --git a/docs/scripts/generate-config-ref.ts b/docs/scripts/generate-config-ref.ts index 0aac852..2f92a19 100644 --- a/docs/scripts/generate-config-ref.ts +++ b/docs/scripts/generate-config-ref.ts @@ -245,6 +245,7 @@ async function main(): Promise { "---", "title: Configuration reference", "description: Full polystella.config.mjs option reference, generated from the zod schema.", + "aiGenerated: true", "---", "", ":::note[Auto-generated]", diff --git a/docs/src/components/ui/page-actions/PageActions.astro b/docs/src/components/ui/page-actions/PageActions.astro index 5c9a2bf..09719d6 100644 --- a/docs/src/components/ui/page-actions/PageActions.astro +++ b/docs/src/components/ui/page-actions/PageActions.astro @@ -6,9 +6,11 @@ import type { HTMLAttributes } from "astro/types"; interface Props extends HTMLAttributes<"div"> { markdownUrl?: string; lastUpdated?: Date; + /** Page was drafted by an AI agent and not yet reviewed by a human. */ + aiGenerated?: boolean; } -const { markdownUrl, lastUpdated, class: className, ...attrs } = Astro.props; +const { markdownUrl, lastUpdated, aiGenerated, class: className, ...attrs } = Astro.props; const baseBtn = "inline-flex cursor-pointer items-center gap-1.5 rounded-md bg-transparent px-2 py-1 text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2"; @@ -18,13 +20,28 @@ const formattedDate = lastUpdated : null; --- -{(markdownUrl || lastUpdated) && ( +{(markdownUrl || lastUpdated || aiGenerated) && (
+ {aiGenerated && ( + <> + + + AI-generated · awaiting review + + {(lastUpdated || markdownUrl) && ( + + )} + + )} + {lastUpdated && ( diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts index 3b5e263..ec5e54a 100644 --- a/docs/src/content.config.ts +++ b/docs/src/content.config.ts @@ -7,6 +7,7 @@ export const collections = { docsCollection({ schemaFields: { audience: z.literal("human").optional(), + aiGenerated: z.boolean().optional(), }, }), ), diff --git a/docs/src/content/docs/adapters/custom-loader.md b/docs/src/content/docs/adapters/custom-loader.md index 5b36471..65b477e 100644 --- a/docs/src/content/docs/adapters/custom-loader.md +++ b/docs/src/content/docs/adapters/custom-loader.md @@ -1,6 +1,7 @@ --- title: Custom loader description: Wrapping a non-glob, non-file Astro loader to opt content into the translation pipeline. +aiGenerated: true --- Astro's content layer supports custom loaders — functions that diff --git a/docs/src/content/docs/adapters/markdown.md b/docs/src/content/docs/adapters/markdown.md index b213a5d..d3a3e7f 100644 --- a/docs/src/content/docs/adapters/markdown.md +++ b/docs/src/content/docs/adapters/markdown.md @@ -1,6 +1,7 @@ --- title: Markdown adapter description: "Translating .md files: frontmatter keys, body inline text, internal links." +aiGenerated: true --- The Markdown adapter handles `.md` files via Satteri by default. Use diff --git a/docs/src/content/docs/adapters/mdx.md b/docs/src/content/docs/adapters/mdx.md index a9c7e66..ff3d9f8 100644 --- a/docs/src/content/docs/adapters/mdx.md +++ b/docs/src/content/docs/adapters/mdx.md @@ -1,6 +1,7 @@ --- title: MDX adapter description: "Translating .mdx files — JSX, props, static data, and limits." +aiGenerated: true --- The MDX adapter reuses the Markdown adapter but parses `.mdx` with diff --git a/docs/src/content/docs/adapters/toml.md b/docs/src/content/docs/adapters/toml.md index dde75d5..cec15f6 100644 --- a/docs/src/content/docs/adapters/toml.md +++ b/docs/src/content/docs/adapters/toml.md @@ -1,6 +1,7 @@ --- title: TOML adapter description: "Translating structured TOML config: keys, urls, wildcards." +aiGenerated: true --- The TOML adapter translates structured data files like diff --git a/docs/src/content/docs/cli/audit-mdx.md b/docs/src/content/docs/cli/audit-mdx.md index f9a4e10..0808752 100644 --- a/docs/src/content/docs/cli/audit-mdx.md +++ b/docs/src/content/docs/cli/audit-mdx.md @@ -1,6 +1,7 @@ --- title: polystella audit-mdx description: "polystella audit-mdx — offline MDX extraction audit." +aiGenerated: true --- `audit-mdx` scans configured `.mdx` sources for likely missed diff --git a/docs/src/content/docs/cli/check-ui.md b/docs/src/content/docs/cli/check-ui.md index 5216244..ebd6388 100644 --- a/docs/src/content/docs/cli/check-ui.md +++ b/docs/src/content/docs/cli/check-ui.md @@ -1,6 +1,7 @@ --- title: polystella check-ui description: "polystella check-ui — offline drift detection over UI-string JSONs." +aiGenerated: true --- Pure drift detection over your project's UI-string JSON files. diff --git a/docs/src/content/docs/cli/index.md b/docs/src/content/docs/cli/index.md index 1ecdfef..06518f1 100644 --- a/docs/src/content/docs/cli/index.md +++ b/docs/src/content/docs/cli/index.md @@ -1,6 +1,7 @@ --- title: CLI overview description: The polystella binary and its verb-style subcommands. +aiGenerated: true sidebar: label: Overview --- diff --git a/docs/src/content/docs/cli/sync-ui.md b/docs/src/content/docs/cli/sync-ui.md index 204271c..fe6081f 100644 --- a/docs/src/content/docs/cli/sync-ui.md +++ b/docs/src/content/docs/cli/sync-ui.md @@ -1,6 +1,7 @@ --- title: polystella sync-ui description: "polystella sync-ui — mechanical UI-string key reconciliation." +aiGenerated: true --- Mechanical (no AI) key reconciliation for UI-string JSONs. Adds diff --git a/docs/src/content/docs/cli/translate-ui.md b/docs/src/content/docs/cli/translate-ui.md index f02dd97..c5175d0 100644 --- a/docs/src/content/docs/cli/translate-ui.md +++ b/docs/src/content/docs/cli/translate-ui.md @@ -1,6 +1,7 @@ --- title: polystella translate-ui description: "polystella translate-ui — sync followed by AI-fill of empty placeholders." +aiGenerated: true --- Sync (key add/remove) followed by AI-fill of empty values. Queued diff --git a/docs/src/content/docs/cli/translate.md b/docs/src/content/docs/cli/translate.md index 6e13100..8bdeaf3 100644 --- a/docs/src/content/docs/cli/translate.md +++ b/docs/src/content/docs/cli/translate.md @@ -1,6 +1,7 @@ --- title: polystella translate description: "polystella translate — markdown pipeline outside astro build." +aiGenerated: true --- Runs the same translation pipeline as `astro build` without booting diff --git a/docs/src/content/docs/concepts/ai-marker.md b/docs/src/content/docs/concepts/ai-marker.md index 3944a7d..a98c469 100644 --- a/docs/src/content/docs/concepts/ai-marker.md +++ b/docs/src/content/docs/concepts/ai-marker.md @@ -1,6 +1,7 @@ --- title: AI marker description: The aiTranslated frontmatter fields baked into every translated file. +aiGenerated: true --- Every AI-translated file gets three frontmatter fields added during diff --git a/docs/src/content/docs/concepts/glossaries.md b/docs/src/content/docs/concepts/glossaries.md index 79f63f6..7eec70e 100644 --- a/docs/src/content/docs/concepts/glossaries.md +++ b/docs/src/content/docs/concepts/glossaries.md @@ -1,6 +1,7 @@ --- title: Glossaries description: Per-locale YAML files that pin do-not-translate terms, preferred translations, and style rules. +aiGenerated: true --- A glossary tells the translator how to handle terminology that diff --git a/docs/src/content/docs/concepts/how-it-works.md b/docs/src/content/docs/concepts/how-it-works.md index b8e5bc2..d1472d7 100644 --- a/docs/src/content/docs/concepts/how-it-works.md +++ b/docs/src/content/docs/concepts/how-it-works.md @@ -1,6 +1,7 @@ --- title: How it works description: End-to-end overview of the PolyStella build pipeline. +aiGenerated: true --- PolyStella is an Astro integration that translates content into diff --git a/docs/src/content/docs/concepts/mode-boundary.md b/docs/src/content/docs/concepts/mode-boundary.md index 16f75ab..e22acdc 100644 --- a/docs/src/content/docs/concepts/mode-boundary.md +++ b/docs/src/content/docs/concepts/mode-boundary.md @@ -1,6 +1,7 @@ --- title: Mode boundary description: Standalone vs Starlight modes — what differs between them. +aiGenerated: true --- PolyStella ships **standalone mode** today. **Starlight mode** is diff --git a/docs/src/content/docs/concepts/overrides.md b/docs/src/content/docs/concepts/overrides.md index 5e1ee6d..4c3cfd6 100644 --- a/docs/src/content/docs/concepts/overrides.md +++ b/docs/src/content/docs/concepts/overrides.md @@ -1,6 +1,7 @@ --- title: Overrides description: Hand-translated files that win over AI output verbatim. +aiGenerated: true --- Sometimes the AI gets something wrong. Or a page is too important diff --git a/docs/src/content/docs/concepts/r2-cache.md b/docs/src/content/docs/concepts/r2-cache.md index d58f500..cbd2a67 100644 --- a/docs/src/content/docs/concepts/r2-cache.md +++ b/docs/src/content/docs/concepts/r2-cache.md @@ -1,6 +1,7 @@ --- title: R2 cache description: How translations are content-addressed and cached in Cloudflare R2. +aiGenerated: true --- PolyStella's R2 cache is the load-bearing piece of the whole diff --git a/docs/src/content/docs/concepts/runtime-bridge.md b/docs/src/content/docs/concepts/runtime-bridge.md index 323c6ff..bd2337a 100644 --- a/docs/src/content/docs/concepts/runtime-bridge.md +++ b/docs/src/content/docs/concepts/runtime-bridge.md @@ -1,6 +1,7 @@ --- title: Runtime bridge description: The seam between config-time and content-sync-time for custom-loader translation. +aiGenerated: true --- Most of PolyStella runs at `astro:config:setup`. File-based content diff --git a/docs/src/content/docs/configuration/index.md b/docs/src/content/docs/configuration/index.md index 585019d..d6a773f 100644 --- a/docs/src/content/docs/configuration/index.md +++ b/docs/src/content/docs/configuration/index.md @@ -1,6 +1,7 @@ --- title: Configuration overview description: Where polystella.config.mjs sits and how it relates to astro.config.mjs. +aiGenerated: true sidebar: label: Overview --- diff --git a/docs/src/content/docs/configuration/reference.md b/docs/src/content/docs/configuration/reference.md index 397a55e..794d277 100644 --- a/docs/src/content/docs/configuration/reference.md +++ b/docs/src/content/docs/configuration/reference.md @@ -1,6 +1,7 @@ --- title: Configuration reference description: Full polystella.config.mjs option reference, generated from the zod schema. +aiGenerated: true --- :::note[Auto-generated] diff --git a/docs/src/content/docs/cookbook/locale-picker.md b/docs/src/content/docs/cookbook/locale-picker.md index cd37c38..2c38d13 100644 --- a/docs/src/content/docs/cookbook/locale-picker.md +++ b/docs/src/content/docs/cookbook/locale-picker.md @@ -1,6 +1,7 @@ --- title: Locale picker description: A copy-paste locale-switcher component for PolyStella sites. +aiGenerated: true --- A minimal, unstyled locale-switcher component. Renders a list of diff --git a/docs/src/content/docs/getting-started/install.md b/docs/src/content/docs/getting-started/install.md index 9118b94..b0c4cc5 100644 --- a/docs/src/content/docs/getting-started/install.md +++ b/docs/src/content/docs/getting-started/install.md @@ -1,6 +1,7 @@ --- title: Install description: How to install PolyStella in your Astro project. +aiGenerated: true --- PolyStella is an Astro integration. It runs at build time, talks to diff --git a/docs/src/content/docs/getting-started/mental-model.md b/docs/src/content/docs/getting-started/mental-model.md index f12888f..dbe4258 100644 --- a/docs/src/content/docs/getting-started/mental-model.md +++ b/docs/src/content/docs/getting-started/mental-model.md @@ -1,6 +1,7 @@ --- title: Mental model description: How PolyStella thinks about content, locales, and the build pipeline. +aiGenerated: true --- PolyStella has three central ideas. Internalise these and the rest diff --git a/docs/src/content/docs/getting-started/quick-start.md b/docs/src/content/docs/getting-started/quick-start.md index 679694b..df892a3 100644 --- a/docs/src/content/docs/getting-started/quick-start.md +++ b/docs/src/content/docs/getting-started/quick-start.md @@ -1,6 +1,7 @@ --- title: Quick start description: "Five-minute setup — the four files that participate in a typical PolyStella project." +aiGenerated: true --- Four files participate in a typical PolyStella setup. None of them diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 9e9163d..eb3081f 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -1,6 +1,7 @@ --- title: PolyStella description: AI-driven content localization for Astro — build-time markdown translation, R2-cached. +aiGenerated: true mode: custom --- diff --git a/docs/src/content/docs/operations/branch-dispatch.md b/docs/src/content/docs/operations/branch-dispatch.md index 36f7bd9..5f58c25 100644 --- a/docs/src/content/docs/operations/branch-dispatch.md +++ b/docs/src/content/docs/operations/branch-dispatch.md @@ -1,6 +1,7 @@ --- title: Branch dispatch description: How polystella.config.mjs reads WORKERS_CI_BRANCH to switch prefixes. +aiGenerated: true --- Branch dispatch is a config-side pattern, not built into the diff --git a/docs/src/content/docs/operations/ci.md b/docs/src/content/docs/operations/ci.md index 0eaf952..a6d1446 100644 --- a/docs/src/content/docs/operations/ci.md +++ b/docs/src/content/docs/operations/ci.md @@ -1,6 +1,7 @@ --- title: CI / Workers Builds description: Wiring up PolyStella in CI so the right R2 prefix is targeted per branch. +aiGenerated: true --- PolyStella is designed to run inside Cloudflare Workers Builds (or diff --git a/docs/src/content/docs/operations/preview-isolation.md b/docs/src/content/docs/operations/preview-isolation.md index 9433e7b..7dddcf4 100644 --- a/docs/src/content/docs/operations/preview-isolation.md +++ b/docs/src/content/docs/operations/preview-isolation.md @@ -1,6 +1,7 @@ --- title: Preview isolation description: Using readFallbackPrefixes to read main's cache without writing back. +aiGenerated: true --- The combination of `r2.readFallbackPrefixes` and `r2.readOnly` diff --git a/docs/src/content/docs/providers/anthropic.md b/docs/src/content/docs/providers/anthropic.md index cde4826..dbb6d8a 100644 --- a/docs/src/content/docs/providers/anthropic.md +++ b/docs/src/content/docs/providers/anthropic.md @@ -1,6 +1,7 @@ --- title: Anthropic provider description: Anthropic Claude as the translation provider. +aiGenerated: true --- The Anthropic provider routes translation through Anthropic's diff --git a/docs/src/content/docs/providers/batching.md b/docs/src/content/docs/providers/batching.md index e7f19d8..2b2ada7 100644 --- a/docs/src/content/docs/providers/batching.md +++ b/docs/src/content/docs/providers/batching.md @@ -1,6 +1,7 @@ --- title: Batching description: "How translateBatch groups segments into LLM calls; batchInputTokenBudget." +aiGenerated: true --- For small files the translator processes the entire file in a diff --git a/docs/src/content/docs/providers/model-selection.md b/docs/src/content/docs/providers/model-selection.md index 23d996c..931e9c6 100644 --- a/docs/src/content/docs/providers/model-selection.md +++ b/docs/src/content/docs/providers/model-selection.md @@ -1,6 +1,7 @@ --- title: Model selection description: Single model id vs per-locale map; how model choice affects the cache. +aiGenerated: true --- The `provider.model` option is either a single model id or a per- diff --git a/docs/src/content/docs/providers/permanent-errors.md b/docs/src/content/docs/providers/permanent-errors.md index 1ee31d8..f3e077e 100644 --- a/docs/src/content/docs/providers/permanent-errors.md +++ b/docs/src/content/docs/providers/permanent-errors.md @@ -1,6 +1,7 @@ --- title: Permanent errors description: PermanentProviderError vs retriable failures — how the retry loop decides. +aiGenerated: true --- When a provider call fails, PolyStella has to decide: retry, or diff --git a/docs/src/content/docs/providers/workers-ai.md b/docs/src/content/docs/providers/workers-ai.md index c0aef5a..ea8e54f 100644 --- a/docs/src/content/docs/providers/workers-ai.md +++ b/docs/src/content/docs/providers/workers-ai.md @@ -1,6 +1,7 @@ --- title: Workers AI provider description: Cloudflare Workers AI as the translation provider. +aiGenerated: true --- The Workers AI provider routes translation through Cloudflare's diff --git a/docs/src/content/docs/reference/breaking-changes.md b/docs/src/content/docs/reference/breaking-changes.md index b0c3810..1cb74f6 100644 --- a/docs/src/content/docs/reference/breaking-changes.md +++ b/docs/src/content/docs/reference/breaking-changes.md @@ -1,6 +1,7 @@ --- title: Breaking changes description: Pre-1.0 breaking-change log. +aiGenerated: true --- PolyStella is in pre-1.0 development. Breaking changes happen. diff --git a/docs/src/content/docs/reference/exports.md b/docs/src/content/docs/reference/exports.md index f735332..aa095f2 100644 --- a/docs/src/content/docs/reference/exports.md +++ b/docs/src/content/docs/reference/exports.md @@ -1,6 +1,7 @@ --- title: Public exports description: "Every export path in package.json — what it provides and when to import from it." +aiGenerated: true --- PolyStella ships twelve public import paths. Each has a narrow, diff --git a/docs/src/content/docs/roadmap.md b/docs/src/content/docs/roadmap.md index 99c7a8f..cf002f3 100644 --- a/docs/src/content/docs/roadmap.md +++ b/docs/src/content/docs/roadmap.md @@ -1,6 +1,7 @@ --- title: Roadmap description: Status of shipped + planned features. +aiGenerated: true --- PolyStella is in active pre-1.0 development. The table below diff --git a/docs/src/content/docs/routing/configuration.md b/docs/src/content/docs/routing/configuration.md index 860c056..1b4fa3c 100644 --- a/docs/src/content/docs/routing/configuration.md +++ b/docs/src/content/docs/routing/configuration.md @@ -1,6 +1,7 @@ --- title: Route configuration description: "The routes and routesImports options." +aiGenerated: true --- Two `polystella.config.mjs` options control which source pages get diff --git a/docs/src/content/docs/routing/shims.md b/docs/src/content/docs/routing/shims.md index 9c442c5..fd9906a 100644 --- a/docs/src/content/docs/routing/shims.md +++ b/docs/src/content/docs/routing/shims.md @@ -1,6 +1,7 @@ --- title: Standalone shims description: How PolyStella generates locale-prefixed route shims that re-render source pages. +aiGenerated: true --- In standalone mode, PolyStella owns the routing layer for locale- diff --git a/docs/src/content/docs/runtime-api/explicit-imports.md b/docs/src/content/docs/runtime-api/explicit-imports.md index c7d0fff..7f9b3ed 100644 --- a/docs/src/content/docs/runtime-api/explicit-imports.md +++ b/docs/src/content/docs/runtime-api/explicit-imports.md @@ -1,6 +1,7 @@ --- title: Explicit imports description: Calling @cloudflare/polystella/runtime functions directly outside .astro templates. +aiGenerated: true --- `Astro.locals` only exists in request-scoped contexts (`.astro` diff --git a/docs/src/content/docs/runtime-api/locals.md b/docs/src/content/docs/runtime-api/locals.md index b824731..0ecf65b 100644 --- a/docs/src/content/docs/runtime-api/locals.md +++ b/docs/src/content/docs/runtime-api/locals.md @@ -1,6 +1,7 @@ --- title: Astro.locals description: "t, lhref, getLocalizedEntry, getLocalizedCollection." +aiGenerated: true --- PolyStella's middleware populates four properties on `Astro.locals` diff --git a/docs/src/content/docs/runtime-api/middleware.md b/docs/src/content/docs/runtime-api/middleware.md index f01b74e..1271ce0 100644 --- a/docs/src/content/docs/runtime-api/middleware.md +++ b/docs/src/content/docs/runtime-api/middleware.md @@ -1,6 +1,7 @@ --- title: Middleware description: How polystella's request middleware is auto-registered; opting out. +aiGenerated: true --- PolyStella registers a request middleware via Astro's diff --git a/docs/src/content/docs/runtime-api/react-hooks.md b/docs/src/content/docs/runtime-api/react-hooks.md index 76a5504..df7491a 100644 --- a/docs/src/content/docs/runtime-api/react-hooks.md +++ b/docs/src/content/docs/runtime-api/react-hooks.md @@ -1,6 +1,7 @@ --- title: React hooks description: useTranslations and useLocalizedHref for React islands. +aiGenerated: true --- For client-rendered React islands inside an Astro project, diff --git a/docs/src/content/docs/troubleshooting/missing-translations.md b/docs/src/content/docs/troubleshooting/missing-translations.md index a4da2f5..4d4ae9a 100644 --- a/docs/src/content/docs/troubleshooting/missing-translations.md +++ b/docs/src/content/docs/troubleshooting/missing-translations.md @@ -1,6 +1,7 @@ --- title: Missing translations description: Diagnosing when a translated page doesn't render. +aiGenerated: true --- A page that should be translated isn't showing up under diff --git a/docs/src/layouts/DocsLayout.astro b/docs/src/layouts/DocsLayout.astro index 04c8347..cbaca98 100644 --- a/docs/src/layouts/DocsLayout.astro +++ b/docs/src/layouts/DocsLayout.astro @@ -18,9 +18,9 @@ import { Badge } from "@/components/ui/badge"; import type { DocsPageProps } from "nimbus-docs/types"; import { getVersionStatus, getVersionAlternates } from "nimbus-docs"; -type Props = DocsPageProps & { audience?: "human" }; +type Props = DocsPageProps & { audience?: "human"; aiGenerated?: boolean }; -const { title, description, sidebar, headings, breadcrumbs, prevNext, mode = "doc", banner, head = [], searchable, noindex, markdownUrl, socialImage, lastUpdated, editUrl, draft, audience, collection, entryId } = Astro.props; +const { title, description, sidebar, headings, breadcrumbs, prevNext, mode = "doc", banner, head = [], searchable, noindex, markdownUrl, socialImage, lastUpdated, editUrl, draft, audience, aiGenerated, collection, entryId } = Astro.props; // `searchable` derives from `noindex` when omitted: a non-crawlable page is // by default not in the site search either. Explicit `searchable: true` @@ -188,7 +188,7 @@ const hasPagination = Astro.slots.has("pagination"); {description && (

{description}

)} - + {(markdownUrl || audience === "human") && ( audience === "human" ? (
diff --git a/docs/src/pages/[...slug].astro b/docs/src/pages/[...slug].astro index 88f70c0..d609143 100644 --- a/docs/src/pages/[...slug].astro +++ b/docs/src/pages/[...slug].astro @@ -80,6 +80,7 @@ const socialImage = entry.data.socialImage ?? `/og/${entry.id}.png`; editUrl={editUrl} draft={entry.data.draft} audience={entry.data.audience} + aiGenerated={entry.data.aiGenerated} collection={entry.collection} entryId={entry.id} >