Terrain Viewer

Fumadocs integration

How this documentation site uses Fumadocs — the visual editor (Studio), the added components and generators, and the Next base-path fixes

The docs are a Next.js app under docs/, on Fumadocs with the Base UI preset, exported statically and served by the main app under /docs. The visual editor comes first, then everything that was added to the stock setup; the rest is Fumadocs as shipped.

Visual editing with Fumadocs Studio

Fumadocs Editor (experimental, 0.x since 2026-01) edits MDX visually with the files as the source of truth. Its standalone form, Studio, needs no change to the site: it opens content/docs in a browser, autosaves to disk, merges edits made in a code editor into the open document, and the normal commit and deploy publish the result. Checked on 2026-10-13 against every page here: all 75 parse, an unedited page serializes back byte for byte, and a one-word edit changes one line, except on the three pages whose prose sits inside a raw <section> wrapper (this one's references page, equations, the advocacy post drafts), where the whole wrapper is re-indented.

cd docs && pnpm studio        # http://localhost:5180; `pnpm docs` (or `pnpm dev` at the root) starts it next to next dev

fumadocs-studio.config.tsx next to the content turns on math ($ blocks are plain text otherwise) and registers component specs: the editor never runs the site's components, a spec gives it a light stand-in to draw in visual mode while the source tab keeps the raw JSX. Gallery is drawn from its groups literal (hero and filmstrip); the data-driven components (the reference tables, the changelog, the embed builder, the maps) are labelled cards. Callout, Cards, Tabs, Accordions, Steps and Files come with the editor. The config's server entry points at fumadocs-studio.server.mjs, the Node-only half: it serves docs/public so the screenshots show in visual mode (prose images are written /screenshots/..., the Gallery literals /docs/screenshots/..., and a middleware drops that prefix). A component without a spec shows its tags as text with the children editable; imports, expressions and anything unknown round-trip verbatim. Studio edits existing files only: create, rename and delete pages in the code editor.

Content components

Used from MDX, registered in docs/src/components/mdx.tsx or imported per page.

ComponentFileWhat it does
Lightboxlightbox.tsx, feature-lightbox.tsxEvery prose image is click-to-open; the page's images form one loop with left/right arrow keys. mdx.tsx tags each img for it.
Gallerygallery.tsxMulti-image rows with captions, grouped, sharing the page lightbox.
VizModeGridviz-mode-grid.tsxThe visualization modes matrix with thumbnails and links into the app.
Mermaidmermaid.tsxDiagrams from fenced blocks (remarkMdxMermaid in source.config.ts) rendered at build time with beautiful-mermaid: no Mermaid runtime in the browser, one SVG per colour scheme in the static HTML (since 2026-10-15; flowchart, class, sequence, state, ER and gantt only).
NationalDatasetsTablenational-datasets-table.tsxThe national terrain sources page: a world map of coverage (world-110m.json, built by scripts/build-world-110m.mjs) plus the sortable table, both read from the app's lib/custom-sources.json at build time.
EmbedBuilderembed-builder.tsxThe link / iframe builder on the Embedding page; can start from a pasted link.
ProjectConfigTableproject-config-table.tsxThe ProjectConfig interface rendered from lib/project-config.ts with fumadocs-typescript's AutoTypeTable, JSDoc included.
UrlParamsReference, InstructionParamsReference, StorageAtomsReferenceurl-params-reference.tsxThe state reference tables, from src/generated/url-params.json.
ScalarReferencescalar-reference.tsxScalar's API reference over the generated openapi.json (URL as an API).

Generators (run before next dev / next build)

  • scripts/build-url-params.mjs scans components/TerrainViewer.tsx for the nuqs parsers, the atom files for atomWithStorage, and writes src/generated/url-params.json and public/openapi.json. Both outputs are tracked in git and regenerated by hand with pnpm run url-params (root) when parameters change or before a release; the docs build regenerates them regardless, so the deployed site is always current, and the deploy workflow warns when the tracked copies are stale.
  • scripts/build-world-110m.mjs builds the simplified country outlines for the national datasets map.

Changelog

changelog-list.tsx parses the repository's CHANGELOG.md (the same file the app's Settings dialog renders) into release entries, and changelogToc() builds the page's right-hand table of contents from the release headings, wired in app/[...slug]/page.tsx. Screenshots referenced from the changelog live in public/screenshots/ and are served to the app too.

Shared source with the app

Some pages are the single source of truth for text the app shows: features/keyboard-shortcuts.mdx and the visualization modes description are imported into the app with Vite's ?raw, and the Vite dev server proxies /docs to this site while serving /docs/content/ itself so those raw imports work in development.

Base-path fixes

The site runs under basePath: "/docs", which one stock piece does not handle:

  • mdx.tsx leaves root-relative links to static assets as plain anchors, so a link to a file the main app serves is not rewritten into the docs subtree.

The page actions (Copy Markdown, Open in ChatGPT and the others) are the stock ones since 2026-10-14. Their withBasePath reads import.meta.env.BASE_URL, which Next's Turbopack derives from basePath since Next 16.3 (vercel/next.js#96225, 2026-08), so the markdown URL is passed to them without the base path. The site carried its own copy of the popover with a pre-prefixed URL from 2026-08-20; once Next 16.3 was in, that pre-prefixing doubled the base path on the Copy Markdown fetch, which is why the copy went.

Other

  • provider.tsx and search.tsx: the root provider and a static search index, since there is no server at runtime.
  • source.config.ts: remark-math + rehype-katex for equations (dev/equations), ordered so KaTeX runs before Shiki.
  • app/global.css: KaTeX styles and the lightbox cursor.

On this page