Skip to main content

compono

Agent-oriented, code-based PPTX generation — "Manim, but for PowerPoint."

compono lets an LLM agent (or a human) describe a slide deck as data — headers, bullet text, stats, tables, charts, images, process sequences, shapes — and get back a real, editable .pptx file. The agent never writes raw x/y/w/h coordinates: a constraint-based layout resolver computes every position from a small set of typed primitives.

Every rendered element is a genuine, editable native shape (p:sp, p:pic, p:graphicFrame) — never a flattened image or embedded video. Open the result in PowerPoint and drag a box around; it's a real object, not a picture of one.

This file is both the human-facing README and the in-context reference an agent uses to call compono correctly — see skills/compono/SKILL.md for the packaged version of the same content.

Install

pip install compono   # not yet published — see CHANGELOG.md for status

For local development, see CONTRIBUTING.md.

Quickstart

from compono import render_deck

spec = {
    "slides": [
        {
            "header": {"title": "Q3 Results", "subtitle": "Engineering team"},
            "body": [
                {
                    "primitive": "text",
                    "mode": "bullets",
                    "content": [
                        "Shipped the new layout resolver",
                        "Cut render time by 40%",
                        "Zero overflow bugs in production",
                    ],
                    "emphasis_indices": [1],
                }
            ],
        }
    ]
}

report = render_deck(spec, "deck.pptx")
print(report.pptx_path, report.warnings)

Or from the command line:

compono validate spec.json
compono render spec.json -o deck.pptx

Core concepts

  • One entry point, two verbs. render_deck(spec, output_path) and validate(spec) are the only two functions you need. validate is cheap — no pptx write, millisecond-scale — so an agent can iterate on a spec before paying render cost.
  • A spec is plain data. Either a raw dict/JSON (what an agent's tool-calling naturally produces) or the typed builder classes (Deck, Header, Text, ...) — both serialize to the identical shape. There's no divergence between the two paths.
  • You never write coordinates. Every primitive claims space in a slide; the resolver (a CSS-flexbox-style directional box model) computes real EMU positions. grid is the one primitive that does true 2D row/column math.
  • Errors are fixes, not diagnoses. Every validation/render failure is {slide, primitive, field, error, detail, fix} — see Error shape below.
  • render_deck returns a report, not just a file{pptx_path, manifest, warnings, actual_layout} — so an agent can reason about what happened without reopening the file.

API reference

from compono import (
    render_deck, validate,
    Deck, Slide, Header, Text, Image, Stat, Grid, Table, Sequence, Chart, Shape,
    DeckValidationError,
)
Symbol Signature Notes
render_deck render_deck(spec, output_path, *, template=None) -> RenderReport Validates, resolves layout, writes a real .pptx. Raises DeckValidationError on any error — nothing is written on failure.
validate validate(spec, *, template=None) -> ValidationReport Schema + layout + text-overflow checks. No file I/O. Never raises — check .valid/.errors.
DeckValidationError exc.errors -> list[dict] The one exception type. Carries the structured error list below.

A Deck is {template?: str, slides: [Slide, ...]}. A Slide is {header?: Header, body: [primitive, ...], notes?: str}. body (and grid.items) accept any primitive, keyed by its "primitive" field.

Error shape

{
  "slide": 3,
  "primitive": "grid.items[1]",
  "field": "content",
  "error": "overflow",
  "detail": "Text is ~14pt too tall for the box at font size 18pt (6 lines).",
  "fix": "Shorten the text, reduce bullet/line count, or split into two slides."
}

Primitive catalog

Every primitive accepts an optional id (needed if another primitive references it, e.g. a connector) and an optional notes (speaker notes).

Primitive Key fields Purpose
header title, subtitle?, eyebrow?, align Slide title region.
text mode (paragraph/bullets), content, columns?, emphasis_indices? Prose or bullet list.
image src?, placeholder, caption?, fit (cover/contain) A real picture, or a first-class placeholder — see below.
stat value, label, trend? A headline number with a label.
grid items, columns, direction, align, justify The one primitive with true 2D layout. Items can be any primitive, including nested grids.
table headers, rows, emphasis_row?, emphasis_col? Renders as a real OOXML table (p:graphicFrame), not an image.
sequence steps ({label, description?}), orientation A row/column of connected step boxes — process/timeline diagrams.
chart chart_type (bar/line/pie), categories, series A real, editable native chart with live data — not a picture of a chart.
shape kind (rect/rounded_rect/oval/line/arrow/connector), fill, border, connects?, text? Freeform shape, optionally with text inside, or a connector between two other primitives by id.

Every schema field's description is written as an instruction (e.g. "Keep under ~60 characters — longer titles will be shrunk by the resolver"), not a bare type label — call Header.model_json_schema() (or any primitive class) to get the full JSON Schema with these descriptions inline.

Image placeholders

Set "placeholder": true (with an optional caption) instead of src when you don't have a real image yet. It renders as an intentional design element — dashed border, centered caption — and render_deck's RenderReport.manifest gets one entry per placeholder: {slide, primitive, rect: {x, y, w, h}, caption}. A later pass (image search/generation/human upload) can fill each reserved rect directly from the manifest EMU rect — no re-layout needed, and the deck-building agent itself never needs image-generation capability.

Worked examples

1. Title slide

{
  "slides": [
    { "header": { "title": "2026 Roadmap", "subtitle": "Platform team", "eyebrow": "Q1 Kickoff" } }
  ]
}

2. Two-column comparison with a connector

{
  "slides": [{
    "header": { "title": "Before vs. After" },
    "body": [
      {
        "primitive": "grid",
        "columns": 2,
        "items": [
          { "id": "before", "primitive": "shape", "kind": "rounded_rect", "fill": "#EF4444",
            "text": { "content": "Manual layout" } },
          { "id": "after", "primitive": "shape", "kind": "rounded_rect", "fill": "#10B981",
            "text": { "content": "Resolver-computed layout" } }
        ]
      },
      { "primitive": "shape", "kind": "connector", "connects": { "from_id": "before", "to_id": "after" } }
    ]
  }]
}

3. Stat + table + chart dashboard

{
  "slides": [{
    "header": { "title": "Q3 Metrics" },
    "body": [
      { "primitive": "stat", "value": "42%", "label": "YoY growth", "trend": "+12% vs Q2" },
      { "primitive": "table", "headers": ["Quarter", "Revenue"], "rows": [["Q1", "10"], ["Q2", "14"]] },
      { "primitive": "chart", "chart_type": "bar", "categories": ["Q1", "Q2"],
        "series": [{ "name": "Revenue", "values": [10, 14] }] }
    ]
  }]
}

4. Process sequence

{
  "slides": [{
    "header": { "title": "Our Process" },
    "body": [{
      "primitive": "sequence",
      "orientation": "horizontal",
      "steps": [
        { "label": "Discover", "description": "Understand the problem" },
        { "label": "Design", "description": "Sketch options" },
        { "label": "Ship", "description": "Release to users" }
      ]
    }]
  }]
}

See examples/minimal.json and examples/full_catalog.json for complete, runnable specs (also used as test fixtures).

Fonts and overflow validation

Overflow checking (validate's layout errors, and the "shrink text on overflow" behavior it protects against) reads real glyph advance widths via fonttools — no rendering required. As of this release, no font is bundled into the package yet (src/compono/fonts/ is a placeholder); validation falls back to a system font if one is found (e.g. arial.ttf on Windows), and is skipped — not faked — with a warning if none is available. A bundled, OFL-licensed safe-font list is planned before the first tagged release; this section will list it once shipped.

CLI

compono validate spec.json
compono render spec.json --template fractal -o deck.pptx

Mirrors validate/render_deck exactly — useful for agent frameworks that can only shell out rather than import Python.

Contributing

See CONTRIBUTING.md for dev setup, branching, and code style. If you're using Claude Code, .claude/README.md describes the build-workflow skill, review subagent, and commit/format hooks set up for this repo.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compono-0.1.0.tar.gz (18.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

compono-0.1.0-py3-none-any.whl (22.5 kB view details)

Uploaded Python 3

File details

Details for the file compono-0.1.0.tar.gz.

File metadata

  • Download URL: compono-0.1.0.tar.gz
  • Upload date:
  • Size: 18.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for compono-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c43c17a64ae4c6daf3d1bbc7ba9e11908c2cc9479573f04f78200ac9f58ed6bc
MD5 139dfa2912dc7ffac49ce585ec0089ee
BLAKE2b-256 a12c52238740a0a9f5e5efb9318e87d07a37dd436b0496f52128e14cbabf8d5d

See more details on using hashes here.

File details

Details for the file compono-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: compono-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for compono-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eb0bfd2062793417ca0d376c83b5bf91505a491cc2f5788047e24704c03457aa
MD5 7d46c678977723852890a7502f3d7701
BLAKE2b-256 a349aa097371bfd6f45cb8f367a99a0cd8b1867fe802bef4d42ef4e624ae05f9

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.0

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page