# DreamLake — Full documentation > Dockit is the DreamLake documentation shell — a Vike + React + Tailwind v4 component library and site framework with sidebar/TOC/search, MDX authoring conventions, and a versioned Netlify deploy pipeline. Generated from https://dockit.dreamlake.ai. 23 pages. --- Source: https://dockit.dreamlake.ai # Dockit Dockit is DreamLake’s documentation shell for Vike, React, Tailwind v4, and MDX. Write pages with frontmatter; Dockit builds the sidebar, breadcrumbs, table of contents, search, and Markdown copies for agents. ## Start building Follow the [Quickstart](/get-started/quickstart.md) to install the package and wire a working site. If you already have a DreamLake docs site, use the [migration guide](/get-started/migration.md). ## Three places to work | Tab | What belongs here | | --- | --- | | **Dockit** | Setup, authoring, customization, search, and publishing. | | **[Reference](/reference/config.md)** | Configuration fields, exports, and component APIs. | | **[Python Autodoc](/python-autodoc.md)** | Generate Python API pages and include them in a Dockit site. | Dockit and Reference share the Dockit package version. Python Autodoc shows its own pinned source revision. See [versioning](/guides/releases.md) for historical docs builds and version selectors. ## Build and publish | Task | Guide | | --- | --- | | Configure branding, tabs, and sidebar sections | [Site config](/guides/site-config.md) | | Write MDX and organize pages | [Authoring](/guides/authoring.md) | | Customize colors and appearance | [Theming](/guides/theming.md) | | Add full-text search | [Search](/guides/search.md) | | Publish Markdown and an agent skill | [AI-readable docs and skills](/guides/llms.md) | | Preserve builds and configure version switching | [Versioning](/guides/releases.md) | | Review package changes | [Release notes](/get-started/release-notes.md) | ## Look up an API Start with [configuration](/reference/config.md) or the [export index](/reference/exports.md). The [shell reference](/reference/components/shell.md) covers Layout, Sidebar, and TOC; [Topbar](/reference/components/topbar.md) covers tabs and version chips. For MDX content, see [Callout](/reference/components/callout.md), [CodeBlock](/reference/components/code-block.md), [Preview](/reference/components/preview.md), [StatusTable](/reference/components/status-table.md), and [Chip](/reference/components/chip.md). --- Source: https://dockit.dreamlake.ai/get-started/quickstart # Quickstart Install Dockit, wire its renderer and page discovery, then build a static documentation site. Dockit packages the MDX compiler, Tailwind plugin, and syntax highlighting; React and Vike are installed alongside it as peers. ## Requirements - **Node 20+** (Vite 7 requires 20.19 or 22.12+) - **pnpm** (any recent version; the DreamLake workspaces pin `pnpm@10.33.0`) ## Install ```bash pnpm add @dreamlake/dockit pnpm add react@latest react-dom@latest tailwindcss vike@^0.4.260 vike-react @mdx-js/react pnpm add -D vite typescript pagefind @types/react@latest @types/react-dom@latest ``` Peer versions dockit expects (org convention: sites track `react@latest`; the peer ranges are floors, not pins): | Peer | Range | | --- | --- | | `react` / `react-dom` | `>=19.0.0` — use `latest` | | `tailwindcss` | `^4.0.0` — **install it directly** (`pnpm add tailwindcss`); peer auto-install does not happen in Netlify's CI pnpm setup (verified empirically) | | `vike` | `^0.4.210` declared — **use `^0.4.260`**, see below | | `vike-react` | `^0.5.0` | | `@mdx-js/react` | `>=3.0.0` | | `vite` | `>=6.0.0` (optional — only needed for the `/vite` plugin factory) | > **Warning:** Dockit nests Vike's plugin inside the `dockit()` plugin array, and the > `vike prerender` CLI could not detect a nested plugin before 0.4.260 > (verified: 0.4.259 fails, 0.4.260 passes). Dev and `vite build` work on > older versions — the failure only shows up at prerender time, so pin the > floor now rather than debugging it at deploy time. Everything else — `@mdx-js/rollup`, `@tailwindcss/vite`, `@vitejs/plugin-react`, shiki and the remark/rehype plugins — comes in as dockit's own dependencies. You never install them directly, and the `dockit()` plugin factory wires them all. (`tailwindcss` is declared as a peer AND belongs in your own dependencies — strict CI installs, Netlify included, do not auto-install peers, and the shell's classes need it resolvable at your site root.) If you are porting a site off the old template, those are exactly the deps you get to delete — see [Migrate an existing site](/get-started/migration.md). > **Note:** Inside a pnpm workspace, depend on the library with > `"@dreamlake/dockit": "workspace:*"` — this site does exactly that. > See the [Netlify monorepo setup](/guides/releases.md) for the deploy side. ## Verify installation ```bash pnpm exec vite --version # ≥ 7 pnpm exec vike --version # ≥ 0.4.260 node -e "import('@dreamlake/dockit').then(m => console.log(Object.keys(m).length, 'exports'))" ``` ## Site structure Create the following files: ```text my-docs/ vite.config.ts # plugins: [...dockit()] site.config.ts # initDocs(...) — the one place you configure renderer/ +config.ts # re-export dockit's Vike config +onRenderHtml.tsx # thin wrapper over @dreamlake/dockit/server +onRenderClient.tsx # thin wrapper over @dreamlake/dockit/client styles/app.css # tailwind + dockit styles pages/ index/+Page.mdx # your first page ``` Already have a docs site on the hand-rolled DreamLake template? Skip this page and follow [Migrate an existing site](/get-started/migration.md) instead — it maps every old file onto the structure below. ## 1. Vite config The `dockit()` factory returns the entire plugin stack — Tailwind v4, MDX with frontmatter + GFM + shiki dual-theme highlighting, the `?raw`-MDX loader, React, and Vike: ```ts file="vite.config.ts" export default defineConfig({ plugins: [...dockit()], server: { port: 3020 }, }) ``` ## 2. Site config `initDocs` populates the shell's singletons: branding, the page list (from an eager glob), the raw sources for search, sidebar section order, and topbar tabs. See [SiteConfig](/reference/config.md) for every field, and the [site config guide](/guides/site-config.md) for how the pieces fit together. ```ts file="site.config.ts" initDocs({ site: { brand: 'MyProject', subtitle: 'docs', repoUrl: 'https://github.com/me/my-project', docsRepoUrl: 'https://github.com/me/my-docs', docsBranch: 'main', breadcrumbRoot: 'MyProject', url: 'https://docs.my-project.dev', docsPagesPath: 'pages', }, pageMetadata: import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?frontmatter', import: 'default' }), rawPages: import.meta.env.DEV ? import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?raw', import: 'default', }) : undefined, sectionOrder: ['Getting started', 'Guides', 'Reference'], tabs: [], // or a TabDef[] — see /components/topbar }) ``` ## 3. Renderer wrappers Vike discovers hooks by their `+`-prefixed filenames, which cannot live inside a published package — so your site carries three one-liners: ```ts file="renderer/+config.ts" export { default } from '@dreamlake/dockit/vike' ``` ```tsx file="renderer/+onRenderHtml.tsx" // site.config MUST come first — it runs initDocs before anything renders. ``` ```tsx file="renderer/+onRenderClient.tsx" ``` > **Warning:** The `site.config` import must be the first import in both renderer > entries. It runs `initDocs` as a side effect; if the shell renders > first, the sidebar and search see empty data. ## 4. Styles ```css file="styles/app.css" @import "tailwindcss"; @import "@dreamlake/dockit/styles.css"; /* Point Tailwind at your own class usage: */ @source "../pages"; @source "../renderer"; ``` Dockit's stylesheet carries the theme tokens, dark-mode overrides, and its own `@source` directive so Tailwind also scans the library's compiled components. Details in [Theming](/guides/theming.md). ## 5. First page ```mdx file="pages/index/+Page.mdx" --- title: Introduction section: Getting started order: 0 --- # Hello Welcome to the docs. ``` ## Run it ```bash pnpm exec vite # dev server pnpm exec vite build && pnpm exec vike prerender pnpm exec pagefind --site dist/client # build the search index ``` That is the whole site. Next, read the [authoring guide](/guides/authoring.md) that make the sidebar build itself. ## Deploying your site The build is fully static, so any static host works: after `pnpm build`, publish the `docs/dist/client` directory (or your docs package's `dist/client`). On Netlify, point the site's publish directory there and enable branch deploys — every pushed branch then serves a snapshot at `--.netlify.app`, which is what powers versioned docs URLs. For versioned releases, the package ships a copy-in release kit at `node_modules/@dreamlake/dockit/templates/` — `set-version.mjs`, `version-branch.sh`, `versions.example.json`, and `netlify.example.toml`. Copy them in, fill the marked `EDIT ME` block with your Netlify site name, and follow [Releases & versioning](/guides/releases.md) for the full flow. ## Orient your agents Use the [agent setup fragment](/guides/llms.md#orient-your-agents) to describe your docs layout, authoring conventions, and generated skill to coding agents. --- Source: https://dockit.dreamlake.ai/get-started/migration # Migrate an existing site The DreamLake docs sites started life as copies of one template — each carrying its own `components/`, `lib/`, and renderer. Migrating one to `@dreamlake/dockit` means deleting the vendored shell and keeping only what is genuinely site-owned: pages, branding, and deploy config. This playbook comes from a real migration — [lakeshore-workspace PR #36](https://github.com/dreamlake-ai/lakeshore-workspace/pull/36), which removed **5,910 lines and added 119**, with the prerendered HTML byte-identical to the pre-migration baseline (modulo hashed asset filenames). Your site should land in the same shape. ## Prerequisites - Read the [Quickstart](/get-started/quickstart.md) first — migration is "make the site look like that," and every target file is shown there. - **`vike >= 0.4.260`.** Dockit nests Vike's plugin inside the `dockit()` plugin array, and earlier versions' `vike prerender` CLI cannot see a nested plugin (verified: 0.4.259 fails, 0.4.260 passes). Bump the range as part of the migration. - A green pre-migration build. Record the baseline numbers — prerendered page count and pagefind's page/word counts — so you can prove parity afterwards. ## What to delete Everything that was extracted into the library. In lakeshore's case this was 5,371 lines across 24 files: - `components/` — the entire vendored shell (Sidebar, Topbar, TOC, TabStrip, SearchPalette, CodeBlock, Callout, Preview, StatusTable, theming, …) - `lib/` — navigation, tabs, search-index, pagefind-search, and the hooks - `renderer/Layout.tsx` - The bulk of `styles/app.css` — theme tokens, dark-mode overrides, and component styles all live in the library's stylesheet now What stays, because it is site-owned: `pages/**`, `public/**`, `scripts/gen-llms.mjs`, `versions.lock.json`, `netlify.toml`, and any version/release scripts. ## Old → new mapping | Old (vendored template) | New (dockit) | | --- | --- | | `site.config.ts` (plain config object) | A single `initDocs()` call. Branding fields carry over unchanged (`brand`, `subtitle`, `repoUrl`, `docsRepoUrl`, `docsBranch`, `breadcrumbRoot`, `url`, `skill`, `summary`); add `versionChips`, `gitHash`, and `docsPagesPath`. | | `lib/tabs.ts` (`TABS` array) | The `tabs` option of `initDocs` — same `TabDef` shape, moved verbatim. | | `lib/navigation.ts` (`SECTION_ORDER`) | The `sectionOrder` option of `initDocs`, moved verbatim. | | `vite.config.ts` hand-rolled plugin stack (MDX + remark/rehype + shiki + Tailwind + React + Vike) | `plugins: [...dockit()]`. Keep any site-specific prelude — version reads, `define` blocks, `server` options — around it, verbatim. | | `renderer/+config.ts`, `+onRenderHtml.tsx`, `+onRenderClient.tsx` (real implementations) | Three thin re-export wrappers over `@dreamlake/dockit/vike`, `/server`, and `/client`. `site.config` must be the **first** import so `initDocs` runs before render. | | `styles/app.css` (hundreds of lines of tokens and overrides) | Three lines: `@import "tailwindcss"`, `@import "@dreamlake/dockit/styles.css"`, plus `@source` lines for `../pages` and `../renderer`. | The wrappers, config call, and CSS are spelled out file by file in the [Quickstart](/get-started/quickstart.md); the full `initDocs` surface is in the [config reference](/reference/config.md). ## package.json changes Add: ```json "@dreamlake/dockit": "^0.1.0" ``` Bump: ```json "vike": "^0.4.260" ``` Remove the docs-stack deps the `dockit()` factory now provides: `@mdx-js/rollup`, the remark/rehype plugins, `shiki`, `@tailwindcss/vite`, and `@vitejs/plugin-react`. Keep the peers (`react`, `react-dom`, `tailwindcss`, `vike`, `vike-react`, `@mdx-js/react`) and the tooling (`vite`, `typescript`, `pagefind`) — see [Quickstart prerequisites](/get-started/quickstart.md#install) for the exact split. The `build` script is unchanged: `vite build && vike prerender && node scripts/gen-llms.mjs && pagefind --site dist/client`. > **Warning:** A directory-form `file:` override of the package **falsely crashes > `vike prerender`** with a duplicate-vike-plugin assert — the symlinked > directory makes Vike's plugin register twice. It is an artifact of the > override, not a bug in your migration. Run `npm pack` in the library > and point the override at the tarball instead; that matches real npm > resolution and prerenders cleanly. Revert the override before > committing. ## Verification checklist Run the same gauntlet lakeshore's migration did, and compare against your pre-migration baseline: 1. **Full build** — `vite build && vike prerender && node scripts/gen-llms.mjs && pagefind --site dist/client` completes green. 2. **Prerender count parity** — the same number of HTML documents as before (lakeshore: 101), and identical pagefind page/word counts. For full confidence, diff the prerendered HTML against the baseline: it should be byte-identical modulo hashed asset filenames. 3. **`check:llms`** — passes with the committed `skills//` unchanged (pages untouched means generated surfaces untouched). 4. **`tsc --noEmit`** — clean. Deleted shell code means deleted types; any residual import of `components/` or `lib/` surfaces here. 5. **Dev smoke** — `vite` dev server: load `/` plus one page per tab and confirm titles, sidebar, and search all behave. Check version-chip scope during migration: tabs inherit the global chips by default, and `TabDef.versionChips` can replace or hide them per tab. See [scoping chips per tab](/guides/site-config.md#scoping-chips-per-tab). ## Ship it Once green, deploy as usual — Netlify needs no changes beyond the lockfile: run `pnpm install` at the workspace root after the dependency edits, and commit both `package.json` and `pnpm-lock.yaml` together. If your site predates versioned deploys (or its release scripts have drifted), the package ships current copies at `node_modules/@dreamlake/dockit/templates/` — see [Releases & versioning](/guides/releases.md). ## Orient your agents Use the shared [agent setup fragment](/guides/llms.md#orient-your-agents) after the migration. It records the page layout and regeneration checks without duplicating renderer implementation details. --- Source: https://dockit.dreamlake.ai/guides/site-config # Site config A dockit site is wired by **one call** — `initDocs` — made from a side-effect module (conventionally `site.config.ts`) that both renderer entries import **first**, so the config lands before anything renders, on the server and on the client. There is no nav config anywhere: the sidebar, tabs, and search all derive from this call plus page frontmatter. ## The full cookbook A complete wiring example; adapt the routes and section names to your site: ```ts file="site.config.ts" const pageMetadata = import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?frontmatter', import: 'default', }) // Pagefind indexes built HTML in production; keep full raw text in dev only. const rawPages = import.meta.env.DEV ? import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?raw', import: 'default' }) : undefined initDocs({ site: { brand: 'DreamLake', subtitle: 'Dockit', repoUrl: 'https://github.com/dreamlake-ai/dockit-workspace', docsRepoUrl: 'https://github.com/dreamlake-ai/dockit-workspace', docsBranch: 'main', breadcrumbRoot: 'Dockit', url: 'https://dockit.dreamlake.ai', skill: 'dockit', summary: 'Dockit is the DreamLake documentation shell — a Vike + React + Tailwind v4 component library and site framework with sidebar/TOC/search, MDX authoring conventions, and a versioned Netlify deploy pipeline.', versionChips: [{ label: 'dockit', version: __DOCKIT_VERSION__, dropdown: true, manifestUrl: '/dockit-versions.json' }], docsPagesPath: 'docs/pages', }, pageMetadata, rawPages, sectionOrder: ['Getting started', 'Build your site', 'Publishing', 'API', 'Shell', 'Content components', 'Python Autodoc'], tabs: [ { id: 'dockit', label: 'Dockit', numeral: 'I', landing: '/', urlPrefix: '/get-started' }, { id: 'reference', label: 'Reference', numeral: 'II', landing: '/reference/config', urlPrefix: '/reference' }, { id: 'python-autodoc', label: 'Python Autodoc', numeral: 'III', landing: '/python-autodoc', urlPrefix: '/python-autodoc', versionChips: [{ label: 'autodoc-py', version: __AUTODOC_REVISION__, prefix: '' }] }, ], }) ``` The two globs matter: `pages` (module glob) feeds navigation from frontmatter; `rawPages` (`?raw` glob) feeds the dev [search](/guides/search.md) index and the palette's markdown preview. `initDocs` fans out to `defineSiteConfig`, `initNavigation`, `initSearchIndex`, and `initTabs` — all singletons mutated in place, so components keep live references. ## Configuration reference The [configuration reference](/reference/config.md) is the canonical field-by-field API. Start with branding (`brand`, `subtitle`, `repoUrl`), documentation ownership (`docsRepoUrl`, `docsBranch`, `docsPagesPath`), and the public `url`. An empty `repoUrl` hides the GitHub icon; an empty `breadcrumbRoot` hides the root crumb. Set `skill`, `summary`, and `skillTriggers` when generating [AI-readable docs and skills](/guides/llms.md). The examples below cover the configuration choices that need coordination with Vite or the page layout. ## Version chips from Vite define Chips render as `[ label | v ]`. Hardcoding the version would drift, so this site injects it at build time from the real `package.json`, along with the git hash: ```ts file="vite.config.ts" const dockitPkg = JSON.parse( readFileSync(new URL('../packages/dockit/package.json', import.meta.url), 'utf-8'), ) as { version?: string } const DOCKIT_VERSION = dockitPkg.version ?? '0.0.0' const GIT_HASH = (() => { try { return execSync('git rev-parse --short=6 HEAD', { encoding: 'utf-8' }).trim() } catch { return 'dev' } })() export default defineConfig({ define: { __DOCKIT_VERSION__: JSON.stringify(DOCKIT_VERSION), __GIT_HASH__: JSON.stringify(GIT_HASH), }, plugins: [...dockit()], }) ``` TypeScript needs to know the globals exist: ```ts file="env.d.ts" /** Injected via Vite `define` in vite.config.ts. */ declare const __DOCKIT_VERSION__: string declare const __GIT_HASH__: string ``` Then `site.config.ts` just references `__DOCKIT_VERSION__` and `__GIT_HASH__` — single source of truth, no lockfile parsing. A chip with `dropdown: true` opens a popover listing past deploys from `/versions.json` by default. Set `manifestUrl` for an independent product manifest; this site uses `/dockit-versions.json`. Set `prefix: ''` for source revisions such as `main · 247f6ba`, supplied here by the build-time `__AUTODOC_REVISION__` constant. See [Versioning](/guides/releases.md). ### Scoping chips per tab By default the same chips show on every tab. A tab can override them with its own `versionChips`, so a multi-surface site shows only the relevant version on each tab: ```ts tabs: [ // cli-only chip on the CLI tab { id: 'cli', label: 'CLI', numeral: 'I', landing: '/cli', urlPrefix: '/cli', versionChips: [{ label: 'cli', version: __CLI_VERSION__ }] }, // no chip on the design tab { id: 'python-design', label: 'Design', numeral: 'II', landing: '/design', urlPrefix: '/design', versionChips: [] }, // omit the field → inherits site.versionChips { id: 'guides', label: 'Guides', numeral: 'III', landing: '/guides', urlPrefix: '/guides' }, ], ``` Omit `versionChips` to inherit the global list, set `[]` to hide chips on that tab, or pass an array to replace them for that tab only. See the [Configuration reference](/reference/config.md#per-tab-version-chips). ## Tabs Tabs group pages by URL prefix. Give each tab an `id`, visible `label` and `numeral`, a `landing` page, and a `urlPrefix` matching its routes. The [TabDef reference](/reference/config.md) lists the complete shape; the [Topbar](/reference/components/topbar.md#tabstrip) documents the rendered control. Filtering is by **URL prefix, not frontmatter**: `/guides/theming` belongs to the tab whose `urlPrefix` is `/guides`, full stop. The site root `/` and any unknown prefix resolve to the first tab, so orphan pages never leave the sidebar empty. Omit `tabs` (or pass `[]`) for a tabless site — the strip disappears and the sidebar shows every section. ## Section order `sectionOrder` is the authored ordering of sidebar section groups. Sections are minted by frontmatter (`section: Guides` just works); any section not listed here falls to the end. Within a section, pages sort by their frontmatter `order` — see [Authoring pages](/guides/authoring.md). ## Skill triggers `skillTriggers` feeds the generated agent skill's "Use when" clause. The [LLM generator](/guides/llms.md) writes `skills//SKILL.md` with a description an agent matches against before deciding to load the skill — good triggers name concrete tasks, not topics: ```ts skillTriggers: [ 'building or restyling a DreamLake docs site', 'writing +Page.mdx pages with frontmatter', 'configuring initDocs, tabs, or version chips', ], ``` Keep them short, verb-first, and distinct; they are concatenated into the skill description alongside `summary`. --- Source: https://dockit.dreamlake.ai/guides/authoring # Authoring pages A page is a folder: `pages//+Page.mdx` renders at `/` (nested folders nest the URL). Every page starts with a YAML block, and those fields are the whole authoring contract — URL, sidebar, search, and the LLM artifacts all derive from them. There is no nav config to edit. The parsed shape is exported as [`PageMeta`](/reference/config.md). ## Directory layout = URL Every page is a `+Page.mdx` inside a folder; the folder path is the URL: ```text pages/ index/+Page.mdx → / get-started/quickstart/+Page.mdx → /get-started/quickstart api/config/+Page.mdx → /api/config ``` `index` is special — it maps to the site root `/`. ## Frontmatter fields | Field | Type | Default | Effect | | --- | --- | --- | --- | | `title` | `string` | folder name | Sidebar label, ``, breadcrumbs, search result title. | | `section` | `string` | `''` | Sidebar group. New section strings just work — order them via `sectionOrder` in [Site config](/guides/site-config.md). | | `order` | `number` | `99` | Global sort key: sidebar position within the section AND the prev/next sequence. | | `description` | `string` | — | `<meta description>`, search result blurb, llms.txt link blurb. | | `draft` | `boolean` | `false` | "Awaiting review" chip in the sidebar; page stays visible. | | `hidden` | `boolean` | `false` | Out of sidebar/search/prev-next until dev mode (**Cmd+Shift+D**). Still reachable by URL. | | `noindex` | `boolean` | `false` | Emits `noindex, nofollow` robots meta; excluded from pagefind and every [LLM surface](/guides/llms.md). | | `dev` | `boolean` | `false` | Shorthand for `hidden: true` + `noindex: true` — an internal developer note in one flag (studio's convention). | | `tocLevel` | `2 \| 3` | `3` | `2` limits the right rail to H2 headings. | | `fullscreen` | `boolean` | `false` | Full-viewport page: no content column, TOC, or footer. | A standard page: ```mdx --- title: Installation section: Getting started order: 1 description: Install the package and its peers. --- ``` An internal note, invisible to readers, robots, and agents: ```mdx --- title: Release runbook section: Dev order: 90 hidden: true noindex: true --- ``` ### Conventions for `order` Leave gaps (0, 1, 2 … 10, 11 … 20, 21) so pages can slot in without renumbering a whole section. This site numbers sections in tens. > **Warning:** Keep values plain strings — the generator that builds the LLM > artifacts parses frontmatter with a simple flat `key: value` reader. > Nested YAML structures will not survive the trip. ## Sidebar auto-discovery The rules, in full: 1. Pages group by their `section` string. Sections render in the `sectionOrder` you pass to `initDocs`; sections not in that list fall to the end, alphabetically — a new section shows up without any config change. 2. Within a section, pages sort by `order` (missing `order` = 99). Leave gaps between sections' order ranges (this site uses 0–4, 10–13, 20–23, …) so inserting a page never renumbers its neighbors. 3. If the site declares [tabs](/reference/components/topbar.md), the sidebar shows only pages whose URL falls under the active tab's `urlPrefix`. 4. `hidden: true` pages stay out of the sidebar (and prev/next) until the reader toggles dev mode with **Cmd+Shift+D**. > **Note:** Adding a page is: create the folder, write frontmatter, done. Moving a > page is: move the folder. Renaming a section is: change the string in > the pages that use it (and `sectionOrder` if you pinned it). ## Naming conventions - Folder names are kebab-case; they become URL segments. - Keep `section` strings short — they render as uppercase mono labels. - One H1 per page, matching (or elaborating) the frontmatter title. The H1 drives the topbar's brand-to-breadcrumb crossfade. - `##` headings feed the right-rail TOC; keep them scannable and front-loaded (they are also the anchors search results deep-link to — see the [search guide](/guides/search.md)). ## Markdown & MDX Pages are MDX: GitHub-flavored markdown, plus imports and JSX where prose is not enough. The MDX component map restyles every primitive (headings, links, tables, code) to the shell's design — write plain markdown and it comes out right. ### Headings and links - Headings get stable slug ids (rehype-slug); hover a heading to grab its anchor. H2/H3 feed the [TOC](/reference/components/shell.md#table-of-contents) in the right rail (`tocLevel: 2` drops the H3s). - Internal links are root-relative (`/guides/search`) — the client router intercepts them; the LLM generator rewrites them to `.md` twins in the exported artifacts. ### Code fences Fences are highlighted by shiki with dual light/dark themes. The meta string takes a `file="…"` chip: ````mdx ```bash file="terminal" pnpm add @dreamlake/dockit ``` ```` ```bash file="terminal" pnpm add @dreamlake/dockit ``` - The language chip comes from the fence language. - The `:set nu` button toggles line numbers **site-wide** — flip it on one block and every block follows. - Copy grabs the raw text. ### Callouts `> **Note:** ` is in scope without an import (as are ``, > ``, and ``): > > ```mdx > > Markdown works inside callouts. ``` See the [Callout page](/reference/components/callout.md) for variants. ### Tables GFM tables render with the template's hairline borders: ```mdx | Flag | Effect | | --- | --- | | `hidden` | Hidden until Cmd+Shift+D | ``` ### Live examples For component demos, follow the [Preview](/reference/components/preview.md) convention: a real component in `examples/`, imported twice — once as a module to render, once with `?raw` to show its own source: ```mdx ``` ### Images and assets Files in `public/` serve from the site root (`/diagram.png`). Prefer SVG; both themes should read well — test with the theme toggle (see [Theming & CSS](/guides/theming.md)). --- Source: https://dockit.dreamlake.ai/guides/theming # Theming & CSS Every color, radius, and font in the shell resolves through prefixed CSS custom properties declared in `@dreamlake/dockit/styles.css`. The `--doc-template-` prefix isolates the shell from the consuming app's own tokens; restyling the shell is overriding variables — no component edits. ## The consumer stylesheet A dockit site owns exactly one CSS file. This site's is the whole recipe: ```css file="styles/app.css" @import "tailwindcss"; @import "@dreamlake/dockit/styles.css"; /* Tell Tailwind where this site's own utility-class usage lives (the library's dist is covered by the @source inside its styles.css). */ @source "../pages"; @source "../examples"; @source "../renderer"; ``` Line by line: - `@import "tailwindcss"` boots Tailwind v4 — no `tailwind.config.js`, everything is CSS-first. - `@import "@dreamlake/dockit/styles.css"` pulls in the shell's `@theme` token block, dark-mode overrides, and component styles. Order matters: the library must come **after** tailwindcss and **before** your own overrides. - Your `@source` lines point Tailwind at the directories where *your* markup uses utility classes, so those utilities get generated. - The library's stylesheet carries its own `@source "./"` at the top. Because `@source` resolves relative to the file it appears in, and the file ships as `dist/styles.css`, that line makes Tailwind scan the package's compiled JS in `dist/` — classes used only inside dockit components are always emitted, without you listing the package yourself. ## Color tokens Declared in an `@theme` block, so each also mints Tailwind utilities (`bg-doc-template-bg`, `text-doc-template-ink`, …): | Token | Light | Role | | --- | --- | --- | | `--color-doc-template-bg` | `#fffefa` | Page background | | `--color-doc-template-panel` | `#fcfbf7` | Cards, footer panels | | `--color-doc-template-rail` | `#fcfbf7` | Sidebar / rail background | | `--color-doc-template-code` | `#faf8f1` | Code block surface | | `--color-doc-template-ink` | `#1a1a1a` | Primary text | | `--color-doc-template-muted` | `#6b6b6b` | Secondary text | | `--color-doc-template-faint` | `rgb(0 0 0 / 0.08)` | Hairline borders | | `--color-doc-template-faint-strong` | `rgb(0 0 0 / 0.16)` | Stronger borders | | `--color-doc-template-chip` | `rgb(0 0 0 / 0.04)` | Chip fills | | `--color-doc-template-search` | `#f5f4f0` | Search field fill | | `--color-doc-template-selected` | `#d9e6f7` | Active sidebar link | | `--color-doc-template-accent` | `#23aaff` | Links, active TOC, highlights | | `--color-doc-template-accent-soft` | `rgb(35 170 255 / 0.09)` | Accent washes | | `--color-doc-template-warn` | `#d68b3a` | Warnings, dev-mode dot | | `--color-doc-template-warn-soft` | `rgb(214 139 58 / 0.10)` | Warning washes | | `--color-doc-template-rail-stroke` | `#d8d4c8` | TOC rail stroke | Radii: `--radius-doc-template` (10px) and `--radius-doc-template-sm` (6px). ## Dark mode A `[data-theme='dark']` block re-declares the same custom properties — every utility and inline style swaps instantly, with zero re-render. The attribute is stamped on `<html>` by: 1. the FOUC-safe inline script in the server-rendered head (first paint), and 2. `ThemeProvider` afterwards (user choice, `system` tracking, cross-tab sync) — see [ThemeToggle](#themetoggle) below. A `dark:` Tailwind variant is wired to the same attribute for your own pages. Shiki code colors are dual-theme (`github-light` / `github-dark`) via CSS variables, so code follows along. ## Fonts | Token | Stack | Used for | | --- | --- | --- | | `--font-doc-template-ui` | Inter Tight, ui-sans-serif, … | Body, headings, links | | `--font-doc-template-mono` | Fira Code, ui-monospace, … | Code, chips, section labels, breadcrumbs | The server renderer emits a single Google Fonts `<link>` loading Inter Tight + **Fira Code** (the org-wide mono font). ## Re-branding walkthrough Restyling the shell is a stanza in your `app.css`, **after** the library import. Override the light values on `:root` and the dark values on `[data-theme='dark']`: ```css file="styles/app.css" @import "tailwindcss"; @import "@dreamlake/dockit/styles.css"; @source "../pages"; @source "../renderer"; /* Brand: violet accent, tighter radii, your own type. */ :root { --color-doc-template-accent: #7c5cff; --color-doc-template-accent-soft: rgb(124 92 255 / 0.09); --color-doc-template-selected: #e6dffb; --radius-doc-template: 6px; --font-doc-template-ui: "Instrument Sans", ui-sans-serif, sans-serif; } [data-theme='dark'] { --color-doc-template-accent: #a78bff; --color-doc-template-selected: #423a5c; } ``` Every component — sidebar selection, TOC highlight, links, [chips](/reference/components/chip.md) — re-resolves through the variables; there is nothing else to touch. If you swap a font stack, remember to also load the webfont (the renderer's default `<link>` only fetches Inter Tight and Fira Code). ## ThemeToggle The theme control comes in two variants, chosen by the `themeToggle` field in [Site config](/guides/site-config.md): - **`'cycle'`** (default) — a single button that steps **light → system → dark**, with a small spring animation on the icon swap. - **`'segmented'`** — the three-button slider from the uikit/dreamlake docs: every theme visible at once, a pill indicator sliding under the active one, inactive icons leaning ±18°. ```ts file="site.config.ts" initDocs({ site: { themeToggle: 'segmented' /* or 'cycle' */ }, // … }) ``` This site is configured with `'segmented'`, so that is what the topbar shows. Here is another toggle, live — both stay in sync because they share the same `ThemeProvider` state: `` takes no props — the variant is site-level config, not a per-instance choice. It must render inside the shell (or any `ThemeProvider`) — on its own it falls back to inert context defaults. ### How theme state flows 1. `ThemeProvider` (mounted by the [Layout](/reference/components/shell.md)) keeps the choice in localStorage under `doc:theme`, synced across tabs and across every consumer of the hook. 2. The provider stamps `data-theme="light" | "dark"` on `<html>`; every color token above re-resolves instantly. 3. On first paint, a tiny inline script in the server-rendered `<head>` reads the same key **before** hydration, so a dark-mode reader never sees a light flash (FOUC-safe). ### Reading the theme yourself ```tsx function MyWidget() { const { theme, setTheme } = useTheme() // 'light' | 'dark' | 'system' return <button onClick={() => setTheme('dark')}>Go dark ({theme})</button> } ``` --- Source: https://dockit.dreamlake.ai/guides/search # Search Dockit search has two engines behind one UI, `SearchPalette`. Press **⌘K** or **/**, or click the topbar search field. Results update as you type; **↑↓** selects a result, **Enter** opens it, and **Esc** closes the overlay. The right pane previews the selected page as rendered Markdown. This page explains what gets indexed when, how the shell scopes the index, and how authoring choices affect findability. <span id="two-engines-one-component" /> ## Production: pagefind The build's final step indexes the prerendered HTML: ```bash pagefind --site dist/client ``` Pagefind ships a WASM index the browser loads on demand — sub-word matching, excerpts with `<mark>` highlights, and it scales with page count. The shell emits the scoping markers itself; there is nothing to add to your pages: | Marker | Where the shell puts it | Effect | | --- | --- | --- | | `data-pagefind-body` | `main.doc-content` — except on `noindex` pages | Only page content is indexed, never chrome (sidebar, topbar, rails). | | `data-pagefind-ignore` | Breadcrumbs, prev/next footer | Excluded even inside the body. | | `data-pagefind-meta="hidden:true"` | Pages with `hidden: true` | Indexed but filtered from results unless dev mode (**Cmd+Shift+D**) is on. | Pages with `noindex: true` are skipped entirely — the shell omits `data-pagefind-body` on them, and pagefind indexes nothing on a page without that marker, so they never enter the pagefind index. ## Dev: the fallback index `vite dev` has no prerendered HTML, so `initSearchIndex` builds an in-memory index from the raw MDX you pass to `initDocs` (the `?raw` glob — see [Site config](/guides/site-config.md)) — frontmatter stripped, code fences dropped, headings captured for higher-weight ranking. Rank order: title match → heading match → description → section → body occurrences → fuzzy title subsequence. The engine switch is automatic — `searchPages()` tries pagefind and falls back. The palette also uses the raw markdown for its preview pane in both modes. ## Writing searchable pages - **Titles carry the most weight** — name pages what readers will type. - **`description` doubles as the result blurb**; a missing one falls back to body context around the match. - **Headings are the second-strongest signal** — real H2/H3 structure beats bold paragraphs. - Code fences are excluded from the dev index — put key terms in prose too, not only in code. > **Note:** If a result surprises you in dev but not production (or vice versa), > remember the engines differ: pagefind matches sub-words against > rendered HTML; the fallback matches whole substrings against stripped > markdown. ## Wiring The [Layout](/reference/components/shell.md) owns the open state and query, renders the [Topbar](/reference/components/topbar.md) search field, and mounts the palette: ```tsx setSearchOpen(false)} query={query} /> ``` ## Props | Prop | Type | Description | | --- | --- | --- | | `open` | `boolean` | Whether the palette is showing. | | `onClose` | `() => void` | Called on Esc / backdrop click. | | `query` | `string` | The live query (the input lives in the Topbar, not the palette). | ## Behavior notes - `hidden: true` pages are excluded from results unless dev mode (**Cmd+Shift+D**) is on; `noindex` pages never enter the pagefind index at all. - The panel is resizable (drag the bottom edge); split position and height persist in localStorage. - Below 640px the palette collapses to a single column and drops the preview pane. --- Source: https://dockit.dreamlake.ai/guides/llms # AI-readable docs and skills One generator, `scripts/gen-llms.mjs`, reads `pages/**/+Page.mdx` — the same single source of truth the site renders — and derives every LLM consumption surface. Run it after documentation changes and check the committed output in CI to keep the site and artifacts in sync. <span id="other-agent-surfaces" /> ## The surfaces | Surface | Output | Purpose | | --- | --- | --- | | Per-page markdown | `/<path>.md` (this page: `/guides/llms.md`) | Agents fetch exactly one page as clean markdown. | | Index | [`/llms.txt`](/llms.txt) | The [llmstxt.org](https://llmstxt.org) map: brand, summary, every page with a blurb. | | Full corpus | [`/llms-full.txt`](/llms-full.txt) | Whole docs in one file for context stuffing. | | Importable skill | `skills/<name>/{SKILL.md,reference/*.md}` at the repo root (committed) | Drop into a Claude skills directory. | | Skill download | `/skills/<name>.zip` | Same skill, fetchable from the site. | The shell links the surfaces automatically: each page's `<head>` gets `<link rel="alternate" type="text/markdown">` and the TOC rail gets a "View as Markdown" link. ## What the generator does - Strips frontmatter and imports; degrades MDX components to plain markdown (`> **Note:** ` → blockquote, `` → its inner code). > - Rewrites internal links per surface — to `.md` twins on the web, to > `reference/*.md` inside the skill. > - Excludes `noindex` pages from every artifact. > - Reads `brand`, `url`, `summary`, `skill`, and > [`skillTriggers`](/reference/config.md) (the curated "Use when" phrases in the > skill's trigger list) from `site.config.ts`; on Netlify the deploy's > `URL` env var overrides the origin, so previews generate > self-consistent absolute links. > > ## Wiring > > The generator runs between prerender and pagefind (see the build > steps in the [Quickstart](/get-started/quickstart.md)), plus two > scripts: > > ```bash > pnpm gen:llms # regenerate everything > pnpm check:llms # exit 1 if the committed skill drifted from pages/ > ``` > > The committed `skills/<name>/` directory is generated output — run > `pnpm gen:llms` after editing pages, and let `check:llms` guard the > repo in CI. > > > `public/_headers` sets `text/markdown` on the twins and `text/plain` > on the indexes; `public/_redirects` exact-matches them ahead of the > SPA fallback so `curl https://…/guides/llms.md` returns markdown, not > the app shell. <span id="install" /> ## Install the Dockit skill The generated skill supplies a `SKILL.md` entry point and a `reference/*.md` file for each public page. Install it to let an agent consult Dockit setup, authoring, theming, and API guidance locally. **Direct download** — grab [`/skills/dockit.zip`](/skills/dockit.zip) and unzip it into your Claude Code skills directory: ```bash curl -LO https://dockit.dreamlake.ai/skills/dockit.zip unzip dockit.zip -d .claude/skills/dockit/ ``` **Pebble marketplace** — DreamLake distributes its skills org-wide through [dreamlake-ai/pebble](https://github.com/dreamlake-ai/pebble), the canonical Claude Code skills marketplace for the org. If you are inside DreamLake, install from pebble to pick up updates alongside every other org skill. ## Orient your agents Paste this fragment into your repo's `CLAUDE.md` so coding agents know how the docs work without rediscovering it every session: ```markdown ## Docs (dockit) - Doc pages live at `docs/pages/<slug>/+Page.mdx` (Vike file routing — nested folders nest URLs). - Frontmatter: `{ title, section, order, description, draft?, hidden?, noindex?, tocLevel?, fullscreen? }`. - The sidebar auto-derives from frontmatter. Never edit a nav config — there isn't one. - MDX extras: ``, ``, and code fences with `file="…"` for a filename chip. - Site config (brand, tabs, section order) is the `initDocs(...)` call in `docs/site.config.ts`. - The docs build generates agent surfaces: `/llms.txt`, a `.md` twin per page, and a skill at `skills/<name>/` in the repo root — import that skill for deep dockit reference. Browsable at https://dockit.dreamlake.ai/guides/llms. - Run `check:llms` before committing page changes, and commit the regenerated `skills/<name>/` with them. ``` ## What's inside — SKILL.md The committed entry point, verbatim: <pre style={{ maxHeight: '32rem', overflow: 'auto', padding: '1rem 1.25rem', background: 'var(--color-doc-template-code)', border: '1px solid var(--color-doc-template-faint)', borderRadius: 'var(--radius-doc-template-sm)', color: 'var(--color-doc-template-ink)', fontFamily: 'var(--font-doc-template-mono)', fontSize: '0.8125rem', lineHeight: 1.6, whiteSpace: 'pre-wrap', }} > {skillMd} </pre> --- Source: https://dockit.dreamlake.ai/guides/releases # Releases & versioning A dockit site can keep every released version of its docs online, each at its own URL, with an in-site switcher in the topbar. This guide is the consumer-facing playbook: the pieces you need, the scripts that drive them, and the deploy mechanics — using this workspace as the worked example throughout. The library ships a copy-in release kit with all of the pieces below — see [Ship it with your own site](#ship-it-with-your-own-site). ## Two independent version axes Do not conflate these; they move on their own schedules: 1. **Your product/library semver** — the thing the docs document (here: `@dreamlake/dockit`, published to npm). Its version lives in the package's own `package.json` and shows in the topbar's [version chips](/reference/components/topbar.md). A product can have its own dropdown and manifest. 2. **The docs-deploy version** — a version stamp for the docs *site* itself, so readers can pin "the docs as of 0.2.0". It lives in two files: the workspace root `package.json` and the manifest at `docs/public/versions.json`. A docs release can ship without a library release (a big guide lands) and vice versa (a patch release with no doc changes worth freezing). On this site, the **Dockit `v0.2.15`** badge selects `/dockit-versions.json`. That product manifest currently contains only `0.2.15`, linked to the current site. `/versions.json` preserves the separate historical docs-build sequence; it must not relabel the Dockit package badge. The **Python Autodoc** tab displays `main` plus the source revision with `prefix: ''` and no dropdown, because no package release is implied by this documentation integration. ```ts versionChips: [{ label: 'dockit', version: __DOCKIT_VERSION__, dropdown: true, manifestUrl: '/dockit-versions.json', }] // Python Autodoc tab override versionChips: [{ label: 'autodoc-py', version: __AUTODOC_REVISION__, prefix: '' }] ``` `prefix` changes the displayed prefix (default `v`). `manifestUrl` selects a path or an absolute manifest URL; it defaults to `/versions.json`. These settings are documented in the [VersionChip reference](/reference/config.md#versionchip). ## versions.json — the manifest The default docs-build manifest is served at `/versions.json`. A chip with `dropdown: true` consumes this file unless `manifestUrl` selects a product-specific manifest. Both manifests use the same schema: ```json file="docs/public/versions.json" { "current": "0.2.1", "stable": "0.1.0", "aliases": { "latest": "https://dockit.dreamlake.ai", "stable": "https://stable.dockit.dreamlake.ai" }, "versions": [ { "version": "0.2.1", "url": "https://v0-2-1--dockit-docs.netlify.app", "date": "2026-07-20" }, { "version": "0.2.0", "url": "https://v0-2-0--dockit-docs.netlify.app", "date": "2026-07-20" }, { "version": "0.1.0", "url": "https://v0-1-0--dockit-docs.netlify.app" } ] } ``` - **`current`** — the newest release; what the topbar chip labels itself when the hostname gives no better answer. - **`stable`** — the version the `stable` alias points at (may trail `current`). - **`aliases`** — stable URLs that always point somewhere sensible; `latest` is your production domain. - **`versions[]`** — newest first; each entry's `url` is the documentation destination for that version. Only describe it as frozen when its deployment is actually retained unchanged. `date` is optional but useful in the popover. The switcher fetches this manifest at runtime, so a reader on an *old* frozen deploy still sees the full, current version list — the manifest on your production domain is the source of truth. That fetch is cross-origin from a frozen `v/*` deploy, so production must serve the selected manifest with a CORS header (`/versions.json` is already in the `netlify.example.toml` template): ```toml file="netlify.toml" [[headers]] for = "/versions.json" [headers.values] Access-Control-Allow-Origin = "*" ``` Use the same header for `/dockit-versions.json` when a product chip selects that path. Changing one manifest does not change the other. ## set-version.mjs — the bump One script updates both files atomically. This is the actual script this workspace runs (embedded from `scripts/set-version.mjs`, not a copy): <pre style={{ maxHeight: '32rem', overflow: 'auto', padding: '1rem 1.25rem', background: 'var(--color-doc-template-code)', border: '1px solid var(--color-doc-template-faint)', borderRadius: 'var(--radius-doc-template-sm)', color: 'var(--color-doc-template-ink)', fontFamily: 'var(--font-doc-template-mono)', fontSize: '0.8125rem', lineHeight: 1.6, whiteSpace: 'pre-wrap', }} > {setVersionSrc} </pre> ```bash pnpm set-version 0.2.1 ``` Idempotent — re-running refreshes the existing entry's url/date instead of duplicating it. Note the URL it writes: the Netlify branch-deploy address, not a pretty subdomain — see [the URL scheme](#netlify-branch-deploys--the-url-scheme) below for why. ## version-branch.sh — the frozen deploy Each release is frozen by pushing HEAD to a `v<x.y.z>` branch. Again the actual script, embedded from `scripts/version-branch.sh`: <pre style={{ maxHeight: '32rem', overflow: 'auto', padding: '1rem 1.25rem', background: 'var(--color-doc-template-code)', border: '1px solid var(--color-doc-template-faint)', borderRadius: 'var(--radius-doc-template-sm)', color: 'var(--color-doc-template-ink)', fontFamily: 'var(--font-doc-template-mono)', fontSize: '0.8125rem', lineHeight: 1.6, whiteSpace: 'pre-wrap', }} > {versionBranchSrc} </pre> With branch deploys enabled, Netlify builds that branch at its version URL. Keep the branch and deployed artifact unchanged to preserve that release snapshot. ## Netlify branch deploys — the URL scheme Enable branch deploys on your Netlify site (Site settings → Build & deploy → Branches and deploy contexts → "Deploy all branches", plus empty `[context.branch-deploy]` / `[context.deploy-preview]` blocks in `netlify.toml`). Then every pushed branch serves at: ```text https://<branch>--<site-name>.netlify.app ``` with non-alphanumeric characters in the branch name dashed — so branch `v0.2.1` on site `dockit-docs` serves at `v0-2-1--dockit-docs.netlify.app`. **These URLs use Netlify's branch-deploy domain** and need no custom DNS once the corresponding deployment exists. They are what `set-version.mjs` writes into the manifest. A CLI `netlify deploy --alias` creates a named **draft deploy**, not a branch deploy. Netlify advises against reusing a deployed branch name as an alias. The existing `prod` script still includes that legacy alias step; its predictable URL is not evidence of branch-deploy retention. Create real branch deploys through a connected repository with branch deployment enabled. See the [Netlify CLI reference](https://cli.netlify.com/commands/deploy/). Netlify exempts the latest successful deploy of each branch from automatic deploy deletion. Older deploys and draft aliases can be removed under the site's retention policy. Keep each release branch unchanged, verify its successful branch deploy, and retain a separate build archive with its source revision and checksum. A Git branch alone does not save the rendered output. See [Netlify deploy retention](https://docs.netlify.com/deploy/manage-deploys/manage-deploys-overview/#automatic-deploy-deletion). > **Note:** You *can* have `v0-2-1.your-domain.com` instead, but only if your DNS > is on Netlify and you **manually add each branch subdomain** in the > Netlify domain-management UI, once per release — the special > `NETLIFY`-type DNS records this creates cannot be set up via the API, > so it cannot be scripted. Unless you have a strong cosmetic reason, > skip this: the `v<x-y-z>--<site>.netlify.app` form avoids that per-version DNS setup. ## The promotion flow — prod and staging Merging to `main` does not change production here. Production and staging are **promoted from the CLI**, each ending in a force-push to a record branch so the Netlify dashboard reflects what is actually live: ```bash pnpm prod # version-branch → build → netlify deploy --prod → force-push netlify-production pnpm staging # build → netlify deploy --prod (staging site) → force-push netlify-staging ``` Both deploy the prebuilt `docs/dist/client` with `--no-build` to a pinned `--site` id. The typical release, end to end: ```bash pnpm set-version 0.2.1 git commit -am "release: docs 0.2.1" pnpm prod ``` `pnpm staging` is the same promotion aimed at a second Netlify site, with no version bookkeeping — use it to preview a release first. > **Warning:** "> > In a pnpm workspace, a bare `netlify deploy` cannot tell which project > it belongs to and hangs on an interactive project picker. Pin it with > `--filter <your-docs-package>` (this workspace passes `--filter docs`) > — keep the flag if you invoke the CLI by hand. ## The other axis: releasing the library itself Publishing the documented package to npm is deliberately separate from all of the above. This workspace wires it as the `release` pane in `mprocs.yaml`: build the library, then `pnpm publish` with a fresh OTP fetched at publish time. Docs releases never publish npm packages, and npm releases never touch `versions.json`. ## Ship it with your own site `@dreamlake/dockit` ships all four pieces as templates: ```bash ls node_modules/@dreamlake/dockit/templates/ # set-version.mjs version-branch.sh versions.example.json netlify.example.toml ``` Copy them in, then edit the marked `EDIT ME` block (your Netlify site name) at the top of each script: ```bash cp node_modules/@dreamlake/dockit/templates/set-version.mjs scripts/ cp node_modules/@dreamlake/dockit/templates/version-branch.sh scripts/ cp node_modules/@dreamlake/dockit/templates/versions.example.json docs/public/versions.json cp node_modules/@dreamlake/dockit/templates/netlify.example.toml netlify.toml ``` Wire the scripts into your root `package.json`: ```json "scripts": { "set-version": "node scripts/set-version.mjs", "version-branch": "bash scripts/version-branch.sh" } ``` The templates assume the same layout as this workspace — root `package.json` holds the docs-deploy version, the manifest lives at `docs/public/versions.json` — and resolve paths from the directory you invoke them in. --- Source: https://dockit.dreamlake.ai/get-started/release-notes # Release notes ## 0.2.15 — 2026-09-08 (library) - Fix early client navigation by allowing React hydration to be superseded and avoiding hydration of stale server markup. - Add metadata-only MDX imports for navigation and avoid loading raw page bodies for production search. - Fetch search previews only while the palette is open, and cancel stale requests. - Make both navigation rails configurable with wider 280px defaults, and stabilize sidebar labels while scrolling. - Support independent product version manifests. ## 0.2.7 — 2026-08-02 (library) - **Per-tab version chips**: new [`TabDef.versionChips`](/reference/config.md) field scopes the topbar version chips to a top-level tab. A tab omitting the field inherits the global [`versionChips`](/reference/config.md); `[]` shows none on that tab; a non-empty array replaces them for that tab only. Lets a site show cli-only chips on the CLI tab, py-only on the Python tab, and none on design/other tabs. ## 0.2.6 — 2026-07-20 (library) - **Dependency layout**: `react`/`react-dom` peers widened to `>=19` (org convention: sites track `react@latest`); `tailwindcss` moved from a dependency to a `^4` peer so it resolves at the consuming site's root under strict CI installs. ## 0.2.5 — 2026-07-20 (library) - **Chip component**: the canonical badge idiom is now a first-class export — [``](/reference/components/chip.md) with `muted` / `accent` / `warn` variants, clickable (`onClick` renders a real button), and `className`/`style` passthrough for one-off colors. Registered in the MDX component map, so `` works in any page without an import. - The topbar **DEV badge**, the gallery status chips, and the `TestStatus` badge now render through `` — no visual change, the idiom is just defined once. ## 0.2.4 — 2026-07-20 (library) - **Search-engine opt-in**: new [`indexable`](/reference/config.md) field — sites are `noindex, nofollow` by default per org policy; set `indexable: true` on the sites meant to be publicly discoverable. In-site search and the llms/skill surfaces are unaffected. ## 0.2.3 — 2026-07-20 (library) - **DEV badge**: the dev-mode indicator in the topbar is now a labeled warn-colored `DEV` chip (was a small orange dot); clicking it still turns the hidden-pages toggle off. - **`headScripts`** `SiteConfig` field: external script URLs injected into every page head at SSR time — restores integrations like the Studio design-picker bridge for migrating sites. ## 0.2.2 — 2026-07-20 (library) - **All-hidden tabs hide**: a tab whose pages are all `hidden` (e.g. an internal `/dev` tab) no longer renders in the topbar unless the hidden-pages toggle is on or you are already inside it. ## 0.2.0 — 2026-07-20 (library) Fleet features upstreamed from the newest DreamLake docs sites, ahead of the org-wide migration onto dockit: - **Segmented ThemeToggle** (from uikit/dreamlake): new [`themeToggle`](/reference/config.md) `SiteConfig` field — `'cycle'` (default, the existing single cycling button) or `'segmented'` (the three-button slider with a sliding indicator). See [Theming](/guides/theming.md#themetoggle). This site runs `'segmented'`. - **Preview fullscreen** (from uikit): an expand button in the [Preview](/reference/components/preview.md) header grows the frame to a full-viewport overlay for wide demos; Esc exits. Icons are inlined SVGs — no new dependency. - **Trailing-slash-tolerant path matching** (from studio): new `normalizePath` / `findPage` exports, used everywhere active-page matching happens (sidebar `aria-current`, breadcrumbs, `<title>` sync, prev/next, TOC md/edit links) so prerender and client routing agree. See [Sidebar](/reference/components/shell.md#sidebar). - **`dev:` frontmatter alias** (from studio): `dev: true` now implies `hidden: true` + `noindex: true`, so studio content migrates unchanged. See [Authoring](/guides/authoring.md). - **`VersionBadge` exported** standalone (donor: uikit), with the version-switcher manifest now fetched production-origin-first so frozen deploys list current releases (needs the `/versions.json` CORS header — in `netlify.example.toml` and [Releases & versioning](/guides/releases.md)). - CodeBlock header controls (language / `:set nu` / copy) are now hover-gated, keeping prose-adjacent code blocks quiet (#8). ## 0.2.1 — 2026-07-20 (docs) - New [Releases & versioning](/guides/releases.md) guide — the versioned docs-deploy flow as consumer-facing docs: the two version axes, the `versions.json` manifest, the actual `set-version.mjs` / `version-branch.sh` scripts embedded from source, Netlify branch-deploy URLs, and the prod/staging promotion flow. The hidden [Versioning](/guides/releases.md) dev page slims down to workspace-internal details. - The library now ships a copy-in release kit at `templates/` — `set-version.mjs`, `version-branch.sh`, `versions.example.json`, `netlify.example.toml` — referenced from the [Quickstart](/get-started/quickstart.md) and [migration guide](/get-started/migration.md). - `versions.json` URL-scheme fix: per-version entries now point at the canonical Netlify branch-deploy addresses (`v<x-y-z>--dockit-docs.netlify.app`), which resolve without any DNS setup, instead of pretty subdomains that required a manual Netlify domain-management step per release. ## 0.2.0 — 2026-07-20 (docs) - Docs reorganized into the five-tab tree — Get Started, Guides, Components, Skills, Reference — with 301 redirects covering the old URLs (see [Netlify](/guides/releases.md)). Deploy internals (Netlify, versioning) moved into the hidden Dev cluster; the [dockit skill](/guides/llms.md) got its own page. - New [migration guide](/get-started/migration.md) for moving an existing docs site onto dockit. - Component gallery added under the hidden Dev section. - Skill-generation upgrades in `gen-llms.mjs`: curated "Use when" triggers via the [`skillTriggers`](/reference/config.md) config field (ships in library 0.1.1 below), quickstart + routing guidance in the generated `SKILL.md`, and a bundled `api-types.md` type reference. - "Orient your agents" CLAUDE.md fragment for consuming repos, in the [Quickstart](/get-started/quickstart.md) and [migration guide](/get-started/migration.md). ## 0.1.1 — 2026-07-20 (library) - `noindex: true` pages are now excluded from the pagefind index: the Layout omits `data-pagefind-body` on them, matching what the docs always said (see [Search](/guides/search.md)). Previously the marker was stamped on every page, so noindex pages still got indexed. - The [`skillTriggers`](/reference/config.md) `SiteConfig` field ships — the curated "Use when" phrases consumed by skill generation. - The package ships the `templates/` release kit — `set-version.mjs`, `version-branch.sh`, `versions.example.json`, and `netlify.example.toml`, ready to copy from `node_modules/@dreamlake/dockit/templates/` (see [Releases & versioning](/guides/releases.md)). ## 0.1.0 — 2026-07-19 - Initial release. The docs shell extracted from `lakeshore-workspace` into the `@dreamlake/dockit` package: components, the navigation/search/tabs singletons plus `initDocs`, the Vike renderer entries, the `dockit()` Vite plugin factory, and `styles.css` with its `@source` directive. - Published as pure ESM, without sourcemaps — see [Package exports](/reference/exports.md). - dockit.dreamlake.ai live, built with the library itself. --- Source: https://dockit.dreamlake.ai/reference/config # Configuration Everything configurable in dockit flows through one call — `initDocs` — made from your `site.config.ts` before the shell renders. ## initDocs(options) ```ts initDocs({ site: { /* Partial */ }, pageMetadata: import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?frontmatter', import: 'default' }), rawPages: import.meta.env.DEV ? import.meta.glob<string>('./pages/**/+Page.mdx', { eager: true, query: '?raw', import: 'default', }) : undefined, sectionOrder: ['Getting started', 'Reference'], tabs: [], }) ``` | Option | Type | Required | Purpose | | --- | --- | --- | --- | | `site` | `Partial` | yes | Branding, URLs, chips — merged over neutral defaults. | | `pageMetadata` | eager `?frontmatter` glob | one of these | Metadata-only loader keeps page bodies out of startup JavaScript. | | `pages` | eager glob of `+Page.mdx` | one of these | Legacy module map; use either `pages` or `pageMetadata`. | | `rawPages` | eager `?raw` glob | no | Feeds fallback full-text search. Prefer dev-only; production Pagefind searches built HTML. | | `sectionOrder` | `string[]` | no | Sidebar section order; unknown sections fall to the end. | | `tabs` | `TabEntry[]` | no | Topbar tab lenses and external links; omit for a tabless site. | | `search` | `SearchConfig` | no | Runtime search options; overrides `site.search` when both are provided. | ## SiteConfig | Field | Type | Effect | | --- | --- | --- | | `brand` | `string` | Top-left brand; llms.txt heading. | | `subtitle` | `string` | Muted label after the brand. | | `repoUrl` | `string` | Topbar GitHub icon (empty hides it). | | `docsRepoUrl` | `string` | Base for Edit/issue links in the TOC rail. | | `docsBranch` | `string` | Branch in the edit link (default `main`). | | `breadcrumbRoot` | `string` | Leftmost crumb (empty omits it). | | `url` | `string` | Public origin — absolute URLs in LLM artifacts. | | `skill` | `string` | Agent-skill name (`skills/<skill>/`, `<skill>.zip`). | | `summary` | `string` | One-liner: llms.txt blockquote + skill description. | | `skillTriggers` | `string[]?` | Curated task phrases for the generated skill's "Use when:" trigger list; unset falls back to the site's section names. | | `versionChips` | `VersionChip[]?` | Topbar chips (see below). Individual tabs can override this via [`TabDef.versionChips`](#tabdef). | | `gitHash` | `string?` | Git-hash chip (empty hides it). | | `docsPagesPath` | `string?` | Repo-relative pages dir for edit links (default `docs/pages`). | | `themeToggle` | `'cycle' \| 'segmented'?` | Topbar theme control: single cycling button (default) or the three-button slider — see [Theming](/guides/theming.md#themetoggle). | | `mdxComponents` | `Record<string, Component>?` | Site-specific MDX tags merged over the library's provider map — pages use them without per-page imports. | | `headScripts` | `string[]?` | External script URLs injected as `<script src defer>` into every page head (e.g. the Studio design-integration bridge). URLs only; non-URL entries are dropped. | | `search` | `SearchConfig?` | Runtime search options; see [Search](/guides/search.md) for multisite indexing. | | `indexable` | `boolean?` | Search-engine visibility. **Default `false`** (org policy): every page carries `noindex, nofollow` unless the site opts in with `indexable: true`. In-site search, llms.txt, and skills are unaffected. | `skillTriggers` phrases should read as task descriptions — "theming the docs shell", "wiring Netlify deploys" — since an agent matches them against what it is currently doing. See [LLM-readable docs](/guides/llms.md) for where they land. ## VersionChip ```ts interface VersionChip { label: string // left segment, e.g. 'dockit' version: string // right segment, rendered as v<version> dropdown?: boolean // true → version switcher prefix?: string // label prefix; defaults to 'v', '' for revisions manifestUrl?: string // defaults to '/versions.json'; path or absolute URL } ``` Each chip renders as a `` — also exported from the library, so a site can place one outside the topbar: ```tsx ``` `prefix` controls display only. Use `prefix: ''` for a revision such as `main · abc1234`; it does not create or imply a package release. With `dropdown`, `manifestUrl` selects the version list. A relative path is fetched from the production origin (`siteConfig.url`) first, then from the current origin; local development tries its own origin first. An absolute HTTP(S) URL is fetched directly. Cross-origin manifests need a CORS header. Without `dropdown`, the badge displays the supplied version and does not fetch a manifest. Use a separate manifest for each product. This site selects `/dockit-versions.json` for Dockit's `0.2.14` badge; its historical docs-build manifest `/versions.json` has a separate version sequence. The Python tab shows a `main` revision without a dropdown. See [Releases & versioning](/guides/releases.md#two-independent-version-axes). ## TabDef ```ts interface TabDef { id: string // stable key label: string // strip label numeral: string // editorial numeral ('I', 'II', …) landing: string // click target urlPrefix: string // scoping prefix for the sidebar filter versionChips?: VersionChip[] // per-tab override of site.versionChips } ``` ### Per-tab version chips `versionChips` on a tab **overrides** the global [`site.versionChips`](#siteconfig) whenever that tab is active: - **Omit** the field → the tab inherits the global chips (the default). - **`[]`** → no chips render on that tab. - **A non-empty array** → exactly those chips render, replacing the global list for that tab only. The active tab is derived from the URL's first segment (same rule the sidebar filter uses), so the chips switch as the reader moves between tabs. A CLI-focused site can scope each surface to its own version: ```ts tabs: [ // Inherits site.versionChips { id: 'overview', label: 'Get Started', numeral: 'I', landing: '/', urlPrefix: '/get-started' }, // cli-only on the CLI tab { id: 'cli', label: 'CLI', numeral: 'II', landing: '/cli', urlPrefix: '/cli', versionChips: [{ label: 'cli', version: '1.4.0' }] }, // py-only on the Python tab { id: 'python', label: 'Python', numeral: 'III', landing: '/python', urlPrefix: '/python', versionChips: [{ label: 'py', version: '0.9.2' }] }, // no chips on the design tab { id: 'python-design', label: 'Design', numeral: 'IV', landing: '/design', urlPrefix: '/design', versionChips: [] }, ], ``` ### External tab links `TabEntry` is `TabDef | ExternalTabDef`. External entries share the strip but link to another site, never become active, and scope no local pages: ```ts interface ExternalTabDef { label: string href: string // absolute destination URL external: true } ``` `internalTabs()` returns only page-scoping tabs; `isExternalTab(entry)` is the exported type guard. ## SearchConfig ```ts interface SearchConfig { mergeIndexes?: MergeIndexDef[] } interface MergeIndexDef { url: string // remote Pagefind bundle directory label: string // source badge shown on remote results } ``` Remote indexes require compatible Pagefind formats and CORS. A failed remote index leaves local search available. See [Search](/guides/search.md) for the complete indexing and palette workflow. ## PageMeta The parsed frontmatter shape — field-by-field effects are documented in [Authoring](/guides/authoring.md): ```ts interface PageMeta { path: string title: string section: string order: number description?: string draft?: boolean hidden?: boolean noindex?: boolean tocLevel?: 2 | 3 fullscreen?: boolean } ``` The `dev: true` frontmatter shorthand folds into `hidden` + `noindex` at parse time — `PageMeta` carries the resolved flags. Path matching against `PageMeta.path` should go through `normalizePath` / `findPage` (both exported), which tolerate a trailing slash. ## The init functions `initDocs` is sugar over four setters, each also exported for piecemeal setups. All mutate module-level singletons in place, so components holding an imported reference always see current data. ```ts function initDocs(opts: InitDocsOptions): void function defineSiteConfig(cfg: Partial): void function initNavigation( modules: Record<string, { frontmatter?: PageFrontmatter }>, sectionOrder?: string[], ): void function initSearchIndex(rawSources: Record<string, string>): void function initTabs(tabs: TabEntry[]): void ``` | Function | Populates | From | | --- | --- | --- | | `defineSiteConfig` | `siteConfig` | your `site` overrides, merged over defaults. | | `initNavigation` | `pages`, `groupedPages`, `groupedVisiblePages` | the eager page glob's frontmatter. | | `initSearchIndex` | `searchIndex`, `rawMarkdown` | the `?raw` glob (dev search fallback). | | `initTabs` | `TABS` | your `TabEntry[]`. | ## Navigation rail widths Both rails default to **280px**: the left sidebar appears at 768px and wider, and the right-hand TOC at 1024px and wider. Override `--doc-sidebar-width` and `--doc-toc-width` in your site stylesheet (after importing Dockit styles): ```css :root { --doc-sidebar-width: 300px; --doc-toc-width: 240px; } ``` Use `240px` for either rail to restore its previous width. The sidebar setting also applies to fullscreen layouts; those layouts omit the TOC. Mobile navigation is unchanged. The sidebar uses a thin scrollbar with a stable gutter, preventing overflow changes from shifting section headings. Section labels stay on one line (with an ellipsis and full-label tooltip if needed); page titles can wrap normally. --- Source: https://dockit.dreamlake.ai/reference/exports # Package exports `@dreamlake/dockit` exposes six entry points for configuration, rendering, build integration, and styles. ## The exports map | Entry | Used from | Purpose | | --- | --- | --- | | `@dreamlake/dockit` | `site.config.ts`, custom components | The library root: `initDocs`, components, hooks, data APIs. | | `@dreamlake/dockit/server` | `renderer/+onRenderHtml.tsx` | The `onRenderHtml` Vike hook — SSR HTML, head tags, fonts, FOUC script. | | `@dreamlake/dockit/client` | `renderer/+onRenderClient.tsx` | The `onRenderClient` hook — hydration + client routing renders. | | `@dreamlake/dockit/vike` | `renderer/+config.ts` | The Vike page config (`passToClient`, `clientRouting`, `prerender`). | | `@dreamlake/dockit/vite` | `vite.config.ts` | The `dockit()` plugin factory — Tailwind, MDX, shiki, React, Vike. | | `@dreamlake/dockit/styles.css` | `styles/app.css` | Theme tokens, dark mode, shell CSS (import after `tailwindcss`). | ## Root export contents **Initialization** — `initDocs`, plus the piecemeal setters: `defineSiteConfig`, `initNavigation`, `initSearchIndex`, `initTabs`. **Config + data singletons** — `siteConfig`, `pages`, `groupedPages`, `groupedVisiblePages`, `TABS`, `searchIndex`, `rawMarkdown`. **Navigation + search APIs** — `getAdjacentPages`, `tabForUrl`, `urlInTab`, `normalizePath`, `findPage`, `internalTabs`, `isExternalTab`, `search` (dev fallback), `searchPages` (pagefind-first), `defaultResults`. **Components** — [`Callout`](/reference/components/callout.md), [`Chip`](/reference/components/chip.md), [`CodeBlock`](/reference/components/code-block.md), [`Preview`](/reference/components/preview.md), [`StatusTable` + `TestStatus`](/reference/components/status-table.md), [`TabStrip`](/reference/components/topbar.md#tabstrip), [`DocFooter`](/reference/components/shell.md), `ThemeToggle` + `ThemeProvider` (see [Theming](/guides/theming.md)), [`SearchPalette`](/guides/search.md), [`Sidebar`](/reference/components/shell.md#sidebar), [`TOC`](/reference/components/shell.md#table-of-contents), [`Topbar`](/reference/components/topbar.md), `VersionBadge`, `SearchResizeHandles`, `ClientOnly`, and the `mdxComponents` map (headings, links, tables, `pre`/`code`, plus `Callout` / `Chip` / `Preview` / `StatusTable` / `TestStatus` in MDX scope). **Shell** — [`Layout`](/reference/components/shell.md) and `useMerge` (the H1-merge context). **Hooks** — `useTheme`, `useHiddenToggle`, `useLineNumbers`, `useLocalStorage` (+ `docKey`, `DOC_PREFIX`), `useMediaQuery`, `useSearchResize`. **Types** — `SiteConfig`, `VersionChip`, `InitDocsOptions`, `PageMeta`, `PageFrontmatter`, `NavGroup`, `TabDef`, `SearchEntry`, `SearchHit`, `SearchResult`, `SubResult`, `PreviewProps`, `ChipProps`, `ChipVariant`, `Theme`, `SearchConfig`, `MergeIndexDef`, `ExternalTabDef`, `TabEntry`, `PageModule`, `VersionBadgeProps`. ## Module shape - Pure ESM (`"type": "module"`), unbundled (`preserveModules`) with `.d.ts` beside every module — tree-shakes cleanly and steps through readable sources. - No source maps ship in the package. - `sideEffects` marks only CSS, so bundlers may drop any unused JS. > **Note:** Vike discovers hooks by `+`-prefixed filenames, which cannot live in > `node_modules` — so your site owns three one-line files re-exporting > `/server`, `/client`, and `/vike`. See the > [Quickstart](/get-started/quickstart.md). --- Source: https://dockit.dreamlake.ai/reference/components/shell # Shell reference `Layout` is the shell every page renders inside. Your site never mounts it directly — the renderer entries (`@dreamlake/dockit/server` and `/client`) wrap each page in it — but understanding its anatomy explains where everything on screen comes from. ## Layout anatomy ```text ┌─ Topbar ────────────────────────────────────────────────┐ │ brand / subtitle [chips] breadcrumb tabs search ⌘K│ ├──────────┬───────────────────────────────┬──────────────┤ │ Sidebar │ main.doc-content │ TOC rail │ │ 280px │ breadcrumb │ 280px │ │ (md+) │ {page MDX} │ (lg+) │ │ │ DocFooter (prev/next) │ │ └──────────┴───────────────────────────────┴──────────────┘ SearchPalette (fixed overlay, ⌘K) ``` The grid is `280px / minmax(0,1fr) / 280px`, max-width 1320px; the sidebar drops below `md`, the TOC below `lg`. Override `--doc-sidebar-width` and `--doc-toc-width` independently in your site stylesheet. The content column caps at 760px at `lg` and up, and carries `data-pagefind-body` so only page prose enters the search index (the breadcrumb and footer are `data-pagefind-ignore`d). On `noindex` pages the marker is omitted entirely, which keeps the whole page out of the index. ## What Layout owns - **Providers** — `ThemeProvider`, the `MDXProvider` carrying the [component map](/reference/exports.md), and the merge context below. - **Search state** — the open flag and query shared by the [Topbar](/reference/components/topbar.md) field and the [SearchPalette](/guides/search.md). - **Merge state** — a rAF-throttled scroll watcher flips a context boolean when the page H1's bottom edge passes under the 40px topbar; the page-top breadcrumb fades out and the topbar crossfades from brand to breadcrumb. Components can read the flag with `useMerge()`. - **Page-top breadcrumb** — a mono `Section / Title` eyebrow rendered above every H1 (including on `/`), hidden again once merged. - **Head sync** — with Vike's client routing, `<head>` is not re-rendered on navigation; Layout syncs `document.title` (`{title} — {brand}`), the description meta, and the robots meta (for `noindex` pages) from the current page's frontmatter. ## Layout props `Layout` takes only `{ children }` — the rendered page. Everything else derives from Vike's page context, the navigation singleton, and `siteConfig`. ## Fullscreen pages `fullscreen: true` in frontmatter turns off the content column, TOC, breadcrumb, and footer, and gives the page the full viewport below the topbar — for playgrounds and dashboards: ```mdx --- title: Playground section: Tools fullscreen: true --- ``` The sidebar stays (at `md+`), and the main region becomes an `overflow-auto` panel sized to `calc(100vh - 40px)`. ## DocFooter The prev/next navigation cards at the bottom of every non-fullscreen page. You are looking at a live instance right now — scroll to the end of this page: the cards below the content are ``, rendered by the Layout automatically. Adjacency is the **global page order** — every page sorted by its `order` frontmatter — not the sidebar grouping, so "Next" walks across section boundaries in reading order. `getAdjacentPages(path)` finds the current page in that sequence and returns its neighbors: ```ts const { prev, next } = getAdjacentPages('/reference/components/shell') ``` Rules: - `hidden: true` pages are skipped, so a reader walking the docs never lands on an internal page — unless dev mode (**Cmd+Shift+D**) is on, which `DocFooter` honors via `useHiddenToggle` (it passes `{ includeHidden: true }` to `getAdjacentPages`). - If the current page is itself hidden, it still participates, so the reader can step back out. - The footer renders nothing when the page has neither neighbor. `` takes no props — the current URL comes from Vike's page context and the sequence from the navigation singleton. Direct use only matters in a custom layout. ## Escape hatch Everything Layout composes is exported individually (`Topbar`, `Sidebar`, `TOC`, `DocFooter`, `SearchPalette`, `mdxComponents`), so a custom shell can rearrange the pieces — see the [exports reference](/reference/exports.md). ## Sidebar The 280px left rail listing every visible page, grouped by section. It is entirely derived — the [conventions](/guides/authoring.md) page covers the authoring side; this page covers behavior. ### Sidebar anatomy ```text │ GETTING STARTED 05 │ ← section header (mono, click to fold) │ Installation │ │ Quick start │ ← active page highlighted │ APP CHROME + 04 │ ← folded group (+ affordance, count stays) │ DEV 02 │ │ Gallery 🔒 │ ← hidden page, dev mode on │ Contributing [DRAFT] │ ← draft chip ``` Each header carries a zero-padded item count on the right and a fold affordance (`−`/`+`) on hover. The rail is sticky below the 40px topbar, scrolls independently, and disappears below `md`. ### Sections and order - Groups come from each page's `section` frontmatter. - Group order = the `sectionOrder` array passed to `initDocs`; unknown sections fall to the end alphabetically (so a new section shows up without breaking the nav). - Page order within a group = `order` frontmatter (missing = 99). Section headers are collapsible; the collapsed set persists in localStorage (key `sidebar-collapsed`), so a reader's pruned sidebar survives reloads. Navigating into a page whose group is folded auto-unfolds that group. Active-page matching is trailing-slash tolerant (`/guides/` matches `/guides`): the sidebar compares against `normalizePath(urlPathname)`, so prerendered HTML and client routing agree on `aria-current` even when they disagree on the trailing slash. The same helper backs the breadcrumbs, prev/next footer, and TOC edit links. ### Tab filtering With [tabs](/reference/components/topbar.md#tabstrip) declared, the sidebar shows only groups containing at least one page under the active tab's `urlPrefix`. The same section label can appear under several tabs — the filter runs per tab, so each lens shows its own slice. With no tabs registered, every section shows. ### Hidden pages — Cmd+Shift+D `hidden: true` pages stay out of the sidebar, search results, and prev/next by default, while remaining reachable by direct URL — the convention for internal/dev notes. Press **Cmd+Shift+D** and: - hidden pages appear in the sidebar (with a lock chip), - the topbar shows an orange dev-mode dot (click it to turn off), - search and the [DocFooter](/reference/components/shell.md#docfooter) include them. `draft: true` marks a page as awaiting review — it stays visible but carries a `DRAFT` chip so reviewers can spot it. > **Note:** `hidden` is about the reader's chrome. `noindex` is about robots and > generated artifacts — it emits a noindex meta and excludes the page > from pagefind and every [LLM surface](/guides/llms.md). Internal pages > usually want both. ### Sidebar direct use `` takes no props — groups come from the navigation singleton (`groupedPages` / `groupedVisiblePages`), the active tab from the current URL, and the fold state from localStorage. The [Layout](/reference/components/shell.md) mounts it on every page, fullscreen included. ## Table of contents The right rail (visible at `lg` and up — you should see it now, to the right) lists this page's headings with a scroll-spy: the animated SVG rail and dot track your position as you read. ### TOC anatomy ```text │ ON THIS PAGE │ │ ● Anatomy │ ← dot rides the rail to the active heading │ │ What it indexes │ │ │ · sub heading │ ← H3: smaller mono, foldable under its H2 │ │ Rail footer links │ │ ┊ │ │ ──────────────────────│ │ View as Markdown │ │ Edit this page │ │ Report an issue │ ``` Headings come from the rendered DOM, not frontmatter: after each navigation the TOC queries `main.doc-content` for anchored headings, so anything MDX renders as an `h2`/`h3` with an id shows up. H3s fold under their parent H2; the fold state persists **per page** in localStorage, and unfolding scrolls the active entry back into view. ### What it indexes - **H2** headings always; **H3** headings by default, rendered in a smaller mono style and foldable under their parent H2. - Set `tocLevel: 2` in frontmatter on long pages where H3 noise would crowd the rail: ```mdx --- title: Very long reference tocLevel: 2 --- ``` ### Rail footer links Below the headings, the rail carries three derived links: | Link | Where it goes | | --- | --- | | **View as Markdown** | The page's `.md` twin generated by [gen-llms.mjs](/guides/llms.md) — `/reference/components/shell.md` for this page. | | **Edit this page** | `{docsRepoUrl}/edit/{docsBranch}/{docsPagesPath}/<slug>/+Page.mdx` — jumps straight to the source on GitHub. | | **Report an issue** | `{docsRepoUrl}/issues/new`. | ### TOC configuration `docsPagesPath` (default `docs/pages`) is the repo-relative directory holding `pages/` — set it to match your layout, e.g. `pages` for a repo-root site or `packages/docs/pages` for a nested package: ```ts file="site.config.ts" site: { docsRepoUrl: 'https://github.com/dreamlake-ai/dockit-workspace', docsBranch: 'main', docsPagesPath: 'docs/pages', } ``` Note the split: `docsRepoUrl` is the workspace repo hosting the pages (edit/issue links); `repoUrl` is the product repo behind the topbar's GitHub icon. They often differ. ### TOC direct use `` takes no props; it reads headings from the rendered `main.doc-content` and page metadata (`tocLevel`, the `.md` path) from the navigation singleton. The [Layout](/reference/components/shell.md) mounts it on every non-fullscreen page. --- Source: https://dockit.dreamlake.ai/reference/components/topbar # Topbar The 40px sticky bar at the top of the shell. Left to right: the brand cluster, the merged breadcrumb, the [TabStrip](#tabstrip), the search field, and the actions cluster (GitHub link, dev-mode dot, theme toggle). ## Anatomy ```text ┌─ 40px, sticky ──────────────────────────────────────────────────────┐ │ brand. subtitle [pkg|v0.2.0 ▾] [a1b2c3] ┊crumb┊ TABS [⌘K ⌕] ○ ◐ │ │ └───────── brand cluster ─────────────┘ tabs search actions └─────────────────────────────────────────────────────────────────────┘ crumb = merged breadcrumb, fades in when the H1 scrolls under the bar ``` ## Props The [Layout](/reference/components/shell.md) owns the search state and passes it down; you only touch these props in a custom shell: | Prop | Meaning | | --- | --- | | `searchOpen` | Whether the palette is open (drives the field's FLIP). | | `onOpenSearch` / `onCloseSearch` | Open/close callbacks, also bound to **⌘K** and **/**. | | `query` / `setQuery` | The live search string, shared with the [SearchPalette](/guides/search.md). | Everything else — brand, chips, tabs, links — comes from `siteConfig` and the `TABS` registry. ## Brand cluster `{brand}. / {subtitle}` renders from `siteConfig`, followed by any **version chips** and the **git hash chip**: ```ts file="site.config.ts" initDocs({ site: { brand: 'DreamLake', subtitle: 'Dockit', versionChips: [{ label: 'dockit', version: __DOCKIT_VERSION__, dropdown: true, manifestUrl: '/dockit-versions.json' }], gitHash: __GIT_HASH__, }, // … }) ``` The chip values are the consumer's business — this site injects them via Vite `define` from the workspace package.json and `git rev-parse --short=6 HEAD` (see [Releases & versioning](/guides/releases.md) for the full pattern). - A chip with `dropdown: true` becomes the **version switcher**: it fetches its `manifestUrl` (default `/versions.json`) and lists aliases + past deploys in a keyboard-dismissable popover. These and every other badge in the shell share one visual idiom — see [Chip](/reference/components/chip.md) for the spec and all the ways to inject badges. - On version-subdomain deploys (`v0-1-0.…`), the chip re-labels itself from `window.location.hostname` after hydration. A custom product manifest must contain that version before the hostname can override the label; this prevents a docs-build version from relabeling a product badge. - Chips can be **scoped per tab**: a `TabDef.versionChips` array overrides `site.versionChips` while that tab is active (omit to inherit, `[]` to show none). Handy for showing a cli-only chip on the CLI tab and none on a design tab — see [Site config](/guides/site-config.md#scoping-chips-per-tab). - `prefix` defaults to `v`; use `prefix: ''` for revision labels. This site's Python tab shows `main` plus its source revision without a dropdown, while Dockit's badge uses `/dockit-versions.json`. - `gitHash` unset (empty) hides the hash chip entirely. ## Breadcrumb merge Scroll this page: once the H1 passes under the bar, the brand cluster collapses and a `Doc Kit / Shell / Topbar` breadcrumb fades in — the merge state from the [Layout](/reference/components/shell.md), read via `useMerge()`. `breadcrumbRoot` in `siteConfig` supplies the leftmost crumb; empty string omits it. ## TabStrip The compact tab cluster on the topbar's right side: uppercase mono labels with a sliding ink underbar that tracks the active (or hovered) tab. You can see it live at the top of this page — the **Reference** tab is active right now. `` renders inline inside the topbar; it carries no sticky/blur/background of its own, and takes no props — all input comes from the `TABS` registry and the current URL via Vike's page context. ### Data source The strip renders the `TABS` registry, populated by `initDocs({ tabs })` (or `initTabs()` directly). Each tab is a `TabDef`: ```ts export interface TabDef { id: string // stable key, e.g. 'components' label: string // strip label, e.g. 'Components' numeral: string // editorial chapter numeral, e.g. 'II' landing: string // where clicking the tab navigates urlPrefix: string // pages under this prefix belong to the tab } ``` This site's registry, for reference: ```ts file="site.config.ts" tabs: [ { id: 'dockit', label: 'Dockit', numeral: 'I', landing: '/', urlPrefix: '/get-started' }, { id: 'reference', label: 'Reference', numeral: 'II', landing: '/reference/config', urlPrefix: '/reference' }, { id: 'python-autodoc', label: 'Python Autodoc', numeral: 'III', landing: '/python-autodoc', urlPrefix: '/python-autodoc' }, ] ``` ### Behavior - The active tab derives from the URL's first path segment (`tabForUrl`). `/` and unknown prefixes resolve to the first tab, so orphan pages never empty the sidebar. - The [Sidebar](/reference/components/shell.md#sidebar) filters its groups to the active tab's pages — tabs are a lens, not separate sites. One tab can carry several sidebar sections: on this site the **Reference** tab (`urlPrefix: '/reference'`) holds *API*, *Shell*, and *Content components* groups. - The underbar tracks the **hovered** tab while the pointer is over the strip (measured with `useLayoutEffect` against live DOM rects, so it survives window resizes), then springs back to the active tab. - **Empty registry:** with no tabs (`tabs: []` or omitted), the strip renders nothing and the sidebar shows every section. ## Search field The field is a FLIP-animated fixed element: closed, it sits at the grid's right; open (**⌘K** — **Ctrl+K** on non-Mac — or **/**), it expands into the palette input. The animation measures the closed rect after each paint, so window resizes stay smooth. Below `lg` it collapses to an icon button. ## Actions cluster - **GitHub link** — `siteConfig.repoUrl` drives the icon; empty string hides it. - **DEV badge** — a warn-variant [Chip](/reference/components/chip.md) reading `DEV` appears while hidden pages are revealed (**Cmd+Shift+D**); clicking it turns the toggle back off. See [Sidebar](/reference/components/shell.md#sidebar) for what the toggle reveals. - **Theme toggle** — light/dark/system; see [Theming](/guides/theming.md). ## Related siteConfig fields | Field | Drives | | --- | --- | | `brand`, `subtitle` | The brand cluster text. | | `versionChips`, `gitHash` | The chips after the brand. | | `breadcrumbRoot` | The leftmost merged-breadcrumb crumb. | | `repoUrl` | The GitHub icon link. | --- Source: https://dockit.dreamlake.ai/reference/components/callout # Callout An admonition block for asides the reader should not skim past. `> **Note:** ` is available in every MDX page without an import — it is > part of the [MDX component map](/reference/exports.md). > > > > > > ## Usage in MDX > > ```mdx > > Body text. Markdown works here — links, `code`, **bold**. > **Warning:** **Careful.** Without a `title` prop, a leading bold sentence is > styled as the title automatically. ``` Which renders as: > **Warning:** **Careful.** Without a `title` prop, a leading bold sentence is > styled as the title automatically. ## Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `'info' \| 'warn'` | `'info'` | Icon + accent color: blue circle-i, or orange warning triangle. | | `title` | `ReactNode` | — | Explicit title row above the body. A leading `<strong>` in the body is styled as a title too. | | `children` | `ReactNode` | — | Body content. | ## When to use - **info** — context the reader should notice but can act on later: version caveats, links to background, "this also works" notes. - **warn** — footguns: irreversible actions, silent misconfiguration, things that look right but aren't. - Keep the body to a few lines. If a callout grows past a short paragraph, it probably wants to be a normal `##` section instead — callouts interrupt reading flow, and stacking more than two in a row makes readers skim all of them. - Callouts are plain `<div>`s — they can appear inside lists, table cells, or [``](/reference/components/preview.md) bodies without special handling. --- Source: https://dockit.dreamlake.ai/reference/components/code-block # CodeBlock `` wraps a shiki-rendered `<pre>` with the docs-template header bar: a language chip, an optional filename, a `:set nu` line-number toggle, and a copy button. You rarely use it directly — **every fenced code block in MDX gets it automatically** via the `pre` mapping in the MDX component map. ## Automatic usage (fenced blocks) ````mdx ```ts file="site.config.ts" ``` ```` renders as: ```ts file="site.config.ts" ``` The `file="…"` meta string becomes the filename chip; the fence language becomes the language chip. Both are extracted by the shiki config inside [`dockit()`](/reference/exports.md). ## Direct usage For code you render outside MDX (or source you already have as a string), compose it yourself: ```tsx <pre><code>echo hello</code></pre> ``` ## Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | — | A `<pre><code>` tree (usually shiki output). | | `filename` | `string` | — | Filename chip in the header bar. | | `lang` | `string` | — | Language chip. Auto-forwarded from the fence language in MDX. | ## Behavior notes - **The header bar only renders when `filename` is set.** Without a `file="…"` meta string, the language chip, line-number toggle, and copy button float in the top-right corner and reveal on hover (or keyboard focus) instead — compare the two fenced blocks above. - The line-number toggle is **site-wide** state ([`useLineNumbers`](/reference/exports.md)) — flipping it in one block flips every block, including the Source tabs of [``](/reference/components/preview.md) frames. - Copy grabs the text content of the inner `<code>` element. - Colors come from the shiki dual theme (github-light / github-dark) and follow the site theme with no re-render — see [Theming](/guides/theming.md). --- Source: https://dockit.dreamlake.ai/reference/components/preview # Preview `` renders a live example next to its source in a tabbed frame. The convention: keep each example as a real component under `examples/`, import it twice — once as a module, once via `?raw` — and hand both to ``. The example below is doing exactly that, about itself: ## Usage ```mdx ``` `.tsx?raw` is native Vite; `.mdx?raw` works too — the `dockit()` plugin ships a loader that lets raw MDX imports bypass the MDX compiler. ## Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | — | The live-rendered example. | | `source` | `string` | — | Source code shown in the Source tab (import the twin via `?raw`). | | `dataSource` | `string` | — | Optional second source — surfaces a `Data` tab. | | `filename` | `string` | — | Filename chip in the frame header (also picks the highlight language). | | `defaultTab` | `'preview' \| 'source' \| 'data'` | `'preview'` | Tab shown first. | | `height` | `number \| string` | — | Force a height for the preview pane. | ## Behavior notes - **Shiki loads lazily.** The Source/Data tab pulls the highlighter in via dynamic `import('shiki')`, so the highlighting bundle is only fetched when someone actually opens a code tab; a plain `<pre>` shows until it resolves. - The highlight language is inferred from the `filename` extension (`ts`, `tsx`, `js`, `json`, `css`, `html`, `md`, `mdx`, `bash`, …); anything unrecognized — or no `filename` — falls back to `tsx`. - The `:set nu` line-number toggle is the same **site-wide** state as [CodeBlock](/reference/components/code-block.md)'s — flipping it here flips every code block on the site. - The filename chip and the copy / line-number controls only appear on the Source and Data tabs; the Preview tab keeps only the fullscreen toggle. - **Fullscreen.** The expand button at the right of the header grows the frame into a full-viewport overlay — for wide demos that need real room. In fullscreen the preview pane drops its padding (full-bleed) and stretches the demo's wrapper to the viewport height; **Esc** or the collapse button exits. - Use `dataSource` when an example has a companion input file (a config, a dataset, a schema) — it surfaces as a third `Data` tab, and `defaultTab="source"` is handy for pages where the code matters more than the render. > **Note:** Because each example is a plain component file, it typechecks with the > site, runs in dev with hot reload, and cannot drift from the code shown > in the Source tab — the tab *is* the file. --- Source: https://dockit.dreamlake.ai/reference/components/status-table # StatusTable `` renders a live test-status board from a `test-results.json` file served at the site root. DreamLake sites use it for a "current build health" page whose data is regenerated by CI — the docs never claim more than the last run proved. ## Usage in MDX ```mdx ``` Both components are in the MDX component map — no import needed. They fetch `/test-results.json` on mount (client-side only, `cache: no-store` so a CI refresh shows immediately) and render an empty state until the file exists. ## Data contract ```json file="public/test-results.json" { "generated_at": "2026-07-19T12:00:00Z", "schema_version": 1, "suites": [ { "id": "unit", "name": "Unit tests", "command": "pnpm test", "cwd": ".", "status": "pass", "duration_s": 12.4, "tests": { "total": 240, "pass": 240, "fail": 0, "skip": 0 } } ], "examples": [ { "id": "hello", "title": "Hello world", "needs_cp": false, "source_link": "https://github.com/…", "status": "pass" } ] } ``` `status` is `pass | fail | skip` (suites also allow `pending`). Optional fields: `exit_code`, `reason`, `log_excerpt`, `doc_link`. ## TestStatus The companion inline chip references a single suite or example — drop it next to prose that describes the thing being tested: ```mdx The install path is covered by in CI. ``` ## Props | Component | Prop | Type | Description | | --- | --- | --- | --- | | `StatusTable` | `kind` | `'suites' \| 'examples'` | Which half of the results file to render (default `'suites'`). | | `TestStatus` | `suite` | `string` | Suite id to reference. | | `TestStatus` | `example` | `string` | Example id to reference (use one of the two). | ## Edge cases - **Missing file:** a 404 (or invalid JSON) renders a bordered "Could not load test results" note — the page still builds and prerenders fine, since the fetch only happens in the browser. - **Empty arrays:** `suites: []` renders a muted "No suites reported yet" hint instead of an empty table. - **`TestStatus` with an unknown id** renders a muted `unknown` chip; while the fetch is in flight it shows `loading…`. A found id renders the status badge plus the *file's* `generated_at` age (e.g. `3h ago`) — the age is per-run, not per-suite. - This docs site does not ship a `test-results.json`, which is why this page shows the markup as snippets rather than a live embed — a live `` here would render the missing-file state. --- Source: https://dockit.dreamlake.ai/reference/components/chip # Chip `` **is** the design guide's chip idiom, packaged as a component — the same badge you see as the topbar's `DEV` indicator, the version chips, and inline status chips. It is available in every MDX page without an import (part of the [MDX component map](/reference/exports.md)), and exported from the library root for use in custom components. ## Implementing badges Every chip in the shell shares one typographic core. If you are building a badge — in a custom component, a consuming site, or another DreamLake surface — this is the spec, and `` renders it exactly: | Property | Value | | --- | --- | | Font | `var(--font-doc-template-mono)` | | Size / weight | `10px` / `600` | | Tracking | `0.08em`, `line-height: 1.4` | | Casing | `uppercase` | | Padding / radius | `2px 7px` / `4px` | | Border | `1px solid color-mix(in srgb, currentColor 45%, transparent)` — a hairline of the text color | | Layout | `inline-flex`, vertically centered | The three variants pick the text / fill pair from the [theme tokens](/guides/theming.md), so they work in both modes: | Variant | Text | Fill | Use for | | --- | --- | --- | --- | | `muted` (default) | `--color-doc-template-muted` | `--color-doc-template-chip` | Neutral labels, absent states. | | `accent` | `--color-doc-template-accent` | `--color-doc-template-accent-soft` | Positive / active states. | | `warn` | `--color-doc-template-warn` | `--color-doc-template-warn-soft` | Caution, in-progress, dev-only. | ### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `'muted' \| 'accent' \| 'warn'` | `'muted'` | The text / fill color pair. | | `onClick` | `() => void` | — | When set, renders a `<button type="button">` with a pointer cursor instead of a `<span>`. | | `title` | `string` | — | Native tooltip. | | `className`, `style` | — | — | Pass through; inline `style` wins over the base + variant styles, so one-off recolors are plain overrides. | | …rest | button attributes | — | `aria-*` and any other button/span attributes are forwarded. | ### Custom colors For a color outside the three variants, override via `style` — the border is `currentColor`-derived, so it follows automatically: ```tsx passing ``` This is exactly how the shell's own [`TestStatus`](/reference/components/status-table.md) badge gets its data-driven pass/fail/skip colors — it renders through `` with style overrides. ## Injecting badges Three injection points, in order of how structural they are: ### 1. Topbar version chips (config) The chips after the brand cluster come from the [`versionChips`](/reference/config.md) config field — one `{ label, version, dropdown? }` entry per chip; `dropdown: true` makes it the version switcher: ```ts file="site.config.ts" initDocs({ site: { versionChips: [{ label: 'dockit', version: __DOCKIT_VERSION__, dropdown: true }], }, // … }) ``` See [Topbar](/reference/components/topbar.md) for where they render and [Releases & versioning](/guides/releases.md) for the manifest behind the dropdown. ### 2. Standalone `VersionBadge` (component) `VersionBadge` — the two-segment `[ label | v0.2.5 ]` chip — is exported from the library root, so the version switcher can live outside the topbar (a custom footer, a landing hero): ```tsx ``` Its segments follow the chip spec above; only the two-segment layout is bespoke. ### 3. Inline in MDX (content) `` is registered in the MDX component map, so any page on any dockit site can drop a badge into prose with no import: ```mdx The legacy importer is deprecated as of 0.3. ``` The legacy importer is deprecated as of 0.3. For badges whose *content* is data, the worked example is `` — the inline test-status chip from the lakeshore docs. An MDX page injects it with just an id, and the chip fetches its state from a CI-generated `/test-results.json`: ```mdx The install path is covered by in CI. ``` It degrades gracefully — a muted `unknown` chip when the id (or the manifest) doesn't exist, as on this site. See [StatusTable](/reference/components/status-table.md) for the full data pipeline. Site-specific badge components follow the same route: build them on `` and register them via the `mdxComponents` [config field](/reference/config.md) so your pages can use them without imports. ## Badges the shell already owns Don't recreate these — they render automatically: - **DEV badge** — the warn `DEV` chip in the topbar actions cluster while hidden pages are revealed (**Cmd+Shift+D**); clicking it turns the toggle off. See [Topbar](/reference/components/topbar.md). - **Git-hash chip** — set `gitHash` in the site config and a mono commit-hash chip renders after the version chips. - **Sidebar `draft` chip** — the "awaiting review" marker from the [`draft` frontmatter flag](/guides/authoring.md). --- Source: https://dockit.dreamlake.ai/python-autodoc # Python Autodoc **autodoc-py** turns a Python package into Dockit API reference pages. It reads source files with Python's abstract syntax tree, then writes MDX pages with navigation metadata, signatures, docstrings, and links to source. Your package does not run during generation. You can document a release without installing its application dependencies or triggering startup code. ## From source to reference ```bash python -m pip install dreamlake-autodoc-py==0.2.0a1 autodoc-py src/my_package \ --module my_package \ --output docs/pages/api \ --section 'Python API' ``` Dockit discovers the generated `+Page.mdx` files through its existing page glob. They participate in the sidebar, search, table of contents, and Markdown exports alongside your hand-written guides. - [Generate API docs](/python-autodoc/usage.md): installation, CLI options, routes, and source links. - [Build integration](/python-autodoc/usage.md#dockit-integration): run generation before a build and keep version branches reproducible. - [Python API display](/python-autodoc/display.md): typed classes, properties, aliases, and async methods. - [Release notes](/python-autodoc/release-notes.md): source versions, compatibility, and distribution status. - [Source repository](https://github.com/dreamlake-ai/autodoc-py): implementation, tests, and contributions. ## What it documents | Python source | Generated reference | | --- | --- | | Modules | Module docstrings, grouped topic pages, or a page per public module | | Classes and functions | Signatures, docstrings, and source links | | Class members | Constructors, methods, annotated attributes, and assigned attributes | | Local imports | Linked indexes for explicit and star re-exports | | Local base classes | Inherited member lists, with overridden names suppressed | | Literal `__all__` | Filtering of locally defined public classes and functions | Private modules and tests are skipped. A generated-file manifest records the pages the tool owns, so removing a Python module removes its generated page without deleting unrelated hand-written pages. ## Generator version The badge identifies the released generator version pinned by this workspace. The first PyPI prerelease is `0.2.0a1`; a release selector will follow when multiple documentation builds are available. The generator source revision and the version of the Python package being documented are independent. ## Static analysis boundaries The generator documents declarations in source. It does not resolve dynamically created members, runtime signatures, external-package inheritance or re-exports, or module-qualified base expressions. Local inheritance follows base declaration order; it does not implement Python's complete C3 method resolution order. Docstrings retain their content, but Sphinx roles and directives are not executed. Keep earlier Sphinx builds if exact historical rendering matters. Use a Python interpreter that can parse the syntax in the release you are documenting; the generator itself requires Python 3.10 or later. --- Source: https://dockit.dreamlake.ai/python-autodoc/usage # Generate API docs ## Install The generator requires Python 3.10 or later and has no runtime dependencies. ```bash python -m pip install dreamlake-autodoc-py==0.2.0a1 ``` Or install the CLI in an isolated environment with uv: ```bash uv tool install 'dreamlake-autodoc-py==0.2.0a1' ``` The distribution name is `dreamlake-autodoc-py`, and the command is `autodoc-py`. This is a standard Python package using setuptools; uv can install and build it without a separate uv-specific package format. See [Release notes](/python-autodoc/release-notes.md) for distribution status. For a reproducible build, append `@COMMIT_SHA` to the Git URL, replacing the placeholder with the exact generator revision you have reviewed. The generator revision and your documented package revision are separate choices. ## Generate pages Pass the directory containing the Python package, its import name, and the destination under your Dockit `pages/` directory: ```bash autodoc-py src/my_package \ --module my_package \ --output docs/pages/api \ --section 'Python API' \ --source-url https://github.com/example/my-project/blob/RELEASE_SHA/src/my_package ``` Replace `RELEASE_SHA` with the documented source revision. `--source-url` points to the package directory, so its path should correspond to the input directory. | Argument | Meaning | | --- | --- | | `source` | Python package directory, such as `src/my_package` or `my_package` | | `--module` | Required import name, such as `my_package` | | `--output` | Required output directory for generated `+Page.mdx` files | | `--section` | Sidebar section; defaults to `Python API` | | `--url-prefix` | Public route root for generated pages; defaults to `/api` | | `--source-url` | Optional URL of the package directory at the documented revision | You can invoke the same CLI through `python -m autodoc_py` after installation. ## Output layout For a package containing `__init__.py`, `client.py`, and `models/item.py`, output follows this structure: ```text docs/pages/api/ +Page.mdx client/+Page.mdx models/item/+Page.mdx .autodoc-py.json ``` The package page is served at `/api`, while the module pages appear at `/api/client` and `/api/models/item`. Frontmatter supplies the page title, section, order, and description. ## Custom API routes If the output lives at `docs/pages/reference/python`, set the matching public route prefix: ```bash autodoc-py src/my_package --module my_package \ --output docs/pages/reference/python \ --url-prefix /reference/python ``` The generator uses absolute routes for module and re-export links, so they resolve both with and without a trailing slash. The output path controls where files are written; `--url-prefix` controls their public links. ## Regeneration Run the same command after changing your Python source. All source files are parsed before output changes, so a syntax error stops generation before replacing pages. The `.autodoc-py.json` manifest tracks generated files. Keep it with the generated pages so a later run can remove obsolete output. Keep hand-written introductions and tutorials outside the generated paths: pages still owned by the generator are replaced during regeneration. ## Docstrings and code MDX expression and JSX characters in prose are escaped. Inline code and fenced examples retain their formatting. Write explanatory docstrings and explicit signatures in the Python source; keep narrative tutorials as hand-written MDX pages. Sphinx directives remain text rather than executing a Sphinx extension pipeline. For a project that depends heavily on Sphinx, inspect the generated reference and retain its existing builds while migrating. ## Dockit integration ### Add the sidebar section Include your chosen section in the site configuration to control its position: ```ts file="site.config.ts" initDocs({ site: { brand: 'My project', subtitle: 'Docs' }, pages, rawPages, sectionOrder: ['Getting started', 'Guides', 'Python API'], }) ``` Use `--section 'Python API'` when generating the pages. If your site uses tabs, give the API tab a `urlPrefix` matching the output path, such as `/api` for `docs/pages/api`. ### Build in sequence Generate before running Vite so the build, search index, and LLM exports see the same pages: ```bash autodoc-py src/my_package --module my_package \ --output docs/pages/api --section 'Python API' pnpm build ``` Install the pinned generator in CI, then run these steps against the checked-out package source. Commit generated MDX and its manifest if you want reference changes reviewed as a diff; alternatively generate them consistently in CI before every docs build. ### Use the workspace submodule The Dockit workspace includes the generator source at `packages/autodoc-py`. Initialize it when cloning the workspace: ```bash git submodule update --init --recursive python -m pip install -e packages/autodoc-py ``` The parent repository records the submodule revision. Updating that recorded revision is an explicit change, so builds can keep using a reviewed generator version even as the generator repository evolves. The Python package is independent of the JavaScript workspace. A normal Dockit build does not install Python or generate API pages unless your build workflow adds that step. ### Versioned documentation For each documentation branch: 1. Check out the matching package release. 2. Run the pinned generator against that release's package directory. 3. Set `--source-url` to the same release commit. 4. Build and inspect the complete static site before publishing the branch. Older releases may use a different package layout or Python syntax. Match the source path and interpreter to the release; do not generate every branch from today's source tree. Keep existing build artifacts until their replacements are verified. A version menu can link to an earlier Sphinx site while a Dockit branch is being prepared. See [Releases and versioning](/guides/releases.md) for branch deploys and the `versions.json` manifest. ### Verify the result Check a package page, a module page, a class with inherited members, and a source link. Confirm that navigation resolves under your deployed URL, search finds an API symbol, and the generated manifest only owns the expected files. Static extraction cannot reproduce runtime-generated APIs. Supplement those cases with hand-written reference pages, and use the [documented limitations](/python-autodoc.md#static-analysis-boundaries) when interpreting the result. ## Group modules into topic pages The default layout creates one page per Python module. When the module tree contains small implementation files, use `--page-map` to choose reader-facing topics instead. Each topic has an introduction, a linked API index, and sections for its functions and classes. Methods stay beneath their classes. ```json file="api-pages.json" { "pages": [ { "slug": "configuration", "title": "Configuration", "description": "Configure a client and read environment values.", "intro": "Start with Client; use the environment helpers for defaults.", "modules": ["my_package", "my_package.config", "my_package.env"] }, { "slug": "jobs", "title": "Jobs", "description": "Submit and inspect jobs.", "modules": ["my_package.jobs*"] } ], "exclude": { "my_package.internal*": "Implementation helpers, not public API." } } ``` ```bash autodoc-py src/my_package --module my_package \ --output docs/pages/reference --url-prefix /reference \ --page-map api-pages.json ``` Every public module must match exactly one topic or an explicit exclusion. An optional `symbols` object maps module names to lists of selected public symbols. References to excluded modules or omitted symbols fail validation instead of silently producing broken links. Anchors include the defining module so similarly named classes on a combined page remain distinct. `.autodoc-routes.json` records old module and symbol URLs for site-specific redirects. The existing generated-file manifest removes old module pages while preserving hand-written pages. Public callable singletons such as `EnvVar = _EnvVar()` are documented without executing their constructors. Google-style argument and return sections become readable lists, and indented examples become fenced Python code. See [params-proto's API reference](https://params-proto.dreamlake.ai/reference) for a site organized into configuration, environment, sweeps, CLI, and compatibility topics. --- Source: https://dockit.dreamlake.ai/python-autodoc/display import "./autodoc.css" # Python API display A small reference specimen showing Python types, aliases, and callable APIs. Generated by Python Autodoc. Types link to local definitions; signatures retain annotations and defaults without importing this module. ## `Vector3` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">named tuple</span> <code className="py-api-name">reference_demo.Vector3</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L8">Source ↗</a> </div> <pre className="py-api-signature"><code>Vector3(<span className="py-api-param">x</span>: <span className="py-api-type">float</span>, <span className="py-api-param">y</span>: <span className="py-api-type">float</span>, <span className="py-api-param">z</span>: <span className="py-api-type">float</span> = <span className="py-api-default">0.0</span>)</code></pre> <p className="py-api-meta">Bases: <span className="py-api-type">NamedTuple</span></p> <div className="py-api-description"> A position in three-dimensional space. <p className="py-api-section-label">Parameters</p> <table className="py-api-fields"> <thead><tr><th>Parameter</th><th>Type / default</th><th>Description</th></tr></thead> <tbody> <tr><td><code>x</code></td><td><code><span className="py-api-type">float</span></code><br /><span className="py-api-default">required</span></td><td>—</td></tr> <tr><td><code>y</code></td><td><code><span className="py-api-type">float</span></code><br /><span className="py-api-default">required</span></td><td>—</td></tr> <tr><td><code>z</code></td><td><code><span className="py-api-type">float</span></code><br /><span className="py-api-default"> = 0.0</span></td><td>—</td></tr> </tbody> </table> <p className="py-api-section-label">Attributes</p> <table className="py-api-fields"> <thead><tr><th>Name</th><th>Type / value</th><th>Description</th></tr></thead> <tbody> <tr><td><a id="vector3.x" /><code>x</code></td><td><code><span className="py-api-type">float</span></code></td><td>Horizontal coordinate, in meters.</td></tr> <tr><td><a id="vector3.y" /><code>y</code></td><td><code><span className="py-api-type">float</span></code></td><td>Vertical coordinate, in meters.</td></tr> <tr><td><a id="vector3.z" /><code>z</code></td><td><code><span className="py-api-type">float</span> = 0.0</code></td><td>Depth coordinate, in meters.</td></tr> </tbody> </table> </div> </section> ## `Scene` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">class</span> <code className="py-api-name">reference_demo.Scene</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L20">Source ↗</a> </div> <pre className="py-api-signature"><code>Scene(<span className="py-api-param">name</span>: <span className="py-api-type">str</span>, *, <span className="py-api-param">origin</span>: <a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a> | <span className="py-api-type">None</span> = <span className="py-api-default">None</span>)</code></pre> <div className="py-api-description"> A named scene with typed spatial coordinates. <p className="py-api-section-label">Parameters</p> <table className="py-api-fields"> <thead><tr><th>Parameter</th><th>Type / default</th><th>Description</th></tr></thead> <tbody> <tr><td><code>name</code></td><td><code><span className="py-api-type">str</span></code><br /><span className="py-api-default">required</span></td><td>Display name for the scene.</td></tr> <tr><td><code>origin</code></td><td><code><a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a> | <span className="py-api-type">None</span></code><br /><span className="py-api-default"> = None</span></td><td>Initial position, or None for the default origin.</td></tr> </tbody> </table> <p className="py-api-section-label">Attributes</p> <table className="py-api-fields"> <thead><tr><th>Name</th><th>Type / value</th><th>Description</th></tr></thead> <tbody> <tr><td><a id="scene.visible" /><code>visible</code></td><td><code><span className="py-api-type">bool</span> = True</code></td><td>Whether the scene is visible.</td></tr> </tbody> </table> </div> </section> ### `Scene.__init__` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">method</span> <code className="py-api-name">reference_demo.Scene.__init__</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L30">Source ↗</a> </div> <pre className="py-api-signature"><code>Scene.__init__(<span className="py-api-param">name</span>: <span className="py-api-type">str</span>, *, <span className="py-api-param">origin</span>: <a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a> | <span className="py-api-type">None</span> = <span className="py-api-default">None</span>)</code></pre> <div className="py-api-description"> <p className="py-api-section-label">Parameters</p> <table className="py-api-fields"> <thead><tr><th>Parameter</th><th>Type / default</th><th>Description</th></tr></thead> <tbody> <tr><td><code>name</code></td><td><code><span className="py-api-type">str</span></code><br /><span className="py-api-default">required</span></td><td>—</td></tr> <tr><td><code>origin</code></td><td><code><a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a> | <span className="py-api-type">None</span></code><br /><span className="py-api-default"> = None</span></td><td>Keyword-only parameter.</td></tr> </tbody> </table> </div> </section> ### `Scene.origin` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">property</span> <code className="py-api-name">reference_demo.Scene.origin</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L35">Source ↗</a> </div> <pre className="py-api-signature"><code>Scene.origin: <a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a></code></pre> <div className="py-api-description"> The current position of the scene. <p className="py-api-section-label">Type</p> <p><code><a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a></code></p> </div> </section> ### `Scene.move` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">method</span> <code className="py-api-name">reference_demo.Scene.move</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L39">Source ↗</a> </div> <pre className="py-api-signature"><code>Scene.move(<span className="py-api-param">position</span>: <a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a>, /, *, <span className="py-api-param">relative</span>: <span className="py-api-type">bool</span> = <span className="py-api-default">False</span>) → <a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a></code></pre> <div className="py-api-description"> Move the scene and return its new origin. <p className="py-api-section-label">Parameters</p> <table className="py-api-fields"> <thead><tr><th>Parameter</th><th>Type / default</th><th>Description</th></tr></thead> <tbody> <tr><td><code>position</code></td><td><code><a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a></code><br /><span className="py-api-default">positional-only</span></td><td>Target position in meters.</td></tr> <tr><td><code>relative</code></td><td><code><span className="py-api-type">bool</span></code><br /><span className="py-api-default"> = False</span></td><td>Interpret the position relative to the current origin.</td></tr> </tbody> </table> <p className="py-api-section-label">Returns</p> <p><code><a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a></code> — The resulting scene origin.</p> </div> </section> ### `Scene.subscribe` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">async method</span> <code className="py-api-name">reference_demo.Scene.subscribe</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L48">Source ↗</a> </div> <pre className="py-api-signature"><code>async Scene.subscribe(<span className="py-api-param">handler</span>: <a className="py-api-type" href="/python-autodoc/display#eventhandler">EventHandler</a>, *, <span className="py-api-param">timeout</span>: <span className="py-api-type">float</span> = <span className="py-api-default">30.0</span>) → <span className="py-api-type">None</span></code></pre> <div className="py-api-description"> Receive position updates asynchronously. <p className="py-api-section-label">Parameters</p> <table className="py-api-fields"> <thead><tr><th>Parameter</th><th>Type / default</th><th>Description</th></tr></thead> <tbody> <tr><td><code>handler</code></td><td><code><a className="py-api-type" href="/python-autodoc/display#eventhandler">EventHandler</a></code><br /><span className="py-api-default">required</span></td><td>Callback invoked for each update.</td></tr> <tr><td><code>timeout</code></td><td><code><span className="py-api-type">float</span></code><br /><span className="py-api-default"> = 30.0</span></td><td>Maximum wait in seconds.</td></tr> </tbody> </table> <p className="py-api-section-label">Returns</p> <p><code><span className="py-api-type">None</span></code></p> <p className="py-api-section-label">Raises</p> <ul><li><code><span className="py-api-type">TimeoutError</span></code> — No update arrived before the timeout.</li></ul> </div> </section> ## `EventHandler` <section className="py-api"> <div className="py-api-header"> <span className="py-api-kind">type alias</span> <code className="py-api-name">reference_demo.EventHandler</code> <a className="py-api-source" href="https://github.com/dreamlake-ai/autodoc-py/blob/v0.2.0a1/examples/reference/__init__.py#L17">Source ↗</a> </div> <pre className="py-api-signature"><code>EventHandler = <span className="py-api-type">Callable</span>[[<a className="py-api-type" href="/python-autodoc/display#vector3">Vector3</a>], <span className="py-api-type">None</span>]</code></pre> <div className="py-api-description"> A callback receiving a position update. </div> </section> --- Source: https://dockit.dreamlake.ai/python-autodoc/release-notes # Release notes These notes track the **autodoc-py generator**, independently of Dockit and of the Python package being documented. The tab badge shows the generator version pinned by the documentation workspace. ## 0.2.0a1 — September 7, 2026 - Configure topic pages with `--page-map` instead of exposing every source module in the sidebar. - Add page introductions, linked API indexes, and namespaced module/symbol anchors. - Keep methods within their class sections and collapse repeated function overload headings. - Document public callable singletons, including `EnvVar` and `piter`, without importing source. - Format Google-style arguments, returns, and indented Python examples for MDX. - Harden Markdown fences and MDX escaping; support Python source encoding declarations. - Add README, license metadata, and public project links to built distributions. The first [PyPI prerelease](https://pypi.org/project/dreamlake-autodoc-py/0.2.0a1/). - Typeset signatures with linked local annotations, positional and keyword-only markers, and defaults. - Present parameter, return, exception, and attribute documentation in structured sections. - Include type aliases, NamedTuple fields, properties, async methods, and class bases. - Bundle the reference stylesheet with the Python package. See the [Python API display](/python-autodoc/display.md) for a rendered example. Validated against params-proto and all 11 modules in Vuer v0.0.29-rc11, including MDX compilation and internal links. ## 0.1.0 — initial source version The existing [`v0.1.0` tag](https://github.com/dreamlake-ai/autodoc-py/tree/v0.1.0) points to source revision [`247f6ba`](https://github.com/dreamlake-ai/autodoc-py/commit/247f6ba4f521419c79b3684d7eeea04ed9378621). This initial version was distributed from Git; it was not published to PyPI. ### Included - Parse Python source without importing the documented package or installing its dependencies. - Generate Dockit MDX pages for modules, public classes and functions, constructors, methods, and class attributes. - Include signatures, docstrings, source links, and navigation frontmatter. - Link local explicit and star re-exports and inherited members; suppress overridden member names. - Honor literal `__all__` for locally defined public classes and functions. - Generate API links that work with or without trailing slashes. - Track generated files in a manifest so removed modules are cleaned up without deleting hand-written pages. - Parse all input Python files before modifying output. ### Compatibility and limits Python 3.10 or later is required. There are no runtime dependencies. The source compatibility audit covered all 149 historical Vuer tags, including `tassa/`, `vuer/`, and `src/vuer/` package layouts. It checked generation and internal links, not runtime behavior or MDX compilation. See the [validation report](https://github.com/dreamlake-ai/autodoc-py/blob/v0.1.0/validation/vuer-tags-2026-09-07.json). Dynamic members, external inheritance and re-exports, full Python C3 method resolution, and Sphinx directive execution remain outside the generator's scope. See [static analysis boundaries](/python-autodoc.md#static-analysis-boundaries). ## Install this revision The distribution name is `dreamlake-autodoc-py`; the executable is `autodoc-py`. Install the prerelease with uv: ```bash uv tool install 'dreamlake-autodoc-py==0.2.0a1' autodoc-py --help ``` The same package requirement works with `python -m pip install`. See [Generate API docs](/python-autodoc/usage.md) for command options and build integration.