Pull requests / #1628

#1628 frontend: Add preview of generated HTML and SVG

open · @thoives · 0 comentários · No GitHub

Server & APIDocumentation

Descrição

## Title
Issue: Resolves #1574 
Also implements previewing generated SVGs - especially nice for mobile!

## Summary
Adds a preview for generated SVG and HTML in the chat. A finished ```svg``` or ```html``` block gets a preview button that flips between the code and a rendered picture or page. The preview is sandboxed, with no scripts and no network by default, follows the light or dark theme, and animates SVG in Chromium and Firefox. On an HTML page, the user can run that one block with scripts, after a warning.

## What changed
- `serve/web/app.js`
  - **Preview frame:** A finished svg or html block gets a preview button and a `sandbox=""` iframe (opaque origin: no scripts, forms, popups or navigation). A meta CSP (`default-src 'none'; style-src 'unsafe-inline'; img-src data:`) blocks the network. Blocks still streaming get no preview.
  - **Document building (`previewDoc()`):**
    - A complete `<!doctype html>` page gets the CSP and theme first, then the page without its doctype. Its own html and head tags merge into ours, and its own CSS still wins.
    - An HTML fragment is wrapped in a minimal page.
  - **Theme:** The frame carries `color-scheme` and the ink color from `--st-ink` and has a transparent background, so it sits on the theme surface in both themes. `setTheme()` calls `refreshPreviews()`, which rebuilds the frames that have been opened.
  - **SVG animation in Firefox:** Firefox does not run SMIL (`<animate>`, `<animateTransform>`) in a document where scripting is off, which is what `sandbox=""` gives. A plain SVG is therefore shown as an `<img>` with a data URL (`svgImage()`), which runs no scripts, loads nothing, and animates. It is checked with `DOMParser` first, a missing `xmlns` is added, and the theme ink is injected so `currentColor` works. If it is not well-formed XML, the inline SVG path is used.
  - **Run with scripts (HTML pages only):** a warning button, visible while the preview is open, opens a `confirm()` dialog. If accepted, only that block is rebuilt as a new iframe with `sandbox="allow-scripts"` (still an opaque origin) and a CSP that allows inline scripts but still no network. It is not remembered, and closing the preview turns it off again. The frame is created anew each time, because the sandbox attribute counts only when a frame is created.
- `serve/web/components.css`: styles for the button group, the pressed state, the preview box (hidden by default, `min(340px,60vh)` high, on `--st-surface-2`, transparent iframe), and an amber outline while scripts run.
- `docs/DETAILS.md`: the Chat bullet describes the preview, the theme, SVG animation, and the scripts button on HTML pages.

## Extra Notes
- Tested with Playwright (Chromium and Firefox, light and dark), using the app's real `markdown()` and click handler. A SMIL SVG gave 4 distinct frames over about a second in both browsers (static in Firefox before the image route). Scripts were off by default, ran only after confirming, `fetch()` was blocked, and they were off again after closing and reopening the preview. A script placed before `<head>` was blocked in safe mode. WebKit/Safari is untested because it would not launch here.
- With scripts on, a `<meta http-equiv=refresh>` can still send the frame to a remote page, which would then run its own scripts in the sandbox. The CSP does not stop navigation, and this was not tested. The sandbox has no same-origin access, so it cannot read the chat or storage.
- An SVG that contains its own `<script>` never runs it, because SVG previews use the image route and have no scripts button.
- Inline SVG with SMIL inside an HTML page stays static in Firefox until the user runs scripts.
- Rebuilding a frame on a theme change reloads it. Closing the preview also resets any scripts.
- The preview state lives on the block, so it resets if the answer is rebuilt while it streams.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Human: seems to work in Firefox with generated SVG (opaque and transparent) and HTML code.

No site

Links install, modelos, releases.