zed/docs
morgankrey 10504e3ce1
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Improve docs AI readiness (#59577)
Context

This PR makes the Zed docs easier for AI tools, search crawlers, and
users to consume without changing the visible docs content. The current
production docs are primarily optimized for browser navigation. They do
not expose first-class Markdown URLs, an `llms.txt` index, page-level
copy affordances, or machine-readable freshness metadata that let users
and agents grab clean, current page content.

Changes

- Generate Markdown copies for docs pages during the mdBook postprocess
step, including `/docs/index.md` as an alias for Getting Started.
- Generate `/docs/llms.txt` from the mdBook chapter list, grouped by
`SUMMARY.md` sections and annotated with page frontmatter descriptions.
- Generate `/docs/sitemap.xml` with `<lastmod>` values for every docs
page.
- Emit machine-readable freshness metadata in HTML via `last-modified`
and `article:modified_time` meta tags.
- Generate Cloudflare Pages `_redirects` for `.html`, extensionless, and
`.md` redirect variants, with channel-aware docs destinations.
- Add discovery hints for agents and crawlers: `rel="llms.txt"`,
`rel="alternate" type="text/markdown"`, and a short generated `llms.txt`
directive in copied Markdown pages.
- Update the docs proxy so `Accept: text/markdown`, `/docs.md`, and
direct `.md` requests can resolve to the generated Markdown artifacts.
- Move primary docs content earlier in the HTML source while preserving
the visible layout, so crawlers and agent scorers encounter the article
before sidebar chrome.
- Move the existing copy-as-Markdown control from the top navigation
into the page-title row, using the generated Markdown alternate link as
the source of truth.
- Split AI-discovery artifact generation out of
`docs_preprocessor/src/main.rs` into a focused module.

Best Practices Adopted

- Use `llms.txt` as a concise navigation index, not a dump of full page
content.
- Link to absolute, canonical Markdown URLs from `llms.txt`.
- Preserve the docs hierarchy in `llms.txt` instead of emitting a flat
sitemap-like list.
- Include short per-link descriptions from existing metadata rather than
inventing summaries.
- Keep `llms.txt`, Markdown copies, sitemap data, redirects, and
freshness metadata generated from the same mdBook source to avoid drift.
- Advertise Markdown alternates with standard HTML metadata and
same-origin URLs.
- Support both explicit Markdown URLs and content negotiation for
clients that prefer Markdown.
- Keep browser copy behavior pointed at generated Markdown alternate
links instead of duplicating route inference in JavaScript.
- Keep the copy-as-Markdown affordance in the page title row without
duplicating header chrome controls.

Validation

- `cargo check -p docs_preprocessor`
- `cargo test -p docs_preprocessor`
- `./script/clippy -p docs_preprocessor`
- `mdbook build ./docs --dest-dir=../target/deploy/docs/`
- `node --check docs/theme/plugins.js`
- `pnpm dlx prettier@3.5.0 docs/theme/plugins.js --check`
- `git diff --check`
- Local artifact checks confirmed generated Markdown pages, `llms.txt`,
`sitemap.xml` lastmod values, HTML freshness metadata, Markdown
alternate links, redirect targets, and preprocessed action/keybinding
tags resolve as expected.
- Worker URL rewrite mock covered `/docs/`, `/docs.md`,
`/docs/index.md`, extensionless docs routes, `.html` routes,
`/docs/llms.txt`, and `/docs/sitemap.xml`.
- High-effort adversarial subagent review found blockers around
channel-aware redirects, shallow-checkout date fallback, file size,
duplicated Markdown path inference, and process spawning. Those were
addressed.

Remaining Notes

- Local `python -m http.server` does not emulate Cloudflare Pages pretty
URLs, `_redirects`, or the docs-proxy Worker, so full local `afdocs`
still cannot prove content negotiation end to end.
- Existing production remains unchanged until this PR is deployed
through the docs workflow.
- Production baseline `npx afdocs check https://zed.dev/docs/ --fixes
--verbose` still reports the original failures before this PR is
deployed: 12 passed, 8 failed, 3 skipped.

Release Notes:

- Improved docs AI-readiness by adding machine-readable discovery,
Markdown access, and freshness metadata.

---------

Co-authored-by: Katie Geer <katie@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-07-08 20:54:51 +00:00
..
.conventions Restructure AI docs (#57614) 2026-06-04 15:55:21 +00:00
.doc-examples agent: Sandboxing polish (#60173) 2026-07-01 14:55:30 +00:00
src Add support for format_on_save only changed lines (#49964) 2026-07-08 16:53:11 +00:00
theme Improve docs AI readiness (#59577) 2026-07-08 20:54:51 +00:00
.gitignore Setup docs deployments with mdBook (#11369) 2024-05-03 15:52:15 -04:00
.prettierignore docs: Add consent banner (#50302) 2026-03-04 15:38:31 +01:00
.prettierrc ci: Add check for formatting default.json (#30034) 2025-05-06 18:55:26 +00:00
.rules Update AI docs for retired hosted models (#49486) 2026-02-18 10:36:38 -06:00
AGENTS.md docs: Update actions format (#54869) 2026-05-07 06:55:11 +00:00
book.toml Improve docs AI readiness (#59577) 2026-07-08 20:54:51 +00:00
README.md agent: More sandboxing changes (#60111) 2026-06-30 11:10:04 +00:00

Zed Docs

Welcome to Zed's documentation.

This is built on push to main and published automatically to https://zed.dev/docs.

To preview the docs locally you will need to install mdBook (cargo install mdbook@0.4.40), generate the action metadata, and then serve:

script/generate-action-metadata
mdbook serve docs

The first command dumps an action manifest to crates/docs_preprocessor/actions.json. Without it, the preprocessor cannot validate keybinding and action references in the docs and will report errors. You only need to re-run it when actions change.

If you use Nix, the development shell provides a pinned mdbook (0.4.40) and a prebuilt docs preprocessor, so you can build the docs without installing anything or compiling the preprocessor on every run:

nix develop -c mdbook build docs

(When actions.json has not been generated, action/keybinding validation is skipped with a warning rather than failing the build.)

It's important to note the version number above. For an unknown reason, as of 2025-04-23, running 0.4.48 will cause odd URL behavior that breaks things.

Before committing, verify that the docs are formatted in the way Prettier expects with:

cd docs && pnpm dlx prettier@3.5.0 . --write && cd ..

Preprocessor

We have a custom mdBook preprocessor for interfacing with our crates (crates/docs_preprocessor).

If for some reason you need to bypass the docs preprocessor, you can comment out [preprocessor.zed-docs-preprocessor] from the book.toml.

Images and videos

To add images or videos to the docs, upload them to another location (e.g., zed.dev, GitHub's asset storage) and then link out to them from the docs.

Putting binary assets such as images in the Git repository will bloat the repository size over time.

Internal notes:

  • We have a Cloudflare router called docs-proxy that intercepts requests to zed.dev/docs and forwards them to the "docs" Cloudflare Pages project.
  • The CI uploads a new version to the Cloudflare Pages project from .github/workflows/deploy_docs.yml on every push to main.

Table of Contents

The table of contents files (theme/page-toc.js and theme/page-doc.css) were initially generated by mdbook-pagetoc.

Since all this preprocessor does is generate the static assets, we don't need to keep it around once they have been generated.

Referencing Keybindings and Actions

When referencing keybindings or actions, use the following formats:

Keybindings

{#kb scope::Action} - e.g., {#kb zed::OpenSettings}.

This will output a code element like: <code>Cmd + , | Ctrl + ,</code>. We then use a client-side plugin to show the actual keybinding based on the user's platform.

By using the action name, we can ensure that the keybinding is always up-to-date rather than hardcoding the keybinding.

Keymap Overlays

{#kb:keymap_name scope::Action} - e.g., {#kb:jetbrains editor::GoToDefinition}.

This resolves the keybinding from a keymap overlay (e.g., JetBrains) first, falling back to the default keymap if the overlay doesn't define a binding for that action. This is useful for sections where the documentation expects a special base keymap to be configured.

Supported overlays: jetbrains.

Actions

{#action scope::Action} - e.g., {#action zed::OpenSettings}.

This will render a human-readable version of the action name, e.g., "zed: open settings", and will allow us to implement things like additional context on hover, etc.

Creating New Templates

Templates are functions that modify the source of the docs pages (usually with a regex match and replace). You can see how the actions and keybindings are templated in crates/docs_preprocessor/src/main.rs for reference on how to create new templates.

We pre-bundle the c15t package because the docs pipeline does not include a JS bundler. If you need to update c15t and rebuild the bundle, use:

mkdir c15t-bundle && cd c15t-bundle
npm init -y
npm install c15t@<version> esbuild
echo "import { getOrCreateConsentRuntime } from 'c15t'; window.c15t = { getOrCreateConsentRuntime };" > entry.js
npx esbuild entry.js --bundle --format=iife --minify --outfile=c15t@<version>.js
cp c15t@<version>.js ../theme/c15t@<version>.js
cd .. && rm -rf c15t-bundle

Replace <version> with the new version of c15t you are installing. Then update book.toml to reference the new bundle filename.

References

  • Template Trait: crates/docs_preprocessor/src/templates.rs
  • Example template: crates/docs_preprocessor/src/templates/keybinding.rs
  • Client-side plugins: docs/theme/plugins.js

Postprocessor

A postprocessor is implemented as a sub-command of docs_preprocessor that wraps the built-in HTML renderer and applies post-processing to the HTML files, to add support for page-specific title and meta tag description values.

An example of the syntax can be found in git.md, as well as below:

---
title: Some more detailed title for this page
description: A page-specific description
---

# Editor

The above code will be transformed into (with non-relevant tags removed):

<head>
  <title>Editor | Some more detailed title for this page</title>
  <meta name="description" contents="A page-specific description" />
</head>
<body>
  <h1>Editor</h1>
</body>

If no front matter is provided, or if one or both keys aren't provided, the title and description will be set based on the default-title and default-description keys in book.toml respectively.

Implementation details

Unfortunately, mdBook does not support post-processing like it does pre-processing, and only supports defining one description to put in the meta tag per book rather than per file. So in order to apply post-processing (necessary to modify the HTML head tags) the global book description is set to a marker value #description# and the HTML renderer is replaced with a sub-command of docs_preprocessor that wraps the built-in HTML renderer and applies post-processing to the HTML files, replacing the marker value and the <title>(.*)</title> with the contents of the front matter if there is one.

Known limitations

The front matter parsing is extremely simple, which avoids needing to take on an additional dependency, or implement full YAML parsing.

  • Double quotes and multi-line values are not supported, i.e. Keys and values must be entirely on the same line, with no double quotes around the value.

The following will not work:

---
title: Some
  Multi-line
  Title
---

neither this:

---
title: "Some title"
---
  • The front matter must be at the top of the file, with only white-space preceding it.
  • The contents of the title and description will not be HTML escaped. They should be simple ASCII text with no unicode or emoji characters.