Back to Onyx

PPTX Skill

backend/onyx/skills/builtin/pptx/SKILL.md

4.4.415.1 KB
Original Source

PPTX Skill

Path convention: All commands run from the session workspace (your working directory). Never cd into the skill directory. Prefix all skill scripts with .opencode/skills/pptx/. All generated files (unpacked dirs, output presentations, thumbnails, PDFs, images) go in outputs/.

Quick Reference

TaskGuide
Read/analyze contentpython -m markitdown presentation.pptx
Edit or create from templateRead editing.md
Create from scratchRead components.md — tested layout library (default); pptxgenjs.md for raw layouts it doesn't cover
Charts from real dataRead charts.md — deck-styled matplotlib PNGs via scripts/chart.py
Iconsnode .opencode/skills/pptx/scripts/icon.js <name> --color <hex> — see pptxgenjs.md
Lint layout (QA step 0)python .opencode/skills/pptx/scripts/lint.py outputs/output.pptx

Build Order (required)

A complete, text-viable deck must exist in outputs/ before any asset enhancement. The worst outcome is a turn that ends with no deck at all.

  1. Draft: full deck — all slides, titles, text content, layout structure — written and saved to outputs/<name>.pptx. On the template-editing path this means an EARLY first clean+pack (see editing.md): edits stranded in outputs/unpacked/ deliver nothing.
  2. Enhance in passes (charts, icons), re-saving the deck after each pass.
  3. Lint + QA last (see QA below).

If time or context is running short at any point, stop enhancing and ship the current saved deck. Never spend the start of a turn generating or inspecting assets for a deck that doesn't exist yet.

Reading Source Materials (context discipline)

Read attached/source documents as text (python -m markitdown file.pdf). Never view_image source pages or figure crops — image reads exhaust the model context in a handful of pages and the turn dies before the deck is built. To reuse figures from a source PDF, extract the embedded images directly — no rendering, no cropping, no visual inspection:

bash
mkdir -p outputs/figs/
pdfimages -png -p source.pdf outputs/figs/fig   # fig-<page>-<n>.png per image
python -c "from PIL import Image; import glob; [print(p, Image.open(p).size) for p in sorted(glob.glob('outputs/figs/*.png'))]"

Pick figures by page number and pixel size (real figures are large; icons and logos are small), place them directly, and verify them in the rendered-slide QA pass at the end — never by viewing each extraction.


Choosing a Template (do this first)

Starting from a well-designed template produces far better decks than building from scratch. There are two distinct sources of templates — check both:

bash
# User-uploaded templates (the user's own / brand decks) — list these first
ls user_library/*.pptx attachments/*.pptx 2>/dev/null
# Built-in templates (generic starting points provided by Onyx)
ls /workspace/templates/pptx/*.pptx 2>/dev/null
  • If any templates are found: present them to the user as two clearly labeled groups — "Your uploaded templates" (from user_library/ and attachments/) and "Built-in templates" (from /workspace/templates/pptx/) — listing the user's own templates first. Briefly describe each (use thumbnail.py to preview if helpful), then ask which one to use — or whether to build from scratch — and wait for their answer before building. Never silently pick one: a user's own brand deck and a generic built-in are not interchangeable, and only the user knows which they want. This is one of the few cases where you should pause and ask rather than act autonomously.
  • If none are found: build from scratch, and let the user know they can upload a brand/template .pptx for a more polished, on-brand result next time.

Once a template is chosen, follow editing.md. Use the from-scratch path only when the user has no template or explicitly opts for it — default to the tested layout library (components.md), and fall back to raw pptxgenjs.md for layouts it doesn't cover.


Reading Content

bash
# Text extraction
python -m markitdown presentation.pptx

# Visual overview
python .opencode/skills/pptx/scripts/thumbnail.py presentation.pptx

# Raw XML
python .opencode/skills/pptx/scripts/office/unpack.py presentation.pptx outputs/unpacked/

Editing Workflow

Read editing.md for full details.

  1. Analyze template with thumbnail.py
  2. Unpack → manipulate slides → edit content → clean → pack

Creating from Scratch

Read pptxgenjs.md for full details.

Use only when no template is available or the user explicitly chooses to build from scratch (see Choosing a Template).


Design Ideas

These ideas are for building from scratch. When developing on a chosen template, inherit the template's existing palette, fonts, and layout language — match it, don't redesign it. Skip the palette/font suggestions below and reuse what the template already defines.

Don't create boring slides. Plain bullets on a white background won't impress anyone. Consider ideas from this list for each slide.

Before Starting

  • Pick a bold, content-informed color palette: The palette should feel designed for THIS topic. If swapping your colors into a completely different presentation would still "work," you haven't made specific enough choices.
  • Dominance over equality: One color should dominate (60-70% visual weight), with 1-2 supporting tones and one sharp accent. Never give all colors equal weight.
  • Dark/light contrast: Dark backgrounds for title + conclusion slides, light for content ("sandwich" structure). Or commit to dark throughout for a premium feel.
  • Commit to a visual motif: Pick ONE distinctive element and repeat it — rounded image frames, icons in colored circles, thick single-side borders. Carry it across every slide.

Color Palettes

Choose colors that match your topic — don't default to generic blue. Use these palettes as inspiration:

ThemePrimarySecondaryAccent
Midnight Executive1E2761 (navy)CADCFC (ice blue)FFFFFF (white)
Forest & Moss2C5F2D (forest)97BC62 (moss)F5F5F5 (cream)
Coral EnergyF96167 (coral)F9E795 (gold)2F3C7E (navy)
Warm TerracottaB85042 (terracotta)E7E8D1 (sand)A7BEAE (sage)
Ocean Gradient065A82 (deep blue)1C7293 (teal)21295C (midnight)
Charcoal Minimal36454F (charcoal)F2F2F2 (off-white)212121 (black)
Teal Trust028090 (teal)00A896 (seafoam)02C39A (mint)
Berry & Cream6D2E46 (berry)A26769 (dusty rose)ECE2D0 (cream)
Sage Calm84B59F (sage)69A297 (eucalyptus)50808E (slate)
Cherry Bold990011 (cherry)FCF6F5 (off-white)2F3C7E (navy)

For Each Slide

Every slide needs a visual element — image, chart, icon, or shape. Text-only slides are forgettable. Render deck-styled charts from real data with charts.md; render icons zero-setup with scripts/icon.js (see pptxgenjs.md).

Layout options:

  • Two-column (text left, illustration on right)
  • Icon + text rows (icon in colored circle, bold header, description below)
  • 2x2 or 2x3 grid (image on one side, grid of content blocks on other)
  • Half-bleed image (full left or right side) with content overlay

Data display:

  • Large stat callouts (big numbers 60-72pt with small labels below)
  • Comparison columns (before/after, pros/cons, side-by-side options)
  • Timeline or process flow (numbered steps, arrows)

Visual polish:

  • Icons in small colored circles next to section headers
  • Italic accent text for key stats or taglines

Typography

Choose an interesting font pairing — don't default to Arial. Pick a header font with personality and pair it with a clean body font.

When building from scratch, use only the fonts below — any other font name silently falls back and renders wrong. Available: Inter, Montserrat, Lato, EB Garamond and Fira Code (monospace), plus Calibri, Cambria, Arial, Times New Roman (rendered via metric-compatible substitutes — Carlito/Caladea/Liberation).

When editing a template, this list does not apply — keep the template's own fonts. A template may use fonts not listed here (e.g. Kabel, Garet); these are typically embedded in the file and render correctly even though they aren't installed system-wide. Do not swap them for an installed font — that destroys the template's design. clean.py/pack.py preserve embedded fonts (ppt/fonts/), so they survive the edit cycle.

Header FontBody FontFeel
MontserratInterModern, geometric — strong default for a contemporary deck
InterInterMinimal; lean on weight (Bold/Light) for hierarchy
EB GaramondInterEditorial — elegant serif title over a clean sans body
LatoLatoProfessional, corporate-safe
CambriaCalibriClassic Office serif/sans pairing
Arial BlackInterBold, high-impact headlines

Use Fira Code for code samples or dense numeric/stat blocks.

ElementSize
Slide title36-44pt bold
Section header20-24pt bold
Body text14-16pt
Captions10-12pt muted

Spacing

  • 0.5" minimum margins
  • 0.3-0.5" between content blocks
  • Leave breathing room—don't fill every inch

Avoid (Common Mistakes)

  • Don't repeat the same layout — vary columns, cards, and callouts across slides
  • Don't center body text — left-align paragraphs and lists; center only titles
  • Don't skimp on size contrast — titles need 36pt+ to stand out from 14-16pt body
  • Don't default to blue — pick colors that reflect the specific topic
  • Don't mix spacing randomly — choose 0.3" or 0.5" gaps and use consistently
  • Don't style one slide and leave the rest plain — commit fully or keep it simple throughout
  • Don't create text-only slides — add images, icons, charts, or visual elements; avoid plain title + bullets
  • Don't forget text box padding — when aligning lines or shapes with text edges, set margin: 0 on the text box or offset the shape to account for padding
  • Don't use low-contrast elements — icons AND text need strong contrast against the background; avoid light text on light backgrounds or dark text on dark backgrounds
  • NEVER use accent lines under titles — these are a hallmark of AI-generated slides; use whitespace or background color instead

QA (Required)

Assume there are problems. Your job is to find them.

Your first render is almost never correct. Approach QA as a bug hunt, not a confirmation step. If you found zero issues on first inspection, you weren't looking hard enough.

Layout Lint (run first)

Before any render or vision pass, run the deterministic layout linter — it catches the mechanical defects (off-slide shapes, sub-margin text, text overflow, overlapping text frames, low-contrast explicit colors, unknown fonts) with exact slide/shape references:

bash
python .opencode/skills/pptx/scripts/lint.py outputs/output.pptx
  • Exit 0 with LINT_CLEAN means no findings; nonzero means ERRORs exist.
  • Fix every ERROR before moving on. Review each WARN and fix it unless the layout is genuinely intentional (e.g., a deliberate design overlap).
  • Re-run after each fix batch — it's instant, so lint until clean before spending time on rendering.
  • The linter checks contrast only for explicit solid colors; text over images/gradients is skipped and noted — verify those visually.
  • Content-dense decks (analytical / consulting-style slides with intentionally tight margins) generate many advisory MARGIN warnings under the default profile. For those, lint with --profile dense (relaxes the margin warning from 0.5" to 0.25"); error-level checks are unchanged. Tight margins on a dense analytical slide are a deliberate style, not a defect — don't strip density to silence standard-profile MARGIN warnings.

Content QA

bash
python -m markitdown output.pptx

Check for missing content, typos, wrong order.

When using templates, check for leftover placeholder text:

bash
python -m markitdown output.pptx | grep -iE "xxxx|lorem|ipsum|this.*(page|slide).*layout"

If grep returns results, fix them before declaring success.

Visual QA

⚠️ USE SUBAGENTS — even for 2-3 slides. You've been staring at the code and will see what you expect, not what's there. Subagents have fresh eyes.

Convert slides to images (see Converting to Images), then use this prompt:

Visually inspect these slides. Assume there are issues — find them.

Look for:
- Decorative lines positioned for single-line text but title wrapped to two lines
- Source citations or footers colliding with content above
- Elements too close (< 0.3" gaps) or cards/sections nearly touching
- Uneven gaps (large empty area in one place, cramped in another)
- Columns or similar elements not aligned consistently
- Low-contrast text over images or gradients (the linter can't check these)
- Low-contrast icons (e.g., dark icons on dark backgrounds without a contrasting circle)
- Text boxes too narrow causing excessive wrapping
- Inconsistent styling across slides (fonts, colors, motifs drifting)
- Leftover placeholder content

For each slide, list issues or areas of concern, even if minor.

Read and analyze these images:
1. /path/to/slide-01.jpg (Expected: [brief description])
2. /path/to/slide-02.jpg (Expected: [brief description])

Report ALL issues found, including minor ones.

Verification Loop

  1. Generate slides → Run lint.py → fix every ERROR (and unjustified WARNs)
  2. Re-run lint after each fix batch — it's instant — until clean
  3. Convert to images → Inspect (subagent vision pass for the final aesthetic check)
  4. List issues found (if none found, look again more critically)
  5. Fix issues → re-run lint (one fix often creates another problem), then re-verify affected slides
  6. Repeat until lint is clean and a full visual pass reveals no new issues

Do not declare success until you've completed at least one fix-and-verify cycle.


Converting to Images

Convert presentations to individual slide images for visual inspection:

bash
python .opencode/skills/pptx/scripts/office/soffice.py --headless --convert-to pdf outputs/output.pptx
pdftoppm -jpeg -r 150 outputs/output.pdf outputs/slide

This creates slide-01.jpg, slide-02.jpg, etc.

To re-render specific slides after fixes:

bash
pdftoppm -jpeg -r 150 -f N -l N outputs/output.pdf outputs/slide-fixed

Dependencies

  • pip install "markitdown[pptx]" - text extraction
  • pip install Pillow - thumbnail grids
  • npm install -g pptxgenjs - creating from scratch
  • LibreOffice (soffice) - PDF conversion (auto-configured for sandboxed environments via .opencode/skills/pptx/scripts/office/soffice.py)
  • Poppler (pdftoppm) - PDF to images