@liustack/pptfast 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ DrawingML out.
7
7
 
8
8
  ## Why
9
9
 
10
- Freeform SVG/HTML-to-PPTX pipelines have a high ceiling but an unstable floor — a weak model (or a strong one having an off turn) produces a deck that's broken, off-brand, or unreadable. pptfast trades freeform drawing for a controlled vocabulary: a semantic IR (zod schema), 13 built-in themes driven by design tokens, an archetype/block layout library with seeded variety, and native DrawingML output where every shape stays editable — not a picture pasted onto a slide.
10
+ Freeform SVG/HTML-to-PPTX pipelines have a high ceiling but an unstable floor — a weak model (or a strong one having an off turn) produces a deck that's broken, off-brand, or unreadable. pptfast trades freeform drawing for a controlled vocabulary: a semantic IR (zod schema), 13 built-in themes bundling a style (design tokens) and a brand (identity chrome), a layout-and-component library with seeded variety, and native DrawingML output where every shape stays editable — not a picture pasted onto a slide.
11
11
 
12
12
  A deck is really five things: a content model, a 2D layout, a visual style, motion, and a narrative. pptfast owns the last four — you (or your agent) own the content model by writing the IR.
13
13
 
@@ -20,6 +20,22 @@ pptfast --help
20
20
 
21
21
  Node >= 18. Or build from source: `git clone https://github.com/liustack/pptfast.git && cd pptfast && pnpm install && pnpm build`.
22
22
 
23
+ ### As a Claude Code plugin
24
+
25
+ The repo doubles as a Claude Code plugin that ships the deck-generation skill:
26
+
27
+ ```
28
+ /plugin marketplace add liustack/pptfast
29
+ /plugin install pptfast@pptfast
30
+ /reload-plugins
31
+ ```
32
+
33
+ The skill drives the CLI, so install the CLI too (`npm install -g @liustack/pptfast`).
34
+
35
+ ### Other agents (Codex, etc.)
36
+
37
+ [`skills/pptfast/SKILL.md`](./skills/pptfast/SKILL.md) is a self-contained Markdown playbook — reference it from your agent's context (e.g. `AGENTS.md`) and it teaches the same schema → outline → validate → render loop.
38
+
23
39
  ## Quick start
24
40
 
25
41
  ```bash
@@ -46,19 +62,33 @@ const bytes = await generatePptx(ir) // Uint8Array, ready to write to a .pptx
46
62
 
47
63
  | Command | Does |
48
64
  |---|---|
49
- | `render <ir.json> -o <out.pptx> [--theme <id>]` | Validate + render to a `.pptx` |
50
- | `validate <ir.json>` | Check the IR, print page-scoped errors |
51
- | `schema` | Print the IR JSON Schema |
65
+ | `render <target> -o <out.pptx> [--theme <id>] [--style <file>] [--draft]` | Validate + render to a `.pptx` — `target` is an IR JSON file, a deck project directory, or a bare deck name (see Deck projects) |
66
+ | `validate <target>` | Check the IR, print page-scoped errors — same `target` forms as `render` |
67
+ | `audit <target> [--json] [--pixels]` | Deterministic geometry review (overflow/out-of-bounds/low-contrast/overlap/content-truncated/content-dropped) same `target` forms as `render`, exits 1 when it finds anything (see Auditing) |
68
+ | `spec validate <spec.json>` | Check a deck spec against the schema and strategy-aware hard gates (see Deck projects) |
69
+ | `assemble <dir\|name> [-o <file>]` | Materialize a deck project directory into a single IR JSON file |
70
+ | `disassemble <ir.json> -o <dir>` | Split an IR JSON file into a deck project directory |
71
+ | `schema [--style \| --spec]` | Print the IR JSON Schema (or the style-override schema, or the deck spec schema) |
52
72
  | `themes [--json]` | List the 13 built-in themes |
53
- | `preview <ir.json> -o <dir>` | Render each slide to a standalone SVG |
73
+ | `narratives [--json]` | List named narrative presets (strategy/pacing/audience axes + theme recommendations) |
74
+ | `preview <target> -o <dir> [--html]` | Render each slide to a standalone SVG (`--html` also writes a self-contained `preview.html`) — same `target` forms as `render`, never gated on placeholder pages |
75
+ | `migrate <input> -o <output>` | Convert a v3 IR file to v4, or a `deck.plan.json` project directory to `deck.spec.json` — deterministic, no model call (see The IR and Deck projects) |
76
+ | `init` | Scaffold `pptfast.config.json` |
77
+ | `check-update` / `self-update` | Check npm for a newer release / update the global install |
54
78
 
55
79
  ## The IR
56
80
 
57
- Run `node dist/cli.js schema` for the full JSON Schema — feed it to a model before asking it to write IR. A deck (`PptxIR`) is a required `filename`, `version`, `theme`, `meta`, `assets`, an optional `brand`, and an ordered list of `slides`; each slide has a `type` (`cover`, `chapter`, `content`, `ending`) and, for content slides, a list of typed `blocks` (`bullets`, `kpi_cards`, `image`, `chart`, …). `assets` is `{ images: { [id]: { src, alt? } } }` — blocks reference images by `asset_id`, so the same image can be reused across slides without duplication.
81
+ Run `node dist/cli.js schema` for the full JSON Schema — feed it to a model before asking it to write IR. A deck (`PptxIR`) has `version` (currently `"4"`, and now the default when omitted), `filename`, an optional `narrative` (a preset id string or a partial axes object — see Narratives below), `theme` (`id` plus optional `style`/`brand` overrides), `meta`, and `assets` all optional with sane defaults — plus a separate optional `brand` (logo placement) and a required ordered list of `slides`. Each slide has a `type` (`cover`, `chapter`, `content`, `ending`), an optional `layout` (an explicit page-layout id that always wins over auto-selection — omit it and pptfast auto-selects one, see Layout selection below), an optional `arrangement` (how a content slide's body is laid out, e.g. `two_column`, `kpi_focus`), and a list of typed `components` (`bullets`, `kpi_cards`, `image`, `chart`, …). `assets` is `{ images: { [id]: { src, alt? } } }` — components reference images by `asset_id`, so the same image can be reused across slides without duplication.
82
+
83
+ A deck also carries an optional `seed` (an integer that keeps auto-selected layouts stable across revisions — see Layout selection below for how it's derived when omitted). Any slide may set a stable `id` (what spec pages and validation error messages reference it by), `placeholder: true` (a slide with no content yet — injected by `assemble` for a spec page nobody has filled in, skipped by the content-quality checks, and blocking `render` unless `--draft`), and an optional `notes` (aliases `note`/`speaker_notes`/`speakerNotes`) that exports as a native PowerPoint speaker note — content for the presenter's own view, never drawn onto the slide canvas and never counted toward any layout capacity. Field names that commonly drift between a model's output and the schema (40 synonym pairs across component types, e.g. kpi `title`→`label`, quote `content`→`text`, swot `strength`→`strengths`, bmc `partners`→`key_partners`) are silently normalized to the canonical name at validate time — `validate`/`render`/`preview` print a note listing what changed, never a hard error. That rescue is scoped to weak-model synonym drift only — it does not cover pre-v4 vocabulary. A v4-labeled document that writes `scenario` instead of `narrative`, `mode`/`delivery` instead of `strategy`/`pacing`, or the old `narrative`/`text`/`presentation` axis values hard-rejects, listing the current names/values, exactly like any other unrecognized field or value. An explicit `version: "3"` (or `"2"`) also hard-rejects, with a migration pointer — see `pptfast migrate` below, the only supported path for old-vocabulary input.
84
+
85
+ Four component types are *full-body*: `swot` (strengths/weaknesses/opportunities/threats), `bmc` (the nine-block Business Model Canvas), `waterfall` (a running-total bridge chart), and `gantt` (dated bars on a shared numeric axis). Each fills a slide's entire content rect and must be the only component on its slide — mixing one in with anything else fails `validate` instead of silently dropping the sibling.
86
+
87
+ The v4 IR schema is frozen as of 0.4.0 — future evolution is additive only (new optional fields, new enum members), and any breaking change ships under a new top-level `version` value with the same hard-reject-and-migration treatment v3 got. `pptfast migrate <v3-file.json> -o <out.json>` deterministically converts a v3 file to v4 (field renames only — same theme, layout selection, content budgets, and visual output) — see Deck projects below for the sibling `deck.plan.json` → `deck.spec.json` conversion.
58
88
 
59
89
  ## Themes
60
90
 
61
- Each theme is a token set (color, type, motif) plus a manifest of which archetypes it allowsthemes never touch layout code.
91
+ A theme bundles a style (design tokens), a brand (identity chrome), and a layout set for each page type — the 13 built-ins below. Every built-in defaults to the *full* set of registered layouts for each page type (every archetype adapts its text color to the theme's actual background, so the full set stays readable everywhere). Narrowing it is a deliberate theme-author choice, not the normonly 3 of the 13 exclude a single chapter layout (a runway-native design whose contrast doesn't clear those themes' accent color). Override the style (`--style`) to re-color a theme.
62
92
 
63
93
  | id | label |
64
94
  |---|---|
@@ -76,15 +106,88 @@ Each theme is a token set (color, type, motif) plus a manifest of which archetyp
76
106
  | `luxe` | Luxe |
77
107
  | `heritage` | Heritage |
78
108
 
109
+ ## Narratives
110
+
111
+ A narrative is three axes, independent of theme (visual style), that set editorial discipline: `strategy` (how the argument is built — `pyramid`, `storytelling`, `instructional`, `showcase`, `briefing`), `pacing` (how dense the content is — `dense`, `balanced`, `spacious`), and `audience` (a tone anchor — `executive`, `technical`, `customer`, `public`, no rendering effect yet). Set the IR's top-level `narrative` to a named preset string (e.g. `"boardroom-report"`) or a partial axes object (e.g. `{ "pacing": "spacious" }`) — an omitted axis, or an omitted `narrative` field entirely, falls back to `general` (`briefing` × `balanced` × `public`). An unknown preset name or axis value is a hard validate error listing what's available.
112
+
113
+ `pacing` drives the content-quality gate and the body-text baseline (paragraph/bullets/callout only — every other component's own type scale and the heading system are unaffected): the per-slide component budget and the bullets budget (item count and per-item length) both tighten from `dense` toward `spacious`, while the body font size grows the other way — density is additionally capped by whichever layout the slide resolves to, whichever ceiling is tighter.
114
+
115
+ | pacing | body text | components / slide | bullets |
116
+ |---|---|---|---|
117
+ | `dense` | 20px | 5 | up to 6 items, ~48 characters each |
118
+ | `balanced` (the default) | 24px | 4 | up to 5 items, ~40 characters each |
119
+ | `spacious` | 32px | 3 | up to 4 items, ~30 characters each |
120
+
121
+ Bullets shrink below their tier's baseline to fit when needed, down to a 14px floor, before any overflow handling kicks in. `pptfast validate` reports the exact numbers that applied to each slide.
122
+
123
+ Run `pptfast narratives [--json]` to list the named presets (each carries soft theme recommendations — a starting suggestion, never a constraint) plus the raw axes tables.
124
+
125
+ ## Layout selection
126
+
127
+ When a slide omits `layout`, pptfast resolves one automatically in four deterministic steps: the page type's full registry pool → the theme's layout set for that page type (full by default, see Themes above) → the narrative's `strategy` softly upweights (×3) a handful of content-layout ids that suit that strategy, everything else stays at a ×1 floor (cover/chapter/ending are never weighted — their character comes from the theme, not the strategy) → a seeded weighted pick, swapped deterministically to the runner-up when it would repeat the immediately preceding slide's layout. An explicit `layout` always wins and skips every step above. Whether the content actually fits is enforced separately by `validate`'s density gate, never by selection — so editing a page's content cannot silently flip its layout.
128
+
129
+ The pick is fully deterministic — the same IR always resolves the same way, so preview and the final render never disagree. Staying stable *across revisions* (editing one page without reshuffling every other page's auto-picked layout) additionally needs a persisted `seed`, resolved in this order:
130
+
131
+ 1. An explicit `ir.seed` — full revision stability, always wins.
132
+ 2. A deck project's own seed: `pptfast assemble` derives one from the spec's filename and page ids the first time a spec omits `seed`, and prints a note with the value — copy it into `deck.spec.json`'s `seed` field to persist it.
133
+ 3. Neither set: a content hash of `filename` + every slide's `heading` (legacy-compatible) — editing any heading reshuffles every auto-picked layout deck-wide.
134
+
135
+ `pptfast assemble` also writes every auto-picked `layout` back into the assembled `deck.json` (a page file's own explicit `layout` is left untouched) — the CLI notes how many pages it filled in.
136
+
137
+ ## Style overrides & project config
138
+
139
+ Override the built-in palette without forking a theme: write a style JSON
140
+ (schema: `pptfast schema --style`) and pass it per-render
141
+ (`--style brand.json`), or pin it project-wide in a `pptfast.config.json`
142
+ (found by walking up from cwd, scaffold one with `pptfast init`).
143
+ Precedence: CLI flag > project config file > user config file > IR (Deck
144
+ projects below has the full four-layer chain). The IR itself can carry the
145
+ same override in `theme.style` for fully self-contained decks.
146
+
147
+ ```json
148
+ { "theme": "consulting", "style": { "colors": { "primary": "#0B5FFF", "accent": "#FF6A00" } } }
149
+ ```
150
+
151
+ ## Deck projects
152
+
153
+ A deck can be authored two ways, and every command that takes IR accepts either: a single **IR JSON file** (everything above), or a **deck project directory** — the same content split across files so an agent can spec out a deck's structure first, then write and revise it page by page instead of holding one growing JSON blob in context.
154
+
155
+ ```
156
+ my-deck/
157
+ deck.spec.json the locked spec: page order, type, and heading for every page
158
+ pages/<page-id>.json one file per filled page (components/layout/arrangement/background/image_side/footnote)
159
+ assets/ local images, auto-registered by filename (image id = filename without extension)
160
+ ```
161
+
162
+ `deck.spec.json` validates on its own, before any page exists: `pptfast spec validate deck.spec.json` checks the schema plus strategy-aware hard gates (boundary pages, heading length, beat rotation, page count vs. pacing). A spec page with no matching `pages/<id>.json` becomes a **placeholder** slide — heading only, not missing — so a partially-written deck always assembles and previews. `pptfast render` refuses to export a deck with unfilled placeholders unless you pass `--draft`. `pptfast preview` never gates on them. A directory still carrying the pre-v4 `deck.plan.json` instead of `deck.spec.json` is not read directly — `pptfast migrate <dir> -o <dir>` converts it in place (writes `deck.spec.json` alongside, never overwrites, never deletes the source — delete `deck.plan.json` yourself once you've confirmed the new file); a directory with both files present is a hard error, never a guessed priority.
163
+
164
+ `pptfast assemble <dir>` materializes spec + pages + assets into a single IR JSON file (`deck.json` by default). `pptfast disassemble <ir.json> -o <dir>` does the reverse (documented-lossy — spec-only fields like `beat`/`focus` have no IR-side home to recover). `render`/`validate`/`preview` accept a directory directly too, assembling in memory first.
165
+
166
+ Deck project directories can be referenced by a bare name instead of a path — `pptfast render my-deck -o out.pptx` resolves `my-deck` under `$PPTFAST_HOME/decks` (`$PPTFAST_HOME` defaults to `~/.pptfast`) when no local file or directory of that name exists. All deck defaults resolve in four layers, highest wins: CLI flag > project `pptfast.config.json` > user `~/.pptfast/config.json` > the deck's own values. Both config layers can set `decksDir` to redirect where bare names resolve — the project layer's value resolves against that config file's own directory (for a team that wants deck projects checked into the repo), the user layer's against `$PPTFAST_HOME`. Project wins when both are set.
167
+
168
+ ## Auditing
169
+
170
+ `pptfast audit <target> [--json]` renders every page off-screen and runs a deterministic geometry review — no LLM screenshot squinting, no variance. Six checks: **overflow** (text past its own box or column), **out-of-bounds** (past the page edge), **low-contrast** (WCAG relative-luminance ratio between text and its resolved background), **overlap** (two components' regions substantially colliding), **content-truncated** (text the renderer had to cut short with an ellipsis to fit), and **content-dropped** (a "+N more" marker — a card list or a whole component that didn't fit and got hidden). Advisory, not a hard gate — `validate` already rejects structurally invalid or over-dense decks. Audit catches what a valid deck can still get wrong at render time (an author-chosen near-background text color, two components whose combined content collides, a card list that had to drop an item to fit).
171
+
172
+ Add `--pixels` (Node only, needs the optional `sharp` dependency) to also catch the one thing the SVG-only checks can't see — text sitting directly on an unscrimmed photo background — by rasterizing the page and sampling real pixels. Every response now also carries a `checks` field (`{ svg: "completed", pixels: "not-requested" | "completed" }`) so a caller can always tell what actually ran instead of reading a missing check as a pass (see `docs/contrast-system.md` for the pixel layer's own determinism caveat).
173
+
174
+ Run it once every page is filled, on the same `target` forms as `validate`/`render` (file, deck project directory, or bare name). Human output groups findings by page (`page 3 (p-kpi): [low-contrast] …`, each message carries a fix suggestion) plus a summary line. `--json` prints the full machine-readable report. The exit code alone is agent-judgeable: `0` clean, `1` when it finds anything — fix the flagged page and re-run `audit` alone, no need to re-render. Skipped placeholder pages are noted, the same "not missing, just not written yet" treatment used everywhere else.
175
+
176
+ ```bash
177
+ pptfast audit examples/basic.json
178
+ # → audited 5 pages, 0 skipped, 0 findings
179
+ ```
180
+
79
181
  ## For AI agents
80
182
 
81
- The recommended loop for an agent generating a deck: read `pptfast schema` to learn the vocabulary, write an IR JSON, run `pptfast validate` and fix whatever it reports (errors carry a page number and a fixable-in-place message — the point is to close this loop without a human), then `pptfast render`. `pptfast preview` gives the agent SVG files it can look at to self-check layout before committing to a render. A Claude Code plugin that wraps this loop as a skill is planned for v0.2 (see Roadmap).
183
+ The recommended loop for an agent generating a deck: read `pptfast schema` to learn the vocabulary, write an IR JSON, run `pptfast validate` and fix whatever it reports (errors carry a page number and a fixable-in-place message — the point is to close this loop without a human), then `pptfast audit` for the same kind of fixable-in-place feedback on what a *valid* deck can still get wrong at render time (overflow, low-contrast, overlap — exit code alone says whether it's clean), then `pptfast render`. `pptfast preview` gives the agent SVG files it can look at to self-check layout before committing to a render. Add `--html` to also write a self-contained `preview.html` for a human to review (keyboard nav, placeholder badges — a remote-URL image asset stays remote, the one self-containment gap). When every page is filled, that `preview.html` also overlays the same `audit` findings (per-page badges plus a findings panel, click to jump to the page) so a human reviewer sees them without a terminal — a deck with any placeholder page shows a one-line "audit skipped" notice instead. The reviewer can leave free-text per-page annotations right in `preview.html` and export them as a `revision-request.json` (a Blob download, no network or file write — preview stays read-only) for the agent to route back through `pages/*.json`. The Claude Code plugin above wraps this loop as a skill ([`skills/pptfast/SKILL.md`](./skills/pptfast/SKILL.md)). This exact loop is exercised by an internal, model-agnostic benchmark (`tests/bench/`, not published to npm) that mechanically scores how well a model follows the skill on a fixed question bank — see `tests/bench/README.md`.
82
184
 
83
185
  ## Roadmap
84
186
 
85
- - **v0.2** — Claude Code plugin + skills wrapping the render loop, design token overrides (`--tokens`), `init`/self-update commands.
86
- - **v0.3** — theme-customization skill (brand colors tokens), custom manifest slots, 1.0.
87
- - **v0.4** — richer motion (more entrance animations), Office real-device testing, web playground.
187
+ - **v0.2** — Claude Code plugin + skill wrapping the render loop (shipped), design token overrides (`--style`), `init`/self-update commands.
188
+ - **v0.3** — narrative-driven axes (strategy/pacing/audience), an explicit layout + component registry with weighted seeded selection, the deck spec/assemble workflow, a deterministic geometry audit, a self-contained HTML preview, and the six-phase skill (shipped; these axes shipped named `scenario`/`mode`/`delivery`, renamed to `narrative`/`strategy`/`pacing` in the v0.4 vocabulary rewrite below).
189
+ - **v0.4** — vocabulary-v4: `scenario`→`narrative`, `mode`→`strategy`, `delivery`→`pacing`, `plan`→`spec` (`deck.plan.json`→`deck.spec.json`), page `rhythm`→`beat`, a deterministic `pptfast migrate` command, no behavior change (shipped, schema frozen as of 0.4.0 — see The IR above).
190
+ - **v0.5+** — theme ecosystem (distributable theme registry, theme-customization skill, custom brand slots), richer motion (more entrance animations), Office real-device testing, web playground, 1.0.
88
191
 
89
192
  ## Credits
90
193
 
package/README.zh-CN.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## 为什么
8
8
 
9
- 自由绘制 SVG/HTML 再转 PPTX 的链路上限很高,但下限不稳定——弱模型(或强模型状态不好时)画出来的往往是版式错乱、脱离品牌规范、甚至无法阅读的产物。pptfast 用受控词汇取代自由绘制:一份语义化 IR(zod schema)、由 design tokens 驱动的 13 个内置主题、带 seed 多样性的 archetype/block 版式库,以及每个图形都保持可编辑的原生 DrawingML 输出——不是贴上去的一张图。
9
+ 自由绘制 SVG/HTML 再转 PPTX 的链路上限很高,但下限不稳定——弱模型(或强模型状态不好时)画出来的往往是版式错乱、脱离品牌规范、甚至无法阅读的产物。pptfast 用受控词汇取代自由绘制:一份语义化 IR(zod schema)、13 个内置主题(各自打包一套 style 设计 tokens brand 品牌标识元素)、带 seed 多样性的 layout/component 版式库,以及每个图形都保持可编辑的原生 DrawingML 输出——不是贴上去的一张图。
10
10
 
11
11
  一份 PPT 本质上是五件事:内容模型、二维布局、视觉样式、动效、叙事。pptfast 负责后四项,内容模型交给你(或你的 agent)通过写 IR 来掌控。
12
12
 
@@ -19,6 +19,22 @@ pptfast --help
19
19
 
20
20
  需要 Node >= 18。也可从源码构建:`git clone https://github.com/liustack/pptfast.git && cd pptfast && pnpm install && pnpm build`。
21
21
 
22
+ ### 作为 Claude Code 插件
23
+
24
+ 本仓库同时是一个 Claude Code 插件,内置整套生成流程的 skill:
25
+
26
+ ```
27
+ /plugin marketplace add liustack/pptfast
28
+ /plugin install pptfast@pptfast
29
+ /reload-plugins
30
+ ```
31
+
32
+ skill 依赖 CLI 驱动,请一并安装 CLI(`npm install -g @liustack/pptfast`)。
33
+
34
+ ### 其他 agent(Codex 等)
35
+
36
+ [`skills/pptfast/SKILL.md`](./skills/pptfast/SKILL.md) 是一份自包含的 Markdown 操作手册——把它引入你的 agent 上下文(例如在 `AGENTS.md` 里引用),即可复用同一套 schema → 大纲 → validate → render 回路。
37
+
22
38
  ## 快速开始
23
39
 
24
40
  ```bash
@@ -44,19 +60,33 @@ const bytes = await generatePptx(ir) // Uint8Array,可直接写成 .pptx 文
44
60
 
45
61
  | 命令 | 作用 |
46
62
  |---|---|
47
- | `render <ir.json> -o <out.pptx> [--theme <id>]` | 校验并渲染成 `.pptx` |
48
- | `validate <ir.json>` | 校验 IR,输出带页码的错误信息 |
49
- | `schema` | 输出 IR JSON Schema |
63
+ | `render <target> -o <out.pptx> [--theme <id>] [--style <file>] [--draft]` | 校验并渲染成 `.pptx`——`target` 可以是 IR JSON 文件、deck 项目目录,或裸名(见「Deck 项目」) |
64
+ | `validate <target>` | 校验 IR,输出带页码的错误信息——`target` 形式同 `render` |
65
+ | `audit <target> [--json] [--pixels]` | 确定性几何审查(溢出/越界/低对比度/重叠/内容截断/内容丢失)——`target` 形式同 `render`,一旦发现问题 exit 1(见「审查」) |
66
+ | `spec validate <spec.json>` | 校验 deck spec 是否符合 schema 与随 strategy 变化的硬门(见「Deck 项目」) |
67
+ | `assemble <dir\|name> [-o <file>]` | 把 deck 项目目录合并成单个 IR JSON 文件 |
68
+ | `disassemble <ir.json> -o <dir>` | 把 IR JSON 文件拆成 deck 项目目录 |
69
+ | `schema [--style \| --spec]` | 输出 IR 的 JSON Schema(或 style 覆盖 schema,或 deck spec schema) |
50
70
  | `themes [--json]` | 列出 13 个内置主题 |
51
- | `preview <ir.json> -o <dir>` | 逐页渲染为独立 SVG |
71
+ | `narratives [--json]` | 列出具名叙事预设(strategy/pacing/audience + theme 推荐) |
72
+ | `preview <target> -o <dir> [--html]` | 逐页渲染为独立 SVG(`--html` 额外写出一个自包含的 `preview.html`)——`target` 形式同 `render`,永远不受占位页拦截 |
73
+ | `migrate <input> -o <output>` | 把 v3 IR 文件转成 v4,或把 `deck.plan.json` 项目目录转成 `deck.spec.json`——确定性转换,不调模型(见「IR」与「Deck 项目」) |
74
+ | `init` | 生成 `pptfast.config.json` 模板 |
75
+ | `check-update` / `self-update` | 检查 npm 上的新版本 / 更新全局安装 |
52
76
 
53
77
  ## IR
54
78
 
55
- 运行 `node dist/cli.js schema` 获取完整 JSON Schema——让模型写 IR 之前先读它。一份 deck(`PptxIR`)由必填的 `filename`、`version`、`theme`、`meta`、`assets`、可选的 `brand`,和一个有序的 `slides` 列表组成。每张 slide 有一个 `type`(`cover`、`chapter`、`content`、`ending`),content 类型的 slide 还带一组带类型的 `blocks`(`bullets`、`kpi_cards`、`image`、`chart` 等)。`assets` 的形状是 `{ images: { [id]: { src, alt? } } }`,block 通过 `asset_id` 引用图片,同一张图可以在多页复用而不必重复内嵌。
79
+ 运行 `node dist/cli.js schema` 获取完整 JSON Schema——让模型写 IR 之前先读它。一份 deck(`PptxIR`)包含 `version`(现为 `"4"`,且省略时默认就是它)、`filename`、一个可选的 `narrative`(预设 id 字符串,或部分轴对象——详见下文「叙事」一节)、`theme`(`id` 加可选的 `style`/`brand` 覆盖)、`meta`、`assets`——均可省略、有默认值——另外还有一个独立的可选 `brand`(logo 位置)字段,以及必填的有序 `slides` 列表。每张 slide 有一个 `type`(`cover`、`chapter`、`content`、`ending`)、一个可选的 `layout`(显式指定页面版式 id,一经设置恒生效、优先于自动选型——省略则由 pptfast 自动选型,详见下文「版式选型」)、一个可选的 `arrangement`(content slide 正文的排布方式,如 `two_column`、`kpi_focus`),以及一组带类型的 `components`(`bullets`、`kpi_cards`、`image`、`chart` 等)。`assets` 的形状是 `{ images: { [id]: { src, alt? } } }`,component 通过 `asset_id` 引用图片,同一张图可以在多页复用而不必重复内嵌。
80
+
81
+ 一份 deck 还可以携带一个可选的 `seed`(整数,让自动选型的版式在多次修订之间保持稳定——省略时如何生成,详见下文「版式选型」)。任意 slide 都可以设置一个稳定的 `id`(spec 的页面和校验报错都靠它引用)、`placeholder: true`(还没有内容的占位 slide——由 `assemble` 为 spec 里没人填写的页面注入,内容质量检查会跳过它,`render` 也会因它拒绝导出,除非加 `--draft`),以及一个可选的 `notes`(同义词 `note`/`speaker_notes`/`speakerNotes`),导出为原生 PowerPoint 演讲者备注——只是给主讲人自己看的内容,不会画到幻灯片画布上,也从不计入任何版式容量。模型输出里容易和 schema 对不上的字段名(跨 component 类型共 40 组同义词,例如 kpi 的 `title`→`label`、quote 的 `content`→`text`、swot 的 `strength`→`strengths`、bmc 的 `partners`→`key_partners`)会在校验时静默改写成规范名——`validate`/`render`/`preview` 会打印一条改了什么的提示,从不因此报错。这套救援机制只覆盖弱模型的同义词漂移,不覆盖 v4 之前的旧词汇。标着 v4 却仍写 `scenario`(而不是 `narrative`)、`mode`/`delivery`(而不是 `strategy`/`pacing`)、或轴值还停留在旧的 `narrative`/`text`/`presentation` 的文档,会像任何其他未识别字段或非法值一样直接硬报错,并列出当前正确的名称和取值。显式写 `version: "3"`(或 `"2"`)同样硬拒绝并给出迁移指引——见下文 `pptfast migrate`,这是旧词汇输入唯一支持的路径。
82
+
83
+ 四种 component 类型是「满幅」的:`swot`(strengths/weaknesses/opportunities/threats 四象限)、`bmc`(九宫格商业模式画布)、`waterfall`(运行合计瀑布图)、`gantt`(共享数轴上的甘特条形图)。各自独占整张 slide 的内容区域,必须是该 slide 唯一的 component——混入其他 component 会在校验时报错,而不是静默丢弃。
84
+
85
+ v4 IR schema 自 0.4.0 起冻结——后续演进只走加法(新增可选字段、新增枚举值),任何破坏性变更都会启用新的顶层 `version` 值,并沿用 v3 那套硬拒绝 + 迁移提示的处理方式。`pptfast migrate <v3-file.json> -o <out.json>` 能确定性地把一份 v3 文件转成 v4(只做字段改名——theme、版式选型、内容预算与视觉输出都不变)——`deck.plan.json` → `deck.spec.json` 的姊妹转换见下文「Deck 项目」。
56
86
 
57
87
  ## 主题
58
88
 
59
- 每个主题是一组 token(配色、字体、motif)加一份 manifest(声明允许使用哪些 archetype)——主题本身从不涉及布局代码。
89
+ 主题(theme)打包了 style(设计 tokens)、brand(品牌标识元素)与每个页型各自的版式(layout)集合——以下是 13 个内置主题。每个内置主题默认对每个页型都开放全部已注册版式(每个 archetype 都会按主题的实际背景色自适应取色,所以全集在任何主题下都保持可读)。收窄集合是主题作者的主动选择,不是常态——13 个主题里只有 3 个排除了单个 chapter 版式(一款 runway 专属设计,在这三个主题的强调色上对比度不够)。覆盖 style(`--style`)即可为某个主题重新配色。
60
90
 
61
91
  | id | label |
62
92
  |---|---|
@@ -74,15 +104,82 @@ const bytes = await generatePptx(ir) // Uint8Array,可直接写成 .pptx 文
74
104
  | `luxe` | Luxe |
75
105
  | `heritage` | Heritage |
76
106
 
107
+ ## 叙事
108
+
109
+ 叙事(narrative)是三条独立于主题(视觉风格)之外的轴,用来定编辑纪律:`strategy`(论证方式——`pyramid`、`storytelling`、`instructional`、`showcase`、`briefing`)、`pacing`(内容密度——`dense`、`balanced`、`spacious`)、`audience`(语气锚点——`executive`、`technical`、`customer`、`public`,目前无渲染效果)。把 IR 顶层的 `narrative` 设为具名预设字符串(如 `"boardroom-report"`),或部分轴对象(如 `{ "pacing": "spacious" }`)——省略任意一轴、或整个省略 `narrative` 字段,均回落到 `general` 预设(`briefing` × `balanced` × `public`)。未知的预设名或轴值会硬报错并列出可用项。
110
+
111
+ `pacing` 驱动内容质量门,也驱动正文字号基线(仅 paragraph/bullets/callout 三件套——其余组件各自的字阶与标题体系不受影响):每页的 component 数预算与 bullets 预算(条目数与单条长度上限)都随 `pacing` 从 `dense` 向 `spacious` 收紧,正文字号则反向增长——密度上限还会再叠加所选 layout 的容量,取两者中更紧的一个。
112
+
113
+ | pacing | 正文字号 | 每页 component 数 | bullets |
114
+ |---|---|---|---|
115
+ | `dense` | 20px | 5 | 至多 6 条,每条约 48 字 |
116
+ | `balanced`(默认) | 24px | 4 | 至多 5 条,每条约 40 字 |
117
+ | `spacious` | 32px | 3 | 至多 4 条,每条约 30 字 |
118
+
119
+ bullets 需要时会在各自档位基线之下收缩以适配空间,最低到 14px 地板,再触发溢出处理。`pptfast validate` 会报出每页实际生效的具体数值。
120
+
121
+ 运行 `pptfast narratives [--json]` 查看全部具名预设(各自带一份软性 theme 推荐表——仅供参考,从不构成约束)及三轴的原始数据表。
122
+
123
+ ## 版式选型
124
+
125
+ 当某页省略 `layout` 时,pptfast 按四个确定性步骤自动选型:该页型的全部注册版式 → 主题该页型的版式集合(默认全集,见上文「主题」)→ 叙事的 `strategy` 对一小撮适配该 strategy 的 content 版式做 ×3 软加权,其余维持 ×1 底权(cover/chapter/ending 三个页型永不加权——它们的个性来自主题,不来自 strategy)→ 按 seed 加权取样,若命中结果与紧邻的上一页版式相同则确定性地换成次优候选。显式 `layout` 恒优先,跳过以上全部步骤。内容装不装得下由 `validate` 的密度门单独把关,从不参与选型——因此改一页的内容不会悄悄翻转它的版式。
126
+
127
+ 选型本身完全确定——同一份 IR 永远选出同一个结果,预览与最终渲染绝不会不一致。但要在**多次修订之间**保持稳定(改一页不搅动其余页的自动选型),还需要一个持久化的 `seed`,按以下顺序解析:
128
+
129
+ 1. 显式 `ir.seed`——完全修订稳定,恒优先
130
+ 2. deck 项目自己的 seed:spec 省略 `seed` 时,`pptfast assemble` 首次运行会用 spec 的 filename + 页面 id 列表派生一个,并打印提示——把这个值写进 `deck.spec.json` 的 `seed` 字段即可固化
131
+ 3. 以上均未设置:回落到 `filename` + 每页 `heading` 的内容哈希(向后兼容旧行为)——改动任何一页标题都会重排全 deck 的自动选型
132
+
133
+ `pptfast assemble` 还会把每一页的自动选型结果写回合并后的 `deck.json`(页面文件里已显式指定的 `layout` 不受影响)——CLI 会提示本次填写了多少页。
134
+
135
+ ## Style 覆盖与项目配置
136
+
137
+ 不必分叉主题即可覆盖内置色板:写一份 style JSON(结构见 `pptfast schema --style`),按次渲染传入(`--style brand.json`),或固化在项目级 `pptfast.config.json` 里(自 cwd 向上查找,用 `pptfast init` 生成模板)。优先级:CLI flag > 项目配置文件 > 用户配置文件 > IR(完整的四层链见下文「Deck 项目」)。IR 自身也可以在 `theme.style` 携带同样的覆盖,做到单文件自包含。
138
+
139
+ ```json
140
+ { "theme": "consulting", "style": { "colors": { "primary": "#0B5FFF", "accent": "#FF6A00" } } }
141
+ ```
142
+
143
+ ## Deck 项目
144
+
145
+ 一份 deck 有两种写法,接受 IR 的每个命令两种都认:单个 **IR JSON 文件**(如上文所述),或者一个 **deck 项目目录**——把同样的内容拆到多个文件里,方便 agent 先规划整体结构,再逐页撰写和修订,而不必把一份不断增长的 JSON 塞进上下文。
146
+
147
+ ```
148
+ my-deck/
149
+ deck.spec.json 锁定的 spec:每一页的顺序、type、heading
150
+ pages/<page-id>.json 每个已填页面一个文件(components/layout/arrangement/background/image_side/footnote)
151
+ assets/ 本地图片,按文件名自动注册(图片 id = 去掉扩展名的文件名)
152
+ ```
153
+
154
+ `deck.spec.json` 可以在任何页面填写之前单独校验:`pptfast spec validate deck.spec.json` 会检查 schema,以及一组随 strategy 变化的硬门(边界页类型、标题长度、beat 轮换、页数是否匹配 pacing)。spec 里某一页如果没有对应的 `pages/<id>.json`,会成为一个**占位页**——只有标题、不算缺失——所以写到一半的 deck 也能正常 assemble 和预览。`pptfast render` 遇到未填的占位页会拒绝导出,除非加 `--draft`。`pptfast preview` 则永远不会因占位页被拦。目录里如果还留着改名前的 `deck.plan.json` 而不是 `deck.spec.json`,不会被直接读取——用 `pptfast migrate <dir> -o <dir>` 原地转换(会在旁边写出 `deck.spec.json`,不覆盖、也不删除原文件——确认新文件无误后自己删掉 `deck.plan.json`)。目录里两个文件同时存在会硬报错,绝不猜测优先级。
155
+
156
+ `pptfast assemble <dir>` 把 spec + pages + assets 合并成一个 IR JSON 文件(默认写到 `deck.json`)。`pptfast disassemble <ir.json> -o <dir>` 做反向操作(有据可查的有损转换——`beat`/`focus` 这类只属于 spec 的字段在 IR 里没有对应位置,无法还原)。`render`/`validate`/`preview` 也都能直接接受一个目录,会先在内存里 assemble 一遍。
157
+
158
+ Deck 项目目录可以用裸名代替路径引用——`pptfast render my-deck -o out.pptx` 在本地找不到同名文件或目录时,会到 `$PPTFAST_HOME/decks` 下找 `my-deck`(`$PPTFAST_HOME` 缺省是 `~/.pptfast`)。所有 deck 默认值按四层优先级解析,从高到低:CLI flag > 项目级 `pptfast.config.json` > 用户级 `~/.pptfast/config.json` > deck 自身的值。两个配置层都可以设置 `decksDir` 来重定向裸名的解析位置——项目层的值相对该配置文件自身所在目录解析(给想把 deck 项目入库的团队用),用户层的值相对 `$PPTFAST_HOME` 解析,两者都设置时项目层优先。
159
+
160
+ ## 审查
161
+
162
+ `pptfast audit <target> [--json]` 会离屏渲染每一页,跑一遍确定性几何审查——不靠 LLM 截图目检,零方差。六类检查:**溢出**(文字超出自己的框或列)、**越界**(超出页面边缘)、**低对比度**(文字与其所在背景的 WCAG 相对亮度对比度不达标)、**重叠**(两个组件的区域大面积相交)、**内容截断**(渲染器为适配版面用省略号截断了文字)、**内容丢失**(一个「+N 更多」标记——一张卡片列表或整个组件放不下被隐藏了)。这是建议性工具,不是硬门——`validate` 已经拦住了结构非法或密度超标的 deck,audit 抓的是一份合法 deck 在渲染层仍可能出现的问题(作者选了一个贴近背景色的文字颜色、两个组件的内容恰好撞在一起)。
163
+
164
+ 加上 `--pixels`(仅 Node,需要可选依赖 `sharp`)还能抓住 SVG 层检查天生看不到的一种情况——文字直接压在一张没有遮罩的照片背景上,做法是把该页光栅化成真实像素再采样。每次结果都会带一个 `checks` 字段(`{ svg: "completed", pixels: "not-requested" | "completed" }`),让调用方随时能分清「没查」和「查了没问题」,不会把未检查项误读成通过(像素层自身的跨平台一致性说明见 `docs/contrast-system.md`)。
165
+
166
+ 建议在所有页面填完之后跑一遍,`target` 形式同 `validate`/`render`(文件、deck 项目目录或裸名)。人读输出按页分组报错(`page 3 (p-kpi): [low-contrast] …`,每条消息都带修正建议),末尾附一行汇总。`--json` 输出完整的机器可读报告。exit code 本身即可供 agent 判断:干净是 `0`,发现问题是 `1`——按报错修那一页,再单独重跑一次 `audit` 即可,不必重新渲染。被跳过的占位页会在汇总里注明,和别处「不算缺失、只是还没写」的处理方式一致。
167
+
168
+ ```bash
169
+ pptfast audit examples/basic.json
170
+ # → audited 5 pages, 0 skipped, 0 findings
171
+ ```
172
+
77
173
  ## 面向 AI agent
78
174
 
79
- 推荐给 agent 的生成回路:先读 `pptfast schema` 学词汇表,写出 IR JSON,跑 `pptfast validate` 并根据报错自纠(错误信息带页码和可直接照抄的修正方式,目的就是让这个回路不必依赖人工介入),再执行 `pptfast render`。`pptfast preview` 能让 agent 在正式渲染前先看一遍 SVG,自查版式是否合理。把这套回路封装成 skill Claude Code plugin 计划在 v0.2 落地(见 Roadmap)。
175
+ 推荐给 agent 的生成回路:先读 `pptfast schema` 学词汇表,写出 IR JSON,跑 `pptfast validate` 并根据报错自纠(错误信息带页码和可直接照抄的修正方式,目的就是让这个回路不必依赖人工介入),再跑 `pptfast audit`——同样是可直接照抄的修正反馈,只是针对一份*合法* deck 在渲染层仍可能出现的问题(溢出、低对比度、重叠——exit code 本身就说明干不干净),最后执行 `pptfast render`。`pptfast preview` 能让 agent 在正式渲染前先看一遍 SVG,自查版式是否合理。加上 `--html` 还会额外写出一个自包含的 `preview.html`,供人工审查(键盘翻页、占位页角标——远程 URL 的图片资产仍是远程链接,这是自包含性上唯一的缺口)。当所有页面都已填写时,这份 `preview.html` 还会叠加同一份 `audit` 检查结果(每页一个数量角标,加一个可点击跳转的 findings 面板),让人工审查者不必打开终端就能看到问题——如果 deck 里还有占位页,则显示一行「audit 已跳过」的提示代替。审查者可以直接在 `preview.html` 里给每页写自由文本批注,并导出为 `revision-request.json`(浏览器 Blob 下载,不联网也不写文件——preview 始终只读),交给 agent 通过 `pages/*.json` 回填。上文的 Claude Code 插件已把这套回路封装成 skill([`skills/pptfast/SKILL.md`](./skills/pptfast/SKILL.md))。这套回路本身由一个模型无关的内部基准测试(`tests/bench/`,不发布到 npm)机械化验证——固定题库,评估模型跟随该 skill 的表现,细节见 `tests/bench/README.md`。
80
176
 
81
177
  ## 路线图
82
178
 
83
- - **v0.2**——封装该回路的 Claude Code plugin + skills、design token 覆盖(`--tokens`)、`init`/自更新命令。
84
- - **v0.3**——主题定制 skill(品牌色 tokens)、自定义 manifest 插槽、1.0 版本。
85
- - **v0.4**——更丰富的动效(更多入场动画)、Office 真机实测、web playground。
179
+ - **v0.2**——封装该回路的 Claude Code plugin + skill(已落地)、design token 覆盖(`--style`)、`init`/自更新命令。
180
+ - **v0.3**——叙事驱动的轴(strategy/pacing/audience)、显式 layout + component 注册表与加权 seed 选型、deck spec/assemble 工作流、确定性几何审查、自包含 HTML 预览、六阶段 skill(已落地。这几条轴当时叫 `scenario`/`mode`/`delivery`,在下面 v0.4 的词汇重构里改名为 `narrative`/`strategy`/`pacing`,行为不变)。
181
+ - **v0.4**——vocabulary-v4 词汇重构:`scenario`→`narrative`、`mode`→`strategy`、`delivery`→`pacing`、`plan`→`spec`(`deck.plan.json`→`deck.spec.json`)、页面级 `rhythm`→`beat`,新增确定性的 `pptfast migrate` 命令,行为不变(已落地,schema 已冻结,自 0.4.0 起——见上文「IR」一节)。
182
+ - **v0.5+**——主题生态(可分发主题注册表、主题定制 skill、自定义品牌插槽)、更丰富的动效(更多入场动画)、Office 真机实测、web playground、1.0 版本。
86
183
 
87
184
  ## 致谢
88
185