Skip to content

UI/svg block rendering#24080

Merged
ServeurpersoCom merged 6 commits into
ggml-org:masterfrom
ServeurpersoCom:ui/svg-block-rendering
Jun 15, 2026
Merged

UI/svg block rendering#24080
ServeurpersoCom merged 6 commits into
ggml-org:masterfrom
ServeurpersoCom:ui/svg-block-rendering

Conversation

@ServeurpersoCom

@ServeurpersoCom ServeurpersoCom commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Overview

A follow-up idea: reuse the SVG support of Mairmaid rendered code from #24032 @allozaur

Implementation

Adds inline rendering of fenced ```svg code blocks in the chat, reusing the existing mermaid diagram UX (block wrapper, header, copy/preview buttons and the zoom/pan/download preview dialog). Every svg is passed through a hardened DOMPurify sanitizer before it touches the DOM: foreignObject, script, style, a, use and image tags are forbidden, href/xlink:href attributes are dropped so no external resource is ever fetched, and a size cap guards against oversized input, which makes model-generated svg safe to render.

The block also renders live while it streams, so the diagram draws progressively and gets one final clean render once the fence closes. The change also rationalises the code shared between mermaid and svg: the twin block styles are merged into grouped CSS selectors and the pre-block transform is extracted into a single createPreTransform factory, so the two paths no longer duplicate each other. Mermaid keeps its own internal sanitization (securityLevel strict) and is untouched by the new svg sanitizer.

Requirements

@ServeurpersoCom
ServeurpersoCom requested a review from a team as a code owner June 3, 2026 15:51
@ServeurpersoCom
ServeurpersoCom marked this pull request as draft June 3, 2026 15:52
@ServeurpersoCom

ServeurpersoCom commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

cc @allozaur Here is the PR that works, need to check my naming of the refactored components.

mermaid-pre.ts            svg-pre.ts
      |                        |
      +-----------+------------+
                  v
          pre-transform.ts
       createPreTransform()
                  |
   <pre class="mermaid">   <pre class="svg-diagram">

@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

It's so much fun!

realtime-svg.mp4

@ServeurpersoCom
ServeurpersoCom marked this pull request as ready for review June 3, 2026 16:13
@coder543

coder543 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

This does look fun!

@coder543

coder543 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

For both this svg renderer and the mermaid one, is there a button to flip back to the source code view?

@ServeurpersoCom

ServeurpersoCom commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

I try to port OmniSVG1.1_8B model to GGUF to see what happens, convert done, just need to add a special decoder!

EDIT: Okay, I got it working, but it's really not SOTA. Plus, they fine-tuned the model poorly. They couldn't even keep the conversational functionality. It's a shame, I'm sure we can do better!

@ServeurpersoCom

ServeurpersoCom commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

For both this svg renderer and the mermaid one, is there a button to flip back to the source code view?

I thought about it, it's a shame to miss seeing the code underneath! We would need Alek's UX opinion, everything that is rendered will have the same look, for example a tab system at the top of the code block.

EDIT: But in this case, the copy button retrieves the code!

@coder543

coder543 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor
Definitely a bit off topic, but I wonder if there should be a different approach to visual elements. Interpreting any code block that has the right language on it is certainly the easiest, but it prevents the model from choosing whether it wants to show code or show a rendered element.

In Gemini, I recently saw it use a timeline component:

image

Naturally, I asked it to explain how that worked:

image

Interestingly, whatever the parent "container" is, there was no way for Gemini to explain what that is without the text vanishing, as you can see with the empty parentheses.

So, Gemini seems to use inline HTML-like blocks with custom components to render things. As one of the trendy things these days, it would be interesting to support things in that way.

I think the biggest drawback of this approach is that the web UI would have to inject a system prompt explaining these capabilities, and some people might not like that, so then it might need to be optional.

On topic, since we're currently already doing this for mermaid, I don't see a problem with extending it to svg, but the long term solution might be more component-oriented? Code blocks like mermaid and svg should definitely offer a button to view the rendered output, but defaulting to the rendered output and only showing that have potential downsides depending on the use case.

Either way, I look forward to playing with this SVG feature!

@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

Yes, these are follow-up refactorings to be done; you can even open a feature request to suggest ideas!

@allozaur

allozaur commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Definitely a bit off topic, but I wonder if there should be a different approach to visual elements. Interpreting any code block that has the right language on it is certainly the easiest, but it prevents the model from choosing whether it wants to show code or show a rendered element.

On topic, since we're currently already doing this for mermaid, I don't see a problem with extending it to svg, but the long term solution might be more component-oriented? Code blocks like mermaid and svg should definitely offer a button to view the rendered output, but defaulting to the rendered output and only showing that have potential downsides depending on the use case.

Either way, I look forward to playing with this SVG feature!

Yes, we will have improved UI for agentic content implemented in near future

Comment thread tools/ui/src/lib/utils/sanitize-svg.ts Outdated
Comment thread tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte Outdated
Comment thread tools/ui/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte Outdated
@ServeurpersoCom
ServeurpersoCom force-pushed the ui/svg-block-rendering branch from 7c9c9a9 to 87830c2 Compare June 11, 2026 16:25
@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

The latest commit makes svg keep updating live even while zoomed, and mounts each svg in a shadow root so svg animations work while keeping the sanitization safe

- Move the svg size ceiling and DOMPurify config out of sanitize-svg.ts into /constants.
- Rename the svg-diagram class to svg-block so the name no longer implies diagrams only.
- Replace the svg, xml and svg tag magic strings in the markdown pipeline with shared constants.
- Promote the data-svg-rendered marker and its sibling data attributes to constants.
Mount each sanitized svg inside an open shadow root so author <style> and
keyframe or smil animations run while staying scoped to the host element.
Relax the sanitizer to forbid only foreignObject and script, which lets
animation, href and external resource refs through for wider compatibility.
Render the inline block and the zoom dialog from the same reactive source,
so a streaming svg keeps drawing live inside the open zoom popup.
@allozaur
allozaur force-pushed the ui/svg-block-rendering branch from 0993cf6 to ef5dff4 Compare June 12, 2026 14:45
@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

Thanks for the rebase :)

@ServeurpersoCom
ServeurpersoCom merged commit 2a6c391 into ggml-org:master Jun 15, 2026
6 checks passed
@sdroege

sdroege commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What do you think about adding a button that switches between rendering SVG/mermaid inline and showing it as code block, maybe next to the copy and preview buttons.

@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

What do you think about adding a button that switches between rendering SVG/mermaid inline and showing it as code block, maybe next to the copy and preview buttons.

I'll handle this in a follow-up PR, since it applies to several features, mermaid diagrams included. For reference, GitHub itself only exposes a "copy" button on rendered diagrams, not a source toggle. But we can do it.

@allozaur

Copy link
Copy Markdown
Contributor

What do you think about adding a button that switches between rendering SVG/mermaid inline and showing it as code block, maybe next to the copy and preview buttons.

I'll handle this in a follow-up PR, since it applies to several features, mermaid diagrams included. For reference, GitHub itself only exposes a "copy" button on rendered diagrams, not a source toggle. But we can do it.

@ServeurpersoCom could u please create an issue for this impprovement and add it as a subtask of #24332

@DeryabinIvan

Copy link
Copy Markdown

For some reason top part of the square-ish svg previews is cropped.

Example 1:
изображение
изображение

Example 2:
изображение
изображение

Rectangular looks okay:
изображение

@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author

Yes This stems from the original CSS and the default size of the code blocks; we need to look at how to improve this: We could make it scrollable like text block.

But you can enable the following option: "Use full height code blocks
Always display code blocks at their full natural height, overriding any height limits."

Disabled:
Sans titre

Option:
Sans titre2

Enabled:
Sans titre3

Same for SVG.

@ServeurpersoCom

Copy link
Copy Markdown
Contributor Author
Mermaid-SVG-Code-Scroll.mp4

am17an pushed a commit to am17an/llama.cpp that referenced this pull request Jun 20, 2026
* ui: add svg block visualizer based on allozaur's mermaid PR

* ui: rationalise diagram block styling and pre transforms shared by mermaid and svg

* ui: live render streaming svg blocks

* ui: also render svg authored in xml code fences

* ui: refactor svg block rendering, address review from allozaur

- Move the svg size ceiling and DOMPurify config out of sanitize-svg.ts into /constants.
- Rename the svg-diagram class to svg-block so the name no longer implies diagrams only.
- Replace the svg, xml and svg tag magic strings in the markdown pipeline with shared constants.
- Promote the data-svg-rendered marker and its sibling data attributes to constants.

* ui: render svg blocks in a shadow root for animation and live zoom

Mount each sanitized svg inside an open shadow root so author <style> and
keyframe or smil animations run while staying scoped to the host element.
Relax the sanitizer to forbid only foreignObject and script, which lets
animation, href and external resource refs through for wider compatibility.
Render the inline block and the zoom dialog from the same reactive source,
so a streaming svg keeps drawing live inside the open zoom popup.
papamoose pushed a commit to papamoose/llama.cpp that referenced this pull request Jun 27, 2026
* ui: add svg block visualizer based on allozaur's mermaid PR

* ui: rationalise diagram block styling and pre transforms shared by mermaid and svg

* ui: live render streaming svg blocks

* ui: also render svg authored in xml code fences

* ui: refactor svg block rendering, address review from allozaur

- Move the svg size ceiling and DOMPurify config out of sanitize-svg.ts into /constants.
- Rename the svg-diagram class to svg-block so the name no longer implies diagrams only.
- Replace the svg, xml and svg tag magic strings in the markdown pipeline with shared constants.
- Promote the data-svg-rendered marker and its sibling data attributes to constants.

* ui: render svg blocks in a shadow root for animation and live zoom

Mount each sanitized svg inside an open shadow root so author <style> and
keyframe or smil animations run while staying scoped to the host element.
Relax the sanitizer to forbid only foreignObject and script, which lets
animation, href and external resource refs through for wider compatibility.
Render the inline block and the zoom dialog from the same reactive source,
so a streaming svg keeps drawing live inside the open zoom popup.
adrianhoehne pushed a commit to adrianhoehne/llama.cpp that referenced this pull request Jul 5, 2026
* ui: add svg block visualizer based on allozaur's mermaid PR

* ui: rationalise diagram block styling and pre transforms shared by mermaid and svg

* ui: live render streaming svg blocks

* ui: also render svg authored in xml code fences

* ui: refactor svg block rendering, address review from allozaur

- Move the svg size ceiling and DOMPurify config out of sanitize-svg.ts into /constants.
- Rename the svg-diagram class to svg-block so the name no longer implies diagrams only.
- Replace the svg, xml and svg tag magic strings in the markdown pipeline with shared constants.
- Promote the data-svg-rendered marker and its sibling data attributes to constants.

* ui: render svg blocks in a shadow root for animation and live zoom

Mount each sanitized svg inside an open shadow root so author <style> and
keyframe or smil animations run while staying scoped to the host element.
Relax the sanitizer to forbid only foreignObject and script, which lets
animation, href and external resource refs through for wider compatibility.
Render the inline block and the zoom dialog from the same reactive source,
so a streaming svg keeps drawing live inside the open zoom popup.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants