Files
Mermaid-Preview-Nova/CLAUDE.md
T
thatguygriffandClaude Opus 5 aebc72b85e Add CLAUDE.md and the test harness
CLAUDE.md records the Nova constraints this extension is built around —
established by reading Nova 14.1's bundle and probing its preview server,
and expensive to re-derive: no previewer extension point, generated pages
must live under the preview root, dot-directories are served, and no API
reaches into a preview WebView. It also flags the one unverified
assumption, that a tab showing Preview still appears in
nova.workspace.textEditors, with its symptom and fix.

Tests/ is the harness that built the extension, kept rather than left in a
scratchpad: a stubbed Nova runtime backed by the real filesystem, node
suites for generation and the four close paths, and a headless-browser
render test that skips itself without a browser. It caught the ./ doubling
in rebased links and checkbox state not surviving serialization.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TueXpMi7eAtgWPuMr4ii52
2026-08-20 23:02:08 -03:00

7.8 KiB

CLAUDE.md

Context for working on this repository.

What this is

A Nova extension that previews Markdown as rendered HTML, with Mermaid diagrams drawn inline. Nova previews Markdown already; the thing it cannot do is run JavaScript in that preview, which is what diagrams need.

The extension is not a previewer and cannot be one. It writes an ordinary HTML file inside the workspace and opens it, and Nova's own HTML preview — a WebView that does run JavaScript — renders it. Every awkward part of the design follows from that one fact.

Hard constraints (do not re-derive these)

These were established by reading Nova 14.1's bundle and probing its running preview server. They are facts about Nova, not preferences.

Constraint Consequence
No preview/previewer extension point. Manifest keys are activationEvents, clips, commands, completions, entitlements, main, sidebars, syntaxes; categories are clips, commands, completions, issues, languages, sidebars, tasks, themes Cannot supply the contents of a preview tab. Generate a file and open it
No webview / HTML UI for extensions; sidebars are TreeView only Rendering can only happen inside Nova's own preview WebView
AssistantsRegistry covers completions, issues, symbols, colors, tasks only No hook to intercept how a file is displayed
Nova's preview server only serves files under the preview root Generated pages must live inside the workspace. A page outside it fails as a blank tab, not an error
The preview server does serve dot-directories (.git/HEAD returns 200) .nova/MermaidPreview/ is a valid home for generated files
The preview server renders .md to HTML on the fly, with Nova's own stylesheet A relative link from a generated page to another .md file shows Nova's rendering, not raw text
Nova's built-in Markdown preview is NovaPreviewMarkdownRenderer over discount, styled by MarkdownAuto/Bright/Dark.css with a custom-stylesheet preference A stylesheet cannot run Mermaid. That preference is not a route to this feature
No API to reach into a preview WebView No scroll sync, ever. Do not promise it
No API to close a tab, or to ask whether a tab is showing preview Cleanup infers a close from onDidDestroy plus a re-check
nova.workspace.openFile options are line and column only Cannot open a file directly into its preview state

The runtime is JavaScriptCore, not Node: no npm require, only relative require("./x.js") between Scripts/ files. setTimeout/clearTimeout and fetch exist. Vendored libraries are not required by the extension — they are copied into the workspace and loaded by the page.

Type definitions worth having open: npm pack @types/nova-editor-node (a single index.d.ts, more precise than the website).

How a preview happens

  1. mermaidpreview.preview resolves the frontmost editor to a source (describeSource), refusing untitled and remote files — they have no path to resolve images and links against.
  2. installAssets copies Assets/ into <output>/assets/ if missing or stale, stamped with ASSET_VERSION. Bump ASSET_VERSION in Scripts/preview.js whenever anything in Assets/ changes, or installed copies go stale.
  3. buildDocument writes a ~2 KB shell: the Markdown embedded as JSON, plus script tags. No rendering happens in the extension.
  4. Assets/preview.js renders in the WebView — markdown-it, then DOM fix-ups, then Mermaid one diagram at a time so a bad diagram fails alone.
  5. Saving rewrites step 3 only. Nova reloads the preview because the file changed. Three megabytes are not rewritten per keystroke — keep it that way.
  6. Closing either tab discards the page; the last one out removes assets/ and the directory.

Layout

extension.json          manifest: commands, config, entitlements
Scripts/                runs in Nova (JavaScriptCore)
  main.js               Controller: lifecycle, commands, watchers, cleanup
  preview.js            output paths, asset install, generate/discard
  render.js             builds the HTML shell — no Nova APIs, so node can test it
  util.js               conf, mkdirp, writeFile, copyFile, removeTree, shortHash
Assets/                 copied into the workspace, runs in the WebView
  preview.js            markdown-it wiring, DOM fix-ups, Mermaid
  preview.css           three themes, resolved in CSS so the page never flashes
  vendor/               markdown-it 14.3.0, mermaid 11.17.0 (both UMD, MIT)
Tests/                  node harness + headless-browser render test
install.sh              copy into Nova's extension directory, register, restart

Two files are called preview.js. Scripts/preview.js decides where files go; Assets/preview.js renders the page. They never see each other.

Conventions

  • 4-space indent, CommonJS, no build step, no runtime dependencies.
  • Comments explain why, especially where something encodes an API limitation. Several exist to stop a future reader "simplifying" a constraint back into a bug — the cleanup grace period is the sharpest example.
  • User-visible strings are sentence case and plain English.
  • Config keys are namespaced mermaidpreview.* and read through util.conf(), which prefers the workspace value.
  • Vendored libraries are committed, never fetched at runtime. The extension makes no network requests and holds no network entitlement.

Testing

Tests/run.sh runs everything: syntax, manifest, and three suites.

  • generate.test.jsScripts/*.js against a stubbed nova global backed by the real filesystem (Tests/nova-stub.js). Covers generation, asset reuse, and discard/pruning.
  • close.test.js — drives the whole Controller through the stub, including command invocation and fake editors that can be destroyed. Covers all four close paths.
  • render.test.js — builds a page through the real render.js and dumps the DOM from headless Chromium, asserting on diagrams, rebased paths, task lists, headings and both themes. Skips itself if no browser is found; set MERMAID_PREVIEW_CHROME to point at one.

The stub is the highest-value tool here and it already caught real bugs (./ doubling in rebased links, checkbox state not surviving serialization). Extend it rather than testing by hand.

In Nova itself:

nova extension validate .    # manifest + bundle (makes network calls)
nova extension activate .    # load into Nova for development

Logs appear in Nova's Extension Console.

State

  • Never published. min_runtime is 10; developed against Nova 14.1.
  • Unverified assumption, worth settling first. Cleanup on tab-close assumes a tab showing Preview still appears in nova.workspace.textEditors. If it does not, flipping a page into Preview will delete it ~2.5 s later (CLEANUP_GRACE_MS in Scripts/main.js). Symptom: the preview stops updating and goes blank after a save. Fix is small — gate the page-tab cleanup on the source file being closed too, leaving only the source-close and quit paths. Recovery for the user is pressing ⌃⇧M again.
  • No icon. Images/extension/ is empty; extension.png at the root is what Nova picks up (see the sibling claude-nova extension).
  • No commits yet.

Things deliberately not done

  • Syntax highlighting in code blocks — another bundle to vendor, and the page is already 3.5 MB. Add highlight.js to Assets/vendor/ and a highlight option in the markdown-it constructor if it becomes worth it.
  • Scroll sync — impossible, see the constraints table.
  • Replacing Nova's built-in .md preview — also impossible. This is a second tab, and the README says so plainly.
  • Inlining assets into a single self-contained page — would make every save a 3.5 MB write.