T-TUIL-json-canvas-vue-async-renderer-seam
State: closed/done · Impact: medium · Complexity: medium
json-canvas-vue bundles marked behind a synchronous MarkdownRenderer seam, so the obsidian
pipeline (async) cannot be plugged in and notes had to bypass the seam. This task widens the seam
to async, makes plain text the default, deletes marked, and ships the obsidian adapter in
markdown-ui.
| Location | Role today |
|---|---|
packages/ts/json-canvas-vue/src/markdown.ts | MarkdownRenderer.render(): string; markedRenderer (default) and plainTextRenderer; registry; renderedMarkdown() sanitizes with DOMPurify unless sanitized |
packages/ts/json-canvas-vue/src/components/CanvasNode/index.vue | markedText computed over renderedMarkdown(props.node.text) into v-html |
packages/ts/json-canvas-vue/src/components/DatasetPanel/index.vue | renderers: MarkdownRenderer[] prop |
packages/ts/json-canvas-vue/src/index.ts | Re-exports the registry API and MarkdownRenderer |
packages/ts/json-canvas-vue/src/markdown.test.ts | Registry and sanitize tests |
packages/ts/json-canvas-vue/demo/components/FullPageDemo.vue | Calls setActiveRenderer |
packages/ts/json-canvas-vue/package.json | Depends on marked and dompurify |
solutions/determined/experiences/notes/app/components/NotesCanvasView.vue | Renders text nodes with ObsidianMarkdown directly, bypassing the seam |
Proposed
Section titled “Proposed”MarkdownRenderer.render returns string | Promise<string>. CanvasNode/index.vue replaces the
computed with a watch on node.text and the renderer version that awaits the result and
writes it only if a monotonic token still matches. plainTextRenderer is the default and
markedRenderer is deleted with the marked dependency; dompurify stays for the seam’s
fallback sanitize. markdown-ui gains app/utils/mdCanvasRenderer.ts: an async
MarkdownRenderer over renderToHtml with sanitized: true, for hosts that mount JsonCanvas
and want the Obsidian dialect.
Approach
Section titled “Approach”markdown.ts: widen the type; deletemarkedRendererand themarked.useblock; defaultactiveRendererIdtoplaintext;renderedMarkdownreturnsPromise<string>after awaiting and sanitizing.CanvasNode/index.vue:watch([() => props.node.text, version], ...)with a per-instance token; drop stale results; keep thev-htmlsites.- Update
DatasetPanel/index.vuetypes,index.tsexports andFullPageDemo.vue. - Tests in
markdown.test.ts: an async renderer resolves into the node; a slower earlier render does not overwrite a newer one. packages/ts/markdown-ui/app/utils/mdCanvasRenderer.ts: the obsidian adapter plus a test.- Root
bun install;bun install --frozen-lockfile;bun run test:runandbun run typecheckinpackages/ts/json-canvas-vue;moon run product-workbench:check(notes canvas unaffected); the demo builds;moon run :check :test --affected. Flip the canvas row of the duplicates ledger indocs/LIBRARIES.md.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
packages/ts/json-canvas-vue/src/markdown.ts | modify | Async seam; plaintext default; delete markedRenderer |
packages/ts/json-canvas-vue/src/components/CanvasNode/index.vue | modify | Watch plus token instead of computed |
packages/ts/json-canvas-vue/src/components/DatasetPanel/index.vue | modify | Type follow-through |
packages/ts/json-canvas-vue/src/index.ts | modify | Export follow-through |
packages/ts/json-canvas-vue/src/markdown.test.ts | modify | Async and ordering tests |
packages/ts/json-canvas-vue/demo/components/FullPageDemo.vue | modify | Renderer picker without marked |
packages/ts/json-canvas-vue/package.json | modify | Delete marked |
packages/ts/json-canvas-vue/README.md | modify | Document the async seam and default |
packages/ts/markdown-ui/app/utils/mdCanvasRenderer.ts | new | Obsidian adapter |
packages/ts/markdown-ui/tests/mdCanvasRenderer.test.ts | new | Adapter test |
docs/LIBRARIES.md | modify | Canvas node markdown row flips to resolved |
bun.lock | modify | Root lockfile regen |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
packages/ts/json-canvas-vue/package.jsonhas nomarkedentry andgrep -rn "from 'marked'" packages/ts/json-canvas-vue/srcreturns nothing. - AC-2:
MarkdownRenderer.renderis typedstring | Promise<string>andgetActiveRendererId()returnsplaintextwith no registration. - AC-3: A test in
markdown.test.tsshows an earlier, slower render resolving after a newer one does not replace the newer output. - AC-4:
bun run test:runandbun run typecheckpass inpackages/ts/json-canvas-vue;moon run product-workbench:checkpasses. - AC-5:
mdCanvasRenderer.tsexists inpackages/ts/markdown-ui/app/utils/with a passing test that renders awikilinkthrough the adapter. - AC-6:
bun install --frozen-lockfilepasses at the root.
Out of scope
Section titled “Out of scope”- Moving
NotesCanvasView.vueonto the seam; it keeps its directObsidianMarkdownrender. - Deleting
dompurifyfrom the package; the seam still sanitizes non-sanitizing renderers. - Editing canvas node text through
MdSurface.
Dependencies
Section titled “Dependencies”- T-A0HT-md-ui-layer-skeleton hosts the adapter.
D-7VMX-markdown-ecosystem-strategyd.7 and d.10: peer content rides the extension seam and the bundledmarkedimplementation is deleted.
Discovery context
Section titled “Discovery context”Member of milestone M-931L; wave 6a of D-K3EM-markdown-editor-chrome-and-plugin-environment.