Everything about the project's documentation: how the doc-site is generated, what the house JSDoc comment style is, and how the living notes render into the site.
1. Generating the docs
One generator, one command (from the repo root):
npm run docs # node scripts/build-docs.mjs -> docs/jsdoc/
The project's single doc-site is JSDoc with the docdash template. npm run docs runs
scripts/build-docs.mjs, which:
- Wires the
notes/tree (and the repo docs) in as JSDoc tutorials. It walksnotes/**, copies each Markdown page into a flat tutorials dir (tmp/jsdoc-tutorials/, git-ignored) with a path-derived id, builds atutorials.jsonhierarchy that mirrors the folder tree (the role the old Doxygen_nav.doxplayed), and rewrites the inter-note Markdown links ([x](../reference/foo.md)) to the generatedtutorial-*.htmlpages so cross-links resolve. - Runs
jsdoc -c jsdoc.config.json, which renders the code API (from the@file+ per-function JSDoc comments) plus those tutorial pages intodocs/jsdoc/, withREADME.mdas the home.
Open docs/jsdoc/index.html. The docdash sidebar carries Tutorials (the whole notes tree, under
Project Notes and Project & Repository) and a Global list of every documented function, with a
search box.
Requirements: Node 24 + the dev dependencies (jsdoc, docdash) from npm install. No Doxygen, no
Graphviz — JSDoc parses ESM / export default natively, which is why it replaced Doxygen here.
The fairyfox docs-site theme
The doc-site is themed to match fairyfox.io (this project is a node in the fairyfox mesh — see
cross-project-sync.md). The theme lives in assets/docs-theme/ and is authored
from scratch — it replaces docdash's stylesheet rather than overriding it (so there's no
!important whack-a-mole against docdash's defaults, which is what leaked white backgrounds before):
fairyfox-docs.cssis the single authoritative stylesheet. It reproduces the hub's docs-site design tokens (dark-first warm palette with an OS-driven light theme, Fraunces/Inter/JetBrains type, the accent + focus ring) and drives docdash's whole DOM — the fixed sidebar<nav>,#main, the#nav-triggermobile drawer, the API member/signature/param blocks, tables, code. It also carries the ported hub.site-header+.subnavstyles for the injected chrome (below).fairyfox-docs.jsinjects, on every page, a copy of the fairyfox.io site-header (the Fairy Fox brand →fairyfox.io+ the hub's fixed mesh-wide primary nav: Home/Projects/Games/Docs/Updates/About) and a well-organized project subnav (the in-docs section bar: Overview · Project Notes · Systems · Reference · Changelog + Repository/Notes links, with the active item tracked by page). This mirrors the siblingfairyfox-gamesproject, which copied the hub header and added an organized subnav to its static site. The header + subnav go in one fixed.ff-topcontainer whose measured height feeds a--ff-header-hCSS var so docdash's sidebar/#mainclear it exactly (even when the subnav wraps); the docdash layout rules are scoped tobody > nav/.ff-top .wrapso they don't collide with the injected<nav>s (docdash's sidebar is itself<nav class="wrap">). A footer at the bottom of#mainlinks back to the main site. The script also injects the self-hosted shared fonts (assets/docs-theme/fonts/fonts.css— Fraunces/Inter/JetBrains served from this origin, no Google Fonts request, matching the project's privacy stance) + the light/darktheme-colormetas, so crossing the boundary from fairyfox.io has no visible jump. On narrow screens (≤820px) the primary hub nav collapses (brand still links home) and the subnav scrolls horizontally, leaving docdash's own hamburger as the only one.- The module sidebar is confined to the API pages. docdash's generated sidebar (Search + Modules +
Global) shows only on the code-reference pages, reached via the subnav's
APIitem (landingglobal.html). The Overview home and every notes tutorial render full-width with no sidebar —fairyfox-docs.jstags those pages with.ff-no-sidebar(isApiPage()= notindex.htmland not atutorial-*page) and the CSS hidesbody > nav+ the mobile hamburger and centres#main. On the API pages the sidebar is decluttered: theGitHub ↗docdash.menuentry is gone, and the script prunes docdash'sHomelink (Overview is home) and theTutorialssection (now the subnav). The subnav's external group carriesDownload ↗(→ GitHub releases),Repository ↗,Notes ↗. - Reading experience + Kindle-style reader menu. Overview + notes render in a centred reading column
with gentle typography driven by tunable
--reading-*CSS vars (font-size, line-height, letter-spacing, width); README/notes images are framed + sized to the column.build-docs.mjsstrips each tutorial's duplicate leading H1 +{#anchor}heading syntax, andfairyfox-docs.jsdrops docdash'sTutorial:prefix + duplicate<h2>. The palette adds a softer light (no stark white) and a sepia theme. An "Aa" button in the header opens a reader panel (Theme Auto/Light/Sepia/Dark · Text size · Line spacing · Width) that writes those vars +data-theme; prefs persist under the origin-widelocalStoragekeyfairyfox:reader, so the choice is shared across all same-origin fairyfox.io sites (the hub +fairyfox-gamesneed only read the same key).
Wiring (in build-docs.mjs, after JSDoc runs): the from-scratch fairyfox-docs.css is copied over the
generated docs/jsdoc/styles/jsdoc.css, replacing docdash's default sheet entirely; fairyfox-docs.js
- the vendored
fonts/(self-hosted Fraunces/Inter/JetBrains woff2 +fonts.css) are copied todocs/jsdoc/assets/docs-theme/(the pathjsdoc.config.json→docdash.scriptslinks).jsdoc.config.jsonalso setsdocdash.meta. This runs both locally and in thepages.ymlCI build. The generated API reference is a deliberately boundaried zone — fully themed via our sheet rather than a bespoke Jekyll shell. Published atfairyfox.io/random-ai-prompt/(GitHub Pages inherits the user-site custom domain; base path = repo slug — no projectCNAME).
Theme is modular (CSS @import partials + JS ES modules)
The theme is small, focused, browser-imported files — no bundler:
- CSS:
assets/docs-theme/fairyfox-docs.cssis a tiny entry of@imports. The real styles live inassets/docs-theme/theme/*.css—tokens.css(design tokens + the light/sepia/dark palettes +--reading-*),base.css(reset/type/links/code + the prettify syntax palette + skip link),layout.css(the docdashbody > navsidebar +#main+ mobile drawer),chrome.css(the injected header/subnav/footer),content.css(reading column + API blocks),reader.css(the Aa menu), anddownload.css. The build copies them intodocs/jsdoc/styles/theme/(so the@imports resolve). - JS:
assets/docs-theme/fairyfox-docs.jsis an ES-module entry thatimports fromassets/docs-theme/modules/*.js—util.js(constants + DOM helpers + page-type checks),chrome.js(skip link + header/subnav/footer + fonts/theme-color),sidebar.js(prune docdash's sidebar + de-dupe tutorial titles),reader.js(the Aa reader menu). The build copiesmodules/alongside and post-processes docdash's<script>tag totype="module"(docdash emits a plain script).
SEO / social + accessibility
build-docs.mjs post-processes every generated HTML page (crawler-visible, not JS-injected): a
per-page <meta name="description"> (pulled from the page's first paragraph), <link rel="canonical">,
Open Graph + Twitter Card tags, robots, and a JSON-LD WebPage/TechArticle block; it also ensures
<html lang="en">, gives docdash's icon-only drawer checkbox an accessible name, and writes a
sitemap.xml + robots.txt. The pages meet WCAG 2.1 AA — verified with @axe-core/playwright
(0 violations across Overview/notes/API/Download in both light and dark): visible focus, a skip link,
underlined in-text links (1.4.1), AA colour contrast on every token, keyboard-operable reader menu
(roles/labels, Escape, focus handling), and landmark/heading structure.
What's covered / what's not
- Code API: every authored
.jsundersrc/+ thedata/process-*.jsbuild scripts, and thetargets/web/React SPA (configjsdoc.config.json:source.includeaddstmp/webapp-docs). JSDoc can't parse JSX, sobuild-docs.mjsbabel-transpilestargets/web/src+ the Netlify function intotmp/webapp-docs(JSX stripped, comments kept) and JSDoc reads that mirror; the@moduletags give clean nav names.README.mdis the landing page (opts.readme). - Notes: the entire
notes/tree +list-credits.md/list-help.md/Upgrade-2-0.mdrender as tutorial pages, auto-discovered bybuild-docs.mjs(no manual nav file to maintain). - Not covered:
node_modules/,output/,tmp/source (only the generatedtmp/webapp-docsmirror is read), the local-onlyassets/, the builttargets/web/dist/, the vendoredweb/frontend/lib*.min.js, and the Pug templates / CSS (no JS doc generator parses Pug or CSS — those are covered conceptually in../systems/).
How the code is documented
JSDoc extracts a real per-function API (it handles ESM / export default where Doxygen could not):
- A
/** @file */header on every authored.js— 165 undersrc/+ the 3data/process-*.jsscripts; the vendoredlib/*.min.jsare left untouched. Each file gets a description, with richer multi-line module headers on the files with real logic. - Per-function JSDoc (
@param/@returns/description) on all server-side code, all 113 block generators, all top-levelweb/frontend/*functions, and the entiretargets/web/SPA (every lib function + provider + the Netlify handler + every React component, each file an@module). The only things without per-function docs are anonymous callbacks (Express route arrows, jQuery closures,$(document).ready, React inline event handlers), which no generator extracts. Quality varies by layer: the engine / core-logic / gui docs are bespoke; the blocks and the classic frontend handlers are accurate generated scaffolds (correct params/returns, humanized descriptions). - The notes pages carry the conceptual depth the code comments don't — the prompt DSL
(prompt-dsl.md), the block catalog (blocks.md),
and the system map (
../systems/) — and they live in the same site, as tutorials.
Terminology note: the /** … */ comments are "JSDoc comments" — pure JavaScript, no TypeScript.
Build inputs vs. gitignored reference
assets/ (the local-only reference area — e.g. the pinned pre-revival source snapshot) is gitignored,
but ESLint / Prettier still walk the filesystem, so it must be excluded in both (eslint.config.js
ignores, .prettierignore). JSDoc only reads its configured source.include, so it isn't affected.
Gitignored ≠ tool-ignored. See fix-patterns.md.
Files (the doc footprint)
jsdoc.config.json— the JSDoc config (source roots, the docdash template + options,opts.tutorials).scripts/build-docs.mjs— generates the note tutorials (+ link rewriting), babel-transpiles thetargets/web/JSX into a JSDoc-readable mirror, and runs JSDoc.docdash(devDependency) — the template (sidebar nav, search box).@babel/core+@babel/preset-react— used bybuild-docs.mjsonly, to strip JSX for documentation (not part of the app build).docs/jsdoc/,tmp/jsdoc-tutorials/, andtmp/webapp-docs/— generated output. git-ignored.
Adding or renaming a note
Nothing extra to maintain — build-docs.mjs auto-discovers every notes/**.md and places it in the
tutorial tree by its folder path (a folder's README.md becomes that section's hub; folders without one
get a synthetic hub). Keep cross-links relative ([x](../reference/foo.md), [x](sibling.md)) so the
build rewrites them to tutorial links.
2. Comment style (house rules — JSDoc)
The conventions documentation passes follow, so comments read as one consistent voice.
- Module docs at the top of the file. Open each module with a short block describing its role and
any non-obvious wiring (ownership, the
createRequireplugin-loading seam, thechdirordering constraint, Express-5 route caveats). The existingcore/*.jsheaders are the style reference. - Function docs with JSDoc tags where the signature isn't self-explanatory:
@param,@returns,@see. Keep them human — describe purpose, not the obvious restatement of the signature. //inline notes for the non-obvious "why" next to the code. Encouraged.- Describe purpose and role, not the mechanics a reader can see. Call out the landmines documented
in
esm-patterns.md.
Hard rules
- Never delete an existing human-written comment. Merge its meaning if it overlaps a new block; otherwise leave it untouched.
- A documentation pass changes only comments — never a line of code. Verify before committing.
- Preserve the licence header where present.
3. Status
The doc-site builds clean — npm run docs (JSDoc + docdash, exit 0, ~244 pages): the README home, the
per-function code API, and the whole notes/ tree as tutorial pages with working cross-links.
Documentation coverage is complete (2026-06-18): @file on every authored .js, and per-function
JSDoc across the whole repo — all server-side code, all 113 blocks, all top-level
web/frontend/* functions, and the entire targets/web/ React SPA (16 modules: every lib function +
provider + the Netlify handler + each React component, via the babel-transpile-then-JSDoc path). Only
anonymous callbacks remain, which no generator extracts. Doxygen was retired here (2026-06-18) in
favour of the single JSDoc site — it couldn't parse the anonymous export default plugins, and one tool
now does the code API (incl. JSX) and the notes.