CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/speaker-toolkit

Seven-skill presentation system: ingest talks into a rhetoric vault, run interactive clarification, generate a speaker profile, create presentations that match your documented patterns, produce the deck illustrations + thumbnail visual layer, create and publish talk-content Agent Skills with talk pages to a Jekyll shownotes site, and verify a recorded screencast against its storyboard. Includes a 113-entry Presentation Patterns taxonomy (83 observable: 64 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns) for scoring, brainstorming, and go-live preparation.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

notes-to-packed.pyskills/presentation-creator/scripts/

#!/usr/bin/env python3
"""Pack a 0-based speaker-notes JSON map into the SetSpeakerNotes wire format.

Input JSON: `{"<0-based slide #>": "notes text", ...}` — the historical
inject-speaker-notes.py format. Output: a UTF-8 file of records
`<1-based #>US<text>` joined by RS, where US=Chr(31), RS=Chr(30) (non-printing
control chars that don't occur in prose notes). AppleScript reads the file as
UTF-8 and passes it to the SetSpeakerNotes VBA macro, which splits it.

Slide numbers are converted 0-based -> 1-based (python-pptx uses 0-based
indices; PowerPoint VBA uses 1-based). Empty notes are dropped.

Usage:
    notes-to-packed.py <notes.json> <out.packed>
"""

import json
import sys
from pathlib import Path

RS = "\x1e"  # record separator
US = "\x1f"  # unit separator


def pack_notes(notes_map: object) -> str:
    """Return RS-joined `<1-based #>US<text>` records, sorted by slide number.

    0-based keys become 1-based; empty notes are dropped. Raises ValueError with
    an actionable message on malformed input (non-object map, non-integer key,
    non-string text, or text containing the RS/US control chars).
    """
    if not isinstance(notes_map, dict):
        raise ValueError(
            'notes JSON must be an object {"<0-based slide #>": "text", ...}'
        )

    def slide_num(key: object) -> int:
        # JSON object keys are strings; an int key can only come from a manifest
        # built in-process. Anything else takes the same rejection path it
        # always did, now without asking int() to consume an arbitrary object.
        if isinstance(key, (str, int)):
            try:
                return int(key)
            except ValueError:
                pass
        raise ValueError(f"notes key {key!r} is not an integer")

    records = []
    for key in sorted(notes_map, key=slide_num):
        num = slide_num(key)
        if num < 0:
            raise ValueError(
                f"notes key {key!r} is negative; slide indices are 0-based (>= 0)"
            )
        text = notes_map[key]
        if text is None or text == "":
            continue
        if not isinstance(text, str):
            raise ValueError(
                f"notes for slide {key} must be a string, got {type(text).__name__}"
            )
        if RS in text or US in text:
            raise ValueError(
                f"notes for slide {key} contain a reserved control char (RS/US)"
            )
        records.append(f"{num + 1}{US}{text}")
    return RS.join(records)


def main() -> None:
    if len(sys.argv) != 3:
        print("usage: notes-to-packed.py <notes.json> <out.packed>", file=sys.stderr)
        sys.exit(2)
    try:
        notes_map = json.loads(Path(sys.argv[1]).read_text())
        packed = pack_notes(notes_map)
    except (OSError, ValueError, json.JSONDecodeError) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        sys.exit(1)
    out_path = sys.argv[2]
    Path(out_path).write_text(packed, encoding="utf-8")
    count = 0 if packed == "" else packed.count(RS) + 1
    print(json.dumps({"records": count, "output": out_path}))


if __name__ == "__main__":
    main()

skills

presentation-creator

SKILL.md

README.md

tile.json