Documentation
¶
Overview ¶
Package tree builds the version-1 native render tree: a layout-free, fully RESOLVED semantic tree that native hosts (Flutter, SwiftUI, Compose, …) render as platform widgets, with everything policy-heavy — URL resolution and filtering, raw-HTML sanitizing, admonition titles, footnote pairing, math/mermaid fallbacks — already applied library-side by Build, through the same shared derivations the HTML renderer uses (render/internal/derive and the resolve package), so the two renderers cannot drift.
Evolution within version 1 ¶
The version stays 1 across additive growth: new OPTIONAL fields may appear on existing nodes, and new block/inline KINDS may appear as the document model grows. Consumers must therefore default-case on unknown kinds (render nothing, or a host-chosen placeholder) rather than treat the kind set as closed — the Flutter model's Unknown nodes and any TypeScript switch's default arm are the intended paths. A version bump is reserved for changes that break existing fields.
Wire schema (version 1, strict) ¶
Tree.MarshalJSON produces the envelope
{"version":1, "blocks":[…], "footnotes":[…]}
where "blocks" and "footnotes" are always arrays (empty, never null). Every block object carries, in order: "kind", "span" (omitted when the node has no source position — same rule and shape as the document codec's spans: {startLine,endLine,startOffset,endOffset}), "id" (see "Block identity" below), its kind-specific fields, and its children — named "children" when they are inlines, "blocks" when they are nested blocks. Inline objects carry "kind", "span" (same omission rule), and kind-specific fields.
Kind names are exactly the document codec's wire names (document.Kind String values): heading, paragraph, blockQuote, admonition, list, codeBlock, mathBlock, diagram, table, thematicBreak, htmlBlock, definitionList, definitionTerm, definitionDesc, footnoteDef, text, softBreak, hardBreak, emphasis, strong, strikethrough, codeSpan, link, image, mathInline, htmlInline, footnoteRef. One naming universe across the library: a kind in the doc JSON and the same kind in the render tree spell identically.
Field presence is strict and documented per type below: fields that distinguish states are always emitted (codeBlock "runs" is null vs an array; list item "task" is null vs a boolean; link/image "url" is present even when empty), while purely-optional decorations use omit-when-empty ("anchorId", "title", "blocked", "display", wiki-link "source").
Block identity ¶
A block's "id" is hex(sha256(source bytes of the block's span))[:16] — the first 8 hash bytes, 16 hex characters — computed over Options.Source[span.StartOffset:span.EndOffset]. It is a CONTENT hash: stable across edits elsewhere in the document (the basis for host-side diffing / itemized rebuilds), which also means two blocks with byte-identical source share an id — hosts needing unique keys disambiguate by position among equal ids. When no source bytes are available for a block (Options.Source is empty or nil — e.g. building from decoded doc JSON — or the node carries the zero span), the id falls back to hex(sha256("\x00mdv-fallback\x00" + kind + ":" + ordinal))[:16], where ordinal is the block's 0-based position in a document-order count of every tree-emitted block (list items and footnotes included). The NUL-delimited prefix domain-separates the fallback preimage from the content-hash form — source bytes can never spell it, since decoded markdown contains no NUL — so a block whose source literally reads e.g. "list:2" cannot collide with a fallback id. Fallback ids are deterministic for a given document but positional, not content-stable.
Index ¶
- Constants
- type Admonition
- type Block
- type BlockQuote
- type Cell
- type CodeBlock
- type CodeSpan
- type DefinitionDesc
- type DefinitionList
- type DefinitionTerm
- type Diagram
- type Emphasis
- type Footnote
- type FootnoteRef
- type HTMLBlock
- type HTMLInline
- type HardBreak
- type Heading
- type Image
- type Inline
- type Link
- type List
- type ListItem
- type MathBlock
- type MathInline
- type Options
- type Paragraph
- type SoftBreak
- type Strikethrough
- type Strong
- type Table
- type Text
- type ThematicBreak
- type TokenRun
- type Tree
Constants ¶
const Version = 1
Version is the render-tree wire-schema version emitted in the envelope's "version" field.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Admonition ¶
type Admonition struct {
Span document.Span
ID string
Variant string // note|tip|important|warning|caution (normalized)
Title string // derived display title ("Note", …)
Blocks []Block
}
Admonition is a callout box. Variant and Title come from the shared derivation (derive.AdmonitionTitle): Variant is normalized (empty → "note") and Title is its title-cased display form — the SAME title the HTML renderer shows. Wire: {"kind":"admonition",span,id,"variant","title","blocks"}.
type Block ¶
type Block interface {
// contains filtered or unexported methods
}
Block is a block-level node of the render tree. Concrete types: Heading, Paragraph, BlockQuote, Admonition, List, CodeBlock, MathBlock, Diagram, Table, ThematicBreak, HTMLBlock, DefinitionList, DefinitionTerm, DefinitionDesc.
type BlockQuote ¶
BlockQuote is quoted block content. Wire: {"kind":"blockQuote",span,id,"blocks"}.
type CodeBlock ¶
type CodeBlock struct {
Span document.Span
ID string
Language string // fence info-string language tag, "" if none
Label string // display label (derive.CodeLabel: tag or "code")
Runs []TokenRun // nil = no runs (highlighting off / unavailable)
Text string // literal source text, always present
}
CodeBlock is a fenced or indented code block. Text always carries the full literal source. Runs is nil (wire: null) when highlighting is off or no token runs are available (unknown/missing language, chroma failure), an array otherwise — whose Text fields concatenate to exactly Text, so a host styling runs and a host printing Text show the same characters.
Wire: {"kind":"codeBlock",span,id,"language","label","runs","text"} — "runs" and "text" always present.
type DefinitionDesc ¶
DefinitionDesc is the description half of a definition-list entry. Wire: {"kind":"definitionDesc",span,id,"blocks"}.
type DefinitionList ¶
type DefinitionList struct {
Span document.Span
ID string
Blocks []Block // DefinitionTerm / DefinitionDesc, in order
}
DefinitionList is a list of DefinitionTerm/DefinitionDesc blocks, in source order (a term may be followed by several descriptions and vice versa, so the pairing stays positional, mirroring the document model). Wire: {"kind":"definitionList",span,id,"blocks"}.
type DefinitionTerm ¶
DefinitionTerm is the term half of a definition-list entry. Wire: {"kind":"definitionTerm",span,id,"children"}.
type Diagram ¶
Diagram is a diagram definition for the host's diagram engine. Wire: {"kind":"diagram",span,id,"source","engine":"mermaid"}.
type Footnote ¶
type Footnote struct {
Span document.Span
ID string
Index int // 1-based, matches FootnoteRef.Index
RefCount int
Blocks []Block
}
Footnote is one resolved footnote definition in the envelope's "footnotes" array, paired with its reference sites: Index matches the FootnoteRef inlines that cite it, and RefCount is the number of such sites (for hosts rendering per-reference backlinks). Pairing and order come from the shared derivation (derive.Footnotes). Wire: {"kind":"footnoteDef",span,id,"index","refCount","blocks"}.
type FootnoteRef ¶
type FootnoteRef struct {
Index int
DefID string // "" (omitted) only when no definition has a matching Index
}
FootnoteRef is a footnote reference site; Index pairs it with the envelope Footnote carrying the same index. It never carries a span (the parser records none).
DefID is the definition's block id (the same value as the matching Footnote.ID, whether that id is a content hash or a positional fallback) — an explicit ref→definition linkage so a host can jump straight to the definition it should key/scroll to, without scanning Tree.Footnotes by Index or reconstructing the link from source line numbers. Populated for every ref whose Index matches a definition in the same Tree, regardless of Options.Source — this is consistent whether the tree came from markdown directly or from previously parsed document JSON (a document.Document has no source bytes on that path, so ordinary block ids there are the positional fallback form, and DefID matches that same fallback value). It is "" (omitted on the wire) only for a ref with no matching definition at all (a hand-built tree with a stray/mismatched Index). Tree.FootnoteByIndex performs the equivalent lookup directly against Tree.Footnotes. Wire: {"kind":"footnoteRef","index",("defId")}.
type HTMLBlock ¶
HTMLBlock is a raw block of HTML markup. Unless Options.AllowRawHTML is set, HTML is the bluemonday-sanitized form (the shared derive.SanitizeHTML policy) and Unsafe is false; with AllowRawHTML, HTML is the raw source markup and Unsafe is true, telling the host the content was NOT sanitized. Wire: {"kind":"htmlBlock",span,id,"html","unsafe"} — both always present.
type HTMLInline ¶
HTMLInline is a raw inline HTML span, sanitized/flagged exactly like HTMLBlock. Wire: {"kind":"htmlInline",span,"html","unsafe"}.
type HardBreak ¶
type HardBreak struct{}
HardBreak is an explicit line break. It never carries a span. Wire: {"kind":"hardBreak"}.
type Heading ¶
type Heading struct {
Span document.Span
ID string
Level int // 1-6
AnchorID string // slugified per-document-unique anchor, "" (omitted on the wire) when Options.HeadingAnchors is off or the document carries none
Children []Inline // heading content
}
Heading is a section heading. Wire: {"kind":"heading",span,id,"level",("anchorId"),"children"}.
type Image ¶
Image is an image with its destination resolved by the same pipeline as Link (a blocked destination yields URL "" + Blocked true, and a declined/absent resolver takes the default resolution path). Wire: {"kind":"image",span,"url",("blocked"),"alt",("title")} — "url" and "alt" always present.
type Inline ¶
type Inline interface {
// contains filtered or unexported methods
}
Inline is an inline node of the render tree. Concrete types: Text, SoftBreak, HardBreak, Emphasis, Strong, Strikethrough, CodeSpan, Link, Image, MathInline, HTMLInline, FootnoteRef.
type Link ¶
type Link struct {
Span document.Span
URL string
Blocked bool // destination was blocked by URL policy
Title string // "" omitted on the wire
Source string // "wikiLink" when resolved from [[…]], else ""
Children []Inline
}
Link is a hyperlink with its destination fully resolved: the shared pipeline (derive.Href — Resolver trust, resolve.DefaultResolution, resolve.SafeURL filtering, percent-encoding) has already run. A policy-blocked destination yields URL "" with Blocked true — distinct from an empty-but-allowed destination (URL "" and Blocked false). Resolver-returned URLs are host-trusted and carried verbatim.
A wiki link resolves INTO a Link node (via the same pipeline, with the resolve.ResolveWikiLink kind and the default ".md" fallback); Source is then "wikiLink" so hosts can style it differently, and "" (omitted on the wire) for an ordinary link. Wire: {"kind":"link",span,"url",("blocked"),("title"),("source"), "children"} — "url" always present.
type List ¶
type List struct {
Span document.Span
ID string
Ordered bool
Start int // first item's number; meaningful when Ordered
Tight bool
Items []ListItem
}
List is an ordered or unordered list. Wire: {"kind":"list",span,id,"ordered","start","tight","items"}.
type ListItem ¶
type ListItem struct {
Span document.Span
ID string
Task *bool // nil: not a task item; else the checked state
Blocks []Block
}
ListItem is one list entry. Task is nil for an ordinary item; for a task-list item it points at the checked state, so the wire "task" field is null | false | true. Wire: {span,id,"task","blocks"} — "task" always present.
type MathBlock ¶
type MathBlock struct {
Span document.Span
ID string
Source string // TeX source, without delimiters
}
MathBlock is display math for the host's math engine. Wire: {"kind":"mathBlock",span,id,"source","engine":"katex"}.
type MathInline ¶
MathInline is inline math for the host's math engine. Display marks a $$…$$ span that renders as display math despite sitting inline. Wire: {"kind":"mathInline",span,"source",("display")}.
type Options ¶
type Options struct {
Resolver resolve.Resolver // optional hook to rewrite link/image/wiki-link targets; nil uses default resolution
HeadingAnchors bool // anchor ids on headings
Highlighting bool // code token runs (see note above)
Math bool // math nodes; off falls back to code shapes
Mermaid bool // mermaid diagram nodes; off falls back to code blocks
AllowRawHTML bool // carry raw HTML unsanitized (unsafe:true)
// Source is the markdown source doc was parsed from, used to derive
// content-hash block ids from node spans (see "Block identity" in
// the package documentation). Empty or nil is valid — e.g. when
// building from decoded doc JSON with no source at hand — and
// switches every block to the deterministic kind+ordinal fallback
// id.
Source []byte
}
Options configures Build. The toggles mirror the HTML renderer's semantics exactly:
- Math/Mermaid off: math and mermaid nodes fall back to the same shapes the HTML path falls back to — a MathBlock becomes a CodeBlock with language "math", a Diagram becomes a CodeBlock with the engine as its language, and a MathInline becomes a CodeSpan of its source.
- HeadingAnchors off: headings carry no anchor id.
- AllowRawHTML: raw HTML nodes carry the raw markup with Unsafe=true; otherwise the bluemonday-sanitized form with Unsafe=false. Unlike the HTML renderer's single Unsafe flag, AllowRawHTML governs ONLY raw HTML: URL policy (resolve.SafeURL) always applies — a blocked destination is reported as url:"" + blocked:true and the host decides what to do with it.
- Highlighting: code blocks carry token runs (CodeBlock.Runs) from the same chroma tokenise seam the HTML renderer highlights through (htmlrender.TokenRuns, cached there). Off — or on with an unknown/missing language, or when chroma cannot reproduce the code text exactly — leaves Runs nil (wire: null) and the host renders Text plain, the tree analogue of the HTML renderer's fallback-to-plain path.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns the recommended Options, matching the HTML renderer's defaults: highlighting, mermaid, math, and heading anchors enabled; raw HTML sanitized.
type Paragraph ¶
Paragraph is a run of inline content. Wire: {"kind":"paragraph",span,id,"children"}.
type SoftBreak ¶
type SoftBreak struct{}
SoftBreak is an intra-paragraph line break that renders as whitespace. It never carries a span (the parser records none). Wire: {"kind":"softBreak"}.
type Strikethrough ¶
Strikethrough is struck-through content. Wire: {"kind":"strikethrough",span,"children"}.
type Strong ¶
Strong is strongly-emphasized (bold) content. Wire: {"kind":"strong",span,"children"}.
type Table ¶
type Table struct {
Span document.Span
ID string
Alignments []document.Alignment
Header []Cell
Rows [][]Cell
}
Table is a pipe table. Alignments has one entry per column; Header is the header row's cells (nil only for hand-built documents without a header row); Rows are the body rows. Wire: {"kind":"table",span,id,"alignments" (array of "none"|"left"|"center"|"right"),"header","rows"} — header and rows always arrays.
type Text ¶
Text is a literal text run (emoji shortcodes already substituted by the parser). Wire: {"kind":"text",span,"value"}.
type ThematicBreak ¶
ThematicBreak is a horizontal rule. Wire: {"kind":"thematicBreak",span,id}.
type TokenRun ¶
type TokenRun struct {
Text string
TokenType string // chroma TokenType.String() name, e.g. "Keyword"
}
TokenRun is one syntax-highlight token of a code block: a slice of the code text tagged with chroma's canonical token-type name.
type Tree ¶
type Tree struct {
Blocks []Block // top-level blocks, in document order
Footnotes []Footnote // footnote definitions, in first-reference order
}
Tree is the built render tree. Its wire form is produced by Tree.MarshalJSON; see the package documentation for the schema.
func Build ¶
Build produces the resolved render tree for doc. It never mutates doc. All policy is applied here, through the same shared code paths the HTML renderer uses: URL resolution/filtering via derive.Href (resolve.Resolver trust contract included), raw-HTML sanitizing via derive.SanitizeHTML, admonition titles via derive.AdmonitionTitle, and footnote pairing via derive.Footnotes.
func (*Tree) FootnoteByIndex ¶ added in v0.11.0
FootnoteByIndex returns the footnote definition in t.Footnotes whose Index matches index (typically a FootnoteRef.Index), so a host can resolve a reference to its definition's ID/Span/Blocks — e.g. for scroll-to-definition — without walking source line numbers. Unlike FootnoteRef.DefID, this always works regardless of Options.Source. Reports ok=false when no definition with that index exists (e.g. a hand-built tree with mismatched refs).
func (*Tree) MarshalJSON ¶
MarshalJSON encodes the tree in the strict version-1 wire format documented on the package: {"version":1,"blocks":[…],"footnotes":[…]} with per-kind block/inline objects. Field order within each object is fixed (kind, span, id, kind fields, children/blocks) and presence rules are strict — see each node type's Wire comment.