Skip to content

Add comprehensive showcase decks demonstrating post-fork features#17

Merged
CodeHalwell merged 4 commits intomasterfrom
claude/powerpoint-examples-suite-uoGqz
Apr 29, 2026
Merged

Add comprehensive showcase decks demonstrating post-fork features#17
CodeHalwell merged 4 commits intomasterfrom
claude/powerpoint-examples-suite-uoGqz

Conversation

@CodeHalwell
Copy link
Copy Markdown
Owner

Summary

This PR adds a complete suite of six self-contained example decks that comprehensively demonstrate every post-fork feature of power-pptx in a single, brand-aligned identity. The showcase covers design tokens & recipes, charts with palettes and gradients, visual effects (shadows, glows, gradients, alpha), animations & transitions, space-aware authoring with the linter, and branded tables with custom borders.

Each deck is small (1–6 slides) and isolates one feature area so the output is easy to review. All scripts share a common design-token spec (_tokens.py) and a lint-on-save helper (_lint.py). A build script (build_all.py) generates all decks and renders PNG thumbnails via LibreOffice + pdftoppm.

Changes

  • Six showcase decks (01_design_system.py through 06_tables.py):

    • 01_design_system.py: Token-driven recipes (title, kpi, bullet, quote, image_hero) + Grid-laid feature cards
    • 02_charts.py: Chart palettes, quick layouts, per-series gradients, per-data-point coloring
    • 03_visual_effects.py: Shadows, glows, soft edges, linear/radial/shape gradients, alpha-tinted glass
    • 04_animations.py: Sequenced entrance/emphasis, per-paragraph reveal, motion paths, transitions
    • 05_space_aware.py: fit_text vs naive sizing, TEXT_TO_FIT_SHAPE fallback, linter detection + auto-fix demo
    • 06_tables.py: Branded table with Cell.borders, alternating row fill, conditional delta coloring
  • Shared utilities:

    • _tokens.py: Single DesignTokens spec (palette, typography, shadows, radii, spacings, chart palette)
    • _lint.py: lint_or_die() helper for consistent pre-save validation
    • build_all.py: Build all decks and render thumbnails (requires LibreOffice + pdftoppm)
    • assets/_make_assets.py: Generate synthetic hero image (radial gradient backdrop)
  • Documentation:

    • README.md: Overview of each deck, build instructions, what to look for in each
  • Pre-built outputs:

    • Six .pptx files in _out/
    • PNG thumbnails (one per slide) in _out/thumbs/

Test notes

The showcase decks are self-validating: each runs the linter before save (via lint_or_die or inline slide.lint().auto_fix()), ensuring all generated presentations are well-formed. The 05_space_aware deck deliberately includes a broken slide to demonstrate linter detection and auto-fix.

To verify:

python examples/showcase/build_all.py
# or individually:
python examples/showcase/01_design_system.py
python examples/showcase/02_charts.py
# ... etc

Each script prints the output path and any lint findings. All decks should save without errors (except 05_space_aware, which prints residual issues after auto-fix as part of the demo).

https://claude.ai/code/session_01TTPAcGiq4DL9nzBGng94Pt

claude added 3 commits April 28, 2026 22:37
…humbnails

Six small, self-contained example decks that each isolate one feature
area (design tokens + recipes, charts, visual effects, animations,
space-aware authoring, tables) plus a build_all script that renders
one PNG per slide via PDF + pdftoppm so reviewers can scan the suite
without opening PowerPoint.

https://claude.ai/code/session_01TTPAcGiq4DL9nzBGng94Pt
…visible

The left "naive Pt(36)" box was inheriting add_textbox's default
auto_size=SHAPE_TO_FIT_TEXT and silently growing to fit, and font
size was being set on the paragraph (no run yet) instead of the run
itself, so it never actually applied. Both boxes ended up rendering
identically — the demo wasn't showing anything.

Fix: pin auto_size=NONE on the naive box, style the runs explicitly,
and tighten the box height to 2.0 inches so 36pt visibly overflows
and fit_text has to drop the size to 30pt to make it fit. The
6pt difference is now obvious at a glance.

https://claude.ai/code/session_01TTPAcGiq4DL9nzBGng94Pt
The previous 2.0in box was tall enough that fit_text picked 30pt and
then the renderer wrapped to 5 lines, which still overflowed — there
was a small discrepancy between fit_text's measurement and the
LibreOffice renderer's wrap width. With a 1.5in box, fit_text picks
22pt (a 14pt drop from the naive 36pt) and the result fits cleanly.

Also added auto_size=TEXT_TO_FIT_SHAPE on the fit_text box as
belt-and-braces, in case the renderer disagrees with fit_text's
Pillow-based measurement by a couple of points.

https://claude.ai/code/session_01TTPAcGiq4DL9nzBGng94Pt
Copilot AI review requested due to automatic review settings April 29, 2026 04:05
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 749d5d997d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/showcase/04_animations.py Outdated
Comment on lines +78 to +79
if shape.shape_type == MSO_SHAPE.ROUNDED_RECTANGLE:
Emphasis.pulse(s3, shape)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use auto_shape_type when selecting KPI cards

The KPI pulse loop never matches any shape because shape.shape_type returns a MSO_SHAPE_TYPE value (e.g. AUTO_SHAPE), but the code compares it to MSO_SHAPE.ROUNDED_RECTANGLE (an auto-shape subtype). In this slide, that makes the Emphasis.pulse(...) calls unreachable, so the deck does not actually demonstrate the per-card emphasis animation it advertises. Filter with shape.auto_shape_type (or check shape_type == AUTO_SHAPE first) so the KPI cards are animated.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive showcase suite for the power-pptx library, featuring six example scripts that demonstrate core functionalities such as design tokens, chart palettes, visual effects, animations, and space-aware text fitting. The suite is supported by build automation, shared design tokens, and a linting utility. Review feedback focuses on cleaning up an unnecessary lint suppression and improving error handling within the build process.

Comment thread examples/showcase/05_space_aware.py Outdated
from pptx.enum.text import MSO_AUTO_SIZE
from pptx.util import Inches, Pt

from _tokens import BRAND # noqa: F401 (kept for parity)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment '# noqa: F401 (kept for parity)' is unnecessary as the import is used in the code (e.g., in the build function or elsewhere). If it is truly unused, it should be removed instead of suppressed.

Comment thread examples/showcase/build_all.py Outdated
def _load(name: str):
path = HERE / f"{name}.py"
spec = importlib.util.spec_from_file_location(name, path)
assert spec and spec.loader
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The assertion 'assert spec and spec.loader' is good for runtime safety, but consider using a more descriptive error message or a proper exception if the loader is missing, as this is a critical part of the build process.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR introduces a self-contained “showcase” suite under examples/showcase/ to demonstrate post-fork power-pptx features (design tokens/recipes, charts, visual effects, animations, space-aware authoring + linting, and tables), alongside build tooling and documentation.

Changes:

  • Added six runnable showcase deck scripts (01_...py06_...py) that build small, focused .pptx outputs.
  • Added shared helpers for consistent branding (_tokens.py), lint-on-save (_lint.py), and an all-decks build + thumbnail renderer (build_all.py).
  • Added README plus an asset generator script for the synthetic hero image.

Reviewed changes

Copilot reviewed 12 out of 41 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
examples/showcase/build_all.py Builds all showcase decks and renders per-slide thumbnails via LibreOffice + pdftoppm.
examples/showcase/assets/_make_assets.py Generates the synthetic hero.jpg asset used by decks.
examples/showcase/_tokens.py Defines shared DesignTokens and chart palette for consistent branding.
examples/showcase/_lint.py Adds a shared “lint then fail on residual errors” helper.
examples/showcase/README.md Documents how to build/run decks and what each deck demonstrates.
examples/showcase/01_design_system.py Deck demonstrating tokens + recipes + Grid layout.
examples/showcase/02_charts.py Deck demonstrating chart palettes, quick layouts, and per-series/point fills.
examples/showcase/03_visual_effects.py Deck demonstrating shadows, glows, gradients, and alpha fills.
examples/showcase/04_animations.py Deck demonstrating animations, motion paths, and transitions.
examples/showcase/05_space_aware.py Deck demonstrating fit_text, auto-size fallback, and the linter + auto-fix.
examples/showcase/06_tables.py Deck demonstrating branded tables and the Cell.borders API.
examples/showcase/.gitignore Ignores Python bytecode/cache in the showcase directory.
Comments suppressed due to low confidence (6)

examples/showcase/build_all.py:1

  • assert spec and spec.loader can be stripped when Python is run with optimizations (-O), which would turn an import failure into a more confusing runtime error downstream. Prefer an explicit check that raises a descriptive exception (e.g., ImportError / FileNotFoundError) including the script path and module name.
    examples/showcase/build_all.py:1
  • If sub/ already contains older slide-*.png from a previous run (especially when a deck’s slide count decreases), the final glob will include stale thumbnails and the reported count will be wrong. Before running pdftoppm, delete existing slide-*.png in sub (or render into a temporary directory and replace) to ensure the output accurately reflects the current deck.
    examples/showcase/build_all.py:1
  • If sub/ already contains older slide-*.png from a previous run (especially when a deck’s slide count decreases), the final glob will include stale thumbnails and the reported count will be wrong. Before running pdftoppm, delete existing slide-*.png in sub (or render into a temporary directory and replace) to ensure the output accurately reflects the current deck.
    examples/showcase/build_all.py:1
  • If sub/ already contains older slide-*.png from a previous run (especially when a deck’s slide count decreases), the final glob will include stale thumbnails and the reported count will be wrong. Before running pdftoppm, delete existing slide-*.png in sub (or render into a temporary directory and replace) to ensure the output accurately reflects the current deck.
    examples/showcase/build_all.py:1
  • Both external conversions can hang indefinitely in some environments (LibreOffice profile/locking issues, font problems, etc.). Consider adding a reasonable timeout= and including a short excerpt of both stdout/stderr in the failure message to make CI and local troubleshooting more reliable.
    examples/showcase/build_all.py:1
  • Both external conversions can hang indefinitely in some environments (LibreOffice profile/locking issues, font problems, etc.). Consider adding a reasonable timeout= and including a short excerpt of both stdout/stderr in the failure message to make CI and local troubleshooting more reliable.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +77 to +79
for shape in s3.shapes:
if shape.shape_type == MSO_SHAPE.ROUNDED_RECTANGLE:
Emphasis.pulse(s3, shape)
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shape.shape_type is the high-level shape category (e.g., auto-shape vs placeholder), not the specific auto-shape kind. Comparing it to MSO_SHAPE.ROUNDED_RECTANGLE will likely never match, so the pulse effect may not be applied to any KPI cards. Use the auto-shape discriminator (commonly shape.auto_shape_type) and/or guard for auto-shapes before comparing to MSO_SHAPE.ROUNDED_RECTANGLE.

Copilot uses AI. Check for mistakes.
Comment thread examples/showcase/04_animations.py Outdated
badge.line.fill.background()
badge.text_frame.text = "Go"
p = badge.text_frame.paragraphs[0]
p.alignment = 2 # center
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a numeric literal for alignment makes the intent and enum mapping unclear and brittle. Prefer the appropriate enum constant (e.g., PP_ALIGN.CENTER) by importing it from pptx.enum.text.

Copilot uses AI. Check for mistakes.
Comment thread examples/showcase/05_space_aware.py Outdated
Comment on lines +192 to +197
"This text is set at 28pt without word-wrapping a long enough string "
"to overflow the height — fit_text would prevent this, but we're "
"skipping it on purpose to show the lint output."
)
otf.paragraphs[0].font.size = Pt(28)
otf.paragraphs[0].font.color.rgb = NEUTRAL
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The demo text says it’s 'without word-wrapping' but otf.word_wrap = True, which makes the explanation misleading. Also, earlier in this file you note paragraph-level font settings may not apply to existing runs; here you set otf.text (creating a run) and then set paragraph.font.size, which may fail to enforce 28pt and weaken the intended overflow/lint demonstration. Update the explanatory text (or the word_wrap setting) and apply font size/color to the created run to ensure the slide reliably triggers the linter as intended.

Suggested change
"This text is set at 28pt without word-wrapping a long enough string "
"to overflow the height — fit_text would prevent this, but we're "
"skipping it on purpose to show the lint output."
)
otf.paragraphs[0].font.size = Pt(28)
otf.paragraphs[0].font.color.rgb = NEUTRAL
"This text is set at 28pt with word-wrapping and uses a long enough "
"string to overflow the height — fit_text would prevent this, but "
"we're skipping it on purpose to show the lint output."
)
p = otf.paragraphs[0]
run = p.runs[0]
run.font.size = Pt(28)
run.font.color.rgb = NEUTRAL

Copilot uses AI. Check for mistakes.
Comment thread examples/showcase/_lint.py Outdated
errors: list[str] = []
for i, slide in enumerate(prs.slides):
for issue in slide.lint().issues:
if issue.severity.value == "error":
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing issue.severity.value to the string 'error' is brittle (it depends on the enum’s .value representation). Prefer comparing against the enum member itself (or a stable attribute like .name) to avoid breakage if the enum changes its underlying values.

Suggested change
if issue.severity.value == "error":
if issue.severity.name.lower() == "error":

Copilot uses AI. Check for mistakes.
Fixes:

* 04_animations.py: the KPI pulse loop never matched any shape because
  shape_type returns AUTO_SHAPE (the high-level category), not the
  specific subtype — pulses were silently no-ops. Filter on shape_type
  first, then auto_shape_type. Three Pulse effects now actually emit
  XML on the KPI slide. (Codex P2 / Copilot)
* 04_animations.py: replace magic `p.alignment = 2` with PP_ALIGN.CENTER
  and apply font to the badge's run, not the paragraph. (Copilot)
* _lint.py: compare severity against LintSeverity.ERROR enum directly
  rather than its `.value` string — robust if the enum representation
  changes. (Copilot)
* 05_space_aware.py: drop the unused `BRAND` import (the
  "kept for parity" comment was dead). (Gemini)
* 05_space_aware.py: fix the lint-demo overflow box — pin
  auto_size=NONE, apply font/color to the run (paragraph-level
  properties don't reach already-authored runs), and rewrite the
  misleading "without word-wrapping" demo text. The lint pass now
  catches both the off-slide rectangle AND the text overflow as
  intended. (Copilot)
* build_all.py: replace `assert spec and spec.loader` with an
  explicit RuntimeError that survives `python -O`. (Gemini)
* build_all.py: clean stale `slide-*.png` before pdftoppm so a
  shrunken deck doesn't leave orphans inflating the count; add
  120s/60s subprocess timeouts and surface stderr/stdout excerpts
  on failure. (Copilot, suppressed)

https://claude.ai/code/session_01TTPAcGiq4DL9nzBGng94Pt
@CodeHalwell CodeHalwell merged commit 09cf1ef into master Apr 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants