A mouse-friendly multitasking terminal built with pnpm, react, typescript, vite, tailwind, storybook, and xterm.js.
pnpm install # install deps
pnpm build # build lib, vscode extension, and website
lib/— Shared React + TailwindCSS frontend library: components, tests, Storybook.lib/src/lib/platform/— platform abstraction (PlatformAdapterinterface, fake + VSCode adapters)lib/src/remote/— remote control:host/(laptop side),client/(phone-side protocol +RemotePtyAdapter),pocket-app/(Pocket shell),ws.ts(shared socket surface)
standalone/— Tauri desktop app (Rust + Vite frontend).standalone/sidecar/— Node.js PTY manager (native PTY via node-pty), bundled as the Tauri sidecarstandalone/src-tauri/— Rust backend bridging webview ↔ sidecar
vscode-ext/— VS Code extension wrapping the lib in a webview (esbuild; node-pty via forked child process)website/— Marketing site (Vite) bundling part of the lib as an interactive demo onFakePtyAdapterserver/— Selfhost coordinating server for remote control (Hono): accounts + passkey auth in local JSON files (no database), WebSocket relay between Pocket clients and Hosts, serves the built Pocket appdor/— ThedorCLI (stricli) staged onto thePATHof every Dormouse-launched terminal; talks to its host over a private control socketserver-lib-common/— Security primitives + remote wire contract shared byserver, the Host module inlib, and the Pocket app (bare ES2022 — no DOM or Node types)dor-lib-common/— Cross-platform external-process spawning (spawnAndCapture) shared bydorand thelibhost. Despite the parallel names, the two*-lib-commonpackages are unrelated:server-lib-commonis remote security/wire,dor-lib-commonis spawn plumbing.canopy/— Experimental 3D/WebXR terminal-rendering lab (Storybook-only, not in the production build). Consumes@diffplug/xterm-addon-webgl-sdf— the webgl addon from our xterm.js fork (sdfbranch). The fork pipeline, SDF rendering architecture, and version-lockstep rules live indocs/specs/webgl-text.md.
The primary job of a spec is to be an accurate reference for the current state of the code. Read the relevant spec before modifying a feature it covers — the spec describes invariants, edge cases, and design decisions that are not obvious from the code alone.
Each spec's own Files / Code Map section is the exhaustive file→spec mapping; the touch points below are the load-bearing areas only.
docs/specs/glossary.md— Canonical vocabulary: the Surface model (terminal Session / browser surface), the six Session layers and their states, theWindow ⊃ Workspace ⊃ Pane ⊃ Surfacehierarchy and Workspace union status, transition verbs, invariants I1–I10, and the Liskov contract on Registry APIs. Read this first; every other spec defers to it when naming a state, a surface kind, or a verb.docs/specs/layout.md— Tiling layout and its interaction model: panes/doors, the Lath engine pointer, passthrough/command modes, command-mode keyboard dispatch, selection overlay, spatial navigation, minimize/reattach, inline rename, kill confirmation, session lifecycle + persistence recovery, pane animations, and the workspaces-rollout ledger (## Future). The tiling engine internals live in tiling-engine.md; layout.md owns the interaction model on top. Touch points:lib/src/components/Wall.tsx+components/wall/,Baseboard.tsx/Door.tsx/TerminalPane.tsx, the registry/lifecycle/persistence modules inlib/src/lib/, and any keyboard/navigation/mode/workspace behavior.docs/specs/shortcuts.md— Quick-reference table of every keyboard shortcut by mode/context. layout.md owns the behavior; update both when a binding changes.docs/specs/tiling-engine.md— Lath, Dormouse's in-house headless tiling engine (it replaced dockview-react; the dependency is gone). Owns the engine internals: the pure core (split-tree model +layout(), the op set with Door restore tokens, sash/neighbor/autoEdge geometry, the headless animator, hierarchical hit-testing), the never-re-parent LathHost adapter with native motion and depth-cycling pointer DnD, the wall store + engine, the pane props contract, and Lath-only persistence. layout.md owns the interaction model on top. Touch points:lib/src/lib/lath/, thelath-*modules +LathHost.tsxinlib/src/components/wall/,Wall.tsx.docs/specs/alert.md— The Activity layer: WATCHING / protocol / command-exit alert tracks, attention model, TODO lifecycle, bell + TODO UI states, notification protocols (OSC 9/9;4/99/777/BEL) with sanitization and security rules, and the Workspace union projection. layout.md defers to it for all alert/TODO behavior. Touch points:activity-monitor.ts,alert-manager.ts, notification parsing interminal-protocol.ts, the bell/TODO UI in pane headers and Doors.docs/specs/terminal-state.md— Per-Session semantic state: CWD, prompt/editing/running/finished lifecycle, command runs, title candidates + header derivation, grouping keys, and the keystroke fallback for shells without OSC integration. Touch points:terminal-state.ts,terminal-state-store.ts, semantic event parsing interminal-protocol.ts, derived pane/door labels.docs/specs/terminal-escapes.md— Registry of every escape sequence Dormouse parses, answers, or deliberately ignores: OSC/CSI tables pointing at the owning spec, parsing location +pty:datastrip semantics, replay-time report filtering, iTerm2 identity, and shell-integration injection. Touch points: OSC/CSI parsing at the PTY data boundary,terminal-report-filter.ts,mouse-mode-observer.ts,standalone/sidecar/shell-integration/, or adding any new escape sequence.docs/specs/transport.md— Adapter-agnostic webview ↔ host protocol: PTY lifecycle + buffering, the reconnection sequence, message contracts, persisted-session types and migrations, and universal adapter invariants. Touch points:pty-manager.ts/pty-host.js/pty-core.js,message-router.ts/message-types.ts, the platform adapters,reconnect.ts/session-save.ts/session-restore.ts/session-types.ts, anything crossing the webview/host boundary.docs/specs/mouse-and-clipboard.md— Terminal-owned text selection, copy (Raw / Rewrapped), paste tiers + bracketed paste, smart URL/path extension, mouse-reporting override UI, and the state matrix for which layer owns mouse events. Touch points: the mouse/selection/clipboard modules inlib/src/lib/,SelectionOverlay.tsx/SelectionPopup.tsx,standalone/sidecar/clipboard-ops.js.docs/specs/theme.md— Theme system: two-layer CSS variable strategy (--vscode-*→--color-*), the consumed-token resolver and registry defaults, terminal color contract, theme data + OpenVSX installer, Storybook simulation, and the theme debugger. Touch points:lib/src/lib/themes/,lib/src/theme.css,ThemePicker.tsx,lib/scripts/bundle-themes.mjs, theme restore in the hosts.docs/specs/dor-cli.md— ThedorCLI staged onto every Dormouse terminal'sPATH: bundling + env contract, thespawnAndCapturerules for external binaries (dor-lib-common), host control-socket plumbing, the Surface handle model, the implemented command set, and the bundled agent skill (dor skill/--install). Touch points:dor/,dor-lib-common/, control-request routing in each host, theWall.tsxcontrol handler.docs/specs/dor-browser.md— The unified browser surface: oneBrowserPanelwith swappablerenderMode(ab-screencast/ab-popout/iframe), canonical params + surface lifetimes, browser chrome, the Display modal + render swaps, the agent-browser connection/input/pop-out stack, and the iframe proxy + shim + CSP boundaries. Builds on the handle model in dor-cli.md. Touch points:BrowserPanel.tsx/AgentBrowserPanel.tsx/IframePanel.tsx/SurfacePaneHeader.tsx, theagent-browser-*modules inlib/src/components/wall/,lib/src/host/, thedor ab/dor iframecommands.docs/specs/vscode.md— VS Code host layer: WebviewView + WebviewPanel hosting, webview ↔ Workspace mapping and union-status reflection onto native chrome, the persistence flow and its ordering rules, theme integration + OSC color-query answering, CSP, and the build/dogfood pipeline. The transport protocol it speaks lives in transport.md. Touch points:vscode-ext/src/, the theme resolver/observer interminal-theme.ts, VS Code commands and context keys.docs/specs/standalone.md— Standalone (Tauri) host layer: the Rust ↔ Node-sidecar bridge (invoke commands, JSON-lines stdio), the webview boot sequence, the AppBar (shell dropdown, theme picker, window controls), localStorage persistence at the adapter boundary, sidecar shutdown ordering + parent-PID watchdog, file drop, logging, and the build/dev workflow (including the vite-onlybeforeDevCommandcaveat). The transport protocol it speaks lives in transport.md. Touch points:standalone/src-tauri/src/lib.rs,standalone/src/(main, AppBar, tauri-adapter),standalone/sidecar/main.js.docs/specs/auto-update.md— Standalone auto-update: check → user-approved download → install-on-quit, the Baseboard update notice, Windows sidecar teardown (ConPTY file locks), post-install markers, and per-platform quit behavior. Touch points:standalone/src/updater.ts,UpdateBanner.tsx, the updater config intauri.conf.json.docs/specs/mobile-terminal-ui.md— The mobile terminal composition (MobileTerminalUi/MobileWall): stable viewport + keyboard reserve, touch modes (Gestures / Select / Mouse), radial gesture-menu geometry and interaction, input modes, and Type-mode native-keyboard rules. Shipped in the website Pocket playground and reused by the real Pocket app. Touch points:lib/src/components/MobileTerminalUi.tsx/MobileWall.tsx/MobileGestureRadialMenu.tsx,lib/src/lib/mobile-gesture-menu.ts.docs/specs/tutorial.md— Website playground tutorial: device-specific routes, thetutrunner + detector + progress state, desktop and Pocket profiles, localStorage keys, the lib hooks that exist for tutorial observability, and the mouse/clipboard feature-coverage matrix. Touch points: the playground pages inwebsite/src/pages/,website/src/lib/tut-*.ts,PocketTerminalExperience.tsx, fake-adapter scenario extensions, theWallEventunion.docs/specs/webgl-text.md— The SDF text-rendering stack for the 3D/WebXR terminal effort: the diffplug/xterm.js fork pipeline (branch strategy, sdf-version lockstep with@xterm/xtermpins, GitHub-release tarball distribution), the SDF glyph architecture in the forked webgl addon (color-free atlas with one texture entry per shape, shader tint/smoothstep contract, raster fallbacks for emoji/custom glyphs/decorated cells, the MSDF-compatible texel reservation), and the canopy Storybook lab with its upstream-vs-fork regression harness. Touch points:canopy/, the fork'saddons/addon-webgl(separate repo), any bump of the fork tarball URL or@xterm/*pins incanopy/package.json.docs/specs/remote-security-model.md— The trust model for remote control: passkeys prove fresh user presence (user credentials — they sync), non-extractable per-browser device keys prove long-lived Client identity, the Host's local ACL authorizes the pair via a local-approval pairing ceremony, and the Host — never the Server — makes the final access decision. Read this first for anything remote; the other three remote specs build on it. Touch points:server-lib-common/src/security/,server/src/handshake.ts, the security modules inlib/src/remote/host/andlib/src/remote/client/.docs/specs/remote-api.md— The protocol a Client speaks afterauthorizeConnection: the shipped terminal-only protocol-v1 (snapshot directory, attach-is-the-resize, last-attach-wins size authority) and the staged remainder (browser surfaces, in-flight replay, semantic scrollback, tethering display, grants, VR Window, WebRTC). Touch points:server-lib-common/src/remote/wire.ts(the fixed wire contract),lib/src/remote/host/remote-api.ts,lib/src/remote/client/.docs/specs/server.md— The selfhost coordinating server: env config, two-JSON-file state, "WebAuthn without a WebAuthn library", the HTTP API, the relay frame flow (one host challenge feeds both signatures → one biometric prompt per connect), the Host webview CSP for self-host relays (DORMOUSE_REMOTE_CONNECT_SRC), Host/Pocket side responsibilities, the testing harness, and instructions for running it end to end. Touch points:server/src/,lib/src/remote/host/enrollment.ts, thedev:pocket-serverflow.docs/specs/pocket-app.md— Pocket app architecture: the remote session is aPlatformAdapter(RemotePtyAdapter), so Pocket is auth screens + the mobile-terminal-ui composition; thelib/src/remote/module layout and the same-origin deployment rule (WebAuthn origin binding + Chrome PNA). Touch points:lib/src/remote/client/+pocket-app/,lib/vite.pocket.config.ts, the Pocket static serving inserver/src/app.ts.docs/specs/deploy.md— Release process: the artifact matrix, release checklist, two-stage pipeline (CI builds unsigned + attests; a local script verifies, signs macOS/Windows, and creates the GitHub Release), Tauri updater manifest, changelog flow, and secrets. Touch points:.github/workflows/release.yml,scripts/sign-and-deploy.sh,scripts/bump-version.sh, the updater config intauri.conf.json.
When updating code covered by a spec, update the spec to match. When the two specs overlap (e.g. pane header elements appear in both), layout.md documents placement and sizing while alert.md documents behavior and visual states.
When editing specs, keep them concise but do not replace invariants or edge cases with only a code pointer. Use Source of truth: for implementation references, and include direction/scope for protocols, command orchestration, and cross-package boundaries. For docs-only compression, spot-check referenced symbols, message directions, and root-vs-package script ownership against code before committing.
Every spec that uses Session / Pane / Door / baseboard / passthrough vocabulary leads with a > See \docs/specs/glossary.md` for ...blockquote (seelayout.md, alert.md, terminal-state.md`). When introducing glossary vocabulary into a spec that lacks the callout, add it in the same edit.
Specs are written ahead of the code on purpose: a new component's spec starts as a full design (a "dream"), gets cut down to the smallest slice that unblocks the broader system, that slice is implemented, and the uncut remainder is kept because it was expensive to generate and stays useful for planning. These conventions keep that workflow from eroding the rule that a spec's body is an accurate reference for the current code:
- The fold. Everything above a spec's
## Futuresection describes the code as it is — present tense, anchored withSource of truth:pointers. Everything not yet built lives under## Future, which is always the last section of the file. A spec with no unbuilt design has no## Futuresection. - Design-stage specs. A spec for a component that doesn't exist yet keeps its whole design under
## Futureand opens with> Status: design — nothing here is implemented yet.It is indexed above like any other spec. - Named scopes. When a dream is cut down, record the cut as a named scope at the top of
## Future(e.g.**Scope: workspaces-rollout**), listing what remains in staged order. A scope is defined in exactly one spec; other specs link to it by name and never restate its contents. Rollout ledgers live in the owning spec's## Future, nowhere else. - Reservations. When unbuilt design constrains present code — a reserved wire field, a reserved ref grammar, an additive-evolution guarantee — state that constraint in the body, marked
Reserved:, pointing at the## Futureitem it serves. Test: if deleting the sentence would let someone break future compatibility today, it belongs in the body. - Promotion is part of done. Implementing a staged item is not finished until its text moves above the fold — rewritten from "will" to "is", with
Source of truth:added — and the built portion is deleted from## Future. Never leave completed plan text (build orders, phase lists) below the fold; delete it — git history keeps the record.
The mechanically checkable parts of these conventions are enforced by scripts/spec-lint.mjs (pnpm lint:specs, also the first step of the root pnpm test): every spec indexed here, ## Future last, relative links/anchors resolving, and backticked repo paths existing on disk.
See PRODUCT.md for users, brand personality, and aesthetic direction (including the anti-references), and DESIGN.md for the full design system — tokens, named rules, and component vocabulary. Key principles:
- Native first — Inside VSCode, feel indistinguishable from a built-in feature. Use the host's theme tokens.
- Information density without intimidation — Dense for power users, approachable for beginners. Progressive disclosure.
- Status at a glance — Scannable in under a second across many terminals.
- No chrome, all content — Minimize UI chrome. Terminals are the content.
- Theme-adaptive — Never hardcode colors. Support light and dark from day one.
The concrete type scale, color strategy (surfaces, foregrounds, header palette, dynamic door bg, selection ring), and shared chrome constants live in
lib/src/components/design.tsx — read it
before adding or changing any text-*, bg-*, text-color-*, or border
class anywhere in lib/src/. The actual @theme token definitions are in
lib/src/theme.css; when adding or removing a token,
update both files together.