terminalppt
v0.1.0
Published
Present a markdown deck as an agentic terminal session — it thinks, it types, and it asks permission before showing you anything.
Maintainers
Readme
terminalppt
Present a markdown deck as an agentic terminal session — the deck thinks, types, and asks permission before it shows you anything.
npx terminalppt # present the built-in demo talk
npx terminalppt my-deck.md # present your own
No install, no config, no build step. It's markdown in, terminal talk out.
Inspired by Anthropic's The Making of Claude Code, which performs a terminal rather than merely styling one. This is the same trick, running in a real one.
Needs a truecolor terminal (Windows Terminal, iTerm2, WezTerm, Kitty, most modern ones). The avatar packs two pixels into every cell as a foreground/background pair, so without 24-bit color it's a brown lump.
What it does
- ASCII title card — the deck title through figlet's
ANSI Regularfont, revealed on an irregular cadence so it reads as a machine printing rather than a CSS animation. - A thinking beat —
✻ Recalling the incident…, with verbs tailored per chapter. A genericLoading…would read as lag; a specific verb reads as thought. - Typewriter prose — ~150 chars/sec with a
▋cursor, inlinecodetinted mid-stream. - Media as permission prompts — images, diagrams and videos don't just appear. The deck asks:
────────────────────────────────────────
Open architecture.png
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
Would you like to see this diagram?
❯ 1. Yes
2. No, skip itSay yes and it hands the file to your OS. Say no and it's simply never in the transcript.
- The avatar — the pixel-art sprite from zakaria.lu, rendered as half-block cells (
▀with a foreground and a background = two square pixels per terminal cell), hands alternating as it types.
Keys
| key | does |
| --- | --- |
| ↑ ↓ 1 2 Enter | choose, and answer media prompts |
| ← → | previous / next chapter |
| Esc | skip the media being offered |
| q | quit |
Writing a deck
Plain markdown. Five block kinds, nothing else:
# Deck Title
_By Someone — Their Role_
## Chapter Title
Prose types out. Backticks give you `inline code`, asterisks give you *emphasis*.
### A heading lands instantly
> @handle (Their Role): A quote, with a speaker line. Two different speakers
> in a row get a beat of silence between them.
```java
// Code appears in a box, all at once. It is not typed — you want people
// reading it, not watching it arrive.
The alt text picks the question: something matching `diagram|architecture|flow` asks *"see this diagram?"*, a `.mp4` asks *"watch this video?"*, a bare URL asks *"open this link?"*.
## Options
--no-motion skip typewriter and spinners (also honours NO_MOTION=1) --plain dump the deck as text and exit
`TERMINALPPT_NO_OPEN=1` stops accepted media from actually shelling out to the OS — used by the harness, handy for headless runs.
## Development
```bash
npm run dev # tsx src/cli.tsx decks/talk.md
npm run build # esbuild -> dist/cli.js
npm run typecheck
npm pack # inspect exactly what shipssrc/harness.tsx drives the real app with fake TTY streams — it presses keys, waits, and dumps rendered frames to stdout. That's how the flow is tested without a terminal:
TERMINALPPT_NO_OPEN=1 npx tsx src/harness.tsx decks/talk.mdThe README GIF is generated the same way, no screen recorder involved — the app is driven through fake streams, its ANSI frames are sampled on a clock, and a rasterizer turns them into a GIF:
FORCE_COLOR=3 TERMINALPPT_NO_OPEN=1 npx tsx scripts/record.tsx decks/demo.md
python scripts/gif.pyHow it fits together
deck.ts markdown -> blocks (heading | prose | quote | code | media)
theme.ts palette, spinner glyphs, thinking verbs, and every timing constant
ascii.ts figlet banner + the avatar sprite as half-block cells
hooks.ts typewriter, spinner, staggered reveal
Chapter.tsx the state machine: thinking -> flowing -> (pausing -> prompting) -> doneEvery delay lives in TIMING in theme.ts. They are the product — the whole
illusion is timing, and it's meant to be tuned in one place.
Contributing
Decks, themes and fixes all welcome. The two things worth knowing before you open a PR:
- ASCII art must never soft-wrap. Anything holding figlet output or the
sprite is measured against its real available width and marked
flexShrink={0}— it overflows or hides, it never reflows. One wrapped column destroys a glyph. - Settled blocks belong in
<Static>. Ink repaints its live frame on every typewriter tick;<Static>prints once into real scrollback and never repaints. It's why a 200-line chapter costs the same per tick as a 5-line one.
License
MIT © Zakaria Bouazza
The pixel-art avatar is from zakaria.lu. If you fork this
for your own talks, swap the sprite in src/ascii.ts — it's a plain ASCII grid
plus a color map, so you can edit it in a text editor.
