| name | slide-authoring |
| description | You MUST use this before any work with slides, including creating or modifying slidedecks, slide bundles, slide design, or generated slide assets. |
| compatibility | Requires Bun for slide tooling. Generated assets require network access and OPENAI_API_KEY or GOOGLE_AI_API_KEY in .env for image generation. |
Slide Authoring
Use this skill for slide content, copy, styling, bundle files, visual design, generated assets, PNG/PDF export quality, or Text Tool compatibility.
Do not use it for application changes under src/ or app/ unless debugging slide preview or export behavior.
Source Of Truth
- Versioned decks live under
decks/<slidedeckId>/.
- Deck manifests live at
decks/<slidedeckId>/deck.json and store title, themeId, slide ids, slide titles, and slide order; the manifest array order is the slide order.
- Versioned slide bundle source lives at
decks/<slidedeckId>/slides/<slideId>/.
- Local SQLite is not the source of truth for versioned slide content.
- Slide numbers are display/order labels only; resolve them through
deck.json, app state, or route payloads before editing a bundle.
Required Design Guide
Before any slide authoring, slide editing, slide design, or generated slide asset work:
- Read
decks/<slidedeckId>/deck.json and resolve the deck's themeId.
- If
themeId is missing, repair the manifest explicitly before continuing.
- Read every Markdown file directly under
themes/<themeId>/.
- Follow that theme guidance for palette, typography, layout, art direction, permanent elements, and generated asset style.
Do not assume a logo, footer, page number, background, title placement, mascot, palette, typography, or locked template element unless the selected theme says so.
Creating Decks And Slides
Use the running app's API to create local decks and starter slide bundles. Do not manually create deck directories, manifests, or starter files unless the app route is unavailable or you are repairing malformed files.
- App URL:
http://localhost:3000
- Check server:
lsof -ti tcp:3000
- Start if needed:
bun run dev
- Create deck:
POST /api/slidedecks with { "themeId": "<themeId>" } when a specific theme is requested.
- Append slide:
POST /api/slidedecks/<slidedeckId>/slides with no body.
- Starter bundles are created by
themes/<themeId>/maker.ts.
- After creation, edit files on disk for deck title, slide titles, copy, and design.
curl -s -X POST http://localhost:3000/api/slidedecks \
-H 'content-type: application/json' \
-d '{"themeId":"default"}'
curl -s -X POST http://localhost:3000/api/slidedecks/<slidedeckId>/slides
Manifest shape:
{
"schemaVersion": 1,
"id": "<slidedeckId>",
"title": "Deck Title",
"themeId": "default",
"slides": [
{ "id": "<slideId>", "title": "Slide Title" }
]
}
Editing Slide Content
When the user asks to change, update, redesign, or fix a slide, edit the HTML bundle on disk, not prompts, SQLite, or the API.
For slide <slideId> in deck <slidedeckId>:
- Follow the Required Design Guide.
- Edit
decks/<slidedeckId>/slides/<slideId>/slide.tsx.
- Edit
decks/<slidedeckId>/slides/<slideId>/styles.css.
- Preserve permanent template elements only when the theme or user says to keep them.
- Verify with a PNG export.
Bundle Contract
- A slide has one UUID, and that UUID is the HTML slide bundle id.
- A renderable bundle contains
slide.tsx and styles.css.
- Bundle previews are served through
/slidedecks/[slidedeckId]/slides/[slideId]/preview.
- Slide-local image assets may be imported from
slide.tsx; the renderer inlines .png, .jpg, .jpeg, .webp, and .svg as data URLs.
- Missing or incomplete bundles should produce useful UI states, not broken pages.
Layout Tool Overrides
styles.css may contain managed blocks between /* slide-layout-overrides:start */ and /* slide-layout-overrides:end */.
- These blocks store user-driven layout edits from the workspace Edit tool against
data-slide-layout-id elements.
- Do not delete them just because they look generated.
- You may flatten them into
slide.tsx and/or styles.css when it improves the source, but preserve the rendered result exactly.
- After flattening, remove the corresponding managed rules and layout-only attributes that are no longer needed.
Text Tool Compatibility
- The Text Tool patches safe text directly in
slide.tsx.
- Put each user-editable label in one simple source text node.
- Prefer direct JSX text or string literals:
<h1>Title</h1>, <p>Body text</p>, <span>{'Label'}</span>.
- Keep independently editable labels in separate leaf elements.
- Avoid mixed rich-content containers like
<p>Hello <strong>world</strong></p> when the whole phrase should be editable.
- Do not put editable copy behind shared variables, props, imported components, external data files,
.map() loops, conditionals, concatenation, dynamic templates, or runtime formatting.
- Do not hide meaningful visible text with
aria-hidden, hidden, display: none, or visibility: hidden.
- Keep theme-controlled locked/permanent elements non-editable.
- If a text target is not editable, inspect rendered
data-slide-text-editable="true" and the source shape, hidden ancestors, nested markup, and generated/shared text.
Visual Slide Iteration
- Iterate from rendered output, not CSS guesses.
- Before using local API routes, check port
3000 with lsof -ti tcp:3000.
- If no process is listening, start the app with
bun run dev.
- Do not start a second dev server if port
3000 is occupied.
- Export PNGs through
http://localhost:3000/api/slidedecks/<slidedeckId>/slides/<slideId>/png.
- Write exported PNGs to a temp folder of your choice.
- Read the PNG with an image-capable file reader, identify concrete layout problems, edit, and export again.
- Use
http://localhost:3000/slidedecks/<slidedeckId>/slides/<slideId>/preview for interactive inspection.
PDF Export Compatibility
The PDF exporter renders each slide inside an iframe via Chromium page.pdf(). In that path, box-shadow with non-zero blur rasterizes as an opaque rectangular block instead of a soft shadow — PNG looks correct, PDF shows a solid color box. filter: drop-shadow() does not have this bug.
- Do not use
box-shadow with a non-zero blur radius for soft shadows. Use filter: drop-shadow(<x> <y> <blur> <color>) instead.
box-shadow is safe when blur is 0: hard offset "stamp" shadows (0.5cqw 0.6cqw 0 ...), spread-only rings (0 0 0 0.24cqw ...), and inset borders all render correctly in PDF.
filter: drop-shadow does not support a spread radius and chains via space-separation (filter: drop-shadow(...) drop-shadow(...)). It establishes a stacking context and casts a shadow on all descendant alpha, including text — for subtle shadows this is imperceptible.
- When verifying PDF output: render via
/api/slidedecks/<deckId>/slides/<slideId>/pdf, then rasterize with sips -s format png <file>.pdf --out <file>.png to inspect.
Generated Visual Assets
Generate slide-ready assets when visuals are valuable and not practical to build as editable HTML/CSS/SVG. Prefer multiple small supporting assets over one full-slide generated image unless the user asks for a single scene. Default to Gemini unless the user asks for OpenAI.
Use generation for art-directed metaphors, scenes, objects, illustrated diagrams, system maps, process machines, journey paths or split-world scenes, product-scene artwork, UI-overlay illustrations, infographic artifacts, character-driven tech artwork, and slide illustration assets.
Do not generate normal layouts, simple icons, plain SVG shapes, CSS backgrounds, or anything more precise as frontend code.
Asset Workflow
- Define the asset brief, output path, target slide, type, mood, and whether it is a cutout, fragment, accent object, diagram piece, or asset sheet.
- If no output path is specified, write to the target slide bundle when known; otherwise use
.tmp-generated-assets/.
- Use style references only when the user provides one or the selected theme has
themes/<themeId>/assets/reference.png|jpg|jpeg|webp; pass it with --reference.
- Write the full prompt yourself. The script only appends the standard pure-white-background requirement.
- Generate with Gemini by default, inspect the transparent image, revise the prompt if needed, then import it from
slide.tsx and size it in styles.css.
bun .agents/skills/slide-authoring/scripts/generate-asset.ts \
--prompt-file /tmp/slide-asset-prompt.txt \
--reference themes/<themeId>/assets/reference.jpg \
--output decks/<deckId>/slides/<slideId>/context-machine.png
Omit --reference when no selected reference exists. Use --provider openai only when requested or Gemini is unavailable.
Asset Prompting
Prompt for a compact supporting asset that matches the theme guidance and any selected reference. It should have a clear silhouette, dominant visual idea, and named metaphor such as a physical scene, object, journey, machine, map, ecosystem, spotlight, or before/after moment. Make meaning through props, placement, state, scale, connector lines, iconography, posture, and visual contrast rather than pasted-on labels.
Avoid text unless explicitly requested; if needed, make it sparse and presentation-legible. Avoid logos, brand marks, trademarked mascots, full-slide compositions, photorealism, 3D rendering, cyberpunk, anime, meme style, generic robots, clipart, stock icons, stat-card layouts, chip grids, generic dashboards, title-with-cards compositions, decorative gradients without a story, tiny UI, and busy backgrounds.
Matching Asset Sheets
When several matching assets are needed, prefer one contact-sheet generation and slice it into files.
Create one clean contact sheet containing 6 separate matching slide assets in a 3 column by 2 row grid.
Each cell contains exactly one centered asset, fully visible, separated by generous gutters, with no overlap between cells.
All assets share the same palette, stroke language, shadow treatment, perspective, visual weight, and level of detail.
Use a plain uniform pure white (#ffffff) background across the entire sheet. Do not create one continuous scene across cells.
Do not add captions, numbers, labels, watermarks, or decorative frames. The cells will be cropped apart later.
Keep every asset comfortably inside its cell with whitespace around it for trimming.
bun .agents/skills/slide-authoring/scripts/generate-asset.ts \
--prompt-file /tmp/slide-asset-sheet-prompt.txt \
--reference themes/<themeId>/assets/reference.jpg \
--output .tmp-generated-assets/context-assets-sheet.png \
--size 2048x2048 \
--quality high
bun .agents/skills/slide-authoring/scripts/slice-sheet.ts \
--input .tmp-generated-assets/context-assets-sheet.png \
--output-dir .tmp-generated-assets/context-assets \
--names "confident-character,worried-character,spec-stack,context-gauge,warning-pin,status-badge" \
--columns 3 \
--rows 2 \
--trim \
--transparent-color white
Inspect every slice. If the grid drifted, overlapped, or varied in style, regenerate the sheet instead of fixing cells independently.
Generator Reference
bun .agents/skills/slide-authoring/scripts/generate-asset.ts \
--prompt-file /tmp/slide-asset-prompt.txt \
--output .tmp-generated-assets/context-split-world.png \
--provider gemini
--prompt or --prompt-file: full image prompt. The script appends the pure-white-background requirement.
--output: required output image path.
--provider: gemini default, or openai.
--reference: optional .png, .jpg, .jpeg, or .webp style reference.
--size: OpenAI size; defaults to 1024x1024.
--quality: OpenAI quality; defaults to high.
The script reads .env automatically, uses GOOGLE_AI_API_KEY / GOOGLE_SLIDE_IMAGE_MODEL for Gemini (gemini-3.1-flash-image-preview default), and OPENAI_API_KEY / OPENAI_IMAGE_MODEL for OpenAI fallback (gpt-image-2 default). After generation, it removes background-connected white with ImageMagick while preserving isolated white details as much as possible.