feat: add asciicast session recording and playback (#469)#19960
feat: add asciicast session recording and playback (#469)#19960SemperFu wants to merge 7 commits intomicrosoft:mainfrom
Conversation
|
@microsoft-github-policy-service agree |
Add 4 GitHub Actions workflows for automated building and publishing: - sync-upstream.yml: Polls MS releases every 6hrs, syncs fork, rebases feature branch - build.yml: Builds x64 + ARM64 unpackaged zip distributions - release.yml: Creates GitHub Releases with versioned artifacts - winget-publish.yml: Auto-publishes SemperFu.TerminalCast to winget These live on main (required for scheduled/release triggers) and don't affect PR microsoft#19960 since that PR is from feature/asciicast. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
|
Playback now opens in a dedicated window instead of a tab in the current window. This fixes the auto-resize issue. There is a setting "autoResizeForPlayback" to use new window or tab. Made OpenCastFile a serializable action with a path parameter, which enables future command-line support and file association. Added a video-player-style transport bar that appears at the bottom of the terminal during cast file playback. Supports play/pause, restart, seek via timeline slider, and speed control (0.5x/1x/2x/4x). The bar auto-hides and reappears on mouse hover and can restart after playback finishes. DemoNew.mp4Tried focus mode for the playback window to hide tabs and titlebar, but you can't move the window. also tried hiding just the tab row, but tabs-in-titlebar mode doesn't render a separate title, so the titlebar was blank. These need more thought, possibly a custom playback window mode? Good stopping point for now |
- Green play icon in tab header for playback tabs - Renamed Asciicast submenu to Recording in context menu - Added autoResizeForPlayback global setting (default: false) - Parse recording dimensions from cast file header - Auto-resize on playback to match recording size (WIP, only works when tab is the only tab) - Replaced mutex recording with lock-free til::spsc channel and writer thread to prevent rendering stalls during recording
- Added OpenCastFileArgs with String Path for serializable action - File picker opens in current window, playback launches in new window - New window has 1 tab + 1 pane so XTWINOPS auto-resize works - Handler skips file picker when path is provided (for deserialization) - Fixed spell check issues (nonexistent, unicode escapes, expect list)
- Playback engine: pause/resume, seek, speed control, position tracking - PlaybackBar UserControl with hover show/hide at bottom of terminal - Timeline slider, play/pause, restart, speed dropdown, time display - Bar auto-hides and reappears on mouse hover
08dfac7 to
7f37240
Compare
- Added a generation counter so restarting playback doesn't leave a stale coroutine running alongside the new one - Switched the replay loop from for to while to fix an unsigned underflow when seeking to position 0 - Replaced std::stod for speed parsing with direct string matching since it breaks on non-English locales - Added missing _writeError check to WriteInitialSnapshot to match the other write methods - Capped the yield spin in StopRecording so it doesn't burn CPU forever on a busy system - Documented what the three hardcoded delays are actually waiting for - Fixed temp file leak in the test helper when ofstream fails to open
…the terminal: - Screen wasn't being cleared before replaying, so restarting drew on top of old content - After a seek, the loop re-emitted the event that _emitAllOutputUpTo already played - Seeking to a point with a different terminal size ignored resize events — only applied the header dimensions. Now it finds the last resize event before the seek position and applies that size


Hey! I've wanted built-in terminal recording for a while and wasn't happy with the existing options, so I built this for my own use. It's been working well, and since #469 has been open for years I figured I'd contribute it upstream. I'm sure it's not exactly how people want it but, just wanted to get the ball rolling. Happy to iterate on feedback
Summary of the Pull Request
Adds built-in terminal session recording and playback using the asciicast format. Records in asciicast v3 (interval-based timing) and plays back both v2 and v3 files, compatible with the asciinema ecosystem (
asciinema play,agg, asciinema.org).Watch an interactive demo of a WT recorded .cast file via asciinema-player.
Demo.mp4
References and Relevant Issues
doc/specs/469 - Session Recording.mdDetailed Description of the Pull Request / Additional comments
Recording
Right-click a tab > "Record session" (or
Ctrl+Shift+R) to open a Save As dialog with a timestamped default filename. A red dot appears in the tab header while recording. The current prompt line is captured with VT attributes as the first event so recordings don't start blank. All subsequent terminal output is written as timestamped VT data. Toggle the shortcut or menu item again to stop.Recording works with any shell or connection type (PowerShell, cmd, WSL, SSH) since it captures raw VT output from the
TerminalOutputevent.Playback
Open cast file from the command palette or
Ctrl+Shift+O. The selected.castfile replays in a new tab with original timing. After playback, the tab shows "Playback complete. Press any key to close." Auto-detects v2 vs v3 from the header.UI integration
Ctrl+Shift+RCtrl+Shift+OArchitecture
AsciicastRecorder (plain C++ class):
ITerminalConnection::TerminalOutputto capture raw VT datastd::mutex; managed byControlCoreAsciicastConnection (new
ITerminalConnectionimplementation):.castfiles, auto-detects v2 vs v3 from headerfire_and_forgetcoroutine with timed delays#comment lines (v3 feature)New files
src/cascadia/TerminalConnection/AsciicastRecorder.h/.cppsrc/cascadia/TerminalConnection/AsciicastConnection.idl/.h/.cppdoc/specs/469 - Session Recording.mdValidation Steps Performed
PR Checklist
doc/specs/469 - Session Recording.md