@cplieger/web-terminal-ui
v3.5.0
Published
Reference touch-first browser UI for @cplieger/web-terminal-engine: a composable kernel + feature modules (tabs, activity monitor, mobile toolbar, context menu, IME, predictive echo, viewport handling) and CSS. One createTerminal() call.
Downloads
5,777
Readme
@cplieger/web-terminal-ui
The reference touch-first browser UI for
@cplieger/web-terminal-engine — the part
that turns the engine's render/scroll/connection/keyboard modules into a
usable terminal on a phone as well as a desktop.
One createTerminal(root, { features }) call builds the entire terminal UI
inside a single container element you provide. A small always-present kernel
(the display output, the hidden keyboard textarea, IME, engine wiring, the
sanitizing input funnel, connection-state, and named layout regions) composes
with opt-in feature modules that own everything above the raw terminal:
- a display-only terminal output (native text selection survives redraws)
and a hidden
<textarea>that owns the keyboard and IME (the kernel) - tabs: multiple independent terminals with a desktop strip, a mobile bottom switcher, and a modal overview sheet
- an activity monitor that drives per-tab status dots (working / idle / needs-input / exited) from the server's status stream
- a mobile key toolbar (Tab / Esc / arrows / Enter / sticky-Ctrl) and a scroll-to-bottom control
- a context menu (Copy / Select All / Paste), reachable by right-click and by long-press on touch
- predictive local echo, IME / composition (CJK, dictation, autocorrect), and viewport + keyboard-inset handling for the iOS soft keyboard, rotation, and font-load reflows
- a connection-status banner and a copy toast
It is published as TypeScript source (no build step) to npm and JSR, alongside the CSS bundle and a reference HTML page. Consumers who want a different UI should depend on the engine directly and skip this package.
Install
npm install @cplieger/web-terminal-ui @cplieger/web-terminal-engine@cplieger/web-terminal-engine is a peer dependency — the UI is built on the engine,
and both halves of the wire protocol must stay in lockstep, so the consumer
pins the engine version explicitly.
Usage
Serve the bundled CSS (css/ concatenated per css/MANIFEST into the
style.css your page links) plus a minimal HTML page that has one empty
container element for the terminal, then call createTerminal(root, { features })
from your entry module. Feature bundles (presets) live at the ./presets
sub-path:
<div id="terminal"></div>
<div id="loading">Loading…</div>
<script type="importmap">
{
"imports": {
"@cplieger/web-terminal-engine": "/vendor/cplieger-web-terminal-engine/index.js",
"@cplieger/web-terminal-ui": "/vendor/cplieger-web-terminal-ui/index.js",
"@cplieger/web-terminal-ui/presets": "/vendor/cplieger-web-terminal-ui/presets.js"
}
}
</script>
<script type="module">
import { createTerminal } from "@cplieger/web-terminal-ui";
import { presetTabbed } from "@cplieger/web-terminal-ui/presets";
createTerminal(document.getElementById("terminal"), {
features: presetTabbed(),
loading: document.getElementById("loading"),
});
// or, for a server that exposes the WebSocket elsewhere / a custom font:
// createTerminal(root, { features: presetTabbed(), wsPath: "/api/shell/ws", fontReady: '14px "MyMono"' });
</script>createTerminal(root, opts?) builds the entire terminal subtree (the kernel
plus every feature's chrome) inside root itself — there is no element-id
contract for the host page to reproduce. Call it exactly once; the engine's
render/connection/scroll modules are single-instance per page (tabs multiplex
sessions over the one kernel). scaffold/index.html is a complete reference page
to copy and adapt.
Four presets are provided (import from @cplieger/web-terminal-ui/presets); each
is a plain feature-array factory, so you can spread and edit it, or hand-pick
individual features instead:
presetSingle()— single-pane desktop UI (context menu, clipboard, scroll-to-bottom, predictive echo, connection banner).presetTouch()—presetSingle()plus the mobile key toolbar.presetTabbed()— the generic tabbed UI:presetTouch()plus tabs, the activity monitor, and animations. Each tab's title is OSC-first — it follows the process window title (OSC 0/2) when the program sets one and keeps it updated, otherwise the last command submitted. The per-tab activity dot reveals itself only when a session reports OSC 9;4 progress, so a plain shell keeps clean, label-only tabs. Requires a server that speaks the session API (/api/sessionsand/ws?session=), such asweb-terminal-server.presetAgentTabbed()— the same feature set aspresetTabbed()(activity monitor included), but withpreferInputTitle: each tab's label follows the latest submitted line (persisted server-side and recovered on reload) and the program's OSC 0/2 title is ignored — for an agent shell such asweb-terminal-kirowhose program emits a non-empty but useless OSC title. Its status dots (idle / working / done / needs-input) come from the same activity monitor, driven by the server's status SSE and its OSC-9 classifier.
Options
| Option | Default | Purpose |
| ----------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| features | (none — bare kernel) | The feature list. Omitted or empty builds only the terminal (no chrome). Use a preset from ./presets or a hand-picked array. |
| wsPath | "/ws" | WebSocket endpoint path the engine connects to. |
| fontReady | '14px "MonaspiceNe NFM"' | CSS font shorthand awaited before the first resize, so the server is sized against the real web font's cell metrics rather than a fallback. |
| loading | (none) | A pre-JS loading overlay element (kept in your served HTML so it paints before this module loads); it is faded out and removed once the first frame renders. |
| theme | (none) | Theme overrides (CSS custom properties on the terminal root): --accent, --tab-hover-bg, --tab-active-bg, --tab-active-fg. The library ships neutral defaults. |
createTerminal() returns a handle: focus() re-focuses the terminal input
(and opens the soft keyboard on touch), and destroy() tears every feature down
and releases the kernel.
What ships
| Path | Purpose |
| ------------------------ | --------------------------------------------------------------------------------------------------------------- |
| src/**/*.ts | The UI modules: the kernel (kernel/), opt-in features (features/), presets, IME, predictive echo, viewport. |
| css/*.css + MANIFEST | The default theme + layout; concatenate in MANIFEST order. |
| scaffold/index.html | A reference HTML page: <head> + one empty root element + importmap. |
Related projects
The web-terminal family:
web-terminal-engine— the Go session engine + TypeScript browser renderer this UI is built on (peer dependency).web-terminal-server— a ready-to-run container that serves this UI over HTTP + WebSocket for any command.
Consumers that ship this UI:
Disclaimer
This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.
This project was built with AI-assisted tooling using Claude Opus and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.
License
GPL-3.0-or-later. See LICENSE.
