This guide is the complete operational and architectural reference for Hyper,
the extensible terminal built on Electron, React, Redux, and xterm.js. It is
written for administrators packaging or deploying Hyper, plugin authors building
on its extension API, and developers who need to understand or reproduce its
internals. Every fact, default, file path, configuration key, and IPC channel
documented here is derived directly from Hyper's source code.
Note
Reference version: Hyper 4.0.0-canary.5. Hyper 4 changed the configuration format from Hyper 3's JavaScript
~/.hyper.jsto a JSONhyper.jsonfile in a platform-specific config directory; the migration path is documented in Configuration Migration.
1. Executive Summary
Hyper is a cross-platform desktop terminal emulator. It is a thin, plugin-driven
Electron application: a Node-powered main process owns the OS-level
concerns (windows, pseudo-terminals, config, plugin installation, auto-update,
native menus), and a React + Redux renderer draws the terminal using
xterm.js. The two halves communicate over a per-window RPC bridge built on
Electron IPC.
| Attribute | Value |
|---|---|
| Product | Hyper ("A terminal built on web technologies") |
| Version | 4.0.0-canary.5 |
| Author / owner | ZEIT, Inc. / Vercel |
| License | MIT |
| App ID | co.zeit.hyper |
| Platforms | macOS, Windows, Linux (x64 + arm64) |
| Runtime | Electron 22.3.25 (Chromium + Node) |
| UI stack | React 18.2, Redux 4.2 (+ react-redux, redux-thunk, reselect, seamless-immutable) |
| Terminal engine | xterm.js 5.3 + addons (fit, search, web-links, webgl, unicode11, image, ligatures, canvas) |
| PTY | node-pty 1.0 |
| Language | TypeScript 5.4 |
| Package manager | pnpm 10.33 |
| Config file | hyper.json (JSON) |
| Extensibility | npm-published plugins/themes via a ~40-method extension API |
Important
Hyper deliberately runs its renderer with
nodeIntegration: trueandcontextIsolation: false, and enables@electron/remote. This is the opposite of the modern hardened-Electron posture, and it exists so that plugins (arbitrary npm packages) canrequireNode modules directly in the renderer. The security consequences of this design are covered in Security Model.
2. Architecture
2.1 Two-process design
┌───────────────────────────────────────────┐
│ Hyper (Electron) │
│ │
~/.config/Hyper/ │ ┌──────────────────┐ IPC/RPC ┌─────┐ │
hyper.json ───watch───▶│ │ MAIN PROCESS │◀──(uuid)───▶│ R │ │
│ │ (app/, Node) │ channel │ E │ │
node-pty ──spawn──────▶ │ │ │ │ N │ │
│ shell (zsh/bash/…) │ │ • windows │ │ D │ │
│ │ │ • sessions(PTY) │ │ E │ │
▼ │ │ • config loader │ │ R │ │
PTY data ──batched────▶ │ │ • plugin loader │ │ E │ │
(16ms / 200KB) │ │ • auto-updater │ │ R │ │
│ │ • native menus │ │ (lib/, React+Redux+xterm.js)
│ └──────────────────┘ └─────┘ │
└───────────────────────────────────────────┘
| Layer | Source dir | Responsibilities |
|---|---|---|
| Main process | app/ |
App lifecycle, BrowserWindow management, node-pty sessions, config load/watch, plugin install + loading, auto-update, native menus, OS integration (CLI install, ssh:// protocol, context menu) |
| Renderer | lib/ |
React UI, Redux store, xterm.js terminal, tab/pane (term-group) layout, dispatching user intents back to main via RPC |
| CLI | cli/ |
Standalone hyper command for plugin management and launching |
| Type contracts | typings/ |
Shared .d.ts definitions: config, hyper, common, action constants |
2.2 Repository structure
hyper/
├── app/ # Electron MAIN process (TypeScript)
│ ├── index.ts # Main entry: app lifecycle, window creation
│ ├── config.ts # Config facade: profiles, watch/reload, decorate
│ ├── config/ # Config subsystem
│ │ ├── config-default.json # Default settings (ground truth for defaults)
│ │ ├── schema.json # Generated JSON Schema for hyper.json
│ │ ├── import.ts / init.ts # Load + merge user/default config
│ │ ├── migrate.ts # Hyper 3 → Hyper 4 migration
│ │ ├── paths.ts # All config/plugin/CLI paths per platform
│ │ ├── open.ts # Open hyper.json in editor
│ │ └── windows.ts # Window position/size persistence (electron-store)
│ ├── session.ts # PTY session (node-pty) + DataBatcher
│ ├── rpc.ts # Per-window RPC Server (IPC bridge)
│ ├── plugins.ts # Plugin loading, npm install, decorate hooks
│ ├── plugins/ # extensions.ts (API surface) + install.ts (yarn)
│ ├── commands.ts # Command map (keymap → action) for the main process
│ ├── menus/ # Native menu builders (per platform + per section)
│ ├── keymaps/ # Default key bindings: darwin/linux/win32 JSON
│ ├── ui/ # window.ts (BrowserWindow), contextmenu.ts
│ ├── updater.ts # Auto-update feed + channel logic
│ ├── auto-updater-linux.ts # Custom Linux update-check implementation
│ ├── notifications.ts # Remote "news" notifications
│ ├── notify.ts # Native desktop notifications
│ └── utils/ # cli-install, colors, map-keys, shell-fallback, …
├── lib/ # RENDERER (React + Redux + xterm.js)
│ ├── index.tsx # Renderer entry: store + RPC wiring
│ ├── command-registry.ts # Renderer-side command handlers
│ ├── actions/ # Redux action creators (sessions, term-groups, ui, …)
│ ├── reducers/ # Redux reducers (sessions, term-groups, ui)
│ ├── containers/ # Connected React containers (hyper, terms, header, …)
│ ├── components/ # React components (term, tabs, split-pane, searchBox, …)
│ ├── store/ # Store config + write-middleware
│ ├── selectors.ts # reselect selectors
│ └── utils/ # config, plugins, ipc, rpc, paste, term-groups, …
├── cli/ # hyper CLI (api.ts + index.ts)
├── typings/ # Shared TypeScript definitions
├── electron-builder.json # Packaging/distribution config
├── webpack.config.ts # Renderer bundling
└── package.json # Root (build) + app/package.json (runtime deps)
2.3 The RPC bridge (the API surface)
Communication between renderer and main is a custom event emitter layered over
Electron IPC. Each window gets a Server instance (app/rpc.ts) keyed by a
freshly generated UUID channel. On did-finish-load, the main process sends an
init message carrying the channel id and profile name; thereafter both sides
emit/on typed events.
Because Hyper has no HTTP API and no class-based route handlers, the RPC event table is the authoritative API surface — AST extraction reports zero endpoints, which is expected for this architecture. The principal events:
Renderer → Main (the user's intents):
| Event | Effect in main process |
|---|---|
new |
Create a session (spawn PTY), optional split direction/profile |
exit |
Terminate a session by uid |
data ({uid, data, escaped}) |
Write bytes to a session's PTY |
resize ({uid, cols, rows}) |
Resize the PTY |
maximize / unmaximize / minimize / close |
Window state |
open external ({url}) |
Open a URL in the default browser |
open context menu (selection) |
Pop up the terminal context menu |
open hamburger menu ({x, y}) |
Pop up the application menu (Win/Linux) |
command (name) |
Execute a named command in the main process |
quit and install |
Trigger an in-place app update |
Main → Renderer (results and OS events): init, session add,
session data, session exit, termgroup add req, split request horizontal/vertical, session clear req, session search, reset/increase/ decrease fontSize req, move left/right req, move jump req, next/prev pane req, open file, open ssh, update available, add notification, enter/ leave full screen, windowGeometry change, plus a family of session move/del … req events that send line-editing escape sequences.
In addition, the main process registers ipcMain.handle request/response
endpoints used by the renderer and plugins:
| IPC handle | Returns |
|---|---|
child_process.exec |
Result of running a command (for plugins) |
child_process.execFile |
Result of running a file |
getDecoratedConfig (profile) |
Final config after plugin decoration |
getDecoratedKeymaps |
Final keymaps after plugin decoration |
getLoadedPluginVersions |
Loaded plugin name/version list |
getPaths / getBasePaths |
Plugin directory paths |
getDeprecatedConfig |
Plugins using deprecated config CSS |
2.4 Terminal session data pipeline
shell (PTY) ──onData──▶ DataBatcher ──flush (16ms or 200KB)──▶ Session 'data'
▲ (prefixes each batch with the │
│ session uid; utf8 StringDecoder) ▼
│ rpc.emit('session data')
keystrokes ◀── pty.write ◀── rpc 'data' ◀──── renderer (xterm.js onData) ◀┘
- Each
Session(app/session.ts) wraps anode-ptyPTY. - A
DataBatcheraccumulates PTY output and flushes it to the renderer at most every 16 ms or when the buffer reaches 200 KB, prefixing each batch with the 36-character session UUID. This batching minimizes IPC calls and GC pressure. The renderer strips the UUID prefix and feeds the rest toxterm.js. - On the renderer side, tabs are root term-groups and panes are nested
term-groups (
lib/actions/term-groups.ts,lib/reducers/term-groups.ts).
Note
Line-editing commands (move/delete by word or line, break, stop, quit, tmux prefix) are implemented in
lib/index.tsxby writing raw escape/control sequences to the active session — e.g.\x1bb(move word left),\x03(Ctrl+C/ break),\x02(tmux prefix). They are not shell-specific keybindings.
3. System Requirements
Runtime (end users)
| Requirement | Detail |
|---|---|
| OS | macOS, Windows 10+, or a mainstream Linux distribution |
| Architecture | x64 or arm64 |
| Disk | Electron app bundle (~150–250 MB depending on platform) |
| Network | Optional — used for plugin install, update checks, and remote notifications |
Build / development
| Requirement | Detail |
|---|---|
| Node.js | Compatible with @types/node 18 (Node 18.x toolchain) |
| pnpm | 10.33+ (enable via corepack enable pnpm) |
| Native build tools | Required to compile node-pty |
| Windows | pnpm add --global windows-build-tools from an elevated prompt |
| macOS | Xcode CLT (agree to license: sudo xcodebuild) |
| Linux (RPM-based) | GraphicsMagick, libicns-utils, xz |
| Linux (Debian-based) | graphicsmagick, icnsutils, xz-utils |
4. Configuration Reference
Hyper's entire configuration is the hyper.json file. This section documents the
file's structure and every key under config, with type, default, and meaning,
as defined by typings/config.d.ts, app/config/schema.json, and
app/config/config-default.json.
4.1 File location and load order
app/config/paths.ts resolves the config directory as follows:
| Condition | Config directory |
|---|---|
XDG_CONFIG_HOME is set |
$XDG_CONFIG_HOME/Hyper |
| Windows | Electron userData directory |
| macOS / Linux | ~/.config/Hyper |
The config file is hyper.json within that directory. In development
(NODE_ENV !== production), if a hyper.json exists at the repo root, that file
is used instead — so developers can keep their personal config separate.
Load sequence (app/config/import.ts → init.ts):
- Ensure plugin directories exist (
plugins/,plugins/local/). - Run Hyper 3 → 4 migration if needed.
- Read
config-default.json(the baseline). - Overlay the platform's default keymap JSON.
- Read the user's
hyper.json; on parse failure, notify and fall back to defaults. - Deep-merge user config over defaults; normalize profiles; merge keymaps; filter out empty plugin entries.
Hyper watches hyper.json with chokidar and reloads ~100 ms after any
change, notifying subscribers and re-running plugin installation if the plugin
list changed (app/config.ts).
4.2 Top-level keys
| Key | Type | Purpose |
|---|---|---|
config |
object | All terminal/appearance/behavior settings |
plugins |
string[] | npm packages to install + load ([@org/]project[#version]) |
localPlugins |
string[] | Local plugin folder names under plugins/local/ |
keymaps |
object | Command → shortcut(s) overrides |
4.3 Root-level config options
These are application-wide (not per-profile) settings.
| Key | Type | Default | Description |
|---|---|---|---|
updateChannel |
'stable' | 'canary' |
stable |
Release channel: polished vs. frequent |
disableAutoUpdates |
boolean | false |
If true, never check for app updates |
autoUpdatePlugins |
boolean | string | true |
Auto-update plugins every 5 h (or a custom interval like 1d, 2h) |
defaultSSHApp |
boolean | true |
Register Hyper as the default ssh:// protocol client |
useConpty |
boolean | (unset) | Force ConPTY on/off on Windows; if unset, node-pty decides |
4.4 Profile (terminal) config options
These can be set at the root and overridden per profile (see
Profiles). Defaults below come from config-default.json.
| Key | Type | Default | Description |
|---|---|---|---|
fontSize |
number | 12 |
Font size in px |
fontFamily |
string | Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace |
Font stack |
fontWeight |
FontWeight | normal |
Normal-text weight (100–900, normal, bold) |
fontWeightBold |
FontWeight | bold |
Bold-text weight |
lineHeight |
number | 1 |
Relative line height |
letterSpacing |
number | 0 |
Relative letter spacing |
uiFontFamily |
string | (unset) | Font for Hyper's own UI chrome |
cursorColor |
string | rgba(248,28,229,0.8) |
Cursor color/opacity |
cursorAccentColor |
string | #000 |
Text color under a BLOCK cursor |
cursorShape |
'BEAM'|'UNDERLINE'|'BLOCK' |
BLOCK |
Cursor shape |
cursorBlink |
boolean | false |
Blinking cursor |
foregroundColor |
string | #fff |
Default text color |
backgroundColor |
string | #000 |
Terminal background (opacity macOS-only) |
selectionColor |
string | rgba(248,28,229,0.3) |
Selection highlight |
borderColor |
string | #333 |
Window/tab border |
colors |
ColorMap | 16-color palette | ANSI 16-color map (see default in User Guide) |
css |
string | "" |
Custom CSS for the main window |
termCSS |
string | "" |
Custom CSS for the terminal area |
padding |
string | 12px 14px |
CSS padding around the terminal |
workingDirectory |
string | "" |
Absolute startup directory |
showHamburgerMenu |
boolean | '' |
"" |
Show hamburger menu (Win/Linux); ''→default true |
showWindowControls |
boolean | 'left' | '' |
"" |
Window controls visibility/side |
scrollback |
number | 1000 |
Lines of scrollback buffer |
shell |
string | "" |
Shell binary path (empty = login shell) |
shellArgs |
string[] | ["--login"] |
Shell arguments |
env |
object | {} |
Extra environment variables |
bell |
'SOUND' | false |
SOUND |
Audible bell or off |
bellSound |
string | null | null |
Base64-encoded bell sound |
bellSoundURL |
string | null | null |
Absolute path to a bell sound file |
copyOnSelect |
boolean | false |
Copy selection to clipboard automatically |
defaultSSHApp |
boolean | true |
(root) see above |
quickEdit |
boolean | false |
Right-click copy/paste (default true on Windows; disables context menu) |
macOptionSelectionMode |
string | vertical |
vertical column mode or force |
webGLRenderer |
boolean | false |
WebGL vs. canvas (canvas supports transparency) |
webLinksActivationKey |
'ctrl'|'alt'|'meta'|'shift'|'' |
"" |
Modifier required to click links |
disableLigatures |
boolean | true |
false enables font ligatures |
screenReaderMode |
boolean | false |
Enable screen-reader access |
imageSupport |
boolean | true |
Sixel + iTerm2 inline image protocol |
preserveCWD |
boolean | true |
New tabs/splits inherit the active pane's CWD |
modifierKeys |
object | (unset) | { altIsMeta, cmdIsMeta } |
windowSize |
[number, number] |
(unset) | Initial window size in px |
Important
Negative knowledge (prevents fabrication): Hyper does not read any system-wide config file, has no HTTP/REST API, no webhooks, and accepts no behavior-changing command-line arguments (
hyper --help/--versiononly print diagnostics). All configuration flows throughhyper.json. There is no telemetry endpoint; the only outbound calls are plugin-registry queries, the update feed, and the remote "news" notification fetch.
4.5 Profiles
defaultProfile (string) names the profile used for new sessions; profiles is an
array of { name, config } where config is a partial set of the profile options
above. app/config.ts:getProfileConfig() merges a profile's overrides onto the
base config (objects are shallow-merged; scalars replaced). If defaultProfile
doesn't match any profile, the first profile's name is used; an empty profile list
is normalized to a single default profile.
4.6 Keymaps
keymaps maps a command name to one shortcut (string) or several (array). User
keymaps are merged over the platform defaults. app/utils/map-keys.ts normalizes
every binding to an array, rewrites deprecated cmd → command, and expands any
command ending in :prefix into nine generated bindings (…:1……:8, …:last)
by appending digits 1–9 to the prefix shortcut. The complete default keymaps
for each platform are listed in the User Guide; the full command catalog is in
Command Reference.
4.7 Window state persistence
Window position and size are persisted separately from hyper.json via
electron-store (app/config/windows.ts), defaulting to position [50, 50] and
size [540, 380]. They are recorded on window close and restored on next launch
(validated against connected displays in app/utils/window-utils.ts).
5. Plugin Extension API
Plugins are npm packages (or local folders) that export one or more recognized
extension methods. The complete set of recognized methods is defined in
app/plugins/extensions.ts as availableExtensions. A package that exports none
of these is rejected with "does not expose any Hyper extension API methods."
5.1 Extension method catalog
| Category | Methods |
|---|---|
| Main-process lifecycle | onApp, onWindow, onWindowClass, onUnload, onRendererWindow |
| Config / environment | decorateConfig, decorateKeymaps, decorateEnv, decorateMenu, decorateBrowserOptions |
| Session / window classes | decorateSessionClass, decorateSessionOptions, decorateWindowClass |
| Redux store | middleware, reduceUI, reduceSessions, reduceTermGroups |
| React component HOCs | decorateTerm, decorateHyper, decorateHyperTerm (compat), decorateHeader, decorateTerms, decorateTab, decorateTabs, decorateNotification, decorateNotifications, decorateTermGroup, decorateSplitPane |
| Component prop getters | getTermProps, getTabProps, getTabsProps, getTermGroupProps |
| State / dispatch mappers | mapHyperTermState, mapTermsState, mapHeaderState, mapNotificationsState, mapHyperTermDispatch, mapTermsDispatch, mapHeaderDispatch, mapNotificationsDispatch |
5.2 How decoration works
app/plugins.ts loads each plugin module, records its _name and _version, and
chains decorators: for a given hook key, every plugin that exports it is called in
order, each receiving the previous result. Object-returning hooks (decorateConfig,
decorateMenu, decorateEnv, etc.) must return an object; class hooks
(decorateSessionClass) must return a function/class. Invalid returns produce a
"invalid return type" notification and are skipped. The decorated results feed
the IPC handles getDecoratedConfig / getDecoratedKeymaps and the window/session
constructors.
For backward compatibility, the main process patches Node's Module._load so
plugins can require('react'), require('react-dom'), require('hyper/component'),
require('hyper/decorate'), etc. (these specific requires are deprecated).
5.3 Plugin installation
plugins entries are synced into an auto-generated package.json inside the
plugin base directory and installed with a bundled standalone Yarn
(bin/yarn-standalone.js) run through Electron-as-Node
(ELECTRON_RUN_AS_NODE=true), serialized through a single-concurrency queue with a
5-minute timeout (app/plugins/install.ts). Installation is triggered on launch
(when the plugin set changed), on config change, on the periodic
autoUpdatePlugins interval, and on demand via the plugins:update command.
5.4 Plugin directories
app/config/paths.ts defines (relative to the config dir):
| Path | Purpose |
|---|---|
plugins/ |
Base: auto-generated package.json + npm-installed node_modules |
plugins/local/ |
Local (unpublished) plugins listed in localPlugins |
plugins/cache/ |
Yarn cache folder used during install |
Tip
Plugin development workflow (
PLUGINS.md): run Hyper in dev mode on thecanarybranch, copy yourhyper.jsonto the repo root, drop or symlink your plugin intoplugins/local/, and add its folder name tolocalPlugins. A successful load logsPlugin <name> (<version>) loaded.Renderer-sideconsole.logappears in the Electron DevTools; main-process logging appears in the launching terminal.
6. Operations
6.1 Running and lifecycle
The main process (app/index.ts) initializes config, enables @electron/remote,
and on the ready event creates the first window. It installs React/Redux DevTools
in dev mode, wires the activate/window-all-closed/open-file/open-url
handlers, builds the native menu, loads plugins (onApp), and (in production)
registers/deregisters the ssh:// protocol client and installs the CLI.
| OS event | Behavior |
|---|---|
activate (macOS dock click, no windows) |
Create a new window |
window-all-closed |
Quit, except on macOS |
open-file |
Send the path to a window (open file) |
open-url |
Parse an ssh:// URL and open a session (open ssh) |
6.2 Auto-update
app/updater.ts builds an update feed URL and polls it 10 s after launch and every
30 minutes thereafter (unless disableAutoUpdates is set):
https://{releases|releases-canary}.hyper.is/update/{platform}{_arm64?}/{version}
releases-canaryis used whenupdateChanneliscanary; switching channels at runtime re-points the feed and re-checks._arm64is appended on Apple Silicon / ARM64 (including Rosetta-translated runs).- On Linux, the platform segment is
deband a customAutoUpdater(app/auto-updater-linux.ts) fetches release metadata over HTTP. Linux cannot install in place (quitAndInstallis unimplemented), socanInstallisfalseand users are only notified. - On macOS/Windows, Electron's native
autoUpdaterdownloads and installs; the renderer'squit and installRPC triggersquitAndInstall().
6.3 Remote notifications
app/notifications.ts fetches https://hyper-news.now.sh on launch and every
30 minutes, sending X-Hyper-Version and X-Hyper-Platform headers. A returned
message is surfaced in the UI; malformed responses raise Bad response.
6.4 CLI installation into PATH
app/utils/cli-install.ts installs the hyper command on first run:
| Platform | Mechanism |
|---|---|
| macOS / Linux | Symlink bin/hyper → /usr/local/bin/hyper; on EACCES, offers an elevated sudo ln -sf … via sudo-prompt or copies the command to the clipboard |
| Linux AppImage | Skipped (AppImage mounts at a temporary path) |
| Windows | Adds the bin directory to the user PATH via the HKCU\Environment registry key, removing stale Squirrel-era paths |
6.5 Windows system context menu
app/utils/system-context-menu.ts adds/removes "Open Hyper here" entries under
HKCU\Software\Classes\…\shell\Hyper for directory background, directory, and
drive, via Tools → Add/Remove Hyper to system context menu.
6.6 Logs and diagnostics
- Open DevTools:
Cmd+Alt+I(macOS) /Ctrl+Shift+I(Win/Linux), or View → Developer Tools. - Enable verbose Electron logging by launching via the CLI with
--verbose(setsELECTRON_ENABLE_LOGGING=1). - Help → Report Issue generates a GitHub issue pre-filled with version, OS,
arch, Electron version,
LANG,SHELL,TERM, and the contents ofhyper.jsonand the plugin list (large payloads are copied to the clipboard instead).
7. Security Model
Caution
Hyper's renderer runs with
nodeIntegration: true,contextIsolation: false, and@electron/remoteenabled (app/ui/window.ts). This is an intentional trade-off that grants renderer code — including every installed plugin — full Node.js and OS access. It is not the hardened contextBridge model recommended for Electron apps that load untrusted remote content.
7.1 Trust boundary
Hyper loads only local content (file://…/index.html) — it never navigates to
remote web pages — so the classic remote-code-execution risk of nodeIntegration
is mitigated by what is loaded. The real trust surface is plugins: a plugin
is arbitrary npm code that executes with full privileges in both processes.
Administrators deploying Hyper in sensitive environments should treat the
plugins/localPlugins lists as a code-allowlist and review plugins as
dependencies.
7.2 Navigation and external content
app/ui/window.ts constrains navigation:
will-navigateandsetWindowOpenHandlerintercept drag-dropped/clicked URLs:file://paths are inserted as escaped shell input;http(s)://URLs are passed through; other navigations are denied. This prevents the terminal window from navigating away from the app.open externalroutes URLs throughshell.openExternal(the OS browser), not an in-app webview.
7.3 Shell input escaping
When the renderer sends data with escaped: true (e.g. dropped file paths), the
main process quotes it for the target shell — wrapping in double quotes for
cmd.exe, single-quotes (with '\\'' escaping) otherwise (app/ui/window.ts).
7.4 Environment hygiene
app/session.ts strips Electron's injected GOOGLE_API_KEY from the shell
environment (so it isn't leaked to child processes) and, on AppImage installs,
fixes PATH so the bundled CLI isn't shadowed by the AppImage mount point.
7.5 Packaging hardening (macOS)
The macOS build is notarized (afterSign: ./bin/notarize.js) and ships an
entitlements file (build/mac/entitlements.plist). The app declares a full set of
NS…UsageDescription strings because plugins may invoke OS capabilities (AppleScript,
camera, contacts, etc.). Linux snap uses classic confinement.
8. Build & Distribution
8.1 Build pipeline
| Script | Action |
|---|---|
pnpm run dev |
Concurrent webpack -w (renderer) + tsc --build --watch (types/main) |
pnpm run app |
Launch the built app via electronmon (auto-restart on change) |
pnpm run build |
Production webpack + tsc -b + Babel-minify the renderer bundle |
pnpm run dist |
build then electron-builder (produces installers) |
pnpm run rebuild-node-pty |
Rebuild the native node-pty for Electron |
pnpm run v8-snapshot |
Build V8 startup snapshots (x64 + arm64) for faster boot |
pnpm run generate-schema |
Regenerate app/config/schema.json from typings/config.d.ts |
pnpm run lint / test:unit / test:e2e |
ESLint / AVA unit tests / AVA + Playwright e2e |
The renderer is bundled by webpack.config.ts (entry lib/index.tsx); the main
process is type-checked and emitted by the TypeScript project references. Babel
(with @babel/preset-react + @babel/preset-typescript) handles the renderer's
JSX/TS. tsconfig.base.json targets ES2022, strict: true, jsx: react,
CommonJS modules.
8.2 Distribution matrix (electron-builder.json)
| Platform | Targets | Arch | Signing / notes |
|---|---|---|---|
| macOS | default (dmg + zip) |
x64, arm64 |
Notarized (afterSign), entitlements, dark-mode support, registers folder & Unix-executable document types |
| Windows | nsis |
x64, arm64 |
RFC-3161 timestamp; installer is non-one-click, per-user, allows changing the install dir; custom installer.nsh |
| Linux | deb, AppImage, rpm, snap, pacman |
— | Category TerminalEmulator; deb uses bzip2 + after-install hook; snap uses classic confinement (published to GitHub) |
Other packaging facts:
appId:co.zeit.hyper; artifact name${productName}-${version}-${arch}.${ext}.- The packaged app directory is
target/; extra resources includebin/yarn-standalone.js,bin/cli.js, and the per-OShyper*CLI binaries. afterPackcopies the V8 snapshot (bin/cp-snapshot.js).- A dedicated
electron-builder-linux-ci.jsonextends the base config but disablesafterSignandnpmRebuildfor CI Linux builds. - The
sshURL scheme is registered as an app protocol (protocols). release.jsis anelectron-builderrelease-notes hook that prompts for a one-line summary and prepends it to the generated changelog.
9. Command Reference
Commands are named actions bound to keyboard shortcuts and menu items. Main-process
commands are defined in app/commands.ts (each emits an RPC event or performs an
OS action); some are handled in the renderer (lib/command-registry.ts).
9.1 Window & application
| Command | Action |
|---|---|
window:new |
Create a new window |
window:minimize / window:zoom / window:close |
Window state (Electron roles) |
window:toggleFullScreen |
Toggle full screen |
window:reload |
Hot-reload the renderer/plugins (RPC reload) |
window:reloadFull |
Full webContents.reload() |
window:devtools |
Toggle detached DevTools |
window:preferences |
Open hyper.json |
window:hamburgerMenu |
Pop up the app menu (Win/Linux) |
window:toggleKeepOnTop |
Toggle always-on-top |
9.2 Tabs & panes
| Command | Action |
|---|---|
tab:new |
New tab (or new window if none focused) |
tab:next / tab:prev |
Cycle tabs |
tab:jump:1…8, tab:jump:last |
Jump to a tab (generated from tab:jump:prefix) |
pane:splitRight / pane:splitDown |
Split vertically / horizontally |
pane:close |
Close the active pane |
pane:next / pane:prev |
Cycle pane focus |
9.3 Editor (terminal input)
| Command | Action / sequence |
|---|---|
editor:clearBuffer |
Clear the scrollback |
editor:selectAll |
Select all text |
editor:search / editor:search-close |
Open / close the find bar |
editor:movePreviousWord / moveNextWord |
ESC b / ESC f |
editor:moveBeginningLine / moveEndLine |
ESC O H / ESC O F |
editor:deletePreviousWord / deleteNextWord |
ESC DEL / ESC d |
editor:deleteBeginningLine / deleteEndLine |
ESC w / \x10B |
editor:break / editor:stop / editor:quit |
Ctrl+C / Ctrl+Z / Ctrl+\ |
editor:tmux |
tmux prefix Ctrl+B |
editor:undo/redo/cut/copy/paste |
Native Electron roles |
9.4 Zoom, plugins, CLI, OS
| Command | Action |
|---|---|
zoom:in / zoom:out / zoom:reset |
Adjust renderer font size |
plugins:update |
Force a plugin update |
cli:install |
Install the hyper CLI into PATH |
systemContextMenu:add / remove |
Windows Explorer context-menu entries |
window:new:<profile>, tab:new:<profile>, pane:splitRight:<profile>, pane:splitDown:<profile> |
Per-profile variants (generated per profile) |
Note
Some commands (
window:close,editor:undo/redo/cut/copy/paste/selectAll,window:minimize/zoom/toggleFullScreen) are executed directly by Electron menu roles.lib/command-registry.tsmarks these asroleCommandsand does not prevent their default, so the OS handles them.
9.5 Native menu structure
app/menus/menu.ts assembles the menu from per-section builders, injecting the
first shortcut for each command as the accelerator:
| Menu | Items |
|---|---|
| Hyper (macOS only) | About, Preferences…, services, hide/quit |
| Shell (macOS) / File (Win/Linux) | New Tab, New Window, Split Down/Right, per-profile submenus, Close, Close Window/Quit |
| Edit | Undo/Redo/Cut/Copy/Paste, Select All, Move to…, Delete…, Clear Buffer, Search (+ Preferences… off-mac) |
| View | Reload, Full Reload, Developer Tools, Zoom Reset/In/Out |
| Tools | Update plugins, Install Hyper CLI in PATH, (Windows) context-menu add/remove |
| Window | Minimize, Zoom, Select Tab (incl. tab-jump 1–9), Select Pane, Front, Toggle Always on Top, Full Screen |
| Help | Hyper Website, Report Issue, (off-mac) About |
The same Edit + Shell builders also produce the in-terminal right-click context
menu (app/ui/contextmenu.ts), filtering Cut/Copy to appear only when there's a
selection.
10. Dependency Reference
10.1 Renderer / shared (root package.json)
| Dependency | Version | Role |
|---|---|---|
react, react-dom |
18.2.0 | UI framework |
redux, react-redux, redux-thunk, reselect |
4.2.1 / 8.1.3 / 2.4.2 / 4.1.8 | State management |
seamless-immutable |
7.1.4 | Immutable term-group state |
xterm + addons |
5.3.0 | Terminal engine (canvas, fit, image, ligatures, search, unicode11, web-links, webgl) |
@electron/remote |
2.1.2 | Renderer→main object bridge |
mousetrap |
(fork) | Keyboard shortcut handling |
styled-jsx, stylis, clsx, color |
— | Styling |
args, chalk, columnify, got, open, ora |
— | CLI tooling |
10.2 Main process (app/package.json)
| Dependency | Version | Role |
|---|---|---|
node-pty |
1.0.0 | Pseudo-terminal spawning |
electron-store |
8.2.0 | Window-state persistence |
chokidar |
^3.6.0 | Config file watching |
default-shell, shell-env, os-locale |
— | Shell/env resolution |
electron-is-dev, electron-fetch, git-describe |
— | Runtime helpers |
recast, @babel/parser, ast-types |
— | Hyper 3 → 4 config migration (AST transform) |
native-process-working-directory |
^1.0.2 | CWD inheritance for splits |
native-reg (optional), sudo-prompt |
— | Windows registry / elevated CLI install |
async-retry, queue, fs-extra, ms, uuid, semver |
— | Utilities |
10.3 Build toolchain (devDependencies)
Electron 22.3.25, electron-builder, electron-rebuild, electron-mksnapshot, electron-link, electronmon, webpack 5.91 + webpack-cli, Babel 7.24, TypeScript 5.4.5, ESLint 8.57 (+ TypeScript/React/import/prettier plugins), AVA 6.1 + Playwright 1.43 (tests), Husky, Prettier, Terser.
11. Code Quality & Patterns
- Strict TypeScript end to end (
strict: true, ES2022). Shared contracts intypings/keep the IPC event payloads, config shape, and Redux actions type-safe across the process boundary (MainEvents/RendererEventstyped emitters inrpc.ts). - Redux with thunks + effects. Action creators return objects carrying an
effect()callback executed by custom store middleware (lib/store/), used to bridge dispatched actions to RPC emits — a clean separation of state change from side effect. - Immutable term-group tree. Tabs/panes form a tree of term-groups kept in
seamless-immutablestate; pane navigation and closing recompute the active session by walking the tree (lib/actions/term-groups.ts). - Decorator chains for extensibility. Every customizable surface — config,
keymaps, menus, env, sessions, windows, React components — runs through the same
decorateEntitychain, giving plugins uniform, composable hooks. - Performance. PTY output batching (16 ms / 200 KB), V8 startup snapshots, and
an imperative
termsregistry for hot-path term access (lib/terms.ts) target responsiveness under heavy output.
Known tech-debt signals (from source)
- Several plugin
requirealiases (react,react-dom,hyper/component,hyper/decorate) are explicitly marked DEPRECATED but retained for compatibility (app/plugins.ts). - The
extendKeymapsplugin hook is deprecated; plugins using it are warned at load time. - A
webLinksActivationKeyTODOnotes that link-activation changes don't apply without restarting the terminal (typings/config.d.ts). - Pre-
xterm.jsCSS selectors are detected and flagged as deprecated rather than removed (app/config.ts:getDeprecatedCSS).
12. Potential Improvements
These are observations for maintainers and downstream packagers, grounded in the current source:
- [ ] Revisit the renderer security posture. With
nodeIntegration: true/contextIsolation: false, consider whether a contextBridge-based plugin API could preserve extensibility while shrinking the renderer's privilege — at minimum, document the plugin trust model prominently for enterprise deployers. - [ ] Plugin provenance. There is no signature/integrity check on installed plugins beyond npm resolution; an allowlist or lockfile-pinning story would help managed environments.
- [ ] Linux in-place updates.
auto-updater-linuxonly notifies; a packaged updater (or clearer per-format guidance) would close the gap with macOS/Windows. - [ ] Deprecation cleanup. The deprecated
requirealiases andextendKeymapshook could be removed in a future major, with a migration note for plugin authors.
13. Quick Reference
| Item | Value |
|---|---|
| Config file | hyper.json |
| Config dir (mac/Linux) | ~/.config/Hyper/ |
| Config dir (Windows) | Electron userData dir |
| Config dir (XDG) | $XDG_CONFIG_HOME/Hyper/ |
| Plugin dirs | plugins/, plugins/local/, plugins/cache/ |
| Default shell args | ["--login"] |
| Default window size | 540 × 380 (min 370 × 190) |
| Scrollback default | 1000 lines |
| PTY batch flush | 16 ms or 200 KB |
| Update feed | https://releases[-canary].hyper.is/update/{platform}{_arm64}/{version} |
| App ID | co.zeit.hyper |
| CLI binary | hyper (symlink /usr/local/bin/hyper on Unix; PATH entry on Windows) |
| Open config | Cmd+, / Ctrl+, |
| DevTools | Cmd+Alt+I / Ctrl+Shift+I |
| Plugin update | Cmd+Shift+U / Ctrl+Shift+U |
| Build installers | pnpm run dist |
| Regenerate schema | pnpm run generate-schema |
| Related repos | hyper-site, hyperpower (sample ext), hyperyellow (sample theme), Awesome Hyper |