Why
CanvasView declares a renderMarkdown?: (text: string, scheme: ColorScheme) => React.ReactElement prop that no code in the renderer consumes. Tracing it:
CanvasView.tsx accepts the prop and passes it into the React context via CanvasProvider
CanvasContext.tsx declares the type on the context value
- Nothing else in
src/renderer/** reads it from context
The reason: TextNodeContent (which would have been the consumer) returns null intentionally — there's a comment explaining the choice was made to bypass a react-native-macos rendering limitation where native Text stops rendering beyond ~1500px from the parent View's origin. All text rendering goes through Skia (SkiaTextRenderer → paragraphBuilder).
So renderMarkdown is vestigial public API. Consumers (e.g. Workspace mobile) pass it in good faith but it has no effect.
What we actually render for text node bodies
Text nodes are parsed by paragraphBuilder.ts which uses the unified/remark ecosystem:
remark-parse — CommonMark
remark-gfm — strikethrough, tables, task lists
remark-frontmatter — YAML (parsed as AST, used for Canvas Candy class extraction, not rendered)
hast-util-from-html — inline HTML
Output is a flat list of styled segments rendered via Skia's text API. Supported inline marks: bold, italic, code, links (rendered as styled runs, not as tappable affordances). Supported blocks: headings, paragraphs, lists, code blocks, blockquotes, tables, task lists. Not supported: anything that requires a React component overlay (images embedded in markdown, custom code-block highlighters, callouts beyond what the extensions/callouts.ts layer recognises).
Done means
Future
Whether to remove the prop entirely is a separate decision — a breaking change. Worth doing eventually, but not in this issue. Document and deprecate first; remove in a later major version.
Why
CanvasViewdeclares arenderMarkdown?: (text: string, scheme: ColorScheme) => React.ReactElementprop that no code in the renderer consumes. Tracing it:CanvasView.tsxaccepts the prop and passes it into the React context viaCanvasProviderCanvasContext.tsxdeclares the type on the context valuesrc/renderer/**reads it from contextThe reason:
TextNodeContent(which would have been the consumer) returnsnullintentionally — there's a comment explaining the choice was made to bypass areact-native-macosrendering limitation where nativeTextstops rendering beyond ~1500px from the parent View's origin. All text rendering goes through Skia (SkiaTextRenderer→paragraphBuilder).So
renderMarkdownis vestigial public API. Consumers (e.g. Workspace mobile) pass it in good faith but it has no effect.What we actually render for text node bodies
Text nodes are parsed by
paragraphBuilder.tswhich uses the unified/remark ecosystem:remark-parse— CommonMarkremark-gfm— strikethrough, tables, task listsremark-frontmatter— YAML (parsed as AST, used for Canvas Candy class extraction, not rendered)hast-util-from-html— inline HTMLOutput is a flat list of styled segments rendered via Skia's text API. Supported inline marks: bold, italic, code, links (rendered as styled runs, not as tappable affordances). Supported blocks: headings, paragraphs, lists, code blocks, blockquotes, tables, task lists. Not supported: anything that requires a React component overlay (images embedded in markdown, custom code-block highlighters, callouts beyond what the
extensions/callouts.tslayer recognises).Done means
renderMarkdownprop inCanvasView.tsxmarked@deprecatedwith JSDoc explaining the situation and pointing at the actual markdown-rendering pathway@deprecatedon the field inCanvasContext.tsxFuture
Whether to remove the prop entirely is a separate decision — a breaking change. Worth doing eventually, but not in this issue. Document and deprecate first; remove in a later major version.