Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

emaxe/eTty

Repository files navigation

eTty

πŸ‡·πŸ‡Ί Русский

Modern terminal emulator built with Electron. Lightweight, fast, and feature-rich.

eTty icon

Features

Terminal

  • Multiple tabs with independent zsh sessions
  • WebGL-accelerated rendering (canvas fallback)
  • Kitty keyboard protocol β€” Shift+Enter, Ctrl+Enter, Ctrl+Shift+Enter sequences
  • Full Unicode support β€” Cyrillic, accented characters, emoji
  • Shell directory tracking via OSC 7 β€” file tree syncs automatically
  • Command busy indicator via OSC 133 β€” navigation buttons reflect shell state
  • Tab drag-and-drop β€” reorder tabs by dragging the grip handle
  • Configurable new tab placement β€” choose whether new tabs open adjacent to the active tab or at the end; hold Cmd/Ctrl while clicking "+" to toggle the behavior (configurable in Settings)
  • Auto-open tab for busy terminal β€” triggering a run-script or cd from the file tree while the active tab is occupied by an agent automatically opens a new tab for the command
  • 2,500 lines scrollback buffer (performance-optimized)
  • Scroll-to-bottom button β€” floating button appears when you scroll up into history; click to jump back to the latest output

Command History

  • Shared global history (5,000 lines) across all tabs
  • Per-tab history files β€” each tab gets its own HISTFILE
  • Smart merging β€” new commands merge back to global on tab close
  • Session restore β€” history survives app restarts
  • Automatic cleanup of orphaned history files

File Tree (Sidebar)

  • Lazy-loaded directory tree with expand/collapse
  • Hidden files toggle
  • Quick navigation β€” cd .. and cd ~ buttons
  • Live sync β€” filesystem watcher with 500ms debounce
  • Context menus β€” new file/folder, rename, delete, copy, paste, open external, copy relative path
  • Hover overlay β€” quick-action buttons appear on hover: cd into directory, copy path, and run script (β–Ά) to execute the file in the active terminal
  • Multi-select β€” Ctrl/Cmd+Click, Shift+Click range, Ctrl+A select all
  • Bulk operations β€” copy (Ctrl+C / Cmd+C), paste (Ctrl+V / Cmd+V), and delete (Delete) on the current selection
  • Drag-and-drop β€” move files/folders within the tree
  • Undo β€” Ctrl+Z to undo last move operation
  • Binary file handling β€” images, videos, executables (.dmg, .exe, etc.) open with the OS default application instead of the code editor
  • Resizable sidebar (150–600px)
  • Path traversal protection β€” all paths validated against CWD

Code Editor

  • CodeMirror 6 with syntax highlighting for 20+ languages: JavaScript/TypeScript, Python, Go, Rust, HTML, CSS/SCSS, JSON, YAML, Markdown, Vue, C#
  • Send to terminal β€” select code and send it to the active shell (Cmd+Enter)
  • Unsaved changes indicator
  • Auto-features β€” bracket closing, fold gutter, active line highlight, search
  • Resizable editor panel

Git Integration

  • Status bar showing Β± +N -N diff stats, updated every 5 seconds
    • Click on stats to toggle Git panel
  • Branch management β€” switch, create, delete branches
  • Inline diff viewer with colored additions/deletions
  • Commit, push, discard β€” all from the GUI
  • Per-file stats β€” additions and deletions count for each changed file

Git Diff Highlighting

  • File tree indicators β€” new files (green), modified (blue), deleted (red + strikethrough)
  • Folder dot markers β€” nested changes bubble up to parent directories (deleted > modified > new)
  • Editor gutter bars β€” 3px color strips left of line numbers for added (green) and modified (blue) lines
  • Auto-refresh β€” polling every 5 seconds + immediate update on file save (Cmd+S)
  • Repository-aware β€” only active when the working directory is inside a git repo

Project Search

  • Modal search dialog β€” Cmd+F (macOS) / Ctrl+F (Windows/Linux), or double-tap Shift
  • File names + contents β€” searches both simultaneously; file-name matches shown first
  • Search options β€” case sensitive, whole word, regex toggles; include/exclude glob patterns
  • Live preview β€” click a result to preview file contents with highlighted matches
  • Quick open β€” double-click or Enter opens the file in the editor and closes the dialog
  • Cancellable β€” long searches abort automatically when the query changes

AI Agents

  • Status-bar launcher β€” one-click launch of detected CLI agents
  • Auto-detection β€” Claude, Codex, Copilot, Cursor Agent, OpenCode, Qwen, Agento
  • Busy lock β€” agent buttons disabled while terminal is busy
  • Active agent highlight β€” launched agent button glows while running
  • Force-disable β€” toggle agents on/off in Settings
  • Proxy support β€” configure HTTP proxy for all agent commands
  • Customizable quick replies β€” define agent-specific quick commands (e.g., /clear, Ok, ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°ΠΉ) via Settings; drag-and-drop to reorder
  • Double-click to select active agent β€” when the terminal is busy but the active agent wasn't auto-detected (e.g., launched manually or via another app), double-click any agent button in the status bar to manually assign it as active. This enables that agent's quick-reply buttons.
  • Per-agent Shift+Enter mode β€” configure how Shift+Enter behaves for each AI agent (Kitty protocol, newline, or Ctrl+J). Qwen and Agento default to Ctrl+J mode; others default to Kitty protocol.

Settings

  • 7 built-in themes: Catppuccin Mocha (default), Monokai, Dracula, One Dark, Nord, Solarized Dark, Gruvbox Dark
  • Focus indicator β€” glow, border, top-line, or none
  • File tree behavior β€” collapse children on close, single/double-click to open
  • Prompt style β€” default (from ~/.zshrc), short, minimal, arrow (for new tabs)
  • Status bar size β€” compact, standard, or large; adjustable in Settings β†’ Appearance
  • New tab placement β€” modifierAdjacent (Cmd/Ctrl opens next to active tab) or modifierEnd (Cmd/Ctrl appends to end); configurable in Settings β†’ Terminal
  • AI agent quick replies β€” CRUD editor with per-agent toggles; drag-and-drop reordering
  • Auto-persisted to disk with debounced saves

Session Persistence

  • Tab state saved on quit, restored on next launch (with confirmation dialog)
  • Per-tab tree state β€” expanded directories and scroll position remembered when switching tabs
  • Menu option to manually restore tabs

Keyboard Shortcuts

Shortcut Action
Cmd+F / Ctrl+F Open project search dialog
Cmd+E / Ctrl+E Toggle editor panel
Cmd+S / Ctrl+S Save file in editor
Cmd+Enter Send selection from editor to terminal
Shift+Enter Kitty protocol: \x1b[13;2u
Ctrl+Enter Kitty protocol: \x1b[13;5u
Ctrl+Shift+Enter Kitty protocol: \x1b[13;6u
Cmd+ArrowLeft (macOS) Home β€” \x1b[H
Cmd+ArrowRight (macOS) End β€” \x1b[F

Tech Stack

Component Technology
Desktop shell Electron 33
Build tooling electron-vite (Vite-based)
Terminal UI xterm.js 5.5 + WebGL, Fit, WebLinks, Search addons
PTY backend node-pty 1.0 (native module)
Code editor CodeMirror 6 with 13 language packages (JS/TS, Python, Go, Rust, HTML, CSS, JSON, YAML, Markdown, Vue, C#)
Git operations simple-git
UI font Roboto (global application font)
Packaging electron-builder
Logging electron-log
Auto-update electron-updater (stub)

Getting Started

Prerequisites

  • Node.js 18+
  • macOS: Xcode Command Line Tools β€” xcode-select --install

Development

npm install
npm run dev

Building

# 1. Compile with electron-vite
npm run build
# 2. Package distributable
npm run dist # macOS .dmg (arm64 + x64)
npm run dist:win # Windows NSIS installer
npm run dist:linux # Linux AppImage + .deb

Output goes to dist/.

Code Signing (macOS)

Without certificates the app is signed ad-hoc β€” fine for local testing. For a signed and notarized release:

export CSC_LINK=/path/to/certificate.p12
export CSC_KEY_PASSWORD=...
export APPLE_ID=you@example.com
export APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx
export APPLE_TEAM_ID=XXXXXXXXXX
npm run build && npm run dist

Verify Signing

codesign -dv --verbose=4 dist/mac-arm64/eTty.app
spctl -a -vvv -t install dist/mac-arm64/eTty.app

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Main Process β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚PtyManagerβ”‚ β”‚FileManagerβ”‚ β”‚ HistoryManager β”‚ β”‚
β”‚ β”‚ node-pty β”‚ β”‚ fs ops β”‚ β”‚ global + per-tab β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚GitServiceβ”‚ β”‚ TabState β”‚ β”‚SettingsStoreβ”‚ β”‚AgentServiceβ”‚ β”‚
β”‚ β”‚ countDiffβ”‚ β”‚ persist β”‚ β”‚ JSON config β”‚ β”‚ detect CLI β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ AppService β€” window, menu, auto-updater, state save on quit β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚pty-handlβ”‚ β”‚fs-handl β”‚ β”‚git-handlβ”‚ β”‚tabs-handβ”‚ β”‚settings β”‚ ... β”‚
β”‚ β”‚ ers β”‚ β”‚ ers β”‚ β”‚ ers β”‚ β”‚ ers β”‚ β”‚-handlersβ”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚ IPC (~50 channels, constants in shared/)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Preload (contextBridge) β”‚
β”‚ ~50 methods on window.electronAPI β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Renderer Process β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Core Infrastructure β”‚ β”‚
β”‚ β”‚ EventBus β”‚ StateStore β”‚ DI Container β”‚ ElectronApiAdapter β”‚ β”‚
β”‚ β”‚ GitStatusService (polling) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚Terminalβ”‚ β”‚FileTreeβ”‚ β”‚EditorPanelβ”‚ β”‚ GitPanel β”‚ β”‚SettingsPage β”‚ β”‚
β”‚ β”‚xterm.jsβ”‚ β”‚ lazy β”‚ β”‚CodeMirror β”‚ β”‚ diff β”‚ β”‚ overlay β”‚ β”‚
β”‚ β”‚ tabs β”‚ β”‚ DnD β”‚ β”‚ 20+ langs β”‚ β”‚ branches β”‚ β”‚ themes β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ TabBar β”‚ β”‚ ProjectSearch β”‚ β”‚ StatusBar β”‚ β”‚
β”‚ β”‚reorder β”‚ β”‚ dialog β”‚ β”‚ git Β± β”‚ cwd β”‚ AI agents β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Architecture Patterns

The codebase follows a layered architecture after a 4-block refactoring:

  • DI Container (renderer/core/container.js) β€” components receive dependencies via constructor injection, not global variables
  • EventBus (renderer/core/event-bus.js) β€” pub/sub communication between decoupled components
  • StateStore (renderer/core/state-store.js) β€” centralized reactive state with subscriptions (ui.theme, ui.sidebarVisible, etc.)
  • IPC_CHANNEL constants (shared/ipc-channels.js) β€” single source of truth for all IPC channel names, no string literals
  • IPC handler modules (main/ipc-handlers/*.js) β€” each domain group in its own file with register*Handlers(ipcMain, deps) signature
  • AppService (main/services/app-service.js) β€” application lifecycle in main process (window, menu, updater, state save)

Data Storage

All user data is stored in ~/.config/eTty/ (Electron userData):

File Purpose
settings.json App settings (theme, focus indicator, file tree behavior, prompt style, agent proxy, force-disabled agents, quick replies)
tabs-state.json Saved tab state for session restore
history/global.zsh_history Shared command history (5000 lines)
history/tabs/<id>.zsh_history Per-tab command history

Notes

  • node-pty is a native module. electron-builder rebuilds it automatically via npmRebuild: true.
  • build/entitlements.mac.plist contains macOS entitlements required for node-pty under hardened runtime (JIT, unsigned memory, library validation).
  • Auto-update is stubbed β€” logs only, no update server configured yet.
  • Settings overlay blocks tab switching to prevent focus leaking to hidden terminals.

License

Private

About

one more terminal app!

Resources

Stars

Watchers

Forks

Packages

Contributors

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /