FabricFabric
Features

Site Preview Block

The site-preview code block renders a multi-file design bundle (HTML + CSS + JS + assets) as a live preview inside Fabric Agents — with comment pins, fullscreen, open-in-browser, one-click publish to a public URL, and auto-sync of reviewer comments.

The site-preview block renders a multi-file design bundle — HTML, CSS, JS, images, fonts — as a live, JS-enabled preview served over the fabric-design:// Electron protocol. Each preview runs in its own sandboxed origin, isolated from the host app and from other previews.

This block is part of the design workflow. Coding sessions never see it; the html-preview block remains the JS-blocked default everywhere else.

At-a-glance UI

The header carries six controls. Hover the iframe area to reveal the pull-handle:

[ Globe ⊕ ] Mesh — Launch landing
                                [ ⊕ navigator ]  [ Comment ]  [ Publish / 🟢 Live ]  [ ↗ ]  [ ⛶ ]  [ + ]
ControlBehaviour
CommentToggle comment-mode. Cursor becomes a crosshair; hovered elements get an outline; click drops a pin and opens a note popover. Click again to exit.
PublishOne-click publish to https://agents.fabric.pro/d/<id>/. While in flight: spinner + "Publishing…". On success: button turns into a green 🟢 Live chip. URL auto-copied to clipboard.
🟢 Live(Replaces Publish after a successful publish) opens the live URL in your default browser.
↗ (External link)Opens the local bundle's index.html via file:// in your default external browser. Useful for cross-browser checks and DevTools work.
⛶ (Maximise)Fullscreen overlay (body portal). Esc returns to inline.
+ / −Stretches the inline iframe taller (≥ 720px) or returns to default. Hidden until hover.

Below the iframe — once you have at least one comment — a stacked comment panel appears with pins, sync controls, and per-pin actions.

Spec

Single bundle

```site-preview
{
  "src": "/abs/path/to/data/design-bundles/landing-v1",
  "entry": "index.html",
  "title": "Landing — Direction A"
}
```

entry defaults to index.html.

Published bundle (with comment auto-sync)

When the agent has published the design, it re-emits the block with publishedId + publishedUrl:

```site-preview
{
  "src": "/abs/path/to/.../landing-v1",
  "entry": "index.html",
  "title": "Landing — Published",
  "publishedId": "ab12cd34efgh5678",
  "publishedUrl": "https://agents.fabric.pro/d/ab12cd34efgh5678/"
}
```

The block detects the publish info and:

  • Shows the public URL chip in the comment panel header (click to copy).
  • Starts auto-polling /d/api/<id>/comments every 30 seconds (first poll 5s after mount).
  • Renders incoming reviewer comments as amber pins on the local preview.

Multiple directions (tabs)

```site-preview
{
  "title": "Three directions",
  "items": [
    { "src": "/abs/path/.../landing-warm",  "label": "Warm" },
    { "src": "/abs/path/.../landing-sharp", "label": "Sharp" },
    { "src": "/abs/path/.../landing-dense", "label": "Dense" }
  ]
}
```

Each item gets its own token + iframe origin — no cross-pollination of cookies, localStorage, etc.

Custom height

```site-preview
{
  "src": "/abs/path/...",
  "title": "Pitch deck",
  "height": 720
}
```

height defaults to 480px and is capped at 1200px. The + button stretches to ≥ 720px on demand.

Comment pins

Click Comment in the header. Then click any element in the preview:

┌─────────────────────────────────┐
│ on .hero h1 — "Welcome to..."   │
│ ┌─────────────────────────────┐ │
│ │ What would you change?      │ │
│ │                             │ │
│ │                             │ │
│ └─────────────────────────────┘ │
│            [ Cancel ] [ Save pin ]│
└─────────────────────────────────┘
  • ⌘/Ctrl+Enter saves; Esc cancels.
  • Saving creates a numbered pin at the click coordinates and adds the comment to the side panel.
  • A chat-ready quote (Design comment on `<h1>` (`.hero h1`) containing "Welcome": <YOUR NOTE>) is auto-copied to the clipboard so you can paste-extend in chat.
  • Empty notes still save (use to bookmark a spot).

Pin colours

ColourSource
PurpleLocal — you captured it in the desktop app
AmberPublic — pulled from /d/api/<id>/comments (a reviewer left it on the public URL)

Comment side-panel actions

Each row in the comment panel:

  • Click row highlights the pin on the preview.
  • → chat copies the formatted quote to clipboard so you can paste-and-extend.
  • × removes the comment locally (does not affect the public copy).

The panel header includes:

  • The published URL (if applicable) — click to copy.
  • Sync now — manual pull from /d/api/<id>/comments (auto-polls every 30s anyway).
  • "Ns ago" — last successful sync.
  • clear — wipe all local comments (does not affect the public copy).

How it works under the hood

  1. The agent writes a bundle directory under <dataPath>/design-bundles/<name>/.
  2. The renderer calls the design-preview:register IPC, which:
    • Validates the directory is inside the workspace root.
    • Issues a short URL-safe token.
    • Maps the token to the directory (TTL: 6 hours).
  3. The block renders an iframe pointing at fabric-design://design/<token>/<entry>.
  4. The protocol handler streams files from disk and injects two things into HTML responses:
    • A subtle scrollbar style (matches the host's 8px scrollbars).
    • A bridge script that handles Alt/⌘+click captures and accepts comment-mode + parameter-slider postMessages from the parent.

Sandbox

sandbox="allow-scripts allow-forms allow-popups allow-same-origin allow-pointer-lock"

allow-same-origin is safe because each bundle gets its own unique origin via the protocol — same-origin to other paths in the same bundle, but cross-origin to everything else. The strict CSP set by the protocol handler keeps third-party requests at bay.

Building bundles

Two practical patterns:

A. Compose from design-system templates

Use render_template with source: "fabric-design-system" (see Design Workflow) to produce HTML from the bundled templates, then transform_data or script_sandbox to write the result into the bundle directory:

1. Read tokens.json (the page shell injects them as CSS vars).
2. render_template source="fabric-design-system" template="page" data={ title, body, tokens }
   → returns absolute path to the rendered HTML.
3. transform_data writes the rendered HTML (and any assets) into design-bundles/<name>/index.html.
4. Emit a site-preview block pointing at the bundle directory.

B. Generate bundles directly with a script

For multi-page or JS-heavy designs, write a transform_data script that produces the whole bundle in one shot — index.html, style.css, main.js, plus any assets. Output goes under <dataPath>/design-bundles/<name>/ and the block points at the directory.

Iteration

When the user asks for changes, edit files in place and re-emit the same site-preview block. The iframe reloads from disk on the next render. Don't make a new bundle directory for every tweak — that's only worth it when the direction genuinely diverges and you want to keep both versions side-by-side via items.

See also

On this page