limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/Data

    Markdown

    Renders a bounded subset of Markdown as an ordinary element tree. Use it for text that arrives as Markdown, such as a message, including while it is still being written.

    Native componentGuide source

    Preview

    CodeSource
    fn view(state: &State, width: f32, palette: ComponentPalette) -> Element {
        Markdown::new(&state.message, palette)
            .width(width)
            .link_action_prefix("open:")
            .render()
            .element
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::Clicked { id } = action {
            if let Some(link) = id.strip_prefix("open:") {
                // link is the block hash, the run index and the URL, joined by colons.
                if let Some(url) = link.splitn(3, ':').nth(2) {
                    state.open_in_browser(url);
                }
            }
        }
    }

    Examples

    Each one is the library drawing it. Press, type and hover.

    Chat marks
    CodeSource
    Markdown::new(CHAT, palette)
        .width(width)
        .link_action_prefix("markdown-chat:")
        .render()
        .element
    Text size1 / 3
    CodeSource
    Markdown::new(SIZED, palette)
        .width(width)
        .font_size(12.0)
        .link_action_prefix("markdown-small:")
        .render()
        .element
    Text size2 / 3
    CodeSource
    Markdown::new(SIZED, palette)
        .width(width)
        .link_action_prefix("markdown-default:")
        .render()
        .element
    Text size3 / 3
    CodeSource
    Markdown::new(SIZED, palette)
        .width(width)
        .font_size(18.0)
        .link_action_prefix("markdown-large:")
        .render()
        .element
    Streaming
    CodeSource
    Markdown::new(STREAMING, palette)
        .width(width)
        .streaming(true)
        .link_action_prefix("markdown-streaming:")
        .render()
        .element
    Limits1 / 2
    CodeSource
    Markdown::new(COMPLETE, palette)
        .width(width)
        .labels(labels)
        .limits(MarkdownLimits { source_chars: 20_000, blocks: 3 })
        .link_action_prefix("markdown-limited:")
        .render()
        .element
    Limits2 / 2
    CodeSource
    Markdown::new(COMPLETE, palette)
        .width(220.0)
        .labels(labels)
        .limits(MarkdownLimits { source_chars: 20_000, blocks: 6 })
        .link_action_prefix("markdown-narrow:")
        .render()
        .element
        .size(Some(220.0), None)
    Addresses for the host
    CodeSource
    link(cx, "https://GitHub.com/limn"),
    link(cx, "https://example.com/docs"),
    link(cx, "file:///etc/passwd")
    Paginated source
    CodeSource
    page.render(width, palette)

    Markdown

    Renders a bounded subset of Markdown as an ordinary element tree. Use it for text that arrives as Markdown, such as a message, including while it is still being written.

    Import

    use limn::{Markdown, MarkdownLabels, MarkdownLimits};

    Markdown has a render of its own, so Component is not needed.

    Usage

    render returns a MarkdownPreview: element goes in the tree, and limited says the source was cut. A link reports UiAction::Clicked with an ID that starts with the link prefix.

    fn view(state: &State, width: f32, palette: ComponentPalette) -> Element {
        Markdown::new(&state.message, palette)
            .width(width)
            .link_action_prefix("open:")
            .render()
            .element
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::Clicked { id } = action {
            if let Some(link) = id.strip_prefix("open:") {
                // link is the block hash, the run index and the URL, joined by colons.
                if let Some(url) = link.splitn(3, ':').nth(2) {
                    state.open_in_browser(url);
                }
            }
        }
    }

    The subset is headings of three levels, paragraphs, bulleted, numbered and checked lists, quotes, subtext lines, fenced code and rules, with strong, emphasis, bold italic, underline, strikethrough, inline code and web links. Tables, images, HTML and nested quotes stay text. Components has the full reference.

    Marks

    WrittenDrawn
    **strong**The bold face (FontFamily::SansBold), in colors.strong
    *emphasis*The italic face, in colors.emphasis
    ***both***FontFamily::SansBoldItalic, in colors.strong
    __underline__A line under the text, as chat clients write it; CommonMark reads it as strong
    ~~strikethrough~~A line through the text
    `code`Monospace over colors.code_fill
    -# subtextA line of its own, 0.8125 times the body’s size, in colors.muted

    Marks nest: __**this**__ and **__this__** are both bold and underlined, and ~~*this*~~ is struck italic. A mark closes at the first copy of its delimiter, so it never holds itself, and neither opens nor closes inside a <…> without spaces, such as an autolink or a chat client’s <:emoji_name:id>. \~ writes a tilde, as \* and \_ write theirs. A web address stops before a | at its end, which closes a table cell or a ||spoiler|| around it.

    Width

    Markdown::new(source, palette).width(560.0).render()

    width is the reading column, kept between 48 and 1,200. A width that is not finite reads as 600.

    Text size

    Markdown::new(source, palette).font_size(state.text_size).render()

    font_size is the size of body text, 14 unless set. Headings, inline and fenced code, list markers and their column, line heights, the space between blocks and the note under a cut preview keep their proportion to it: at 21, everything is half as large again. A drawn diagram keeps its own size; only the words on its card follow. A size that is not finite and positive is ignored, and the rest is kept between 8 and 96. In a column too narrow for a large size, a list gives up its nesting first and then the width of its marker column, so an item’s text always starts inside the column.

    code_font_size(size) sets fenced code independently of the body, in logical pixels. Without it, code keeps its existing proportion to the body. Invalid numbers and nonpositive sizes are ignored; valid sizes stay within 1 to 96. code_word_wrap(false) preserves each code source line in a horizontal viewport; prose continues to wrap. Both settings take effect in cached and streaming previews. A standalone CodeBlock offers the same controls as font_size(size) and word_wrap(bool); its label keeps its original size. Give the preview a stable .id(message_id) to preserve code viewport offsets when rebuilding it each frame. Each code fence then receives its own ID within that message, including identical fences. Standalone blocks take .id(block_id). Anonymous instances have independent temporary identities.

    Streaming

    Markdown::new(&state.partial, palette).streaming(true).render()

    With streaming(true) the source is still being written: an unclosed delimiter at its end already styles its text, a web address there has no action until it ends, and the blocks above the last one are reused from the previous render.

    Markdown::new(source, palette).link_action_prefix("open:").render()

    link_action_prefix is the start of every link action; markdown_link: unless set. The prefix is a &'static str.

    Markdown::web_link(value) gives one address in the normalized form the renderer would open, or None for one it refuses. Markdown::link_icon(address) gives the glyph its link wears. Markdown::web_links(source) lists the complete web addresses in a text with their ranges.

    Labels

    let labels = MarkdownLabels {
        code_fallback: "CODE",
        truncated_note: "Long text: showing the first blocks only.",
        ..MarkdownLabels::DEFAULT
    };
    Markdown::new(source, palette).labels(labels).render()

    labels replaces the renderer’s own strings with the host’s language: code_fallback labels a code block with no language and truncated_note is the line under a cut preview. The other fields label a drawn diagram and its actions.

    Limits

    Markdown::new(source, palette)
        .limits(MarkdownLimits { source_chars: 20_000, blocks: 3 })
        .render()

    limits sets how much of the source is rendered: source_chars and blocks. A limit of zero reads as one. When the source is cut, limited is true on the preview.

    Colors

    colors(MarkdownColors) replaces the colors derived from the palette. MarkdownColors::for_palette(palette) is the default to start from.

    Markdown::new(source, palette).plain_with_autolinks(21.0).render()

    plain_with_autolinks(line_height) keeps the source literal, as one paragraph with that line height: only complete HTTP(S) addresses are styled and become links. Nothing is cached or cut.

    Diagrams

    A closed mermaid fence that Diagram can read is drawn instead of shown as code. diagram_action_prefix(prefix) gives every diagram an action that switches between the drawing and its source: the prefix followed by the diagram’s ID as sixteen hexadecimal digits. The host keeps the IDs it was asked to show as source and passes them to diagram_sources.

    Source by pages

    let page = Markdown::source_page(source, 0);
    page.render(width, palette)

    Markdown::source_page(source, start_byte) returns a MarkdownSourcePage: a page of the exact source as text, with previous_byte and next_byte for the pages around it. Its render(width, palette) shows it as an element.

    Long lists

    Hold Markdown::begin_frame(scope, width) while building a long list of messages; see the notes on the cache in Components.

    Actions

    ActionWhen
    UiAction::Clicked { id } with id = the link prefix, then the block hash, the run index and the URL, joined by colonsA link is activated.
    UiAction::Clicked { id } with id = the diagram prefix and sixteen hexadecimal digitsThe switch on a diagram or on its source is activated.

    API reference

    The guide covers composition, options and actions. Generate the complete Rust type and builder reference from the checkout:

    cargo doc -p limn --open

    Component API overview · State and actions

    The example is drawn by crates/limn-gallery/src/pages/markdown.rs.