Skip to content

Markdown

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

Markdown

🌐 This page in: English · Português

Since 0.2.0-preview.26

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, italic, 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, which navigates on BOTH targets (Photon hit-tests the run since 0.2.0-preview.28)
Fenced code CodeBlock, highlighted by the same rules the CodeEditor uses, with its copy button
```mermaid fence a Mermaid diagram, drawn natively by the design system
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, because a row breaks between runs instead of between words, so a sentence with three code spans would wrap at the spans.

Emphasis draws on every target

Since 0.2.0-preview.28

*italic* slants now: TypeStyle grew the axis (it composes with weight and mono, so **bold *italic*** and italic code both exist), and the fence this page carried ("italic parses but paints upright") closed with it, one loop in the component later, exactly as it promised. On Photon the same release made the whole paragraph draw RUN BY RUN, so the bold word, the inline code span and the mid-sentence link stopped dissolving into flat prose there. See Photon for the fragment mechanics.

One parser, every side

The parser (MarkdownParser.Parse / .Inline, public, because 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.
  • 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. A ```mermaid fence is the exception that renders: see Mermaid.

Clone this wiki locally