Design¶
This page gives the condensed rationale. The canonical, longer design document
is spec.md in
the repo root — written deliberately as the diff against
wordlive — and the staged
roadmap is in
IMPLEMENTATION.md.
Why pptlive exists¶
There is no good Python library for driving a live Microsoft PowerPoint session. The options today are:
| Library | Target | Mechanism |
|---|---|---|
python-pptx |
.pptx file on disk |
OOXML I/O |
pptlive |
Running POWERPNT.exe |
COM (pywin32) |
File-side libraries can't help when the user has the deck open — PowerPoint
holds the lock, and any change you make on disk is invisible until they close
and re-open. The round-trip (close the deck, let the agent write, re-open to
see it, close again) is slow and error-prone. COM is the only live path. And
raw pywin32 is brutally LLM-hostile: magic integer constants (MsoShapeType,
PpPlaceholderType, XlChartType), untyped late-bound dispatch, modal-dialog
footguns, STA threading rules.
xlwings exists for Excel; wordlive for Word. pptlive is the equivalent for
PowerPoint, with one extra goal: be first-class for LLM tool use, not
retrofitted.
Design principles¶
The four principles, in priority order:
- Politeness first. Default behaviour preserves the user's viewed
slide, shape/text
Selection, and focus. They keep editing — or presenting — alongside your script. Operations that must move the screen say so explicitly (deck.go_to(...), thedeck.showverbs,scope.allow_view_move()). Jumping the active slide is the PowerPoint equivalent of stomping the cursor — and more jarring, because it's a full-screen change. - Semantic anchors over
Selection. Operations target hierarchical named handles —ph:S:KIND,shape:S:N,shapeid:S:ID,para:S:N:P,cell:S:N:R:C,notes:S,comments:S— not the live selection. Anchors are stable enough to address in JSON and visible to an LLM as strings; the selection is neither. - Atomic undo. Every
deck.edit()block fences a single undo entry viaStartNewUndoEntry, so one Ctrl-Z reverts the whole intent — even though PowerPoint has no Word-styleUndoRecord. See Concepts. - Structured I/O. Reads return dataclasses / dicts; the CLI emits one JSON object per invocation; exit codes are deterministic. No string scraping anywhere. See the Errors page for the contract.
Underlying all four: an escape hatch. Every wrapper exposes .com. When
pptlive doesn't cover something, drop to raw COM rather than giving up.
The three things PowerPoint changes vs. Word¶
pptlive copies wordlive's structure almost verbatim. Three places where PowerPoint's object model forced a divergence:
- Atomic undo, by a different mechanism. Word brackets a block with
Application.UndoRecord. PowerPoint has no such bracket — but it groups consecutive in-session COM edits into one undo entry by default, andStartNewUndoEntry()is a verified boundary primitive. Soedit()fences on entry and the block is one Ctrl-Z. (One of three spec assumptions a live spike corrected.) - PowerPoint must be visible.
Application.Visible = Falseraises in most builds (verified: "Hiding the application window is not allowed"). Soconnect()has no hidden mode — politeness is about not moving the user's view, not about working unseen. - Anchors are hierarchical, not a global offset. There is no
document-wide character stream and no deck-wide
range:. Addressing is slide → shape → paragraph, slide-index first. z-order drifts as shapes are added/removed, soshape:S:Nis always resolved live and listings emit a stableShape.Id,Shape.Name, andalt_textfor re-identification.
What's out of scope¶
- Cross-platform support. COM is Windows-only. We don't pretend otherwise.
- Cloud co-authoring / PowerPoint on the web. Microsoft Graph is a different stack and a different problem.
- Full PowerPoint object-model coverage. Anything we don't cover is one
.comaccess away. - Replacing
python-pptx. Different surface, different problem. Generate a deck from scratch in a batch job? Usepython-pptx. Edit the deck the user is looking at? Use pptlive. - Designer / AI layout suggestions. Out of scope.
Architecture at a glance¶
your code / LLM
│
▼
┌───────────────────────────────────────────────────┐
│ pptlive public API │
│ attach / connect → PowerPoint │
│ │ │
│ ▼ │
│ Presentation │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ slides anchor_by_id show │
│ │ │ │
│ ▼ ▼ │
│ Slide → shapes → Shape ── is-a ──┐ │
│ │ ▼ │
│ ▼ Anchor │
│ Table / Chart (text, set_text,
│ format_text, …) │
└───────────────────────────────────────────────────┘
│
▼
EditScope (StartNewUndoEntry + SelectionSnapshot)
│
▼
pywin32 → PowerPoint.Application (COM, STA-threaded)
The library is intentionally flat: ~15 modules, no plugin system, no hierarchy
beyond PowerPoint → Presentation → Slide → Shape → Anchor. _com.py is the
only module that touches pywin32, which is what makes the whole surface
unit-testable against a fake-COM fixture with no PowerPoint installed.
What comes next¶
The roadmap lives in
IMPLEMENTATION.md.
The current release covers the politeness / anchors / EditScope core, the
LLM-first CLI, the slide lifecycle, shapes & geometry (fill / border, z-order
restacking, the delete-proof shapeid:S:ID anchor), text structure
(paragraphs, font formatting, bullets), tables (cells as cell:S:N:R:C
anchors), slide render + live selection (here:), pictures (alt text + per-shape
export), charts (embedded-Excel data) and SmartArt diagrams (node-tree read /
set_nodes) — both with coarse recolor_text for dark-theme labels, deck-wide
styling via the theme and master (palette, heading/body fonts, master text
styles, background), fuzzy find / replace across shapes / cells /
notes, threaded review comments, the whole-deck snapshot
vision read, explicit save / save_as / PDF export, and live
slide-show control (deck.show). It also ships an optional MCP server
for Claude Desktop and other MCP clients. Deferred: event sinks
(SlideShowNextSlide, WindowSelectionChange), an async wrapper, transitions &
animations, and custom slide-layout authoring (creating new layouts — distinct
from the theme/master styling that already ships).
Full design document¶
For the unabridged version — the original motivation, the error taxonomy in
more detail, the rejected alternatives, the resolved spikes, and the open
questions — see
spec.md in the
repo root.