Terminals

A terminal panel — a Svelte SPA on xterm.js.

A terminal panel — a Svelte SPA on xterm.js.

Package: @devframes/plugin-terminals

Interactive and read-only sessions in the browser

What it does

  • Read-only output — via devframe's streaming channels.
  • Interactive shells — PTY-backed terminal sessions you can type into, including full-screen TUI programs.
  • Presets — named commands launchable in one click.

Interactive shells use zigpty's prebuilt native bindings (Linux/macOS/Windows, x64/arm64), falling back to pipe-based emulation where they can't load.

Standalone

pnpx @devframes/plugin-terminals

Mount into a Vite host

// vite.config.ts
import { terminalsVite } from '@devframes/plugin-terminals/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    terminalsVite(),
  ],
})

Programmatic

createTerminalsDevframe(options) returns a definition; declare presets to seed the launcher:

import { createTerminalsDevframe } from '@devframes/plugin-terminals'

export default createTerminalsDevframe({
  presets: [
    { id: 'dev', title: 'pnpm dev', command: 'pnpm', args: ['dev'] },
  ],
})

Hub aggregation

Mounted into a hub, the devframe spawns on its own channel (devframes:plugin:terminals:output) and mirrors every session into ctx.terminals (the hub's registry, on devframe:terminals); foreign hub sessions render read-only.

ctx.terminals is the source of truth; the devframe, the sole PTY provider, duck-types a minimal register / update / events shape to run without @devframes/hub.

startChildProcess() sessions carry a getResult() accessor (tinyexec's Result: awaitable { stdout, stderr, exitCode }, plus live getters and kill()).

Focusing a session

Via the hub's cross-iframe dock activation, an activation with a sessionId selects that session:

// e.g. right after ctx.terminals.startChildProcess(..., { id: sessionId, ... })
await rpc.call('hub:docks:activate', {
  dockId: 'devframes_plugin_terminals',
  params: { sessionId },
})

Focus is one-shot; an unknown id waits for the session.

Deep linking

Standalone, the panel keeps the selected session in the URL hash (#id=<sessionId>), so a copied link reopens it (deep-linking guide).

RPC

Namespaced devframes:plugin:terminals:*:

FunctionTypePurpose
listquery (snapshot)Sessions (status, mode, command).
presetsquery (snapshot)Declared launcher presets.
spawnactionStart from a preset id or command+mode.
writeactionInput to an interactive session.
resizeactionResize the PTY (columns × rows).
restartactionRestart, keeping scrollback.
renameactionRename a session.
terminateactionEnd the process; keep the session.
removeactionKill and discard.
clear-exitedactionDiscard stopped sessions.

Status and mutations mirror into shared state, keeping panels in sync.

Source

plugins/terminals