Commit 29352cf
authored
Fixes #9847.
The `mo.ui.chat` component does not render marimo Markdown/HTML the way
the rest
of the notebook does — marimo admonitions, in particular, come out
unstyled.
When a model returns a rich object, the backend serializes it to HTML
via
`as_html(response).text` (`marimo/_plugins/ui/_impl/chat/chat.py`). For
an
admonition, `mo.md(...)` produces:
```html
<span class="markdown prose dark:prose-invert contents"><div class="admonition error">
<p class="admonition-title">Error</span>
<span class="paragraph">Nope!</span>
</div></span>
```
The frontend renders assistant text parts with `MarkdownRenderer`, which
wraps
**Streamdown**. Streamdown sanitizes with `rehype-sanitize` using the
default
GitHub schema, which strips `class` from most elements. The
`admonition`/`prose`
classes that carry all the styling are removed, so the content renders
as plain
text. The marimo CSS itself is fine and global
(`frontend/src/css/admonition.css`); it just never matches because the
class
names are gone.
## Fix
Configure Streamdown's rehype pipeline to **preserve class names**. We
extend
`rehype-sanitize`'s default schema to allow `className` on every element
and pass
it via `rehypePlugins`, reusing Streamdown's other defaults
(`rehype-raw` to
parse the HTML, `rehype-harden` to lock down links/images). Scripts,
inline
styles, and unsafe URLs are still sanitized away — we only stop
discarding class
names, which are not an execution vector.
This is a single-renderer fix: backend-rendered marimo HTML
(admonitions, prose,
etc.) now styles correctly through Streamdown, and normal streamed LLM
markdown
is unchanged. Interactive marimo UI elements (`<marimo-...>`) still
route to
marimo's HTML renderer in `chat-display.tsx`, since they're custom
elements that
Streamdown can neither keep nor hydrate.
## Fix malformed paragraph tags
`mo.md(...)` also emitted malformed HTML for admonitions — note the
`<p class="admonition-title">Error</span>` above. `_md` rewrote `<p>` to
`<span class="paragraph">` to allow nested block elements, but the
opening
replacement only matched bare `<p>` while the closing replacement
matched every
`</p>`, so an attributed paragraph (the admonition title) was left with
a
mismatched close tag. Browsers auto-correct this, but the stricter
sanitize/parse path does not. We now rewrite only attribute-less
`<p>...</p>`
pairs, so attributed paragraphs keep a balanced `</p>`.
1 parent efd920d commit 29352cf
7 files changed
Lines changed: 122 additions & 7 deletions
File tree
- frontend
- src/components
- chat
- markdown
- __tests__
- marimo/_output
- tests/_output
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
144 | 145 | | |
145 | 146 | | |
146 | 147 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
64 | | - | |
| 63 | + | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| |||
Lines changed: 43 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
8 | 13 | | |
9 | 14 | | |
10 | 15 | | |
| |||
148 | 153 | | |
149 | 154 | | |
150 | 155 | | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
151 | 173 | | |
152 | 174 | | |
153 | 175 | | |
| |||
187 | 209 | | |
188 | 210 | | |
189 | 211 | | |
| 212 | + | |
190 | 213 | | |
191 | 214 | | |
192 | 215 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
269 | 269 | | |
270 | 270 | | |
271 | 271 | | |
272 | | - | |
273 | | - | |
274 | | - | |
275 | | - | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
276 | 282 | | |
277 | 283 | | |
278 | 284 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
278 | 278 | | |
279 | 279 | | |
280 | 280 | | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
281 | 320 | | |
282 | 321 | | |
283 | 322 | | |
| |||
0 commit comments