captureCallback
captureCallback(fn, options)
Asynchronous function that captures all writes to process.stdout
and process.stderr
that occur within a callback function, and renders an animated terminal screencast.
Returns a promise that resolves either a string
if the output
format option is 'svg'
, 'json'
, or 'yaml'
, or a Buffer
if the output format is 'png'
.
Arguments
fn(capture) => any
Callback function within which terminal output is captured. Can be synchronous or asynchronous. The callback function will be passed a NodeCapture
instance.
Within the scope of this function, all writes to process.stdout
and process.stderr
, (and by extension calls to console.log
and console.error
) will be captured.
optionsObject
A config object to specify the following common options, as well as the additional options listed in the next section.
Additional Options
silentboolean
Silently capture output to process.stdout
and process.stderr
. Defaults to true
.
connectStdinboolean
Connect capture session to process.stdin
to read input from the user. Defaults to false
.
This option must be enabled if you want to read keyboard input from the underlying stdin
tty stream.
NodeCapture API
A NodeCapture
instance is passed to the captured callback function as its only argument. It allows you to interact with the capture recording.
capture.columns
number
The column width of the capture recording window, set by the columns
option. Within the scope of the captured callback, you can also get this value from process.stdout.columns
, or the process.stdout.getWindowSize()
function.
capture.rows
number
The row height of the capture recording window, set by the rows
option. Within the scope of the captured callback, you can also get this value from process.stdout.rows
, or the process.stdout.getWindowSize()
function.
capture.start(command)
(command?) => void
Start the capture. You can optionally pass a command prompt string to include in the beginning of the capture - see emulating a command below for an example of this.
capture.finish(error)
(error?) => void
Finish the capture, optionally passing in an error to display at the end of the recording.
capture.wait(ms)
(ms) => void
Adds the specified number of milliseconds to the capture recording.
capture.setTitle(title, icon)
(title) => void
Set the title and icon of the terminal window in the recorded capture. The title
argument can be a string
to set the window title, or a { title: string, icon: string }
object to set both the title and icon (see available icons here). Passing empty strings will clear the title / icon.
Under the hood, this is just a convenience function that writes OSC escape sequences to change the window title and icon. See the window title and icon configuration section for more info, as well as this usage example.
capture.createInterface(options)
(options?) => Interface
Create a new readline.Interface
instance. This is just a wrapper around the built in readline.createInterface
function, injecting the capture recording's input stream. The same options can be provided as readline.createInterface
, with the exception of the input
, tabSize
, and terminal
fields.
capture.emitKeypress(key)
(key) => Promise
Writes a key string
to the capture's input stream, simulating a single write to process.stdin
in the terminal environment. Returns a promise that resolves once the write has been made.
The provided key string can be a keyword to specify a particular keypress or sequence of keypresses. Each keyword represents an escape sequence to write to the input stream. See the reference table below for a full list of available keywords.
Keypress Keywords Reference
Keyword | Keypresses | Escape Sequence |
---|---|---|
'space' | Space | ' ' |
'backspace' | Backspace | '\x7F' |
'tab' | Tab | '\t' |
'escape' | Esc | '\x1b' |
'return' | Return | '\r' |
'enter' | Enter | '\n' |
'up' | '\x1b[A' | |
'down' | '\x1b[B' | |
'right' | '\x1b[C' | |
'left' | '\x1b[D' | |
'clear' | Clear | '\x1b[E' |
'insert' | Insert | '\x1b[2~' |
'delete' | Delete | '\x1b[3~' |
'pageup' | PgUp | '\x1b[5~' |
'pagedown' | PgDn | '\x1b[6~' |
'home' | Home | '\x1b[7~' |
'end' | End | '\x1b[8~' |
'f1' | F1 | '\x1b[11~' |
'f2' | F2 | '\x1b[12~' |
'f3' | F3 | '\x1b[13~' |
'f4' | F4 | '\x1b[14~' |
'f5' | F5 | '\x1b[15~' |
'f6' | F6 | '\x1b[17~' |
'f7' | F7 | '\x1b[18~' |
'f8' | F8 | '\x1b[19~' |
'f9' | F9 | '\x1b[20~' |
'f10' | F10 | '\x1b[21~' |
'f11' | F11 | '\x1b[23~' |
'f12' | F12 | '\x1b[24~' |
'ctrl-a' | + A | '\x01' |
'ctrl-b' | + B | '\x02' |
'ctrl-c' | + C | '\x03' |
'ctrl-d' | + D | '\x04' |
'ctrl-e' | + E | '\x05' |
'ctrl-f' | + F | '\x06' |
'ctrl-k' | + K | '\x0b' |
'ctrl-n' | + N | '\x0e' |
'ctrl-p' | + P | '\x10' |
'ctrl-u' | + U | '\x15' |
'ctrl-v' | + V | '\x16' |
'ctrl-w' | + W | '\x17' |
'ctrl-up' | + | '\x1bOa' |
'ctrl-down' | + | '\x1bOb' |
'ctrl-right' | + | '\x1bOc' |
'ctrl-left' | + | '\x1bOd' |
'ctrl-clear' | + Clear | '\x1bOe' |
'ctrl-insert' | + Insert | '\x1b[2^' |
'ctrl-delete' | + Delete | '\x1b[3^' |
'ctrl-pageup' | + PgUp | '\x1b[5^' |
'ctrl-pagedown' | + PgDn | '\x1b[6^' |
'ctrl-home' | + Home | '\x1b[7^' |
'ctrl-end' | + End | '\x1b[8^' |
'meta-b' | + B | '\x1bb' |
'meta-f' | + F | '\x1bf' |
'meta-d' | + D | '\x1bd' |
'meta-delete' | + Delete | '\x1b[3;3~' |
'meta-backspace' | + Backspace | '\x1b\x7F' |
'shift-tab' | + Tab | '\x1b[Z' |
'shift-up' | + | '\x1b[a' |
'shift-down' | + | '\x1b[b' |
'shift-right' | + | '\x1b[c' |
'shift-left' | + | '\x1b[d' |
'shift-clear' | + Clear | '\x1b[e' |
'shift-insert' | + Insert | '\x1b[2$' |
'shift-delete' | + Delete | '\x1b[3$' |
'shift-pageup' | + PgUp | '\x1b[5$' |
'shift-pagedown' | + PgDn | '\x1b[6$' |
'shift-home' | + Home | '\x1b[7$' |
'shift-end' | + End | '\x1b[8$' |
'ctrl-shift-delete' | + + Delete | '\x1b[3;6~' |
capture.emitKeypressSequence(sequence)
(sequence) => Promise
Makes a sequence of writes capture's input stream, simulating a user typing a sequence of keys to process.stdin
. Between each write, a fixed number of milliseconds will be added to the capture recording to animate a user typing. This can be configured with the keystrokeAnimationInterval
option.
Accepts either a string
or a string[]
array. If a string
is passed, it will be split into characters. If a string[]
array is passed, keywords can be used like in capture.emitKeypress
above. Returns a promise that resolves after all writes have been made.
Usage
Capturing writes to stdout
Here is an example of capturing a callback function that writes to process.stdout
:
import { captureCallback } from 'cli-screencast';
// Capture terminal output with artificial timing
captureCallback((capture) => {
console.log('1st write...');
capture.wait(1500); // Wait 1.5s
process.stdout.write('2nd write...');
capture.wait(1500); // Wait another 1.5s
console.log('\n3rd write...');
capture.wait(1500); // Final 1.5s pause
}, { columns: 50, rows: 10 }).then((svg) => {
// Use or save the generated SVG string here
});
Capturing input from stdin
Here is an example of capturing a callback function that gets input from process.stdin
. Input from stdin
can be mocked using capture.emitKeypress
and capture.emitKeypressSequence
methods, or the connectStdin
option can be enabled and you can provide the input yourself. If all the input required by your callback function is not mocked, then connectStdin
must be enabled, or else you will not be able to interact with process.stdin
and the capture will hang.
In this example, capture.emitKeypressSequence
is used to mock typing Hello World!
and then hitting Return:
import { captureCallback } from 'cli-screencast';
captureCallback(async (capture) => {
// Create readline interface
const rl = capture.createInterface();
// Ask the user a question
const promise = new Promise((resolve) => {
rl.question('Write a message: ', resolve);
});
// Wait 1 second before typing response
capture.wait(1000);
// Mock user typing "Hello World!" and pressing Enter
capture.emitKeypressSequence([
'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', 'return',
]);
// Await the mocked input result
const result = await promise;
// Display the user's response
console.log(`Your Message: ${result}`);
// Close the readline interface
rl.close();
}, { columns: 50, rows: 10 }).then((svg) => {
// Use or save the generated SVG string here
});
Emulating a command
You can emulate capturing a command by passing a command string to the capture.start
method. A command prompt with animated keystrokes will be included at the start of the capture. This capture example emulates running the command echo Hello World!
:
import { captureCallback } from 'cli-screencast';
captureCallback((capture) => {
// Emulate running 'echo Hello World!'
capture.start('echo Hello World!');
// Command output
console.log('Hello World!');
}, { columns: 50, rows: 10, cursorHidden: true }).then((svg) => {
// Use or save the generated SVG string here
});
The keystrokeAnimationInterval
option can be configured to customize the speed of the keystroke animation, and the prompt prefix can be customized via the prompt
option. The command prompt can be captured without an animation by disabling the keystrokeAnimation
option.