strapkit

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/xterm

Optionally, install the fit addon if you want the terminal to automatically size itself to its container:

npm install @xterm/addon-fit

Add 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. cd into 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 with sk.read().

On this page