Summary
marmot-markdown passes raw HTML tags through as literal text, so <details> / <summary> disclosure markup reaches consumers as plain Inline::Text instead of a structured block. Downstream clients therefore can't render a native collapsible disclosure without reimplementing HTML parsing themselves. Requesting that the parser recognize <details> / <summary> and emit a structured block through the AST and the marmot-uniffi FFI.
Current behavior
The parser intentionally does not interpret raw HTML:
crates/marmot-markdown/src/inline.rs:405-406 — "tag-like sequences pass through unchanged", buf.push('<').
- No HTML-block leaf exists in
crates/marmot-markdown/src/block.rs — the verbatim leaves are only FencedCode / MathBlock (line 127). A line starting with <details> just accumulates into Leaf::Paragraph.
Dumping marmot_markdown::parse for a disclosure shows two shapes depending on blank lines.
No blank lines — one paragraph, tags as literal Text, body inline-parsed:
Input:
<details>
<summary>Tap to expand</summary>
Hidden body **bold**
</details>
Output:
Document { blocks: [ Paragraph { inlines: [
Text("<details>"), SoftBreak,
Text("<summary>Tap to expand</summary>"), SoftBreak,
Text("Hidden body "), Strong([Text("bold")]), SoftBreak,
Text("</details>") ] } ] }
With blank lines — split into separate paragraphs:
Document { blocks: [
Paragraph { inlines: [Text("<details>"), SoftBreak, Text("<summary>Sum</summary>")] },
Paragraph { inlines: [Text("Body para")] },
Paragraph { inlines: [Text("</details>")] } ] }
Impact
Because the structure is never emitted, any consumer that wants to render a disclosure has to reimplement HTML parsing on top of the AST — mapping tag boundaries that fall inside Text inlines, or re-parsing a body substring — which is fragile and must be duplicated in every client that consumes marmot-markdown. Concretely, a downstream client change that attempted app-side detection is a silent no-op (it renders the literal tags) while its unit tests stay green, because those tests hand-build an assumed AST the real parser never produces.
Requested change
Recognize <details ...> / <summary ...> and emit a structured block, e.g.:
Block::Details {
summary: Vec<Inline>,
open: bool,
body: Vec<Block>,
}
surfaced through marmot-uniffi as MarkdownBlockFfi::Details { summary, open, body }, so every consumer renders it directly and consistently.
Suggested semantics:
- Honor the
open attribute (initially expanded).
- Summary carries inline formatting; body is block-level markdown.
- Keep the existing DoS bounds (source-scan / tag-length caps).
- Keep escaped (
<details>) and inline-code (`<details>`) forms literal.
Summary
marmot-markdownpasses raw HTML tags through as literal text, so<details>/<summary>disclosure markup reaches consumers as plainInline::Textinstead of a structured block. Downstream clients therefore can't render a native collapsible disclosure without reimplementing HTML parsing themselves. Requesting that the parser recognize<details>/<summary>and emit a structured block through the AST and themarmot-uniffiFFI.Current behavior
The parser intentionally does not interpret raw HTML:
crates/marmot-markdown/src/inline.rs:405-406— "tag-like sequences pass through unchanged",buf.push('<').crates/marmot-markdown/src/block.rs— the verbatim leaves are onlyFencedCode/MathBlock(line 127). A line starting with<details>just accumulates intoLeaf::Paragraph.Dumping
marmot_markdown::parsefor a disclosure shows two shapes depending on blank lines.No blank lines — one paragraph, tags as literal
Text, body inline-parsed:Input:
Output:
With blank lines — split into separate paragraphs:
Impact
Because the structure is never emitted, any consumer that wants to render a disclosure has to reimplement HTML parsing on top of the AST — mapping tag boundaries that fall inside
Textinlines, or re-parsing a body substring — which is fragile and must be duplicated in every client that consumesmarmot-markdown. Concretely, a downstream client change that attempted app-side detection is a silent no-op (it renders the literal tags) while its unit tests stay green, because those tests hand-build an assumed AST the real parser never produces.Requested change
Recognize
<details ...>/<summary ...>and emit a structured block, e.g.:surfaced through
marmot-uniffiasMarkdownBlockFfi::Details { summary, open, body }, so every consumer renders it directly and consistently.Suggested semantics:
openattribute (initially expanded).<details>) and inline-code (`<details>`) forms literal.