Slide Editing
Slides are HTML content stored inside the deck JSON. Each slide's content
field is a self-contained HTML string rendered at the intrinsic dimensions for
its aspect ratio: 16:9 is 960x540, 1:1 is 1080x1080, 9:16 is 540x960, and 4:5
is 864x1080. These canonical dimensions come from the shared aspect-ratio
registry; do not assume a fixed 1920x1080 canvas.
Slide HTML Structure
Every slide uses this wrapper:
<div class="fmd-slide" style="padding: 80px 110px; display: flex; flex-direction: column; justify-content: flex-start;">
<!-- Slide content here -->
</div>
Styling Rules
These are fallback defaults only. When a design system is linked, its hydrated
tokens control color, typography, spacing, borders, imagery, and slide defaults;
a reference deck controls composition and markup idiom only. The generic
Impeccable-inspired quality bar can flag hierarchy, contrast, density, and
anti-pattern issues, but it cannot replace the active system.
When no system is linked, generated slides may use these conventions:
| Element |
Style |
| Background |
bg-[#000000] (pure black) |
| Font |
font-family: 'Poppins', sans-serif on all text |
| Section labels |
font-size: 16px; font-weight: 700; letter-spacing: 3px; text-transform: uppercase; color: #00E5FF |
| Headings |
font-size: 40px; font-weight: 900; color: #fff; line-height: 1.15; letter-spacing: -1px |
| Title slides |
font-size: 54px; font-weight: 900 with justify-content: center |
| Bullet points |
● character (8px, white), gap: 20px, font-size: 22px, color: rgba(255,255,255,0.85) |
| Sub-bullets |
○ (open circle), padding-left: 36px |
| Bold terms |
<strong style="font-weight: 800; color: #fff;">Term</strong> + description in rgba(255,255,255,0.55) |
| Accent color |
#00E5FF (cyan) for section labels, emphasis, highlights |
Fit and Density
Fit the main content to the native content area, not merely to the outer
wrapper. For the default 16:9 canvas, the standard 80px 110px padding leaves
740x380px. Keep titles to two lines, content slides to three short bullets or
three compact cards, and two-column slides to two or three short items per
column. If the source is denser, split it across slides. Never use zoom,
transform: scale(), clipping, or scroll overflow to hide a fit issue; body
text must remain at least 16px. Explicitly reduced slide padding is allowed when
the content still needs the space.
Updating a Slide
To edit a slide's content:
Inspect the current context: call view-screen to get the active deck,
slide ID, HTML, and any slides-selection style/edit target.
For a focused replacement or translation of currently selected text, if the
result includes a matching exact selectedText range and currentSlideId, skip
get-deck and go directly to the bounded update-slide edit below.
For a targeted persisted read, pass that stable slideId to get-deck so
only the target slide is returned; use compact=false when you need its
full HTML.
Retrieve before generating: when the edit changes facts, brand language,
or layout, follow the creative-context skill and query those roles
separately. Respect opt-out, pinned packs, and the exact reuse ladder.
Modify the content HTML string for the intended slide. Preserve an
approved native template or component when it already fits; generate
net-new structure only when the relevant corpus is empty.
Update the slide with update-slide using deckId, slideId, and
ordered edits. When view-screen returns an exact selectedText range,
edit immediately: send one literal replace with the selected text as
find, expectedMatches: 1, and currentSlideContentHash as
baseContentHash; do not load the full deck, use fullContent, or wait
for layout-fit. If the text is truncated, ambiguous, contains markup that
prevents a literal match, or the edit is structural, use targeted
get-deck first, use its contentHash as baseContentHash, then read
back. For code-style work, request compact=false and format=true. Use
exact replace, insert before/after, replace between markers, or regex
replace. All edits are
applied in memory under the deck lock; if one required edit fails, nothing is
written. Set format=true on update-slide to persist readable Prettier
line breaks. Use fullContent only for an intentional full rewrite - do not
regenerate a slide to make a small change. Do not write deck rows directly
or add raw full-deck writes; use patch-deck for browser/editor changes.
Read a write back with get-deck (or a thumbnail's .slide-content
textContent), never document.body.innerText: sidebar thumbnails use
content-visibility: auto, so innerText is empty for them in a hidden
tab, and the canvas only ever shows the selected slide.
For browser/editor code, enqueue granular deck operations through
patch-deck / DeckContext.tsx instead of replacing the whole deck JSON.
For factual edits, compare changed text against the retrieved source and
preserve quote, speaker, date, metric, and uncertainty status. Existing HTML
or visual similarity is not proof of source fidelity.
Skipping a Slide
Set a slide's skipped: true via a patch-deck patch-slide operation to
exclude it from Present/Presenter playback without deleting it — the slide
stays in the deck, editor, and exports. Set skipped: false (or omit it) to
include it again. The rail's right-click menu on each slide thumbnail offers
Cut, Copy, Paste, Delete, New slide, Duplicate slide, and Skip slide as the
same operations.
Click-to-reveal animations
Animations are metadata over the final slide HTML, not alternate slide markup.
Read the full target slide, keep its existing visual structure, and patch the
complete ordered animations list with elementPath values from that exact
HTML. Elements omitted from the list remain visible immediately, so labels and
headings need no duplicate markup. Do not add hidden duplicates, layout
spacers, absolute-positioned copies, transforms, or placeholder content to
simulate reveals. When content and reveals change together, send both fields in
one patch-deck operation. To remove reveals, send animations: [] with the
existing content and verify the persisted slide afterward.
Array order is reveal order, and each entry needs a non-empty id, a 0-based
elementIndex, and a type of appear, fade, slide-up, or zoom; the
schema rejects the operation otherwise. Nothing checks that ids are unique, but
the editor keys its reveal list by id, so a duplicate makes "remove" and
"change type" hit every entry sharing it.
elementPath has to come from the exact final HTML because it is positional:
every segment is a child index, so inserting or removing a sibling anywhere
along the path retargets it. The runtime resolves the path first and falls back
to elementIndex only when it fails to resolve, which is why a stale path
silently reveals the wrong element instead of erroring. get-deck with
compact=true reports each step's order, id, target, and type for verification.
If retrieval produces a new immutable context pack, keep its contextPackId
and reuse labels with the deck provenance. Existing slide HTML is not proof of
which source version influenced it.
Freeform Canvas Objects
Manual text boxes and other freeform canvas objects are absolutely positioned
children of .fmd-slide. Give each one a stable data-slide-object-id:
<div
class="fmd-text-box"
data-slide-object-id="slide-object-unique-id"
style="position: absolute; left: 160px; top: 120px; width: 420px;"
>
Editable text
</div>
- Preserve
data-slide-object-id when updating, moving, resizing, or styling an
existing object.
- Mint a new unique object ID when duplicating an object.
- Do not use runtime-only
data-builder-id values in saved slide HTML.
- Keep generated flex and grid content in normal flow. Do not silently
absolute-position a nested layout child just to make it draggable; create a
deliberate freeform object instead.
- Build editable shapes with styled HTML elements such as
div. Do not use
inline SVG, which the slide sanitizer removes.
Image Placeholders
For visual elements (diagrams, charts, photos), use placeholder divs:
<div class="fmd-img-placeholder" style="width: 100%; height: 300px; border-radius: 12px;">
Description of the image
</div>
Never try to recreate complex visuals with raw HTML/CSS. Use placeholders and generate proper images via the image generation flow.
Slide Layouts
Common layout patterns:
- Title slide: Single centered heading,
justify-content: center
- Section divider: Large single word, centered
- Content: Section label + heading + bullet list
- Two-column: Flex row with
gap: 40px, text left, image right
- Table: CSS grid with alternating row backgrounds
1---2name: slide-editing3description: Edit individual slides, including content formatting, HTML styling, and bounded source and visual-fidelity checks. Use when changing an existing slide rather than creating a new deck.4---56# Slide Editing78Slides are HTML content stored inside the deck JSON. Each slide's `content`9field is a self-contained HTML string rendered at the intrinsic dimensions for10its aspect ratio: 16:9 is 960x540, 1:1 is 1080x1080, 9:16 is 540x960, and 4:511is 864x1080. These canonical dimensions come from the shared aspect-ratio12registry; do not assume a fixed 1920x1080 canvas.1314## Slide HTML Structure1516Every slide uses this wrapper:1718```html19<div class="fmd-slide" style="padding: 80px 110px; display: flex; flex-direction: column; justify-content: flex-start;">20 <!-- Slide content here -->21</div>22```2324## Styling Rules2526These are fallback defaults only. When a design system is linked, its hydrated27tokens control color, typography, spacing, borders, imagery, and slide defaults;28a reference deck controls composition and markup idiom only. The generic29Impeccable-inspired quality bar can flag hierarchy, contrast, density, and30anti-pattern issues, but it cannot replace the active system.3132When no system is linked, generated slides may use these conventions:3334| Element | Style |35|---------|-------|36| Background | `bg-[#000000]` (pure black) |37| Font | `font-family: 'Poppins', sans-serif` on all text |38| Section labels | `font-size: 16px; font-weight: 700; letter-spacing: 3px; text-transform: uppercase; color: #00E5FF` |39| Headings | `font-size: 40px; font-weight: 900; color: #fff; line-height: 1.15; letter-spacing: -1px` |40| Title slides | `font-size: 54px; font-weight: 900` with `justify-content: center` |41| Bullet points | `●` character (8px, white), gap: 20px, font-size: 22px, color: rgba(255,255,255,0.85) |42| Sub-bullets | `○` (open circle), padding-left: 36px |43| Bold terms | `<strong style="font-weight: 800; color: #fff;">Term</strong>` + description in rgba(255,255,255,0.55) |44| Accent color | `#00E5FF` (cyan) for section labels, emphasis, highlights |4546## Fit and Density4748Fit the main content to the native content area, not merely to the outer49wrapper. For the default 16:9 canvas, the standard `80px 110px` padding leaves50740x380px. Keep titles to two lines, content slides to three short bullets or51three compact cards, and two-column slides to two or three short items per52column. If the source is denser, split it across slides. Never use zoom,53`transform: scale()`, clipping, or scroll overflow to hide a fit issue; body54text must remain at least 16px. Explicitly reduced slide padding is allowed when55the content still needs the space.5657## Updating a Slide5859To edit a slide's content:60611. **Inspect the current context**: call `view-screen` to get the active deck,62 slide ID, HTML, and any `slides-selection` style/edit target.63 For a focused replacement or translation of currently selected text, if the64 result includes a matching exact `selectedText` range and `currentSlideId`, skip65 `get-deck` and go directly to the bounded `update-slide` edit below.66 For a targeted persisted read, pass that stable `slideId` to `get-deck` so67 only the target slide is returned; use `compact=false` when you need its68 full HTML.692. **Retrieve before generating**: when the edit changes facts, brand language,70 or layout, follow the `creative-context` skill and query those roles71 separately. Respect opt-out, pinned packs, and the exact reuse ladder.723. **Modify the content** HTML string for the intended slide. Preserve an73 approved native template or component when it already fits; generate74 net-new structure only when the relevant corpus is empty.754. **Update the slide** with `update-slide` using `deckId`, `slideId`, and76 ordered `edits`. When `view-screen` returns an exact `selectedText` range,77 edit immediately: send one literal replace with the selected text as78 `find`, `expectedMatches: 1`, and `currentSlideContentHash` as79 `baseContentHash`; do not load the full deck, use `fullContent`, or wait80 for layout-fit. If the text is truncated, ambiguous, contains markup that81 prevents a literal match, or the edit is structural, use targeted82 `get-deck` first, use its `contentHash` as `baseContentHash`, then read83 back. For code-style work, request `compact=false` and `format=true`. Use84 exact replace, insert before/after, replace between markers, or regex85 replace. All edits are86 applied in memory under the deck lock; if one required edit fails, nothing is87 written. Set `format=true` on `update-slide` to persist readable Prettier88 line breaks. Use `fullContent` only for an intentional full rewrite - do not89 regenerate a slide to make a small change. Do not write deck rows directly90 or add raw full-deck writes; use `patch-deck` for browser/editor changes.91 Read a write back with `get-deck` (or a thumbnail's `.slide-content`92 `textContent`), never `document.body.innerText`: sidebar thumbnails use93 `content-visibility: auto`, so `innerText` is empty for them in a hidden94 tab, and the canvas only ever shows the selected slide.955. For browser/editor code, enqueue granular deck operations through96 `patch-deck` / `DeckContext.tsx` instead of replacing the whole deck JSON.97986. For factual edits, compare changed text against the retrieved source and99 preserve quote, speaker, date, metric, and uncertainty status. Existing HTML100 or visual similarity is not proof of source fidelity.101102## Skipping a Slide103104Set a slide's `skipped: true` via a `patch-deck` `patch-slide` operation to105exclude it from Present/Presenter playback without deleting it — the slide106stays in the deck, editor, and exports. Set `skipped: false` (or omit it) to107include it again. The rail's right-click menu on each slide thumbnail offers108Cut, Copy, Paste, Delete, New slide, Duplicate slide, and Skip slide as the109same operations.110111## Click-to-reveal animations112113Animations are metadata over the final slide HTML, not alternate slide markup.114Read the full target slide, keep its existing visual structure, and patch the115complete ordered `animations` list with `elementPath` values from that exact116HTML. Elements omitted from the list remain visible immediately, so labels and117headings need no duplicate markup. Do not add hidden duplicates, layout118spacers, absolute-positioned copies, transforms, or placeholder content to119simulate reveals. When content and reveals change together, send both fields in120one `patch-deck` operation. To remove reveals, send `animations: []` with the121existing content and verify the persisted slide afterward.122123Array order is reveal order, and each entry needs a non-empty `id`, a 0-based124`elementIndex`, and a `type` of `appear`, `fade`, `slide-up`, or `zoom`; the125schema rejects the operation otherwise. Nothing checks that ids are unique, but126the editor keys its reveal list by id, so a duplicate makes "remove" and127"change type" hit every entry sharing it.128129`elementPath` has to come from the exact final HTML because it is positional:130every segment is a child index, so inserting or removing a sibling anywhere131along the path retargets it. The runtime resolves the path first and falls back132to `elementIndex` only when it fails to resolve, which is why a stale path133silently reveals the wrong element instead of erroring. `get-deck` with134`compact=true` reports each step's order, id, target, and type for verification.135136If retrieval produces a new immutable context pack, keep its `contextPackId`137and reuse labels with the deck provenance. Existing slide HTML is not proof of138which source version influenced it.139140## Freeform Canvas Objects141142Manual text boxes and other freeform canvas objects are absolutely positioned143children of `.fmd-slide`. Give each one a stable `data-slide-object-id`:144145```html146<div147 class="fmd-text-box"148 data-slide-object-id="slide-object-unique-id"149 style="position: absolute; left: 160px; top: 120px; width: 420px;"150>151 Editable text152</div>153```154155- Preserve `data-slide-object-id` when updating, moving, resizing, or styling an156 existing object.157- Mint a new unique object ID when duplicating an object.158- Do not use runtime-only `data-builder-id` values in saved slide HTML.159- Keep generated flex and grid content in normal flow. Do not silently160 absolute-position a nested layout child just to make it draggable; create a161 deliberate freeform object instead.162- Build editable shapes with styled HTML elements such as `div`. Do not use163 inline SVG, which the slide sanitizer removes.164165## Image Placeholders166167For visual elements (diagrams, charts, photos), use placeholder divs:168169```html170<div class="fmd-img-placeholder" style="width: 100%; height: 300px; border-radius: 12px;">171 Description of the image172</div>173```174175Never try to recreate complex visuals with raw HTML/CSS. Use placeholders and generate proper images via the image generation flow.176177## Slide Layouts178179Common layout patterns:180181- **Title slide**: Single centered heading, `justify-content: center`182- **Section divider**: Large single word, centered183- **Content**: Section label + heading + bullet list184- **Two-column**: Flex row with `gap: 40px`, text left, image right185- **Table**: CSS grid with alternating row backgrounds