Dev mode (@sigx/terminal-dev)#

@sigx/terminal-dev is an HMR dev runner for SignalX terminal apps. Run your TUI under an in-process Vite dev server, save a component file, and watch the running terminal app update in place — no restart, no lost state.

Terminal
pnpm add -D @sigx/terminal-dev

Running an app#

The package installs a sigx-terminal-dev binary:

Terminal
sigx-terminal-dev                  # auto-detect src/main.tsx and similar
sigx-terminal-dev src/main.tsx     # point at the mount module
sigx-terminal-dev --root path/to/app
sigx-terminal-dev --config vite.config.ts   # layer your own Vite config on top

Point it at the module that mounts your app:

TSX
/** @jsxImportSource @sigx/runtime-core */
// src/main.tsx — the "mount module"
import { defineApp, terminalMount } from '@sigx/terminal';
import { App } from './App';

defineApp(<App />).mount({ fullscreen: true }, terminalMount);

Your tsconfig.json needs the usual SignalX JSX setup ("jsx": "react-jsx" and "jsxImportSource": "@sigx/runtime-core", or the @sigx/terminal facade — see Installation).

What a save does#

  • Edit a component module (a file that calls component(...), like App.tsx): just that module re-executes. Every live instance re-runs the new setup against its existing context and re-renders in place. The terminal never tears down, and state outside the edited setup — parent components, stores, module-level signals elsewhere — survives.
  • Edit the mount module (or a module nothing accepts): the app restarts in-process — clean terminal teardown, module cache dropped, entry re-imported.
  • Break the build: the error is reported (above the live region while mounted); the next successful save recovers automatically.
  • Quit with Ctrl+C: the dev process exits 0. Quitting the app is the normal end of a dev session, so wrappers like pnpm scripts don't report a failure. (Raw mode delivers Ctrl+C to the app as a key; the app's conventional exit 130 is translated by the bin, and other exit codes pass through.)

Programmatic API#

For embedding the dev runner in your own tooling:

TypeScript
import { startDev } from '@sigx/terminal-dev';

const handle = await startDev({
    entry: 'src/main.tsx',
    root: process.cwd(),
    onError: (err) => { /* entry failed to (re)start */ },
    onRestart: () => { /* entry (re)started */ },
});

// handle.server  — the ViteDevServer
// handle.runner  — the Vite ModuleRunner
await handle.restart();   // manual in-process restart
await handle.close();

For a custom Vite setup, use terminalDevPlugin() — the Vite plugin that injects HMR identity registration and self-accept into component modules and keeps @sigx/* external for SSR. The HMR runtime that patches live instances lives at @sigx/terminal-dev/hmr and is injected automatically.

How it works#

  • An in-process Vite dev server (middleware mode, silent) plus a module runner executes the app in Node. @sigx/* packages stay out of the hot graph wherever possible, so the renderer's terminal state and the reactivity instance survive hot updates.
  • The plugin gives every component(...) definition a stable identity (moduleId:index). When an edited module re-executes, the HMR runtime re-runs the new setup with each live instance's existing context, swaps ctx.renderFn, and calls ctx.update() — the contract @sigx/runtime-core exposes for HMR. Factories previously defined under the same identity are repointed at the new setup too, so parents that captured a component reference before the edit (tab catalogs, navigation) mount the new code on the next visit.
  • Mount modules (those calling defineApp / renderTerminal / mountTerminal) never self-accept — re-executing one would mount a second app — so edits there surface as a full reload, which the runner intercepts: tear down the terminal, clear the module cache, re-import the entry.

Limitations#

These are inherent to setup-rerun HMR:

  • Signals created inside the edited component's setup are re-created (their state resets); state anywhere else survives.
  • onMounted does not re-fire for already-mounted instances on a hot update.

Peer dependency#

@sigx/terminal-dev lists @sigx/runtime-core (0.6.x) as a peer dependency and bundles Vite. Install it as a dev dependency.

Next steps#