Skip to content

Markdown

Edgar Mesquita edited this page Aug 12, 2026 · 7 revisions

Markdown

Since 0.2.0-preview.25

Markdown, rendered as the design system. One call and a document — a doc page, a chat answer, a release note — comes out in the app's own theme, because every block lowers to the components the app is already made of. There is no second "basic" renderer to drift from: the default look is the default theme, and MaterialTheme.FromSeed(…) rebrands a rendered document with nothing else changed.

Markdown(source)

Markdown(source)
{
    Style = new MarkdownStyle          // optional; defaults read from the theme
    {
        Heading1 = TypeRole.Display,   // roles per heading level
        CodeLineNumbers = true,        // gutter on fenced code (off by default)
        BlockGap = 18,                 // vertical rhythm between blocks
    },
}

What lowers to what

Markdown Component
Heading 1–4 Text at TypeRole rungs (Heading, Title, TitleSmall, Label)
Paragraph ONE Text with TextRun spans — bold, inline code, links inside the sentence
inline code a mono span a step below the prose size, keeping the paragraph's line box
[link](href) TextRun.Destination — navigates on web; prose on Photon until run-level hit testing
Fenced code CodeBlock, highlighted by the same rules the CodeEditor uses, with its copy button
List (-, 1.) marker gutter + span paragraphs, one nesting rung
> quote a start-bordered box
Table Grid rows under a hairline-framed card
--- Divider

A paragraph is one Text with spans, never a row of Texts — a row breaks between runs instead of between words, so a sentence with three code spans would wrap at the spans.

One parser, every side

The parser (MarkdownParser.Parse / .Inline, public — the block model is usable without the component) is written in the eqc subset and runs on every side of the framework: the same C# parses during SSR and on Photon, and its transpiled twin parses in the browser. That is what makes the server's render and the client's re-render agree by construction — and what lets a chat message streaming into client state re-render with zero server round-trips.

Parity is not aspired to, it is pinned: one canonical document and one shape dumper live in both test suites (MarkdownParserTests.cs, markdown-parser.spec.ts) with identical expected literals. If the twin ever disagrees with the C# parser, a suite breaks before a page does.

The scanner design (code spans claim their characters first, one left-to-right walk, no regex) comes from the documentation site that proved it in production; the site keeps its own dialect (callouts from bold-led quotes, eq: version marks) as transforms over these blocks.

Fences (v1, deliberate)

  • Emphasis is asterisk-only_underscores_ stay literal; prose about code is full of identifiers_with_underscores.
  • Italic parses but paints uprightTypeStyle has no slant axis yet (weight and mono are its only variances), so TextRun cannot say it. The model keeps Italic; when the type system grows the axis, one loop in the component changes.
  • Images read as their alt text linking to the source — an Image node is an explicitly sized slot by spec, and markdown carries no dimensions to size one with.
  • Raw HTML never passes through — an injection surface on web, meaningless on Photon.
  • Fenced code only — no four-space indent blocks; GitHub-alert callouts (> [!NOTE]) are an app-level dialect for now.

Clone this wiki locally