teasr
Automated project showcase capture — screenshots and GIFs from web apps, desktop, and terminal. Single binary, no runtime dependencies.
Download
·
Report Bug
·
CI Integration
Showcase
| Web Capture | Terminal Capture |
![]() |
![]() |
| Local HTML | Markdown |
![]() |
![]() |
Contents
- Why teasr
- Installation
- Quick Start
- Capture Modes
- Configuration Reference
- CLI Reference
- Output Formats
- CI Integration
- Workspace
- Agent Skill
- License
Why teasr
| teasr | Node/Playwright approach | |
|---|---|---|
| Runtime | Single binary | Node.js + npm install |
| Terminal render | Built-in (ANSI → SVG → PNG) | External tools |
| GIF encoding | gifski (pure Rust) | FFmpeg or ImageMagick |
| Config | teasr.toml | JS/TS config file |
| Server cleanup | Process group kill | Manual or best-effort |
Installation
Shell installer (recommended):
curl -fsSL https://raw.githubusercontent.com/urmzd/teasr/main/install.sh | sh
Cargo:
cargo install teasr-cli
GitHub Action: see CI Integration below.
Quick Start
Create teasr.toml in your project root:
[server]
command = "npm run dev"
url = "http://localhost:3000"
timeout = 10000
[output]
dir = "./showcase"
formats = [{ output_type = "png" }]
[[scenes]]
type = "web"
uri = "/"
name = "homepage"
formats = [{ output_type = "gif" }, { output_type = "png" }]
[[scenes.interactions]]
type = "snapshot"
[[scenes.interactions]]
type = "click"
selector = "#get-started"
[[scenes.interactions]]
type = "snapshot"
[[scenes]]
type = "terminal"
name = "cli-help"
theme = "dracula"
cols = 90
rows = 24
formats = [{ output_type = "gif" }, { output_type = "png" }]
[[scenes.interactions]]
type = "type"
text = "teasr --help"
speed = 50
[[scenes.interactions]]
type = "key"
key = "enter"
[[scenes.interactions]]
type = "wait"
duration = 2000
Then run:
teasr run
Output files are written to ./showcase/.
Capture Modes
All three capture modes — terminal, web, screen — use a unified [[scenes.interactions]] syntax. Every interaction type is accepted by every mode; unsupported interactions are silently skipped (visible with --verbose). The web scene loads remote URLs, local files (HTML/SVG/PDF), or Markdown files through the same headless-Chrome renderer.
Interaction Types
| Type | Fields | Description |
|---|---|---|
type | text, speed (ms per char, optional) | Type text (terminal: PTY input, web: keyboard events) |
key | key (e.g. "enter") | Press a key |
click | selector (CSS selector, optional) | Click an element |
hover | selector (CSS selector, optional) | Hover over an element |
scroll-to | selector (CSS selector, optional) | Scroll an element into view |
wait | duration (ms, default 1000) | Pause. Terminal/screen emit one frame at the end of the pause (duration_ms = duration); web emits nothing — follow with snapshot to capture post-pause state. |
snapshot | name (optional) | Capture the current state as a frame |
Every interaction also accepts a hidden flag (default false). Hidden interactions execute normally but their frames are excluded from output — useful for setup steps (e.g. typing a command) that should not appear in the final GIF or screenshot.
[[scenes.interactions]]
type = "type"
text = "cd my-project"
hidden = true
[[scenes.interactions]]
type = "key"
key = "enter"
hidden = true
[[scenes.interactions]]
type = "wait"
duration = 500
hidden = true
Web
Loads a URI in headless Chrome (via chromiumoxide). The uri field picks what to load:
http:///https://— remote URL (a leading/joins against[server].urlif configured)*.md/*.markdown— Markdown file rendered to styled HTML- anything else — local file (HTML, SVG, PDF) loaded via
file://
Requires Chrome or Chromium to be installed.
# Remote URL (or server-relative path with [server])
[[scenes]]
type = "web"
uri = "/dashboard"
name = "dashboard"
viewport = { width = 1440, height = 900 }
formats = [{ output_type = "png" }, { output_type = "gif" }]
[[scenes.interactions]]
type = "click"
selector = "#open-modal"
[[scenes.interactions]]
type = "snapshot"
name = "modal-open"
# Local HTML / SVG
[[scenes]]
type = "web"
uri = "./docs/preview.html"
name = "docs-preview"
[[scenes.interactions]]
type = "snapshot"
# PDF (page selection via the `page` field)
[[scenes]]
type = "web"
uri = "./spec.pdf"
page = 2
[[scenes.interactions]]
type = "snapshot"
# Markdown (rendered with the bundled GitHub-style template)
[[scenes]]
type = "web"
uri = "./README.md"
theme = "dark" # "light" (default) or "dark"
flavor = "github" # "github" (default), "commonmark", or "custom"
full_page = true
[[scenes.interactions]]
type = "snapshot"
Web scene fields:
| Field | Type | Default | Description |
|---|---|---|---|
uri | string | required | Remote URL, server-relative path, local file, or Markdown file |
name | string | uri value | Output filename base |
viewport | object | 1280x720 | { width, height } |
formats | array | output.formats | Per-scene format override |
interactions | array | [] | Sequence of interactions |
full_page | boolean | false | Capture full page height instead of just the viewport |
frame_duration | integer | 100 | Milliseconds per frame in GIF output |
page | integer | 1 | PDF page to capture (only applies when uri is a PDF) |
theme | string | "light" | Markdown theme: "light" or "dark" (Markdown only) |
flavor | string | "github" | Markdown flavor: "github", "commonmark", "custom" (Markdown only) |
stylesheet | string | — | Path to a custom CSS file appended after the default styles (Markdown only) |
template | string | — | Path to a full HTML template with {{content}} placeholder; overrides the default template (Markdown only) |
Markdown flavor details:
| Flavor | Behavior |
|---|---|
github | GitHub Flavored Markdown — tables, task lists, autolinks, strikethrough, footnotes |
commonmark | Strict CommonMark — no extensions |
custom | GFM extensions enabled; pair with stylesheet to apply your own visual style |
Supported interactions: click, hover, scroll-to, wait, snapshot, type, key
Terminal
Scripts an interactive PTY session, captures frames at each interaction, and renders them as animated GIFs or PNGs with terminal chrome (title bar, traffic light buttons).
[[scenes]]
type = "terminal"
name = "test-output"
theme = "dracula"
cols = 100
rows = 24
formats = [{ output_type = "gif" }, { output_type = "png" }]
frame_duration = 80
[[scenes.interactions]]
type = "type"
text = "cargo test 2>&1"
speed = 50
[[scenes.interactions]]
type = "key"
key = "enter"
[[scenes.interactions]]
type = "wait"
duration = 2000
Terminal scene fields:
| Field | Type | Default | Description |
|---|---|---|---|
name | string | "recording" | Output filename base |
theme | string | "dracula" | "dracula" or "monokai" |
cols | integer | 80 | Terminal width in columns |
rows | integer | 24 | Terminal height in rows |
interactions | array | [] | Sequence of interactions |
frame_duration | integer | 100 | Milliseconds per frame in GIF output |
formats | array | output.formats | Per-scene format override |
Supported interactions: type, key, wait, snapshot
Screen
Captures a display, window, or region using native screen capture (xcap). Screenshots are automatically wrapped in macOS-style window chrome (matching terminal output). Supports multi-frame GIF output when multiple snapshot + wait interactions are configured.
[[scenes]]
type = "screen"
name = "native-app"
setup = "open MyApp.app"
delay = 2000
theme = "dracula"
title = "My App"
formats = [{ output_type = "gif" }, { output_type = "png" }]
[[scenes.interactions]]
type = "snapshot"
[[scenes.interactions]]
type = "wait"
duration = 1000
[[scenes.interactions]]
type = "snapshot"
Screen scene fields:
| Field | Type | Default | Description |
|---|---|---|---|
name | string | "screen" | Output filename base |
display | integer | primary | Display index (ignored if window is set) |
window | string | — | Window title or app name substring (case-insensitive) |
region | object | full display | { x, y, width, height } |
setup | string | — | Shell command run before capture |
delay | integer | — | Milliseconds to wait after setup |
interactions | array | [] | Sequence of interactions |
frame_duration | integer | 100 | Milliseconds per frame in GIF output |
title | string | "Screen Capture" | Title shown in chrome frame title bar |
theme | string | "dracula" | Chrome frame theme: "dracula" or "monokai" |
formats | array | output.formats | Per-scene format override |
Supported interactions: snapshot, wait
Configuration Reference
[server]
Optional. Starts a process before capture and health-polls it until ready. The process group is killed on exit — no orphaned processes.
[server]
command = "npm run dev"
url = "http://localhost:3000"
timeout = 10000 # ms to wait for server to be ready (default: 10000)
[output]
[output]
dir = "./showcase" # default: "./teasr-output"
formats = [{ output_type = "png" }] # default: [{ output_type = "png" }]. Options: "png", "gif", "mp4"
[[scenes]]
Each [[scenes]] entry is one of the three types described above. The type field is required and must be "web", "terminal", or "screen".
Config file discovery walks up from the current directory to the filesystem root, so running teasr from any subdirectory of your project will find teasr.toml at the root.
CLI Reference
teasr [COMMAND]
Commands:
run Run capture scenes from teasr.toml (alias: `showme`)
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
teasr run
teasr run [OPTIONS]
Options:
-c, --config <PATH> Path to teasr.toml (default: auto-discover)
-o, --output <DIR> Output directory (overrides config)
--formats <FMT,...> Output formats: png, gif, mp4 (overrides config)
--verbose Enable debug logging
--timeout <MS> Global timeout in ms [default: 60000]
--fps <N> Frames per second (overrides config)
--seconds <N> Target output duration in seconds (overrides config)
--scene-timeout <N> Per-scene wall-clock timeout in seconds (overrides config)
-h, --help Print help
--formats accepts comma-separated values: --formats png,gif,mp4
Output Formats
| Format | Notes |
|---|---|
png | Lossless screenshot. Native, no external tools required. |
gif | Animated GIF from multi-frame session recording, encoded with gifski (pure Rust). |
mp4 | Video output from multi-frame session recording. |
CI Integration
The GitHub Action downloads the appropriate pre-built binary from releases, installs Chrome, and runs teasr run. All configuration comes from teasr.toml.
- uses: urmzd/teasr@v1
with:
version: "latest" # optional, pin to e.g. "0.11.0"
scenes: "web,terminal" # optional, default: all
install-chrome: "true" # optional, set "false" if Chrome is already available
install-fonts: "" # optional, space-separated font families
config: "" # optional, path to teasr.toml
args: "" # optional, extra flags for `teasr run`
Supported runners: ubuntu-*, macos-*, windows-* on x64 and ARM64.
Workspace
teasr is a Cargo workspace with two crates:
| Crate | Description |
|---|---|
teasr-cli | CLI entry point (teasr binary) |
teasr-core | Capture, config, orchestration, and ANSI → SVG → PNG terminal rendering |
Agent Skill
This repo’s conventions are available as portable agent skills in skills/.
License
Apache-2.0



