# zudo-doc > Documentation base framework built with Astro and MDX. --- # Getting Started > Source: /pj/zudo-doc/docs/getting-started Welcome to the Getting Started section. Choose a topic below to begin. --- # 0.1.0 > Source: /pj/zudo-doc/docs/changelog/0.1.0 Initial release of zudo-doc. ### Features - Astro 6 with MDX content collections - Tailwind CSS v4 design token system with 16-color palette - Sidebar navigation with auto-generated tree from file structure - i18n support (English and Japanese) - Admonition components (Note, Tip, Info, Warning, Danger) - Code highlighting with Shiki - Table of contents with scroll spy - Color scheme switching with multiple built-in themes - Search functionality --- # Basic Components > Source: /pj/zudo-doc/docs/components/basic-components These are the standard Markdown and MDX elements styled through zudo-doc's design token system. No imports are needed. ## Headings Headings from `h2` to `h4` appear in the table of contents sidebar. ### Heading 3 #### Heading 4 ```mdx ## Heading 2 ### Heading 3 #### Heading 4 ``` ## Text Formatting **Bold text**, *italic text*, and ~~strikethrough~~. Combine them: ***bold and italic***. ```mdx **Bold text**, *italic text*, and ~~strikethrough~~. Combine them: ***bold and italic***. ``` ## Inline Code Use backticks to mark `inline code` within a sentence. ```mdx Use backticks to mark `inline code` within a sentence. ``` ## Code Blocks Fenced code blocks support syntax highlighting via Shiki. Specify the language after the opening backticks. ```ts function greet(name: string): string { return `Hello, ${name}!`; } ``` ````mdx ```ts function greet(name: string): string { return `Hello, ${name}!`; } ``` ```` ### Supported Languages Shiki supports a wide range of languages. Common ones include: | Language | Identifier | | -------- | ---------- | | TypeScript | `ts`, `typescript` | | JavaScript | `js`, `javascript` | | HTML | `html` | | CSS | `css` | | JSON | `json` | | Bash / Shell | `bash`, `sh`, `shell`, `zsh` | | Markdown | `md`, `markdown` | | MDX | `mdx` | | YAML | `yaml`, `yml` | | Python | `python`, `py` | | Rust | `rust`, `rs` | | Go | `go` | | SQL | `sql` | | GraphQL | `graphql` | | Diff | `diff` | | TOML | `toml` | | Astro | `astro` | | JSX / TSX | `jsx`, `tsx` | See the [full list of supported languages](https://shiki.style/languages) for all available identifiers. ## Unordered Lists - First item - Second item - Nested item - Another nested item - Deeply nested - Third item ```mdx - First item - Second item - Nested item - Another nested item - Deeply nested - Third item ``` ## Ordered Lists 1. First step 2. Second step 1. Sub-step one 2. Sub-step two 3. Third step ```mdx 1. First step 2. Second step 1. Sub-step one 2. Sub-step two 3. Third step ``` ## Blockquotes > This is a blockquote. It can span multiple lines > and supports **formatting** within it. ```mdx > This is a blockquote. It can span multiple lines > and supports **formatting** within it. ``` ## Tables | Feature | Status | Notes | | ---------- | ----------- | ------------------ | | MDX | Supported | Via `@astrojs/mdx` | | Shiki | Built-in | Code highlighting | | Tailwind | v4 | Token-based design | ```mdx | Feature | Status | Notes | | ---------- | ----------- | ------------------ | | MDX | Supported | Via `@astrojs/mdx` | | Shiki | Built-in | Code highlighting | | Tailwind | v4 | Token-based design | ``` ## Links Internal link: [Getting Started](/docs/getting-started) External link: [Astro documentation](https://docs.astro.build) ```mdx Internal link: [Getting Started](/docs/getting-started) External link: [Astro documentation](https://docs.astro.build) ``` ## Horizontal Rules Use three dashes to create a horizontal rule: --- ```mdx --- ``` --- # Develop > Source: /pj/zudo-doc/docs/develop Development notes for zudo-doc itself. --- # Introduction > Source: /pj/zudo-doc/docs/getting-started/introduction Welcome to **zudo-doc** — a documentation base framework built with Astro 6 and MDX. zudo-doc is designed to be simple and extensible. Start with the basics and add features as you need them. ## Why zudo-doc? - **Fast dev server** — Vite-powered, pages compile on demand with instant HMR - **MDX support** — write documentation with JSX components embedded in Markdown - **Tailwind CSS v4** — utility-first styling with a tight design token system - **Color scheme support** — themeable 16-color palette with light and dark defaults - **Static export** — generates pure HTML, zero JS by default (React islands only where needed) - **i18n ready** — English and Japanese routing built in - **llms.txt** — auto-generated AI-consumable documentation files - **Versioning** — maintain multiple documentation versions with version-prefixed URLs - **Minimal base** — extend with your own components and layouts If you're coming from Docusaurus, you'll find the MDX workflow familiar but with a lighter footprint and full control over the design system. ## How It Works zudo-doc uses [Astro's Content Collections](https://docs.astro.build/en/guides/content-collections/) to manage documentation pages. Each `.mdx` file in `src/content/docs/` becomes a page with automatic sidebar navigation, table of contents, and prev/next links. The color scheme system uses a 16-color palette. By swapping a single color scheme, the entire site's appearance changes. ## Quick Start ```bash pnpm create zudo-doc my-docs cd my-docs pnpm install pnpm dev ``` Then open [http://localhost:4321](http://localhost:4321) to see your docs. See the [Installation](/docs/getting-started/installation/) guide for all CLI options and setup details. If you want to explore the full project including these showcase docs, clone the repository directly instead: ```bash git clone https://github.com/zudolab/zudo-doc.git my-docs cd my-docs pnpm install pnpm dev ``` --- # Configuration > Source: /pj/zudo-doc/docs/guides/configuration zudo-doc is configured through a small set of files in the `src/config/` directory and the root `astro.config.ts`. ## Site Settings The main settings file is `src/config/settings.ts`: ```ts colorScheme: "Default Dark", colorMode: { defaultMode: "light", lightScheme: "Default Light", darkScheme: "Default Dark", respectPrefersColorScheme: true, }, siteName: "zudo-doc", siteDescription: "Documentation base framework...", base: "/", trailingSlash: false, docsDir: "src/content/docs", locales: { ja: { label: "JA", dir: "src/content/docs-ja" }, }, mermaid: true, noindex: false, editUrl: false, siteUrl: "", sitemap: true, colorTweakPanel: true, docMetainfo: true, docTags: true, math: true, docHistory: true, claudeResources: { claudeDir: ".claude", }, headerNav: [ { label: "Getting Started", labelKey: "nav.gettingStarted", path: "/docs/getting-started", categoryMatch: "getting-started" }, { label: "Guides", labelKey: "nav.guides", path: "/docs/guides", categoryMatch: "guides" }, { label: "Reference", labelKey: "nav.reference", path: "/docs/reference", categoryMatch: "reference" }, { label: "Claude", labelKey: "nav.claude", path: "/docs/claude", categoryMatch: "claude" }, ], }; ``` ### colorScheme The active color scheme name. Must match a key in `src/config/color-schemes.ts`. Available built-in schemes: **Default Light** and **Default Dark**. You can add custom schemes in `src/config/color-schemes.ts`. See the [Color guide](/docs/reference/color) for a full list of available schemes, previews, and instructions on adding custom schemes. ### colorMode Configures light/dark mode behavior. Set to `false` to disable color mode switching entirely and use only the scheme specified in `colorScheme`. When enabled, accepts a `ColorModeConfig` object: | Property | Type | Description | |----------|------|-------------| | `defaultMode` | `"light"` \| `"dark"` | The initial color mode before any user preference is applied | | `lightScheme` | string | Color scheme name to use in light mode | | `darkScheme` | string | Color scheme name to use in dark mode | | `respectPrefersColorScheme` | boolean | When `true`, automatically matches the user's OS-level light/dark preference | ```ts colorMode: { defaultMode: "light", lightScheme: "Default Light", darkScheme: "Default Dark", respectPrefersColorScheme: true, }, // or disable color mode switching: colorMode: false, ``` ### base The base path for deploying to a subdirectory. Set this when your site is served from a subpath rather than the root domain. Default: `"/"` For example, to serve the site at `https://example.com/my-docs/`: ```ts base: "/my-docs", ``` All internal links (sidebar, navigation, prev/next, search) are automatically prefixed with the base path. This maps directly to [Astro's `base` config option](https://docs.astro.build/en/reference/configuration-reference/#base). Inline markdown links within MDX content (e.g., `[link text](/docs/some-page)`) are **not** automatically rewritten. When using a non-root base, use relative links in content files instead. ### trailingSlash Controls whether URLs end with a trailing slash. When `true`, all internal links are generated with a trailing slash (e.g., `/docs/guides/` instead of `/docs/guides`). This also sets Astro's [`trailingSlash`](https://docs.astro.build/en/reference/configuration-reference/#trailingslash) to `"always"` or `"never"` accordingly. Default: `false` ```ts trailingSlash: true, ``` Some hosting platforms (e.g., AWS Amplify, Cloudflare Pages) work better with trailing slashes enabled. If you experience 404 errors on page navigation, try setting this to `true`. ### docsDir The directory path (relative to the project root) where English documentation MDX files are stored. This uses Astro's `glob()` loader, so it can point to any directory. Default: `"src/content/docs"` ```ts docsDir: "docs", // content in ./docs/ at project root ``` This is similar to Docusaurus' `docs.path` option — useful when using zudo-doc as a documentation tool for a larger project where docs live at the project root level. ### locales A map of additional locale configurations. Each key is a locale code, and the value is an object with `label` (display name for the language switcher) and `dir` (content directory path). Default: `{}` ```ts locales: { ja: { label: "JA", dir: "src/content/docs-ja" }, ko: { label: "KO", dir: "src/content/docs-ko" }, }, ``` For each locale entry, zudo-doc automatically: - Creates an Astro content collection (`docs-{code}`) - Registers the locale in Astro's i18n routing - Adds it to the language switcher ### siteName The site name displayed in the header and used in page titles. Page titles are formatted as `{page title} | {siteName}`. ### siteDescription A short description of the site. Used in meta tags for SEO and social sharing. Default: `""` ### siteUrl The full base URL of your site (e.g., `"https://example.com"`). Required for generating absolute URLs in the sitemap. Default: `""` ### noindex When `true`, adds `noindex` and `nofollow` meta tags to all pages. Useful for internal documentation sites that should not be indexed by search engines. Default: `false` ### mermaid Enables Mermaid diagram support. When `true`, fenced code blocks with the `mermaid` language identifier are rendered as diagrams. When `false`, mermaid code blocks are displayed as plain code. Default: `true` ### math Enables KaTeX math equation rendering. When `true`, inline math (`$...$`), block math (`$$...$$`), and fenced `math` code blocks are rendered as equations. Default: `true` ### editUrl Base URL for "Edit this page" links. The full URL is constructed as `editUrl + contentDir + "/" + entryId`. Set to `false` to disable edit links. Default: `false` (disabled) ```ts editUrl: "https://github.com/my-org/my-repo/edit/main/", // or disable: editUrl: false, ``` ### sitemap Enables automatic `sitemap.xml` generation during build. The sitemap includes all built HTML pages except 404. Default: `true` ### docMetainfo Shows metadata (created date, last updated date, author) under the page title. Dates and author are extracted from git history at build time. Default: `true` ### docTags Enables tag support for documentation pages. When `true`, pages can use the `tags` frontmatter field, and tag index pages are generated at `/docs/tags/`. Default: `true` ### colorTweakPanel Enables an interactive color scheme tweak panel. When `true`, a palette icon appears in the header and users can open a fixed-position panel at the bottom of the page to edit all 16 palette colors, base theme colors, and semantic token overrides in real-time. Changes are persisted in `localStorage`. Default: `false` (set to `true` to enable) See the [Color Tweak Panel guide](/docs/guides/color-tweak-panel) for usage details and how to export custom color schemes. ### docHistory Enables per-document git revision history viewer. When `true`, each doc page shows a **History** button that opens a side panel with the document's git commit history and a line-by-line diff viewer for comparing revisions. At build time, git history is extracted for every content file and saved as JSON files to `dist/doc-history/`. In dev mode, history is served dynamically via a Vite middleware. Default: `false` (set to `true` to enable) See the [Document History guide](/docs/guides/doc-history) for usage details, keyboard shortcuts, and technical information. ### claudeResources Configures the Claude Code Resources Viewer, which auto-generates documentation pages from your `.claude/` directory. Set to `false` to disable. | Property | Type | Description | |----------|------|-------------| | `claudeDir` | string | Path to the `.claude` directory (relative to project root) | | `projectRoot` | string (optional) | Project root override for monorepo setups | ```ts claudeResources: { claudeDir: ".claude", }, // or disable: claudeResources: false, ``` See the [Claude Resources guide](/docs/guides/claude-resources) for details on what gets generated and how it works. ### headerNav An array of navigation links rendered in the site header bar. Each item supports the following properties: | Property | Type | Description | |----------|------|-------------| | `label` | string | Display text shown in the header | | `labelKey` | string (optional) | i18n translation key (e.g., `"nav.gettingStarted"`) — overrides `label` when a translation is available | | `path` | string | URL path the link navigates to | | `categoryMatch` | string (optional) | Links this header tab to a sidebar category. When active, only the matched category's sidebar is shown | ```ts headerNav: [ { label: "Getting Started", labelKey: "nav.gettingStarted", path: "/docs/getting-started", categoryMatch: "getting-started" }, { label: "Guides", labelKey: "nav.guides", path: "/docs/guides", categoryMatch: "guides" }, { label: "Reference", labelKey: "nav.reference", path: "/docs/reference", categoryMatch: "reference" }, { label: "Claude", labelKey: "nav.claude", path: "/docs/claude", categoryMatch: "claude" }, ], ``` ## Astro Configuration The root `astro.config.ts` controls the build pipeline: ```ts output: "static", integrations: [mdx(), react(), searchIndexIntegration()], i18n: { defaultLocale: "en", locales: ["en", ...Object.keys(settings.locales)], routing: { prefixDefaultLocale: false }, }, vite: { plugins: [tailwindcss()], }, markdown: { shikiConfig: { theme: shikiTheme }, }, }); ``` Key aspects: - **output**: Set to `"static"` for full static HTML export - **integrations**: MDX support, React islands, and MiniSearch search indexing - **i18n**: English (default at `/docs/...`) and Japanese (`/ja/docs/...`). Default locale has no URL prefix - **Tailwind CSS**: Loaded via the `@tailwindcss/vite` plugin instead of an Astro integration - **Shiki theme**: Automatically derived from the active color scheme's `shikiTheme` property — no manual sync needed The Shiki code highlighting theme is tied to the color scheme. When you change `colorScheme` in settings, the syntax highlighting theme updates automatically. ## Logo The site logo is displayed on the landing page as a masked SVG shape. The file is located at `public/img/logo.svg`. The default logo is a randomly generated geometric pattern created with [`@takazudo/kumiko-gen`](https://www.npmjs.com/package/@takazudo/kumiko-gen). To regenerate it with a new random pattern: ```bash pnpm run generate:logo ``` You can also replace `public/img/logo.svg` with any custom SVG file. The logo is rendered as a CSS mask using the alpha channel, so: - The SVG **must have a transparent background** — opaque backgrounds render as a filled rectangle - The stroke or fill color in the SVG file does not matter; the displayed color is controlled by the site's color tokens ## Color Scheme Config Color schemes are defined in `src/config/color-schemes.ts`. Each scheme provides 22 color properties plus a `shikiTheme`: - `background`, `foreground`, `cursor`, `selectionBg`, `selectionFg` - `palette` — 16 color slots (`palette[0]` through `palette[15]`) - `shikiTheme` — matching Shiki theme name for syntax highlighting - `semantic` (optional) — overrides for `surface`, `muted`, `accent`, and `accentHover` For details on adding or customizing color schemes, see the [Color guide](/docs/reference/color). --- # Guides > Source: /pj/zudo-doc/docs/guides Explore our guides to learn about zudo-doc features and customization. --- # Demo: Hide Sidebar > Source: /pj/zudo-doc/docs/guides/layout-demos/hide-sidebar This page demonstrates the `hide_sidebar: true` frontmatter option. Notice that the left sidebar is hidden — the content is centered in a narrower container for a focused reading experience. ## What's Happening The left sidebar navigation is hidden on this page. Here is the frontmatter used: ```yaml --- title: "Demo: Hide Sidebar" hide_sidebar: true hide_toc: false --- ``` ## Table of Contents Still Visible The table of contents on the right side is still visible, providing quick access to sections on this page. Only the left sidebar is affected. ## Mobile Navigation On mobile devices, the hamburger menu still provides full navigation access. The `hide_sidebar` option only affects the desktop sidebar. ## When to Use Use `hide_sidebar: true` for pages where sidebar navigation is a distraction — standalone articles, landing pages, or focused content. See the [Frontmatter reference](/docs/guides/frontmatter/#hide_sidebar) for full documentation. ## See Also - [Hide Table of Contents](/docs/guides/layout-demos/hide-toc/) — hides the right-side TOC - [Hide Sidebar & Table of Contents](/docs/guides/layout-demos/hide-both/) — hides both panels --- # Design System > Source: /pj/zudo-doc/docs/reference/design-system zudo-doc uses a **tight token strategy** — instead of importing the full Tailwind framework, only `preflight` and `utilities` are loaded, skipping the default theme entirely. A small, intentional set of design tokens is defined from scratch. This page covers the full system: spacing, typography, colors, border radius, and breakpoints. ## Tight Token Strategy Tailwind CSS ships with hundreds of built-in values — colors, spacing scales, font sizes, and more. In a themeable project, these defaults cause problems: hardcoded values ignore theme changes and create inconsistency. zudo-doc solves this with selective imports in `src/styles/global.css` — only `preflight` (reset) and `utilities` (functional classes) are imported, while the default theme layer is skipped entirely: ```css @import "tailwindcss/preflight"; @import "tailwindcss/utilities"; @theme { /* Only define what we need — no resets necessary */ --color-bg: var(--zd-bg); --color-fg: var(--zd-fg); /* ... */ } ``` Because the default theme is never loaded, no `--*: initial` reset block is needed. Only explicitly defined tokens in `@theme` work. Using an undefined token (like `p-4` or `bg-gray-500`) produces no effect — the value simply doesn't exist. This gives you a guarantee: **invalid tokens cause build errors or missing styles, not visual bugs**. This is the core philosophy: define only what the project needs. Every value in the system is intentional, and accidental use of Tailwind defaults is immediately visible. ## Spacing zudo-doc separates spacing into two axes: **horizontal (hsp)** and **vertical (vsp)**. They serve different purposes in layout — horizontal spacing controls inline rhythm and gutters, while vertical spacing controls content flow and section separation. The vertical scale is stretched at larger sizes to give content more breathing room. ### Horizontal Spacing (hsp) | Token | Value | Example Class | |-------|-------|---------------| | `hsp-2xs` | `0.25rem` (4px) | `px-hsp-2xs` | | `hsp-xs` | `0.375rem` (6px) | `px-hsp-xs` | | `hsp-sm` | `0.5rem` (8px) | `gap-x-hsp-sm` | | `hsp-md` | `0.75rem` (12px) | `px-hsp-md` | | `hsp-lg` | `1rem` (16px) | `px-hsp-lg` | | `hsp-xl` | `1.5rem` (24px) | `px-hsp-xl` | | `hsp-2xl` | `2rem` (32px) | `px-hsp-2xl` | ### Vertical Spacing (vsp) | Token | Value | Example Class | |-------|-------|---------------| | `vsp-2xs` | `0.25rem` (4px) | `py-vsp-2xs` | | `vsp-xs` | `0.5rem` (8px) | `py-vsp-xs` | | `vsp-sm` | `0.75rem` (12px) | `gap-y-vsp-sm` | | `vsp-md` | `1rem` (16px) | `py-vsp-md` | | `vsp-lg` | `1.5rem` (24px) | `py-vsp-lg` | | `vsp-xl` | `2rem` (32px) | `py-vsp-xl` | | `vsp-2xl` | `3rem` (48px) | `py-vsp-2xl` | Both axes also include `0` (`0px`) and `px` (`1px`) utility values. ```html Content with asymmetric spacing Grid items ``` Notice that `hsp-lg` is `1rem` while `vsp-lg` is `1.5rem` — the vertical axis is stretched at larger sizes. This is intentional — vertical flow needs more room than horizontal rhythm. ## Typography ### Font Sizes | Token | Value | Example | |-------|-------|---------| | `caption` | `0.75rem` / 12px | `text-caption` | | `small` | `0.875rem` / 14px | `text-small` | | `body` | `1rem` / 16px | `text-body` | | `subheading` | `1.125rem` / 18px | `text-subheading` | | `heading` | `1.875rem` / 30px | `text-heading` | | `display` | `3.75rem` / 60px | `text-display` | ### Font Weights | Token | Value | Example | |-------|-------|---------| | `normal` | `400` | `font-normal` | | `medium` | `500` | `font-medium` | | `semibold` | `600` | `font-semibold` | | `bold` | `700` | `font-bold` | ### Line Heights | Token | Value | Example | |-------|-------|---------| | `tight` | `1.25` | `leading-tight` | | `snug` | `1.375` | `leading-snug` | | `normal` | `1.5` | `leading-normal` | | `relaxed` | `1.625` | `leading-relaxed` | ### Font Families | Token | Stack | Example | |-------|-------|---------| | `sans` | System sans-serif stack | `font-sans` | | `mono` | System monospace stack | `font-mono` | ```html Page Title Body text inline code ``` ## Border Radius | Token | Value | Example | |-------|-------|---------| | `DEFAULT` | `0.25rem` (4px) | `rounded` | | `lg` | `0.5rem` (8px) | `rounded-lg` | | `full` | `9999px` | `rounded-full` | ```html Default radius Card with larger radius Pill badge ``` ## Breakpoints | Token | Value | Example | |-------|-------|---------| | `sm` | `640px` | `sm:flex` | | `lg` | `1024px` | `lg:grid-cols-2` | | `xl` | `1280px` | `xl:max-w-5xl` | ```html Responsive horizontal padding ``` ## Colors Colors use a **three-tier strategy**: raw palette values (Tier 1) flow into semantic tokens (Tier 2), which feed into component-scoped tokens (Tier 3). Each tier only references the tier above it, so swapping a color scheme updates the entire site at once. See the [Color reference](/docs/reference/color) for full details on the color token system, color schemes, and customization. ## Usage Rules The default Tailwind theme is not imported — only `tailwindcss/preflight` and `tailwindcss/utilities` are loaded. Only project-defined tokens work. ### Do ```html Primary text Panel Link Proper spacing Grid gaps Heading ``` ### Don't ```html Broken Breaks on theme switch ``` All tokens are defined in `src/styles/global.css`. That file is the single source of truth for the design system. --- # /CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/root **Path:** `CLAUDE.md` # zudo-doc Minimal documentation framework built with Astro 6, MDX, Tailwind CSS v4, and React islands. ## Tech Stack - **Astro 6** — static site generator with Content Collections - **MDX** — via `@astrojs/mdx`, content directory configurable via `docsDir` setting - **Tailwind CSS v4** — via `@tailwindcss/vite` (not `@astrojs/tailwind`) - **React 19** — for interactive islands only (TOC scroll spy, sidebar toggle, collapsible categories) - **Shiki** — built-in code highlighting, theme set from active color scheme - **TypeScript** — strict mode via `astro/tsconfigs/strict` ## Commands - `pnpm dev` — runs Astro dev server (port 4321) and doc-history-server (port 4322) concurrently via `run-p` (predev kills stale processes) - `pnpm dev:astro` — Astro dev server only (port 4321) - `pnpm dev:history` — doc history API server only (port 4322) - `pnpm dev:stable` — alternative build-then-serve dev mode (avoids HMR crashes on content file add/remove) - `pnpm dev:network` — Astro dev server with `--host 0.0.0.0` for LAN access - `pnpm build` — static HTML export to `dist/` - `pnpm check` — Astro type checking - `pnpm b4push` — pre-push validation: format check → typecheck → build → link check → E2E tests ## Key Directories ``` packages/ ├── ai-chat-worker/ # CF Worker for AI chat API ├── search-worker/ # CF Worker for search API ├── doc-history-server/ # Doc history REST API + CLI generator └── create-zudo-doc/ # CLI scaffold tool src/ ├── components/ # Astro + React components │ └── admonitions/ # Note, Tip, Info, Warning, Danger ├── config/ # Settings, color schemes ├── content/ │ ├── docs/ # English MDX content │ └── docs-ja/ # Japanese MDX content (mirrors docs/) ├── hooks/ # React hooks (scroll spy) ├── layouts/ # Astro layouts (doc-layout) ├── pages/ # File-based routing │ ├── docs/[...slug] # English doc routes │ └── ja/docs/[...slug] # Japanese doc routes └── styles/ └── global.css # Design tokens (@theme) & Tailwind config ``` ## Conventions ### Components: Astro vs React - Default to **Astro components** (`.astro`) — zero JS, server-rendered - Use **React islands** (`client:load`) only when client-side interactivity is needed - Current React islands: `toc.tsx`, `mobile-toc.tsx`, `sidebar-toggle.tsx`, `sidebar-tree.tsx`, `theme-toggle.tsx`, `doc-history.tsx`, `color-tweak-panel.tsx`, `color-tweak-export-modal.tsx` ### Content Collections - Schema defined in `src/content.config.ts` (Zod validation) - Uses Astro 5 `glob()` loader with configurable `base` directory from settings - Content directories: `docsDir` (default: `src/content/docs`), `docsJaDir` (default: `src/content/docs-ja`) - Required frontmatter: `title` (string) - Optional: `description`, `sidebar_position` (number), `category` - Sidebar order is driven by `sidebar_position` ### Admonitions Available in all MDX files without imports (registered globally in doc page): ``, ``, ``, ``, `` — each accepts optional `title` prop. ### Terminology: "Update docs" When we say "update docs" or "update our doc," it means updating the **showcase documentation** content in `src/content/docs/` (English) and `src/content/docs-ja/` (Japanese). Since zudo-doc is a documentation framework, its own content directories serve as the default showcase. These are the pages visible when running `pnpm dev`. ### i18n - English (default): `/docs/...` — content in `docsDir` (default: `src/content/docs`) - Japanese: `/ja/docs/...` — content in `docsJaDir` (default: `src/content/docs-ja`) - Configured in `astro.config.ts` with `prefixDefaultLocale: false` - Japanese docs should mirror the English directory structure ## CI Pipeline Production (`main-deploy.yml`) and PR (`pr-checks.yml`) workflows use parallel build jobs: - **build-site** — shallow clone (`fetch-depth: 1`), `SKIP_DOC_HISTORY=1 pnpm build` - **build-history** — full clone (`fetch-depth: 0`), `@zudo-doc/doc-history-server generate` - **deploy/preview** — merges both artifacts, deploys to Cloudflare Pages E2E tests run with full clone and inline doc-history generation (no `SKIP_DOC_HISTORY`). ## Feature Change Checklist When adding or removing a feature from zudo-doc, update the `create-zudo-doc` generator to stay in sync: 1. **`src/config/settings.ts`** — Add/remove the setting field 2. **`packages/create-zudo-doc/src/settings-gen.ts`** — Add/remove the setting in generated output 3. **`packages/create-zudo-doc/src/scaffold.ts`** — Add/remove dependencies in `generatePackageJson()` 4. **`packages/create-zudo-doc/src/strip.ts`** — Add/remove import stripping and file removal for the feature 5. **`packages/create-zudo-doc/src/__tests__/scaffold.test.ts`** — Update tests 6. Run `/l-sync-create-zudo-doc` to verify no drift remains ## Design Tokens & CSS See `src/CLAUDE.md` for design token system (three-tier color strategy, color rules, scheme configuration) and CSS conventions (component-first strategy, tight token strategy). --- # Admonitions > Source: /pj/zudo-doc/docs/components/admonitions Admonitions are callout blocks for highlighting important information. All five types are globally available — no imports needed. ## Note :::note This is a note — use it for general information and tips. ::: :::note[Custom Title] Notes are great for calling out important information readers shouldn't miss. ::: ```mdx :::note This is a note — use it for general information and tips. ::: :::note[Custom Title] Notes are great for calling out important information readers shouldn't miss. ::: ``` ## Tip :::tip This is a tip — use it for helpful suggestions and best practices. ::: :::tip[Pro Tip] Use keyboard shortcuts to speed up your workflow. ::: ```mdx :::tip This is a tip — use it for helpful suggestions and best practices. ::: :::tip[Pro Tip] Use keyboard shortcuts to speed up your workflow. ::: ``` ## Info :::info This is an info block — use it for additional context or background information. ::: :::info[Did You Know?] This feature was introduced in version 2.0. ::: ```mdx :::info This is an info block — use it for additional context or background information. ::: :::info[Did You Know?] This feature was introduced in version 2.0. ::: ``` ## Warning :::warning This is a warning — use it to flag potential issues or things to watch out for. ::: :::warning[Deprecation Notice] This API will be removed in the next major version. Please migrate to the new API. ::: ```mdx :::warning This is a warning — use it to flag potential issues or things to watch out for. ::: :::warning[Deprecation Notice] This API will be removed in the next major version. Please migrate to the new API. ::: ``` ## Danger :::danger This is a danger alert — use it for critical warnings about data loss or breaking changes. ::: :::danger[Breaking Change] Running this command will permanently delete all data. This action cannot be undone. ::: ```mdx :::danger This is a danger alert — use it for critical warnings about data loss or breaking changes. ::: :::danger[Breaking Change] Running this command will permanently delete all data. This action cannot be undone. ::: ``` ## Component Syntax In addition to the directive syntax shown above, you can use JSX component syntax. This is useful when you need more control or are building custom layouts. ```mdx Default note with auto-generated title. Warning with a custom title. ``` The component syntax supports the same five types (`Note`, `Tip`, `Info`, `Warning`, `Danger`) and an optional `title` prop. ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `title` | string | Type name (e.g., "Note") | Override the default title text | ## Color Reference Each admonition type uses a semantic color token for its border and title color: | Type | Color Token | Typical Color | |------|------------|---------------| | Note | accent | Blue | | Tip | success | Green | | Info | info | Cyan | | Warning | warning | Yellow | | Danger | danger | Red | --- # Components > Source: /pj/zudo-doc/docs/components Explore the components and content features available in zudo-doc. --- # Generator CLI Testing > Source: /pj/zudo-doc/docs/develop/generator-cli-testing # Generator CLI Testing The `create-zudo-doc` generator CLI scaffolds new zudo-doc projects with various feature combinations. Testing it requires verifying that each combination builds, runs, and produces the correct files and settings. Two Claude Code skills automate this workflow: | Skill | Purpose | |-------|---------| | `/l-generator-cli-tester ` | Test a single generation pattern | | `/l-run-generator-cli-whole-test` | Run all 9 patterns, fix bugs, verify everything passes | ## Test Patterns Each pattern enables a different feature combination: | Pattern | Description | |---------|-------------| | `barebone` | Everything OFF — minimal project | | `search` | Only search enabled | | `i18n` | Only i18n enabled | | `sidebar-filter` | Only sidebar filter enabled | | `claude-resources` | Only Claude Resources enabled | | `color-tweak-panel` | Only color tweak panel enabled (uses API) | | `light-dark` | Light-dark color scheme mode | | `lang-ja` | Japanese as default language | | `all-features` | Everything ON | ## Running Tests ### Full test suite Run all 9 patterns end-to-end, automatically fix any failures, and verify: ``` /l-run-generator-cli-whole-test ``` With headless browser rendering checks: ``` /l-run-generator-cli-whole-test --headless ``` ### Single pattern Test one pattern in isolation: ``` /l-generator-cli-tester barebone /l-generator-cli-tester all-features --headless ``` ## What Each Test Checks Each pattern goes through these steps: 1. **Scaffold** — Run the generator CLI (or programmatic API) with pattern-specific flags 2. **Install** — `pnpm install` in the generated project 3. **Build** — `pnpm build` to verify static export succeeds 4. **Dev server** — Start `pnpm dev`, wait 8 seconds, verify the process is still running 5. **File verification** — Check expected files are present/absent based on enabled features 6. **Settings verification** — Read the generated `settings.ts` and confirm correct values 7. **Showcase comparison** — Compare generated code against the main zudo-doc showcase 8. **Headless browser** (with `--headless`) — Render pages in a real browser, check for JS errors, verify visual elements (search icon, language switcher, theme toggle, etc.) ## The Bug-Fix Workflow The `/l-run-generator-cli-whole-test` skill has a structured bug-fix phase: 1. **Phase 1** — Run all 9 patterns and collect results 2. **Phase 2** — For each failure: diagnose which step failed, read the relevant generator source file, apply a minimal fix, rebuild the CLI and re-test the failing pattern, then commit each fix individually 3. **Phase 3** — Re-run all 9 patterns from scratch to ensure no regressions 4. **Phase 4** — Output a summary report ### Common failure categories | Failure | Likely cause | Fix location | |---------|-------------|--------------| | Missing module at build time | Dependency not in generated `package.json` | `scaffold.ts` — `generatePackageJson()` | | Import not found | Import not stripped for disabled feature | `strip.ts` | | Type error in settings.ts | Settings field missing or wrong | `settings-gen.ts` | | Component references stripped component | Template usage not stripped | `strip.ts` patch patterns | | Feature file exists when it shouldn't | File not removed during stripping | `strip.ts` | ## Key Generator Files | File | Role | |------|------| | `packages/create-zudo-doc/src/scaffold.ts` | Copies template, generates `package.json` | | `packages/create-zudo-doc/src/strip.ts` | Removes features/imports based on options | | `packages/create-zudo-doc/src/settings-gen.ts` | Generates `settings.ts` | | `packages/create-zudo-doc/src/constants.ts` | Feature definitions, color scheme lists | | `packages/create-zudo-doc/src/cli.ts` | CLI argument parsing | | `packages/create-zudo-doc/src/api.ts` | Programmatic API | ## Notes - Test directories are created under `__inbox/` (gitignored) to avoid polluting the repo - The `barebone` pattern is the baseline — if it fails, fix it before testing others - `colorTweakPanel` has no CLI flag — patterns using it (`color-tweak-panel`, `all-features`) use the programmatic API - The `--headless` flag adds browser-level rendering checks on top of process-level checks - Always rebuild the CLI (`pnpm build` in `packages/create-zudo-doc`) before testing and after each fix --- # Installation > Source: /pj/zudo-doc/docs/getting-started/installation ## Prerequisites - **Node.js 18+** — required for Astro 6 - **pnpm** — recommended package manager You can also use npm, yarn, or bun, but this guide uses pnpm for all examples. ## Create a New Project The fastest way to get started is with the `create-zudo-doc` CLI. It scaffolds a new project with an interactive setup wizard. ```bash pnpm create zudo-doc ``` Or with other package managers: ```bash npm create zudo-doc yarn create zudo-doc bunx create-zudo-doc ``` For non-interactive usage (CI, automation, AI agents), use `--yes` to accept defaults or pass flags directly: ```bash pnpm create zudo-doc my-docs --yes pnpm create zudo-doc my-docs --lang ja --scheme Dracula --no-i18n --pm pnpm --install ``` See the [CLI reference](/docs/reference/create-zudo-doc) for all available flags. The CLI walks you through the following options: ### Project Name Enter a name for your project directory (default: `my-docs`). ### Default Language Choose the default language for your documentation site. Supported languages include English, Japanese, Chinese (Simplified/Traditional), Korean, Spanish, French, German, and Portuguese. ### Color Scheme Mode Choose how your site handles color schemes: - **Light & Dark (toggle)** — users can switch between light and dark themes. Pick from pre-configured pairings (GitHub, Catppuccin, Solarized, Rosé Pine, Atom One, Everforest, Gruvbox, Ayu) or choose light and dark schemes individually. - **Single scheme** — one fixed color scheme for the entire site. 40 schemes are available, including Dracula, Nord, TokyoNight, and more. When using light/dark mode, you can also set the default mode (light or dark) and whether to respect the user's system color scheme preference. ### Features Select which optional features to include: | Feature | Default | Description | |---------|---------|-------------| | [i18n (multi-language)](/docs/guides/i18n/) | Off | Add a secondary language with mirrored content directories | | [Pagefind search](/docs/guides/search/) | On | Full-text search across all documentation | | [Sidebar filter](/docs/guides/sidebar-filter/) | On | Real-time filtering of sidebar navigation items | | [Claude Resources](/docs/guides/claude-resources/) | Off | Auto-generated documentation for Claude Code components | | [Color scheme preview](/docs/guides/color-scheme-preview/) | Off | Browse 50+ preset color schemes via the Color Tweak Panel | ### Package Manager Choose your preferred package manager: pnpm (recommended), npm, yarn, or bun. After scaffolding, the CLI will ask whether to install dependencies for you. The installer generates a `src/config/settings.ts` file with your choices. You can change these settings at any time after project creation. ## Alternative: Clone the Repository You can also start by cloning the full repository directly: ```bash git clone https://github.com/zudolab/zudo-doc.git my-docs cd my-docs pnpm install ``` Cloning the repository gives you the complete project including the documentation source and all features enabled. Use this approach if you want to explore the full codebase or contribute to zudo-doc itself. ## Development ```bash pnpm dev ``` The dev server starts on port 4321 with Vite for instant hot module replacement. Make sure port 4321 is available. If another process is using it, Astro will automatically pick the next available port. ## Build ```bash pnpm build ``` This generates static HTML in the `dist/` directory. You can deploy it to any static hosting service. ## Type Checking ```bash pnpm check ``` Runs Astro's built-in type checker with strict TypeScript mode. Never commit the `dist/` directory to source control. It is already excluded via `.gitignore`. ## Project Structure After installation, your project looks like this: ``` src/ ├── components/ # Astro + React components │ └── admonitions/ # Note, Tip, Info, Warning, Danger ├── config/ │ ├── settings.ts # Site name, active color scheme │ ├── color-schemes.ts # All available color presets │ └── color-scheme-utils.ts ├── content/ │ ├── docs/ # English MDX content │ └── docs-ja/ # Japanese MDX content ├── layouts/ # Page layouts ├── pages/ # File-based routing └── styles/ └── global.css # Design tokens & Tailwind config ``` See the [Writing Docs](/docs/getting-started/writing-docs) guide for how to create and organize your documentation pages. --- # Frontmatter > Source: /pj/zudo-doc/docs/guides/frontmatter Every MDX file in zudo-doc starts with a YAML frontmatter block delimited by `---`. This page documents all available fields. ## Complete Example ```mdx --- title: My Documentation Page description: A comprehensive guide to something important. sidebar_position: 3 sidebar_label: My Page tags: [tutorial, setup] --- Your content here. ``` ## Minimal Example Only `title` is required: ```mdx --- title: Quick Start --- ``` ## Field Overview | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `title` | string | Yes | -- | Page title shown in heading, sidebar, and browser tab | | `description` | string | No | -- | Subtitle shown below the title | | `sidebar_position` | number | No | `999` | Sort order within the sidebar category | | `sidebar_label` | string | No | Value of `title` | Override label shown in sidebar | | `category` | string | No | Directory name | Reserved for future use | | `search_exclude` | boolean | No | `false` | Exclude the page from search indexing | | `tags` | string[] | No | -- | Tags for cross-cutting navigation | | `draft` | boolean | No | `false` | Exclude from production build (visible in dev) | | `unlisted` | boolean | No | `false` | Built but hidden from sidebar, search, and search engines | | `hide_sidebar` | boolean | No | `false` | Hide the left sidebar, center content in a narrower container | | `hide_toc` | boolean | No | `false` | Hide the right-side table of contents | | `standalone` | boolean | No | `false` | Hidden from sidebar nav but still indexed (unlike `unlisted`) | | `slug` | string | No | Derived from file path | Custom URL slug override | | `pagination_next` | string \| null | No | Auto | Override next page link, set to `null` to hide | | `pagination_prev` | string \| null | No | Auto | Override previous page link, set to `null` to hide | ## Fields ### `title` - **Type:** `string` - **Required:** Yes The page title. It is displayed as the `h1` heading on the page, in the sidebar navigation, in the browser tab, and in search results. ```mdx --- title: Getting Started with zudo-doc --- ``` ### `description` - **Type:** `string` - **Required:** No A short description or subtitle for the page. Shown below the title on the rendered page. Also useful for SEO meta tags. ```mdx --- title: Installation description: How to install and set up zudo-doc for your project. --- ``` ### `sidebar_position` - **Type:** `number` - **Required:** No - **Default:** `999` (appears at the end) Controls the sort order of the page within its sidebar category. Lower numbers appear first. ```mdx --- title: Introduction sidebar_position: 1 --- ``` For category index pages (`index.mdx`), the `sidebar_position` also controls where the entire category appears relative to other categories in the sidebar. ### `sidebar_label` - **Type:** `string` - **Required:** No - **Default:** Value of `title` Overrides the label displayed in the sidebar navigation. Use this when you want a shorter or different label than the full page title. ```mdx --- title: Configuring Color Schemes and Themes sidebar_label: Color Schemes --- ``` ### `category` - **Type:** `string` - **Required:** No - **Default:** Derived from the directory structure (first directory segment) Reserved for future use. This field is defined in the content schema but is not currently used by the sidebar or routing logic. Categories are always derived from the directory structure. Organize pages into categories by placing them in directories under `src/content/docs/`. The directory name becomes the category name. ### `search_exclude` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, excludes the page from the search index. Useful for internal pages, changelogs, or imported content that should not appear in search results. ```mdx --- title: Internal Changelog search_exclude: true --- ``` ### `tags` - **Type:** `string[]` - **Required:** No Assigns tags to the page for cross-cutting navigation. Tags are displayed as clickable badges on the page, and tag index pages are auto-generated at `/docs/tags/` and `/docs/tags/[tag]`. ```mdx --- title: Deploying to Netlify tags: [deployment, netlify, hosting] --- ``` Tags are useful for grouping related pages across different sidebar categories. ### `draft` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, the page is excluded from the production build entirely. Draft pages are still visible during development (`pnpm dev`) for preview purposes. ```mdx --- title: Upcoming Feature draft: true --- ``` ### `unlisted` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, the page is built and accessible via its URL, but is hidden from the sidebar navigation, search index, and search engines (via `noindex` meta tag). ```mdx --- title: Internal Notes unlisted: true --- ``` ### `hide_sidebar` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, the left sidebar is hidden on desktop and the content is centered in a narrower container. The mobile hamburger menu still provides access to navigation. Useful for landing pages, standalone articles, or any page where a full-width reading experience is preferred. ```mdx --- title: About This Project hide_sidebar: true --- ``` See the [live demo](/docs/guides/layout-demos/hide-sidebar/) to experience this layout option in action. ### `hide_toc` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, the right-side table of contents (section navigation) and the mobile TOC are both hidden. The main content area expands to fill the available width. ```mdx --- title: Changelog hide_toc: true --- ``` See the [live demo](/docs/guides/layout-demos/hide-toc/) to experience this layout option in action. Combine `hide_sidebar` and `hide_toc` to create a clean, centered reading layout with no navigation panels: ```mdx --- title: About hide_sidebar: true hide_toc: true --- ``` See the [live demo](/docs/guides/layout-demos/hide-both/) to see both options combined. ### `standalone` - **Type:** `boolean` - **Required:** No - **Default:** `false` When set to `true`, the page is hidden from sidebar navigation and site index but remains accessible via its URL and is still indexed by search engines. Unlike `unlisted`, standalone pages do not get a `noindex` meta tag. ```mdx --- title: Terms of Service standalone: true --- ``` Use `standalone` for pages that should be publicly accessible but don't belong in the documentation navigation (e.g., legal pages, special landing pages). Use `unlisted` when you also want to prevent search engine indexing. ### `slug` - **Type:** `string` - **Required:** No - **Default:** Derived from the file path Overrides the URL slug for the page. Use this when you want a different URL than what the file path would produce. ```mdx --- title: Getting Started with zudo-doc slug: quickstart --- ``` This page would be accessible at `/docs/quickstart` instead of the default path derived from its file location. ### `pagination_next` - **Type:** `string | null` - **Required:** No - **Default:** Automatically determined by sidebar order Overrides the "Next" page link shown at the bottom of the document. Set to a document path (e.g., `"guides/configuration"`) to link to a specific page, or set to `null` to hide the next link entirely. ```mdx --- title: Final Step pagination_next: null --- ``` ### `pagination_prev` - **Type:** `string | null` - **Required:** No - **Default:** Automatically determined by sidebar order Overrides the "Previous" page link shown at the bottom of the document. Set to a document path (e.g., `"getting-started/introduction"`) to link to a specific page, or set to `null` to hide the previous link entirely. ```mdx --- title: Getting Started pagination_prev: null --- ``` --- # Demo: Hide Table of Contents > Source: /pj/zudo-doc/docs/guides/layout-demos/hide-toc This page demonstrates the `hide_toc: true` frontmatter option. The right-side table of contents is hidden, and the main content area expands to fill the available width. ## What's Happening The table of contents is hidden on this page. Here is the frontmatter used: ```yaml --- title: "Demo: Hide Table of Contents" hide_sidebar: false hide_toc: true --- ``` ## Sidebar Still Visible The sidebar on the left side is still visible for navigation. Only the right-side table of contents is affected. ## Desktop and Mobile Both the desktop table of contents (right sidebar) and the mobile table of contents are hidden when `hide_toc: true` is set. ### Subsection Example This subsection exists to demonstrate that even though the page has headings and subsections, no table of contents is generated. ## When to Use Use `hide_toc: true` for pages with few headings, short content, or when the extra content width is more valuable than section navigation. See the [Frontmatter reference](/docs/guides/frontmatter/#hide_toc) for full documentation. ## See Also - [Hide Sidebar](/docs/guides/layout-demos/hide-sidebar/) — hides the left sidebar - [Hide Sidebar & Table of Contents](/docs/guides/layout-demos/hide-both/) — hides both panels --- # Component First Strategy > Source: /pj/zudo-doc/docs/reference/component-first zudo-doc follows a **component-first strategy**: always express UI as components with Tailwind utility classes. Never create custom CSS class names with separate stylesheets. ## The Problem When projects use a utility CSS framework alongside a component framework, developers frequently fall back to traditional CSS patterns. Instead of composing utility classes inside components, they create custom CSS class names — `.profile-card`, `.btn-primary`, `.sidebar-nav` — with separate stylesheets or CSS modules. This creates a fragmented codebase: - Some components use Tailwind utilities inline - Others introduce custom CSS classes with BEM naming or CSS modules - Some mix both approaches in the same file For AI agents, this is a particularly common failure mode. Given a task like "build a profile card," an agent will often generate a `.profile-card` class with a CSS module — the pattern seen most often in training data. Over time, the codebase becomes a patchwork of conflicting styling approaches. ## The Rule **The component itself is the abstraction.** CSS class names like `.card` or `.btn-primary` are unnecessary — the component handles encapsulation, and utility classes handle styling. - Need a card? Create a `` component with utility classes - Need a button variant? Create a `` component - Need a layout pattern? Create a `` component ## In zudo-doc zudo-doc uses **Astro components** (`.astro`) and **React islands** (`.tsx`) with **Tailwind CSS v4** utilities. The same rule applies to both: ### Astro Components Most UI is server-rendered Astro — zero JavaScript, utility classes inline: ```astro ``` No `.footer` class. No `footer.module.css`. The component **is** the abstraction. ### React Islands Interactive components use React with `client:load`, same utility approach: ```tsx // src/components/sidebar-toggle.tsx const [open, setOpen] = useState(false); return ( setOpen(!open)} > {label} ); } ``` ### Anti-Pattern Do **not** create CSS class names in a zudo-doc project: ```css /* WRONG — don't create custom CSS classes */ .profile-card { display: flex; gap: 1rem; padding: 1.5rem; } .profile-card__name { font-size: 1.25rem; font-weight: 600; } ``` ```astro {name} ``` Instead: ```astro {name} ``` ## Component Variants via Props Instead of CSS modifier classes (`.btn--primary`, `.btn--secondary`), use component props: ```tsx function Button({ variant = "primary", children }) { const styles = { primary: "bg-accent text-bg hover:bg-accent-hover", secondary: "bg-surface text-fg border border-muted", }; return ( {children} ); } ``` Usage: ```tsx Save Cancel ``` No `.btn-primary` class to maintain. The `variant` prop is type-safe, auto-completable, and self-documenting. ## Component Composition Complex layouts are built by composing smaller components — not by adding more CSS: ```astro {users.map((user) => ( {user.name} {user.email} ))} ``` Each piece — ``, the list layout — is a component. No `.user-list__item` or `.user-list__avatar` class names needed. ## Using zudo-doc's Design Tokens Always use project tokens instead of arbitrary values: ```astro ``` See [Design System](/docs/reference/design-system) for available spacing, typography, and color tokens. ## When Custom CSS Is Acceptable The **only** place custom CSS belongs in zudo-doc is in `src/styles/global.css`: - **Content typography** — the `.zd-content` class styles rendered MDX elements (headings, paragraphs, lists) because these elements are generated by the MDX pipeline, not authored as components - **Design token definitions** — the `@theme` block that registers Tailwind tokens Everything else — every component, every layout, every UI element — uses utility classes directly. ## Rules Summary 1. **Always create components** — not CSS classes 2. **Use utility classes directly** in component markup 3. **Never create CSS module files** or custom class names 4. **Use props for variants** — not CSS modifiers 5. **Compose components** — build complex UI from smaller components, not from more CSS 6. **Use project tokens** — `text-fg`, `bg-surface`, `p-hsp-md`, not arbitrary values --- # /e2e/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/e2e **Path:** `e2e/CLAUDE.md` # E2E Tests ## Architecture 5 Playwright fixtures, each with its own port, build, and `settings.ts`: | Fixture | Port | Purpose | |---|---|---| | sidebar | 4500 | Sidebar persistence, filter | | i18n | 4501 | Locale fallback, translation | | theme | 4502 | Light/dark toggle, hydration | | smoke | 4503 | General features (search, TOC, code blocks, mermaid, doc history, etc.) | | versioning | 4504 | Version switcher, banners | Configured in `playwright.config.ts`. Each fixture runs `astro preview` on its port. ## Adding Tests **No new fixture needed in most cases.** The `testMatch` pattern is `${name}*.spec.ts`, so: - `smoke-search.spec.ts` automatically runs against the smoke fixture - `sidebar-filter.spec.ts` automatically runs against the sidebar fixture To add a test: create `e2e/{fixture-name}-{feature}.spec.ts`. No config changes needed. To add content for tests: add MDX files to the fixture's `src/content/docs/` directory, then enable any needed settings in its `src/config/settings.ts`. ## Two Test Patterns **Static HTML tests** (no browser needed) — read pre-built `dist/` with `readFileSync`: ```typescript const html = readDistFile("docs/some-page/index.html"); expect(html).toContain("expected string"); ``` **Browser tests** — use Playwright `page` fixture for interactive features: ```typescript test("feature works", async ({ page }) => { await page.goto("/docs/some-page"); await expect(page.locator('[aria-label="Search"]')).toBeVisible(); }); ``` ## Fixture Setup Pipeline (`setup-fixtures.sh`) Each fixture shares framework source from repo root via **symlinks**, but has its own content and settings: - **Symlinked**: `components/`, `hooks/`, `integrations/`, `layouts/`, `plugins/`, `styles/`, `types/`, `utils/`, `node_modules/` - **Copied** (has relative imports): `astro.config.ts`, `content.config.ts`, `src/config/*.ts` (except `settings.ts`) - **Fixture-specific**: `src/config/settings.ts`, `src/content/docs/` All fixtures are pre-built sequentially before Playwright runs (`astro build`), then Playwright only runs `astro preview`. The smoke fixture also initializes a git repo for doc-history testing (2 commits). ## Commands ```bash pnpm test:e2e # Full suite (setup + all tests) pnpm test:e2e:ci # CI suite (skips @local-only tests) npx playwright test e2e/smoke-search.spec.ts --project smoke # Single test file npx playwright test --project smoke # All tests for one fixture ``` ## `@local-only` Tag Tests that are too specific for CI (flaky DOM operations, timing-sensitive UI checks) can be tagged `@local-only` in the test title: ```typescript test("HSL picker opens from color swatch @local-only", async ({ page }) => { ... }); ``` - `pnpm test:e2e` — runs everything (local dev, `b4push`) - `pnpm test:e2e:ci` — skips `@local-only` tests (CI workflows) ## Sidebar Test Helper `e2e/sidebar-helpers.ts` exports `desktopSidebar(page)` and `waitForSidebarHydration(page)` for tests that interact with the sidebar React island. --- # Tabs > Source: /pj/zudo-doc/docs/components/tabs Use `` and `` to create tabbed content panels. Both components are globally available — no imports needed. ## Basic Usage ```bash npm install zudo-doc ``` ```bash pnpm add zudo-doc ``` ```bash yarn add zudo-doc ``` ````mdx ```bash npm install zudo-doc ``` ```bash pnpm add zudo-doc ``` ```bash yarn add zudo-doc ``` ```` The `default` prop on `TabItem` sets which tab is initially active. If omitted, the first tab is shown. ## Synced Tabs Use `groupId` to sync tab selection across multiple tab groups on the same page. The selected tab is persisted in localStorage, so it carries across page navigations. ```js console.log("Hello"); ``` ```ts console.log("Hello" as string); ``` ```js const sum = (a, b) => a + b; ``` ```ts const sum = (a: number, b: number): number => a + b; ``` ````mdx ```js console.log("Hello"); ``` ```ts console.log("Hello" as string); ``` ```js const sum = (a, b) => a + b; ``` ```ts const sum = (a: number, b: number): number => a + b; ``` ```` ## Tabs Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `groupId` | string | — | Sync tab selection across groups with the same `groupId` (persisted in localStorage) | ## TabItem Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | string | (required) | Tab button text | | `value` | string | Value of `label` | Unique identifier for the tab | | `default` | boolean | `false` | Set as the initially active tab | --- # Writing Docs > Source: /pj/zudo-doc/docs/getting-started/writing-docs ## Creating a Document Create an `.mdx` file in `src/content/docs/`. Use frontmatter for metadata: ```mdx --- title: My Page description: A brief summary of this page. sidebar_position: 1 --- Your content here. ``` ### Frontmatter Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | Page title (shown in sidebar and header) | | `description` | string | No | Subtitle shown below the title | | `sidebar_position` | number | No | Sort order within category (lower = first) | | `sidebar_label` | string | No | Override sidebar display label | See the [Frontmatter guide](/docs/guides/frontmatter) for the complete reference of all available fields. ## Directory Structure Organize docs into categories using directories: ``` src/content/docs/ getting-started/ introduction.mdx # sidebar_position: 1 installation.mdx # sidebar_position: 2 writing-docs.mdx # sidebar_position: 3 guides/ configuration.mdx # sidebar_position: 1 sidebar.mdx # sidebar_position: 2 ``` Each directory becomes a collapsible sidebar category automatically. The category name is derived from the directory name (kebab-case converted to Title Case). ## Admonitions zudo-doc supports Docusaurus-style admonitions. They are registered globally — no imports needed: This is a **note** — use it for general information that readers should be aware of. This is a **tip** — use it for helpful suggestions and best practices. This is an **info** block — use it for additional context or background information. This is a **warning** — use it to flag potential issues or things to watch out for. This is a **danger** alert — use it for critical warnings about data loss or breaking changes. ### Custom Titles You can provide a custom title to any admonition using the `title` prop. ### Admonition Syntax Two syntaxes are supported. No imports needed for either. **Directive syntax** (recommended for content authors): ```mdx :::note[Optional Title] Content here. ::: ``` **JSX component syntax**: ```mdx Default note with auto-generated title. Warning with a custom title. ``` Each admonition type maps to a palette slot for its border and title color: | Type | Palette Slot | Typical Color | |------|-------------|---------------| | Note | p4 | Blue | | Tip | p2 | Green | | Info | p6 | Cyan | | Warning | p3 | Yellow | | Danger | p1 | Red | For a complete list of all available components including admonitions, code blocks, and more, see the [Components](/docs/components/) reference. ## i18n (Internationalization) zudo-doc supports English and Japanese out of the box. Japanese docs mirror the English directory structure in `src/content/docs-ja/`. See the [i18n guide](/docs/guides/i18n) for detailed instructions on managing translations and language routing. ## MDX Features You can use React components in your documentation. Interactive components hydrate as Astro islands — the rest ships as pure HTML with zero JavaScript. ```mdx ``` Use `client:load` for components that need interactivity. Omit it for components that only render static HTML. ## Navigation zudo-doc automatically generates: - **Sidebar** — collapsible categories with items sorted by `sidebar_position` - **Table of Contents** — right sidebar with h2–h4 headings (visible on wide screens) - **Prev/Next links** — bottom navigation between docs - **Breadcrumbs** — category path shown above the title --- # Demo: Hide Sidebar & Table of Contents > Source: /pj/zudo-doc/docs/guides/layout-demos/hide-both This page demonstrates combining `hide_sidebar: true` and `hide_toc: true`. Both navigation panels are hidden, creating a clean, centered reading layout. ## What's Happening Both the sidebar and table of contents are hidden. Here is the frontmatter used: ```yaml --- title: "Demo: Hide Sidebar & Table of Contents" hide_sidebar: true hide_toc: true --- ``` ## Clean Reading Experience With both panels removed, the content is centered on the page in a narrower container. This layout is ideal for: - Landing pages - Standalone articles - Focused reading content - About pages ## Mobile Navigation Even with both panels hidden, the mobile hamburger menu still provides full navigation access. Readers are never stranded without a way to navigate. ### Subsection Example This subsection shows that headings still render normally — they simply don't appear in a table of contents. ## When to Use Combine both options when you want maximum focus on the content itself, with no surrounding navigation panels. See the [Frontmatter reference](/docs/guides/frontmatter/#hide_sidebar) for full documentation. ## See Also - [Hide Sidebar](/docs/guides/layout-demos/hide-sidebar/) — hides just the left sidebar - [Hide Table of Contents](/docs/guides/layout-demos/hide-toc/) — hides just the right-side TOC --- # Sidebar > Source: /pj/zudo-doc/docs/guides/sidebar ## Item Ordering Pages within a category are sorted by the `sidebar_position` frontmatter field. Lower values appear first. Pages without `sidebar_position` default to `999` and appear at the end of the category, sorted alphabetically by filename. ```mdx --- title: My Page sidebar_position: 3 --- ``` Always set `sidebar_position` explicitly to control page order. Without it, pages are sorted alphabetically, which may not match your intended reading order. ## Category Ordering Categories in the sidebar are ordered by the `sidebar_position` of their **index page** (`index.mdx`). If a category has no index page or no `sidebar_position`, it falls back to alphabetical ordering. For example, to make "Getting Started" appear first and "Guides" second: ``` getting-started/index.mdx → sidebar_position: 0 guides/index.mdx → sidebar_position: 1 reference/index.mdx → sidebar_position: 2 ``` ## Category Index Pages Creating an `index.mdx` inside a directory gives the category a landing page. The category name in the sidebar becomes a clickable link to this page. A typical category index page uses the `` component to automatically list all pages in the category: ```mdx --- title: Guides sidebar_position: 1 --- Explore our guides to learn about zudo-doc features. ``` The `` component renders a grid of links to all pages in the category (excluding the index itself), sorted by `sidebar_position`. Without an `index.mdx`, the category heading is a toggle-only control — clicking it expands or collapses the category, but does not navigate anywhere. ## Default Open/Closed Categories automatically open when the current page is inside them. All other categories are collapsed by default. ## Sidebar Label Use the `sidebar_label` frontmatter field to display a different label in the sidebar than the page title. This is useful when the full title is too long for the sidebar. ```mdx --- title: Getting Started with zudo-doc Installation sidebar_label: Installation sidebar_position: 2 --- ``` The `sidebar_label` only affects the sidebar. The page title and heading still use `title`. ## Directory Structure Directories inside `src/content/docs/` become sidebar categories. The directory name is converted from kebab-case to Title Case automatically. ``` src/content/docs/ getting-started/ → "Getting Started" index.mdx → category index (sidebar_position: 0) introduction.mdx → sidebar_position: 1 installation.mdx → sidebar_position: 2 writing-docs.mdx → sidebar_position: 3 guides/ → "Guides" index.mdx → category index (sidebar_position: 1) configuration.mdx → sidebar_position: 1 frontmatter.mdx → sidebar_position: 2 ``` The sidebar only supports one level of nesting. Subdirectories within a category directory are not supported. ## Sort Order (Ascending / Descending) By default, items within a category are sorted in ascending order (lowest `sidebar_position` first, then alphabetical). You can reverse this to descending order per category using `_category_.json`: ```json title="_category_.json" { "label": "Changelog", "position": 10, "sortOrder": "desc" } ``` When `sortOrder` is `"desc"`, items are sorted in reverse — highest position first, then reverse alphabetical. This is useful for date-based content (changelogs, release notes) where the newest items should appear at the top. You can also set `sortOrder` in `src/config/sidebars.ts` for manual sidebar configurations: ```ts { type: "category", label: "Releases", sortOrder: "desc", items: [ { type: "autogenerated" }, ], } ``` For changelog-style categories, combine `sortOrder: "desc"` with date-prefixed filenames (e.g., `2026-03-10-release.mdx`) for automatic newest-first ordering without manually setting `sidebar_position`. ## Ordering Recommendations - Set `sidebar_position` on every page for predictable ordering - Use `sidebar_position: 0` on category index pages - Use sequential numbers (1, 2, 3...) for pages within each category - Leave gaps between numbers (e.g., 10, 20, 30) if you expect to insert pages later - Use `sortOrder: "desc"` in `_category_.json` for newest-first categories --- # Color > Source: /pj/zudo-doc/docs/reference/color zudo-doc uses a **three-tier color strategy** to keep every color on the site themeable. The default Tailwind theme is not imported, and the `@theme` block resets the color namespace with `--color-*: initial` before defining project tokens — only project tokens work. This ensures switching a color scheme updates the entire site at once. ## Three-Tier Color Strategy Colors are organized into three tiers. Each tier only references the tier above it: | Tier | Name | Purpose | Defined In | |------|------|---------|------------| | 1 | **Palette** | Raw color values from the active color scheme | `ColorSchemeProvider` → `:root` | | 2 | **Semantic** | Design meaning — what each color represents | `src/styles/global.css` `@theme` | | 3 | **Component** | Scoped overrides for specific components | `.zd-content` in `global.css` | This layering means you can: - **Swap the palette** (change color scheme) → entire site updates - **Remap a semantic token** (e.g. make `accent` blue instead of cyan) → every component using `accent` updates - **Override a component token** without affecting other components ## Tier 1: The 16-Color Palette zudo-doc's color system uses a **16-color palette**. Each color scheme provides values for 16 indexed color slots plus background, foreground, and selection colors. ### Palette Index Convention Every color scheme must follow this standard index mapping. This ensures components and semantic tokens work consistently across all themes: | Index | Role | Description | |-------|------|-------------| | p0 | Dark surface | Deepest surface (code blocks, overlays) | | p1 | Danger | Red family — errors, destructive actions | | p2 | Success | Green family — confirmations, tips | | p3 | Warning | Yellow/amber — caution messages | | p4 | Info | Blue family — informational highlights | | p5 | Accent | Primary interactive color (links, CTA) | | p6 | Neutral | Slate/cyan — borders, secondary elements | | p7 | Secondary | Muted accent or secondary neutral | | p8 | Muted | Gray — borders, secondary text, comments | | p9 | Background | Page background | | p10 | Surface | Elevated surface (panels, sidebars) | | p11 | Text primary | Main body text | | p12 | Accent variant | Brighter or alternate accent | | p13 | Decorative | Purple/lavender — non-semantic decoration | | p14 | Accent hover | Hover state for interactive elements | | p15 | Text secondary | Secondary text or muted foreground | The actual hex values differ between light and dark schemes, but the **role** of each index stays the same. For example, p1 is always a danger/red color — `#dd3131` in light mode, `#da6871` in dark mode. This consistency is what makes it safe to use palette tokens directly and easy to create new schemes. ### How Palette Colors Are Injected The `ColorSchemeProvider` component (`src/components/color-scheme-provider.astro`) reads the active color scheme and injects CSS custom properties on `:root` at build time: ```css :root { --zd-bg: #282a36; --zd-fg: #f8f8f2; --zd-cursor: #f8f8f2; --zd-sel-bg: #44475a; --zd-sel-fg: #ffffff; --zd-0: #21222c; /* palette slot 0 (Black) */ --zd-1: #ff5555; /* palette slot 1 (Red) */ /* ... through --zd-15 */ } ``` These `--zd-*` properties are the source of truth. Everything downstream — semantic tokens, component tokens, Tailwind utilities — resolves back to them. ## Tier 2: Semantic Tokens In `src/styles/global.css`, the `@theme` block maps palette properties into Tailwind-compatible tokens with design meaning: ```css @theme { --color-*: initial; /* reset ALL Tailwind defaults */ /* Base */ --color-bg: var(--zd-bg); --color-fg: var(--zd-fg); --color-sel-bg: var(--zd-sel-bg); --color-sel-fg: var(--zd-sel-fg); /* Raw palette access (p0–p15) */ --color-p0: var(--zd-0); --color-p1: var(--zd-1); /* ... through --color-p15 */ /* Semantic aliases */ --color-surface: var(--zd-surface); --color-muted: var(--zd-muted); --color-accent: var(--zd-accent); --color-accent-hover: var(--zd-accent-hover); --color-code-bg: var(--zd-code-bg); --color-code-fg: var(--zd-code-fg); --color-success: var(--zd-success); --color-danger: var(--zd-danger); --color-warning: var(--zd-warning); --color-info: var(--zd-info); } ``` Once registered in `@theme`, these become standard Tailwind utility classes: `bg-surface`, `text-accent`, `border-muted`, etc. ### Semantic Token Reference | Token | Default Palette Slot | Usage | |-------|---------------------|-------| | `bg` | `--zd-bg` (p9) | Page background | | `fg` | `--zd-fg` (p11) | Primary text | | `surface` | `p0` (Dark surface) | Panel/sidebar surfaces | | `muted` | `p8` (Muted) | Muted text, borders, comments | | `accent` | `p5` (Accent) | Links, active states, CTA | | `accent-hover` | `p14` (Accent hover) | Hover state for accent | | `sel-bg` | `--zd-sel-bg` | Selection background | | `sel-fg` | `--zd-sel-fg` | Selection foreground | | `code-bg` | `p10` (Surface) | Code block background | | `code-fg` | `p11` (Text primary) | Inline code text | | `success` | `p2` (Success) | Success states, confirmations | | `danger` | `p1` (Danger) | Errors, destructive actions | | `warning` | `p3` (Warning) | Warning messages | | `info` | `p4` (Info) | Informational highlights | ### Per-Scheme Semantic Overrides Each color scheme can override the default palette-slot mapping via the `semantic` property in `src/config/color-schemes.ts`. Values can be either a **palette index** (number) or a **direct color string**: ```ts "My Custom Scheme": { // ...palette, background, foreground... semantic: { accent: 6, // use palette[6] — no color duplication accentHover: 14, // use palette[14] surface: "#f4efdd", // direct color string (not in palette) }, }, ``` Using palette indices instead of repeating color strings eliminates duplication and ensures semantic colors stay in sync when palette colors change (e.g., via the [Color Tweak Panel](/docs/guides/color-tweak-panel)). The same `number | string` type (called `ColorRef`) is also supported for `cursor`, `selectionBg`, and `selectionFg`: ```ts "My Theme": { background: "#1a1a2e", foreground: "#e0e0e0", cursor: 6, // use palette[6] instead of duplicating the color selectionBg: "#3a3a5e", selectionFg: 15, // use palette[15] palette: [...], shikiTheme: "one-dark-pro", }, ``` The resolution logic lives in `src/config/color-scheme-utils.ts` — each property falls back to its default palette slot when not explicitly set. ## Tier 3: Component Tokens Some components define their own color variables that consume Tier 2 semantic tokens. These are internal implementation details. ### Content Typography The `.zd-content` class provides direct element styling using semantic tokens — no external typography plugin: ```css .zd-content { color: var(--color-fg); font-size: var(--text-body); line-height: var(--leading-relaxed); } .zd-content :where(a) { color: var(--color-accent); } .zd-content :where(code:not(pre code)) { color: var(--color-code-fg); background-color: var(--color-code-bg); } .zd-content :where(li::marker) { color: var(--color-muted); } /* ... */ ``` Tier 3 tokens are internal to their components. When building your own UI, use Tier 2 semantic tokens or Tier 1 palette tokens directly. ## Using Color Tokens ### Prefer semantic tokens Use semantic tokens for standard UI patterns: ```html Primary text Secondary text Link Page background Panel or sidebar Bordered element ``` ### Fall back to palette tokens when needed Use `p0`–`p15` when no semantic token fits — for badges, decorative elements, or status indicators: ```html Error text (red) Success text (green) Warning text (yellow) Info text (blue) ``` ## Color Schemes To change the active color scheme, edit `src/config/settings.ts`: ```ts colorScheme: "Default Dark", // Change this siteName: "zudo-doc", // ... }; ``` ### Default Themes zudo-doc includes a light and dark default theme. See `src/config/color-schemes.ts` for available schemes. ## Adding a Custom Color Scheme Add a new entry to the `colorSchemes` object in `src/config/color-schemes.ts`: ```ts "My Theme": { background: 9, foreground: 11, cursor: 6, selectionBg: 10, selectionFg: 11, palette: [ "#16213e", "#e74c3c", "#2ecc71", "#f39c12", // p0-3: dark surface, danger, success, warning "#3498db", "#9b59b6", "#1abc9c", "#ecf0f1", // p4-7: info, accent, neutral, secondary "#2c3e50", "#1a1a2e", "#2a2a4e", "#e0e0e0", // p8-11: muted, background, surface, text "#5dade2", "#bb8fce", "#48c9b0", "#c0c0c0", // p12-15: accent variant, decorative, hover, text secondary ], shikiTheme: "one-dark-pro", // Optional: override semantic defaults semantic: { accent: 12, // use p12 as accent instead of default p5 surface: "#1f1f3a", // direct color string also works }, }, ``` The `ColorScheme` interface requires: - `background`, `foreground` — `ColorRef` (palette index or color string, typically p9 and p11) - `cursor`, `selectionBg`, `selectionFg` — `ColorRef` (palette index or color string) - `palette` — 16-element tuple (color slots 0–15) - `shikiTheme` — a Shiki theme name for syntax highlighting - `semantic` (optional) — override default palette-slot mappings using `ColorRef` values (palette index or color string) ## What NOT to Do **Don't use Tailwind defaults** — they are reset to `initial`: ```html No visible color Works correctly ``` **Don't hardcode hex values** — it breaks theming: ```html Breaks on theme switch Adapts to any theme ``` **Don't reference component-scoped variables** in your own components: ```css /* WRONG — don't use hardcoded colors */ .my-component { color: #3b82f6; } /* RIGHT — use semantic tokens */ .my-component { color: var(--color-accent); } ``` --- # Reference > Source: /pj/zudo-doc/docs/reference Technical reference for zudo-doc's components, design system, and color architecture. --- # /packages/ai-chat-worker/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/packages--ai-chat-worker **Path:** `packages/ai-chat-worker/CLAUDE.md` # ai-chat-worker Standalone Cloudflare Worker sub-package for the AI chat API. Independent of the Astro docs site. ## Tech Stack - **Cloudflare Workers** — runtime - **Cloudflare KV** — rate limiting storage - **Anthropic Messages API** — Claude Haiku via raw `fetch` (no SDK) - **TypeScript** — strict mode, `@cloudflare/workers-types` ## Commands - `pnpm dev` — local dev server (requires `.dev.vars` with `ANTHROPIC_API_KEY`) - `pnpm run deploy` — deploy to Cloudflare Workers via Wrangler - `pnpm typecheck` — TypeScript type checking ## Architecture ``` src/ ├── index.ts # Worker entry — routing, validation, CORS ├── audit-log.ts # Audit logging + IP hashing via Web Crypto ├── claude.ts # Claude API call + docs context fetching/caching ├── cors.ts # CORS headers (exposes Retry-After) ├── input-screen.ts # Prompt injection input screening ├── rate-limit.ts # Per-IP rate limiting via KV └── types.ts # Env, request/response, Claude API types ``` ### Request Flow 1. CORS preflight → `cors.ts` 2. Method + path check → 405/404 3. Hash client IP (SHA-256 via Web Crypto) → `audit-log.ts` 4. JSON parse + message validation → 400 (audit logged as `invalid_input`) 5. Prompt injection screening → 400 (rejects obvious injection attempts before they consume rate limit quota; audit logged) 6. Rate limit check → 429 (audit logged as `rate_limit`) 7. Fetch `llms-full.txt` from docs site (cached in-memory, best-effort) → `claude.ts` 8. Call Claude API with hardened system prompt (XML-tagged context + explicit guardrails) → response (audit logged) ### Key Design Decisions - **No SDK** — raw `fetch` to Anthropic API keeps bundle small for Workers - **Fail-open rate limiting** — KV errors allow requests through (chat availability > strict enforcement) - **Best-effort caching** — module-level cache for `llms-full.txt` is ephemeral (Workers isolates are recycled) - **Rate limit after validation** — invalid requests don't consume the caller's quota - **Fire-and-forget audit logging** — every interaction logged to KV (`audit:` prefix) with 7-day TTL; IPs stored as SHA-256 hashes for privacy - **Prompt hardening** — system prompt uses XML tags for context separation and explicit guardrails against off-topic / injection attempts - **Input screening** — regex pre-filter catches common prompt injection patterns before reaching the Claude API ## Configuration See `README.md` for full setup instructions (vars, secrets, KV namespace). ## Conventions - All responses include CORS headers (including error responses) - Error responses use `{ error: string }` format - Rate limit uses `cf-connecting-ip` for client IP - History capped at 50 messages to limit API cost - KV keys use bucket pattern: `rate:min:{ip}:{bucket}` / `rate:day:{ip}:{bucket}` with TTL = 2x window - Audit log keys: `audit:{date}:{timestamp-ms}:{random}` with 7-day TTL --- # Details > Source: /pj/zudo-doc/docs/components/details Use `` for collapsible content sections. This component is globally available — no imports needed. ## Basic Usage This content is hidden by default and revealed when the user clicks the summary. ```mdx This content is hidden by default and revealed when the user clicks the summary. ``` ## Default Title When no `title` prop is provided, the summary text defaults to "Details". This collapsible section uses the default title. ```mdx This collapsible section uses the default title. ``` ## Rich Content You can include any MDX content inside a `` block, including code blocks, lists, and other components. Here is a sample configuration: ```ts export default { site: "https://example.com", output: "static", }; ``` Key points: - The `site` field sets the base URL - The `output` field controls the build mode - All fields are optional with sensible defaults ````mdx Here is a sample configuration: ```ts site: "https://example.com", output: "static", }; ``` Key points: - The `site` field sets the base URL - The `output` field controls the build mode - All fields are optional with sensible defaults ```` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `title` | string | `"Details"` | The clickable summary text | --- # Contribution > Source: /pj/zudo-doc/docs/getting-started/contribution ## Background zudo-doc was created as a documentation framework by [Takazudo](https://x.com/Takazudo). It was originally built for personal use — a lightweight alternative to Docusaurus, tailored to the specific workflow and design preferences that kept coming up across multiple projects. Over time, it grew into a standalone framework with features like the 16-color terminal palette system, MDX content collections, i18n support, and Claude Code integration. ## Development Most of the source code in this project was written with the help of [Claude Code](https://claude.com/claude-code). The framework is openly available under the MIT License. ## Maintenance This project is maintained on a best-effort basis. Active maintenance and thorough review of contributions may be limited. Rather than waiting for official support, you are encouraged to **fork the repository** and implement your own modifications as needed. In the AI era, forking and customizing is often faster than waiting for upstream changes. ## How to Contribute If you'd like to contribute: 1. Fork the repository on GitHub 2. Create a topic branch from `main` 3. Make your changes and ensure `pnpm b4push` passes (type check, build, E2E tests, format check) 4. Open a pull request with a clear description of what you changed and why Bug reports and feature requests are welcome as [GitHub Issues](https://github.com/zudolab/zudo-doc/issues). ## License zudo-doc is released under the [MIT License](https://github.com/zudolab/zudo-doc/blob/main/LICENSE). ## Author - **Takeshi Takatsudo** ([@Takazudo](https://x.com/Takazudo)) --- # Header Navigation > Source: /pj/zudo-doc/docs/guides/header-navigation zudo-doc supports a three-level navigation hierarchy inspired by Docusaurus: - **Header nav** — top-level tabs in the site header (biggest categories) - **Sidebar categories** — collapsible groups in the sidebar (2nd level) - **Sidebar items** — individual pages within categories (3rd level) ## Configuration Define header navigation items in `src/config/settings.ts`: ```ts // ... headerNav: [ { label: "Getting Started", labelKey: "nav.gettingStarted", path: "/docs/getting-started", categoryMatch: "getting-started" }, { label: "Guides", labelKey: "nav.guides", path: "/docs/guides", categoryMatch: "guides" }, { label: "Reference", labelKey: "nav.reference", path: "/docs/reference", categoryMatch: "reference" }, ], }; ``` Each item supports the following properties: | Property | Type | Description | |----------|------|-------------| | `label` | string | Display text shown in the header | | `labelKey` | string (optional) | i18n translation key that overrides `label` when a translation is available | | `path` | string | URL path the link navigates to | | `categoryMatch` | string (optional) | Links this header tab to a sidebar category | ### `labelKey` The `labelKey` property enables localized header navigation labels. When set, zudo-doc looks up the key in the current locale's translation file and uses the translated string instead of `label`. If no translation is found, `label` is used as a fallback. Translation keys follow the `nav.*` namespace convention (e.g., `"nav.gettingStarted"`, `"nav.guides"`). ### `categoryMatch` The `categoryMatch` property connects a header tab to a specific sidebar category. When a header tab with `categoryMatch` is active, the sidebar filters to show only the pages within that category. For example, `categoryMatch: "guides"` links the tab to the `guides/` content directory. When a user navigates to any page under `/docs/guides/`, the "Guides" tab becomes active and the sidebar shows only the guides category. ## Active State A header nav item is considered active when the current page URL starts with the item's `path`. For example, visiting `/docs/guides/configuration` activates the "Guides" tab because the URL starts with `/docs/guides`. ## Mobile Behavior On small screens (`< 640px`), header nav items are hidden to save space. The sidebar toggle provides access to all navigation on mobile. --- # Sidebar Filter > Source: /pj/zudo-doc/docs/guides/sidebar-filter ## Overview The sidebar filter is a text input at the top of the sidebar that filters navigation items in real time as you type. It helps users quickly find pages in large documentation sites without scrolling through the full sidebar tree. The sidebar filter is enabled by default in projects created with `create-zudo-doc`. ## How It Works The filter input appears at the top of the sidebar, above the navigation tree. As you type: - Both category names and individual page titles are matched against your query - Matching is case-insensitive - When a category name matches, all of its child pages are shown - When only child pages match, the parent category is shown with just the matching children - All matching categories are automatically expanded during filtering - Clearing the input restores the full sidebar tree ## Keyboard Shortcut Press `Ctrl+/` (Windows/Linux) or `Cmd+/` (macOS) to focus the filter input from anywhere on the page. This works when the sidebar is visible (desktop viewport or mobile sidebar open). ## Enabling or Disabling ### With create-zudo-doc The sidebar filter is on by default. To disable it during project creation: ```bash pnpm create zudo-doc my-docs --no-sidebar-filter ``` To explicitly enable it: ```bash pnpm create zudo-doc my-docs --sidebar-filter ``` ### In an Existing Project The sidebar filter is built into the `SidebarTree` React component (`src/components/sidebar-tree.tsx`). It is always rendered as part of the sidebar — there is no separate setting in `settings.ts` to toggle it. To remove it from a scaffolded project, you would need to modify the component directly. For most documentation sites, the sidebar filter is useful even with a small number of pages. It becomes essential as your content grows beyond a few dozen pages. --- # /packages/create-zudo-doc/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/packages--create-zudo-doc **Path:** `packages/create-zudo-doc/CLAUDE.md` # create-zudo-doc CLI scaffold tool for creating new zudo-doc documentation sites. Generates a project with configurable features, color schemes, and i18n support. ## Key Files | File | Role | |------|------| | `src/scaffold.ts` | Copies template from main project, generates `package.json` with appropriate dependencies | | `src/strip.ts` | Removes files, imports, and code for disabled features | | `src/settings-gen.ts` | Generates `src/config/settings.ts` with user-chosen options | | `src/constants.ts` | Feature definitions, color scheme lists, light-dark pairings | | `src/cli.ts` | CLI argument parsing (commander) | | `src/api.ts` | Programmatic API (`createZudoDoc()`) | | `src/prompts.ts` | Interactive prompts (inquirer) | | `src/index.ts` | Entry point | ## Testing ### Unit tests ```bash pnpm test ``` Runs vitest tests in `src/__tests__/`. ### Generator CLI integration tests Two Claude Code skills test the full scaffold-build-run cycle: - `/l-generator-cli-tester ` — Test a single generation pattern - `/l-run-generator-cli-whole-test` — Run all 9 patterns, fix bugs, verify everything #### Test patterns | Pattern | Description | |---------|-------------| | `barebone` | Everything OFF — minimal project | | `search` | Only search enabled | | `i18n` | Only i18n enabled | | `sidebar-filter` | Only sidebar filter enabled | | `claude-resources` | Only Claude Resources enabled | | `color-tweak-panel` | Only color tweak panel enabled (API only, no CLI flag) | | `light-dark` | Light-dark color scheme mode | | `lang-ja` | Japanese as default language | | `all-features` | Everything ON | Always rebuild the CLI before testing: ```bash pnpm build ``` ## Adding a New Feature When adding a feature to the main zudo-doc project that the generator should support: 1. **`src/scaffold.ts`** — Add/remove dependencies in `generatePackageJson()` 2. **`src/strip.ts`** — Add stripping logic for files and imports when the feature is disabled 3. **`src/settings-gen.ts`** — Add the setting field to generated `settings.ts` 4. **`src/constants.ts`** — Add feature to definitions if needed 5. **`src/__tests__/scaffold.test.ts`** — Update tests After changes, run `/l-sync-create-zudo-doc` to verify no drift remains between the main project and the generator. --- # Math Equations > Source: /pj/zudo-doc/docs/components/math-equations When `math` is enabled in settings (default: `true`), you can render mathematical equations using KaTeX. ## Inline Math Use single dollar signs to embed math within a line of text. The formula $E = mc^2$ is inline. ```mdx The formula $E = mc^2$ is inline. ``` ## Block Math Use double dollar signs to render a centered display equation. $$ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} $$ ```mdx $$ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} $$ ``` ## Fenced Code Block Use a fenced code block with the `math` language identifier. ```math \nabla \times \mathbf{E} = -\frac{\partial \mathbf{B}}{\partial t} ``` ````mdx ```math \nabla \times \mathbf{E} = -\frac{\partial \mathbf{B}}{\partial t} ``` ```` ## Configuration Math support is controlled by the `math` setting in `src/config/settings.ts`: ```ts // ... math: true, // enabled by default }; ``` ## More Examples ### Quadratic Formula $$ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$ ```mdx $$ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$ ``` ### Summation $$ \sum_{i=1}^{n} i = \frac{n(n+1)}{2} $$ ```mdx $$ \sum_{i=1}^{n} i = \frac{n(n+1)}{2} $$ ``` ### Matrix $$ \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax + by \\ cx + dy \end{bmatrix} $$ ```mdx $$ \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax + by \\ cx + dy \end{bmatrix} $$ ``` --- # Site Search > Source: /pj/zudo-doc/docs/guides/search ## How Search Works zudo-doc uses [MiniSearch](https://lucaong.github.io/minisearch/) for full-text site search. A search index is generated from your content files (title, description, and body text) and served as a single JSON file. The browser loads this index and performs all searching client-side — no server or external service required. ## Using Search Open the search dialog with: - **Keyboard shortcut**: `Ctrl+K` (Windows/Linux) or `Cmd+K` (macOS) - **Search button**: Click the search icon in the header Type your query and results appear instantly. Click a result to navigate to that page. ## Works Everywhere Search works in all environments: - **Dev mode** (`pnpm dev`): The search index is generated on-the-fly via Vite middleware - **Production build** (`pnpm build`): The index is written to `search-index.json` alongside the static site - **Electron**: Loads the same index file — no special handling needed No need to build before testing search. It works immediately with `pnpm dev`. ## Excluding Pages from Search Pages with `search_exclude: true`, `draft: true`, or `unlisted: true` in their frontmatter are automatically excluded from the search index. To explicitly exclude a page, add `search_exclude: true` to its frontmatter: ```mdx --- title: My Internal Page search_exclude: true --- ``` This is useful for pages like changelogs, imported content (e.g., CLAUDE.md), or internal-only pages that would clutter search results. ## Search Features MiniSearch provides: - **Fuzzy matching**: Tolerates typos (up to 20% character difference) - **Prefix search**: Finds results as you type (e.g., "conf" matches "Configuration") - **Field boosting**: Title matches rank 3x higher than body text, descriptions 2x - **Instant results**: The full index is loaded once and queried in-memory --- # AI Assistant API > Source: /pj/zudo-doc/docs/reference/ai-assistant-api API specification for the AI assistant chat endpoint. ## Endpoint ``` POST /api/ai-chat Content-Type: application/json ``` ### Request Body ```ts interface AiChatRequest { message: string; history: ChatMessage[]; } interface ChatMessage { role: "user" | "assistant"; content: string; } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `message` | `string` | Yes | The user's current message. Must be non-empty. | | `history` | `ChatMessage[]` | Yes | Previous conversation messages. Invalid entries are filtered out silently. | ### Success Response (200) ```ts interface AiChatResponse { response: string; } ``` The `response` field contains the assistant's reply as a markdown string. **Example:** ```json // Request { "message": "How do I add a new page?", "history": [] } // Response { "response": "Create an MDX file in `src/content/docs/`:\n\n1. Add frontmatter with `title`\n2. Write your content in MDX\n3. The page appears in the sidebar automatically" } ``` ### Error Response (400 / 500) ```ts interface AiChatErrorResponse { error: string; } ``` | Status | Condition | |--------|-----------| | 400 | Invalid JSON body | | 400 | `message` is not a non-empty string | | 400 | `history` is not an array | | 500 | `AI_CHAT_MODE` is not set to `"local"` or `"remote"` | | 500 | Claude CLI or Anthropic API call failed | ## Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `AI_CHAT_MODE` | Yes | `"local"` or `"remote"` | | `ANTHROPIC_API_KEY` | Remote mode only | Anthropic API key | | `PUBLIC_ENABLE_MOCKS` | No | Set to `"true"` to enable MSW mock responses (dev mode only, ignored in production) | ## Backend Modes ### Local Mode (`AI_CHAT_MODE=local`) Spawns `claude -p --model haiku` as a subprocess. Documentation context is piped via stdin. 60-second timeout. ### Remote Mode (`AI_CHAT_MODE=remote`) Calls the Anthropic Messages API with `claude-haiku-4-5-20251001`. Documentation content is included in the system prompt. ## Documentation Context The endpoint loads `llms-full.txt` (generated by the [llms.txt integration](/docs/guides/llms-txt)) as context. The file is loaded once and cached in memory. --- # /packages/doc-history-server/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/packages--doc-history-server **Path:** `packages/doc-history-server/CLAUDE.md` # doc-history-server Standalone package for document git history with dual modes: REST API server for local dev, CLI batch generator for CI builds. Extracted from `src/utils/doc-history.ts` to decouple expensive git operations from the Astro build pipeline. ## Tech Stack - **Node.js** — HTTP server + CLI (no framework dependency) - **Git** — `execFileSync` calls for log, show, follow - **TypeScript** — strict mode, ESM ## Commands - `pnpm dev -- --port 4322 --content-dir --locale :` — start REST API server - `pnpm generate -- --content-dir --locale : --out-dir ` — batch generate JSONs - `pnpm typecheck` — TypeScript type checking - `pnpm build` — build via tsup (ESM + DTS) ## Architecture ``` src/ ├── index.ts # Server entry — parses args, starts HTTP server ├── cli.ts # CLI entry — parses args, batch generates JSONs ├── args.ts # Shared argument parsing with bounds checking ├── server.ts # HTTP server (GET /doc-history/{slug}.json, /health) ├── git-history.ts # Core git logic (log, show, follow, rename tracking) ├── shared.ts # Shared helpers (getContentDirEntries) └── types.ts # DocHistoryEntry, DocHistoryData types ``` ### Server Mode (Local Dev) - Runs on configurable port (default 4322) - `GET /doc-history/{slug}.json` — returns full history for a document - `GET /doc-history/{locale}/{slug}.json` — locale-prefixed history - `GET /health` — health check - File index refreshes every 10 seconds (picks up new/renamed files) - CORS headers for cross-origin dev access ### CLI Mode (CI Build) - Generates `{slug}.json` files in the output directory - Reports progress and timing - Used by CI `build-history` job (parallel with Astro build) ### Astro Integration In dev mode, `src/integrations/doc-history.ts` proxies `/doc-history/*` requests to this server. In build mode, the Astro integration falls back to inline generation when `SKIP_DOC_HISTORY` is not set. Root `pnpm dev` runs both Astro and this server via `run-p`. ## Key Design Decisions - **Synchronous git** — `execFileSync` is acceptable for dev server (same as original integration). CI uses the CLI which is inherently sequential - **Repo-relative paths** — API responses use relative file paths to avoid leaking absolute server paths - **`--follow` for renames** — tracks file history across renames with multiple fallback strategies - **pnpm --filter paths** — when run via `pnpm --filter`, CWD is the package dir, so content paths need `../../` prefix for repo-relative resolution in CI ## CLI Arguments | Flag | Required | Default | Description | |------|----------|---------|-------------| | `--content-dir` | Yes | — | Content directory to scan | | `--locale :` | No | — | Additional locale (repeatable) | | `--out-dir` | CLI only | — | Output directory for JSONs | | `--port` | Server only | 4322 | Server port | | `--max-entries` | No | 50 | Max commits per file | --- # Mermaid Diagrams > Source: /pj/zudo-doc/docs/components/mermaid-diagrams Use fenced code blocks with the `mermaid` language to render diagrams. Mermaid is loaded on-demand — pages without mermaid blocks incur no overhead. ## Flowchart ```mermaid graph LR A[Start] --> B{Decision} B -->|Yes| C[Action] B -->|No| D[Other Action] C --> E[End] D --> E ``` ````mdx ```mermaid graph LR A[Start] --> B{Decision} B -->|Yes| C[Action] B -->|No| D[Other Action] C --> E[End] D --> E ``` ```` ## Sequence Diagram ```mermaid sequenceDiagram participant User participant App participant API User->>App: Click button App->>API: Fetch data API-->>App: JSON response App-->>User: Render result ``` ````mdx ```mermaid sequenceDiagram participant User participant App participant API User->>App: Click button App->>API: Fetch data API-->>App: JSON response App-->>User: Render result ``` ```` ## State Diagram ```mermaid stateDiagram-v2 [*] --> Draft Draft --> Review : Submit Review --> Published : Approve Review --> Draft : Request Changes Published --> Archived : Archive Archived --> [*] ``` ````mdx ```mermaid stateDiagram-v2 [*] --> Draft Draft --> Review : Submit Review --> Published : Approve Review --> Draft : Request Changes Published --> Archived : Archive Archived --> [*] ``` ```` ## Configuration Mermaid support is controlled by the `mermaid` setting in `src/config/settings.ts`: ```ts // ... mermaid: true, // enabled by default }; ``` See the [Mermaid documentation](https://mermaid.js.org/) for all supported diagram types. --- # Document History > Source: /pj/zudo-doc/docs/guides/doc-history ## Overview zudo-doc can show the git revision history for each documentation page. When enabled, a **History** button appears on every doc page, opening a side panel where you can browse past revisions and compare any two versions with a line-by-line diff viewer. This is useful for: - Reviewing how a document has evolved over time - Understanding what changed between revisions - Auditing content changes across your documentation This feature requires that your documentation is tracked in a git repository. History is extracted from git commits that touched each file. ## Enabling Document History Set `docHistory` to `true` in `src/config/settings.ts`: ```ts // ... docHistory: true, }; ``` The feature is **disabled by default** to keep builds fast for projects that don't need it. ## Using the History Viewer Once enabled, each doc page shows a floating **History** button in the bottom-right corner. ### Browsing Revisions Click the button to open the revision panel. It slides in from the right and shows all git commits that modified the current document, newest first. Each entry displays: - **Commit hash** (short form) - **Date** of the commit - **Author** name - **Commit message** (first line) ### Comparing Revisions To view a diff between two revisions: 1. Select **A** (the older revision) by clicking the **A** button next to a commit 2. Select **B** (the newer revision) by clicking the **B** button next to another commit 3. Click the **Compare** button The diff viewer shows a **side-by-side comparison** — the older revision on the left, the newer revision on the right: - **Green background** — lines added in the newer revision (right side) - **Red background** — lines removed from the older revision (left side) - **Gray cells** — empty placeholders where one side has no corresponding line - **Unchanged lines** — context shown on both sides By default, A is set to the second-most-recent commit and B to the most recent, so you can immediately compare the latest change by clicking **Compare**. ### Keyboard Shortcuts - **Escape** — closes the history panel - The panel also closes automatically when navigating to another page ## How It Works ### Build Mode During `pnpm build`, an Astro integration extracts git history for every content file and writes JSON files to `dist/doc-history/`. Each JSON file contains the full revision list for a single document, including file content at each commit. The output structure mirrors the content directory: ``` dist/doc-history/ getting-started.json guides/writing-docs.json guides/color.json ja/getting-started.json # locale-prefixed ja/guides/writing-docs.json ``` ### Dev Mode In dev mode (`pnpm dev`), a Vite middleware handles history requests dynamically. When you open the history panel, it runs git commands on the fly — no pre-generation needed. This means history is always up-to-date with your latest commits. Build time increases when `docHistory` is enabled because git history must be extracted for every content file. For large documentation sites with many files and deep history, this can add noticeable time to the build. The default limit is **50 revisions** per document. ### Production Output Size Since zudo-doc generates fully static sites with no server-side runtime, there is no server available to run `git` commands at request time. Instead, the build step pre-extracts all history data and ships it as static JSON files in the `dist/doc-history/` directory. Each JSON file contains the **full file content at every revision** (up to 50 commits). This means the total output size scales with: - Number of documentation files - Number of git commits per file - Size of each file at each revision For a small-to-medium documentation site this is typically negligible. For large sites with deep history, the `doc-history/` directory can grow significantly. These files are only fetched on demand (when a user clicks the History button), so they do not affect initial page load — but they do add to your deployment size. If deployment size is a concern, consider keeping `docHistory` disabled for production builds (`docHistory: false`) and only using it during local development, where history is served dynamically via a Vite middleware with no pre-generation cost. ## Localization Support Document history works with all configured locales. Each locale's content directory produces its own set of history files, prefixed with the locale code: - English docs: `/doc-history/{slug}.json` - Japanese docs: `/doc-history/ja/{slug}.json` - German docs: `/doc-history/de/{slug}.json` The history panel automatically requests the correct locale-specific history based on the current page. ## Technical Details ### Data Format Each document's history is stored as a JSON file with the following structure: ```ts interface DocHistoryData { slug: string; // Document route path filePath: string; // File path in the repository entries: Array<{ hash: string; // Full commit hash date: string; // ISO 8601 date author: string; // Commit author name message: string; // Commit message (first line) content: string; // Full file content at this revision }>; } ``` ### Diff Algorithm The diff viewer uses the [`diff`](https://www.npmjs.com/package/diff) library's `diffLines` function to compute line-level differences between two revisions, displayed in a side-by-side table layout similar to GitHub's split diff view. Adjacent removed and added blocks are paired into the same rows so you can see what changed at a glance. ### Rename Tracking The history extraction uses `git log --follow` to track file renames. If a document was moved or renamed, its full history (including commits before the rename) is preserved. The History component is a React island loaded with `client:idle`, meaning it only hydrates after the page becomes idle. This ensures it doesn't impact initial page load performance. --- # AI Chat Worker (Cloudflare) > Source: /pj/zudo-doc/docs/reference/ai-chat-worker Standalone Cloudflare Worker that provides an AI chat API endpoint, independent of the Astro documentation site. ## Overview The AI Chat Worker is a sub-package at `packages/ai-chat-worker/` that deploys as a Cloudflare Worker. It provides the same chat functionality as the built-in [AI Assistant API](/docs/reference/ai-assistant-api), but runs as a standalone service on Cloudflare Workers runtime. This is useful when: - You want to host the chat API independently from the documentation site - You're deploying the docs as a static site (no server-side rendering) - You want to use Cloudflare Workers runtime for the API backend The Worker fetches `llms-full.txt` from your deployed documentation site and uses it as context for Claude API calls. ## Endpoint ``` POST / Content-Type: application/json ``` The Worker responds at its root URL. ### Request Body ```ts interface AiChatRequest { message: string; history: ChatMessage[]; } interface ChatMessage { role: "user" | "assistant"; content: string; } ``` | Field | Type | Required | Description | | --------- | ---------------- | -------- | ------------------------------------------------------------------------ | | `message` | `string` | Yes | The user's current message. Must be non-empty, max 4000 characters. | | `history` | `ChatMessage[]` | Yes | Previous conversation messages. Invalid entries are filtered out silently. | ### Success Response (200) ```ts interface AiChatResponse { response: string; } ``` The `response` field contains the assistant's reply as a markdown string. ### Error Responses | Status | Condition | | ------ | ------------------------------ | | 400 | Invalid JSON body | | 400 | `message` is not a non-empty string | | 400 | `message` exceeds 4000 character limit | | 400 | Message rejected by input screening | | 405 | Request method is not POST | | 429 | Rate limit exceeded (includes `Retry-After` header) | | 500 | Anthropic API call failed | ## Security The Worker includes layered defenses against prompt injection and abuse: ### Hardened System Prompt The system prompt uses XML tags (``, ``) to clearly separate instructions from user-supplied content. Explicit guardrails instruct the model to: - Only answer questions about the provided documentation - Never reveal system instructions, configuration, or API keys - Reject attempts to override its instructions - Redirect off-topic questions back to documentation ### Input Screening Before a message reaches Claude, a lightweight regex-based filter (`src/input-screen.ts`) checks for common prompt injection patterns such as requests to ignore previous instructions, reveal configuration, or bypass restrictions. Matched messages are rejected with a 400 response. This filter runs before rate limiting so that injection attempts do not consume the caller's rate limit quota. ### API Key Isolation The `ANTHROPIC_API_KEY` is stored as a Cloudflare Worker secret and never included in the prompt context. Claude cannot leak what it does not know. ### Message Length Limit Messages are capped at 4000 characters. Longer messages are rejected with a 400 response before reaching the Claude API. ## Environment Setup ### Variables Set `DOCS_SITE_URL` in `wrangler.toml` to point at your deployed documentation site: ```toml title="packages/ai-chat-worker/wrangler.toml" [vars] DOCS_SITE_URL = "https://your-docs-site.example.com" RATE_LIMIT_PER_MINUTE = "10" RATE_LIMIT_PER_DAY = "100" ``` | Variable | Default | Description | | -------- | ------- | ----------- | | `DOCS_SITE_URL` | — | Your deployed documentation site URL | | `RATE_LIMIT_PER_MINUTE` | `10` | Max requests per IP per minute | | `RATE_LIMIT_PER_DAY` | `100` | Max requests per IP per day | The Worker fetches `${DOCS_SITE_URL}/llms-full.txt` to load documentation context. ### KV Namespace Rate limiting uses a Cloudflare KV namespace. Create it before deploying: ```sh cd packages/ai-chat-worker npx wrangler kv namespace create RATE_LIMIT ``` Update the `id` in `wrangler.toml` `[[kv_namespaces]]` with the returned namespace ID. ### Rate Limiting Behavior The Worker enforces per-IP rate limits using the `cf-connecting-ip` header provided by Cloudflare. - **Best-effort enforcement** — KV reads and writes are not atomic, so concurrent requests from the same IP may slightly exceed the configured limits - **Fail-open** — if KV is unavailable (outage, misconfiguration), requests are allowed through. Chat availability takes priority over strict rate enforcement - **Invalid config** — non-numeric values for `RATE_LIMIT_PER_MINUTE` or `RATE_LIMIT_PER_DAY` fall back to the defaults (10/min, 100/day) - **429 response** — includes a `Retry-After` header (seconds until the current window resets), exposed via CORS for browser access ### Audit Logging Every chat interaction is logged to KV for security analysis. This enables detection of prompt injection attempts and abuse patterns. **Logged fields:** | Field | Description | | ----- | ----------- | | `timestamp` | ISO 8601 timestamp | | `ipHash` | SHA-256 hash of the client IP (raw IP is never stored) | | `message` | User's message, truncated to 500 characters | | `responsePreview` | First 200 characters of the response | | `blocked` | Whether the request was rejected | | `blockReason` | `"rate_limit"`, `"invalid_input"`, or `"prompt_injection"` (when blocked) | **Storage details:** - Uses the same `RATE_LIMIT` KV namespace with `audit:` key prefix (separate from `rate:` keys) - Logs expire automatically after 7 days - Logging is fire-and-forget — failures do not affect the API response - IP addresses are hashed with SHA-256 via the Web Crypto API before storage ### Secrets Add the Anthropic API key as a Cloudflare Worker secret: ```sh cd packages/ai-chat-worker npx wrangler secret put ANTHROPIC_API_KEY ``` ## Deployment ### Manual ```sh cd packages/ai-chat-worker pnpm install pnpm run deploy ``` ### CI/CD The repository includes a GitHub Actions workflow (`.github/workflows/ai-chat-worker-deploy.yml`) that automatically deploys the Worker on push to `main` when files in `packages/ai-chat-worker/` change. Required GitHub secrets: - `CLOUDFLARE_API_TOKEN` — Cloudflare API token with Workers write permission - `CLOUDFLARE_ACCOUNT_ID` — Your Cloudflare account ID The workflow can also be triggered manually via `workflow_dispatch`. ## Relationship to AI Assistant The built-in [AI Assistant](/docs/guides/ai-assistant) runs as part of the Astro site using the `@astrojs/node` adapter. The AI Chat Worker is a standalone alternative that provides the same chat capability without requiring server-side rendering in the docs site. | Feature | Built-in AI Assistant | AI Chat Worker | | -------------------------- | ---------------------------- | -------------------------- | | Runtime | Node.js (Astro SSR) | Cloudflare Workers | | Deployment | Part of the docs site | Independent service | | Docs site requirement | Hybrid mode (SSR) | Static site is sufficient | | Documentation context | Loaded from local file | Fetched from deployed site | ## Sub-Package Location ``` packages/ai-chat-worker/ ├── src/ │ ├── index.ts # Worker entry point │ ├── audit-log.ts # Audit logging + IP hashing │ ├── claude.ts # Claude API integration + docs context fetching │ ├── cors.ts # CORS header handling │ ├── input-screen.ts # Prompt injection input screening │ ├── rate-limit.ts # Per-IP rate limiting via KV │ └── types.ts # Type definitions ├── wrangler.toml # Cloudflare Worker configuration ├── package.json ├── tsconfig.json └── README.md ``` --- # /packages/search-worker/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/packages--search-worker **Path:** `packages/search-worker/CLAUDE.md` # search-worker Cloudflare Worker sub-package providing a server-side search API. Additional option for large doc bases — the primary search remains client-side MiniSearch (`src/components/search.astro`). ## Tech Stack - **Cloudflare Workers** — runtime - **Cloudflare KV** — rate limiting storage - **MiniSearch** — full-text search (same engine as client-side) - **TypeScript** — strict mode, `@cloudflare/workers-types` ## Commands - `pnpm dev` — local dev server (requires `wrangler.toml` with correct `DOCS_SITE_URL`) - `pnpm run deploy` — deploy to Cloudflare Workers via Wrangler - `pnpm typecheck` — TypeScript type checking ## Architecture ``` src/ ├── index.ts # Worker entry — routing, validation, CORS ├── cors.ts # CORS headers (exposes Retry-After) ├── rate-limit.ts # Per-IP rate limiting via KV (60/min, 1000/day) ├── search.ts # MiniSearch index loader + search logic └── types.ts # Env, request/response types ``` ### Request Flow 1. CORS preflight → `cors.ts` 2. Method + path check → 405/404 3. Hash client IP (SHA-256 via Web Crypto) 4. JSON parse + query validation → 400 (query required, max 500 chars) 5. Rate limit check → 429 with `Retry-After` 6. Fetch `search-index.json` from docs site (cached with 5-minute TTL) → `search.ts` 7. MiniSearch search with prefix, fuzzy, and boost → results ### Key Design Decisions - **Additive, not replacement** — client-side MiniSearch handles most users. Worker is for API consumers and huge doc bases - **Index from deployed site** — fetches `${DOCS_SITE_URL}/search-index.json`, same data as client-side - **5-minute cache TTL** — balances freshness with performance. Isolate recycle also clears cache - **Same search config as client** — `prefix: true, fuzzy: 0.2, boost: { title: 3, description: 2 }` ## Configuration - `DOCS_SITE_URL` — base URL of the deployed docs site (set in `wrangler.toml` `[vars]`) - `RATE_LIMIT` — KV namespace for rate limiting (create via `wrangler kv namespace create`) - `RATE_LIMIT_PER_MINUTE` / `RATE_LIMIT_PER_DAY` — configurable in `wrangler.toml` ## Conventions - All responses include CORS headers (including error responses) - Error responses use `{ error: string }` format - Rate limit uses `cf-connecting-ip` for client IP - Query length capped at 500 characters - Default result limit: 20, max: 100 --- # SiteTreeNav > Source: /pj/zudo-doc/docs/components/site-tree-nav ## Overview `SiteTreeNav` is a React island component that renders a responsive grid of expandable category cards showing the full documentation tree. It provides a sitemap-style index of all documentation pages, with categories that can be expanded and collapsed. This component is **not** directly available in MDX content. It requires server-side data fetching, so it must be imported in Astro page templates. The live demo below uses `SiteTreeNavDemo`, an Astro wrapper registered as a global MDX component. ## Live Example Below is `SiteTreeNav` rendered with the full documentation tree (same as the [home page](/)): ## Usage `SiteTreeNav` is used in `src/pages/index.astro` to display the home page sitemap: ```astro --- const categoryOrder = settings.headerNav .map((n) => n.categoryMatch) .filter((v): v is string => !!v); const { allDocs, categoryMeta } = await loadLocaleDocs("en"); const navDocs = allDocs.filter((doc) => !doc.data.unlisted); const tree = buildNavTree(navDocs, "en", categoryMeta); const groupedTree = groupSatelliteNodes(tree, categoryOrder); --- ``` The component requires `client:idle` (or another Astro client directive) because it uses React state for expand/collapse interactivity. ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `tree` | `NavNode[]` | (required) | The navigation tree built from content collection | | `categoryOrder` | `string[]` | — | Custom ordering of top-level categories | | `categoryIgnore` | `string[]` | — | Category slugs to hide from the tree | | `ariaLabel` | `string` | `"Site index"` | Accessibility label for the nav element | ## Features - **Responsive grid layout** — uses `repeat(auto-fill, minmax(min(18rem, 100%), 1fr))` to adapt from single-column on mobile to multi-column on wider screens - **Expandable categories** — each category card is collapsible, open by default - **Hierarchical tree** — nested pages are indented with connector lines showing the tree structure - **Category links** — top-level categories display an arrow icon and link to their index page - **Leaf pages** — individual doc pages are rendered as clickable links ## Source The component is defined at `src/components/site-tree-nav.tsx`. --- # Internationalization (i18n) > Source: /pj/zudo-doc/docs/guides/i18n zudo-doc supports multiple languages using Astro's built-in i18n routing. ## How It Works - **English (default):** `/docs/...` — content lives in `src/content/docs/` - **Japanese:** `/ja/docs/...` — content lives in `src/content/docs-ja/` The language switcher in the site header lets users toggle between available languages. ## Settings-Based Locale Configuration Locales are configured in `src/config/settings.ts`: ```ts // ... locales: { ja: { label: "JA", dir: "src/content/docs-ja" }, }, }; ``` For each locale entry, zudo-doc automatically: - Creates an Astro content collection (`docs-{code}`) - Registers the locale in Astro's i18n routing - Generates the appropriate page routes - Adds the locale to the language switcher This means adding a new locale only requires a settings entry and a content directory — no manual collection or route setup needed. ## Directory Structure Localized docs mirror the English directory structure: ``` src/content/ ├── docs/ │ ├── getting-started/ │ │ ├── introduction.mdx │ │ └── installation.mdx │ └── guides/ │ └── configuration.mdx └── docs-ja/ ├── getting-started/ │ ├── introduction.mdx │ └── installation.mdx └── guides/ └── configuration.mdx ``` ## Language Switcher The language switcher appears in the header's right edge. It shows the current language code (EN or JA) and a link to switch to the alternate language. The switcher automatically computes the alternate URL by adding or removing the `/ja/` prefix. ## UI Translation Keys zudo-doc uses a translation key system for built-in UI strings. Keys are organized by namespace: - `nav.*` — Header navigation labels (e.g., `nav.gettingStarted`, `nav.guides`) - `toc.*` — Table of contents labels - `search.*` — Search dialog text - `code.*` — Code block UI (copy button, etc.) - `doc.*` — Document-level UI (edit link, metadata labels, etc.) These keys allow the entire UI (not just content) to be localized across all configured languages. ## Astro Configuration i18n routing is configured in `astro.config.ts`: ```ts i18n: { defaultLocale: "en", locales: ["en", ...Object.keys(settings.locales)], routing: { prefixDefaultLocale: false, }, }, ``` With `prefixDefaultLocale: false`, English pages are served without a language prefix (`/docs/...`) while Japanese pages use the `/ja/` prefix (`/ja/docs/...`). The `locales` array in `astro.config.ts` is automatically derived from `settings.locales`, so you only need to maintain locales in one place. ## Adding a New Language To add a new language (e.g., Korean): 1. Add the locale to `settings.ts`: ```ts locales: { ja: { label: "JA", dir: "src/content/docs-ja" }, ko: { label: "KO", dir: "src/content/docs-ko" }, }, ``` 2. Create a content directory: `src/content/docs-ko/` 3. Mirror the English directory structure with translated files 4. Add a new page route at `src/pages/ko/docs/[...slug].astro` (copy from an existing locale route) 5. Add UI translations for the new locale Step 1 is the key step — adding the locale to `settings.ts` automatically creates the content collection and registers the i18n routing. The remaining steps provide the content and page routes. --- # /src/CLAUDE.md > Source: /pj/zudo-doc/docs/claude-md/src **Path:** `src/CLAUDE.md` # Source Code Rules ## Design Token System Uses a 16-color palette system. ### Three-Tier Color Strategy **Tier 1 — Palette** (injected by `ColorSchemeProvider` on `:root`): - `--zd-bg`, `--zd-fg`, `--zd-sel-bg`, `--zd-sel-fg`, `--zd-cursor` - `--zd-0` through `--zd-15` (16 palette slots) **Tier 2 — Semantic tokens** (in `global.css` `@theme`, resolved per scheme): - Palette access: `p0`–`p15` → `bg-p0`, `text-p8`, `border-p1`, etc. - Base: `bg`, `fg` → `bg-bg`, `text-fg` - UI: `surface`, `muted`, `accent`, `accent-hover`, `sel-bg`, `sel-fg` - Content: `code-bg`, `code-fg`, `success`, `danger`, `warning`, `info` **Tier 3 — Component tokens** (scoped to specific components): - Content: `.zd-content` direct element styling in `global.css` (consumes Tier 2) Each tier only references the tier above it. ### Color Rules - **NEVER** use Tailwind default colors (`bg-gray-500`, `text-blue-600`) — they are reset to `initial` - **ALWAYS** use project tokens: `text-fg`, `bg-surface`, `border-muted`, `text-accent`, etc. - Prefer semantic tokens (`text-accent`, `bg-code-bg`, `text-danger`) for standard UI - Use palette tokens (`p0`–`p15`) only when no semantic token fits ### Changing Scheme - Edit `colorScheme` in `src/config/settings.ts` - Available: Dracula, Catppuccin Mocha, Nord, TokyoNight, Gruvbox Dark, Atom One Dark - Add schemes in `src/config/color-schemes.ts` (22 color props + `shikiTheme`) - `ColorRef` type: `background`, `foreground`, `cursor`, `selectionBg`, `selectionFg`, and semantic overrides accept `number | string` — number = palette index, string = direct color ### Color Tweak Panel - Enabled via `colorTweakPanel: true` in settings - Interactive panel at page bottom for live color editing (palette, base, semantic tokens) - Export button generates `ColorScheme` TypeScript code for clipboard copy - State persisted in `localStorage` (`zudo-doc-tweak-state`) ## CSS & Components - Before writing or editing CSS, Tailwind classes, color tokens, or component markup, invoke `/zudo-doc-css-wisdom` to load project-specific rules - Tailwind v4: imports `tailwindcss/preflight` + `tailwindcss/utilities` (no default theme) - No `--*: initial` resets needed — default theme is simply not imported - Content typography: `.zd-content` class in `global.css` (no prose plugin — direct element styling with `:where()` selectors) - **Component-first strategy**: always use Tailwind utility classes directly in component markup — never create CSS module files or custom CSS class names. The component itself is the abstraction. - **Tight token strategy**: prefer existing spacing (`hsp-*`, `vsp-*`), typography (`text-caption`, `text-small`, etc.), and color tokens. Avoid arbitrary values (`text-[0.8rem]`, `py-[0.35rem]`) when an existing token is close enough. --- # CategoryNav > Source: /pj/zudo-doc/docs/components/category-nav ## Overview `CategoryNav` is an Astro component that automatically lists child pages of a category as a card grid. It is designed for use in category `index.mdx` files to create landing pages that link to all child pages. `CategoryNav` is globally available in all MDX doc pages — no import needed. It is registered alongside the admonition components (`Note`, `Tip`, etc.) in the doc page route. ## Live Example Here is `CategoryNav` rendering the Getting Started category: ## Usage Use `CategoryNav` in a category's `index.mdx` to create a landing page: ```mdx --- title: Getting Started sidebar_position: 0 --- Welcome to the Getting Started section. Choose a topic below to begin. ``` Since `CategoryNav` is an Astro component (zero client-side JavaScript), no `client:` directive is needed. ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `category` | `string` | (required) | The category slug (e.g., `"guides"`, `"reference"`) | | `lang` | `Locale` | auto-detected | Override the locale for the content collection | The `lang` prop is automatically detected from the current URL path, so you typically don't need to set it. ## Features - **2-column grid** — displays `sm:grid-cols-2` on desktop, single column on mobile - **Card display** — each card shows the page title (as a link) and description (from frontmatter) - **Hover effect** — card border highlights on hover; the title link is always underlined - **Auto-excludes index** — the category's own index page is automatically filtered out - **Sorted by position** — cards follow `sidebar_position` ordering from frontmatter ## Where to Use Add `CategoryNav` to any category's `index.mdx` to generate a landing page. For example, the Getting Started section uses it: ``` src/content/docs/ getting-started/ index.mdx ← uses introduction.mdx installation.mdx writing-docs.mdx ``` ## Source The component is defined at `src/components/category-nav.astro`. --- # Claude Code Resources > Source: /pj/zudo-doc/docs/guides/claude-resources ## Overview zudo-doc can automatically generate documentation pages from your project's Claude Code resources — CLAUDE.md files, custom commands, skills, and agents. When enabled, these appear under the **Claude** header navigation section. ## Enabling Claude Resources Set `claudeResources` in `src/config/settings.ts`: ```ts claudeResources: { claudeDir: ".claude", }, ``` Set to `false` to disable: ```ts claudeResources: false, ``` ## What Gets Generated The integration scans your `.claude/` directory and generates MDX documentation pages for: | Resource | Source | Output | |----------|--------|--------| | CLAUDE.md files | Project root and subdirectories | `claude-md/` | | Commands | `.claude/commands/*.md` | `claude-commands/` | | Skills | `.claude/skills/*/SKILL.md` | `claude-skills/` | | Agents | `.claude/agents/*.md` | `claude-agents/` | Each resource type gets its own index page with a list of all items, plus individual detail pages. ## How It Works The integration runs at build time during the `astro:config:setup` hook: 1. Scans the configured `.claude/` directory 2. Discovers CLAUDE.md files, commands, skills (with references), and agents 3. Generates MDX files in `src/content/docs/claude-*/` directories 4. These files are picked up by Astro's content collections and rendered as documentation pages The generated pages appear under the **Claude** header navigation tab, configured via `headerNav` with `categoryMatch: "claude"`. Generated files are written to `src/content/docs/` and are recreated on every build. Do not edit them manually — your changes will be overwritten. ## Configuration Options | Option | Type | Description | |--------|------|-------------| | `claudeDir` | string | Path to the `.claude/` directory (relative to project root) | | `projectRoot` | string | Optional project root override for resolving CLAUDE.md file paths | The `projectRoot` option is useful in monorepo setups where the `.claude/` directory is not at the project root. Skills with a `references/` subdirectory will have their reference documents included as separate linked pages, making it easy to browse bundled knowledge bases. --- # llms.txt > Source: /pj/zudo-doc/docs/guides/llms-txt zudo-doc can automatically generate `llms.txt` and `llms-full.txt` files, making your documentation easily consumable by AI tools and large language models. The [llms.txt standard](https://llmstxt.org/) is an emerging convention that helps AI tools discover and read your documentation. Many AI-powered tools already look for these files. ## How It Works When enabled, zudo-doc generates two files at build time: - **`/llms.txt`** — An index listing all documentation pages with their titles, descriptions, and URLs - **`/llms-full.txt`** — The full content of all documentation pages combined into a single file These files provide a machine-friendly view of your documentation that AI tools can consume without parsing HTML. ## Enabling llms.txt The feature is **enabled by default**. To explicitly control it, set `llmsTxt` in `src/config/settings.ts`: ```ts // ... llmsTxt: true, // enabled by default }; ``` To disable it: ```ts llmsTxt: false, ``` ## Generated Output ### llms.txt (Index) The index file lists each documentation page with its title, description, and URL: ``` # My Documentation > Site description from settings ## Docs - [Introduction](/docs/getting-started/introduction): Learn what the project is and how it works. - [Configuration](/docs/guides/configuration): Global settings and configuration reference. ``` ### llms-full.txt (Full Content) The full content file includes the complete text of every documentation page, separated by headings: ``` # My Documentation > Site description from settings ## Introduction Welcome to the project... ## Configuration The project is configured through... ``` ## Page Exclusion Pages with any of the following frontmatter fields are automatically excluded from both generated files: - `draft: true` — Draft pages excluded from build - `unlisted: true` — Built but hidden pages - `search_exclude: true` — Pages excluded from search ```mdx --- title: Internal Notes search_exclude: true --- ``` ## Multi-Locale Support When locales are configured, zudo-doc generates separate llms.txt files for each locale: - **English (default):** `/llms.txt` and `/llms-full.txt` - **Japanese:** `/ja/llms.txt` and `/ja/llms-full.txt` Each locale's files contain only the pages from that locale's content directory. URLs in each file use the appropriate locale prefix. ## HTML Discovery When `llmsTxt` is enabled, zudo-doc adds `` tags to the `` of every page, helping AI tools discover the files even when the site is deployed at a subpath: ```html ``` This supplements the standard path-based discovery (`/llms.txt` at the domain root) which may not work when your site uses a `base` path like `/pj/zudo-doc/`. ## Dev Mode The generated files are available during development via Vite middleware. When running `pnpm dev`, requests to `/llms.txt` and `/llms-full.txt` are handled dynamically — no build step needed. No need to build before testing. The llms.txt files work immediately with `pnpm dev`, just like search. ## Production Build During `pnpm build`, the files are written to the `dist/` directory as static text files alongside the rest of your site. --- # CategoryTreeNav > Source: /pj/zudo-doc/docs/components/category-tree-nav ## Overview `CategoryTreeNav` is an Astro component that displays child pages of a category as a nested tree of links (`ul > li > ul > li` pattern). It is an alternative to `CategoryNav` which uses a card-grid style — `CategoryTreeNav` is more compact and suited to categories with deeper nesting. `CategoryTreeNav` is globally available in all MDX doc pages — no import needed. It is registered alongside the admonition components (`Note`, `Tip`, etc.) in the doc page route. ## Live Example Here is `CategoryTreeNav` rendering the Guides category: ## Usage Use `CategoryTreeNav` in a category's `index.mdx` to create a compact tree-style listing: ```mdx --- title: Getting Started sidebar_position: 0 --- Welcome to the Getting Started section. ``` Since `CategoryTreeNav` is an Astro component (zero client-side JavaScript), no `client:` directive is needed. ## Comparison with CategoryNav | Feature | CategoryNav | CategoryTreeNav | |---------|-------------|-----------------| | Layout | 2-column card grid | Nested tree list | | Description | Shown in each card | Shown inline after title | | Nesting | Flat (children only) | Up to 3 levels deep | | Best for | Landing pages, overviews | Deep hierarchies, compact listings | ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `category` | `string` | (required) | The category slug (e.g., `"guides"`, `"reference"`) | | `lang` | `Locale` | auto-detected | Override the locale for the content collection | The `lang` prop is automatically detected from the current URL path, so you typically don't need to set it. ## Source The component is defined at `src/components/category-tree-nav.astro`. --- # HtmlPreview > Source: /pj/zudo-doc/docs/components/html-preview `` renders live HTML/CSS demos inside an isolated iframe with viewport presets (Mobile / Tablet / Full) and a collapsible source code panel with syntax highlighting. It is globally available in all MDX files without imports. ## Basic Usage Hello, world! `} css={` .box { padding: 24px; background: #3b82f6; color: #fff; border-radius: 8px; font-family: system-ui, sans-serif; text-align: center; } `} /> ````mdx Hello, world! `} css={` .box { padding: 24px; background: #3b82f6; color: #fff; border-radius: 8px; font-family: system-ui, sans-serif; text-align: center; } `} /> ```` ## Responsive Layout Use the viewport buttons (Mobile / Tablet / Full) to see how content reflows at different widths. Try resizing with the drag handle at the bottom-right of the preview area. Card 1 Card 2 Card 3 Card 4 `} css={` .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; padding: 16px; font-family: system-ui, sans-serif; } .card { padding: 20px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 6px; text-align: center; color: #334155; } `} /> ## HTML Only When no `css` prop is provided, only the HTML source is shown. First item Second item Third item `} /> ## Default Open Code Use `defaultOpen` to show the source code expanded by default. Click me `} css={` .btn { padding: 10px 20px; background: #8b5cf6; color: #fff; border: none; border-radius: 6px; font-size: 14px; font-family: system-ui, sans-serif; cursor: pointer; } .btn:hover { background: #7c3aed; } `} /> ## Fixed Height Use the `height` prop to set a fixed iframe height instead of auto-sizing. This preview has a fixed height of 300px. Content that exceeds this height will scroll within the iframe. `} css={` .scroll-content { padding: 16px; font-family: system-ui, sans-serif; color: #334155; line-height: 1.6; } `} /> ## External Resources You can inject external resources (CSS frameworks, webfonts, scripts) into previews. Configure globally via `settings.ts` or per-component via props. ### Global Configuration Set `htmlPreview` in `src/config/settings.ts` to apply resources to all previews: ```ts htmlPreview: { head: ``, css: `body { font-family: 'Noto Sans JP', sans-serif; }`, js: `console.log('preview loaded');`, } ``` ### Per-Component Props Use `head` and `js` props to add resources to individual previews. These are merged after global values. Waiting... `} css={` #output { padding: 16px; font-family: system-ui, sans-serif; color: #334155; } `} js={` document.getElementById('output').textContent = 'Hello from JS!'; `} /> ````mdx Waiting... `} css={` #output { padding: 16px; font-family: system-ui, sans-serif; color: #334155; } `} js={` document.getElementById('output').textContent = 'Hello from JS!'; `} /> ```` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `html` | string | (required) | HTML content to render inside the preview iframe | | `css` | string | `undefined` | CSS styles applied inside the preview iframe | | `head` | string | `undefined` | Raw HTML injected into `` (links, meta, fonts) | | `js` | string | `undefined` | JavaScript executed inside the preview iframe | | `title` | string | `undefined` | Title displayed in the preview header bar | | `height` | number | auto | Fixed iframe height in pixels. When omitted, height auto-adjusts to content | | `defaultOpen` | boolean | `false` | Show the source code panel expanded by default | ## Supported Languages The source code panel uses a lightweight Shiki instance with the following languages: - `html` — for the HTML and Head panels - `css` — for the CSS panel - `javascript` — for the JS panel Unsupported languages fall back to plain text (no highlighting). This is a smaller subset than the [full language list](/docs/components/basic-components#supported-languages) available in standard code blocks. ## Notes - The preview renders inside an isolated `` with a CSS reset (Tailwind v4 preflight), so styles do not leak in or out. - Previews run without a `sandbox` attribute. The `srcdoc` content is author-controlled MDX, so no additional isolation is needed. - Global resources from `settings.htmlPreview` are injected before per-component props. - The `html`, `css`, and `js` props support template literals with indentation. Leading whitespace is automatically stripped (dedented) in the source code display. - Client-side hydration is handled automatically by the component wrapper — no `client:load` directive needed in MDX. --- # Documentation Skill Symlinker > Source: /pj/zudo-doc/docs/guides/doc-skill-symlinker ## Overview The **doc skill symlinker** creates a [Claude Code skill](https://docs.anthropic.com/en/docs/claude-code/skills) from your documentation content. Once set up, you can invoke the skill inside any Claude Code session to look up your docs — making all your written documentation available as AI context. This is especially useful when: - Working on another project that depends on this one - Answering questions about configuration, components, or usage patterns - Sharing institutional knowledge with your team through Claude Code ## How It Works The setup script: 1. Creates a `.claude/skills//` directory in your project 2. Generates a `SKILL.md` that tells Claude how to use your docs 3. Symlinks the `src/content/docs/` directory into the skill folder 4. Symlinks the skill into `~/.claude/skills/` so it's available globally After setup, the skill appears as a slash command in any Claude Code session. ## Setup Run the setup script: ```bash pnpm run setup:doc-skill ``` You'll be prompted for a skill name. The default is `-wisdom` (e.g., `zudo-doc-wisdom`). ``` === zudo-doc Skill Setup === Skill name [zudo-doc-wisdom]: Created docs symlink: ... Created docs-ja symlink: ... Generated SKILL.md Done! Skill 'zudo-doc-wisdom' is ready. ``` ## Using the Skill Once set up, use the skill in any Claude Code session: ``` /zudo-doc-wisdom sidebar ``` Claude will look up the relevant documentation article and use it to answer your question. This works from any project directory — the skill is symlinked globally. ### Example Workflow Suppose you're building a site with zudo-doc and want to know how to configure the footer: ``` /zudo-doc-wisdom footer configuration ``` Claude reads the footer guide from your docs and provides an accurate, documentation-backed answer. ## What Gets Created After running the setup script, the following files are created: ``` .claude/skills// ├── SKILL.md # Skill definition with frontmatter and instructions ├── docs -> src/content/docs/ # Symlink to English docs └── docs-ja -> src/content/docs-ja/ # Symlink to Japanese docs (if present) ``` And a global symlink: ``` ~/.claude/skills/ -> .claude/skills/ ``` The `.claude/skills//` directory contains symlinks and a generated `SKILL.md`. It is safe to add to `.gitignore` if you prefer, or commit it to share the skill definition with your team. ## Re-running Setup You can re-run the setup script at any time to regenerate the skill. Existing symlinks are replaced automatically. ```bash pnpm run setup:doc-skill ``` If you rename your project or change the docs directory structure, re-run the setup script to update the skill. --- # Versioning > Source: /pj/zudo-doc/docs/guides/versioning zudo-doc supports maintaining multiple versions of your documentation side by side. When versioning is enabled, older (or pre-release) versions are served under version-prefixed URLs, and a version switcher appears in the layout for users to navigate between versions. ## Overview Versioning lets you: - Keep the **latest** documentation at `/docs/...` as usual - Serve older versions at `/v/{version}/docs/...` - Show a **version switcher** so users can navigate between versions - Display **version banners** to indicate outdated or unreleased content ## Enabling Versioning Versioning is **disabled by default**. To enable it, add a `versions` array in `src/config/settings.ts`: ```ts // ... versions: [ { slug: "1.0", label: "1.0.0", docsDir: "src/content/docs-v1", locales: { ja: { dir: "src/content/docs-v1-ja" }, }, banner: "unmaintained", }, ], }; ``` Set `versions` to `false` (or omit it) to disable versioning: ```ts versions: false, ``` ## Configuration Reference Each version entry accepts the following properties: | Property | Type | Required | Description | |----------|------|----------|-------------| | `slug` | string | Yes | Version identifier used in URL paths (e.g., `"1.0"`, `"v2"`) | | `label` | string | Yes | Display label shown in the version switcher (e.g., `"1.0.0"`) | | `docsDir` | string | Yes | Content directory for this version's English docs | | `locales` | object | No | Per-locale content directories (same shape as main `locales` setting) | | `banner` | `"unmaintained"` \| `"unreleased"` \| `false` | No | Banner type displayed on versioned pages | ## Directory Structure Each version's content lives in its own directory. The latest (current) version uses the default `docsDir`, while older versions use the directories specified in their configuration: ``` src/content/ ├── docs/ # Latest version (default) ├── docs-ja/ # Latest version — Japanese ├── docs-v1/ # Version 1.0 — English ├── docs-v1-ja/ # Version 1.0 — Japanese ├── docs-v0.9/ # Version 0.9 — English └── docs-v0.9-ja/ # Version 0.9 — Japanese ``` Each version directory must exist and contain valid MDX content files. zudo-doc creates a separate Astro content collection for each version, so missing directories will cause build errors. ## URL Structure The latest version is served at the default docs URL. Older versions use a `/v/{slug}/` prefix: - **Latest:** `/docs/getting-started/introduction` - **Version 1.0:** `/v/1.0/docs/getting-started/introduction` - **Version 0.9:** `/v/0.9/docs/getting-started/introduction` With locales, the locale prefix comes after the version prefix: - **Latest (Japanese):** `/ja/docs/getting-started/introduction` - **Version 1.0 (Japanese):** `/v/1.0/ja/docs/getting-started/introduction` ## Version Switcher When one or more versions are configured, a **version switcher** automatically appears in both the header bar and the doc content area. It displays the current version label and provides links to switch between all available versions, including the latest. The switcher preserves the current page slug when navigating between versions, so users land on the same topic in the selected version. If a page does not exist in an older version, that version's link is automatically **disabled** (greyed out and not clickable) instead of linking to a 404 page. This is determined at build time by scanning each version's content directory. The dropdown also includes an **"All versions"** link at the bottom, which navigates to the [versions listing page](/docs/versions/). ## Versions Listing Page A dedicated versions page is automatically generated at `/docs/versions/` (and `/ja/docs/versions/` for Japanese). This page lists all configured versions with their labels, status badges, and documentation links — similar to [Docusaurus' versions page](https://docusaurus.io/versions). The page is auto-generated from the `versions` array in `settings.ts`. No manual maintenance is needed — adding or removing versions in the configuration automatically updates the listing page. ## Version Banners Each version can display a banner at the top of every page to inform users about the version's status. Two banner types are available: ### Unmaintained ```ts banner: "unmaintained", ``` Shows a warning-styled banner indicating the user is viewing documentation for an older version, with a link to the latest version. ### Unreleased ```ts banner: "unreleased", ``` Shows an info-styled banner indicating the user is viewing documentation for an unreleased version, with a link to the latest stable version. ### No Banner ```ts banner: false, ``` Disables the banner for that version. This is the default when `banner` is omitted. Use `"unmaintained"` for archived versions that are no longer updated, and `"unreleased"` for documentation of upcoming features that haven't shipped yet. ## Multi-Locale Support Each version can define its own locale directories, independent of the main locale configuration: ```ts versions: [ { slug: "1.0", label: "1.0.0", docsDir: "src/content/docs-v1", locales: { ja: { dir: "src/content/docs-v1-ja" }, }, banner: "unmaintained", }, ], ``` For each version and locale combination, zudo-doc creates a separate content collection (e.g., `docs-v-1.0-ja`) and generates the corresponding page routes. ## Creating a New Version When you're ready to archive the current documentation as a new version: 1. **Copy the current content directory** to a new versioned directory: ```bash cp -r src/content/docs src/content/docs-v2 ``` 2. **Copy locale directories** if applicable: ```bash cp -r src/content/docs-ja src/content/docs-v2-ja ``` 3. **Add the version to settings**: ```ts versions: [ { slug: "2.0", label: "2.0.0", docsDir: "src/content/docs-v2", locales: { ja: { dir: "src/content/docs-v2-ja" }, }, banner: "unmaintained", }, // ...existing versions ], ``` 4. **Update the current docs** — The files in `src/content/docs/` now represent the latest version. Update them as needed for the new release. The `versions` array defines older or pre-release versions only. The current/latest version always uses the main `docsDir` and is not listed in the array. --- # Changelog > Source: /pj/zudo-doc/docs/changelog Track the changes, improvements, and fixes in each release of zudo-doc. --- # create-zudo-doc CLI > Source: /pj/zudo-doc/docs/reference/create-zudo-doc ## Usage ```bash create-zudo-doc [project-name] [options] ``` When run without flags, the CLI launches an interactive wizard. All options can be specified via flags for non-interactive (CI/agent) usage. ## Options ### Project | Flag | Description | Default | |------|-------------|---------| | `--name ` | Project name (or first positional arg) | `my-docs` | | `--lang ` | Default language code | `en` | | `--pm ` | Package manager: `pnpm`, `npm`, `yarn`, `bun` | `pnpm` | | `--[no-]install` | Install dependencies after scaffolding | prompt | ### Color Scheme | Flag | Description | Default | |------|-------------|---------| | `--color-scheme-mode ` | `single` or `light-dark` | `light-dark` | | `--scheme ` | Color scheme (single mode) | `Dracula` | | `--light-scheme ` | Light scheme (light-dark mode) | `GitHub Light` | | `--dark-scheme ` | Dark scheme (light-dark mode) | `GitHub Dark` | | `--default-mode ` | `light` or `dark` (light-dark mode) | `dark` | | `--[no-]respect-system-preference` | Respect OS color scheme preference | `true` | ### Features | Flag | Description | Default | |------|-------------|---------| | `--[no-]i18n` | Multi-language support | off | | `--[no-]search` | Pagefind full-text search | on | | `--[no-]sidebar-filter` | Real-time sidebar filtering | on | | `--[no-]claude-resources` | Claude Code docs generation | off | ### General | Flag | Description | |------|-------------| | `-y, --yes` | Use defaults for unspecified options, skip all prompts | | `-h, --help` | Show help message | ## Supported Languages The `--lang` flag accepts any of the following language codes: | Code | Language | |------|----------| | `en` | English | | `ja` | Japanese | | `zh-cn` | Chinese (Simplified) | | `zh-tw` | Chinese (Traditional) | | `ko` | Korean | | `es` | Spanish | | `fr` | French | | `de` | German | | `pt` | Portuguese | The default language determines the locale used for root pages (`/docs/...`). When i18n is enabled, a secondary language is added automatically (English when the default is non-English, Japanese when the default is English). ## Examples ### Interactive mode ```bash pnpm create zudo-doc ``` ### Non-interactive with all defaults ```bash pnpm create zudo-doc my-docs --yes ``` ### Japanese site with Dracula theme ```bash pnpm create zudo-doc my-docs --lang ja --scheme Dracula --no-i18n --pm pnpm --install ``` ### Light/dark mode with custom schemes ```bash pnpm create zudo-doc my-docs \ --color-scheme-mode light-dark \ --light-scheme "GitHub Light" \ --dark-scheme "GitHub Dark" \ --default-mode dark \ --yes ``` ### CI/automation usage ```bash pnpm create zudo-doc my-docs \ --lang en \ --scheme Nord \ --no-i18n \ --search \ --no-claude-resources \ --pm pnpm \ --install \ --yes ``` ## Programmatic API The package also exports a programmatic API: ```ts await createZudoDoc({ projectName: "my-docs", defaultLang: "en", colorSchemeMode: "single", singleScheme: "Dracula", features: ["search", "sidebarFilter"], packageManager: "pnpm", install: true, }); ``` --- # Color Scheme Preview > Source: /pj/zudo-doc/docs/guides/color-scheme-preview ## Overview The color scheme preview lets you browse 50+ preset color schemes and apply them to your site in real-time. This functionality is built into the Color Tweak Panel via a "Scheme..." dropdown in the panel header bar. ## How It Works When the Color Tweak Panel is enabled, a **"Scheme..."** dropdown appears in the panel header bar. It lists all available preset schemes. Selecting a scheme: - Loads all of the preset's palette, base, and semantic colors into the tweak state - Applies the colors to the page immediately - Persists the choice in `localStorage` so it survives page reloads and navigation - Does not modify any source files — the change is client-side only After loading a preset, you can further tweak individual colors as needed using the panel's color pickers. ## Enabling the Color Tweak Panel Set `colorTweakPanel` to `true` in `src/config/settings.ts`: ```ts title="src/config/settings.ts" colorTweakPanel: true, // ... }; ``` When enabled, a palette icon appears in the header bar. Clicking it toggles the panel open/closed. The "Scheme..." dropdown is in the panel header. ## Available Color Schemes The panel includes 50+ preset color schemes such as Dracula, Nord, Solarized Dark, Solarized Light, Catppuccin Mocha, Catppuccin Latte, Tokyo Night, Gruvbox Dark, GitHub Dark, GitHub Light, and many more. Bundled schemes (those included in your project's `color-schemes.ts`) appear first in the dropdown, followed by a separator and the remaining presets. Each scheme specifies a 16-color palette, background/foreground colors, selection colors, a Shiki code highlighting theme, and optional semantic color overrides. The scheme preview is particularly useful during development to quickly evaluate how your content looks across different themes before committing to a final scheme. ## See Also - [Color Tweak Panel](/docs/guides/color-tweak-panel) — for interactive editing of individual palette colors and semantic tokens in the browser --- # Color Tweak Panel > Source: /pj/zudo-doc/docs/guides/color-tweak-panel ## Overview The color tweak panel is an interactive editor that lets users modify all color values on the page in real-time. It opens as a fixed panel at the bottom of the viewport and provides controls for the 16-color palette, base theme colors, and semantic token overrides. This is useful for: - Designing new color schemes visually instead of editing config files - Previewing how color changes affect the entire site - Exporting the result as a `ColorScheme` object to paste into `src/config/color-schemes.ts` ## Enabling the Panel Set `colorTweakPanel` to `true` in `src/config/settings.ts`: ```ts title="src/config/settings.ts" colorTweakPanel: true, // ... }; ``` When enabled, a palette icon appears in the header bar (to the left of the search icon). Clicking it toggles the panel open/closed. The panel is an optional feature. When `colorTweakPanel` is `false` (the default), no panel code or FOUC-prevention script is included in the output. ## Panel Sections The panel is divided into three sections: ### Palette (16 colors) Shows 16 color swatches in an 8-column grid, labeled `p0` through `p15`. Click any swatch to open a native color picker and change that palette slot. Changes are applied instantly to all CSS custom properties that reference the slot. ### Base Theme Controls for the five base colors: `bg` (background), `fg` (foreground), `cursor`, `sel-bg` (selection background), and `sel-fg` (selection foreground). These map directly to `--zd-bg`, `--zd-fg`, `--zd-cursor`, `--zd-sel-bg`, and `--zd-sel-fg`. ### Semantic Tokens Shows all 10 semantic tokens with their current resolved colors and default palette mappings. Each token displays which palette slot it defaults to (e.g., `accent` defaults to `p6`). You can override any semantic token with a custom color via the color picker. Overridden tokens show a **reset** link to restore the palette default. ## Persistence All changes are saved to `localStorage` (key: `zudo-doc-tweak-state`) and automatically re-applied on page load and during View Transition navigations. An inline script runs before first paint to prevent FOUC (flash of unstyled content). To clear all tweaks and restore the original color scheme, click **Reset all** in the panel header. ## Exporting a Color Scheme Click the **Export** button in the panel header to open a modal dialog showing the current color configuration as TypeScript code. The output matches the `ColorScheme` interface format and can be pasted directly into `src/config/color-schemes.ts`. Click **Copy** to copy the code to your clipboard. The export workflow lets you design a scheme visually in the browser, then save it as a permanent scheme in your project configuration. ## Scheme Chooser The panel header bar includes a **"Scheme..."** dropdown that lets you browse and load from 50+ preset color schemes (Dracula, Nord, Solarized, Catppuccin, and many more). Selecting a preset: - Loads all of the preset's palette, base, and semantic colors into the tweak state - Applies the colors to the page immediately - Persists the choice in `localStorage` so it survives page reloads and navigation - After loading a preset, you can further tweak individual colors as needed Click **Reset all** in the panel header to discard all tweaks and restore the original scheme from `src/config/settings.ts`. --- # Footer > Source: /pj/zudo-doc/docs/guides/footer The footer appears at the bottom of every page and is configurable through `src/config/settings.ts` under the `footer` property. It supports a multi-column link layout and optional copyright text. ## Configuration The `footer` property in `src/config/settings.ts` accepts a `FooterConfig` object: ```ts footer: { links: [ { title: "Docs", items: [ { label: "Getting Started", href: "/docs/getting-started" }, { label: "Guides", href: "/docs/guides" }, ], }, { title: "Community", items: [ { label: "GitHub", href: "https://github.com/zudolab/zudo-doc" }, ], }, ], copyright: `Copyright © ${new Date().getFullYear()} Your Name. Built with zudo-doc.`, } satisfies FooterConfig as FooterConfig | false, ``` ### links An array of columns displayed in the footer. Each column has a `title` and an `items` array of links. | Property | Type | Description | |----------|------|-------------| | `title` | string | Column heading displayed above the links | | `items` | `FooterLinkItem[]` | Array of link items in the column | Each item in the `items` array has: | Property | Type | Description | |----------|------|-------------| | `label` | string | Display text for the link | | `href` | string | URL the link points to | External links (URLs starting with `http://` or `https://`) automatically open in a new tab with `rel="noopener noreferrer"`. Internal links are resolved with the site's configured base path. ### copyright Optional copyright text displayed centered below the link columns. When link columns are present, the copyright is separated from them by a top border. When no links are configured, the copyright displays without a border. The copyright field supports **HTML content** — you can include `` tags for links. Links in the copyright are automatically styled with `text-accent` color and underlined. ```ts copyright: `Copyright © ${new Date().getFullYear()} Your Name. Built with zudo-doc.`, ``` ## Adding Columns Add more columns by appending objects to the `links` array. The footer uses a responsive grid — columns stack on small screens and flow horizontally on larger screens. ```ts footer: { links: [ { title: "Docs", items: [ { label: "Getting Started", href: "/docs/getting-started" }, { label: "Guides", href: "/docs/guides" }, ], }, { title: "Community", items: [ { label: "GitHub", href: "https://github.com/zudolab/zudo-doc" }, { label: "Discord", href: "https://discord.gg/example" }, ], }, { title: "More", items: [ { label: "Blog", href: "https://example.com/blog" }, { label: "Changelog", href: "/docs/changelog" }, ], }, ], copyright: `Copyright © ${new Date().getFullYear()} My Project.`, }, ``` The grid layout uses `repeat(auto-fit, minmax(12rem, 1fr))` on large screens, so columns will automatically adjust their width based on available space. ## Disabling the Footer To remove the footer entirely, set `footer` to `false` in settings: ```ts footer: false as FooterConfig | false, ``` When set to `false`, the footer component is not rendered on any page. ## i18n (Localized Labels) Footer columns and link items support locale-specific overrides via the optional `locales` field. When a page is rendered for a non-default locale, the footer resolves localized titles and labels automatically. Internal link hrefs are also prefixed with the locale path (e.g. `/ja/docs/guides`). ```ts footer: { links: [ { title: "Docs", locales: { ja: { title: "ドキュメント" } }, items: [ { label: "Getting Started", href: "/docs/getting-started", locales: { ja: { label: "はじめに" } }, }, { label: "Guides", href: "/docs/guides", locales: { ja: { label: "ガイド" } }, }, ], }, ], }, ``` Each `FooterLinkColumn` accepts an optional `locales` field with per-locale `{ title }` overrides. Each `FooterLinkItem` accepts an optional `locales` field with per-locale `{ label }` overrides. When no locale override exists, the default `title` or `label` is used. External links are never locale-prefixed — only internal hrefs (those that don't start with `http://` or `https://`) get the locale prefix. ## TypeScript Types The footer configuration uses these interfaces defined in `src/config/settings-types.ts`: ```ts interface FooterLinkItem { label: string; href: string; locales?: Record; } interface FooterLinkColumn { title: string; items: FooterLinkItem[]; locales?: Record; } interface FooterConfig { links: FooterLinkColumn[]; copyright?: string; } ``` The `footer` property in settings is typed as `FooterConfig | false`, allowing it to be either a configuration object or `false` to disable. ## Styling The footer uses the project's design token colors for consistent theming: - `border-muted` — top border separating the footer from content, and the divider above the copyright - `bg-surface` — footer background - `text-fg` — column titles - `text-muted` — link text and copyright text - `text-accent` — link hover color The layout is responsive: a single column on mobile, two columns on small screens, and auto-fit columns on large screens. See `src/components/footer.astro` for implementation details. --- # Changelog > Source: /pj/zudo-doc/docs/guides/changelog zudo-doc includes a changelog section for tracking release notes and version history. The changelog uses descending sidebar sort so the newest entries always appear at the top. ## Directory Structure Changelog entries live in a dedicated content directory: ``` src/content/docs/ └── changelog/ ├── _category_.json # Category config with desc sort ├── index.mdx # Category index page ├── 0.2.0.mdx # Newer entry (sidebar_position: 2) └── 0.1.0.mdx # Older entry (sidebar_position: 1) ``` ## Category Configuration The `_category_.json` file configures descending sort order so newer entries appear first in the sidebar: ```json title="_category_.json" { "label": "Changelog", "position": 10, "sortOrder": "desc" } ``` With `sortOrder: "desc"`, entries with higher `sidebar_position` values appear first. This means newer versions naturally sort to the top. ## Adding a New Entry To add a new changelog entry: 1. Create a new MDX file in `src/content/docs/changelog/` named after the version (e.g., `0.2.0.mdx`) 2. Set the `sidebar_position` to a value higher than the previous entry 3. Mirror the file in the Japanese content directory (`src/content/docs-ja/changelog/`) ```mdx title="src/content/docs/changelog/0.2.0.mdx" --- title: "0.2.0" description: Short summary of this release. sidebar_position: 2 --- Summary of changes in this release. ### Features - Feature A - Feature B ### Bug Fixes - Fix for issue X ``` Use incrementing `sidebar_position` values for each new version. Combined with `sortOrder: "desc"` in the category config, this ensures the newest entry always appears at the top of the sidebar. ## Entry Format Each changelog entry is a standard MDX file. A recommended structure: - **Title**: The version number (e.g., `"0.2.0"`) - **Description**: A brief summary of the release - **Content sections**: Features, Bug Fixes, Breaking Changes, etc. There is no enforced format — use whatever structure fits your project. The examples above follow a common convention similar to [Keep a Changelog](https://keepachangelog.com/). ## Version Bump Script zudo-doc includes a `scripts/version-bump.sh` script that automates version management: ```bash # Bump version and create changelog entry ./scripts/version-bump.sh 0.2.0 # Bump version, create changelog entry, and snapshot current docs ./scripts/version-bump.sh 1.0.0 --snapshot ``` The script performs the following steps: 1. Updates the `version` field in `package.json` 2. Creates a changelog entry MDX file in both English and Japanese directories 3. Sets the correct `sidebar_position` automatically (incremented from existing entries) ### Doc Snapshots When called with `--snapshot`, the script also archives the current documentation as a versioned snapshot before bumping. This integrates with zudo-doc's [versioning system](/docs/guides/versioning): 1. Copies `src/content/docs/` to `src/content/docs-v{old}/` 2. Copies `src/content/docs-ja/` to `src/content/docs-v{old}-ja/` 3. Prints the version config entry to add to `src/config/settings.ts` The `--snapshot` flag archives the **old** version's docs, not the new version. After the script runs, `src/content/docs/` represents the new version and the snapshot preserves the previous state. ## Version Bump Skill For [Claude Code](https://claude.com/claude-code) users, zudo-doc includes a `/zudo-doc-version-bump` skill that orchestrates the entire release workflow. Instead of running the script manually, the skill handles everything end-to-end: 1. Analyzes commits since the last git tag and categorizes them (breaking, features, fixes, other) 2. Proposes a version bump type (major/minor/patch) based on the changes 3. Runs `version-bump.sh` to update `package.json` and create changelog entries 4. Fills in the changelog templates with actual commit details (both EN and JA) 5. Runs `pnpm b4push` to validate the build 6. Commits, pushes, and waits for CI 7. Creates a git tag and GitHub release 8. Guides you through npm publishing (or skips it for private packages) ```bash # Run the skill in Claude Code /zudo-doc-version-bump # Or skip the proposal step by specifying the bump type /zudo-doc-version-bump patch ``` The skill requires at least one `v*` tag to exist. If this is the first release, create the initial tag manually: `git tag v0.1.0 && git push --tags`. ## Header Navigation The changelog section is linked from the header navigation. This is configured in `src/config/settings.ts`: ```ts headerNav: [ // ...other items { label: "Changelog", labelKey: "nav.changelog", path: "/docs/changelog", categoryMatch: "changelog" }, ], ``` ## i18n Mirror changelog entries in the Japanese content directory (`src/content/docs-ja/changelog/`) to provide translated release notes. The `_category_.json` and directory structure should match the English version. --- # AI Assistant > Source: /pj/zudo-doc/docs/guides/ai-assistant ## Overview The AI assistant adds a chat dialog to your documentation site. Users can click the sparkle icon in the header to open the dialog and ask questions about the documentation content. The assistant uses the full documentation text (generated by the [llms.txt integration](/docs/guides/llms-txt)) as context, so it can answer questions about any page on the site. ## Enabling the Assistant Set `aiAssistant` to `true` in `src/config/settings.ts`: ```ts title="src/config/settings.ts" aiAssistant: true, // ... }; ``` When enabled: - A sparkle icon appears in the header bar - The `@astrojs/node` adapter is added automatically (the site switches from static to hybrid mode) - The `POST /api/ai-chat` endpoint becomes available ## Environment Setup Create a `.env` file (see `.env.example`): ```env title=".env" # "local" uses Claude Code CLI, "remote" uses Anthropic API AI_CHAT_MODE=local ``` ### Local Mode Uses the [Claude Code CLI](https://code.claude.com/docs/en/overview) (`claude -p`) as the backend. No API key needed — uses your existing Claude Code authentication. ```env AI_CHAT_MODE=local ``` This is the simplest setup for local development. ### Remote Mode Calls the Anthropic Messages API directly. Requires an API key. ```env AI_CHAT_MODE=remote ANTHROPIC_API_KEY=sk-ant-... ``` Uses `claude-haiku-4-5-20251001` for fast, low-cost responses. ## Chat Dialog The dialog is a React island (`src/components/ai-chat-modal.tsx`) using the native `` element. ### Layout - **Narrow viewports** (below `lg`/1024px): Full viewport width and height - **Wide viewports** (1024px and above): Centered, 90vw/90vh with a max width of 52.5rem, with a border ### Features - Balloon-style message bubbles (user on right, assistant on left) - Markdown rendering in assistant responses (bold, italic, code, lists, links) - "Thinking..." indicator during API calls - Error messages displayed inline - Backdrop click or Escape to close - Conversation resets on close ## MSW Mock (Development Only) For UI development without a real backend, enable MSW (Mock Service Worker). MSW is **dev-only** — it never runs in production builds. **Setup:** 1. Generate the service worker file (one-time): ```bash npx msw init public/ --save ``` 2. Set the environment variable: ```env title=".env" PUBLIC_ENABLE_MOCKS=true ``` 3. Run the dev server (`pnpm dev`). The mock handler (`src/mocks/handlers.ts`) returns a predefined response with a simulated delay. This is useful for styling and testing the chat UI without consuming API credits. MSW requires `aiAssistant: true` in settings. The generated `public/mockServiceWorker.js` file is gitignored — each developer generates it locally. ## API Reference For the full endpoint specification (request/response types, error codes, environment variables), see the [AI Assistant API reference](/docs/reference/ai-assistant-api). ## Cloudflare Worker (Standalone API) For deploying the chat API as a standalone Cloudflare Worker (separate from the Astro site), see the [`packages/ai-chat-worker/`](/docs/reference/ai-chat-worker) sub-package. The Worker fetches `llms-full.txt` from your deployed documentation site and uses it as context for Claude API calls — useful when you want to host the API independently or deploy the docs as a static site. ## File Structure ``` src/ ├── components/ │ ├── ai-chat-modal.tsx # React island — chat dialog UI │ └── mock-init.tsx # MSW initializer (dev only) ├── mocks/ │ ├── handlers.ts # MSW mock response handlers │ ├── browser.ts # MSW browser worker setup │ ├── server.ts # MSW node server setup │ └── init.ts # Conditional MSW initialization ├── pages/ │ └── api/ │ └── ai-chat.ts # Server endpoint (local/remote modes) ├── types/ │ └── ai-chat.ts # ChatMessage, AiChatRequest/Response types └── utils/ └── render-markdown.ts # Lightweight markdown-to-HTML renderer ``` --- # Layout Pattern Demos > Source: /pj/zudo-doc/docs/guides/layout-demos Live examples of different layout configurations using `hide_sidebar` and `hide_toc` frontmatter options. --- # Claude > Source: /pj/zudo-doc/docs/claude Claude Code configuration reference. ## Resources --- # doc-reviewer > Source: /pj/zudo-doc/docs/claude-agents/doc-reviewer **Model:** `sonnet` # Doc Reviewer Agent You are a documentation reviewer agent. Your job is to review MDX documentation files for quality. ## Review Checklist - Is the title clear and descriptive? - Does the content match the title? - Are code examples correct and runnable? - Is the writing concise and free of jargon? ## Output Format Provide feedback as a numbered list of suggestions, grouped by severity: - **Critical**: Errors that would confuse readers - **Suggestion**: Improvements that would help clarity --- # check-docs > Source: /pj/zudo-doc/docs/claude-commands/check-docs Review all MDX files in `src/content/docs/` for: 1. Broken internal links 2. Missing frontmatter (`title` is required) 3. Invalid MDX syntax (unescaped `<`, `{`, `}`) 4. Missing `sidebar_position` on index pages Report findings as a checklist. --- # check-docs > Source: /pj/zudo-doc/docs/claude-skills/check-docs # Check Docs Scan documentation files for common issues and report problems found. ## When to Use - Before publishing or merging documentation changes - As part of a review workflow to catch broken links - When reorganizing docs structure to verify no links broke ## How It Works 1. Build the site with `pnpm build` 2. Run the link checker with `pnpm check:links` to verify all internal links in the built output 3. Scan all `.mdx` and `.md` files in the configured `docsDir` 4. Check for broken internal links (references to pages that don't exist) 5. Check for missing frontmatter fields (`title` is required) 6. Report findings as a summary The recommended command to run all checks: ```bash pnpm build && pnpm check:links ``` ## Example Usage ```bash /check-docs ``` This skill runs `pnpm build && pnpm check:links` to build the site and verify all links. ## Checks Performed - **Broken links (build output)** — `pnpm check:links` scans the built HTML for broken internal links - **Broken links (source)** — Internal `[text](./path)` links that point to non-existent files - **Missing title** — MDX files without a `title` in frontmatter - **Empty files** — Files with no content after frontmatter - **Duplicate sidebar_position** — Multiple files in the same category with the same position value --- # example-skill > Source: /pj/zudo-doc/docs/claude-skills/example-skill ## File Structure ``` example-skill/ ├── SKILL.md ├── scripts/ │ └── validate.sh ├── references/ │ ├── best-practices.md │ └── skill-anatomy.md └── assets/ └── skill-template.md ``` - [references/best-practices.md](./ref-best-practices) - [references/skill-anatomy.md](./ref-skill-anatomy) - [assets/skill-template.md](./asset-skill-template) # Example Skill This is a sample skill to verify that the Claude Resources integration generates skill documentation correctly. ## When to Use - When testing the `claude-resources` integration - As a template for creating new skills ## How It Works 1. The skill is discovered from `.claude/skills/example-skill/SKILL.md` 2. Frontmatter (`name`, `description`) is extracted 3. The body content is rendered as an MDX page ## Example Usage ```bash /example-skill ``` ## Notes - Skills can include `scripts/`, `assets/`, and `references/` subdirectories - The `description` frontmatter is shown in the skills index page --- # l-generator-cli-tester > Source: /pj/zudo-doc/docs/claude-skills/l-generator-cli-tester # Generator CLI Pattern Tester Test a single `create-zudo-doc` CLI generation pattern by scaffolding a project, building it, running the dev server briefly, and verifying the expected files and settings. ## Usage ``` /l-generator-cli-tester /l-generator-cli-tester --headless ``` Where `` is one of the test patterns listed below. ### Options - `--headless` — After standard checks, also run headless browser verification using `/headless-browser` to confirm pages actually render (Step 8.5). Without this flag, headless checks are skipped. ## Test Patterns | Pattern | Description | |---------|-------------| | `barebone` | Everything OFF — minimal project | | `search` | Only search enabled | | `i18n` | Only i18n enabled | | `sidebar-filter` | Only sidebar filter enabled | | `claude-resources` | Only claude resources enabled | | `color-tweak-panel` | Only color tweak panel enabled (uses API) | | `light-dark` | Light-dark color mode | | `lang-ja` | Japanese as default language | | `all-features` | Everything ON | ## Step 0: Build the CLI Before running any test, set `REPO_ROOT` and build the CLI: ```bash REPO_ROOT=$(git rev-parse --show-toplevel) cd packages/create-zudo-doc && pnpm build ``` If the build fails, stop and report the error. ## Step 1: Create Temp Directory ```bash mkdir -p __inbox/generator-test- ``` ## Step 2: Run the Generator Set `REPO_ROOT` to the repository root (absolute path). Run the generator from within the temp directory. Always use `--no-install` to handle installation separately. ### CLI Commands per Pattern **barebone:** ```bash cd __inbox/generator-test-barebone && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --no-sidebar-filter --no-i18n --no-claude-resources \ --color-scheme-mode single --scheme "Default Dark" --no-install ``` **search:** ```bash cd __inbox/generator-test-search && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --search --no-sidebar-filter --no-i18n --no-claude-resources \ --color-scheme-mode single --scheme "Default Dark" --no-install ``` **i18n:** ```bash cd __inbox/generator-test-i18n && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --no-sidebar-filter --i18n --no-claude-resources \ --color-scheme-mode single --scheme "Default Dark" --no-install ``` **sidebar-filter:** ```bash cd __inbox/generator-test-sidebar-filter && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --sidebar-filter --no-i18n --no-claude-resources \ --color-scheme-mode single --scheme "Default Dark" --no-install ``` > Note: Sidebar filter stripping is not yet implemented (TODO in strip.ts). The filter is built into `sidebar-tree.tsx` and is always included regardless of the flag. This test mainly verifies the flag doesn't cause errors. **claude-resources:** ```bash cd __inbox/generator-test-claude-resources && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --no-sidebar-filter --no-i18n --claude-resources \ --color-scheme-mode single --scheme "Default Dark" --no-install ``` **color-tweak-panel:** > `colorTweakPanel` has NO CLI flag. Use the programmatic API instead: ```bash cd __inbox/generator-test-color-tweak-panel && \ node --input-type=module -e " await createZudoDoc({ projectName: 'test-project', colorSchemeMode: 'single', singleScheme: 'Default Dark', features: ['colorTweakPanel'], packageManager: 'pnpm', install: false, }); console.log('Scaffolding complete.'); " ``` **light-dark:** ```bash cd __inbox/generator-test-light-dark && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --no-sidebar-filter --no-i18n --no-claude-resources \ --color-scheme-mode light-dark --light-scheme "Default Light" --dark-scheme "Default Dark" \ --no-install ``` **lang-ja:** ```bash cd __inbox/generator-test-lang-ja && \ node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \ --no-search --no-sidebar-filter --no-i18n --no-claude-resources \ --lang ja --color-scheme-mode single --scheme "Default Dark" --no-install ``` **all-features:** ```bash cd __inbox/generator-test-all-features && \ node --input-type=module -e " await createZudoDoc({ projectName: 'test-project', colorSchemeMode: 'light-dark', lightScheme: 'Default Light', darkScheme: 'Default Dark', defaultMode: 'dark', respectPrefersColorScheme: true, features: ['i18n', 'search', 'sidebarFilter', 'claudeResources', 'colorTweakPanel'], packageManager: 'pnpm', install: false, }); console.log('Scaffolding complete.'); " ``` > Note: `all-features` uses the API because `colorTweakPanel` has no CLI flag. ## Step 3: Install Dependencies ```bash cd __inbox/generator-test-/test-project && pnpm install ``` If installation fails, report the error and stop. ## Step 4: Build ```bash cd __inbox/generator-test-/test-project && pnpm build ``` If the build fails, report the error and stop. ## Step 5: Dev Server Smoke Test Start the dev server, wait for startup, check it didn't crash, then kill it: ```bash cd __inbox/generator-test-/test-project && \ timeout 15 pnpm dev 2>&1 & DEV_PID=$! sleep 8 if kill -0 $DEV_PID 2>/dev/null; then echo "DEV_SERVER: OK — process still running" kill $DEV_PID 2>/dev/null wait $DEV_PID 2>/dev/null else wait $DEV_PID EXIT_CODE=$? echo "DEV_SERVER: FAILED — process exited with code $EXIT_CODE" fi ``` If the dev server crashed, report the error. ## Step 6: Verify Files Check that expected files exist or don't exist in `__inbox/generator-test-/test-project/`. ### File Expectations per Pattern Use these tables to verify. Check each file with `test -e `. **barebone** — minimal, everything stripped: | File | Expected | |------|----------| | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | ABSENT | | `src/pages/ja/` | ABSENT | | `src/content/docs-ja/` | ABSENT | | `src/integrations/claude-resources/` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | | `src/components/color-tweak-export-modal.tsx` | ABSENT | | `src/components/doc-history.tsx` | ABSENT | | `src/components/ai-chat-modal.tsx` | ABSENT | | `src/integrations/doc-history.ts` | ABSENT | | `src/integrations/llms-txt.ts` | ABSENT | | `src/integrations/sitemap.ts` | ABSENT | | `src/pages/docs/` | PRESENT | | `src/content/docs/` | PRESENT | | `src/config/settings.ts` | PRESENT | | `astro.config.ts` | PRESENT | **search:** | File | Expected | |------|----------| | `src/components/search.astro` | PRESENT | | `src/components/language-switcher.astro` | ABSENT | | `src/integrations/claude-resources/` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | **i18n:** | File | Expected | |------|----------| | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | PRESENT | | `src/pages/ja/` | PRESENT | | `src/content/docs-ja/` | PRESENT | | `src/integrations/claude-resources/` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | **sidebar-filter:** | File | Expected | |------|----------| | `src/components/search.astro` | ABSENT | | `src/components/sidebar-tree.tsx` | PRESENT | | `src/components/language-switcher.astro` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | **claude-resources:** | File | Expected | |------|----------| | `src/integrations/claude-resources/` | PRESENT | | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | **color-tweak-panel:** | File | Expected | |------|----------| | `src/components/color-tweak-panel.tsx` | PRESENT | | `src/components/color-tweak-export-modal.tsx` | PRESENT | | `src/config/color-tweak-presets.ts` | PRESENT | | `src/utils/color-convert.ts` | PRESENT | | `src/utils/export-code.ts` | PRESENT | | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | **light-dark:** | File | Expected | |------|----------| | `src/components/theme-toggle.tsx` | PRESENT | | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | ABSENT | | `src/components/color-tweak-panel.tsx` | ABSENT | **lang-ja:** | File | Expected | |------|----------| | `src/components/search.astro` | ABSENT | | `src/components/language-switcher.astro` | ABSENT | | `src/components/theme-toggle.tsx` | ABSENT | | `src/pages/docs/` | PRESENT | | `src/content/docs/` | PRESENT | **all-features:** | File | Expected | |------|----------| | `src/components/search.astro` | PRESENT | | `src/components/language-switcher.astro` | PRESENT | | `src/pages/ja/` | PRESENT | | `src/content/docs-ja/` | PRESENT | | `src/integrations/claude-resources/` | PRESENT | | `src/components/theme-toggle.tsx` | PRESENT | | `src/components/color-tweak-panel.tsx` | PRESENT | | `src/components/color-tweak-export-modal.tsx` | PRESENT | | `src/config/color-tweak-presets.ts` | PRESENT | ## Step 7: Verify Settings Read `__inbox/generator-test-/test-project/src/config/settings.ts` and check: ### Settings Expectations per Pattern **barebone:** - `colorScheme: "Default Dark"` - `colorMode: false` - `locales: {}` (empty) - `colorTweakPanel: false` - `claudeResources: false` **search:** - `colorScheme: "Default Dark"` - `colorMode: false` **i18n:** - `locales:` should contain `ja` entry with `dir: "src/content/docs-ja"` **sidebar-filter:** - Same as barebone but with `sidebarFilter` defaulting to included **claude-resources:** - `claudeResources:` should be truthy (object with `claudeDir`) **color-tweak-panel:** - `colorTweakPanel: true` **light-dark:** - `colorMode:` should be an object with `defaultMode`, `lightScheme: "Default Light"`, `darkScheme: "Default Dark"` **lang-ja:** - `colorScheme: "Default Dark"` - Check `src/config/i18n.ts` for `defaultLocale = "ja"` - Check `astro.config.ts` for `defaultLocale: "ja"` **all-features:** - `colorMode:` should be an object (light-dark mode) - `locales:` should contain `ja` entry - `claudeResources:` should be truthy - `colorTweakPanel: true` ## Step 8: Compare Against Showcase For the feature being tested, briefly compare the generated project against the main zudo-doc showcase: - Read the equivalent component/config in `src/` (the showcase) and in the generated project - Verify they share the same structure (the generated version may have stripped imports/features, but the enabled feature's code should match) This is a sanity check, not a full diff. Focus on the feature under test. ## Step 8.5: Headless Browser Check (only with `--headless`) **Skip this step unless `--headless` was passed.** Start the dev server and use `/headless-browser` (Tier 1: headless-check.js) to verify pages actually render in a browser. ### 8.5a. Start dev server ```bash cd __inbox/generator-test-/test-project npx astro dev --port 14350 & DEV_PID=$! sleep 6 ``` ### 8.5b. Check pages with headless browser Check the index page and a docs page: ```bash HC=~/.claude/skills/headless-browser/scripts/headless-check.js node $HC --url "http://localhost:14350/" --screenshot viewport --no-block-resources node $HC --url "http://localhost:14350/docs/getting-started" --screenshot viewport --no-block-resources ``` For **i18n** and **all-features** patterns, also check the Japanese page: ```bash node $HC --url "http://localhost:14350/ja/docs/getting-started" --screenshot viewport --no-block-resources ``` ### 8.5c. Verify results - All pages should return `statusCode: 200` - `pageErrors` should be empty (no JS errors) - `networkErrors.failedRequests` — ignore `net::ERR_ABORTED` (Vite HMR re-optimization, normal in dev). Flag any other failures. - **Read the screenshots** with the Read tool and visually confirm: - **search**: search icon (magnifying glass) visible in header - **i18n**: "EN / JA" language switcher in header - **light-dark**: theme toggle icon in header - **color-tweak-panel**: color tweak icon in header - **claude-resources**: page renders without errors - **all-features**: all icons present (search, theme toggle, language switcher, color tweak) - **barebone**: no extra icons in header (no search, no theme toggle, no language switcher) - **lang-ja**: Japanese content ("ようこそ" title) ### 8.5d. Kill dev server ```bash kill $DEV_PID 2>/dev/null; wait $DEV_PID 2>/dev/null ``` ## Step 9: Clean Up ```bash rm -rf ./__inbox/generator-test- ``` Always use relative path with `./` prefix for cleanup. ## Step 10: Report Results Provide a clear pass/fail report: ``` ## Pattern: ### Scaffold: PASS/FAIL ### Install: PASS/FAIL ### Build: PASS/FAIL ### Dev Server: PASS/FAIL ### File Verification: PASS/FAIL - [list any unexpected files present/absent] ### Settings Verification: PASS/FAIL - [list any mismatches] ### Showcase Comparison: PASS/FAIL - [notes] ### Headless Browser: PASS/FAIL/SKIPPED - [only if --headless was passed] ### Overall: PASS/FAIL ``` ## Important Notes - Always `cd` back to the repo root between major steps (use absolute paths) - The `--yes` flag auto-fills all unspecified options with defaults. Feature defaults with `--yes`: search=true, sidebarFilter=true, i18n=false, claudeResources=false, colorTweakPanel=false - Use `--no-install` with CLI to prevent auto-install, then install manually for better error visibility - Sidebar filter stripping is TODO — the filter is always included regardless of the `--sidebar-filter` flag - `colorTweakPanel` has no CLI flag — use the API approach for `color-tweak-panel` and `all-features` patterns - The dev server smoke test uses `pnpm dev` (generated projects have a single `dev` script) - If any step fails, still report all steps attempted before stopping - The `--headless` flag enables Step 8.5 (headless browser visual check). Without it, only process-level checks are performed --- # l-run-generator-cli-whole-test > Source: /pj/zudo-doc/docs/claude-skills/l-run-generator-cli-whole-test # Generator CLI Whole Test Runner Run ALL `create-zudo-doc` generator patterns end-to-end, fix any failures, and verify everything passes. ## When to Use - Before releasing a new version of `create-zudo-doc` - After modifying generator source files (`scaffold.ts`, `strip.ts`, `settings-gen.ts`) - After adding/removing features from the main zudo-doc project - User says "run all generator tests", "whole test", "l-run-generator-cli-whole-test" ### Options - `--headless` — Pass `--headless` to each `/l-generator-cli-tester` invocation, enabling headless browser checks (visual rendering verification via `/headless-browser`). Without this flag, only process-level checks are performed. ## Prerequisites Build the CLI before testing: ```bash cd packages/create-zudo-doc && pnpm build ``` If the build fails, fix the TypeScript errors first before proceeding. ## Phase 1: Run All Test Patterns Run each pattern by invoking `/l-generator-cli-tester `. Start with `barebone` as the baseline. ### Test order Run in this order (CLI flags and details are defined in `/l-generator-cli-tester`): 1. **`barebone`** — All optional features OFF. Must pass first — if this fails, fix it before testing others. 2. **`search`** — Only search enabled 3. **`i18n`** — Only i18n enabled 4. **`sidebar-filter`** — Only sidebar filter enabled 5. **`claude-resources`** — Only Claude Resources enabled 6. **`color-tweak-panel`** — Only color tweak panel enabled (uses API, no CLI flag) 7. **`light-dark`** — Light-dark color scheme mode 8. **`lang-ja`** — Japanese as default language 9. **`all-features`** — Everything ON, maximum complexity (uses API) ### Running each pattern For each pattern, invoke the companion skill: ``` /l-generator-cli-tester /l-generator-cli-tester --headless # if --headless was passed to this skill ``` This skill handles scaffold generation, `pnpm install`, `pnpm build`, `pnpm dev` smoke test, feature verification, and optionally headless browser rendering checks for one pattern. ### Collect results Track results in a summary table as you go: ``` | Pattern | Build | Dev | Features | Status | |--------------------|-------|-----|----------|--------| | barebone | PASS | PASS| PASS | ok | | search | FAIL | - | - | FAIL | | i18n | PASS | PASS| PASS | ok | | ... | ... | ... | ... | ... | ``` Record the first error message for any failing pattern. ## Phase 2: Fix Bugs For each failing pattern: ### 2a. Diagnose the failure - Read the error output from `/l-generator-cli-tester` - Determine which phase failed: scaffold, build, dev, or feature check - Common failure categories: - **Build error: missing module** — dependency not in generated `package.json` → fix `scaffold.ts` `generatePackageJson()` - **Build error: import not found** — import not stripped for disabled feature → fix `strip.ts` - **Build error: type error in settings.ts** — settings field missing/wrong → fix `settings-gen.ts` - **Build error: component references stripped component** — template usage not stripped → fix `strip.ts` patch patterns - **Dev server crash** — runtime error in generated code → read the generated file and trace the issue to the source - **Feature check fail** — feature file exists when it should be removed, or missing when it should exist → fix `strip.ts` ### 2b. Read the generator source files The key files to examine: | File | Role | |------|------| | `packages/create-zudo-doc/src/scaffold.ts` | Copies template, generates `package.json` | | `packages/create-zudo-doc/src/strip.ts` | Removes features/imports based on options | | `packages/create-zudo-doc/src/settings-gen.ts` | Generates `settings.ts` | | `packages/create-zudo-doc/src/constants.ts` | Feature definitions and color schemes | | `packages/create-zudo-doc/src/cli.ts` | CLI argument parsing | | `packages/create-zudo-doc/src/api.ts` | Programmatic API | ### 2c. Apply the fix - Edit the appropriate generator source file - Target the root cause in the generator, not the generated output - Keep fixes minimal and focused ### 2d. Rebuild and re-test After each fix: ```bash cd packages/create-zudo-doc && pnpm build ``` Then re-run the failing pattern: ``` /l-generator-cli-tester ``` ### 2e. Commit each fix Commit each fix individually with a descriptive message: ``` fix(create-zudo-doc): fix generation — ``` Examples: - `fix(create-zudo-doc): fix barebone generation — strip remark-directive import when unused` - `fix(create-zudo-doc): fix i18n generation — add missing content.config.ts patching` - `fix(create-zudo-doc): fix light-dark generation — include theme-toggle.tsx in dependencies` ## Phase 3: Final Verification After all fixes are applied: ### 3a. Re-run ALL patterns Run every pattern again from scratch to ensure fixes didn't break other patterns: ``` /l-generator-cli-tester barebone /l-generator-cli-tester search /l-generator-cli-tester i18n /l-generator-cli-tester sidebar-filter /l-generator-cli-tester claude-resources /l-generator-cli-tester color-tweak-panel /l-generator-cli-tester light-dark /l-generator-cli-tester lang-ja /l-generator-cli-tester all-features ``` ### 3b. Run existing unit tests ```bash cd packages/create-zudo-doc && pnpm test ``` All tests must pass. If any fail, fix them and commit. ### 3c. Build the CLI one final time ```bash cd packages/create-zudo-doc && pnpm build ``` ## Phase 4: Summary Output a final report: ``` ## Generator CLI Whole Test Results ### Test Results | Pattern | First Run | After Fixes | Status | |--------------------|-----------|-------------|--------| | barebone | PASS | PASS | ok | | search | FAIL | PASS | fixed | | i18n | PASS | PASS | ok | | sidebar-filter | PASS | PASS | ok | | claude-resources | FAIL | PASS | fixed | | color-tweak-panel | PASS | PASS | ok | | light-dark | PASS | PASS | ok | | lang-ja | PASS | PASS | ok | | all-features | FAIL | PASS | fixed | ### Summary - Patterns tested: 9 - Passed on first try: 6 - Needed fixes: 3 - Unit tests: PASS ### Fixes Applied 1. `scaffold.ts`: Added missing `minisearch` dependency for search pattern 2. `strip.ts`: Fixed claude-resources import stripping regex 3. `strip.ts`: Fixed all-features light-dark theme-toggle handling ### Final Status: ALL PASS ``` ## Important Notes - Always build the CLI (`pnpm build` in `packages/create-zudo-doc`) before testing and after each fix - Fix the generator source code, never the generated output - The `barebone` pattern is the baseline — if it fails, fix it before testing others - Test directories should be placed in `__inbox/` (gitignored) to avoid polluting the repo - Each fix should be a separate commit for clear git history - If a fix for one pattern breaks another, investigate the interaction before committing --- # l-sync-create-zudo-doc > Source: /pj/zudo-doc/docs/claude-skills/l-sync-create-zudo-doc # Sync create-zudo-doc Generator Detect and fix drift between the main zudo-doc project and the `create-zudo-doc` CLI generator. ## When to Use - After adding or removing a feature from zudo-doc - When the drift-detection test fails - Periodically to verify generator health - User says "sync generator", "check generator drift", "l-sync-create-zudo-doc" ## Step 1: Analyze Drift Compare the main project's source files with what the generator produces. ### 1a. Settings drift Read both files and extract setting field names: - **Main**: `src/config/settings.ts` — the canonical settings object - **Generator**: `packages/create-zudo-doc/src/settings-gen.ts` — what gets generated Compare field names. Any field in the main settings that is missing from the generator output is drift. ### 1b. Dependency drift Compare dependencies: - **Main**: `package.json` — root dependencies - **Generator**: `packages/create-zudo-doc/src/scaffold.ts` `generatePackageJson()` — generated deps Check for packages used in the template files (astro.config.ts, components, integrations) that are not included in the generated package.json. ### 1c. Import/strip drift Compare astro.config.ts imports: - **Main**: `astro.config.ts` — all imports (this is the template that gets copied) - **Generator**: `packages/create-zudo-doc/src/strip.ts` — what gets stripped For each conditional import in astro.config.ts (features gated by `settings.X`), verify that either: 1. The feature is always enabled in generated settings AND the dependency is in generated package.json, OR 2. The import is stripped by strip.ts AND the integration file is removed ### 1d. Integration/component drift Check for React islands and integration files that should be stripped when their feature is disabled: - `src/components/*.tsx` — React islands - `src/integrations/*.ts` — Astro integrations For each component/integration gated by a setting, verify strip.ts handles it. ## Step 2: Report Findings Present a clear drift report: ``` ## Settings Drift - Missing in generator: fieldA, fieldB - Extra in generator (not in main): fieldC ## Dependency Drift - Missing from generated package.json: packageX (used by featureY) - Unnecessary in generated package.json: packageZ (feature disabled) ## Import/Strip Drift - Import not stripped: import X from "y" (feature disabled but import remains) - Missing file removal: src/integrations/z.ts (feature disabled but file kept) ## No Drift Detected (if everything is in sync) ``` ## Step 3: Apply Fixes For each drift item found: 1. **Settings drift** → Update `settings-gen.ts` to add missing fields with sensible defaults 2. **Dependency drift** → Update `scaffold.ts` `generatePackageJson()` to add/remove deps 3. **Import/strip drift** → Update `strip.ts` to add stripping patterns 4. **Component drift** → Update `strip.ts` to add `removeIfExists` calls After fixes: 1. Run `cd packages/create-zudo-doc && pnpm build` to verify TypeScript compiles 2. Run `cd packages/create-zudo-doc && pnpm test` to verify tests pass (including drift-detection test) 3. Commit with message: `fix(create-zudo-doc): sync generator with main project` ## Key Files | File | Role | |------|------| | `src/config/settings.ts` | Canonical settings (source of truth) | | `astro.config.ts` | Template that gets copied to scaffolded projects | | `packages/create-zudo-doc/src/settings-gen.ts` | Generates settings.ts | | `packages/create-zudo-doc/src/scaffold.ts` | Generates package.json, copies template | | `packages/create-zudo-doc/src/strip.ts` | Strips disabled features from template | | `packages/create-zudo-doc/src/__tests__/scaffold.test.ts` | Integration tests | --- # zudo-doc-css-wisdom > Source: /pj/zudo-doc/docs/claude-skills/zudo-doc-css-wisdom # zudo-doc CSS & Component Rules **IMPORTANT**: These rules are mandatory for all code changes in this project that touch CSS, Tailwind classes, color tokens, or component markup. Read the relevant section before making changes. ## How to Use Based on the topic, read the specific reference doc: | Topic | File | |-------|------| | Spacing, typography, layout tokens | `src/content/docs/reference/design-system.mdx` | | Component-first methodology | `src/content/docs/reference/component-first.mdx` | | Color tokens, palette, schemes | `src/content/docs/reference/color.mdx` | Read ONLY the file relevant to your task. Apply its rules strictly. ## Quick Rules (always apply) ### Component First (no custom CSS classes) - **NEVER** create CSS module files, custom class names, or separate stylesheets - **ALWAYS** use Tailwind utility classes directly in component markup - The component itself is the abstraction — `.card`, `.btn-primary` are forbidden - Use props for variants, not CSS modifiers ### Design Tokens (no arbitrary values) - **NEVER** use Tailwind default colors (`bg-gray-500`, `text-blue-600`) — they are reset to `initial` - **NEVER** use arbitrary values (`text-[0.875rem]`, `p-[1.2rem]`) when a token exists - **ALWAYS** use project tokens: `text-fg`, `bg-surface`, `border-muted`, `p-hsp-md`, `text-small` - Spacing: `hsp-*` (horizontal), `vsp-*` (vertical) — see design-system.mdx for full list - Typography: `text-caption`, `text-small`, `text-body`, `text-heading` etc. ### Color Tokens (three-tier system) - **Tier 1** (palette): `p0`–`p15` — raw colors, use only when no semantic token fits - **Tier 2** (semantic): `text-fg`, `bg-surface`, `border-muted`, `text-accent` — prefer these - **NEVER** use hardcoded hex values in components - Palette index convention (consistent across all themes): - p1=danger, p2=success, p3=warning, p4=info, p5=accent - p8=muted, p9=background, p10=surface, p11=text primary ### Astro vs React - Default to **Astro components** (`.astro`) — zero JS, server-rendered - Use **React islands** (`client:load`) only when client-side interactivity is needed - Both follow the same utility-class approach --- # zudo-doc-translate > Source: /pj/zudo-doc/docs/claude-skills/zudo-doc-translate # zudo-doc Translation Skill Translate documentation between English and Japanese following project-specific conventions. ## i18n Structure - English docs: `src/content/docs/` — routes at `/docs/...` - Japanese docs: `src/content/docs-ja/` — routes at `/ja/docs/...` - Directory structures must mirror each other exactly (same filenames, same folder hierarchy) - Locale settings: `locales` in `src/config/settings.ts` - Astro i18n config: `astro.config.ts` with `prefixDefaultLocale: false` (English has no prefix, Japanese uses `/ja/`) ## Translation Rules ### Keep in English (do NOT translate) - Component names: ``, ``, ``, ``, ``, ``, ``, `` - Code blocks — code is universal - File paths: `src/content/docs/...`, `.claude/skills/...`, etc. - CLI commands: `pnpm dev`, `pnpm build`, etc. - Technical terms that are standard in English (e.g., component, props, frontmatter, slug) - Frontmatter field keys (`title`, `description`, `sidebar_position`, `category`) ### Translate - Frontmatter field values (e.g., the `title` value, the `description` value) - The `title` prop of admonition components (e.g., ``) - Prose content, headings, list items, table cells (except as noted below) ### Table conventions - In tables with a "Required" column: use **"Yes"** / **"No"** directly, NOT "はい" / "いいえ" — Japanese conversational yes/no is unnatural in technical documentation ### Internal links - Adjust link paths when translating: - En→Ja: `/docs/getting-started` → `/ja/docs/getting-started` - Ja→En: `/ja/docs/getting-started` → `/docs/getting-started` ## File Naming - Japanese files use the **same filenames** as English (e.g., `writing-docs.mdx`) - Only the parent directory differs: `docs/` vs `docs-ja/` - Example: `src/content/docs/guides/writing-docs.mdx` → `src/content/docs-ja/guides/writing-docs.mdx` ## Workflow ### En→Ja Translation 1. Read the English source file from `src/content/docs/` 2. Check if the corresponding Japanese file already exists in `src/content/docs-ja/` - If it exists, read it first — use it as a base and update from the English source rather than overwriting from scratch - If it does not exist, create the file at the equivalent path in `src/content/docs-ja/` 3. Translate the content following the rules above 4. Verify internal links point to `/ja/docs/...` ### Ja→En Translation 1. Read the Japanese source file from `src/content/docs-ja/` 2. Check if the corresponding English file already exists in `src/content/docs/` - If it exists, read it first — use it as a base and update from the Japanese source rather than overwriting from scratch - If it does not exist, create the file at the equivalent path in `src/content/docs/` 3. Translate the content following the rules above 4. Verify internal links point to `/docs/...` (no `/ja/` prefix) ### Post-Translation Checks - Frontmatter keys are unchanged (only values translated) - All admonition component names remain in English - Code blocks are untouched - Internal links use the correct locale prefix - Directory structure mirrors the source language --- # zudo-doc-version-bump > Source: /pj/zudo-doc/docs/claude-skills/zudo-doc-version-bump # /zudo-doc-version-bump Bump the version, generate changelog doc pages, commit, tag, and create a GitHub release. ## Preconditions Before doing anything else, verify ALL of the following. If any check fails, stop and tell the user. 1. Current branch is `main` 2. Working tree is clean (`git status --porcelain` returns empty) 3. At least one `v*` tag exists (`git tag -l 'v*'`). If no tag exists, tell the user to create the initial tag first (e.g. `git tag v0.1.0 && git push --tags`). Find the latest version tag: ```bash git tag -l 'v*' --sort=-v:refname | head -1 ``` ## Analyze changes since last tag Run: ```bash git log ..HEAD --oneline ``` and ```bash git diff ..HEAD --stat ``` Categorize each commit by its conventional-commit prefix: - **Breaking Changes**: commits with an exclamation mark suffix (e.g. `feat!:`) or BREAKING CHANGE in body - **Features**: `feat:` prefix - **Bug Fixes**: `fix:` prefix - **Other Changes**: everything else (`docs:`, `chore:`, `refactor:`, `ci:`, `test:`, `style:`, `perf:`, etc.) ## Propose version bump Based on the changes: - If there are breaking changes → propose **major** bump - If there are features (no breaking) → propose **minor** bump - Otherwise → propose **patch** bump If the user passed an argument (`major`, `minor`, or `patch`), use that directly instead of proposing. Present the proposal to the user: ``` Proposed bump: {current} → {new} ({type}) Breaking Changes: - description (hash) Features: - description (hash) Bug Fixes: - description (hash) Other Changes: - description (hash) ``` Only show sections that have entries. **Wait for user confirmation before proceeding.** If this is a **major** version bump, ask the user whether they want to archive the current docs as a versioned snapshot (i.e. run with `--snapshot`). Explain that this copies the current docs to a versioned directory for the old version. ## Run version-bump.sh Run the existing version bump script to update package.json and create changelog entry files: ```bash ./scripts/version-bump.sh {NEW_VERSION} # Or with snapshot for major bumps: ./scripts/version-bump.sh {NEW_VERSION} --snapshot ``` This script: 1. Updates `version` in `package.json` 2. Creates `src/content/docs/changelog/{NEW_VERSION}.mdx` (EN) 3. Creates `src/content/docs-ja/changelog/{NEW_VERSION}.mdx` (JA) 4. With `--snapshot`: copies current docs to versioned directories and prints settings.ts entry to add ## Fill in changelog content After the script creates the template files, **replace the placeholder content** with the actual categorized changes from the commit analysis. ### English changelog (`src/content/docs/changelog/{NEW_VERSION}.mdx`) ```mdx --- title: {NEW_VERSION} description: Release notes for {NEW_VERSION}. sidebar_position: {value from script} --- Released: {YYYY-MM-DD} ### Breaking Changes - Description (commit-hash) ### Features - Description (commit-hash) ### Bug Fixes - Description (commit-hash) ### Other Changes - Description (commit-hash) ``` ### Japanese changelog (`src/content/docs-ja/changelog/{NEW_VERSION}.mdx`) ```mdx --- title: {NEW_VERSION} description: {NEW_VERSION}のリリースノート。 sidebar_position: {value from script} --- リリース日: {YYYY-MM-DD} ### 破壊的変更 - Description (commit-hash) ### 機能 - Description (commit-hash) ### バグ修正 - Description (commit-hash) ### その他の変更 - Description (commit-hash) ``` Rules: - Only include sections that have entries - Use today's date for the release date - Each entry should be the commit subject with the short hash in parentheses ## Build and test Run the full build and test suite to make sure everything is good: ```bash pnpm b4push ``` If anything fails, fix the issue and re-run. Do not proceed with committing until all checks pass. ## Commit changes Stage and commit **all** version bump changes — include any files modified by b4push formatting fixes: ```bash git add package.json src/content/docs/changelog/{NEW_VERSION}.mdx src/content/docs-ja/changelog/{NEW_VERSION}.mdx # Also stage any other modified files (e.g. formatting fixes from b4push) git diff --name-only | xargs git add git commit -m "chore: Bump version to v{NEW_VERSION}" ``` ## Push and wait for CI Push the commits first (without the tag) and wait for CI to pass: ```bash git push ``` Then check CI status. Use `gh run list --branch main --limit 1 --json status,conclusion,headSha` and verify the `headSha` matches the pushed commit. Poll every 30 seconds, with a **maximum of 10 minutes**. If CI is still running after 10 minutes, ask the user whether to keep waiting or proceed. If CI fails, investigate the failure with `gh run view --log-failed`, fix the issue, commit, and push again. **Do not tag or publish until CI is green.** ## Tag, push tag, and create GitHub release **Ask the user for confirmation before tagging.** ```bash git tag v{NEW_VERSION} git push --tags ``` After pushing the tag, create a GitHub release. Use `awk` to strip only the YAML frontmatter (first `---` to second `---`) from the changelog file: ```bash NOTES=$(awk 'BEGIN{f=0} /^---$/{f++; next} f>=2' src/content/docs/changelog/{NEW_VERSION}.mdx) gh release create v{NEW_VERSION} --title "v{NEW_VERSION}" --notes "$NOTES" ``` ## Publish to npm (if applicable) If the package is **not** marked as `"private": true` in `package.json`, tell the user to publish: ``` The package is ready for npm publishing. Run: pnpm publish (This requires browser-based 2FA and must be done manually.) ``` If the package is `"private": true`, skip this step and inform the user: ``` Package is marked as private — skipping npm publish. ``` ## Done Report the summary: - Version bumped: `{OLD_VERSION}` → `{NEW_VERSION}` - Changelog created (EN + JA) - Git tag: `v{NEW_VERSION}` - GitHub release: link to the release - npm publish status (published / skipped for private package)