| name | edit-presentation |
| description | Use whenever the user wants to read, write, or add slides to the PowerPoint deck open in the side pane. Routes user intent to the single right office_ppt_* tool so the model picks one batch call instead of chaining many low-level slide writes. |
| license | MIT |
| metadata | {"category":"office-routing","primary-interface":"office_ppt_* tools"} |
| triggers | ["PowerPoint","slide","deck","presentation","幻灯片","演示","PPT","bullets","大纲"] |
Edit Presentation
This PowerPoint deck is open in the side pane. You have office_ppt_* tools that read and write it directly. Route the user's intent to one well-chosen tool before reaching for many.
Intent → tool routing
| User says | First tool |
|---|
| "What's in this deck / 大纲" | office_ppt_get_outline (slide-by-slide title + bullets, read-only) |
| "Read slide N / what's on this slide" | office_ppt_get_slide_text({ slideIndex }) (1-based: slide 1 = first slide); call get_outline first if you don't already know the index |
| "What's selected / 选中的" | office_get_selection (cross-host, text only) |
| "Write me a deck about X / 加 N 页关于 Y" | office_ppt_build_deck (single call; pass all slides at once) |
| "Add ONE slide about Z" | office_ppt_build_deck with a one-element slides array |
| "Replace this whole deck with..." | office_ppt_build_deck({ position: "replace-deck", confirmReplaceDeck: true }) |
| "Change the title of slide N" | office_ppt_set_slide_title |
| "Add a subtitle / add bullet points to slide N / 给这页加要点" | office_ppt_set_slide_content({ slideIndex, subtitle?, bullets? }) — writes into the existing slide's subtitle/body placeholders; use this, NOT build_deck (which would make a new slide) |
| "Delete slide N / 删除第 N 页 / drop slides 2 and 5" | office_ppt_delete_slides({ indices: [N, ...] }) — indices are 1-based (slide 1 = first slide); validated against deck size; sorted descending so multi-deletes are safe |
| "Delete this shape / 删除选中的形状 / remove the selected box(es)" | office_ppt_delete_selected_shapes (no args — operates on the user's current selection; the chip above the composer shows what will be deleted) |
| "Insert this image / 插入图片" | office_ppt_insert_image (lands on the active slide — caller should navigate first) |
| "Check the slides / are there layout problems / 检查排版" | office_ppt_verify_slides (read-only: overlap, off-slide, low-contrast) |
Choosing a layout + filling it (office_ppt_build_deck)
build_deck lands content in the chosen master layout's real placeholders (title / subtitle / body), not free-floating text boxes. Pick the layout per slide and supply only the fields that layout uses:
| Layout | Use for | Fields it fills |
|---|
title | cover / opening slide | title + subtitle |
title-content | a normal content slide (the default) | title + content[] (bullets) |
section | a section divider between topics | title |
two-content | title with two parallel bodies | title + content[] (lands in the first body) |
title-only | a big headline, or a slide you fill with a table | title |
blank | no placeholders at all | text boxes only |
table ({ headers: string[], rows: string[][] }) builds a native PowerPoint table on the slide and can be combined with title/content.
Read before a big write. Before building onto a non-empty deck, call office_ppt_get_outline (or rely on the <doc_state> already in context — it lists slide count, the layout catalog, and per-slide layout) so you append in the right place and reuse the deck's real layout names.
Worked example — one call, three slides (cover + bullets + table)
{
"slides": [
{ "layout": "title", "title": "Q3 Review", "subtitle": "Product & Growth" },
{ "layout": "title-content", "title": "Highlights",
"content": ["Revenue up 18% QoQ", "Two new enterprise logos", "Churn down to 1.2%"] },
{ "layout": "title-only", "title": "By region",
"table": { "headers": ["Region", "Q2", "Q3"
Design rules — decks must look professional, not machine-generated
Apply these to every build_deck call. They are what separates a usable deck from an obviously auto-generated one.
Bullet text is plain text. Never prefix glyphs (•, -, ▸, ①, emoji) or manual numbers in content[] strings — the placeholder renders its own native bullets, and baked-in glyphs double them ("• • text"). Emoji are not icons; leave them out entirely.
One idea per slide, at most 4–5 bullets, each under ~12 words. If a topic needs more, split it into two slides instead of cramming. A bullet is a cue, not a paragraph — no trailing periods, no full sentences when a fragment works.
Titles are short assertions, not labels. "Churn fell to 1.2% in Q3" beats "Q3 Churn Data". Keep titles to one line.
Vary the layout by content type — a deck that is all title-content reads as machine output:
| Content is... | Use |
|---|
| the opening | title (cover: title + subtitle) — always start with one |
| a topic change | section divider — insert one between major parts |
| a process / steps | content[] written as "Step 1: ..." lines, or a 2-column table (Step / Action) |
| a comparison (A vs B, before/after) | two-content, or a table with one column per option |
| numbers / metrics / rankings | a table — never bullets full of figures |
| one key number or takeaway | title-only with the number/claim as the title |
Structure the whole deck before the call: cover → (section → content slides)... → closing takeaway. Compose the full slides array with this shape rather than emitting a flat run of bullet slides.
Never invent data. If the user didn't supply figures, write the claim without numbers or ask — a fabricated statistic on a slide is worse than a blank.
Visual design is your job, ON BY DEFAULT — the user reviews content, not styling
Whenever you create or rewrite a deck, make it look designed by default. The user should NOT have to ask for "a dark theme" or "make it pretty" — assume they want a good-looking result and that their job is to review the content, not to art-direct. So on essentially every build_deck that authors real slides, pass a deck-wide theme (you pick colors that fit the topic) AND build each slide with a design type (below) instead of plain placeholders.
Only skip the design and stay plain when: the user explicitly asks for "plain / simple / 简单 / 纯文本 / 不要花哨", OR you are appending onto an existing deck that has its own look (see next). Otherwise: design it.
Adding to an existing deck — it MATCHES automatically, just don't pass a theme
When the user adds a slide or two to a deck that already has slides, the new slide should match the existing deck — and the code does this for you:
- Do NOT pass a
theme when appending. With no theme, an appended design slide automatically samples the existing deck's background, accent and fonts and uses them, so it matches. Passing a theme overrides that and risks a clash — only pass one when creating a NEW deck or restyling the whole deck.
- So on an append you can still use
design types (they adopt the deck's palette) OR plain placeholder layouts (which inherit the master theme directly). Both match.
- Reserve a
theme + a full designed build for a new deck, or an explicit restyle the whole deck (position: "replace-deck", rebuild every slide).
- If the deck's colors can't be read (unusual master), an appended design slide falls back to the default palette — on a heavily-branded deck, plain placeholder layouts are the safest exact match when in doubt.
Use design slide types — the reliable way to look good
Every slide can take a design type. The code then composes a polished, consistent slide from your content — it owns position, size, spacing and color, so you just choose the right type and write the content. This is the primary way to build a good-looking deck — prefer it over plain bullet slides and over hand-placing elements.
design | Use for | You supply |
|---|
cover | the opening slide | title, subtitle |
section | a divider between parts | title |
bullets | key points (a designed bullet slide) | title, content[] (short lines) |
stats | 2–4 headline numbers | title, items[] = { value, heading } |
process | a sequence of steps | title, items[] = { heading, text } |
compare | 2–3 options side by side | title, items[] = { heading, text } |
Match the type to the content: numbers → stats, steps/pipeline → process, A-vs-B → compare, a topic change → section, key takeaways → bullets, the first slide → cover. A deck that varies these reads as designed; a deck of only bullets does not.
Do ALL of this for a designed deck:
- One
build_deck call, one theme, every slide a design type — so the whole deck is consistent. If redesigning an existing deck, use position: "replace-deck" (confirmReplaceDeck: true) and rebuild every slide. Never leave some slides designed and others plain — mixed styling is the #1 thing that looks broken.
- Vary the design types — open with
cover, use section dividers, put numbers in stats, steps in process, and keep bullets short. Don't make every slide bullets.
- Keep text short —
bullets lines and items[].text should be one short line each (≤ ~10 Chinese chars / ~8 English words). The code shrinks-to-fit, but short content always looks better.
Worked example — a designed deck (cover + stats + process + compare)
{
"theme": { "background": "#0E1B2C", "accent": "#3EC5B7", "body": "#E6ECF5" },
"slides": [
{ "design": "cover", "title": "CAR-T 细胞疗法", "subtitle": "嵌合抗原受体 T 细胞 · 肿瘤免疫治疗的新纪元" },
{ "design": "stats", "title": "临床影响",
"items": [ { "value": "83%", "heading": "完全缓解率" },
{ "value":
If a slide needs a layout no design type covers, fall back to hand-placed elements (see Freeform below).
{
"slides": [ ... ],
"theme": {
"background": "#0F2A43",
"accent": "#4C8DFF",
"body": "#E6ECF5",
"headingFont": "微软雅黑",
"bodyFont": "微软雅黑"
}
}
Rules:
- A background alone looks bare — always pair it with an
accent. The accent colors the slide titles and draws a vertical accent band down the left edge; that visual anchor is what makes the deck look designed rather than "a bullet list on a colored background". The minimum designed theme is background + accent + body.
- To recolor text you MUST set
background. Text colors are applied only when a background is set, so contrast can be guaranteed. A code-level readability guard auto-fixes any text/accent color too faint on the background — but pick colors that already contrast (light on dark, dark on light) and an accent that clearly pops.
- Restraint wins. One background, one accent, a coordinated body color, at most one heading font + one body font. Never a rainbow — that reads as amateur.
- Fit the topic. Calm blues/teals for medical & science, deep navy for finance/consulting, an electric accent for tech, warm neutrals for humanities. Invent a palette that suits the subject.
- Vary layouts too. Color is only half of "designed" — still use
section dividers, tables for data, and title-only big-number slides so the deck isn't the same bullet layout every slide (see the design rules above).
- Fonts are safe without a background (headingFont/bodyFont apply on their own). For Chinese decks,
微软雅黑 or 思源黑体 are reliable.
- Design is the default, not an add-on. Theme every real deck you author; only stay plain when the user explicitly asked for plain, or you're appending to an existing themed deck (then match it).
Example palettes (background · accent · body — starting points, adapt or replace to fit the topic, not required presets):
| Feel | background | accent | body |
|---|
| Dark tech / keynote | #0F2A43 | #4C8DFF | #E6ECF5 |
| Midnight executive | #1E2761 | #7C9CF5 | #F2F5FF |
| Warm academic (light) | #F4F1EA | #1F3A68 | #33373D |
| Clean clinical (light) | #FFFFFF | #0B6E6E | #2B2F33 |
Freeform slides — design element-by-element (elements)
For a slide that needs a real designed layout (a hero stat, a side-panel, a split composition) the theme colors alone aren't enough. Give the slide an elements array instead: explicitly-positioned text boxes and color blocks you compose yourself, like a designer using a canvas. When a slide has elements, its title/subtitle/content/table are ignored — you own the whole slide.
- Coordinates are percentages of the slide, 0–100 (
x left, y top, w width, h height). They scale to any slide size.
- Array order is z-order. Paint background/
box elements first, then the text on top.
kind:"box" = a filled rectangle (fill). kind:"text" = a text box (text, fontSize pt, color, bold, align, font, optional fill).
- You are responsible for the layout — keep elements inside 0–100, leave margins (~6% padding), don't overlap text boxes, and keep text readable against whatever box sits behind it. Run
office_ppt_verify_slides after.
- Mix freely: some slides freeform, others plain
title/content. Use freeform where it earns its keep (covers, section dividers, stat slides), not for every bullet list.
Worked example — a hero-stat slide (left color panel + big number + caption)
{
"layout": "blank",
"elements": [
{ "kind": "box", "x": 0, "y": 0, "w": 40, "h": 100, "fill": "#0F2A43" },
{ "kind": "text", "x": 6, "y": 34, "w": 30, "h": 30,
"text": "83%", "fontSize":
This is the tool that gets close to a hand-designed slide. Quality depends on the model doing the layout well — think about alignment, spacing, and hierarchy the way a designer would.
Verify after building
After a multi-slide build_deck, call office_ppt_verify_slides (read-only, no args = whole deck) to catch shapes that overlap, run off the slide, or have low-contrast text. It checks geometry and color only — it does not confirm the wording or that the slides match the user's intent, so still rely on <doc_state> / get_slide_text for content review. Report any warnings to the user instead of silently re-editing.
Anti-patterns (do NOT do these)
-
Calling build_deck once per slide. It's a batch tool — pass all slides in a single slides array. Looping it creates one slide per call and wastes round-trips.
-
Reading then re-creating slides to change a title. Use office_ppt_set_slide_title directly.
-
Probing outline before writing into an empty deck. A blank deck only has one default slide; you can build_deck immediately.
-
Re-pasting the full slide content in chat after build_deck. The user sees the deck. Reply with at most ~3 lines confirming what landed (e.g. "Created 5 slides covering A, B, C.") and stop.
-
Prefixing bullet glyphs or numbers in content[]. The placeholder already renders native bullets — "• Revenue up 18%" shows as "• • Revenue up 18%". Pass "Revenue up 18%".
Destructive operation needs explicit confirmation
office_ppt_build_deck({ position: "replace-deck" }) deletes every existing slide before inserting the new ones. The tool refuses unless you also pass confirmReplaceDeck: true. Only set that flag when the user has clearly said "replace / rebuild / 重写整篇 / 替换 / overwrite". For "write / draft / add / append / make / create / 写 / 写一个 / 加 / 起草 / 做一份", use position: "end" (default) — even when the user says "write me a deck" on a non-empty deck. "写" is a creation verb, not a replacement verb. The user has to explicitly say "replace" or "重写整篇" to trigger replace-deck.
Misclassification examples (do NOT use replace-deck for these):
- "写一个 3 页的产品介绍幻灯片" →
position: "end" ✓ (write/append)
- "make a deck about onboarding" →
position: "end" ✓ (create/append)
- "加 5 页关于 Q3 计划" →
position: "end" ✓ (add)
- "draft 10 slides on the launch" →
position: "end" ✓ (draft)
Correct uses of replace-deck (only when the user explicitly said so):
- "重写整个 deck 为产品介绍" →
position: "replace-deck", confirmReplaceDeck: true
- "replace this whole deck with the team-building agenda" → replace-deck
- "用一份关于 X 的 5 页幻灯片替换整个文档" → replace-deck
API caveats (v1)
- Slide indices are 1-based, not 0-based.
office_ppt_get_outline returns index: 1 for the first slide, index: 2 for the second, etc. Every tool that takes a slideIndex (or an indices array — get_slide_text, set_slide_title, delete_slides) expects the same 1-based numbering. This matches the user's mental model (PowerPoint's navigator labels slides 1, 2, 3) and the chip above the chat composer ("Slide 4 selected"). Errors include the valid 1..N range so off-by-one mistakes are caught at the boundary.
- Title detection is placeholder-aware.
get_outline / get_slide_text / set_slide_title target the slide's real title placeholder (PowerPointApi 1.8), falling back to the first non-empty text shape on older clients. get_slide_text tags every shape with its placeholder role (title / subtitle / body, or text for a free text box) — use those roles to pick the right shape before a write.
insert_image targets the active slide. PowerPoint.js does not expose a slide-targeted addImage in stable types, so the legacy common API is used. To insert on slide N, navigate to that slide before calling the tool.
- Slide reorder is unavailable.
position: "start" currently appends like "end" because there is no stable reorder API across PowerPoint clients. The user can drag in the slide navigator if order matters.
delete_slides is destructive and has no in-tool undo. Only Word's Ctrl/Cmd-Z restores deleted slides. Confirm with the user before deleting more than one slide unless they explicitly said so.
Asking the user
If the request has a genuine fork you cannot settle from context — two or more plausible directions that would change what you build — call office_ask_user once (pass options for a choice, multiSelect to let them pick several). Use it sparingly: not as a reflex, not to confirm a default you can reasonably pick, and never to ask permission for an edit (the approval gate handles that). If the user skips, proceed with the most sensible default instead of asking again.
When in doubt
Pick the single tool whose name most directly matches the user's verb:
- "outline" / "list slides" / "大纲" →
office_ppt_get_outline
- "read slide" / "show slide N" →
office_ppt_get_slide_text
- "write" / "draft" / "make a deck" / "add slide(s)" →
office_ppt_build_deck
- "title" / "headline" / "标题" →
office_ppt_set_slide_title
- "delete" / "drop" / "remove" / "删除" →
office_ppt_delete_slides (whole slides) or office_ppt_delete_selected_shapes (the user's selected shape(s); pick this when the request refers to "this", "the selected", or matches the chip's "· N shapes")
- "image" / "picture" / "图片" →
office_ppt_insert_image
- "verify" / "check layout" / "overlap" / "检查" →
office_ppt_verify_slides
One well-chosen tool beats five guesses.