gpui-markdown
A small Markdown renderer for GPUI, built on gpui’s own
StyledText / InteractiveText so paragraphs wrap properly and links are clickable
through a real callback — unlike renderers that only cx.open_url externally.
It is host-agnostic: styling comes in via MarkdownStyle, and the host supplies
closures for clicking a [[wiki-link]]/#tag, rendering an image, rendering a
mermaid diagram, syntax-highlighting code, and click-to-caret. Standard
[text](url) links open externally.
📖 Full reference: every public item, with signatures, parameter tables, return contracts, edge cases, and cost notes, lives in API.md.
This is the markdown crate of the Zorite workspace, in two layers:
gpui_markdown::syntax— always compiled, dependency-free: the shared construct recognition (linkables, GitHub alert kinds + fold chars, table styles, heading scales,key:: valueproperties,^block-idanchors,#Heading/#^idlink-target splitting, and![[embed]]lines with block/section extraction) that this reader, thegpui-editorWYSIWYG view, and the PDF exporter all consume, so what a construct IS is defined once.- The reader view —
MarkdownViewand everything around it, behind the default-onviewfeature, which owns thegpui+markdowndependencies. Consumers that only need recognition (like gpui-editor) depend withdefault-features = false.
Features
Section titled “Features”- Headings, paragraphs, bold / italic /
strikethrough/inline code/<mark>highlight, hard breaks - Bullet / numbered / nested / task lists (
- [ ]/- [x]), blockquotes, fenced code blocks, thematic breaks - GFM tables — content-measured columns, column alignment, plus per-table
visual designs (striped / header-shaded / minimal) chosen by a hidden
<!-- table:STYLE -->marker - GitHub alerts —
> [!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION]blockquotes render with a colored bar, bold title, and optional host-supplied icons; the natural inline form (> [!NOTE] like so) works too. Obsidian’s fold char makes a callout collapsible —> [!NOTE]-renders folded (title + chevron only),+open; clicking the title dispatches toon_alert_toggleso the host can flip the char in the source - Collapsible headings — every heading gets a hover-revealed fold chevron;
a folded heading’s whole section is skipped. The fold set is host-owned
(
folded_headings+on_heading_toggle) since this view is rebuilt every frame - Properties — consecutive
key:: valuelines render as a two-column panel: per-key icons (viaMarkdownStyle::property_icon), muted keys, and values with#tag/[[wiki-link]]segments as clickable pills - Block ids, anchors & embeds — a trailing
^block-idmarker hides from the rendered text;[[Note#Heading]]/[[Note#^id]]link targets display asNote → anchor; and a standalone![[Note]]line transcludes the target’s content in a quoted box via a host resolver (on_embed), nested embeds included - Inline (in-flow) images — an image that doesn’t lead its paragraph
renders as a small in-flow thumbnail via
on_inline_image, wrapping with the text; a click dispatches toon_image_preview - Clickable task checkboxes — a
- [ ]box click dispatches its source offset toon_task_toggleso the host can flip[ ]↔[x]and persist - Syntax highlighting — fenced code with a language tag colors its tokens via
a host-supplied
on_highlightclosure (bring your own engine; Zorite passes gpui-component’s tree-sitter highlighter) - Footnotes and reference-style
[text][id]links/images; raw HTML shown literally (never executed) [[wiki-links]](and[[target|label]]aliases) and#tags→ clickable, dispatched to your callback- Images, mermaid diagrams, and math —
$$…$$blocks and inline$…$formulas — rendered by host-supplied closures (the host owns loading / async render / interaction); each falls back gracefully (math → its raw LaTeX) - In-page find — highlight matches and scroll the active one into view
(
search+find_matches/match_count) - Click-to-caret — report the source offset nearest a click, for entering an
editor at the clicked character (
on_click_source) SNIPPETS— authoring snippets a host can surface in a/command palette- Editor helpers — pure
(text, caret)transforms (no gpui/input dependency) for building a Markdown editor: list continuation, indent/outdent, and re-indent
See sample.md for a document exercising everything.
Quick start
Section titled “Quick start”use std::rc::Rc;use gpui_markdown::{MarkdownView, MarkdownStyle};
// In a render method, returning an `impl IntoElement`:MarkdownView::new("note-1", source_text) // unique id + markdown source .style(MarkdownStyle::default()) // or map your theme onto it .on_wiki_link(Rc::new(|title, window, cx| { // navigate to page `title` in your app })) .on_image(Rc::new(|info| { /* render a real image */ todo!() })) .on_mermaid(Rc::new(|src| { /* render a diagram */ todo!() })) .on_math(Rc::new(|latex| { /* typeset a `$$…$$` block → element */ todo!() })) .on_inline_math(Rc::new(|latex| { /* inline `$…$` → (raster, w, h) */ None }))MarkdownView implements RenderOnce (hence IntoElement), so it drops into any
GPUI element tree. The full builder surface — embeds, folds, find, click-to-caret,
task toggles — is in API.md.
Per-table visual designs
Section titled “Per-table visual designs”A GFM table can carry a hidden style marker — an HTML comment on the line directly above it — that the renderer honors and hides:
<!-- table:striped -->| Name | Role ||:------|:---------|| Ada | Engineer || Marker | Look |
|---|---|
(none) / <!-- table:grid --> | full outer box + all gridlines (default) |
<!-- table:striped --> | alternate body rows shaded; a rule under the header |
<!-- table:header --> | only the header row shaded |
<!-- table:minimal --> | no box/gridlines; a rule under the header |
Shading uses MarkdownStyle::code_bg; borders use muted_color. Any other Markdown
viewer just ignores the comment and shows a plain table, so the marker degrades
gracefully.
Supported syntax
Section titled “Supported syntax”Every node ParseOptions::gfm() produces is rendered: headings, paragraphs,
bold/italic/strikethrough/inline-code, links (inline, autolink, reference-style),
images, ordered/unordered/nested/task lists, blockquotes (nested), fenced code,
thematic breaks, tables (with alignment + the per-table designs above), footnotes
(references + definitions), and raw HTML (shown literally — except <mark>…</mark>,
honored as a highlight). Plus math — $$…$$ blocks (math_flow) and inline
$…$ (math_text), typeset by a host renderer — and Zorite-style
[[wiki-links]] and #tags.
Not handled (not enabled by gfm()): frontmatter (YAML/TOML) and MDX. Footnote
references render as [label] markers but aren’t click-to-jump (that would need
anchors this text-based renderer doesn’t have).
Also rendered: GitHub alerts on blockquotes (both marker forms, plus the
foldable -/+ variant), Zorite-style [[wiki-links]] and #tags
(namespaced #a/b included — the grammar is the shared syntax module’s),
[[Note#Heading]] / [[Note#^id]] anchors (displayed as Note → anchor),
trailing ^block-id markers (hidden), key:: value property panels,
standalone ![[Note]] embeds, and table-style / math-alignment control
comments, which — like all HTML comments — never render.
The syntax module’s recognizers back all of it and are public — every one is
documented in API.md.
Status
Section titled “Status”Feature-complete for CommonMark + GFM. The view parses with the
markdown crate (mdast); syntax is pure
text and dependency-free. Not yet published to crates.io (gpui is a git-only
dependency).
License
Section titled “License”GPL-3.0-or-later.