CoolFace
Apppublic

tfrere/research-article-template-editor

sourceHugging Faceupdated 4mo agoView on Hugging Face
3likes
embed-studio.md215 linesDownload Raw Back to docs
1# Embed Studio - Architecture Document2 3## Overview4 5The Embed Studio is a dedicated UI mode within the editor for creating, editing, and previewing HTML embed visualizations (D3.js charts). It isolates the dataviz workflow from the article editing flow, providing a focused chat + preview experience similar to the standalone [dataviz-agent-space](https://huggingface.co/spaces/tfrere/dataviz-agent-space).6 7## Context8 9The research-article-template uses `<HtmlEmbed>` components to embed self-contained D3.js charts into articles. These are `.html` files in `app/src/content/embeds/` with strict conventions (scoped CSS, IIFE scripts, ColorPalettes, responsive, etc.) documented in `.ai/skills/create-html-embed/directives.md`.10 11In the editor, users need to create and iterate on these charts without leaving the editor. The Embed Studio solves this by providing a dedicated panel with an AI assistant specialized in D3 chart generation.12 13## Storage14 15### Y.Map("embeds")16 17Embed HTML content is stored in a collaborative Yjs Map, keyed by filename:18 19```20Y.Map("embeds") = {21  "d3-scaling-chart.html": "<div class='d3-scaling-chart'>...</div>",22  "d3-performance.html": "<div class='d3-performance'>...</div>"23}24```25 26The ProseMirror node (`htmlEmbed`) only stores the `src` attribute as a reference key. The actual HTML lives in the shared Y.Map, enabling real-time collaboration on embed content.27 28### Node attributes29 30The `htmlEmbed` TipTap node stores:31 32| Attribute | Type | Description |33|-----------|------|-------------|34| `src` | string | Filename key into Y.Map("embeds") |35| `title` | string | Chart title (displayed above) |36| `desc` | string | Chart description |37| `wide` | boolean | Wide layout mode |38| `downloadable` | boolean | Show download button |39| `height` | number | Last known content height (pixels) |40 41The `height` attribute eliminates layout jumps: once a chart reports its height, it is persisted and used as the iframe's initial height on subsequent loads.42 43## UI: Two Modes44 45### 1. Inline Preview (article view)46 47When the user is editing the article, the `htmlEmbed` NodeView shows a read-only preview:48 49```50┌─────────────────────────────────────────┐51│ 📊 Chart Title              [Edit] [⋮] │52├─────────────────────────────────────────┤53│                                         │54│         <iframe preview>                │55│                                         │56└─────────────────────────────────────────┘57```58 59- The iframe renders the chart using `srcdoc` with the full HTML document (buildDoc wrapper)60- Height comes from the stored `height` attribute (default: 400px)61- Clicking "Edit" opens the Embed Studio panel62- No code editing in this mode63 64### 2. Embed Studio Panel (creation/editing)65 66A full-width panel (drawer or overlay) opens with a split layout:67 68```69┌────────────────────────────┬─────────────────────────────┐70│  Chat (D3 context)         │  Live Preview               │71│                            │                             │72│  System prompt includes:   │  ┌─────────────────────┐    │73│  - D3 embed directives     │  │                     │    │74│  - ColorPalettes API       │  │   [rendered chart]   │    │75│  - Current chart HTML      │  │                     │    │76│                            │  └─────────────────────┘    │77│  User: "make a bar chart   │                             │78│   showing model sizes"     │  Toggle: [Preview] [Code]   │79│                            │                             │80│  AI: Creating chart...     │                             │81│                            │       [Save & Close]        │82└────────────────────────────┴─────────────────────────────┘83```84 85**Key design decisions:**86 87- The chat in this panel has a **separate system prompt** with D3 directives injected. This avoids bloating the main article chat with 500+ lines of D3 conventions.88- The chat history is **per-embed** (scoped to the `src` key), so each chart has its own conversation thread.89- The live preview uses **double-buffered iframes** (A/B swap with cross-fade) from the dataviz-agent pattern to avoid flashes on update.90- An optional "Code" toggle shows the raw HTML for power users (future enhancement).91 92## AI Tools93 94The Embed Studio provides three tools to the AI (following the dataviz-agent pattern):95 96### createEmbed(src, html, title, source)97 98Create or fully replace the HTML for an embed. Writes to `Y.Map("embeds")`.99 100### patchEmbed(src, search, replace)101 102Exact string replacement in the current HTML. More efficient than full rewrite for small changes (color tweaks, label updates, data changes). Reads from and writes to `Y.Map("embeds")`.103 104### readEmbed(src)105 106Read the current HTML content. The AI should call this before patching to verify exact content.107 108## Preview Infrastructure109 110### buildDoc(html, isDark, primaryColor)111 112Wraps a chart HTML fragment into a complete HTML document with:113 114- CSS variables for theming (`--primary-color`, `--text-color`, `--surface-bg`, etc.)115- `data-theme="dark"` attribute when in dark mode116- ColorPalettes polyfill (provides `window.ColorPalettes.getColors()`, `.getPrimary()`, etc.)117- Height reporting script (see below)118- Base styles (box-sizing, font stack, padding)119 120### Height reporting121 122A script injected by `buildDoc()` observes the chart container and reports its height to the parent:123 124```js125// Injected into every chart iframe126new ResizeObserver(entries => {127  const height = Math.ceil(entries[0].contentRect.height);128  window.parent.postMessage({ type: 'embedResize', height }, '*');129}).observe(document.body);130```131 132The NodeView listens for this message and updates the node's `height` attribute:133 134```js135window.addEventListener('message', (e) => {136  if (e.data?.type === 'embedResize') {137    updateAttributes({ height: e.data.height });138  }139});140```141 142On subsequent renders, the iframe starts at the stored height, eliminating layout jumps.143 144### Preview strategy: srcdoc first145 146Initial implementation uses `<iframe srcdoc="...">` directly. This avoids backend changes and keeps the architecture simple.147 148If we hit limitations (CSP restrictions, large HTML payloads, script execution issues), we migrate to a server-side preview route (`POST /api/preview` returning an ID, iframe loads `/api/preview/:id`), following the dataviz-agent pattern.149 150## Export151 152### Updated export API153 154The `toMdx()` function returns an object instead of a plain string:155 156```typescript157interface ExportResult {158  mdx: string;                        // The MDX content with frontmatter159  embeds: Record<string, string>;     // filename -> HTML content160}161```162 163The caller is responsible for writing the embed files to `app/src/content/embeds/`.164 165### MDX output166 167Each embed in the article exports as:168 169```mdx170<HtmlEmbed src="d3-scaling-chart.html" title="Chart Title" desc="Description" />171```172 173The HTML files are exported separately from the Y.Map("embeds") contents.174 175## System Prompt for D3 Generation176 177The Embed Studio injects the D3 directives into the AI's system prompt. The content comes from two sources:178 1791. **Tool descriptions** (from dataviz-agent): create/patch/read tools with usage guidelines1802. **Embed conventions** (from research-article-template): structure, ColorPalettes, CSS variables, mount guard, D3 CDN loading, legends, controls, tooltips, responsiveness, error handling, accessibility, checklist181 182These are only injected when the Embed Studio is open, keeping the main article chat lightweight.183 184## Implementation Plan185 186### Phase 1: Core infrastructure187 1881. Create `Y.Map("embeds")` in `Editor.tsx` and pass to the embed store1892. Create `EmbedStore` (similar to FrontmatterStore) with get/set/observe/patch operations1903. Replace the current atomic `htmlEmbed` NodeView with an iframe-based preview1914. Implement `buildDoc()` with CSS variables and ColorPalettes polyfill1925. Implement height reporting via postMessage193 194### Phase 2: Embed Studio panel195 1966. Create `EmbedStudio.tsx` - the split-panel UI (chat + preview)1977. Create a dedicated chat hook (`useEmbedChat`) with D3 system prompt1988. Implement `createEmbed`, `patchEmbed`, `readEmbed` AI tools (backend + frontend)1999. Double-buffered iframe preview (A/B swap)20010. Wire "Edit" button on inline NodeView to open the studio201 202### Phase 3: Polish203 20411. Per-embed chat history persistence20512. Code view toggle20613. Export API update (`toMdx` returns `{ mdx, embeds }`)20714. Data file upload support (CSV/JSON stored in Y.Map)20815. Screenshot-based validation (Playwright, optional)209 210## References211 212- [dataviz-agent-space](../../../dataviz-agent-space/) - Standalone D3 chart generation agent213- [research-article-template embeds skill](../../../research-article-template/.ai/skills/create-html-embed/) - Embed authoring conventions214- [ChartFrame.jsx](../../../dataviz-agent-space/frontend/src/components/ChartFrame.jsx) - Double-buffered iframe + buildDoc pattern215