The core vocabulary of the Muse codebase — enough to read the source and AGENTS.md
without getting lost. For the architecture, boot order, and the do-not-break rules, go to
AGENTS.md.
Mental model. Muse renders one preview: a chosen font × theme ×
language, highlighted by Shiki. Everything selectable is an
asset with a stable id, sourced either from the repo (data/*.json, listed in the
catalog) or uploaded at runtime (kept in localStorage). A pub/sub state store holds
the selection, mirrors it to localStorage and the URL hash, and notifies subscribers
that re-render. That's the whole app.
- Asset — anything selectable: a font, theme, or language. Each has a unique id and shows up as a pill.
- Font / Theme / Language — the three asset kinds. A theme is VSCode theme JSON
(
colors+tokenColors). A language pairs a manifest with a sample code file. - Preview — the rendered code block in the center pane (
renderPreview()insrc/preview.js). The app's single visual output.
- id / canonical id / filename stem — the one identifier for an asset: the JSON
filename stem === the
idfield inside it, and that id is what lives in the URL hash,localStorage, and Shiki's registry.rebuild-index.mjsenforces the match. - Manifest — the small JSON describing a font or language. Font:
id,name,stack, optionalcssUrl/ligatures. Language:id,label,shikiLang,sample,summary. (Themes have no manifest — the theme JSON is the asset.) - catalog — the in-memory set of valid ids (
{fonts, themes, languages}) instate.js. EverysetStatevalidates against it; an unknown id falls back to a default with aconsole.error. Runtime adds/removes must keep it in sync (extendCatalog/removeFromCatalog) or a freshly uploaded selection bounces back to default. data/_index.json— the catalog on disk: the auto-generated list of repo asset ids, fetched at boot. Never hand-edit —rebuild-index.mjs/ CI regenerate it.
- Repo asset — backed by a committed
data/file, listed in_index.json, travels in shared URLs. - Runtime asset — uploaded in-browser, persisted in
localStorageonly. A shared URL pointing at one falls back to defaults on another device. - Built-in theme — a theme Shiki ships (names in
data/themes/_builtin.json); available with no repo file. The committed repo themes are currently light themes Shiki doesn't ship. - Installed / found font — a font already on the visitor's machine: either auto-detected
from the
LOCAL_FONTSprobe (fonts.js, via a canvas-width trick) or found by the user typing its name. No download needed. - Custom font / theme — a runtime upload (URL,
@font-face, or theme JSON). Its id gets acustom-prefix (slugify()inui/uploaders.js) so it can't shadow a repo id — repo files must never use that prefix (CI rejects them). For themes there are two layers: the persisted custom theme vs its live in-memory runtime theme (runtimeThemes/getRuntimeTheme).
- State store — the pub/sub store in
src/state.js:getState(read a copy),setState(write + persist + notify),subscribe(fires immediately, then on change). Defaults:jetbrains-mono/one-dark-pro/python. - State precedence —
URL hash > localStorage > defaults. On load the hash wins; on change,localStorageand the hash update together. - URL hash — the shareable serialization of the setup. The app writes it via
history.replaceState(no feedback loop); a pasted hash applies live viahashchange. - localStorage keys —
muse:state(selection),muse:custom-fonts,muse:custom-themes,muse:found-fonts. All JSON; corrupt data falls back to defaults rather than crashing.
- Highlighter — the Shiki instance (
src/themes.js), created once and reused via the cachedgetHighlighter()promise. Loaded fromesm.sh. - Theme name override — before handing a custom theme to Shiki, do
{ ...raw, name: id }so Shiki, the hash, andlocalStorageagree on the canonical id. Critical invariant. - Render token / race guard —
renderTokeninpreview.js: each render grabs a token and bails if a newer one started, so out-of-order async renders never paint.
- Pill — a selectable chip: a font/theme in a sidebar (fonts left, themes right) or a
language in the lang tabs. Custom pills are removable;
search.jsfuzzy-filters them. - Uploader dialog — the Add Font / Add Theme modals that create runtime assets. Theme colors are validated hex-only to block CSS injection through Shiki's inline styles.
- Export — turns the current setup into a paste-ready VS Code
settings.json(ui/export.js).
- No-build-step — plain HTML + vanilla ES modules + JSON. No bundler/tests/linter; Shiki
loads at runtime from
esm.sh. Deployed as GitHub Pages from the repo root. rebuild-index.mjs— the only script: validates every asset and regenerates_index.json.--checkvalidates without writing (PR CI). Node 18+, zero deps..nojekyll— the empty root file that stops Jekyll dropping_-prefixed files (_index.json,_builtin.json). Without it the app won't boot.
Words live here; the "do not break this" rules live in AGENTS.md under Critical invariants.