Skip to main content

compono

Agent-oriented, code-based PPTX generation. Describe a deck as typed primitives — an LLM agent never writes raw coordinates or touches OOXML.

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.

See it in action

compono isn't scoped to one deck genre — the same primitives compose into client proposals, conference talks, research talks, college presentations, or a lighter explainer. Every image below is rendered directly from the matching examples/*.json spec (.pptx → PNG via LibreOffice, see scripts/render_example_screenshots.py) — nothing here is a mockup:

Client proposal Conference talk
KPI grid Planner/Executor architecture
Research talk Fun explainer
Loss curves Roast levels

shape + connector compose into real diagrams, not just colored boxes — a layered system architecture, built entirely from examples/architecture_diagram.json:

Layered architecture: client → gateway → services → queue → database

See examples/ for the full specs (client_proposal.json, conference_talk.json, research_talk.json, college_presentation.json, fun_explainer.json, architecture_diagram.json, and full_catalog.json).

Install

pip install compono
# or
uv add compono

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, fill_style (solid default, or gradient), 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/full_catalog.json for a complete, runnable spec (also used as a test fixture).

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.

MCP server

compono-mcp exposes validate/render_deck as MCP tools, for any MCP-compatible client — not just Claude Code.

pip install compono-mcp
# or
uv add compono-mcp

Add to your MCP client config (Claude Desktop / Claude Code style):

{ "mcpServers": { "compono": { "command": "compono-mcp" } } }

Exposes validate_deck/render_deck_tool tools (identical spec shape to the Python API) and a compono://reference resource carrying the full agent-facing reference doc, for clients without Claude Code's skill system.

Claude Code plugin

This repo is also a Claude Code plugin marketplace, bundling the skills/compono/SKILL.md reference doc as an installable skill:

/plugin marketplace add Shaik-Hamzah123/compono
/plugin install compono

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.2.tar.gz (21.2 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.2-py3-none-any.whl (24.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: compono-0.1.2.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for compono-0.1.2.tar.gz
Algorithm Hash digest
SHA256 31e2c5dd9a52afb8302cfaf29116c8ba7b7243e0b78916a4ac5be29a4582b02d
MD5 f55ba8ef3d0857337e671e09048730d1
BLAKE2b-256 23a0a61f543aea5215026c437f636314398ada42b407e0cec4362091ec7c688f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compono-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 24.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for compono-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 dd3825b6cd16acf4f03d69372fc785f029c0824941b1921088ff805a38eb34dd
MD5 a72dafa735d25cf8b7afe452200171e4
BLAKE2b-256 a6c4357bfd27d81c98aeec6a161f090bbed13ab3362a232b75eda5b7b7d1699b

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

This release

0.1.2 This release

2 files

0.1.0

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