Skip to content

Terminal recordings

Every recording on this page is a real terminal session. A script called a tape types into bash on a pseudo-terminal, runs the rich binary built from this repository, and waits for the screen before each step. Nothing is drawn by hand. Press play, pause anywhere, and select the text: the recordings are asciinema casts, not videos.

The screenshots under each recording are taken from the same run. CI re-runs every tape and fails when any screenshot's text no longer matches, so this page cannot drift from what the CLI does. How it works is at the end.

Point at a card, or move to it with Tab, to play its recording. With reduced motion turned on in your system settings the cards stay still.

One binary, every format

Markdown, CSV and source code, each rendered from a plain rich FILE.

rich rendering a Markdown file, a CSV table and Python source
Markdown CSV Code
Markdown rendered by rich A CSV file as a table Python highlighted by rich

Tape · Cast · GIF · Page

Watch a file

rich --watch renders a file and renders it again on every save. Here the tape edits service.json from outside the terminal while rich watches it.

rich --watch re-rendering a JSON file after an edit
Before the edit After the edit
replicas is 2 replicas is 5

Tape · Cast · GIF · Page

rich view --pager numbers and highlights a file, then hands it to less: page down, then /characters to search.

rich view paging a Rust file through less and searching it
Opened Paged down Searched
The top of rule.rs One page further Matches of characters highlighted

Tape · Cast · GIF · Page

Ask for a line

rich input reads one line for a script. --placeholder shows a hint while the line is empty, and --required refuses an empty answer with an error under the line. The answer is captured with $(…).

rich input showing a placeholder, refusing an empty answer, then taking one
Placeholder Required Answered
The prompt with its placeholder An answer is required The answer echoed by the script

Tape · Cast · GIF · Page

Components

Every rs-rich-interact component, one after another, recorded in a small fixture project:

  • a fuzzy file picker with a highlighted preview;
  • a multi-select;
  • an input with suggestions and validation;
  • a confirmation sheet with a diff and four choices;
  • a form that reports an error under its field;
  • a pager that searches.

See Interactive components.

rs-rich-interact's components, one after another
Select Input Confirm
A fuzzy pick with a preview Suggestions as you type A confirmation sheet
Form MultiSelect Pager
A form with an error Marking several Searching

Tape · Cast · GIF · Page

Ask for more in a script

rich write, rich file, rich color and rich asset, each captured into a shell variable with $(…): several lines of a commit message, a path picked from the fixture project with its preview, a colour from the names and the palette, and an emoji and a box style used together. See Ask in a script.

rich file browsing a project and picking a file
rich write rich file rich file
Several lines typed A directory listed A file previewed
rich color rich color rich asset
Named colours with a swatch The 256 palette Box styles previewed

write tape · cast · page · file tape · cast · page · color tape · cast · page · asset tape · cast · page

Explore a document

rich explore on a YAML file: a container opened, the breadcrumbs following the cursor, the path copied, a search that keeps the match's ancestors, and the JSONPath captured by the script. See Explore it interactively.

rich explore folding, searching and picking a node of a YAML file
Folded Opened Searched
The document folded below the root A container opened, its node previewed A search keeping its ancestors

Tape · Cast · GIF · Page

An interactive component

rs-rich-interact's Viewport, run as its example pager, pages a Markdown file on the alternate screen. Each key repaints only the cells that changed. Enter gives the terminal back, as it was, with the line it was left at. See Interactive components.

A Markdown file paged in rich_interact's Viewport
Opened Paged down Given back
The top of the README One page further The shell again, with the line it was left at

Tape · Cast · GIF · Page

Overlays and chrome

rs-rich-interact's overlays and chrome, run as its overlays example: a file list wrapped in Overlays. Ctrl+O opens the command palette, which lists every key the list has, by category, with its shortcut, and runs the one picked. The help overlay groups the keys and searches them as you type. Under the list, a status bar shows a badge, a spinner, a note and key hints from the keymap. Breadcrumbs sit over the list, and Ctrl+K opens the region's actions in a modal. See Overlays and chrome.

The command palette over a file list, searched and run
The palette Searched Run
Every key of the list, by category "move down" found The list moved down

Tape · Cast · GIF · Page

Help Searched
Every key, grouped by context The keys for "page"

Tape · Cast · GIF · Page

The status bar Region actions A command ran
A badge, a spinner and key hints under the list The region's actions in a modal The note changed by the command

Tape · Cast · GIF · Page

Charts from data

rich chart draws CSV, JSON or numbers piped in: bars labelled by a column, every numeric column as a line, a sparkline from printf, a heatmap, and a column that is not there refused with the ones that are. See the charts guide.

rich chart drawing a CSV file as bars, lines, a sparkline and a heatmap
Bars Lines Piped in, and a mistake
rich chart --kind bar rich chart with every numeric column as a line A sparkline from printf, a heatmap, and a missing column

Tape · Cast · GIF · Page

Dependency trees

rich deps draws a Cargo workspace's dependencies from cargo metadata (here a saved copy, so the recording never changes): a tree that marks crates resolved at several versions, --why for what pulls a crate in, and --graph for the same through the diagram layout. See Dependencies and schemas.

rich deps showing a tree, the paths to syn, and a graph
Tree Why syn Graph
rich deps --depth 2 rich deps --why syn rich deps --graph --depth 1

Tape · Cast · GIF · Page

DOT in Markdown

A ```dot fence in a Markdown document is drawn in place of the code block; --dot-backend off leaves it as code, as upstream renders it. See the diagrams guide.

A Markdown document with a DOT fence, drawn and then left as code
Drawn --dot-backend off
The release pipeline drawn as a graph The DOT source left as a code block

Tape · Cast · GIF · Page

Micro assets

Emoji-sized images in text, :micro:name:. A pseudo-terminal speaks no image protocol, so these show each asset's emoji, or its image as half-block cells with RICH_MICRO=blocks; a Kitty, iTerm2 or Sixel terminal draws the image itself in the same cells. See the built-in library for the images.

The same line with emoji and half-blocks, then fun/heart previewed

Tape · Cast · GIF · Page

Making an asset: a 128×128 PNG previewed through the pipeline, written into the user layer with rich micro create --add, listed and used.

rich micro create making team/rocket from a PNG
Through the pipeline Added and used
rocket.png fitted to 16×16 pixels, magnified team/rocket listed and used in text

Tape · Cast · GIF · Page

Which mode a terminal gets, and why: rich doctor with the variables kitty and iTerm2 set.

rich doctor's micro line under five terminal settings

Tape · Cast · GIF · Page

rich micro: the built-in library listed, status/loading previewed, and :micro: codes in --print --emoji text.

rich micro list, preview and markup
List Preview In text
rich micro list rich micro preview status/loading micro assets in --print text

Tape · Cast · GIF · Page

Micro assets in the interactive views: an icon per row, a badge in the status bar, an icon on the first crumb and before each of the command palette's categories, then rich explore --icons.

Micro assets in the status bar, breadcrumbs, rows and command palette
Chrome Palette Explorer
micro assets in the status bar, breadcrumbs and rows micro assets before the palette's categories rich explore --icons

Tape · Cast · GIF · Page · Explorer tape

How it works

A tape is a short script, one step per line. This is the watch tape:

# rich --watch re-renders a file every time it is saved.
Set Title "rich --watch · re-render on save"
Set Size 90x22
Write service.json '{"service": "checkout", "replicas": 2, "healthy": true, "regions": ["eu-west-1", "us-east-1"]}'
Type "rich --watch service.json"
Sleep 300ms
Enter
Wait /"replicas": 2/
Sleep 1s
Screenshot before
Exec "sed -i 's/\"replicas\": 2/\"replicas\": 5/' service.json"
Wait /"replicas": 5/
Sleep 1s
Screenshot after
Ctrl+C
Wait /❯\s*$/

rich record runs it: the CLI's own recorder, built on the rs-rich-record crate. It starts a shell (bash unless the tape sets another) on a pseudo-terminal with a pinned environment (a temporary home and working directory, TERM=xterm-256color, truecolor, UTF-8, UTC), puts the binaries under test first on PATH, and follows the screen with a terminal emulator. Wait blocks until the screen shows the given text or matches a /regex/, so a tape never depends on a fixed delay.

From one run it writes, under docs/media/tapes/<tape>/:

  • for each Screenshot: a PNG, an SVG with selectable text, and a plain-text grid of the screen. The SVG and the text grid come from the same frame as the rest of rich's export, so a screenshot looks like any other rich SVG;
  • the whole session as an asciinema cast, with the keys pressed;
  • a GIF with the keys shown as they are pressed, and an MP4 when FFmpeg is installed;
  • an HTML page (<tape>.html, the Page links above): a small player and the screenshots, all inline, with text you can select. It fetches nothing and plays only when asked;
  • provenance.json: the tape's fingerprint, the recorder and rich versions, the commit, and the screenshots written, so the next run removes only the ones the tape no longer takes.

Box-drawing characters are drawn as lines rather than font glyphs, so table borders join between rows, and block, quadrant and braille characters are drawn as shapes, so images and progress bars have no seams. Text uses DejaVu Sans Mono and emoji use Twemoji, in colour, both embedded in the recorder, so a PNG or GIF looks the same on every machine; --font FILE chooses another text font. An emoji cluster such as 👩‍👧, 👍🏽, ❤️ or 🇺🇸 takes one cell as wide as rich measures it, so the columns after it line up.

Step Meaning
Set Size 100x28, Set Title "…", Set TypingDelay 40ms, Set Timeout 15s, Set Env NAME value Configure the session
Set WindowFrame off, Set Caption "…", Set KeyOverlay off Presentation: leave out the window frame (title bar and buttons) around PNGs, SVGs and video; add a line of text under them and under the page's player; leave out the keys shown in video and the player. The frame and the overlay are on by default
Output gif png, Output demo.html Write only these formats (png, svg, cast, gif, mp4, html; text grids always), or name the file a per-tape format goes to, as in VHS. Several words or lines add up; --format can still narrow them
Set Shell zsh Run in bash (the default), zsh, fish or sh, each without your profile or rc files and with the same ❯ prompt. CI records in all four
Write FILE "text", Exec "command" Prepare or change files, outside the terminal
Type "text" Type into the terminal, one character at a time
Enter, Tab, Space, Backspace, Escape, arrows, Home, End, PageUp, PageDown, Ctrl+C Press a key; a number after it repeats it
Wait "text", Wait /regex/ Wait until the screen shows it, or has since the previous step began (so fast output that scrolls past is not missed)
Sleep 500ms Pause the recording
Screenshot NAME Save the screen
Hide, Show Leave steps out of the recording
Resize 80x24 Resize the terminal
Mask /regex/ "text" Replace matches in the text grids --check compares, for output that differs on every run such as temporary paths or timings; images keep what was shown

Shells and emoji

A tape can type and print emoji, and the recorder draws them as the program measures them: rich, and modern terminals, give 👩‍👧 two cells. A shell's line editor may not agree. bash, zsh and fish take character widths from the C library, which counts 👩‍👧 as four, so moving the cursor back across a joined emoji at the prompt (Left, Home, Backspace) redraws the line in the wrong place, as it does in a real terminal. The command that runs is still right; only the echoed line is garbled. Keep such text out of line editing: put it in a file with Write and run that, or have the program print it.

The docs' tapes use bash, as CI does. macOS ships bash 3.2, whose line editing is older than CI's; rich record warns when it finds a bash older than 4, and a newer one (brew install bash) first on PATH gives recordings that match.

Regenerate everything, or check it as CI does:

cargo build -p rs-rich-cli -p rs-rich-interact --bins --examples
rich=target/debug/rich
$rich record --bin-dir target/debug --output docs/media/tapes docs/tapes/*.tape
$rich record --check --bin-dir target/debug --output docs/media/tapes docs/tapes/*.tape

Record your own

rich record is not limited to rich: a tape can drive any program you can start from a shell, in bash, zsh, fish or sh (Set Shell). Write a tape, then:

rich record demo.tape                       # writes recordings/demo/
rich record --format gif,png demo.tape      # only the GIF and the PNGs
rich record --format html demo.tape         # the page: a player and the screenshots
rich record --window-frame off --caption 'Save to redraw' demo.tape
rich record --check demo.tape               # fail if a screenshot changed
Option Meaning
--output DIR Write to DIR/<tape>/ (default recordings)
--check Compare each screenshot's text with DIR/<tape>/<name>.txt instead of writing, and report screenshots the last write listed that the tape no longer takes; exits non-zero on any difference
--format LIST Any of png, svg, cast, gif, mp4, html, or all (the default). Text grids are always written. A tape's Output narrows it further
--window-frame on\|off, --caption TEXT, --key-overlay on\|off Override the tape's Set WindowFrame, Set Caption and Set KeyOverlay
--no-video Skip the GIF, the MP4 and the HTML page's player
--bin-dir DIR Put DIR first on the session's PATH (default: the directory of the running rich)
--font FILE Draw PNG and GIF text in another font

$REPO in a tape is the directory rich record was started in, so a tape can copy fixtures (Exec "cp $REPO/fixtures/data.json ."). Linux and macOS are supported; on Windows it builds through ConPTY but needs the tape's shell on PATH, and is experimental. Use the recorder from Rust through the rs-rich-record crate: tape::parse, record::record, then record::write or record::check.

Limits, and what a tape can do

A tape is code: record only tapes you trust. Exec runs its command with sh, and everything Type sends runs in the shell, with your user's permissions. The recorder pins the environment and works in a temporary directory so recordings repeat, not to contain the tape.

What the recorder does limit, so a mistake fails clearly rather than filling the disk or memory:

Limit
Terminal size Set Size and Resize from 2x2 to 500x200
Durations Sleep, Wait and Set Timeout at most 3600s
Write A relative path inside the working directory: no /…, no ..
Exec 60 seconds; the first 64 KiB of its error output is reported
Tape names A tape's name is its output directory: ...tape (named ..) is refused
Stale screenshots Removed only when provenance.json lists them from an earlier run; other files in the output directory are never touched
Frames At most 12 a second; a burst over 32 KiB is kept as a repaint of the screen
Video The first 5 minutes; a GIF, MP4 or HTML page of a longer recording is an error (use --no-video)
Images At most 100 million pixels; a GIF at most 65535 pixels a side