Skip to content

feat(slides): add techne:slides, a gate and renderer for talk decks - #74

Merged
ajbarea merged 3 commits into
mainfrom
feat/slides-skill
Sep 22, 2026
Merged

ajbarea merged 3 commits into
mainfrom
feat/slides-skill

Conversation

@ajbarea

@ajbarea ajbarea commented Sep 22, 2026

Copy link
Copy Markdown
Owner

Adds techne:slides: get a talk deck ready to present, and gate the .pptx before it reaches a room.

slides.py check parses the package with the stdlib and exits 0 / 1 / 2 like techne:latex:

  • BLOCK: slides without a title placeholder, WCAG contrast (7:1, large 4.5:1, --level AA), pictures without alt text, em-dashes.
  • WARN: text under 14pt, fonts that PowerPoint or Google Slides will substitute, slides without notes, duplicate titles.
  • REVIEW: figures and walls of text on talk slides (slide 1 and a Backup slides divider onward are exempt), long titles.

Contrast is measured against what is actually behind the text: its own fill, the topmost filled shape under it, then the first of slide, layout and master that defines a background. Pictures, gradients, theme-styled, translucent and grouped surfaces are reported as unchecked rather than guessed.

slides.py render exports through PowerPoint over COM (native Windows, or from WSL by staging on the Windows side) or LibreOffice with a private profile, then writes a PNG per slide and 2x2 contact sheets. It only writes into a folder that is new, empty, or already marked as its own, and quits PowerPoint only when it started the instance and nothing else is open.

SKILL.md carries the toolchain split (Typst + Touying for decks presented as PDF, pptxgenjs when a .pptx is required), writing for a newcomer audience (claim headlines, an agenda, one idea per slide, numbers kept to the notes and backup, discussion stops), the pptxgenjs traps (table margins are inches since v3.8.0), and briefing the presenter.

Checked:

  • 40 unit tests over in-test OOXML decks. The 17 that cover the fixes in the second commit fail against the first commit's script and pass against this one.
  • Run on a real 30-slide pptxgenjs deck: it found a 3.48:1 stage label, and the deck passes once that's fixed.
  • Run on the draft deck that deck replaced: 31 untitled slides, 59 contrast failures, 6 em-dashes.
  • Rendered end to end through PowerPoint from WSL. The temp folder was removed and PowerPoint quit afterwards.
  • Pointed render at a folder holding someone's slide-hero.png and a PDF: it refused and left both untouched.
  • make validate green.

slides.py check parses a .pptx with the stdlib and gates it on real
title placeholders, WCAG contrast resolved through fills and the shape
underneath, alt text, em-dashes, small text, portable fonts, speaker
notes, and figures or dense text on talk slides. slides.py render
exports through PowerPoint (native or from WSL) or LibreOffice, then
rasterizes and writes contact sheets. The SKILL.md carries the
toolchain choice, how to write for a newcomer audience, the pptxgenjs
traps, and briefing the presenter.
… foreign files

render now claims its output folder with a marker and refuses a
non-empty folder it did not create, so it can no longer delete a
user's slide-*.png or overwrite the PDF beside their deck. check
stops at the first level that defines a background and treats
pictures, gradients, theme-styled, translucent and grouped surfaces
as unmeasured instead of misjudged; titles inherit the master title
size; paragraphs keep their separation; only an exact Backup or
Appendix divider starts the backup section; theme font references
resolve through the theme; a broken part is an ERROR, not a crash.
render also finds MSI Office, isolates LibreOffice's profile and
checks it wrote a PDF, and writes its .ps1 as UTF-8 with a BOM.
@ajbarea ajbarea added the enhancement New feature or request label Sep 22, 2026
@ajbarea ajbarea self-assigned this Sep 22, 2026
@ajbarea

ajbarea commented Sep 22, 2026

Copy link
Copy Markdown
Owner Author

Findings (all fixed in c7fd7e2, each with a regression test that fails on the first commit)

  • slides.py render: deleted every slide-*.png and overwrote <deck>.pdf in whatever folder it was given. I reproduced it: rendering into a folder holding slide-hero.png and an existing PDF removed the first and replaced the second. It now claims only new, empty, or marker-bearing folders.
  • slides.py background(): a picture, gradient or theme (bgRef) background fell through to the master's solid colour, so white-on-photo text was a false BLOCK and dark-on-photo a false pass. The first level that defines a background now decides, and non-solid means unchecked.
  • slides.py surfaces: pictures, gradient/pattern fills, p:style theme fills, alpha fills and group-local coordinates were ignored or misread. All are now unknown surfaces.
  • slides.py text: paragraphs were joined with no separator ("Top 10" + "5 items" read as the figure 105). Paragraphs and a:br now stay apart.
  • Titles with inherited size were held to the 7:1 small-text threshold. They now take the master title size.
  • Any title starting with "Backup" (a talk about backups) switched off the talk-slide gates for the rest of the deck. Only an exact Backup / Backup slides / Appendix divider does now; long-title runs on every slide, as documented.
  • Theme font references (+mn-lt) were flagged as non-portable. They resolve through the master's theme now.
  • A slide part missing from the zip crashed with a traceback. It is an ERROR finding now.
  • Render: MSI Office layout not detected; an open LibreOffice window could swallow the job while soffice exited 0; a non-ASCII temp path broke the BOM-less .ps1. Fixed: both install layouts, a private LibreOffice profile plus a PDF-exists check, UTF-8 with BOM.

Refuted

  • "The discussion slides' white text is never contrast-checked": traced the 30 unchecked runs on the real deck; every one is a slide-number field that inherits its colour. The dark slides resolve to #111111 and are measured.

Verified clean

render only rmtrees the temp folder it created with mkdtemp; PowerPoint Quit() only when this script started the instance and nothing else is open; slide order follows sldIdLst, not file names; the multi-line <p:ph tag is parsed as a title; the conftest fixture and every doc listing reference the new skill; make validate green.

@ajbarea
ajbarea merged commit 8b4de9b into main Sep 22, 2026
3 checks passed
@ajbarea
ajbarea deleted the feat/slides-skill branch September 22, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant