CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

jbaruch/speaker-toolkit

Eleven skills for conference speakers covering rhetoric analysis, talk authoring, deck building and export, CFP submissions, publishing, illustrations, shownotes, and screencast production, informed by a 113-entry Presentation Patterns taxonomy (83 observable: 64 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns).

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

thumbnails.mdskills/illustrations/references/

Thumbnail Generation — Detail

Reference for Step 13 (thumbnail) in SKILL.md. The thumbnail-generation-rules steering rule is auto-loaded — apply it, don't restate it.

Requested Scope

A shownotes-publisher generation handoff supplies thumbnail_scope: source_only. Run the read-only preflight and Sub-steps 1–8, retaining their speaker-selection and approval gates. Return the approved source's actual absolute path as thumbnail_source to the caller. Do not invoke shownotes-publisher or run Sub-steps 9–10 in this scope; the caller already owns placement and publication. It needs no YouTube URL and may run before delivery.

Other thumbnail requests retain the full workflow below. On return from an authorized shownotes handoff, reuse any approved thumbnail_source it returns and continue at Sub-step 9 instead of generating the image twice.

Pre-Flight

Before any thumbnail work, confirm the inputs:

  1. speaker-profile.json — publishing_process.thumbnail config and visual_style_history.
  2. Image-lane authentication — follow the selected provider lane; an API lane uses its configured key.
  3. Validated outline.yaml — talk slug, metadata, and slide references; the style_anchor block (if present) feeds --portrait-style.
  4. YouTube video URL — provided by the speaker for the full workflow; unnecessary for source_only.
  5. Tracking database — load through the ingress owner's strict reader and retain its SHA-256. Use the exact non-empty config.python_path for the mutation in Sub-step 10. Missing or invalid config stops this flow and invokes Skill(skill: "vault-ingress") at Step 1.

Set host_python to the current host's explicit absolute interpreter path (not a PATH lookup). The sole interpreter-bootstrap exception is one stdlib-only owner read:

"{host_python}" "{speaker_toolkit_root}/skills/vault-ingress/scripts/read-tracking-database.py" \
  "~/.claude/rhetoric-knowledge-vault/tracking-database.json"

Resolve vault_root and python_path from that report, then immediately repeat the read with "{python_path}" against the resolved database and require the same database bytes/SHA-256. The unconfigured host_python is authorized only for the first owner-reader invocation. Every command below uses the configured interpreter; never fall back to a PATH interpreter.

Read tracking-database.json through the vault path used by presentation-creator. Selection and composition may read the generations in the owner compatibility contract without mutation. Before placing the approved thumbnail or writing tracking state, require the owner-current database, current independent records, and talk schema 8. Invoke Skill(skill: "vault-ingress") for legacy generations and stop this run. Unknown future generations are no usable prior state. Preserve every schema field and use the presentation-creator exact-generation atomic-write contract.

For the full workflow, if shownotes are needed too and don't exist yet, confirm that page publication is authorized before generating the thumbnail. An already authorized shownotes request invokes Skill(skill: "shownotes-publisher"); resume here after it returns. The source-only scope returns to its existing caller instead, as specified above.

Sub-step 1: Slide Selection

Scan the outline for high-impact slides. Suggest 3–5 candidates ranked by visual engagement:

  • Illustrations — highest impact, already designed for visual punch.
  • Bold claims / provocative statements — text that triggers curiosity.
  • Key diagrams / architecture visuals — concrete and recognizable.
  • Demo screenshots — when the demo is the main attraction.

Avoid: bio slides, shownotes URL slides, bullet-heavy slides, generic titles.

Present candidates to the speaker with slide numbers and brief descriptions. The speaker picks one — never auto-select.

Sub-step 2: Slide Image Resolution

Resolution chain:

  1. Existing illustration (preferred) — check illustrations/ for slide-{NN}.* matching the chosen slide.
  2. PPTX extraction — use the helper mode:
    "{python_path}" "{speaker_toolkit_root}/skills/illustrations/scripts/generate-thumbnail.py" \
      --extract-slide deck.pptx 15 --output slide-15.png
    Uses LibreOffice headless or PowerPoint AppleScript on macOS.
  3. Ask the speaker — if extraction fails, request a screenshot or exported image of the slide.

Sub-step 3: Speaker Photo

Resolution order:

  1. publishing_process.thumbnail.speaker_photo_path from speaker profile.
  2. Ask the speaker to provide a path or URL.

The photo must be a real photograph — never AI-generated. Expression should convey engagement and energy, not a neutral corporate headshot.

Sub-step 4: Hook Title Text

NOT the full talk title. A 3–5 word hook designed for thumbnail readability at small sizes.

  • Propose 2–3 options based on the talk's thesis and key claim.
  • The speaker confirms or edits.
  • ALL CAPS is standard.
  • Must be readable at 160×90 pixels (YouTube search-result size).

Sub-step 5: Aesthetic Selection

Walk the precedence chain (thumbnail-generation-rules Rule 7):

  1. publishing_process.thumbnail.aesthetic_preference — explicit speaker preference ("photo" or "comic_book"). Honor it and stop.
  2. visual_style_history.default_illustration_style — fuzzy-match against the comic-book family (comic_book / comic-book / halftone / illustrated / cartoon / caricature) → recommend comic_book. Documented styles outside that set (retro_tech_manual, watercolor, etc.) → out-of-scope; ask the speaker before generating.
  3. visual_style_history.confirmed_visual_intents — same fuzzy-match against each entry's pattern and rule fields.
  4. Default → photo.

Lead with the recommended aesthetic. Offer a two-candidate side-by-side only when the speaker is genuinely undecided — not as a default. Comic-book is high-variance: when it works, CTR rises significantly; when it misses, it looks off-brand.

Sub-step 6: Pass Through the Deck's Style Anchor

If outline.yaml has a style_anchor block (with full / imgtxt anchor paragraphs), pass the matching format's anchor paragraph to generate-thumbnail.py via --portrait-style "<anchor>". The script pre-stylizes the speaker photo into the anchor's medium (sepia tech-manual, watercolor, pen-and-ink, etc.) before composition, so the output palette matches the deck. Without this pass-through, photographic skin tones beside an illustrated background produce a jarring two-medium composite even when the aesthetic is otherwise correct.

If Phase 2 didn't produce a style anchor (stock-image-only deck), omit --portrait-style.

Sub-step 7: Generate

# Single recommended candidate (the precedence-chain winner)
"{python_path}" "{speaker_toolkit_root}/skills/illustrations/scripts/generate-thumbnail.py" \
  --slide-image illustrations/slide-15.png \
  --speaker-photo ~/photos/headshot.jpg \
  --title "JUDGMENT DAY" \
  --subtitle "DevNexus 2026" \
  --vault ~/.claude/rhetoric-knowledge-vault \
  --aesthetic <photo|comic_book> \
  --style slide_dominant \
  --title-position top \
  --brand-colors "#5B2C6F,#C0392B" \
  --output thumbnail.png

# Anchor-matched (when the deck has a style_anchor block)
"{python_path}" "{speaker_toolkit_root}/skills/illustrations/scripts/generate-thumbnail.py" \
  --slide-image illustrations/slide-15.png \
  --speaker-photo ~/photos/headshot.jpg \
  --title "JUDGMENT DAY" \
  --aesthetic photo \
  --portrait-style "<full anchor paragraph>" \
  --output thumbnail-anchored.png

Apply other speaker preferences from publishing_process.thumbnail:

  • style_preference → --style
  • title_position → --title-position
  • brand_colors → --brand-colors

aesthetic_preference is consumed at Sub-step 5 — don't re-apply it.

The script (per invocation, runs once per --aesthetic):

  • Sends slide image + speaker photo + prompt to Gemini as multimodal input.
  • Uses the researched prompt strategy per the chosen aesthetic.
  • Validates output: exactly 1280×720, <2MB, PNG preferred.
  • Saves to the specified output path; without --output, its default filename follows the emitted format. Retain the actual path and format from its report.

Sub-step 8: Speaker Review

Present the generated thumbnail. If rejected, iterate — never regenerate from scratch:

  • "Face looks wrong" → adjust style variant or try a different slide image. Don't add face-preservation language (it trips Gemini's safety filter on photo aesthetic).
  • "Text is unreadable" → increase contrast, change --title-position.
  • "Too busy" → switch to a simpler style variant.
  • "Wrong mood" → adjust expression guidance in the prompt.

Sub-step 9: Place Thumbnail in Shownotes Site

If publishing_process.shownotes.enabled is true, the SSG template expects the thumbnail at a specific path relative to the shownotes site root — otherwise the live page falls back to a placeholder with no warning.

Resolve the destination using publishing_process.shownotes:

{shownotes.source.path_or_url}/{shownotes.thumbnail_path_template}

with {slug} bound to the shownotes page's actual filename stem (the outline's talk slug for a new page). For Jekyll-based shownotes the default template is assets/images/thumbnails/{slug}-thumbnail.png — the nested thumbnails/ subdirectory AND the -thumbnail suffix are both mandatory. Do not strip either.

For a Jekyll page, retain its actual filename stem when resolving the convention path, including a legacy date prefix; see the shownotes owner's placement workflow. Set {thumbnail_source} to the generator's actual output path and {shownotes_thumbnail_destination} to the resolved absolute site path. Use the placement owner for every shownotes write, preserving the local source:

"{python_path}" "{speaker_toolkit_root}/skills/shownotes-publisher/scripts/place-thumbnail.py" \
  --source "{thumbnail_source}" \
  --destination "{shownotes_thumbnail_destination}"

Use the script contract linked by the placement workflow. Require exit 0 and ok: true before recording its emitted destination path in Sub-step 10. On failure, apply its reported repair and rerun; never substitute a raw copy.

If shownotes.ssg_template_pointer is set, read that file after a site redesign to re-derive the path convention. Don't reinvent it from folklore.

Sub-step 10: Tracking Database Update

Prepare this complete thumbnails[] record from the approved source and placement receipt; the example values are not defaults:

{
  "schema_version": 1,
  "talk_slug": "judgment-day",
  "youtube_url": "https://youtube.com/watch?v=...",
  "source_slide_num": 15,
  "speaker_photo_used": "/path/to/headshot.jpg",
  "thumbnail_path": "illustrations/thumbnail.png",
  "shownotes_thumbnail_path": "assets/images/thumbnails/judgment-day-thumbnail.png",
  "dimensions": "1280x720",
  "file_size_kb": 185,
  "created_at": "2026-04-20",
  "approved": true
}

Persist it with the ingress owner's upsert_thumbnail mutation, expecting the exact existing record for the slug or {"$missing": true}. In the same plan, use update_talk_publishing to set thumbnail_generated: true on the exact talk filename, expecting its current value. Dry-run the whole plan, review it, apply against the reported input SHA, and re-read as specified by the owner mutation contract. Keep that talk at schema_version: 8. illustrations is an authorized writer of current thumbnail and talk records; vault-ingress remains their schema owner. Never rewrite tracking-database.json directly.

skills

README.md

tile.json