T-RCWI-md-obsidian-md-sync-helpers
State: closed/done · Impact: high · Complexity: small
The editor’s rendered widgets are framework-free DOM built from obsidian-md’s own markup, but
the renderer only exposes an async renderToHtml, and KaTeX and mermaid rendering are inlined in
two Vue components. This task adds renderToHtmlSync, renderMath and renderMermaid to the core
entry, moves LoadedNote to core, and makes MathBlock and MermaidBlock consume the helpers.
| Location | Role today |
|---|---|
packages/ts/obsidian-md/src/processor.ts#renderToHtml | Async processor.process; no sync variant |
packages/ts/obsidian-md/src/vue/components/MathBlock.ts | Lazy import('katex') and renderToString inline |
packages/ts/obsidian-md/src/vue/components/MermaidBlock.ts | Lazy import('mermaid'), initMermaid once-per-theme guard, id sequence inline |
packages/ts/obsidian-md/src/vue/context.ts#LoadedNote | Type lives in the Vue subpath; the engine’s Vue-free entry cannot import it |
packages/ts/obsidian-md/src/vue/index.ts | Re-exports LoadedNote |
packages/ts/obsidian-md/src/index.ts | Core exports; no LoadedNote, no render helpers |
packages/ts/obsidian-editor/package.json | No direct katex or mermaid dependency |
solutions/determined/experiences/notes/app/plugins/note-source.client.ts | Imports LoadedNote type from @sksizer/obsidian-md/vue |
packages/ts/obsidian-md/test/vue-components.test.ts | Existing math and mermaid component tests, the oracle for the refactor |
packages/ts/obsidian-md/test/fixtures/every-feature.md | Fixture for the sync-equals-async test |
Proposed
Section titled “Proposed”renderToHtmlSync(markdown, options = {}): stringin the processor module, built oncreateObsidianProcessor(options).use(rehypeStringify, { allowDangerousHtml: false })andprocessSync; throwsError('renderToHtmlSync: highlight requires the async renderToHtml')whenoptions.highlightis set, becauserehypeShikiis async.packages/ts/obsidian-md/src/math.ts:loadKatex(): Promise<KatexModule>(memoized lazy import) andrenderMath(latex: string, displayMode: boolean): Promise<string>returning thehtmlAndMathmlmarkup withthrowOnError: false.packages/ts/obsidian-md/src/mermaid.ts:renderMermaid(source: string, theme = 'default'): Promise<string>with the once-per-themeinitializeguard and the id sequence moved fromMermaidBlock.LoadedNotemoves topackages/ts/obsidian-md/src/types.ts; the Vue context module imports and re-exports it so@sksizer/obsidian-md/vuekeeps exporting the type.- The core entry exports
renderToHtmlSync,renderMath,loadKatex,renderMermaid, and theLoadedNotetype. MathBlockandMermaidBlockcall the helpers; the inline copies are deleted.packages/ts/obsidian-editor/package.jsonaddskatexandmermaidas direct dependencies at the versionsobsidian-mdpins.
Approach
Section titled “Approach”- Write
packages/ts/obsidian-md/test/render-sync.test.ts:renderToHtmlSync(fixture)equalsawait renderToHtml(fixture)for the every-feature fixture withmath: true; throws withhighlight. - Write
packages/ts/obsidian-md/test/render-math.test.ts(block and inline markup containkatex-mathml; invalid TeX returns markup, not a throw) andpackages/ts/obsidian-md/test/render-mermaid.test.ts(mockmermaidmodule:initializecalled once for two renders with the same theme, twice across two themes; render failure rejects). - Add
renderToHtmlSyncto the processor module; add the math and mermaid modules. - Move
LoadedNoteto the core types module; re-export from the Vue context module and the core entry. - Rewrite
MathBlockandMermaidBlockover the helpers; run the Vue components test unchanged. - Add the two dependencies to the editor manifest;
bun install. bun testandtsc --noEmitin both packages;moon run product-workbench:checkfor the notes consumer ofLoadedNote.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
packages/ts/obsidian-md/src/processor.ts | modify | renderToHtmlSync |
packages/ts/obsidian-md/src/math.ts | new | loadKatex, renderMath |
packages/ts/obsidian-md/src/mermaid.ts | new | renderMermaid with once-per-theme init |
packages/ts/obsidian-md/src/types.ts | modify | LoadedNote |
packages/ts/obsidian-md/src/vue/context.ts | modify | Import and re-export LoadedNote from core |
packages/ts/obsidian-md/src/vue/components/MathBlock.ts | modify | Consume renderMath |
packages/ts/obsidian-md/src/vue/components/MermaidBlock.ts | modify | Consume renderMermaid |
packages/ts/obsidian-md/src/index.ts | modify | Export the helpers and LoadedNote |
packages/ts/obsidian-md/README.md | modify | Document renderToHtmlSync, renderMath, renderMermaid under “Core usage” |
packages/ts/obsidian-md/test/render-sync.test.ts | new | Sync equals async; throws with highlight |
packages/ts/obsidian-md/test/render-math.test.ts | new | Helper tests |
packages/ts/obsidian-md/test/render-mermaid.test.ts | new | Init guard and failure tests |
packages/ts/obsidian-editor/package.json | modify | katex, mermaid dependencies |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
bun test packages/ts/obsidian-mdpasses with the three new suites and the unchangedpackages/ts/obsidian-md/test/vue-components.test.ts. - AC-2:
renderToHtmlSyncoverpackages/ts/obsidian-md/test/fixtures/every-feature.mdwith{ math: true }is byte-equal toawait renderToHtml(...)with the same options. - AC-3:
renderToHtmlSync(x, { highlight: rehypeShiki() })throws. - AC-4:
grep -rn "import('katex')\|import('mermaid')" packages/ts/obsidian-md/srcmatches only the new math and mermaid modules. - AC-5:
import type { LoadedNote } from '@sksizer/obsidian-md'andfrom '@sksizer/obsidian-md/vue'both typecheck;moon run product-workbench:checkpasses withsolutions/determined/experiences/notes/app/plugins/note-source.client.tsunchanged. - AC-6:
packages/ts/obsidian-editor/package.jsonlistskatexandmermaidunderdependenciesandbun install --frozen-lockfileat the repo root succeeds.
Out of scope
Section titled “Out of scope”- Editor widgets that consume the helpers (T-UYTR-md-engine-tables, T-JPB0-md-engine-math, T-VCFL-md-engine-mermaid, T-DSVW-md-engine-html-details, T-E99V-md-engine-transclusion-expansion).
- A sync KaTeX path; KaTeX stays lazy and widgets fill from a cache.
- Moving
sliceEmbedor the embed cycle guards; they are already core.
Dependencies
Section titled “Dependencies”- No task dependency. D-7VMX: the helpers wrap the existing remark pipeline and never add a parser.
- Generalizing deletes the original per
docs/PRINCIPLES.md: the inline copies in the two Vue components go.
Discovery context
Section titled “Discovery context”Member of milestone M-931L; the obsidian-md prerequisites of decision
D-K3EM-markdown-editor-chrome-and-plugin-environment.