## Overview

zudo-doc can show the git revision history for each documentation page. With `docHistory` enabled and the `DocHistory` chrome binding installed, a **History** button appears on each detail page, opening a side panel where you can browse past revisions and compare any two versions with a line-by-line diff viewer.

This is useful for:

- Reviewing how a document has evolved over time
- Understanding what changed between revisions
- Auditing content changes across your documentation

[Dynamic content unavailable in text export]

This feature requires that your documentation is tracked in a git repository. History is extracted from git commits that touched each file.



## Enabling Document History

Set `docHistory` to `true` in `zfb.config.ts`:

```ts title="zfb.config.ts"
export default defineConfig(
  zudoDoc({
    // ...
    docHistory: true,
  }),
);
```

Fresh scaffolds statically wire the real History island into all generated route shapes when `docHistory` is selected. No bindings module or route edit is required for the button. An older existing scaffold must update each document stub to the current generated shape: import `DocHistory` statically and overlay it after `chromeBindings`, so the island remains scanner-reachable without dropping other host slots.

The feature is **disabled by default** to keep builds fast for projects that don't need it.

[Dynamic content unavailable in text export]

The Compare view's line-by-line diff rendering needs the optional peer dependency `diff`. `create-zudo-doc` adds it to `package.json` automatically whenever `docHistory` is selected (directly, or through `bodyFootUtilArea`, which forces `docHistory` on). If you flip `docHistory` on by hand in an existing project instead of through the generator, run `pnpm add diff` yourself — see [Configuration — docHistory](https://zudo-doc.takazudomodular.com/docs/guides/configuration#site-settings-dochistory) for the full optional-peer contract, which mirrors `math`'s `katex` requirement.



## Dates-Only Mode

To keep git-based metadata without the history viewer, hand-edit `zfb.config.ts`. There are no new CLI flags for `docHistoryUi` or `docMetainfoFields`.

```ts title="zfb.config.ts"
export default defineConfig(
  zudoDoc({
    docHistory: true,
    docHistoryUi: false,
    docMetainfo: true,
    docMetainfoFields: ["updated"],
  }),
);
```

The preBuild Created/Updated/Author manifest is retained. Displaying metadata still requires passing that manifest to `docHistoryMeta` through `chromeBindingsModule` (see the [configuration reference](https://zudo-doc.takazudomodular.com/docs/guides/configuration#site-settings-docmetainfo)). `["updated"]` shows Updated even when its formatted value matches Created; `[]` renders no metadata block.

`docHistoryUi: false` suppresses the History button and DocHistory island, the plugin's history JSON generation, and its dev proxy. The view-source link remains available. Neither per-page `doc_history: true` nor `bodyFootUtilArea.docHistory: true` can override this global setting.

If your host has a separate CI `build-history` job, remove that job and its artifact upload, download, and merge steps. The standalone generator CLI does not read site settings and still generates whenever invoked. You can also remove the unused `dev:history` process from host development scripts. Changing the setting does not delete these scripts or existing output files.

### Environment-Variable Interactions

This table assumes `docHistory: true`. Earlier rows take precedence. “Normal” means preBuild generates the manifest from the available git history.

| Condition | preBuild metadata | JSON with `docHistoryUi: true` | JSON with `docHistoryUi: false` |
|---|---|---|---|
| `SKIP_DOC_HISTORY=1` | Empty manifest | Skipped | Skipped |
| `DOC_HISTORY_SKIP_POSTBUILD=1` | Normal | Skipped | Skipped (redundant setting) |
| `GEN_DOC_HISTORY=1` | Normal | Generated | Skipped (UI setting wins) |
| `CI` / `GITHUB_ACTIONS` active | Normal | Generated | Skipped (UI setting wins) |
| Otherwise, local build | Normal | Skipped | Skipped |

Leave `SKIP_DOC_HISTORY=1` unset to retain dates. `DOC_HISTORY_SKIP_POSTBUILD=1` is redundant with `docHistoryUi: false`. The viewer, JSON, and proxy sections below describe behavior with `docHistoryUi` enabled.

## Per-Page Visibility

Once `docHistory` is on globally, each page decides whether to display the **History** button via two rules:

1. **Auto-suppression on category-index pages** — pages whose entry id ends with `/index` (for example, `guides/index.mdx`) hide the button by default. These pages are typically navigation hubs or landing pages, where a revision history is rarely useful.
2. **Detail pages show the button by default** — every concrete leaf page (anything that is not a category index) shows the button.

You can override the default per page with the `doc_history` frontmatter flag:

```yaml
---
title: My Page
doc_history: false # always hide the button on this page
---
```

```yaml
---
title: My Category Index
doc_history: true # opt back in even though this is an index page
---
```

When global `docHistory` and `docHistoryUi` are enabled, frontmatter overrides the page-type default in both directions:

| Page type | No frontmatter | `doc_history: true` | `doc_history: false` |
| --- | --- | --- | --- |
| Detail page (leaf) | shown | shown | hidden |
| Category-index page (`*/index`) | hidden | shown | hidden |

Use `doc_history: true` on an index page when its content is substantial enough to warrant version tracking (for example, a category landing page with significant prose). Use `doc_history: false` on a detail page that is generated, ephemeral, or otherwise should not surface its commit log.

## Using the History Viewer

Once enabled, each detail page shows a **History** button inside the [body foot util area](https://zudo-doc.takazudomodular.com/docs/guides/body-foot-util-area) — the small right-aligned strip between the article and the footer. Category-index pages hide the button by default; see [Per-Page Visibility](https://zudo-doc.takazudomodular.com/docs/guides/doc-history#per-page-visibility) above.

### Browsing Revisions

Click the button to open the revision panel. It slides in from the right and shows all git commits that modified the current document, newest first. Each entry displays:

- **Commit hash** (short form)
- **Date** of the commit
- **Author** name
- **Commit message** (first line)

### Comparing Revisions

To view a diff between two revisions:

1. Select **A** (the older revision) by clicking the **A** button next to a commit
2. Select **B** (the newer revision) by clicking the **B** button next to another commit
3. Click the **Compare** button

The diff viewer shows a **side-by-side comparison** — the older revision on the left, the newer revision on the right:

- **Green background** — lines added in the newer revision (right side)
- **Red background** — lines removed from the older revision (left side)
- **Gray cells** — empty placeholders where one side has no corresponding line
- **Unchanged lines** — context shown on both sides

[Dynamic content unavailable in text export]

By default, A is set to the second-most-recent commit and B to the most recent, so you can immediately compare the latest change by clicking **Compare**.



### Keyboard Shortcuts

- **Escape** — closes the history panel
- The panel also closes automatically when navigating to another page

## How It Works

Git history extraction is handled by a standalone package: `@takazudo/zudo-doc-history-server` (located at `packages/doc-history-server/`). This package operates in two modes, one for development and one for production builds.

### Build Mode

In production, a standalone CLI generator batch-processes all content files and writes JSON history files to `dist/doc-history/`. This runs as an independent CI job, parallel to the zfb site build:

- **build-site** job: full clone (`fetch-depth: 0`), builds the zfb site with `pnpm build` (no `SKIP_DOC_HISTORY` — preBuild needs full git history for the SSG meta block)
- **build-history** job: full clone (`fetch-depth: 0`), runs `@takazudo/zudo-doc-history-server generate`
- **deploy** job: merges both artifacts and deploys

This parallel strategy reduces total CI build time because the history generation (which requires a full git clone and walks every commit) runs independently of the zfb build.

[Dynamic content unavailable in text export]

The per-page dropdown JSON in `dist/doc-history/` is generated automatically in CI, but for **local** builds it is opt-in. A plain `pnpm build` followed by `pnpm preview` will **not** show the history dropdown — the Created/Updated/Author block still appears, only the dropdown data is absent. To include it, build with:

```sh
GEN_DOC_HISTORY=1 pnpm build
```

This is useful when previewing a locally-built `dist/` or bundling an offline reader. See the [deployment guide](https://zudo-doc.takazudomodular.com/docs/guides/deployment) for details.

Conversely, a shallow-clone CI variant that wants to keep the Created/Updated/Author metadata but explicitly skip this same postBuild step can set `DOC_HISTORY_SKIP_POSTBUILD=1` instead of `SKIP_DOC_HISTORY=1` — see the deployment guide's decision table.



The output structure mirrors the content directory:

```
dist/doc-history/
  getting-started.json
  guides/writing-docs.json
  guides/color.json
  ja/getting-started.json      # locale-prefixed
  ja/guides/writing-docs.json
```

### Dev Mode

In dev mode, the doc-history-server runs as a standalone REST API server on port 4322, launched concurrently with the zfb dev server. This is not automatic just from setting `docHistory: true` in an existing project — a fresh `create-zudo-doc` scaffold with `docHistory` selected emits a `dev` script of `run-parallel dev:zfb dev:history` (running `zfb dev` and the installed `doc-history-server` bin concurrently) specifically because a plain `zfb dev` never starts the :4322 server on its own. The doc-history zfb plugin's `devMiddleware` hook proxies `/doc-history/*` requests from the browser to this server.

Because `run-parallel` does not forward trailing args, `pnpm dev -- --host 0.0.0.0` fails with an error naming the remedy rather than being silently ignored (`run-p`, which it replaced, ignored it silently) — use the dedicated `pnpm dev:network` script instead, which binds zfb to `0.0.0.0` for LAN access while the doc-history server stays loopback-only (reached via the same `/doc-history/*` proxy). For any other zfb flag, forward it directly to the zfb process with `<pm> run dev:zfb -- <flags>` rather than through `pnpm dev`.

When you open the history panel, it runs git commands on the fly -- no pre-generation needed. The server's file index refreshes every 10 seconds, so new or renamed files are picked up automatically. This means history is always up-to-date with your latest commits.

[Dynamic content unavailable in text export]

Build time increases when `docHistory` is enabled because git history must be extracted for every content file. For large documentation sites with many files and deep history, this can add noticeable time to the build. The default limit is **50 revisions** per document.



### Production Output Size

Since zudo-doc generates fully static sites with no server-side runtime, there is no server available to run `git` commands at request time. Instead, the build step pre-extracts all history data and ships it as static JSON files in the `dist/doc-history/` directory.

Each JSON file contains the **full file content at every revision** (up to 50 commits). This means the total output size scales with:

- Number of documentation files
- Number of git commits per file
- Size of each file at each revision

For a small-to-medium documentation site this is typically negligible. For large sites with deep history, the `doc-history/` directory can grow significantly. These files are only fetched on demand (when a user clicks the History button), so they do not affect initial page load — but they do add to your deployment size.

[Dynamic content unavailable in text export]

If you need metadata but not revision browsing in production, use `docHistory: true` with `docHistoryUi: false` and remove the host's standalone history-generation job. This keeps dates without shipping revision JSON. Set `docHistory: false` when you do not need either feature.



## Localization Support

Document history works with all configured locales. Each locale's content directory produces its own set of history files, prefixed with the locale code:

- English docs: `/doc-history/{slug}.json`
- Japanese docs: `/doc-history/ja/{slug}.json`
- German docs: `/doc-history/de/{slug}.json`

The history panel automatically requests the correct locale-specific history based on the current page.

## Technical Details

### Data Format

Each document's history is stored as a JSON file with the following structure:

```ts
interface DocHistoryData {
  slug: string;          // Document route path
  filePath: string;      // File path in the repository
  entries: Array<{
    hash: string;        // Full commit hash
    date: string;        // ISO 8601 date
    author: string;      // Commit author name
    message: string;     // Commit message (first line)
    content: string;     // Full file content at this revision
  }>;
}
```

### Diff Algorithm

The diff viewer uses the [`diff`](https://www.npmjs.com/package/diff) library's `diffLines` function to compute line-level differences between two revisions, displayed in a side-by-side table layout similar to GitHub's split diff view. Adjacent removed and added blocks are paired into the same rows so you can see what changed at a glance.

### Rename Tracking

The history extraction uses `git log --follow` to track file renames. If a document was moved or renamed, its full history (including commits before the rename) is preserved.

[Dynamic content unavailable in text export]

The History component is a client island that hydrates lazily — it only kicks in after the page becomes idle, so it doesn't impact initial page load performance.



## The doc-history-server Package

The history extraction logic lives in a standalone package (`packages/doc-history-server/`) rather than inside the zfb build pipeline. This design enables:

- **Parallel CI builds** -- history generation and site build run as independent jobs
- **Independent development** -- the server can be developed, tested, and versioned separately
- **Flexible usage** -- the same package provides both a dev server and a CI generator

For full details on the REST API endpoints, CLI arguments, data types, and architecture, see the [Doc History Server reference](https://zudo-doc.takazudomodular.com/docs/reference/doc-history-server).