agentskills.codes
BE

Create Beamer-style academic PPTX presentations using python-pptx. Produces publication-quality .pptx files with navy-blue Metropolis theme (16:9, frame title bars, progress bar) for conference talks, job market presentations, and seminar slides. Called by /present command.

Install

mkdir -p .claude/skills/beamer-ppt && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13451" && unzip -o skill.zip -d .claude/skills/beamer-ppt && rm skill.zip

Installs to .claude/skills/beamer-ppt

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Create Beamer-style academic PPTX presentations using python-pptx. Produces publication-quality .pptx files with navy-blue Metropolis theme (16:9, frame title bars, progress bar) for conference talks, job market presentations, and seminar slides. Called by /present command.
274 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)

About this skill

Beamer-ppt-Creator

Purpose

This skill generates professional academic PPTX presentations that faithfully replicate the visual style of LaTeX Beamer (Metropolis theme). Output is a .pptx file that can be opened, edited, and presented directly in PowerPoint or LibreOffice Impress — no LaTeX installation required.

When to Use

  • Called by /present command to produce the final slides/slides.pptx
  • Preparing conference, seminar, or job market slides
  • Converting a completed economics paper into a slide deck

Design Principles

  • One idea per slide — split if content overflows
  • Minimum 20pt for body text; 24pt for frame titles
  • Consistent palette — navy blue primary, one accent color only
  • Figures over tables — embed PNG images at ≥ 200 DPI
  • Last slide = Takeaways, never "Questions?"

Implementation

This skill executes Python code using python-pptx. Always install dependencies first:

pip install python-pptx pdf2image --break-system-packages
apt-get install -y poppler-utils 2>/dev/null || true

Color Palettes by Theme

ThemeTitle Bar bgAccentSlide bg
A. Metropolis (default)RGB(0, 35, 82) navyRGB(180, 30, 30) redRGB(245, 245, 245) light gray
B. Minimal (job market)RGB(0, 35, 82) navyRGB(0, 35, 82) navyRGB(255, 255, 255) white
C. Madrid (traditional)RGB(31, 73, 125) dark blueRGB(189, 152, 44) goldRGB(255, 255, 255) white

Core Helper Functions

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
import os

# ── Presentation setup ───────────────────────────────────────────
prs = Presentation()
prs.slide_width  = Inches(13.33)   # 16:9 widescreen (Beamer aspectratio=169)
prs.slide_height = Inches(7.5)

# ── Color definitions (Metropolis theme) ─────────────────────────
NAVY  = RGBColor(0, 35, 82)
RED   = RGBColor(180, 30, 30)
LGRAY = RGBColor(245, 245, 245)
WHITE = RGBColor(255, 255, 255)
BLACK = RGBColor(30, 30, 30)
MGRAY = RGBColor(100, 100, 100)


def add_bg(slide, prs, color):
    """Full-slide background rectangle."""
    shape = slide.shapes.add_shape(
        1, 0, 0, prs.slide_width, prs.slide_height)
    shape.fill.solid()
    shape.fill.fore_color.rgb = color
    shape.line.fill.background()
    return shape


def add_frame_title(slide, prs, text, bg=NAVY, fg=WHITE):
    """Navy title bar (1.1 in tall) — mimics Beamer \\frametitle."""
    bar = slide.shapes.add_shape(
        1, 0, 0, prs.slide_width, Inches(1.1))
    bar.fill.solid()
    bar.fill.fore_color.rgb = bg
    bar.line.fill.background()
    tf = bar.text_frame
    tf.word_wrap = False
    tf.margin_left = Inches(0.3)
    tf.margin_top  = Inches(0.22)
    p = tf.paragraphs[0]
    p.text = text
    p.font.bold  = True
    p.font.size  = Pt(24)
    p.font.color.rgb = fg
    p.alignment  = PP_ALIGN.LEFT


def add_progress_bar(slide, prs, current, total, color=NAVY):
    """Metropolis-style thin progress bar at bottom."""
    h   = Inches(0.055)
    top = prs.slide_height - h
    # Gray track
    track = slide.shapes.add_shape(
        1, 0, top, prs.slide_width, h)
    track.fill.solid()
    track.fill.fore_color.rgb = RGBColor(200, 200, 200)
    track.line.fill.background()
    # Filled portion
    filled_w = int(prs.slide_width * current / max(total, 1))
    if filled_w > 0:
        bar = slide.shapes.add_shape(1, 0, top, filled_w, h)
        bar.fill.solid()
        bar.fill.fore_color.rgb = color
        bar.line.fill.background()


def add_speaker_notes(slide, notes_text):
    """Add speaker notes to a slide."""
    slide.notes_slide.notes_text_frame.text = notes_text

Slide Factory Functions

# ── 1. Title slide ───────────────────────────────────────────────
def make_title_slide(prs, title, subtitle, author, institute, date_line):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_bg(slide, prs, NAVY)

    def _tb(left, top, w, h):
        tb = slide.shapes.add_textbox(
            Inches(left), Inches(top), Inches(w), Inches(h))
        tb.text_frame.word_wrap = True
        return tb.text_frame

    # Paper title
    tf = _tb(1, 1.7, 11.33, 2.0)
    p = tf.paragraphs[0]
    p.text = title; p.font.bold = True
    p.font.size = Pt(34); p.font.color.rgb = WHITE
    p.alignment = PP_ALIGN.CENTER

    # Subtitle
    if subtitle:
        p2 = tf.add_paragraph()
        p2.text = subtitle; p2.font.size = Pt(20)
        p2.font.color.rgb = LGRAY; p2.alignment = PP_ALIGN.CENTER

    # Author + institute
    tf2 = _tb(1, 4.3, 11.33, 1.4)
    p3 = tf2.paragraphs[0]
    p3.text = author; p3.font.size = Pt(18)
    p3.font.color.rgb = WHITE; p3.alignment = PP_ALIGN.CENTER
    p4 = tf2.add_paragraph()
    p4.text = institute; p4.font.size = Pt(15)
    p4.font.color.rgb = LGRAY; p4.alignment = PP_ALIGN.CENTER

    # Date / conference
    tf3 = _tb(1, 6.1, 11.33, 0.8)
    p5 = tf3.paragraphs[0]
    p5.text = date_line; p5.font.size = Pt(13)
    p5.font.color.rgb = LGRAY; p5.alignment = PP_ALIGN.CENTER
    return slide


# ── 2. Content slide (bullet list) ──────────────────────────────
def make_content_slide(prs, title, bullets,
                       current=None, total=None, bg=LGRAY):
    """
    bullets: list of (indent_level, text) tuples.
    indent_level 0 = top-level bullet, 1 = sub-bullet.
    """
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_bg(slide, prs, bg)
    add_frame_title(slide, prs, title)

    tb = slide.shapes.add_textbox(
        Inches(0.5), Inches(1.3), Inches(12.33), Inches(5.8))
    tf = tb.text_frame; tf.word_wrap = True
    for i, (lvl, text) in enumerate(bullets):
        p = tf.paragraphs[i] if i == 0 else tf.add_paragraph()
        p.text = text; p.level = lvl
        p.font.size = Pt(20 if lvl == 0 else 17)
        p.font.color.rgb = BLACK
        p.space_before = Pt(8 if lvl == 0 else 4)

    if current and total:
        add_progress_bar(slide, prs, current, total)
    return slide


# ── 3. Figure slide ──────────────────────────────────────────────
def make_figure_slide(prs, title, img_path, caption="",
                      current=None, total=None):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_bg(slide, prs, LGRAY)
    add_frame_title(slide, prs, title)

    slide.shapes.add_picture(
        img_path,
        left=Inches(1.17), top=Inches(1.3),
        width=Inches(11.0), height=Inches(5.2))

    if caption:
        cap = slide.shapes.add_textbox(
            Inches(0.5), Inches(6.6), Inches(12.33), Inches(0.7))
        cap.text_frame.paragraphs[0].text = caption
        cap.text_frame.paragraphs[0].font.size = Pt(11)
        cap.text_frame.paragraphs[0].font.color.rgb = MGRAY

    if current and total:
        add_progress_bar(slide, prs, current, total)
    return slide


# ── 4. Regression table slide ────────────────────────────────────
def make_table_slide(prs, title, headers, rows,
                     footnote="", highlight_last_col=True,
                     current=None, total=None):
    """
    headers: list of str (first col is row label).
    rows:    list of lists of str.
    Last column is treated as the preferred specification (bolded).
    """
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_bg(slide, prs, LGRAY)
    add_frame_title(slide, prs, title)

    nc = len(headers); nr = len(rows) + 1
    tbl = slide.shapes.add_table(
        nr, nc,
        Inches(0.5), Inches(1.4),
        Inches(12.33), Inches(4.5)).table

    # Header row — navy background, white bold text
    for j, h in enumerate(headers):
        c = tbl.cell(0, j)
        c.text = h
        c.text_frame.paragraphs[0].font.bold = True
        c.text_frame.paragraphs[0].font.size = Pt(14)
        c.text_frame.paragraphs[0].font.color.rgb = WHITE
        c.fill.solid(); c.fill.fore_color.rgb = NAVY

    # Data rows
    for i, row in enumerate(rows):
        for j, val in enumerate(row):
            c = tbl.cell(i + 1, j)
            c.text = str(val)
            c.text_frame.paragraphs[0].font.size = Pt(13)
            if highlight_last_col and j == nc - 1:
                c.text_frame.paragraphs[0].font.bold = True

    if footnote:
        fn = slide.shapes.add_textbox(
            Inches(0.5), Inches(6.0), Inches(12.33), Inches(1.2))
        fn.text_frame.paragraphs[0].text = footnote
        fn.text_frame.paragraphs[0].font.size = Pt(10)
        fn.text_frame.paragraphs[0].font.color.rgb = MGRAY

    if current and total:
        add_progress_bar(slide, prs, current, total)
    return slide


# ── 5. Two-column slide ──────────────────────────────────────────
def make_two_col_slide(prs, title, left_bullets, right_bullets,
                       current=None, total=None):
    """Two-column layout (e.g. Robustness slide)."""
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_bg(slide, prs, LGRAY)
    add_frame_title(slide, prs, title)

    for col_bullets, left_offset in [(left_bullets, 0.4),
                                      (right_bullets, 6.9)]:
        tb = slide.shapes.add_textbox(
            Inches(left_offset), Inches(1.35),
            Inches(5.8), Inches(5.8))
        tf = tb.text_frame; tf.word_wrap = True
        for i, (lvl, text) in enumerate(col_bullets):
            p = tf.paragraphs[i] if i == 0 else tf.add_paragraph()
            p.text = text; p.level = lvl
            p.font.size = Pt(18 if lvl == 0 else 15)
            p.font.color.rgb = BLACK
            p.space_before = Pt(6 if lvl == 0 else 3)

    if current and total:
        add_progress_bar(slide, prs, current, total)
    return slide

PDF → PNG Conversion (for figures from /plot)

import subprocess

def pdf_to_png(pdf_path, dpi=200):
    """Convert PDF figure to PNG for embedding in PPTX."""
    png_base = pdf_path.replace(".pdf", "")
    try:
   

---

*Content truncated.*

Search skills

Search the agent skills registry