@apisurf/canonui
v0.2.1
Published
canonui — build and preview the documents that canon holds: render one into static HTML, or serve it locally.
Maintainers
Readme
@apisurf/canonui
The canonui command: build and preview the documents that canon holds.
npm i -g @apisurf/canonui
canonui ls # what is in the database, and what to build
canonui build retry-guide # -> ./dist/retry-guide
canonui serve retry-guide # build it, then open it in a browser
canonui serve # every document, behind a list of them
canonui open ./public # serve a folder that is already built
canonui helpFour commands, none of which writes anything. canon is the agent's half of the
pair — it creates and edits content, one record per command — and this is the
human's: it opens the same database read-only, says what is in it, and turns a
document into a folder of static files.
It is usable on its own. canonui ls prints the slugs the other commands take, and
build and serve resolve the only document themselves when there is one — so
nothing here sends you to canon to find out what to type.
Everything is documented in the command itself — canonui help, then
canonui help <command> — and in the repository README.
What is in here
Two halves of one package, and the seam between them is a JSON file.
| File | What it is |
| ---------------- | -------------------------------------------------------------- |
| src/bin.ts | Argument parsing and dispatch. One parser per command |
| src/list.ts | ls, and the document build picks when given none |
| src/db.ts | Opening the file canon owns, read-only and unmigrated |
| src/build.ts | Open the database, reduce a document to a snapshot, render it |
| src/serve.ts | serve — build a document, or all, then hand it to the server |
| src/home.ts | The page listing every document, in front of serve's builds |
| src/open.ts | open — work out which built folder was meant |
| src/http.ts | The static file server both of them end at |
| src/render.ts | Spawning Astro with the theme, parameterised by environment |
| src/help.ts | Every help page. One screen each |
| src/howto.ts | Worked examples: preview, publish, snapshot |
| src/format.ts | The listing ls prints, and its JSON |
| src/version.ts | The build's version, and the schema it expects |
| theme/ | The Astro theme — the only part that renders anything |
Only src/ links against SQLite, and only theme/ knows what HTML is. That is
what lets canonui build --snapshot-only hand the same input to some other
renderer, and what lets the theme be worked on without a database in reach.
The theme
One document builds into one page. One layout with three named slots (head,
aside, footer) and a default behind each, one component per block type, and
one stylesheet of plain CSS with tokens on :root.
The page is two columns: the document — title, description, a copy button, then every block in order — and a rail listing its titled blocks and the headings inside markdown blocks. There is no framework, and the client-side runtime is five small scripts: the wide toggle, the copy button, the one that puts it under each section's heading, the rail's scroll tracking, and mermaid when the document holds a diagram — bundled into the site rather than fetched from a CDN, so it works offline.
| File | What it is |
| ------------------------------------ | --------------------------------------------------- |
| theme/layouts/Site.astro | The shell, and every slot a theme would replace |
| theme/components/Block.astro | One block, drawn according to its type |
| theme/components/Toc.astro | The contents rail |
| theme/components/Copy.astro | The button that copies the document as markdown |
| theme/components/SectionCopy.astro | The same button, twice under each section's heading |
| theme/pages/index.astro | The document, at / |
| theme/styles/theme.css | The whole of the styling |
| theme/lib/snapshot.ts | The snapshot, read once at build time |
The markdown twin
/index.md is written by the command rather than the theme, with
bundleDocument — the same function behind canon cat, so what a reader copies
out of the site is byte for byte canon cat --toc --no-meta. The theme builds on
a machine that has no workspace to resolve that function from; the command
already carries it, which is what keeps the site and the CLI one renderer. The
copy button fetches it rather than embedding it, so the button wants the site
served rather than opened off the disk, where fetch has no origin to work
from; everything else on the page works either way.
Each top-level section is written the same way, to /sections/<anchor>.md,
with bundleSection. Where the sections fall is theme/lib/sections.ts, which
the command imports so that the files it writes and the headings the page puts
buttons on are cut by the same parse.
The theme sits under theme/ rather than Astro's default src/, which this
package uses for the command itself. astro.config.mjs points srcDir at it.
Build parameters
src/render.ts passes these to Astro as environment variables, so the package
stays immutable at build time and two builds cannot race over a config file.
| Variable | What it sets |
| ----------------- | ----------------------------------------------- |
| CANON_SITE_DATA | Path to the snapshot JSON. Required |
| CANON_OUT_DIR | Absolute path to write the site to. Required |
| CANON_BASE | Sub-path the site is served under, e.g. /docs |
| CANON_SITE_URL | Canonical origin, for absolute URLs |
| CANON_HOME | Href of the list of documents, for a link back |
