Skip to content

Add Deck for multi-slide and multi-image output#18

Open
sjquant wants to merge 6 commits into
mainfrom
claude/elegant-archimedes-l1b0io
Open

Add Deck for multi-slide and multi-image output#18
sjquant wants to merge 6 commits into
mainfrom
claude/elegant-archimedes-l1b0io

Conversation

@sjquant

@sjquant sjquant commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Why

quickthumb could render a single canvas to PPTX (one slide) and PDF (one page), but had no way to produce a multi-slide deck or a batch of related images from several canvases. This adds a thin container that sequences existing canvases into multi-output documents and image sets, reusing the entire per-canvas render pipeline so each slide looks identical to rendering that canvas on its own.

Changes

  • New Deck (quickthumb/deck.py), exported from the package alongside DeckDiagnostic:
    • Collect slides with Deck([...]), .slide(canvas), .add(*canvases); supports len(), indexing, and iteration.
    • render(path) dispatches on extension: .pdf → one multi-page PDF, .pptx → one multi-slide PPTX, raster (.png/.jpg/.jpeg/.webp) → a zero-padded numbered sequence (slides_01.png, …), returning the written paths.
    • to_pdf() / to_pptx() return document bytes; contact_sheet(columns=…) composes all slides into a single grid Canvas.
    • diagnose() aggregates each slide's findings tagged with slide_index, plus a mixed-slide-size warning when slide dimensions differ (PPTX uses the first slide's size for the whole deck).
    • to_json() / from_json() round-trip via each canvas's serialization.
  • Refactored the PDF and PPTX exporters to emit one page/slide per canvas (save_canvases / export_bytes_canvases); the single-canvas API is unchanged.
  • Tests: tests/test_deck.py adds black-box coverage for composition, the numbered raster sequence, PDF/PPTX page counts, mixed-size handling, diagnostics, contact sheet, and JSON round-trip.
  • Docs: README section + feature-matrix row, a new docs/api/deck.md reference page (wired into nav and the API overview), and a Decks section in docs/exports.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB


Generated by Claude Code

claude added 6 commits June 24, 2026 12:33
Introduce a Deck container that collects Canvas slides and exports them
to a multi-page PDF, a multi-slide PPTX, a numbered raster sequence, or a
single contact-sheet grid. Slides may have mixed sizes; diagnose()
aggregates per-slide findings and warns on size mismatch. Refactor the
PDF and PPTX exporters to emit one page/slide per canvas while keeping
the single-canvas API unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
- render(): drop the format-based dispatch escape hatch, reject a raster
  format override on document output, and give .svg its own clear error.
- slides property returns a copy so the Canvas type guard can't be bypassed.
- contact_sheet validates image paths up front for consistent errors.
- PPTX exporter no longer validates the first slide twice.
- Document that mixed-size decks clip larger later slides in PPTX.
- Add tests for render dispatch, to_pptx() bytes, and column clamping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
Add an optional default size to Deck (Deck(width, height) and
Deck.from_aspect_ratio) so slides can be written as bare Canvas() that
inherit it, instead of repeating dimensions on every slide. An explicitly
sized canvas keeps its own size.

Canvas now accepts being constructed without a size: it stays "unsized"
(accepting layer builders but refusing render/diagnose/serialize with a
clear error) until a size is assigned directly or injected when the canvas
is added to a sized deck. The Deck constructor takes width/height first;
pre-built canvases now pass via Deck(slides=[...]).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
- Drop the dummy first-slide arg from the multi-canvas exporter calls by
  making PdfExporter/PptxExporter accept an optional canvas; single-canvas
  save()/export_bytes() now guard for it explicitly.
- Preserve a deck's default size and a deck-level theme across to_json/
  from_json; the shared theme resolves $theme.* tokens in every slide.
- Share one aspect_ratio_dimensions() helper between Canvas and Deck and
  raise ValidationError for malformed ratios.
- Pre-validate all slide assets before writing a raster sequence so a
  failing slide leaves no partial output on disk.
- contact_sheet() accepts an (R,G,B[,A]) tuple and validates the
  background eagerly instead of failing opaquely at render time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
The contact-sheet grid was speculative surface area: nothing in the
library, examples, or workflows used it, and a proof-sheet image is a
separate concern from the multi-page/multi-slide/sequence outputs the
deck exists for. Drop the method, its private helpers, the now-unused PIL
import, and the related tests and docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
Deck(slides=[...]) already covers seeding several slides at once and
.slide() covers appending one, so a separate variadic add() was redundant
surface area. Drop it and update tests and docs to use the constructor or
chained slide() calls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLVAwDncqBSdzXnid1YMFB
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.

2 participants