Fill the empty image slots of an existing Google Slides deck with AI-generated pictures composed for each frame's shape. Runs on any accessible deck URL, including decks slide-forge did not generate. Use for: 表紙に絵を入れて, 章扉の画像枠を埋めて, 空いている画像枠に画像を生成, fill the image placeholders. Not: decks still being generated (put aiImage in the spec instead); shape-drawn diagrams; replacing pictures already in place; verification (slide-qa).
Scanned 9/19/2026
Install to Claude Code
npx -y skills add wfukatsu/slide-forge --skill image-slots --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Image Slots?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wfukatsu-image-slots)More formats (shields.io, HTML) on the badges page.
---
name: image-slots
description: >-
Fill the empty image slots of an existing Google Slides deck with
AI-generated pictures composed for each frame's shape. Runs on any
accessible deck URL, including decks slide-forge did not generate.
Use for: 表紙に絵を入れて, 章扉の画像枠を埋めて, 空いている画像枠に画像を生成,
fill the image placeholders.
Not: decks still being generated (put aiImage in the spec instead);
shape-drawn diagrams; replacing pictures already in place; verification
(slide-qa).
---
*[日本語](SKILL.ja.md)*
# Fill Image Slots on an Existing Deck
Working directory: the slide-forge root — `${CLAUDE_PLUGIN_ROOT}` when running
from an installed plugin, `/path/to/slide-forge` on a local clone.
## Important
- **Prefer the spec path when the deck is still being generated.** If the deck
comes from a slide-forge deck spec, add an `aiImage` figure with `x`/`y`/`w`/`h`
omitted and regenerate — `build_deck.py` puts it in the slot. That keeps the
spec the source of truth, which is the repo's standing rule (see slide-qa:
*fixes happen in the source, not the artifact*). **This skill is for decks
that have no spec behind them**, or for adding imagery to a deck that already
exists and must keep its URL.
- **This writes to a live deck.** Take a version snapshot first and report the
revision ID:
`.venv/bin/python scripts/snapshot_version.py <URL>`
- **Always `--dry-run` first** and show the user which slides and frames will be
filled, with the prompt each one will use, before generating anything.
- **Slots that already hold a picture are never touched.** Replacing an existing
picture is out of scope — delete it by hand first if that is the intent.
- **`GEMINI_API_KEY` is required** and the image model has **zero free-tier
quota**; the key must belong to a billing-enabled project
(`references/images.md`).
- **Image generation can be switched off for the whole toolkit**
(`imageGeneration: false` in `config/settings.json`). Check it before
proposing this skill — `.venv/bin/python scripts/settings.py --show`. When it
is off, `fill_image_slots.py` stops before reading the deck; either the user
turns it on or the frames stay empty (`references/settings.md`).
- **Verify with thumbnails afterwards.** A clean API response cannot show a
picture whose subject got cropped. Hand off to the `slide-qa` skill.
## Quick Reference
| Task | Use |
|------|-----|
| List the fillable frames (no API image calls, no changes) | `.venv/bin/python scripts/fill_image_slots.py <URL> --dry-run` |
| Fill every empty frame | `.venv/bin/python scripts/fill_image_slots.py <URL>` |
| One slide, explicit subject | `… <URL> --slide 3 --prompt "a data center at night"` |
| Pick a frame when a slide has several | `… --slot 1` (0-based, in the order the survey lists them) |
| Also use frames only *inferred* from the deck's own usage | `--include-inferred` (off by default — see below) |
| Change the illustration style | `--style isometric` (`flat_vector` / `line_art` / `blueprint` / `paper` / `photo`) |
| Snapshot before writing | `.venv/bin/python scripts/snapshot_version.py <URL>` |
| What a slot is, and how images are composed for it | `references/images.md` |
## How a frame is found
Same three sources as template registration (`references/template-schema.md`):
1. a **PICTURE-family placeholder** on the slide (`PICTURE` / `CLIP_ART` /
`DIAGRAM` / `MEDIA` / `OBJECT` / `SLIDE_IMAGE`)
2. an **empty image element** on the slide — it renders as nothing, so it is a
slot, not a decoration
3. otherwise the **layout's** `imageSlots`, largest frame first
Sources 1 and 2 are **declarations** — the template says a picture belongs there.
The third kind of frame `imageSlots` can hold, one *inferred* from other slides
placing pictures in the same spot, is **excluded by default**: on a real deck
that inference matches the body area of ordinary content slides, and filling it
everywhere would bury the deck in pictures. Measured on a 78-slide template,
the default finds 0 fillable frames where the inference offers 39. Opt in per
run with `--include-inferred` when you actually want that.
Frames on the slide itself win over the layout's, because a slide that carries
its own placeholder is more specific than the layout it was made from. The
survey lists layout frames largest-first; frames found on the slide come in the
order the slide stores them, so read the `--dry-run` output rather than assuming
an order when picking `--slot`.
**A picture is layered over the frame, not poured into it.** An empty PICTURE
placeholder is not consumed — the image is created at the frame's coordinates
and the placeholder stays underneath. It renders as nothing, but it is still
there in the editor. The spec path (`build_deck.py`) behaves the same way.
Pass `--template templates/<id>.json` when the deck was generated from a
registered template — its verified roles and palette are then used. Without it
the deck is analyzed on the fly, so the skill works on **any** deck.
## What gets drawn
The prompt defaults to the slide's **title** — its TITLE placeholder, or the
topmost text if it has none — so a divider titled "Chapter 1: Rebuilding the
Data Platform" asks for a picture about that. Body text is deliberately
excluded: a slide's bullets read as "① Verify ② Distribute ③ Reflect" and make
a poor drawing instruction. Override per run with `--prompt`. A slide with
**no text at all** is skipped with a message rather than guessed at — pass
`--prompt` for those.
The picture is generated for the frame's shape: the closest aspect ratio the
model supports, plus a prompt instruction naming the edges and percentage the
fill will crop, so the subject survives. It is then placed with `fit="cover"`,
exactly filling the frame. Details in `references/images.md`.
Generation is cached by (model, style, aspect, full prompt), so re-running does
not redraw or re-bill; `--force` overrides.
## Flow
1. **Snapshot** — `snapshot_version.py <URL>`; keep the revision ID for the report.
2. **Survey** — `fill_image_slots.py <URL> --dry-run`. Show the user the slide
numbers, frames, and prompts. If everything is "already has a picture" or no
frames exist, say so and stop — do not invent frames.
3. **Confirm subjects.** The auto-derived prompts come from slide text and are
often literal. Offer to set `--prompt` per slide when the subject reads badly
as a picture (long body text, numbers, product names).
4. **Fill** — run without `--dry-run`. Each placement prints the chosen aspect
ratio and the composition note.
5. **QA** — invoke the `slide-qa` skill on the deck URL and check the pictures
are not cropped through their subject and do not fight the layout's text.
6. **Report** — slides filled, the style used, the snapshot revision ID, and the
deck URL.
## Troubleshooting
| Symptom | Cause / fix |
|---|---|
| `Nothing to fill` | Every frame already holds a picture, or the layouts reserve none. Check with `inspect_template.py <URL>`; place the image by coordinates via a deck spec instead |
| `skipped: no text on this slide to build a prompt from` | Title-less slide — pass `--prompt` |
| The picture's subject is cut off | The frame's ratio is far from anything the model can produce. Re-run that slide with `--prompt` describing a centred, simple subject, or `--force` for a different draw |
| `HTTP 429 / limit: 0` | The API key's project has no image quota — billing must be enabled |
| Picture looks stretched or letterboxed | Report it: frames are always filled with `cover`, so this means the fit-up pass failed (the run prints a warning when it cannot read the created image's size) |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!