Setting up the terminal
Install xterm.js, mount a terminal, and connect it to Strapkit.
xterm.js is a terminal emulator that runs in
the browser. Pair it with Strapkit's terminal: option and you have
a real interactive shell — line editing, history, signals, colors —
all rendered on the page.
Install
npm install @xterm/xtermOptionally, install the fit addon if you want the terminal to automatically size itself to its container:
npm install @xterm/addon-fitAdd a container to the page
xterm renders into a regular DOM element. Give it a fixed-size parent:
<div id="term" style="height: 400px; width: 100%; background: #1e1e1e;"></div>The background matches xterm's default theme — without it, you'll
see a flash of white before the terminal mounts.
Without the fit addon
If you don't need auto-sizing, you can set the terminal dimensions
directly with cols and rows:
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
const term = new Terminal({
cursorBlink: true,
convertEol: true,
fontSize: 13,
cols: 80,
rows: 24,
});
term.open(document.querySelector('#term'));This gives you a fixed-size terminal. Simple, no extra dependency.
With the fit addon
The fit addon measures the container element and sets cols/rows
automatically, reflowing content when the container resizes:
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import '@xterm/xterm/css/xterm.css';
const term = new Terminal({
cursorBlink: true,
convertEol: true,
fontSize: 13,
});
const fit = new FitAddon();
term.loadAddon(fit);
term.open(document.querySelector('#term'));
fit.fit();Resize with the window
window.addEventListener('resize', () => fit.fit());If the container itself can resize (a draggable splitter, a collapsing
sidebar), use a ResizeObserver instead:
new ResizeObserver(() => fit.fit()).observe(document.querySelector('#term'));Connect it to Strapkit
Pass the Terminal to the constructor as terminal:. Strapkit wires
keystrokes to the shell, pipes the shell's output back to xterm, and
starts the shell automatically when you call init():
import Strapkit from '@strapkit/core';
const sk = new Strapkit({ apiKey: API_KEY, terminal: term });
await sk.init();That's the whole integration. The user gets a working shell prompt;
typing node, npm, ls, anything the runtime supports, just works.
Putting it together
With fit addon
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import '@xterm/xterm/css/xterm.css';
import Strapkit from '@strapkit/core';
const term = new Terminal({
cursorBlink: true,
convertEol: true,
fontSize: 13,
});
const fit = new FitAddon();
term.loadAddon(fit);
term.open(document.querySelector('#term'));
fit.fit();
new ResizeObserver(() => fit.fit()).observe(document.querySelector('#term'));
const sk = new Strapkit({ apiKey: API_KEY, terminal: term });
await sk.init();Without fit addon
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import Strapkit from '@strapkit/core';
const term = new Terminal({
cursorBlink: true,
convertEol: true,
fontSize: 13,
cols: 80,
rows: 24,
});
term.open(document.querySelector('#term'));
const sk = new Strapkit({ apiKey: API_KEY, terminal: term });
await sk.init();Don't try to drive the shell with term.write() directly — that paints
text into the terminal display without sending it to the shell, which
won't actually run anything.
Things to know
-
The shell starts in
/strapkit.cdinto your project on startup if you want a different default:sk.shellExec('cd /app'); -
The shell's view of the filesystem is the runtime's filesystem. Anything you wrote with
sk.write()shows up immediately; anything the shell creates (logs, build output) is readable withsk.read().