Skip to content

T-VCFL-md-engine-mermaid

State: closed/done · Impact: high · Complexity: small

A ```mermaid fence renders a diagram in read mode but stays source text in live mode, and the engine has no way to change an option (like the mermaid theme) after mount. This task adds the mermaid plugin’s fence widget and the runtime-mutable optionsCompartment with setOptions(view, partial).

LocationRole today
packages/ts/obsidian-editor/src/decorations.tsFencedCode case: fence background and CodeLangWidget label; a mermaid fence stays source text
packages/ts/obsidian-editor/src/state.tsmodeFacet, modeCompartment, optionsFacet; nothing reconfigurable at runtime
packages/ts/obsidian-editor/src/types.ts#ObsidianEditorOptionsNo mermaidTheme
packages/ts/obsidian-editor/src/vue/index.ts#useObsidianEditorNo way to change options after mount
packages/ts/obsidian-md/src/vue/components/MermaidBlock.tsRead-mode mermaid component; T-RCWI-md-obsidian-md-sync-helpers extracts renderMermaid from it

The plugin roster comes from T-4TE2-md-engine-plugin-contract; requestRedraw and the bench from T-0GV4-md-engine-line-endings-corpus.

The state module gains optionsCompartment and setOptions(view: EditorView, partial: Partial<ObsidianEditorOptions>): void, which reconfigures the compartment by merging partial over the current optionsFacet value. ObsidianEditorOptions gains mermaidTheme?: string (default 'default').

packages/ts/obsidian-editor/src/plugins/mermaid.ts:

  • Inactive FencedCode whose info word is mermaid → block replace widget. The widget fills from a cache keyed by theme + source; on a miss it shows the fence source, calls renderMermaid(source, theme) (T-RCWI-md-obsidian-md-sync-helpers) and issues requestRedraw(view) on resolve. Because the cache key includes the theme, setOptions(view, { mermaidTheme }) re-renders every diagram on the next rebuild.
  • Render failure → the widget carries cm-mermaid-error and shows the fence source; clicking it puts the cursor in the fence. Active fence → plain source, as any code fence.

useObsidianEditor returns setOptions(partial) bound to the view.

  1. Write packages/ts/obsidian-editor/test/plugins/mermaid.plugin.test.ts with a mocked renderMermaid: inactive fence → widget; after resolve plus requestRedraw the widget DOM contains the mock SVG; active fence → no widget; rejection → cm-mermaid-error with the source text; two renders under one theme hit the cache once per source (spy).
  2. Add optionsCompartment and setOptions to the state module; route obsidianEditorExtensions’ options through the compartment.
  3. Implement the plugin and add mermaid to BUILTIN_PLUGINS.
  4. Test the theme swap: setOptions(view, { mermaidTheme: 'dark' }) → the next rebuild calls renderMermaid with 'dark' and the document (state.sliceDoc()) is byte-identical before and after.
  5. Extend useObsidianEditor and the Vue test; export setOptions from the package entry.
  6. bun test, tsc --noEmit, moon run obsidian-editor:bench.
LocationKindChange
packages/ts/obsidian-editor/src/plugins/mermaid.tsnewmermaid plugin: fence widget, theme-keyed cache, error fallback
packages/ts/obsidian-editor/src/plugins/index.tsnewCreated by T-4TE2-md-engine-plugin-contract; mermaid joins BUILTIN_PLUGINS
packages/ts/obsidian-editor/src/state.tsmodifyoptionsCompartment, setOptions
packages/ts/obsidian-editor/src/types.tsmodifymermaidTheme?: string
packages/ts/obsidian-editor/src/vue/index.tsmodifysetOptions(partial) in the return
packages/ts/obsidian-editor/src/index.tsmodifyExport setOptions
packages/ts/obsidian-editor/test/plugins/mermaid.plugin.test.tsnewWidget, cache, theme-swap and failure tests
packages/ts/obsidian-editor/test/vue.test.tsmodifysetOptions returned and effective
  • AC-1: bun test packages/ts/obsidian-editor passes, including the new suite.
  • AC-2: an inactive mermaid fence renders one block widget; after the mocked renderMermaid resolves and a requestRedraw, the widget DOM contains the mock markup; with the cursor inside the fence the source is shown instead.
  • AC-3: setOptions(view, { mermaidTheme: 'dark' }) makes the next rebuild call renderMermaid with theme 'dark', and state.sliceDoc() is byte-identical across the call.
  • AC-4: a rejected render produces a widget with class cm-mermaid-error containing the fence source.
  • AC-5: useObsidianEditor returns setOptions; the Vue test proves a theme change re-renders without touching the document.
  • AC-6: moon run obsidian-editor:bench stays within 1.5× of the recorded baseline.
  • Read-mode mermaid and the theme-bound Vue wrapper (MermaidBlock via T-RCWI-md-obsidian-md-sync-helpers; MdMermaidBlock in T-A0HT-md-ui-layer-skeleton).
  • Wiring mermaidTheme to the product dark-mode signal; the chrome layer calls setOptions({ mermaidTheme }) from its theme watcher.
  • Any other runtime-mutable option; setOptions is generic but only mermaidTheme gets a consumer here.

Member of milestone M-931L; engine step E8 of decision D-K3EM-markdown-editor-chrome-and-plugin-environment.

T-RCWI-md-obsidian-md-sync-helpers


← Back to Tasks