
Deck Builder
- 2 installs
- 9 repo stars
- Updated June 11, 2026
- timescale/marketing-skills
Turns any document into a branded slide deck, generating a .pptx via python-pptx in Claude Code or structured slide-by-slide markdown in Cowork.
About
Reads source material such as blog posts, briefs, white papers, or notes and produces a structured branded presentation, generating a .pptx file programmatically in Claude Code or template-mapped slide markdown with speaker notes in Cowork. A marketer uses it to turn a document into slides or build a pitch deck.
- Uses the team's exported Google Slides template via python-pptx
- No Fly List check before generating content when Tiger Den is available
Deck Builder by the numbers
- 2 all-time installs (skills.sh)
- Ranked #548 of 688 Office & Documents skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/timescale/marketing-skills --skill deck-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 9 |
| Last updated | June 11, 2026 |
| Repository | timescale/marketing-skills ↗ |
What it does
Turns any document into a branded slide deck, generating a .pptx via python-pptx in Claude Code or structured slide-by-slide markdown in Cowork.
Files
Deck Builder
Turn any document into a branded slide deck. The skill reads source material, selects a deck structure, writes slide-by-slide content with speaker notes, and either generates a .pptx file (Claude Code) or produces structured markdown mapped to your template layouts (Cowork).
When to use this skill
- Turning a blog post, white paper, brief, or case study into a presentation
- Creating a pitch deck, conference talk, or internal review deck from scratch or from notes
- When someone says "make me a deck about X" or "turn this into slides"
- When someone needs a presentation that follows the team's branded Google Slides template
When NOT to use this skill
- Creating HTML reports or dashboards (use ghost-paper)
- Writing long-form content like blog posts or articles (use brand-voice-writer)
- Reviewing existing slide content for brand consistency (use content-reviewer)
Step 0: No Fly List check
If Tiger Den is available, fetch the No Fly List before generating any content:
get_marketing_reference(slug: "no-fly-list")If Tiger Den is not available, warn the user: "No Fly List check skipped — Tiger Den is not connected. Verify manually that no restricted customers are referenced in the deck before sharing." Then proceed.
If Tiger Den is available, load the returned names as a hard constraint: never include any No Fly List customer in any slide content — not as named examples, proof points, customer quotes, or case study references. If the source document references a No Fly List customer, omit those references from all outputs.
Instructions
1. Load context
Read the local reference files in this skill's references/ directory:
- Slide Writing Guide — presentation-specific writing rules, density limits, layout decision tree
- Deck Structures — skeleton structures for common deck types
Template layout map: Read Template Layout Map for the team's slide template layouts and placeholder details. If this file contains only the example/placeholder content (not real template data), the template has not been set up yet. Jump to the Template setup section at the end of this skill before proceeding.
2. Read the source document
Read the full input document. If the user pasted content, read it in full. If they provided a URL or file path, fetch and read it.
Analyze the source and identify:
- Document type: blog post, white paper, brief, case study, meeting notes, raw ideas
- Core argument/thesis: the single main point
- Key supporting points: 3-7 major ideas that support the thesis
- Data points and metrics: numbers, benchmarks, comparisons
- Quotes: customer quotes, expert opinions, notable statements
- Logical sections: natural content groupings
Summarize the source in 2-3 sentences and present it to the user for confirmation before proceeding. This prevents building a deck from a misread document.
3. Choose a deck structure and build the deck plan
3a. Audit content types. Before choosing a structure or assigning layouts, classify every distinct piece of information from the source into content types:
- Metric — a number worth highlighting (revenue, speed, percentage)
- Comparison — A vs B, before/after, us vs them
- List — capabilities, features, steps in a process
- Quote — customer quote, testimonial, notable statement
- Process/Timeline — sequential phases, roadmap stages
- Visual — architecture diagram, screenshot, workflow
- Narrative — context-setting, argument, analysis
Present the audit as a short inventory. This inventory drives layout selection in 3c — each content type maps to a different layout via the decision tree. If most items land in "List," challenge yourself: can any be reframed as metrics, comparisons, or timelines? A bullet that says "10-20x faster ingest" is a metric, not a list item. A set of 4-6 short features is a grid, not a bullet list. Scan for visual patterns too. As you audit, flag any set of items that forms a recognizable visual pattern: 3 numbered blurbs or ranked items with no named labels (→ card groups), an ordered scale or phased progression (→ 5-step progression), comparison (→ two-column or comparison layout), process/timeline (→ timeline). Visual patterns override the default "list" classification — a set of 3 ranked takeaways is cards, not bullets. Named-label content (e.g., "Scalability / Reliability / Performance") stays in bullets or comparison layouts until arbitrary-label cards are supported in a future helper update.
3b. Choose a structure. Based on the document type and the user's stated purpose, select a structure from the Deck Structures reference.
- If the user specifies a purpose ("this is for a conference talk"), use that
- If they do not, infer from the document type and ask for confirmation
- If no existing structure fits, propose a custom one
3c. Build the deck plan. For each slide in the chosen structure, assign a layout and generation strategy. Use the content audit from 3a and the layout decision tree in the slide writing guide to match content type to layout, then look up the strategy and clone source in the Quick Reference table in the template layout map.
Layout diversity rule: No single layout may be used for more than 30% of the deck's slides (rounded up). No more than 2 consecutive slides may use the same layout. If the plan violates either rule, reassign layouts using the content audit — a "list" slide with a standout metric should become a big number slide; a list of 4-6 items should become a grid; a before/after comparison should become a two-column or comparison slide.
Present the deck plan as a table for user approval:
| # | Title | Layout | Strategy | Clone source | Notes summary |
|---|---|---|---|---|---|
| 1 | [Headline title] | Layout 1 (dark title) | Placeholder | — | Opening remarks... |
| 2 | [Headline title] | Layout 9 (bullets) | Clone | idx 22 | Problem context... |
| 3 | [Headline title] | Layout 7 (grid) | Clone | idx 5 | Feature overview... |
Content overflow: If a slide's source content exceeds the density limits in the slide writing guide (e.g., 8 bullets when the max is 5, or 7 grid items when the max is 6), split across multiple slides. Note split slides as "4a" and "4b" in the plan. Split at natural thematic breaks, not arbitrarily.
Grid categories (Layout 7): The feature grid's column headers are category labels, not individual item names. Group items into exactly 2 categories. Choose categories by the strongest shared attribute among subsets: functional area (writing vs. analysis), workflow stage (create vs. review), or user benefit (speed vs. quality). State the proposed grouping in the deck plan so the user can adjust.
Before presenting the deck plan, verify it passes the layout diversity rule. If it doesn't, fix it first.
Wait for user approval of the deck plan before proceeding to step 4.
4. Write the slides
For each slide in the approved structure:
1. Use the layout from the deck plan — already assigned in step 3b 2. Write the title — concise, headline-style, under the character limit for that layout 3. Write the body content — follow the density rules from the slide writing guide. Respect the placeholder constraints from the template layout map (max characters, max bullets) 4. Write speaker notes — the full argument and supporting detail goes here. Speaker notes hold everything the slide doesn't. Include data sources, context, and transition cues 5. Apply voice rules — follow the voice rules in the slide writing guide: WABL aggressively, no em dashes, active voice, one message per slide, no AI filler
Speaker notes are mandatory on every slide, including title slides and section dividers. Notes should be 50-150 words per the slide writing guide. For title/closing slides, write the opening or closing remarks. For content slides, write the full argument behind the headline with data sources and transition cues. Never leave notes blank. If the source material does not provide enough detail, synthesize appropriate talking points from the document's thesis and supporting arguments.
Image handling: Cloned slides carry over all images from the source template slide (logos, screenshots, product icons). In the .pptx output, leave these as template defaults. After generation, report which slides contain template images that may need replacement. In Cowork markdown output, note images as [IMAGE: description of what goes here].
5. Self-check
Before generating output, review all slides against:
- [ ] Every slide has a clear single message
- [ ] No slide exceeds the density limits from the slide writing guide
- [ ] No em dashes used anywhere; active voice throughout
- [ ] Every slide has speaker notes (50-150 words, never blank)
- [ ] The narrative arc flows logically from slide to slide
- [ ] Layout assignments match the content type (data -> big number, comparison -> two-column, etc.)
- [ ] No single layout used on more than 30% of slides
- [ ] No more than 2 consecutive slides share the same layout
- [ ] Content that exceeds layout capacity has been split across slides
- [ ] Slides with cloned template images are noted for manual replacement
6. Generate output
Detect the runtime and generate the appropriate output.
Claude Code: Generate .pptx
Write a single Python generation script that does all of the following. Copy the helper functions from the "Programmatic Generation Strategy" section in the template layout map and define them inline at the top of the script. Do not import them from an external file.
1. Install python-pptx if not already available:
pip install python-pptx 2>/dev/null || pip3 install python-pptx 2>/dev/null2. Locate the template. (Claude Code only — Cowork skips this step.) Check for deck-template.pptx in the skill directory (skills/deck-builder/deck-template.pptx relative to plugin root). If not found, stop and tell the user: "The deck template file is missing. Download deck-template.pptx from the team's Google Drive (Tiger Data Templates folder) and save it to: plugins/tiger-marketing-skills/skills/deck-builder/deck-template.pptx". Do not proceed until the file is in place.
3. Load the template and record the original slide count:
prs = Presentation(template_path)
original_count = len(prs.slides)
template_slides = list(prs.slides) # snapshot before appending4. Define helper functions inline: duplicate_slide, replace_shape_text, replace_bullet_list, find_body_shape, find_text_shapes_by_position, delete_shape, delete_slide, trim_table, set_cell_text. Copy these verbatim from the template layout map reference.
5. Generate each slide from the deck plan — clone every slide, including placeholder layouts:
duplicate_slide(prs, template_slides[clone_source_idx])→ find shapes by role using the Clone Source Slides section → replace text withreplace_shape_text()orreplace_bullet_list()- For placeholder shapes:
replace_shape_text(slide.placeholders[idx], "text")— never useplaceholder.text =directly, as this strips bodyPr, paragraph, and run formatting - Set speaker notes on every slide:
slide.notes_slide.notes_text_frame.text = notes
6. Delete original template slides by iterating in reverse from original_count - 1 down to 0: delete_slide(prs, i)
7. Save and report: File path, total slide count, any slides that fell back to speaker-notes mode, and which slides contain template images that may need manual replacement.
Fallback: If cloning fails for any slide, create a slide from the layout and put body content in speaker notes with [REPLACE: Body] markers. Report which slides need manual touch-up.
Cowork: Structured markdown output
Produce slide-by-slide markdown in the conversation. Format each slide as:
---
## Slide [N]: [Title]
**Layout:** [Cowork description from template layout map]
**Title:** [Title text]
**Body:**
[Body content — bullets, paragraphs, or key metric as appropriate for the layout]
**Speaker notes:**
[Full speaker notes]
---For the Layout field: use the Cowork description from the layout's entry in the template layout map — a short visual description of what the layout looks like (e.g., "Dark centered title slide" or "Title + content area"). Do not use layout index numbers or slide numbers.
Include a preamble at the top:
How to build this deck in Google Slides: Open your template. For each slide below, right-click a slide in the panel → Change layout and pick the layout matching the Layout field. Or find a slide in the template that visually matches the description, duplicate it, and replace the text. Paste title and body content directly into each shape. Speaker notes go in the notes panel (View → Show speaker notes).
7. Optional: Tiger Den content enrichment
If the search_content tool is available, search for related existing content (blog posts, case studies, benchmarks) that could strengthen specific slides. Use the published_after parameter to filter to content from the last 18 months. Add relevant links to speaker notes as supporting material.
8. Optional: Voice profiles
If the user mentions who will present the deck and the get_voice_profile tool is available, load that person's voice profile to adjust the speaker notes tone. The slide content itself stays brand-voiced; the voice profile influences speaker notes only.
Template setup (first-time)
If the template layout map has not been configured, guide the user through setup:
What you need
1. The team's Google Slides template — the canonical branded template 2. A .pptx export — File > Download > Microsoft PowerPoint (.pptx) from Google Slides 3. Upload to Google Drive — place the .pptx in the shared references folder as deck-template.pptx
Auto-generate the layout map (Claude Code)
In Claude Code, inspect the .pptx programmatically to auto-generate the template layout map:
from pptx import Presentation
from pptx.util import Inches, Pt
prs = Presentation('/tmp/deck-template.pptx')
for i, layout in enumerate(prs.slide_layouts):
print(f"### Layout {i}: {layout.name}")
print(f"**Placeholders:**")
print(f"| idx | name | type | width | height |")
print(f"|-----|------|------|-------|--------|")
for ph in layout.placeholders:
print(f"| {ph.placeholder_format.idx} | {ph.name} | {ph.placeholder_format.type} | {ph.width} | {ph.height} |")
print()Run this script, review the output, and save it to references/template-layout-map.md in this skill's directory. Add semantic roles and content constraints for each layout based on what you see.
Manual setup (Cowork)
If in Cowork, ask the user to describe each slide layout in their template:
- Layout name (as shown in the slide master)
- What it looks like (title only, title + bullets, two columns, big number, etc.)
- What content goes where
Write the template layout map based on their description.
Dependencies
- Required: Template layout map (
references/template-layout-map.md) — must be configured before first use - Claude Code only:
python-pptxPython package (auto-installed),deck-template.pptxin Google Drive - Optional: Tiger Den MCP for content search and voice profiles
Deck Structures
Skeleton structures for common presentation types. Each structure lists the slide sequence, the role each slide plays in the narrative arc, and which slides are optional.
Select the structure that best matches the user's purpose. Adapt as needed: add or remove slides based on content depth, and adjust the balance between sections.
---
Product Overview (10-12 slides)
Use for: introducing the product to prospects, partner briefings, analyst briefings.
| # | Slide role | Narrative position | Optional? |
|---|---|---|---|
| 1 | Title slide | — | No |
| 2 | Problem statement | Situation | No |
| 3 | Market context / why now | Situation | Yes |
| 4 | Solution overview | Resolution | No |
| 5 | Key capability 1 | Resolution | No |
| 6 | Key capability 2 | Resolution | No |
| 7 | Key capability 3 | Resolution | Yes |
| 8 | Architecture / how it works | Resolution | Yes |
| 9 | Proof points / benchmarks | Proof | No |
| 10 | Customer logos or case study highlight | Proof | No |
| 11 | Pricing / packaging | Action | Yes |
| 12 | CTA / next steps | Action | No |
Tips: Capabilities slides work best when each one leads with a user outcome, not a feature name. The architecture slide is optional for non-technical audiences.
---
Conference Talk (15-20 slides)
Use for: conference presentations, meetup talks, webinar keynotes.
| # | Slide role | Narrative position | Optional? |
|---|---|---|---|
| 1 | Title slide | — | No |
| 2 | Speaker intro | — | Yes |
| 3 | Hook / provocative question | Situation | No |
| 4 | Agenda / roadmap | — | Yes |
| 5-7 | Core section 1 (2-3 slides) | Complication | No |
| 8-10 | Core section 2 (2-3 slides) | Resolution | No |
| 11-13 | Core section 3 (2-3 slides) | Resolution | Yes |
| 14 | Live demo placeholder | Resolution | Yes |
| 15 | Key takeaway | Resolution | No |
| 16 | Q&A slide | — | Yes |
| 17 | Resources / links | Action | No |
Tips: Open with a hook, not an agenda. The audience decides in the first 60 seconds whether to pay attention. Use section dividers between core sections. Demo slides should be a placeholder with talking points in speaker notes. Always end with a resources slide so attendees can follow up.
---
Sales Pitch (8-10 slides)
Use for: sales meetings, prospect demos, executive briefings.
| # | Slide role | Narrative position | Optional? |
|---|---|---|---|
| 1 | Title slide | — | No |
| 2 | Customer pain / challenge | Situation | No |
| 3 | Market trend / why now | Complication | Yes |
| 4 | Product positioning | Resolution | No |
| 5 | Differentiation / why us | Resolution | No |
| 6 | Proof: benchmark or case study | Proof | No |
| 7 | Customer quote | Proof | Yes |
| 8 | Next steps / CTA | Action | No |
Tips: Keep it short. Sales decks that run over 10 slides lose executives. The differentiation slide should be concrete (a comparison table or benchmark), not vague claims. End with a specific next step, not "any questions?"
---
Internal Review / Status Update (8-12 slides)
Use for: team all-hands, executive reviews, quarterly business reviews, project updates.
| # | Slide role | Narrative position | Optional? |
|---|---|---|---|
| 1 | Title slide | — | No |
| 2 | Executive summary / TL;DR | — | No |
| 3-5 | Progress against goals (2-3 slides) | Situation | No |
| 6 | Key metrics dashboard | Situation | No |
| 7 | Wins and highlights | Resolution | Yes |
| 8 | Blockers and risks | Complication | No |
| 9 | Decisions needed | Action | Yes |
| 10 | Next steps / priorities | Action | No |
Tips: Lead with the executive summary so decision-makers get the point immediately. Progress slides should show actuals vs. targets, not just activities. The blockers slide should include proposed solutions, not just problems. If decisions are needed, frame each one as a clear choice with trade-offs.
---
Case Study Presentation (6-8 slides)
Use for: customer success stories, sales enablement, webinar case studies.
| # | Slide role | Narrative position | Optional? |
|---|---|---|---|
| 1 | Title slide (customer name + outcome) | — | No |
| 2 | Customer profile | Situation | No |
| 3 | Challenge / problem | Complication | No |
| 4 | Solution / approach | Resolution | No |
| 5 | Results with metrics | Resolution | No |
| 6 | Customer quote | Proof | No |
| 7 | Lessons learned | Resolution | Yes |
| 8 | CTA | Action | No |
Tips: The title slide should lead with the outcome ("How [Company] reduced query time by 90%"), not just the company name. The results slide needs real numbers. If you don't have metrics, the case study isn't ready for a deck.
---
Custom Structure
If none of the above structures fit, build a custom one. Follow these principles:
1. Open with context (1-2 slides): set the scene, establish why the audience should care 2. Build the argument (3-7 slides): present the core content, one message per slide 3. Prove it (1-2 slides): evidence, data, examples, quotes 4. Close with action (1-2 slides): clear next step or takeaway
Total deck length guidelines:
- 5-minute presentation: 5-8 slides
- 15-minute presentation: 10-15 slides
- 30-minute presentation: 15-25 slides
- Rule of thumb: 1.5-2 minutes per slide
Slide Writing Guide
Rules for writing presentation content. These supplement the brand voice guide's slides section with mechanics specific to projected content.
Density rules
Slides are not documents. Every word must earn its place.
| Layout type | Max words on slide | Max bullets | Max bullet nesting |
|---|---|---|---|
| Title slide | 15 | 0 | 0 |
| Section divider | 10 | 0 | 0 |
| Content slide | 40-60 | 5 | 1 level (no sub-sub-bullets) |
| Two-column | 30 per column | 4 per column | 1 level |
| Big number / stat | 5 (plus the number) | 0 | 0 |
| Image + text | 20-30 | 3 | 0 |
| Quote slide | 30 (the quote) | 0 | 0 |
If content exceeds these limits, split across two slides or move detail to speaker notes.
Bullet point discipline
- Lead with the action or key noun. Not "It is important to note that..." but "Reduce query latency by 90%."
- Parallel structure. All bullets on a slide use the same grammatical form. If one starts with a verb, they all start with verbs.
- Standalone meaning. Each bullet should make sense on its own without needing to read the others.
- No sentences. Bullets are fragments or short phrases, not full sentences with periods. Exception: speaker notes use full sentences.
- One idea per bullet. If a bullet has "and" in it, consider splitting it.
Narrative arc
A deck tells a story. Structure the narrative so each slide builds on the previous one.
Standard arc (situation-complication-resolution): 1. Situation — What is the current state? Set the scene. 2. Complication — What changed or what's the problem? Create tension. 3. Resolution — How do we solve it? Release the tension.
Applied to deck sections:
- Opening slides: establish the situation (context, market, audience pain)
- Middle slides: build the complication (why the status quo fails, what's at stake)
- Closing slides: deliver the resolution (solution, proof, next steps)
Transitions between slides: The title of each slide should logically follow from the previous slide. A reader should be able to read just the titles in sequence and follow the story.
Layout decision tree
Use this to match content type to the right slide layout:
| Content type | Recommended layout | Why |
|---|---|---|
| A single impressive metric | Big number / stat | The number IS the message |
| A vs B comparison | Two-column | Side-by-side makes the contrast visual |
| A list of capabilities or features | Content slide with bullets | Scannable list format |
| 3 numbered blurbs / ranked principles / phased items (ordered, no named labels) | Card groups (clone slide 22) | Numeric badges carry the ordering; equal-weight cards treat each blurb as a peer — better than bullets for emphasis |
| An ordered scale or progression (maturity levels, rollout phases, severity tiers) | 5-step progression (clone slide 40) | Horizontal flow reads as sequence; overlaid descriptions tie each step to context |
| A customer quote or testimonial | Quote slide | Lets the quote breathe |
| A process or sequence of steps | Content slide with numbered list | Sequential numbered format |
| An architecture diagram or visual | Image + text | Image dominates, text supports |
| Transitioning between major sections | Section divider | Signals a shift in topic |
| Key takeaway or conclusion | Title slide (reused) or big number | Emphasis through simplicity |
When in doubt, use fewer elements per slide. A slide with one strong message beats a slide with three competing points.
Speaker notes conventions
Speaker notes serve a different audience than the slide itself. The slide is for the room; the notes are for the presenter.
What goes in speaker notes:
- The full argument behind the slide's headline
- Data sources and citations ("Source: 2025 Stack Overflow survey")
- Transition cues ("After this slide, pause for questions")
- Talking points that expand on the bullets
- Context the presenter needs but the audience doesn't
What stays on the slide:
- The headline (core message)
- Supporting evidence in minimal form (bullets, key numbers)
- Visuals
Speaker notes length: 50-150 words per slide. Enough to guide the presenter, not so much that they read a script.
Title writing
Slide titles are headlines, not labels.
| Weak title (label) | Strong title (headline) |
|---|---|
| "Performance Results" | "Query latency drops 90% after migration" |
| "Our Solution" | "One platform for metrics, events, and analytics" |
| "Customer Feedback" | "93% of teams ship faster with Tiger Data" |
| "Next Steps" | "Start your proof of concept this week" |
A good slide title passes the "so what?" test. If someone reads only the title, they should understand the point of the slide.
Voice rules
Apply these to all slide content and speaker notes:
- WABL: Would Anything of value Be Lost if this were removed? If no, cut it. Slides tolerate zero fluff.
- No em dashes — do not use em dashes (—) anywhere
- Active voice — always prefer active over passive
- One message per slide — each slide has exactly one clear point; competing points go on separate slides
- Lead with the problem — start from the audience's pain, not the product
- No generic AI language — avoid filler phrases that scream "AI wrote this"; every line should sound intentional
- Short, direct — if a bullet can be shorter without losing meaning, make it shorter
Template Layout Map — Tiger Data Google Slides Template
Auto-generated from deck-template.pptx (50 example slides, 16 layouts).Important: How this template works
This Google Slides template uses direct shapes for most content, not formal placeholders. Most layouts only have 1-3 true placeholders (title, subtitle, slide number). However, the template example slides (50 slides in the .pptx) contain all the shapes needed, fully positioned and styled.
Generation strategy: Clone specific template slides and replace text in the cloned shapes rather than creating blank slides from layouts. Shapes are identified by structural role (placeholder index, area, position) rather than by name, because Google Slides exports use opaque shape names that change on re-export.
- Claude Code (.pptx): Clone template slides, replace shape text programmatically, then delete the original template slides.
- Cowork (markdown): Map content to the layout types below. The user applies the right layout in Google Slides and places content into the shapes.
---
Layout 0: Title Slide (dark, left-aligned)
Used in: Slide 3 Role: Title slide variant with left-aligned title and subtitle. Dark background.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Left-aligned, max ~60 chars |
| 1 | Subtitle | subtitle | Below title, max ~120 chars |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Opening slide, closing slide. Choose between Layout 0 (dark, left) and Layout 1 (dark, centered) based on preference. Cowork description: Dark title slide, left-aligned
Layout 1: Title Slide (dark, centered)
Used in: Slide 1 Role: Primary title slide. Title and subtitle centered.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Centered, max ~60 chars |
| 1 | Subtitle | subtitle | Below title, max ~120 chars |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Default opening/closing slide. Most common title layout. Cowork description: Dark centered title slide
Layout 2: Title Slide (light, lower position)
Used in: Slide 2 Role: Title slide variant with title positioned lower on the slide. Light background.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Lower centered, max ~60 chars |
| 1 | Subtitle | subtitle | Below title, max ~120 chars |
Use when: Alternative title slide when you want a lighter look or need to pair with background imagery above the title. Cowork description: Light title slide, centered lower
Layout 3: Title Slide (light, left-aligned, lower)
Used in: Slide 4 Role: Title slide with left-aligned, lower-positioned title. Light background.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Left-aligned, lower, max ~60 chars |
| 1 | Subtitle | subtitle | Below title |
Use when: Alternative title slide for a lighter, left-aligned look. Cowork description: Light title slide, left-aligned lower
Layout 4: Bold Statement (right-aligned)
Used in: Slide 15 Role: Bold statement or quote with right-aligned title.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Right-aligned, large text (50pt), max ~20 chars (1 line) or ~40 chars (2 lines) |
| 1 | Subtitle | subtitle | Right-aligned below title |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Bold statement slide, key quote, or provocative question. Text dominates the right side. Cowork description: Bold statement, large text right-aligned
Layout 5: Flexible Canvas (blank with slide number)
Used in: Slides 20-21, 25-32, 36, 47-48 Role: The most versatile layout. Only has a slide number placeholder; all other content is direct shapes. Used for a wide variety of slide types in the template.
| idx | name | type | notes |
|---|---|---|---|
| 12 | Slide number | slide_number | Auto-populated |
Example uses from template:
- 3-metric stat cards with "100X" numbers (slides 20-21)
- Image + text with photo and quote/testimonial (slides 25-27)
- Customer quote slides with photos (slides 28, 30)
- Customer testimonials grid (slides 28, 32)
- Logo grid with product icons (slides 47-48)
- Comparison table (slide 36)
Generation: Clone the source slide and replace text in shapes by role. See the Clone Source Slides section below. Cowork description: Flexible canvas — find a slide in the template that visually matches the content type (quotes, stat cards, logo grid) and duplicate it
Layout 6: Flexible Canvas (alt, with slide number)
Used in: Slides 13-14, 18-19, 22, 40-41, 46, 49-50 Role: Second flexible canvas layout. Similar to Layout 5 but slight positioning differences.
| idx | name | type | notes |
|---|---|---|---|
| 12 | Slide number | slide_number | Auto-populated |
Example uses from template:
- Bold statement with Tiger Data bar (slides 13-14)
- Big number / key metric with caption (slides 18-19)
- 3 numbered card groups — pillars / principles / value props (slide 22)
- 5-step horizontal progression — maturity scale, phased rollout, severity tiers (slides 40-41)
- Button/icon library (slide 46)
- Checkmark lists — light and dark mode (slides 49-50)
Generation: Clone the source slide and replace text in shapes by role. See the Clone Source Slides section below. Cowork description: Flexible canvas — find a slide in the template that visually matches the content type (big number, comparison, timeline) and duplicate it
Layout 7: Feature Grid
Used in: Slides 6, 11-12 Role: Grid layout for multiple items — features, team members, or photo galleries.
| idx | name | type | notes |
|---|---|---|---|
| 12 | Slide number | slide_number | Auto-populated |
Example uses from template:
- 6-item feature grid with title + description per item (slide 6)
- 10-person photo grid with names (slide 11)
- 15-person photo grid with labels (slide 12)
Use when: Listing 4-6 features/capabilities, team photos, or any grid of similar items.
Generation: Clone slide 6 and replace text in grid item shapes. See the Clone Source Slides section below. Cowork description: Two-column feature grid with left sidebar label
Layout 8: Full-Width Quote / Text Block
Used in: Slide 8 Role: Large text block filling most of the slide. Tiger Data bar image at bottom.
| idx | name | type | notes |
|---|---|---|---|
| 12 | Slide number | slide_number | Auto-populated |
Use when: Long customer quote, mission statement, or key paragraph that needs to stand alone. Cowork description: Full-width quote or text block
Layout 9: Title + Content (primary content layout)
Used in: Slides 10, 23-24, 33-35, 37-39, 42-44 Role: The primary content workhorse. Title at top, content area below. Most-used layout in the template.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Top of slide, full width, max ~60 chars |
| 12 | Slide number | slide_number | Auto-populated |
Example uses from template:
- Table of contents with numbered sections (slide 10)
- Text slide with bullet points (slides 23-24)
- Logo grid (slide 33)
- Feature list with categories (slide 34)
- Data table (slide 35)
- Big number + graphic + caption (slide 37)
- Screenshot with title + description (slide 38)
- 3-column comparison with metrics (slide 39)
- Flowchart / process diagram (slide 42)
- Timeline / process with icons (slides 43-44)
Use when: Any standard content slide — bullets, tables, diagrams, screenshots, metrics. This is the default choice for most slide content. Cowork description: Title + content area (bullets, table, diagram, or metric)
Generation: Clone the specific template slide matching the content type (bullets → slide 23, timeline → slide 43, etc.) and replace text in shapes by role. Title is set via placeholder idx=0. Body and other shapes are identified by area and position. See the Clone Source Slides section below.
Layout 10: Large Title (unused)
Used in: Not used in any template slides Role: Large title with slide number. Reserved/unused variant.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Large centered title |
| 2 | Slide number | slide_number | Alternative position |
| 12 | Slide number | slide_number | Auto-populated |
Layout 11: Title + Body (with Tiger Data header)
Used in: Slides 5, 7 Role: Content slide with Tiger Data branded header bar, title, and body text area.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | "Tiger Data" or topic header, max ~40 chars |
| 1 | Body | body | Main content area — bullets or text, max ~300 chars |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Branded content slides where the Tiger Data header bar should be visible. Good for agenda slides, key content sections, or slides that need strong branding. This is the best layout for fully programmatic body content since it has a real BODY placeholder. Cowork description: Title + body with Tiger Data header bar
Layout 12: Section Divider (primary)
Used in: Slides 16, 45 Role: Section header/divider. Large section number and title text.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Section number | title | "01", "02", etc. |
| 2 | Section title | title | "Title of Section", max ~50 chars |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Transitioning between major sections of the deck. Put the section number in placeholder 0 and the section name in placeholder 2. Cowork description: Section divider — large number and title
Layout 13: Agenda
Used in: Slide 9 Role: Numbered agenda/roadmap with items in a horizontal layout.
| idx | name | type | notes |
|---|---|---|---|
| 12 | Slide number | slide_number | Auto-populated |
Use when: Agenda or roadmap slide. Content is in direct shapes (numbered items laid out horizontally). Clone slide 9 and modify the item text. Cowork description: Agenda with numbered items
Layout 14: Large Title (alt, unused)
Used in: Not used in any template slides Role: Alternative large title layout. Reserved/unused.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Title | title | Large title |
| 2 | Slide number | slide_number | Alternative position |
| 12 | Slide number | slide_number | Auto-populated |
Layout 15: Section Divider (alt)
Used in: Slide 17 Role: Alternative section divider. Same structure as Layout 12 with slight visual differences.
| idx | name | type | notes |
|---|---|---|---|
| 0 | Section number | title | "01", "02", etc. |
| 2 | Section title | title | Max ~50 chars |
| 12 | Slide number | slide_number | Auto-populated |
Use when: Same as Layout 12. Choose based on visual preference (dark vs. light variant). Cowork description: Section divider, alt style — large number and title
---
Quick Reference: Layout Selection Guide
| Purpose | Recommended Layout | Strategy | Clone source (0-indexed) |
|---|---|---|---|
| Opening / closing slide | Layout 1 (dark, centered) | Clone + replace | 0 |
| Alt title slide (light) | Layout 2 or 3 | Clone + replace | 1 or 3 |
| Section divider | Layout 12 or 15 | Clone + replace | 15 or 16 |
| Bold statement / key quote | Layout 4 (right-aligned) | Clone + replace | 14 |
| Content with Tiger Data header | Layout 11 (title + body) | Clone + replace | 4 |
| Content with bullets | Layout 9 (clone slide 23) | Clone + replace | 22 |
| Big number / key metric | Layout 6 (clone slide 18) | Clone + replace | 17 |
| Feature grid (4-6 items) | Layout 7 (clone slide 6) | Clone + replace | 5 |
| Customer quotes (3-up) | Layout 5 (clone slide 28) | Clone + replace | 27 |
| Table / data | Layout 9 (clone slide 35) | Clone + replace | 34 |
| Timeline / process | Layout 9 (clone slide 43) | Clone + replace | 42 |
| Screenshot + caption | Layout 9 (clone slide 38) | Clone + replace | 37 |
| 3-column comparison | Layout 9 (clone slide 39) | Clone + replace | 38 |
| Agenda | Layout 13 (clone slide 9) | Clone + replace | 8 |
| 3 numbered blurbs / ranked items (no named labels) | Layout 6 (clone slide 22) | Clone + replace | 21 |
| 5-step progression / maturity scale | Layout 6 (clone slide 40) | Clone + replace | 39 |
| Logo grid | Layout 9 (clone slide 33) | Clone + replace | 32 |
---
Programmatic Generation Strategy
How to use this section: When generating a .pptx, compose a single Python script. Copy all helper functions below into the top of the script and define them inline. Do not treat them as external imports — the generation script must be self-contained.
For Claude Code .pptx generation, clone every slide from a template source slide and use replace_shape_text() to set content. Never use add_slide(layout) + placeholder.text = — this produces bare XML that strips bodyPr settings (spAutoFit), paragraph properties (alignment, spacing, bullets), and run properties (font size, bold, color). The result is text that renders at wrong sizes and overflows shape boundaries.
1. All slides — including placeholder layouts (0-4, 11, 12, 15): Clone a template slide that uses the target layout using duplicate_slide(). Set text via replace_shape_text(slide.placeholders[idx], "text") for placeholders, or find shapes by role for direct shapes. replace_shape_text() preserves the template's paragraph and run formatting.
2. Clone source selection: For layouts with multiple template slides (e.g., Layout 9 is used by slides 23, 35, 38, 39, 43), clone the one matching the content type. For layouts with one template slide (e.g., Layout 1 → slide 1, Layout 4 → slide 15, Layout 12 → slide 16), always clone that slide.
3. After all slides are generated: Remove the original 50 template slides by iterating in reverse order and deleting each one.
4. Fallback: If cloning fails (e.g., python-pptx internal API changes), fall back to creating a slide from the layout and putting body content in speaker notes with [REPLACE: Body] markers. Report which slides need manual touch-up.
5. Multiline text in fixed-size shapes: For shapes with spAutoFit (text shrinks to fit), prefer a single string and let the shape wrap naturally. Explicit \n line breaks create separate paragraphs, each at full font size, which can overflow the shape even with auto-fit enabled. Use \n only for shapes designed for multi-paragraph content (grid items, bullet lists).
Helper functions
duplicate_slide(prs, source_slide)
Deep-copy a template slide, preserving all shapes, images, and formatting. Copies relationships and remaps rId references in the cloned XML so images resolve correctly:
import copy
def duplicate_slide(prs, source_slide):
dest = prs.slides.add_slide(source_slide.slide_layout)
dest_spTree = dest.shapes._spTree
# Clear layout-inherited shapes
for child in list(dest_spTree):
dest_spTree.remove(child)
# Copy all children from source
for child in source_slide.shapes._spTree:
dest_spTree.append(copy.deepcopy(child))
# Copy relationships and build rId mapping for remapping
rId_map = {}
for rel in source_slide.part.rels.values():
if "notesSlide" in rel.reltype:
continue
if "slideLayout" in rel.reltype:
for dest_rel in dest.part.rels.values():
if "slideLayout" in dest_rel.reltype:
rId_map[rel.rId] = dest_rel.rId
break
continue
if rel.is_external:
new_rId = dest.part.rels.get_or_add_ext_rel(rel.reltype, rel.target_ref)
else:
new_rId = dest.part.rels.get_or_add(rel.reltype, rel.target_part)
rId_map[rel.rId] = new_rId
# Remap rId references in copied shape XML
ns_r = '{http://schemas.openxmlformats.org/officeDocument/2006/relationships}'
for attr in ['embed', 'link', 'id']:
qualified = f'{ns_r}{attr}'
for elem in dest_spTree.iter():
val = elem.get(qualified)
if val and val in rId_map:
elem.set(qualified, rId_map[val])
return destreplace_shape_text(shape, new_text)
Replace text in a shape while preserving the first run's formatting. Supports multi-line text via \n (each line becomes a new paragraph cloned from the first):
def replace_shape_text(shape, new_text):
if not shape.has_text_frame:
return
tf = shape.text_frame
ns = '{http://schemas.openxmlformats.org/drawingml/2006/main}'
# Save first paragraph XML as formatting template
template_p = copy.deepcopy(tf.paragraphs[0]._p) if tf.paragraphs else None
# Remove all existing paragraphs
for p in list(tf._txBody.iterchildren(f'{ns}p')):
tf._txBody.remove(p)
# Create one paragraph per line, cloned from template
for line in new_text.split('\n'):
if template_p is not None:
new_p = copy.deepcopy(template_p)
for r in new_p.findall(f'.//{ns}r'):
for t in r.findall(f'{ns}t'):
t.text = ''
runs = new_p.findall(f'.//{ns}r')
if runs:
t_els = runs[0].findall(f'{ns}t')
if t_els:
t_els[0].text = line
else:
new_p = etree.SubElement(tf._txBody, f'{ns}p')
r = etree.SubElement(new_p, f'{ns}r')
t = etree.SubElement(r, f'{ns}t')
t.text = line
tf._txBody.append(new_p)replace_bullet_list(shape, items)
Replace bullet list content, preserving paragraph formatting:
def replace_bullet_list(shape, items):
if not shape.has_text_frame or not items:
return
tf = shape.text_frame
template_p = copy.deepcopy(tf.paragraphs[0]._p)
# Clear existing paragraphs
for p in list(tf._txBody.iterchildren(
'{http://schemas.openxmlformats.org/drawingml/2006/main}p'
)):
tf._txBody.remove(p)
# Add new paragraphs from template
ns = 'http://schemas.openxmlformats.org/drawingml/2006/main'
for item in items:
new_p = copy.deepcopy(template_p)
runs = new_p.findall(f'.//{{{ns}}}r')
if runs:
for t in runs[0].findall(f'{{{ns}}}t'):
t.text = item
for run in runs[1:]:
for t in run.findall(f'{{{ns}}}t'):
t.text = ''
tf._txBody.append(new_p)find_body_shape(slide)
Find the body text shape (largest text area, excluding title and slide number):
def find_body_shape(slide):
candidates = []
for shape in slide.shapes:
if not shape.has_text_frame:
continue
if shape.is_placeholder:
idx = shape.placeholder_format.idx
if idx in (0, 12, 2): # title, slide number
continue
candidates.append(shape)
if not candidates:
return None
return max(candidates, key=lambda s: (s.width or 0) * (s.height or 0))find_text_shapes_by_position(slide)
Find non-title, non-slide-number text shapes sorted by position:
def find_text_shapes_by_position(slide):
shapes = []
for shape in slide.shapes:
if not shape.has_text_frame:
continue
if shape.is_placeholder:
idx = shape.placeholder_format.idx
if idx in (0, 12, 2):
continue
shapes.append(shape)
return sorted(shapes, key=lambda s: (s.top or 0, s.left or 0))delete_shape(slide, shape)
Remove a shape from a slide (used to delete unused grid slots):
def delete_shape(slide, shape):
shape._element.getparent().remove(shape._element)delete_slide(prs, slide_index)
Remove a slide by index:
def delete_slide(prs, slide_index):
rId = prs.slides._sldIdLst[slide_index].get(
'{http://schemas.openxmlformats.org/officeDocument/2006/relationships}id'
)
prs.part.drop_rel(rId)
prs.slides._sldIdLst.remove(prs.slides._sldIdLst[slide_index])trim_table(table, keep_rows, keep_cols)
Remove excess rows and columns from a cloned table, then redistribute column widths to fill the original table width. The template table has 13 rows and 6 columns; call this after populating cells with set_cell_text():
def trim_table(table, keep_rows, keep_cols):
ns = '{http://schemas.openxmlformats.org/drawingml/2006/main}'
tbl = table._tbl
# Remove excess rows
rows = tbl.findall(f'{ns}tr')
for tr in rows[keep_rows:]:
tbl.remove(tr)
# Remove excess columns and redistribute widths
grid = tbl.find(f'{ns}tblGrid')
gridCols = grid.findall(f'{ns}gridCol')
total_width = sum(int(gc.get('w', 0)) for gc in gridCols)
for gc in gridCols[keep_cols:]:
grid.remove(gc)
remaining = grid.findall(f'{ns}gridCol')
col_width = total_width // len(remaining)
for gc in remaining:
gc.set('w', str(col_width))
# Remove excess cells from each remaining row
for tr in tbl.findall(f'{ns}tr'):
cells = tr.findall(f'{ns}tc')
for tc in cells[keep_cols:]:
tr.remove(tc)set_cell_text(cell, text)
Replace table cell text while preserving the template's run formatting (bold, font size, color, typeface). Use this instead of cell.text = "value", which wipes all formatting:
def set_cell_text(cell, text):
ns = '{http://schemas.openxmlformats.org/drawingml/2006/main}'
tc = cell._tc
txBody = tc.find(f'{ns}txBody')
if txBody is None:
cell.text = text
return
paras = txBody.findall(f'{ns}p')
if not paras:
cell.text = text
return
first_para = paras[0]
pPr = first_para.find(f'{ns}pPr')
template_pPr = copy.deepcopy(pPr) if pPr is not None else None
runs = first_para.findall(f'{ns}r')
template_rPr = None
if runs:
rPr = runs[0].find(f'{ns}rPr')
if rPr is not None:
template_rPr = copy.deepcopy(rPr)
endParaRPr = first_para.find(f'{ns}endParaRPr')
template_endRPr = copy.deepcopy(endParaRPr) if endParaRPr is not None else None
for p in paras:
txBody.remove(p)
new_p = etree.SubElement(txBody, f'{ns}p')
if template_pPr is not None:
new_p.append(template_pPr)
new_r = etree.SubElement(new_p, f'{ns}r')
if template_rPr is not None:
new_r.append(template_rPr)
new_t = etree.SubElement(new_r, f'{ns}t')
new_t.text = text
if template_endRPr is not None:
new_p.append(template_endRPr)---
Clone Source Slides: Shape Role Map
These are the specific template slides to clone for each slide type. After cloning, use the helper functions above to find and replace text in shapes. Always use `replace_shape_text()` for placeholder text — never placeholder.text =.
Slide 1 — Title Slide (Layout 1)
Clone source index: 0
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Subtitle | slide.placeholders[1] (ph_idx=1) | replace_shape_text(slide.placeholders[1], "Subtitle") |
| Slide number | ph_idx=12 | Leave as-is |
Also use for: Layout 0 (clone slide 3, idx 2), Layout 2 (clone slide 2, idx 1), Layout 3 (clone slide 4, idx 3).
Slide 15 — Bold Statement (Layout 4)
Clone source index: 14
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Statement text") — single string, let shape wrap naturally (50pt, spAutoFit) |
| Slide number | ph_idx=12 | Leave as-is |
Note: This template slide has no subtitle placeholder (ph_idx=1). The layout defines one but the slide instance does not include it. Put subtitle content in speaker notes.
Slide 16 — Section Divider (Layout 12)
Clone source index: 15
| Role | How to find | Replacement |
|---|---|---|
| Section number | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "01") — renders at 100pt |
| Section title | slide.placeholders[2] (ph_idx=2) | replace_shape_text(slide.placeholders[2], "Section Name") |
| Slide number | ph_idx=12 | Leave as-is |
Also use for: Layout 15 (clone slide 17, idx 16).
Slide 5 — Title + Body with Tiger Data Header (Layout 11)
Clone source index: 4
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Body | slide.placeholders[1] (ph_idx=1) | replace_shape_text(slide.placeholders[1], "Body text") |
| Slide number | ph_idx=12 | Leave as-is |
Slide 23 — Content with Bullets (Layout 9)
Clone source index: 22 (0-indexed)
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Body bullets | find_body_shape(slide) — largest text shape (ph_idx=4294967295) | replace_bullet_list(shape, items) |
| Slide number | ph_idx=12 | Leave as-is |
Slide 6 — Feature Grid (Layout 7)
Clone source index: 5
The grid has a sidebar title on the left, then a 2-column x 3-row grid of items. The first row is structured differently from rows 2-3.
Grid structure (shapes sorted by top, left):
| Position | Role | Size | Notes |
|---|---|---|---|
| (0.34, 2.55) | Sidebar title | 2.24x0.92 | Left panel, x < 3.0. Set to grid heading. Max ~18 chars total; use \n for 2 lines (30pt bold). |
| (3.52, 0.64) | Left col header | 2.20x0.44 | Item 1 title (short, bold) |
| (6.50, 0.64) | Right col header | 1.99x0.71 | Item 4 title (short, bold) |
| (3.52, 1.41) | Left col row 1 desc | 2.64x0.72 | Item 1 description (separate from title) |
| (6.50, 1.41) | Right col row 1 body | 2.62x1.30 | Item 4 description — single-paragraph, desc only |
| (3.52, 2.36) | Left col row 2 | 2.64x1.30 | Item 2 — combined title + description (2 paragraphs) |
| (6.50, 2.68) | Right col row 2 | 2.55x1.30 | Item 5 — combined title + description |
| (3.52, 3.99) | Left col row 3 | 2.64x1.30 | Item 3 — combined title + description |
| (6.50, 3.99) | Right col row 3 | 2.55x1.30 | Item 6 — combined title + description |
Important: The column header shapes are styled as category labels (large, bold), not individual items. The original template uses them to group features (e.g., "Postgres Made Easier" / "Postgres Made More Reliable"). Always assign short category labels to headers, then distribute items into the body shapes below.
Replacement strategy for N items: 1. Set sidebar title via replace_shape_text(sidebar, "Grid heading") 2. Group items into 2 categories. Set left and right col headers to the category labels (NOT individual item names) 3. Distribute items into body slots: row 1 descs (2 slots) + rows 2-3 bodies (4 slots) = 6 item slots total 4. Row 1 desc slots: replace_shape_text(shape, "Item Title\nItem Description") — combined title + description 5. Row 2-3 body slots: replace_shape_text(shape, "Item Title\nItem Description") — combined title + description 6. Fill left column top-to-bottom, then right column top-to-bottom 7. If fewer than 6 items, delete unused shapes from the bottom up. If more, split across slides.
| Role | How to find | Replacement |
|---|---|---|
| Sidebar title | Text shape with x < 3.0" | replace_shape_text(shape, "Heading") — max ~18 chars, use \n for 2 lines |
| Col headers (2) | Text shapes at y≈0.64", sorted by left | replace_shape_text(shape, "Item title") |
| Row 1 descs (2) | Text shapes at y≈1.41", sorted by left | replace_shape_text(shape, "Description") |
| Row 2-3 bodies (4) | Text shapes at y > 2.0", sorted by (top, left) | Combined: replace_shape_text(shape, "Title\nDescription") |
| Slide number | ph_idx=12 | Leave as-is |
Fewer than 6 items: Delete unused body shapes from the bottom up using delete_shape(). Fill left column top-to-bottom first, then right column.
More than 6 items: Split across two grid slides. Clone slide 6 twice. Group items so each slide's pair of categories makes sense together.
Choosing category labels: Column headers must be short (2-4 words). Choose categories by the most salient shared attribute: functional area (writing vs. analysis), workflow stage (create vs. review), or user benefit (speed vs. quality). If the source material suggests natural groupings, use those.
Slide 18 — Big Number / Key Metric (Layout 6)
Clone source index: 17
| Role | How to find | Replacement |
|---|---|---|
| Big number | find_body_shape(slide) — largest text shape (ph_idx=4294967295, ~22 sq in) | replace_shape_text(shape, "100X") |
| Caption | Second text shape by area (TEXT_BOX) | replace_shape_text(shape, "Caption text") |
| Slide number | ph_idx=12 | Leave as-is |
Slide 28 — Customer Quotes 3-up (Layout 5)
Clone source index: 27
| Role | How to find | Replacement |
|---|---|---|
| Header | find_body_shape(slide) — largest text shape (ph_idx=4294967295) | replace_shape_text(shape, "Header") |
| Quote text boxes (3) | TEXT_BOX shapes sorted by left position (at y≈3.85") | replace_shape_text(shape, "Quote text") per box |
| Photos (3) | PICTURE shapes | Leave as template photos or note for manual replacement |
| Slide number | ph_idx=12 | Leave as-is |
Slide 35 — Data Table (Layout 9)
Clone source index: 34
The template table has 13 rows x 6 columns with Latin placeholder text. After populating cells with real data, call trim_table() to remove unused rows and columns:
for shape in slide.shapes:
if hasattr(shape, 'has_table') and shape.has_table:
table = shape.table
# Fill cells using set_cell_text to preserve formatting:
set_cell_text(table.cell(0, 0), "Header 1")
set_cell_text(table.cell(1, 0), "Data 1")
# Then trim to actual data size (redistributes column widths):
trim_table(table, keep_rows=4, keep_cols=3) # adjust to your data
break| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Table | Shape where shape.has_table == True | Fill cells, then trim_table(table, rows, cols) |
| Slide number | ph_idx=12 | Leave as-is |
Slide 38 — Screenshot + Caption (Layout 9)
Clone source index: 37
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Description | find_body_shape(slide) — largest non-title text shape (ph_idx=4294967295) | replace_shape_text(shape, "Description") |
| Screenshot | PICTURE shape | Leave as placeholder or note for manual replacement |
| Slide number | ph_idx=12 | Leave as-is |
Slide 39 — 3-Column Comparison (Layout 9)
Clone source index: 38
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Metric numbers (3) | Small TEXT_BOX shapes at y≈2.59", sorted by left | replace_shape_text(shape, "24%") per box |
| Column descriptions (3) | Larger TEXT_BOX shapes at y≈3.94", sorted by left | replace_shape_text(shape, "Description") per box |
| Column images (3) | PICTURE shapes | Leave as placeholders |
| Slide number | ph_idx=12 | Leave as-is |
Slide 22 — 3 Numbered Card Groups (Layout 6)
Clone source index: 21
| Role | How to find | Replacement |
|---|---|---|
| Title | Text shape at y < 2.0", w > 6.0" (ph_idx=4294967295) | replace_shape_text(shape, "Title") — max ~60 chars |
| Card groups (3) | GROUP shapes at y≈2.4, sorted by left | Iterate group.shapes to access children |
| Card number | Group child TEXT_BOX matching "01"/"02"/"03" | Leave as-is (renders at 25pt bold) |
| Card description | Group child TEXT_BOX at y≈3.32 | replace_shape_text(shape, "Description") — max ~50 chars |
| Dividers (2) | PICTURE shapes at y≈3.14 between groups | Leave as-is |
| Slide number | ph_idx=12 | Leave as-is |
Note: The template has 3 equal GROUP shapes at y=2.4, each 2.38" wide × 1.68" tall. Each group contains a roundRect AUTO_SHAPE background, a number TEXT_BOX, and a description TEXT_BOX. Iterate the groups in left-to-right order; for each group, iterate its children to find the description TEXT_BOX by position (y≈3.32 relative to slide).
Label handling: The numeric badge ("01"/"02"/"03") is the card's label and visual emphasis. The description TEXT_BOX is the only writable content field. Do not use this layout for items that need arbitrary text labels (e.g., "Change Management," "Scalability"); those require overriding the badge's font formatting (25pt bold centered → smaller left-aligned), which is not supported by the current helper functions. Until that support ships, use this layout only for content where numeric ordering is appropriate.
Known constraint: The 1.68" card height fits ~50 characters of description text. Longer text clips. For longer items, split across two card slides or move detail to speaker notes.
Slide 40 — 5-Step Progression (Layout 6)
Clone source index: 39
| Role | How to find | Replacement |
|---|---|---|
| Title | Text shape at y < 2.0", w > 6.0" (ph_idx=4294967295) | replace_shape_text(shape, "Title") — max ~60 chars |
| Column labels (5) | TEXT_BOX shapes at y≈2.15, sorted by left | replace_shape_text(shape, "Step 1") — max ~12 chars |
| Column rectangles (5) | AUTO_SHAPE shapes at y≈2.56, sorted by left | Leave as-is (colored backgrounds) |
| Column descriptions (5) | TEXT_BOX shapes at y≈2.74, sorted by left | replace_shape_text(shape, "Description") — max ~60 chars |
| Tick marks (5) | LINE shapes at y≈3.79 | Leave as-is |
| Axis line | LINE shape at y≈4.26, w > 9" | Leave as-is |
| Bottom caption | ph_idx=4294967295 at y≈4.47 | If axis label provided: replace_shape_text(shape, "Axis label"). If not: delete_shape(slide, shape) to remove cleanly — cloning otherwise leaves the template's placeholder text visible |
| Slide number | ph_idx=12 | Leave as-is |
Note: The template has 5 colored AUTO_SHAPE rects forming a scale bar, with column labels above and description text boxes overlaid on the rects. When finding column descriptions, filter candidate shapes to TEXT_BOX before sorting by position — rects and descriptions overlap vertically (rects at y=2.56, descriptions at y=2.74), so a naive y-range search returns both.
Caption handling: The bottom caption is not optional output — the template renders with placeholder text ("LOREM IPSUM") unless explicitly handled. When the deck plan calls for an axis label, replace it. When no axis label is provided, either delete the shape via delete_shape() or clone idx 40 (slide 41) instead, which has no caption shape.
Alternative: Slide 41 (idx 40) is a compact variant — same upper layout but with small horizontal tick LINE segments between rects (y=3.35) instead of vertical ticks, and no bottom caption. Clone idx 40 when no axis label is needed.
Slide 43 — Timeline / Process (Layout 9)
Clone source index: 42
| Role | How to find | Replacement |
|---|---|---|
| Title | slide.placeholders[0] (ph_idx=0) | replace_shape_text(slide.placeholders[0], "Title") |
| Body text | find_body_shape(slide) — largest non-title text shape (ph_idx=4294967295) | replace_shape_text(shape, "Body") |
| Timeline headings (3) | TEXT_BOX shapes at y≈3.60", sorted by left | replace_shape_text(shape, "Phase 1") — max ~15 chars (9pt, 1.63" wide) |
| Timeline details (3) | TEXT_BOX shapes at y≈3.85", sorted by left | replace_shape_text(shape, "Detail") — max ~45 chars across 2 lines (8pt, 1.63x0.36") |
| Timeline labels (3) | TEXT_BOX shapes at y≈4.25", sorted by left | replace_shape_text(shape, "Q1") — max ~6 chars (7pt, 0.55" pill) |
| Slide number | ph_idx=12 | Leave as-is |
Note: The timeline has exactly 3 columns with icons. If you need more or fewer phases, note it for manual adjustment.
Slide 9 — Agenda (Layout 13)
Clone source index: 8
| Role | How to find | Replacement |
|---|---|---|
| Numbers column | First TEXT_BOX by left position (x≈3.0") | Multi-line text: "01\n\n02\n\n03..." |
| Items column | Second TEXT_BOX by left position (x≈3.5") | Multi-line text: "Item 1\n\nItem 2\n\nItem 3..." |
| Slide number | ph_idx=12 | Leave as-is |