# 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 `/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.js`** — `Scripts/*.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: ```sh 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.