Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
42face3
Add tuirec recording guide and hero-gif docs
tig May 24, 2026
39961e4
Add tuirec recording guide for UICatalog scenarios and CharMap GIF
tig May 24, 2026
70cb605
Update tuirec.md: document --kitty-keyboard nav key bug (tuirec#54)
tig May 24, 2026
640aad0
Improve CharMap GIF: show Box Drawing, Emojis, and context menu
tig May 24, 2026
2269ba1
Move charmap.gif to scenario directory
tig May 24, 2026
a8bae11
Fix CharMap GIF: avoid wide glyphs, add placement guidance
tig May 24, 2026
c445b2f
Restructure tuirec docs and fix quit key guidance
tig May 24, 2026
53a1e3b
Remove bespoke script guidance; README.md is the single source
tig May 24, 2026
17eaef5
Update CharMap keystroke script: Arrows -> Box Drawing -> Emoji
tig May 24, 2026
156dfbe
Re-record CharacterMap.gif with updated keystroke script
tig May 24, 2026
53b53ec
Fix View GIF placement: docfx/images/views/, not alongside .cs
tig May 24, 2026
0ee7561
Add IDesignable.GetDemoKeyStrokes() and upgrade OutputView for tuirec
tig May 24, 2026
12e7cf6
Add GetDemoKeyStrokes() to more views and generate 39 view GIFs
tig May 24, 2026
5bb4d86
Address CR feedback: fix redundant override, dispose leak, generic re…
tig May 24, 2026
81eaa6d
Update IDesignable.GetDemoKeyStrokes XML docs per review feedback
tig May 25, 2026
43bc89f
Use tuirec v0.4.2 --trim, fix Code syntax highlighting, add end-pause
tig May 25, 2026
1d39a20
Fix preroll artifacts: concealed-char baseline + GetDemoKeyStrokes fo…
tig May 25, 2026
380c52c
Fix visible preroll dot: use bg-colored foreground for baseline char
tig May 25, 2026
cc0a2af
fix: reduce baseline sleep to 50ms to eliminate GIF preroll gray screen
tig May 25, 2026
051d1af
feat: update CharMap demo to page down 30x then show context menu
tig May 25, 2026
231bd4a
feat: improve demo keystrokes for CharMap, Tabs, TreeView, Markdown, …
tig May 25, 2026
e4dbe1d
chore: remove accidentally committed artifacts and add gitignore entries
tig May 25, 2026
8b31a46
feat: add GIF img tags to all View XML docs for API documentation
tig May 25, 2026
3d6d0c3
chore: remove stray images/views/PopoverMenu.gif
tig May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ log.*
.mcp.json
.env
tmpclaude-*-cwd

# tuirec artifacts
artifacts/
docfx/artifacts/
local_packages/
*.cast
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
312 changes: 312 additions & 0 deletions Scripts/tuirec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
# 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`](https://github.com/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 Key | tuirec Token |
|---|---|
| `Key.CursorDown` | `CursorDown` |
| `Key.CursorLeft` | `CursorLeft` |
| `Key.Tab` | `Tab` |
| `Key.Tab.WithShift` | `Shift+Tab` |
| `Key.Enter` | `Enter` |
| `Key.Esc` | `Esc` |
| `Key.B` | `B` (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 steps** — `wait: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**:

| What | Where |
|------|-------|
| Scenario in a subdirectory | `Examples/UICatalog/Scenarios/<ScenarioDir>/<ScenarioName>.gif` |
| Scenario directly in `Scenarios/` | `Examples/UICatalog/Scenarios/<ScenarioName>.gif` |
| View-derived class | `docfx/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](https://github.com/gui-cs/tuirec/issues/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:**

| Step | Tokens | What happens |
|------|--------|--------------|
| 1 | `wait:1200` | Let the CharMap UI fully render |
| 2 | `Tab,Tab` | Move focus to category list |
| 3 | `A` | CollectionNavigator jumps to "Arrows" |
| 4 | `wait:1800` | Pause so viewer sees arrow characters |
| 5 | `B,o` | Type "Bo" — jumps to "Box Drawing" |
| 6 | `wait:1800` | Pause so viewer sees box-drawing characters |
| 7 | `E` | Type "E" — jumps to "Emoji" |
| 8 | `wait:1800` | Pause so viewer sees emoji characters |
| 9 | `Tab` | Return focus to charmap grid |
| 10 | `CursorDown` ×3 | Navigate to a glyph |
| 11 | `Shift+F10` | Open context menu (Copy Glyph / Copy Code Point) |
| 12 | `wait:1500,Escape` | Let viewer see the menu, then dismiss |
| 13 | `Escape` | Quit |

**Key techniques demonstrated:**
- **CollectionNavigator typing** — type category name prefixes to jump directly
(much better than scrolling through dozens of categories with arrow keys)
- **Context menu** — `Shift+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

| Problem | Cause | Fix |
|---------|-------|-----|
| Wide glyphs misaligned in GIF | Emoji/CJK chars are 2-cell wide; agg renders per-cell | Avoid emoji/CJK categories; use single-width ranges (Arrows, Box Drawing, etc.) |
| Nav keys ignored with `--kitty-keyboard` | tuirec bug [#54](https://github.com/gui-cs/tuirec/issues/54) — sends wrong codepoints | Remove `--kitty-keyboard` |
| App doesn't quit | Wrong quit key or key not delivered | Use `Escape` (the default quit key); check `--kitty-keyboard` interaction |
| Blank frames at start/end | Pre/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 views | Use `--trim=false` for views with no visual change during demo |
| Recording times out | App stuck / wrong keystrokes | Check with `--verbosity high`, fix script |
| `--binary` permission error | Relative path on Windows | Use `./` prefix or absolute path with forward slashes |
| Backtick text missing | PowerShell interpolation | Use single-quoted `$ks` variable |

---

## Agent Workflow Summary

When asked to record a scenario GIF:

1. **Build** — `dotnet build Examples/ScenarioRunner -c Release`
2. **Find scenario name** — `dotnet 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. **Record** — `tuirec 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
30 changes: 30 additions & 0 deletions Scripts/tuirec/hero-gif.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Hero GIF Guidance

For recording Terminal.Gui app/scenario GIFs, use:

- [`./README.md`](./README.md) — Full recording workflow with tuirec

## Quick Reference

```powershell
# Install tuirec (one-time)
go install github.com/gui-cs/tuirec/cmd/tuirec@latest

# Build ScenarioRunner (before any recording)
dotnet build Examples/ScenarioRunner/ScenarioRunner.csproj -c Release

# Record a scenario (cross-platform: use dotnet to run the DLL)
$dll = "./Examples/ScenarioRunner/bin/Release/net10.0/ScenarioRunner.dll"
$ks = 'wait:1000,<keystrokes>,Escape'
tuirec record --binary dotnet --args "$dll,run,<Scenario Name>" --name <id> `
--keystrokes $ks --startup-delay 2000 --drain 2000 --cols 120 --rows 30 --open
```

See `README.md` (this directory) for complete guidance including keystroke syntax,
PowerShell quoting rules, and the `--kitty-keyboard` decision tree.

## File Placement

- **Scenario GIFs** go alongside the scenario `.cs` file:
`Examples/UICatalog/Scenarios/<ScenarioDir>/<ScenarioName>.gif`
- **View GIFs** go in `docfx/images/views/<ViewName>.gif`
12 changes: 12 additions & 0 deletions Terminal.Gui/ViewBase/IDesignable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ public interface IDesignable
/// </summary>
/// <returns><see langword="true"/> if the view successfully loaded demo data.</returns>
public bool EnableForDesign () => false;

/// <summary>
/// Returns a tuirec-format keystroke string for recording a demo GIF of this view.
/// The string uses tuirec token syntax (e.g. <c>"wait:500,Enter,wait:800,Escape"</c>).
/// This is a new API added in v2 to support automated documentation generation.
/// </summary>
Comment thread
tig marked this conversation as resolved.
/// <returns>
/// A keystroke string for tuirec, or <see langword="null"/> if no demo interaction is defined.
/// Both <see langword="null"/> and an empty string indicate the view has no interactive demo;
/// the view will be recorded as a static 2-second capture in either case.
/// </returns>
public string? GetDemoKeyStrokes () => null;
}
4 changes: 4 additions & 0 deletions Terminal.Gui/Views/Bar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Terminal.Gui.Views;
/// Serves as the base class for <see cref="Menu"/>, <see cref="MenuBar"/>, and <see cref="StatusBar"/>.
/// </summary>
/// <remarks>
/// <img src="../images/views/Bar.gif" alt="Bar demo"/>
/// <para>
/// Any <see cref="View"/> can be added to a <see cref="Bar"/>. However, <see cref="Bar"/> is designed to work
/// with <see cref="Shortcut"/> objects, which display a command, help text, and key binding aligned
Expand Down Expand Up @@ -348,4 +349,7 @@ public virtual bool EnableForDesign ()

return true;
}

/// <inheritdoc/>
public virtual string? GetDemoKeyStrokes () => "wait:500,Tab,wait:500,Tab,wait:500,Tab,wait:800";
}
Loading
Loading