Skip to main content

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.

note

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.

warning

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.columnsnumber

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.rowsnumber

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
KeywordKeypressesEscape 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
});
result
1st write...1st write...2nd write...1st write...2nd write...3rd write...

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
});
result
Write a message: Write a message: HWrite a message: HeWrite a message: HelWrite a message: HellWrite a message: HelloWrite a message: Hello Write a message: Hello WWrite a message: Hello WoWrite a message: Hello WorWrite a message: Hello WorlWrite a message: Hello WorldWrite a message: Hello World!Write a message: Hello World!Your Message: Hello World!

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
});
result
> > e> ec> ech> echo> echo > echo H> echo He> echo Hel> echo Hell> echo Hello> echo Hello > echo Hello W> echo Hello Wo> echo Hello Wor> echo Hello Worl> echo Hello World> echo Hello World!> echo Hello World!Hello World!

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.