Back to Terminal Gui

Recording Terminal.Gui Apps with `tuirec`

Scripts/tuirec/README.md

2.4.211.5 KB
Original Source

Recording Terminal.Gui Apps with tuirec

Use this guide when an issue or PR asks for a GIF/video capture of a Terminal.Gui app or scenario. The recording tool is gui-cs/tuirec — a Go CLI that spawns the target app in a PTY, injects keystrokes, records terminal output as an asciinema v2 cast, and renders an animated GIF via agg.

Install

powershell
# Requires Go 1.22+
go install github.com/gui-cs/tuirec/cmd/tuirec@latest
tuirec --version

# agg is auto-downloaded on first use — no separate install needed.

Verify: tuirec --version. If not on PATH, add $(go env GOPATH)\bin to PATH.

Quick Start — Recording a UICatalog Scenario

powershell
# 1. Build ScenarioRunner (do this ONCE before recording)
dotnet build Examples/ScenarioRunner/ScenarioRunner.csproj -c Release

# 2. Record (cross-platform: use dotnet to run the DLL)
$dll = "./Examples/ScenarioRunner/bin/Release/net10.0/ScenarioRunner.dll"
$ks = 'wait:1200,Tab,Tab,wait:400,A,wait:1800,B,o,wait:1800,E,wait:1800,Tab,wait:400,CursorDown,CursorDown,CursorDown,wait:400,Shift+F10,wait:1500,Escape,wait:400,Escape'

tuirec record `
    --binary dotnet `
    --args "$dll,run,Character Map" `
    --name CharacterMap `
    --title "Character Map" `
    --keystrokes $ks `
    --startup-delay 2000 `
    --drain 1500 `
    --cols 120 --rows 30 `
    --open --copy

Output: artifacts/CharacterMap.gif and artifacts/CharacterMap.cast.

Copy the GIF to the scenario directory:

powershell
Copy-Item artifacts/CharacterMap.gif Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.gif

Recording UICatalog Scenarios

Prerequisites

  1. Build ScenarioRunner — always build before recording to avoid startup noise:
    powershell
    dotnet build Examples/ScenarioRunner/ScenarioRunner.csproj -c Release
    
  2. Know the scenario name — list available scenarios:
    powershell
    dotnet run --project Examples/ScenarioRunner -c Release --no-build -- list
    

Finding the Right Keystrokes

Each scenario has a GetDemoKeyStrokes() method that defines a canonical interaction sequence for benchmarking. Use this as your starting point:

powershell
# Find the demo keystrokes for a scenario:
grep -n "GetDemoKeyStrokes" Examples/UICatalog/Scenarios/<ScenarioFile>.cs

The demo keystrokes show what keys the scenario expects and what UI flow is interesting. Translate them to tuirec syntax:

Terminal.Gui Keytuirec Token
Key.CursorDownCursorDown
Key.CursorLeftCursorLeft
Key.TabTab
Key.Tab.WithShiftShift+Tab
Key.EnterEnter
Key.EscEsc
Key.BB (or `B` for literal)

Composing the Keystroke Script

Principles for a great recording:

  1. Start with wait:1000 — let the UI render fully after startup-delay.
  2. Add wait: between logical stepswait:500 to wait:1500 between groups of actions so viewers can follow what's happening.
  3. Keep it short — 10–20 seconds of real-time interaction. Fewer keystrokes with generous waits beats many rapid keystrokes.
  4. Show variety — demonstrate 2–3 features of the scenario, not just scrolling. Navigate between controls, trigger category changes, etc.
  5. End with Escape — the default Terminal.Gui quit key.
  6. Avoid wide glyphs — Emoji and CJK characters cause misaligned rendering in terminal recordings (agg renders each cell as monospace but wide glyphs consume 2 cells). Prefer categories with single-width characters (Arrows, Box Drawing, Block Elements, Mathematical Operators, etc.).

Template Command

powershell
$dll = "./Examples/ScenarioRunner/bin/Release/net10.0/ScenarioRunner.dll"
$ks = '<your keystroke script here>'

tuirec record `
    --binary dotnet `
    --args "$dll,run,<Scenario Name>" `
    --name <ScenarioName> `
    --title "<Scenario Name>" `
    --keystrokes $ks `
    --startup-delay 2000 `
    --drain 2000 `
    --cols 120 --rows 30 `
    --verbosity high `
    --open --copy

# Copy GIF to scenario directory
Copy-Item artifacts/<ScenarioName>.gif Examples/UICatalog/Scenarios/<ScenarioDir>/<ScenarioName>.gif

Output File Placement

GIFs live alongside the .cs file they document:

WhatWhere
Scenario in a subdirectoryExamples/UICatalog/Scenarios/<ScenarioDir>/<ScenarioName>.gif
Scenario directly in Scenarios/Examples/UICatalog/Scenarios/<ScenarioName>.gif
View-derived classdocfx/images/views/<ViewName>.gif

Use --name <ScenarioName> (PascalCase matching the class name) so the output file is named correctly. The --name value determines the artifact filenames.

Critical: --kitty-keyboard Decision

Known bug (gui-cs/tuirec#54): tuirec currently encodes navigation keys (CursorUp, CursorDown, CursorLeft, CursorRight, PageUp, PageDown, Home, End) incorrectly under --kitty-keyboard — it sends fabricated CSI u codepoints that the Kitty spec doesn't define. Terminal.Gui ignores or misinterprets these sequences.

Workaround until fixed:

  • Omit --kitty-keyboard for demos that use navigation keys.
  • Add --kitty-keyboard only when you need modifier disambiguation for non-navigation keys (Ctrl+M vs Enter, Ctrl+I vs Tab, Ctrl+Q, etc.) and the demo doesn't rely on arrow/page/home/end keys.

Once the bug is fixed, --kitty-keyboard should be the default for all Terminal.Gui recordings (it provides cleaner modifier handling).

--args for ScenarioRunner

The --args flag uses comma-separated values (not space-separated):

powershell
--args "run,Character Map"      # Correct: two args ["run", "Character Map"]
--args "run Character Map"      # WRONG: one arg "run Character Map"

PowerShell Quoting

Always assign keystrokes to a single-quoted $ks variable to preserve backtick literals:

powershell
# Correct — single quotes prevent PowerShell backtick interpolation:
$ks = 'wait:1000,`search text`,Enter,wait:500,Escape'

# WRONG — PowerShell eats the backticks:
--keystrokes "wait:1000,`search text`,Enter"

Example: Character Map Scenario

powershell
$dll = "./Examples/ScenarioRunner/bin/Release/net10.0/ScenarioRunner.dll"

# Navigate to category list, browse Arrows → Box Drawing → Emoji, then context menu
$ks = 'wait:1200,Tab,Tab,wait:400,A,wait:1800,B,o,wait:1800,E,wait:1800,Tab,wait:400,CursorDown,CursorDown,CursorDown,wait:400,Shift+F10,wait:1500,Escape,wait:400,Escape'

tuirec record `
    --binary dotnet `
    --args "$dll,run,Character Map" `
    --name CharacterMap `
    --title "Character Map" `
    --keystrokes $ks `
    --startup-delay 2000 `
    --drain 1500 `
    --cols 120 --rows 30 `
    --open --copy

Script breakdown:

StepTokensWhat happens
1wait:1200Let the CharMap UI fully render
2Tab,TabMove focus to category list
3ACollectionNavigator jumps to "Arrows"
4wait:1800Pause so viewer sees arrow characters
5B,oType "Bo" — jumps to "Box Drawing"
6wait:1800Pause so viewer sees box-drawing characters
7EType "E" — jumps to "Emoji"
8wait:1800Pause so viewer sees emoji characters
9TabReturn focus to charmap grid
10CursorDown ×3Navigate to a glyph
11Shift+F10Open context menu (Copy Glyph / Copy Code Point)
12wait:1500,EscapeLet viewer see the menu, then dismiss
13EscapeQuit

Key techniques demonstrated:

  • CollectionNavigator typing — type category name prefixes to jump directly (much better than scrolling through dozens of categories with arrow keys)
  • Context menuShift+F10 (the PopoverMenu.DefaultKey) shows the right-click menu on the selected glyph
  • Generous waits — 1800ms between feature demonstrations so viewers can absorb each state change

Recording Individual View Sub-classes with EnableForDesign

(Coming soon — will use a dedicated design-mode runner that instantiates a single View with EnableForDesign() and records its interactions.)


Recording Standalone Example Apps

For apps in Examples/ that are not UICatalog scenarios:

powershell
$dll = "./Examples/<AppName>/bin/Release/net10.0/<AppName>.dll"
$ks = 'wait:1000,<keystrokes>,Escape'

tuirec record `
    --binary dotnet `
    --args "$dll" `
    --name <app-id> `
    --title "<App Name> Demo" `
    --keystrokes $ks `
    --startup-delay 2000 `
    --drain 2000 `
    --cols 120 --rows 30 `
    --open --copy

Validation Checklist

After every recording, verify:

  • tuirec record exited with code 0 and wrote both .gif and .cast.
  • Error check — no errors in the cast:
    powershell
    Select-String -Path artifacts/<name>.cast -Pattern "error|unknown|not found|usage:" -CaseSensitive:$false
    
  • GIF is not blank — file size > 100KB for a typical scenario recording. (A blank/static GIF is typically < 50KB.)
  • Visual check — open the GIF (--open flag) and confirm:
    • The app content is visible (menu bar, controls, content).
    • The interaction sequence is visible (scrolling, focus changes, etc.).
    • The recording ends cleanly (no frozen frame or abrupt cutoff).
  • Output path is correct — scenario GIFs go with their scenario code:
    Examples/UICatalog/Scenarios/<ScenarioDir>/<ScenarioName>.gif
    

Troubleshooting

ProblemCauseFix
Wide glyphs misaligned in GIFEmoji/CJK chars are 2-cell wide; agg renders per-cellAvoid emoji/CJK categories; use single-width ranges (Arrows, Box Drawing, etc.)
Nav keys ignored with --kitty-keyboardtuirec bug #54 — sends wrong codepointsRemove --kitty-keyboard
App doesn't quitWrong quit key or key not deliveredUse Escape (the default quit key); check --kitty-keyboard interaction
Blank frames at start/endPre/postroll not trimmed--trim is on by default in v0.4.2+; ensure tuirec is up-to-date
GIF validation: 1 frame--trim removes all frames for static viewsUse --trim=false for views with no visual change during demo
Recording times outApp stuck / wrong keystrokesCheck with --verbosity high, fix script
--binary permission errorRelative path on WindowsUse ./ prefix or absolute path with forward slashes
Backtick text missingPowerShell interpolationUse single-quoted $ks variable

Agent Workflow Summary

When asked to record a scenario GIF:

  1. Builddotnet build Examples/ScenarioRunner -c Release
  2. Find scenario namedotnet run --project Examples/ScenarioRunner -c Release --no-build -- list
  3. Read GetDemoKeyStrokes() — find it in the scenario source file
  4. Compose keystrokes — translate to tuirec syntax, add waits, keep short
  5. Recordtuirec record --binary ... --args "run,<Name>" --keystrokes $ks ...
  6. Validate — error-grep the cast, check GIF file size, visual confirm
  7. If nav keys fail — remove --kitty-keyboard and retry
  8. Report — share the output paths and exact command used

Reference

  • tuirec repo: https://github.com/gui-cs/tuirec
  • Full keystroke syntax: tuirec agent-guide (embeds the complete reference)
  • CLI flags: tuirec record --help
  • ScenarioRunner: Examples/ScenarioRunner/ — CLI that runs individual UICatalog scenarios