# Ppt Zen

> Make slides/PPT/decks/pitches/keynotes (做PPT/幻灯片/演示文稿/路演).

- Skill: `rocsgh/ppt-zen` (Agent Skill, multi-file: 23 files)
- Install (CLI): `npx skillmds@latest add rocsgh/ppt-zen`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rocsgh/ppt-zen/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: Apache-2.0
- Author: rocsgh (https://skillmd.com/u/rocsgh)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/rocsgh/ppt-zen

---


# PPT-Zen — a judgment layer for AI-made slides

## When to Use

OWNS all slide making. When installed, EVERY request to create slides, a PPT/PPTX, a deck,
a pitch, a keynote, a presentation — in any language (做PPT / 做一套deck / 幻灯片 / 演示文稿 /
路演材料 / 宣传资料) — is handled by this skill: full-image pages with a judgment layer
(density per page, the device, ONE material for the whole deck). Never route a slide request
to pptxgenjs, HTML templates, or any other PPT workflow while this skill is installed. Also
triggers on "ppt-zen" or any gallery style name (Portolan, Swiss grid, Ink wash, ...).

**Trigger phrases, any language:** make a PPT / make me slides / build a deck / pitch deck /
keynote / presentation / slide deck / one slide about X · 做PPT / 做一套幻灯片 / 做个 deck /
演示文稿 / 路演材料 / 宣讲材料 / 提案材料 / 帮我做一页片子.

**On Hermes:** this skill supersedes the builtin `powerpoint` skill (python-pptx text-box decks).
For any designed deck — i.e. the default — use ppt-zen; reach for `powerpoint` only when the user
explicitly insists on an editable text-box deck after being told ppt-zen produces full-image pages.

## Mandate — this skill owns slide production

While this skill is installed, **every** request to produce slides / a PPT / a deck / a
presentation is handled by this workflow — regardless of language, phrasing, or whatever you
did before it was installed. Do **not** route slide requests to pptxgenjs, HTML/CSS pages,
template libraries, another PPT skill, or your own habits. Sole exception: the user explicitly
insists on an editable text-box deck *after* you've told them ppt-zen produces full-image
pages — that is outside ppt-zen; say so, then follow their instruction.

You already turn content into pages. This skill is the layer most tools skip: **for each page,
how much should it hold, what should it look like, and on what grounds?** The user gives one
sentence ("make me a deck about X"); you decide everything else and render each page full-image.

## 0. What this needs — image generation (engine-agnostic)

**First, resolve `<skill_dir>`** — the directory this SKILL.md lives in (Claude Code: the skill
folder; Hermes: the `skill_dir` that `skill_view` returns). Every bundled path named below —
`scripts/`, `styles/`, `references/`, `examples/` — is relative to `<skill_dir>`, **not** to your
current working directory: you work in the user's project, so always call the helpers as
`python3 <skill_dir>/scripts/...`.

Every page is a generated image. You need exactly one way to make images. In order of preference:

1. **The agent already has an image-generation tool.** Use it. The contract this skill assumes is
   minimal: `generate(prompt: str, size: str) -> image bytes/file`, landscape, ≥1.3 MP. If your
   runtime's tool has a different signature, adapt — you only ever need "prompt in, one landscape
   image out."
2. **No image tool? Use the bundled helper.** `python3 <skill_dir>/scripts/gen_image.py "<full-page prompt>" out.jpg`
   calls any OpenAI-compatible images endpoint. Copy `.env.example` to `.env`, set
   `IMAGE_API_BASE_URL`, `IMAGE_API_KEY`, `IMAGE_MODEL`, `IMAGE_SIZE` (and `IMAGE_MAX_ATTEMPTS`,
   the per-page retry budget — default 3). It POSTs `{model, prompt, size, n:1}` to
   `{BASE}/images/generations` and reads `data[0].b64_json` (or `data[0].url`). Run
   `python3 <skill_dir>/scripts/gen_image.py --check` first — it reports the config, probes the
   endpoint, and turns any failure into a plain verdict with the fix. The probe **generates one
   billable image** on the user's endpoint; `--check-config` reports the same config and spends
   nothing, so use it when you only need to know whether a key is present.

**This skill ships NO image key and NO model.** It is the judgment + prompts; the pixels come from
your model.

### 0.1 No image path works — get the key without breaking stride

The key ask should feel like a step inside the work, not a gate in front of it. Four rules:

1. **Reuse silently.** `gen_image.py` picks up an exported `OPENAI_API_KEY` on its own (when no
   base URL points elsewhere) and `--check` says so. If that probe passes, mention it in one line
   and continue — ask nothing.
2. **Work first, ask at render time.** Never open with the key question. Build the full plan —
   densities, devices, verbatim text, complete image prompts — and show it. Only when page 1 is
   about to render, ask once, in one message: *"To render I need an image key — paste it here
   (plus the base URL if it's a relay/gateway). Or say 'placeholders' and I'll assemble a draft
   you can look at first."* The user has just seen their deck take shape; this reads as the last
   step, not a wall.
3. **Paste is all they do.** When the user pastes a key (and optionally a URL/model), *you* write
   `<skill_dir>/.env` from it, run `--check`, and keep going. Never send the user off to edit
   files or read docs mid-flow; `references/providers.md` has ready-to-paste blocks (OpenAI,
   generic relays, 火山方舟/豆包 Seedream) for when they only know their provider's name.
4. **Configured once, never asked again.** The `.env` persists in the skill directory; every
   future deck inherits it. If a later `--check` fails, lead with its verdict, not the setup speech.

If they'd rather not configure anything now → the **dry-run judgment pack** (§8, step 2b), which
has one command: `python3 <skill_dir>/scripts/judgment_pack.py --init <N> --style <slug>` writes
the `slides/PLAN.md` skeleton (the same one plan file step 1 writes — there is no second plan
file), you fill in every stanza (that authoring *is* the judgment), and
`python3 <skill_dir>/scripts/judgment_pack.py slides` renders the placeholders and assembles
`draft.pptx`. The deck's judgment still gets made; only the pixels wait.

Chat-only gateways are the classic trap: many "OpenAI-compatible" relays proxy
`/chat/completions` and nothing else. `--check` names that case specifically.

> **Never build slides any other way.** No HTML/CSS pages, no pptxgenjs/text-box layouts, no
> "themed template" tools — every page is ONE generated image, or it isn't a ppt-zen deck.
> The dry-run judgment pack is the *only* sanctioned fallback: it produces placeholders that are
> visibly placeholders, never a different kind of slide dressed up as a deck.

> Aspect ratio: image models rarely emit native 16:9. Generate landscape (e.g. `1536x1024`, 3:2)
> and let assembly **cover-crop to 16:9** — so keep every title and key element clear of the top
> and bottom ~8% of the frame (say so in each prompt). `<skill_dir>/scripts/assemble_pptx.py` does the crop.

## 1. The one rule of density (this is the whole method in one line)

For every page ask: **is this "sayable in a line", or does it "only hold up when several things
sit side by side"?** Because **hearing is linear; seeing is simultaneous.**

| the page is… | → knob | put on screen |
|---|---|---|
| one claim / one number / a chapter beat | **HEADLINE** | one word, one number, one sentence |
| steps, a comparison, a list, a matrix | **DETAIL** | lay the things out so they can be scanned |

Two rules that run automatically:
- **Evidence pages must be DETAIL** — right after "AI already names competitors", show the names.
  Evidence you can't see doesn't count.
- **Dense stretches need relief** — never stack two *visually heavy* treatments back-to-back.
  A run of DETAIL pages is fine when each stays light (three labels, one chart, a 2×2 — see the
  example's evidence block), but land on a breathing HEADLINE page after the block.

You decide the knob per page. You do **not** ask the user.

## 2. The four axes (independent — don't merge them)

| axis | what | who decides |
|---|---|---|
| **Density** | headline ↔ detail | you, per page (rule §1) |
| **Skeleton** | how the frame is cut (grid / light-band / flowline / color-field / standoff… or none) | auto, by the page's job |
| **Device** | **the page's argument, drawn as a thing** | you, per page — the highest-value axis |
| **Material** | what it's made of (ink wash / copperplate / cinematic / Swiss grid…) | **chosen once, whole deck** |

## 3. Device — draw the argument (most-skipped axis)

Material is "made of what"; skeleton is "how the frame is cut" — **neither says what to draw.**
The device is the gap between them: **this page's meaning, drawn as an object.**
- "a score" → a measuring stick; "70% at work, 30% at home" → a balance scale; "asking once is
  luck" → dots scattering then converging (that *is* sampling); "AI names only 1–2 brands" → a funnel.
- Test: **looking at the object, can you guess what the page is about?** If not, change it.
- **One main device per page.** Two competing illustrations = no illustration.
- Some pages have none (a pure comparison, a list, a single number). Forcing a device there loses.

**How to find it (don't skip to drawing):** ① keyword the page in 2–3 words → ② map each to a
visible thing → ③ sketch 2–3 candidates → ④ pick the simplest that reads, not the cleverest.

**Illustration has levels, not on/off** — and good pages stack them:
`L0` material+type only · `L1` faint margin studies (credibility, no info) · `L2` typography IS the
image (a chapter word) · `L3` a small mark that replaces a sentence (a red ✗ = "this one's false")
· `L4` a main device that owns the page. "No main device" ≠ blank — it still gets L1/L2 or L3.

**Cross-page consistency (single-page checks miss this):** for any recurring character or concept,
write its identity down and put it (and its bans) into *every* page's prompt — a model doesn't know
what the other pages drew. (e.g. "Maya = a person, never a robot/gears/circuitry.")

## 4. Material — chosen once; pick from the gallery

Material is the deck's identity: change skeleton and device per page, but **hold material fixed** so
ten pages read as one artifact. (Only break it if the material *change itself* is the argument, once,
at a real turning point.)

Browse `styles/<slug>/STYLE.md` (or the live gallery at https://pptzen.xyz, and `styles.json` for a
machine-readable index of slug → name / medium / hand / prompt_formula). Each pack ships a reusable
`prompt_formula` (SURFACE / SKELETON / DEVICE / END / CRITICAL). Resolve a user's named style to its
slug via `styles.json` (match `name`, `slug`, or `aliases`) and record the resolved slug in the plan.

**Three-layer selection** — medium → hand → world:
- **medium** = the craft (cinema, ink, copperplate, tile…). **hand** = whose treatment within it
  (a cold monumental hand vs. a warm hazy hand are the same medium, different eyes). **world** = the
  scene the deck lives in. If the user only gives a vibe, pick a medium that fits the subject's own
  materials; ask one question only if genuinely stuck.

## 5. Facts vs. form — decide the FORM, never invent FACTS

The judgment layer owns **how a page looks**, not **what it claims**. Hard rule:

> **Never invent metrics, quotations, prices, dates, company names, or fundraising asks.**

Use only facts the user supplied. For any number/claim you don't have, write a visible placeholder
(`[TO CONFIRM]`, `<your metric>`) — never a plausible-looking fabrication. Before generating, list the
factual claims each page will show and confirm every one traces to the user's input. A deck is
presented as true; a beautiful slide with a made-up "$28k MRR" is a liability, not a feature.

**How facts get in — two modes** (this is how "one sentence in" and "never invent" coexist):
- **Source-backed (default):** the sentence sets the deck; facts come from whatever material the
  user attached or the conversation already contains. If a data page has no source, it ships with
  placeholders — a complete *structural* draft the user fills in, not a fact-ready deck.
- **Research mode (only if your runtime can browse and the user asked for it):** you may gather
  facts, but every claim gets its source recorded in `slides/PLAN.md`, in the stanza of the page
  that uses it (the `why:` line is the place for it).
The one-sentence promise is about *form* — the user never answers layout questions. It is not a
license to hallucinate content.

## 6. Full-image generation — five hard rules

Each page is one self-contained prompt. Always:
1. **Density in layers** — background texture fills but recedes; one hero ~70%; foreground accents
   limited; **no floating data cards / sidebars / widgets.** And no *themed-template* look: no
   rounded card grids, numbered step boxes, or generic diagram shapes with a texture behind them.
   The page is a scene in the material's world — if it would look at home in a generic template
   gallery, it is off-style.
2. **Never invent glyphs** — state the exact text to render and forbid any other words; decorative
   marks must be geometric only (ticks, numbers, dimension lines). Non-Latin scripts especially:
   spell out the exact characters and forbid additions.
3. **Suppress structural words** — end every prompt with a CRITICAL line forbidding prompt-structure
   words (STYLE / SURFACE / DEVICE / VERBATIM / label / caption…) from appearing in the image.
4. **Pin exact text length** — "render exactly these N words/characters — no additions", or the model
   adds a stray word.
5. **Formulas style words; TEXT supplies them** — a pack's SURFACE says things like "a label on a
   small cartouche", "hand-lettered on a specimen card", "inscribed in a plaque", "lettering struck
   in gold". Those are *treatments* for words the page's TEXT already carries — **never** instructions
   to add words. If TEXT names no words for a text-bearing object, that object stays **blank** (or is
   left out): a blank tag is correct, an invented one is a bug — it comes back as smudged pseudo-text.
   **CJK text:** each glyph instance from TEXT renders once, cleanly, in the material's own lettering
   style — repeated characters (人人, 哈哈) stay repeated exactly as written; no double exposure, no
   ghost strokes, no half-rendered duplicates — regenerate the page if any character ghosts.

**Compose every page prompt mechanically — do not improvise the material:**
```
SURFACE:  paste the chosen pack's prompt_formula SURFACE **verbatim** from
          styles/<slug>/STYLE.md (or styles.json). Writing your own material
          description instead is the #1 way decks come out off-style.
SKELETON: <auto by page role; or "plain">
DEVICE:   <this page's argument as an object; or "none">
TEXT:     <exact words to render, and where>
CRITICAL: render ONLY the text above; every letter correct; no invented glyphs; no other language;
          no structure words; text-bearing objects from SURFACE stay blank unless TEXT names their
          words; keep key content clear of top/bottom ~8%.
```
Worked reference: `<skill_dir>/examples/relayboard-portolan/gen.py` shows ten real page prompts built this way.
Dense/text-heavy pages: generate at higher resolution than single-word pages.

## 7. If it's presented live (constraints you only learn from real rooms)

- **Confirm the screen aspect first** — big venues are often ultra-wide, not 16:9. This is one of the
  few things worth asking the user.
- **Leave a presenter corridor** — keep key info out of the bottom third; on ultra-wide, push subjects
  to the sides and keep the middle open (the speaker stands there).
- **Assertion capsule** — a fixed-position one-line conclusion on every page ("so what?"); detail
  pages especially need a place that states what to conclude.
- **Bilingual citations** — `Local Title (English Title)`; person name localized on top, original
  smaller/lighter below; facts only, no adjectives.

## 8. Workflow (one sentence in → a deck out)

0. **Preflight — prove the image path before page 1.** Never open a ten-page run on an unverified
   endpoint; the failure lands after the user has waited. Either generate **one test image**
   with the agent's own image tool, or run `python3 <skill_dir>/scripts/gen_image.py --check`
   — which **generates one billable image** on their endpoint, so say so rather than spending
   it silently (`--check-config` checks the config alone and costs nothing).
   If it fails: **stop** and walk the user through the fix (§0.1 — the 30-second `.env` setup, the
   provider templates, or the dry-run pack). Do not start generating and hope.
1. **Plan** — write `slides/PLAN.md`. **This is the deck's one and only plan file** — never write a
   second plan anywhere: one `## NN-slug` stanza per page, in reading order, each carrying
   `why:` (optional — the grounds for this page's density/device call) · `density:` (HEADLINE or
   DETAIL) · `device:` · `text:` (the verbatim on-slide copy) · `prompt:` (the complete
   ready-to-paste image prompt, indented under `prompt: |`, composed per §6), plus a header naming
   the resolved `style:` slug and a `language:` line. Want the skeleton written for you:
   `python3 <skill_dir>/scripts/judgment_pack.py --init <N> --style <slug>`.
   **On-slide text defaults to the language the user is working in** —
   a Chinese conversation gets a Chinese deck unless they ask otherwise (the demo decks being English
   does not make English the default). This file is the judgment log (the part nobody can
   screenshot) **and** the prompt pack step 2b renders from — one file, both jobs.
   Decide material once, up front.
2. **Generate** — one image per page into `slides/NN.jpg`, using the agent's image tool or
   `python3 <skill_dir>/scripts/gen_image.py`. Use deterministic filenames; regenerate a single
   page in isolation. Work in the user's project directory and call the helper by its full
   `<skill_dir>` path (`gen_image.py` reads `.env` from the current directory first, then from
   `<skill_dir>` next to the script).
   - **Say where you are** — a page takes tens of seconds and ten of them look like a hang.
     Announce every page as you start it (`page 3/10 — evidence grid`), and confirm when it lands.
   - **Retry the page, not the deck** — on a single-page failure try that page up to 2 more times
     before surfacing anything; if it still fails, run `--check` and give the user its verdict,
     not a traceback. (`gen_image.py` already retries 5xx/timeouts twice itself; a 4xx is config
     and won't fix itself.)
   - **Resume, never restart** — filenames are deterministic, so a re-run after an interruption
     **skips every page whose `slides/NN-*.jpg` already exists** and says so
     ("pages 1–4 already generated, resuming at 5"). Only regenerate an existing page when the
     user asked for that page.
2b. **No image path? Ship the judgment pack instead** (§0.1 — do *not* abort the task).
   The plan from step 1 **is** the pack — same `slides/PLAN.md`, nothing to convert:
   - Not written yet? `python3 <skill_dir>/scripts/judgment_pack.py --init <N> --style <slug>`
     writes the skeleton. **Fill in every stanza** — density, device, verbatim text, and
     the **complete ready-to-paste image prompt** (the chosen pack's SURFACE block verbatim, plus
     SKELETON / DEVICE / TEXT / CRITICAL). A skeleton left as-is is not a deliverable.
   - `python3 <skill_dir>/scripts/judgment_pack.py slides` then renders one placeholder per page
     and assembles `draft.pptx`, so the structure can be reviewed and walked through today. It
     **skips any page whose image already exists** — the same resume rule as a real run, which is
     how a user drops real images in one at a time and re-runs.
   - Rendering a single page by hand stays available:
     `python3 <skill_dir>/scripts/placeholder_page.py slides/NN-role.jpg --page 3/10
     --density DETAIL --device "<one line>" --title "<verbatim title>"`.
   Then tell the user the exit: hand any prompt card to any image tool they already have
   (Midjourney, 即梦, Doubao, a web UI), drop the result into `slides/` over the placeholder of the
   same name, reassemble. The key becomes an optional last step instead of a wall.
3. **QC the images** — proofread every character of every title against the plan, confirm no
   invented glyphs and no cross-page contradiction, and check nothing important sits in the
   top/bottom ~8% that assembly will crop. Two failures to look for by name: **ghost strokes**
   (a character rendered twice, doubled or smeared — regenerate) and **filled blank tags** (a
   card / cartouche / plaque the material introduced carrying words the plan never gave it —
   regenerate; it should be empty). Then the **style check**: put your page next to
   `styles/<slug>/samples/01.jpg` — same world? A business template with the material's texture
   behind it FAILS; regenerate with the pack's SURFACE pasted verbatim. This gate is where the
   quality comes from.
4. **Assemble, then check the deck** — `python3 <skill_dir>/scripts/assemble_pptx.py slides/ deck.pptx`
   (16:9 full-bleed; non-16:9 images are center cover-cropped). Open the result: page order,
   crop, nothing eaten. Regenerate and reassemble single pages as needed. The output is an
   **image-based** `.pptx`: great to present, but text isn't editable — fixing a typo means
   regenerating that one page. PDF / Keynote / Google Slides import the same images.

See `examples/relayboard/` for a full ten-page worked deck plus its judgment log.
Optional deep-dives ship in `references/` (design laws, skeleton library, visual patterns,
delivery coaching — currently in Chinese; load them when you need the underlying reasoning).
`references/providers.md` is the exception: English, and the one to open when the image endpoint
is the problem.

## Delivery self-check

- [ ] Image path proven **before** page 1 (a test image, or `gen_image.py --check`)?
- [ ] Density set per page? Evidence pages detailed, dense blocks landing on a breathing page?
- [ ] Each page: a device that reads (or a deliberate "none"), one main device only?
- [ ] Material fixed across the whole deck? Style resolved to a real slug?
- [ ] **Every factual claim traces to the user's input? No invented numbers/quotes/prices/dates?**
- [ ] Five hard rules on every prompt? Key content clear of the crop zone?
- [ ] Recurring characters/concepts pinned identically in every prompt?
- [ ] Proofread every character after generation? Assembled and opened the `.pptx`?

