Sorbet is a desktop terminal workspace built with Electron, React, and xterm.js. Instead of a single terminal pane or fixed tab strip, it gives you a canvas of movable terminal cards that can be opened, resized, minimized, restored, themed, and tuned to fit the way you work.
It is especially useful for running multiple CLI agents side by side in one tiling workspace. Tools like Claude Code CLI, OpenAI Codex CLI, GitHub Copilot CLI, Amazon Kiro CLI, Google Gemini CLI, OpenCode, and similar agent-driven terminal tools become much more powerful when you can keep several sessions visible at once for coding, research, debugging, review, or general help with tasks on your computer.
Version 1.0.0 is the first stable Sorbet release. It brings the project from prototype territory into a practical daily-driver desktop terminal with PTY-backed sessions, persistent workspace state, bundled and custom themes, user-editable preferences, and Linux release packaging.
- Multi-session terminal workspace with draggable, resizable terminal cards
- Real PTY-backed shells powered by
node-pty - Persistent workspace layout and active theme
- Minimize, maximize, restore, and close controls for each terminal window
- Editable terminal titles with a hover affordance
- Seven bundled themes, including the new default
Sorbettheme - Custom user themes loaded from JSON files
- User-editable preferences for theme, font, font size, clipboard behavior, and more
- Clipboard support with multiline paste, middle-click paste, and configurable copy/paste hotkeys
- Native application menu with Sorbet-specific Help and Preferences entries
- Secure preload bridge with context isolation enabled
- Linux release packaging for
AppImage,deb, andrpm - Windows installer build automation through GitHub Actions
README.mdfor product overview, installation, and developmentOVERVIEW.mdfor architecture and implementation detailsCHANGELOG.mdfor release historyROADMAP.mdfor planned product direction and future features
- Electron for the desktop shell and privileged main process
- React for the renderer UI
- Zustand for renderer-side state management
- xterm.js for terminal rendering
node-ptyfor shell process managementreact-grid-layoutfor the draggable/resizable workspace canvas- Vite for renderer development and bundling
- TypeScript across both main and renderer processes
- Open multiple terminal sessions in the same window
- Drag cards by the full title bar
- Resize cards with smoother, finer grid movement
- Minimize sessions to a dock and restore them later
- Maximize a session to focus on a single terminal
- Close sessions cleanly and terminate their PTYs
- Spawn new cards horizontally to the right of the most recent card when space allows
- Real shell processes, not simulated command output
- xterm.js rendering with automatic fit-on-resize behavior
- Automatic PTY resize when cards change size
- Editable session names
- Shell-driven title updates via escape sequences
- Click-to-focus terminal behavior
- Web links opened in the system browser
- Clipboard support:
Cmd/Ctrl+Shift+Cto copy selection by defaultCmd/Ctrl+Shift+Vto paste by default- middle-click paste enabled by default
- optional right-click paste via preferences
- multiline paste support through xterm’s paste path
- Built-in theme switcher with these bundled themes:
SorbetMidnight GraphiteDraculaNordTokyo NightCatppuccin MochaGruvbox Dark
- Custom JSON themes loaded from the user theme folder
- User-editable preferences file for:
- default theme
- font family
- font size
- line height
- letter spacing
- scrollback
- clipboard shortcuts and paste behavior
At a high level, the application is split into three parts:
- The Electron main process creates the native window, spawns PTY-backed shell sessions, manages menus, loads and watches user configuration files, and persists workspace settings.
- The preload script exposes a small, typed API on
window.sorbetso the renderer can safely request PTY, clipboard, and storage operations. - The React renderer manages the workspace layout, terminal lifecycle, theme selection, user preferences, and card interactions.
When a new card is created, the renderer computes a layout position, adds a session to the Zustand store, and mounts a TerminalCard. That card initializes xterm.js, asks the main process to create a PTY, wires terminal input/output over IPC, and reacts to live preference changes such as font or clipboard behavior.
- Node.js 18 or newer
- npm 9+ recommended
- Platform support depends on Electron and
node-pty
Because node-pty includes native bindings, the first install may require platform-specific build tooling:
- macOS: Xcode Command Line Tools
- Windows: Visual Studio Build Tools and Python
- Linux: a standard native build toolchain for Node modules
If the native module does not build correctly during install, try:
npm rebuild node-ptynpm installTo install Sorbet globally from npm and launch it from anywhere:
npm i -g @pinkpixel/sorbetThen run:
sorbetFor v1.0.0, Sorbet publishes Linux desktop packages in these formats:
AppImagedebrpm
The Windows installer is built separately in GitHub Actions as a .exe artifact.
Start the full development environment with:
npm startYou can also launch the same flow with the project launcher:
./sorbetIf you want the local checkout exposed on your PATH during development, run:
npm linkThis launches:
- the Vite dev server for the renderer on
http://localhost:5173 - the TypeScript compiler in watch mode for the Electron main process
- Electron after both the dev server and compiled main/preload output are ready
When the Electron app window closes, the full dev session now exits automatically.
You can also run the pieces separately if needed:
npm run devThis starts the renderer and main-process watchers only.
npm run electronThis waits for the renderer dev server and compiled main files, then starts Electron.
Create a production build with:
npm run buildThat produces:
dist/rendererfor the bundled React UIdist/mainfor the compiled Electron main and preload scripts
To package the application into the base Electron output directories, run:
npx electron-builderFor Sorbet release artifacts, use the dedicated packaging scripts:
npm run dist:linux:x64
npm run dist:linux:arm64These generate Linux packages in release/ for:
AppImagedebrpm
For the arm64 release, run the command on an arm64 Linux machine or runner so native modules such as node-pty rebuild for the correct target architecture.
The icon set used by the packaged app and installers is generated from assets/icon.png with:
npm run iconsWindows installers are built in GitHub Actions from .github/workflows/windows-installer.yml and uploaded as workflow artifacts. On release events, the workflow also uploads the generated .exe file to the GitHub release.
v1.0.0 establishes the first stable Sorbet release line with:
- real shell-backed terminals instead of simulated output
- a polished card-based workspace with minimize and maximize flows
- persisted layout and theme state between launches
- custom themes and user-editable preferences JSON
- Linux packaging with desktop integration metadata and generated app icons
- Windows installer automation for release distribution
The main process resolves a shell using this order:
SORBET_SHELL- common system shell paths such as Bash or Zsh
- the current
SHELLenvironment variable /bin/shas a fallback
On Windows, the app defaults to powershell.exe.
Interactive shells like Bash, Zsh, and Fish are launched with -i so they behave like interactive terminal sessions.
Sorbet stores workspace state through electron-store:
- the terminal card layout
- the selected theme ID
Sorbet also creates user-editable JSON files under Electron’s userData directory:
preferences.jsonthemes/*.json
You can open these from the native menu:
File -> Preferences -> Edit Preferences JSONFile -> Preferences -> Create New Theme
The generated preferences file includes inline guidance, example font-family strings, recommended monospace fonts, and Nerd Fonts links. Extra helper keys that start with _ are ignored by the app.
| Shortcut | Action |
|---|---|
Cmd/Ctrl+T |
Open a new terminal session |
Cmd/Ctrl+Shift+C |
Copy terminal selection |
Cmd/Ctrl+Shift+V |
Paste into the active terminal |
sorbet/
├── src/
│ ├── main/
│ │ ├── main.ts # Electron lifecycle, PTY creation, menus, user config, IPC
│ │ └── preload.ts # Safe renderer bridge exposed as window.sorbet
│ └── renderer/
│ ├── components/
│ │ ├── TerminalCard.tsx # xterm.js setup, PTY wiring, clipboard, card controls
│ │ └── ThemePicker.tsx # Theme selection dropdown
│ ├── store/
│ │ └── index.ts # Zustand store for sessions, layout, and theme
│ ├── themes/
│ │ └── index.ts # Built-in theme definitions and preference defaults
│ ├── types/
│ │ └── index.ts # Renderer-side TypeScript types
│ ├── App.tsx # Workspace shell and grid layout orchestration
│ ├── app.css # Global renderer styles and xterm overrides
│ └── main.tsx # React entry point
├── scripts/
│ └── start-electron.cjs # Launch helper for Electron in development
├── dist/ # Build output
├── index.html # Vite HTML entry
├── logo.png # Sorbet logo
├── package.json # Scripts, dependencies, metadata
├── tsconfig.json # Renderer TypeScript config
├── tsconfig.main.json # Main-process TypeScript config
└── vite.config.ts # Vite configuration
The Electron main process is responsible for:
- creating the main browser window
- loading the Vite dev server in development or built files in production
- managing PTY session lifecycle
- forwarding PTY output and exit events to the renderer
- persisting layout and theme settings
- exposing native application menus
- opening Sorbet help links externally
- creating and watching user configuration files
The preload script exposes:
window.sorbet.ptywindow.sorbet.storewindow.sorbet.clipboardwindow.sorbet.platform
This keeps the renderer isolated from direct Node.js or Electron APIs while still allowing the UI to control terminal sessions and clipboard integration.
The React renderer manages:
- visible and minimized sessions
- active and maximized session state
- grid layout data
- built-in and custom theme selection
- user preference loading
- terminal card mounting/unmounting
Built-in theme definitions live in src/renderer/themes/index.ts. Each theme includes:
- an ID and display name
- xterm-compatible terminal colors
- a UI accent color used across the app shell
Use File -> Preferences -> Create New Theme to create a theme template in the user themes directory, then edit the JSON file. Sorbet watches that directory and automatically adds valid theme files to the picker.