Horizon is a GPU-accelerated terminal board that puts all your sessions
on an infinite canvas. Organize by workspace, launch from presets, quick-nav fast, and never lose a terminal again.
Tabbed terminals hide your work. Tiled terminals box you in. Horizon gives you a canvas — an infinite 2D surface where every terminal lives as a panel you can place, resize, and group however you want.
Think of it as a whiteboard for your terminal sessions with a structured workflow on top. Start with color-coded workspaces, launch preset panels, jump with Quick Nav, and fit the active workspace whenever you want a clean overview.
|
Pan and zoom freely across a boundless workspace surface. Place terminals anywhere. A minimap in the corner keeps you oriented — click it to jump. |
Group related panels into color-coded workspaces. Auto-arrange with three layout modes — rows, columns, grid — or drag panels freely. |
|
24-bit color, mouse reporting, scrollback, alt-screen, and Kitty keyboard protocol. Powered by the Alacritty terminal engine — the same one behind the fastest terminal on the planet. |
First-class Claude Code, Codex, OpenCode, Gemini CLI, and KiloCode integration. Session persistence and resume work where the underlying agent CLI supports it. A live usage dashboard tracks token spend across agents. |
|
A built-in git status panel watches your repo in the background. See changed files, inline diffs, and hunk-level detail — no context switching. |
Ctrl+click a URL to open it. Hover a file path and click to jump to it. Horizon sees what your terminal prints and makes it interactive. |
|
Ctrl+Shift+H opens a fast overlay that discovers hosts from SSH config and Tailscale. Search, filter, and connect instantly. Type user@filter to override the SSH user. Connected sessions are grouped into a Remote Sessions grid workspace. |
Open the config with Ctrl+Shift+, — a side panel with YAML syntax highlighting and live preview. Toggle between light and dark themes or follow the system; every change applies instantly to the canvas behind it. |
|
Close Horizon, come back tomorrow. Your workspaces, panel positions, scroll positions, and terminal history are restored exactly as you left them. |
Drop a |
Grab the latest release from Releases — no dependencies needed.
| Platform | Raw binary | Surge installer | |
|---|---|---|---|
| Linux x64 | horizon-linux-x64.tar.gz |
horizon-installer-linux-x64.bin |
Extract and run, or use the installer for managed stable updates |
| macOS arm64 | horizon-osx-arm64.tar.gz |
horizon-installer-osx-arm64.bin |
Extract and run, or use the installer for managed stable updates |
| macOS x64 | horizon-osx-x64.tar.gz |
horizon-installer-osx-x64.bin |
Extract and run, or use the installer for managed stable updates |
| Windows x64 | horizon-windows-x64.exe |
horizon-installer-win-x64.exe |
Run the raw binary directly, or use the installer for managed stable updates |
Homebrew and other package-manager installs keep using the package manager's own upgrade flow. Horizon only offers the in-app update prompt for installs created by the Surge installer.
Stable releases are available through the peters/horizon tap on macOS and Linux x64:
brew install peters/horizon/horizonIf you prefer to add the tap explicitly first:
brew tap peters/horizon
brew install horizonTo update or remove it later:
brew upgrade horizon
brew uninstall horizon
brew untap peters/horizonStable releases are submitted to the official Windows Package Manager catalog. After a release's manifest PR is approved, install, upgrade, or remove Horizon with:
winget install Peters.Horizon
winget upgrade Peters.Horizon
winget uninstall Peters.HorizonStable releases are also published to the Snap Store on Linux x64 as a classic snap:
sudo snap install horizon-ui --classic
snap refresh horizon-ui
snap remove horizon-uiClassic confinement is intentional. Horizon launches host shells and host tools such as ssh, git, xdg-open, pgrep, lsof, and optional tailscale helpers, so a strict sandbox would compromise core workflows.
git clone https://github.com/peters/horizon.git
cd horizon
git lfs install
git lfs pull
cargo run --releaseRequires Git LFS for bundled assets and Rust 1.88+. Linux needs system headers for GPU rendering — see AGENTS.md for per-distro install commands.
Most app shortcuts use Ctrl+Shift to avoid conflicts with shell programs (Ctrl+C, Ctrl+K, Ctrl+B, etc.) and OS-level bindings. Canvas zoom keeps the standard Ctrl/Cmd+0, Ctrl/Cmd+Plus, and Ctrl/Cmd+Minus bindings. They are configurable through the shortcuts: block in your config file and editable from the built-in settings panel.
Duplicate or overlapping bindings are rejected, including near-conflicts such as Ctrl+B and Ctrl+Shift+B.
| Shortcut | What it does |
|---|---|
| Ctrl+Shift+K | Quick-navigate to any workspace |
| Ctrl+Shift+N | New terminal panel |
| Ctrl+Shift+W | Focus the active workspace at the current zoom |
| Ctrl+Shift+9 | Fit the active workspace into view |
| Ctrl+Shift+H | Open Remote Hosts overlay |
| Ctrl+Shift+J | Open the sessions picker |
| Ctrl+Shift+B | Toggle sidebar |
| Ctrl+Shift+U | Toggle HUD |
| Ctrl+Shift+M | Toggle minimap |
| Ctrl+Shift+A | Align visible attached workspaces into a horizontal row |
| Ctrl+Shift+, | Open settings editor |
| Ctrl+Shift+F | Focus the terminal search bar |
| Ctrl+0 | Reset canvas zoom to 100% |
| Ctrl+Plus | Zoom canvas in |
| Ctrl+Minus | Zoom canvas out |
| F11 | Fullscreen the active panel |
| Escape | Exit active panel fullscreen |
| Ctrl+Shift+F11 | Toggle window fullscreen |
| Ctrl+Shift+S | Save the active Markdown editor |
| Ctrl+Shift+C | Copy the current terminal selection |
| Ctrl+Shift+V | Paste into the focused terminal |
| Ctrl+Shift+R | Reconnect the focused disconnected SSH panel |
If you do not want to start by dragging panels around the canvas, use Horizon like this:
- Create a workspace from the toolbar or with Ctrl+double-click on the canvas.
- Add a terminal from your first preset with Ctrl+Shift+N.
- Jump between workspaces with Quick Nav using Ctrl+Shift+K.
- Use Ctrl+Shift+W to refocus the current workspace or Ctrl+Shift+9 to fit it into view.
- Use the workspace header controls for Rows, Cols, or Grid when you want a structured layout without leaving the canvas.
| Interaction | What it does |
|---|---|
| Middle-mouse drag | Pan the canvas |
| Space + Left-click drag | Pan the canvas |
| Minimap click-and-drag | Jump to that area of the canvas |
| Ctrl+Scroll | Zoom around the cursor |
| Ctrl+Click | Open URL or file path under cursor |
| Ctrl+double-click canvas | Create a new workspace |
| Ctrl+double-click inside a workspace | Add a new terminal |
On macOS, substitute Cmd for Ctrl. Copy and paste use the standard Cmd+C / Cmd+V bindings, and on Windows you can also use Ctrl+Insert / Shift+Insert. The SSH reconnect shortcut is contextual and is disabled if another global shortcut overlaps with Ctrl+Shift+R.
The settings editor writes back to the same config file Horizon loaded. By default that is ~/.horizon/config.yaml, and config.yml is also supported when discovered or passed explicitly. You can define workspaces, panel presets, feature flags, and keyboard shortcuts:
appearance:
theme: auto # auto, light, or dark
shortcuts:
command_palette: Ctrl+Shift+K
new_terminal: Ctrl+Shift+N
focus_active_workspace: Ctrl+Shift+W
fit_active_workspace: Ctrl+Shift+9
open_remote_hosts: Ctrl+Shift+H
toggle_sidebar: Ctrl+Shift+B
toggle_hud: Ctrl+Shift+U
toggle_minimap: Ctrl+Shift+M
align_workspaces_horizontally: Ctrl+Shift+A
toggle_settings: Ctrl+Shift+Comma
zoom_reset: Ctrl+0
zoom_in: Ctrl+Plus
zoom_out: Ctrl+Minus
fullscreen_panel: F11
exit_fullscreen_panel: Escape
fullscreen_window: Ctrl+Shift+F11
save_editor: Ctrl+Shift+S
search: Ctrl+Shift+F
workspaces:
- name: Backend
cwd: ~/projects/api
panels:
- kind: shell
- kind: claude
- kind: open_code
- kind: git_changes
- name: Frontend
cwd: ~/projects/web
panels:
- kind: shell
- kind: shell
presets:
- name: Shell
alias: sh
kind: shell
- name: Claude Code
alias: cc
kind: claude
- name: OpenCode
alias: oc
kind: open_code
- name: Gemini CLI
alias: gm
kind: gemini
- name: KiloCode
alias: kc
kind: kilo_code
- name: Git Changes
alias: gc
kind: git_changes
# Optional: disable the default attention feed
features:
attention_feed: falseUse key names like Plus, Minus, Comma, Escape, and F11 in YAML instead of punctuation-only shortcut components such as Ctrl++.
Dictate straight into a terminal: every terminal-backed panel's title bar gets a mic button (Editor, Git Changes, and Usage panels have no PTY, so they get none), and a Ventrilo-style push-to-talk hotkey (default F9, hold to record) dictates into the focused panel. Audio is transcribed locally by transcribe.cpp — nothing leaves the machine — and the text is inserted as if typed.
It is a compile-time opt-in because it builds a native C++ inference library. The dependency comes from crates.io with the C++ sources vendored inside (no git submodules); you need CMake and a C++ compiler, plus on Linux the ALSA headers for microphone capture (pkg-config and libasound2-dev on Debian/Ubuntu, alsa-lib-devel on Fedora):
cargo speech # alias for: cargo run --release --features speech (CPU inference; Metal on macOS)
cargo speech-cuda # NVIDIA GPU inference (needs the CUDA toolkit)
cargo speech-vulkan # any GPU via Vulkan (needs the Vulkan SDK to build)When a GPU backend is compiled in (Metal ships automatically in plain speech on macOS), the auto backend probes discrete GPUs first and always falls back to CPU; a CPU-only build simply runs on CPU.
features:
speech:
enabled: true
model: /path/to/models/whisper-large-v3-Q8_0.gguf # any transcribe.cpp GGUF
language: "no" # ISO hint; "auto" detects. Supported set = the model's GGUF metadata
task: transcribe # translate = speak any language, insert English text
backend: auto # auto | cpu | cuda | vulkan | metal
input_device: "" # microphone name (exact or substring, e.g. "NT-USB"); "" = system default
hotkey: "F9" # push-to-talk; same syntax as the shortcuts table, "" disables
hotkey_mode: hold # hold (Ventrilo-style) | toggle
preload: false # true = load the model at startup; first dictation becomes instantThe push-to-talk hotkey listens in the main window (it targets the focused panel there); panels in detached windows can still dictate via their title-bar mic button.
Recommended models (prebuilt GGUFs under handy-computer on Hugging Face): whisper-large-v3-turbo (fast multilingual), whisper-large-v3 (multilingual with a working translate task), parakeet-tdt-0.6b-v3 (fast, 25 European languages), and for Norwegian — including dialects — NB-Whisper Large converted per the transcribe.cpp docs. A model's supported languages are read from its GGUF metadata at load time.
Settings → General → Features → Speech Input exposes all of this with a model-aware UI: the spoken-language list and translation targets are read from the model's own GGUF metadata, the push-to-talk key is rebindable by pressing it, the microphone is picked from the devices the audio host reports (machines with several — webcam, USB mic — often default to the wrong one), the actually-selected backend is shown next to auto, and saved changes apply live — no restart.
Dictation outcomes that would otherwise be invisible are surfaced as a transient message: a tap too short to transcribe, a recording in which no speech was detected (named with the microphone it came from), a push-to-talk press while the previous dictation is still processing, and speech errors.
For switching languages without touching settings, define profiles — each with its own model, language, output, and push-to-talk key. The key is the language (Ventrilo-style channel binds): hold F1 to dictate Norwegian, F2 for English, F3 to speak Norwegian and insert English. Each profile's model loads lazily on first use and then stays warm; set preload: true on a profile to load it at startup instead, so its first dictation is instant (the model's memory is resident from launch — budget accordingly).
features:
speech:
enabled: true
backend: auto
hotkey_mode: hold
profiles:
- name: Norsk
model: ~/models/nb-whisper-large-Q8_0.gguf
language: "no"
task: transcribe
hotkey: F1
preload: true # optional: load at startup for an instant first dictation
- name: English
model: ~/models/whisper-large-v3-Q8_0.gguf
language: en
task: transcribe
hotkey: F2
- name: NO→EN
model: ~/models/whisper-large-v3-Q8_0.gguf
language: "no"
task: translate
target_language: en
hotkey: F3Profile hotkeys are validated against each other and against every global shortcut. The mic button uses the last-used profile; with no profiles: list, the flat model/language/hotkey fields act as a single profile as before.
For Norwegian — including dialects — the strongest model is NB-Whisper Large, a Whisper fine-tune by the National Library of Norway (NbAiLab/nb-whisper-large, trained on ~20 000 hours of Norwegian speech). It is not distributed as a GGUF, so convert it once with transcribe.cpp (community whisper fine-tunes convert like the stock checkpoints; NbAiLab ships sharded safetensors that must be merged into a single model.safetensors first), quantize to Q8_0, then:
features:
speech:
enabled: true
model: /path/to/nb-whisper-large-Q8_0.gguf
language: "no" # bokmål output; "nn" for nynorsk
task: transcribeTwo NB-Whisper quirks worth knowing: it normalizes dialect speech into standard written Norwegian rather than transcribing verbatim (usually what you want), and it ignores the translate task — for spoken-Norwegian → English text, point the model at stock whisper-large-v3 (prebuilt GGUF under handy-computer) and set Output to Translate to en.
For agents — when a user asks for speech support: build with the right feature for the machine's GPU (speech-cuda for NVIDIA, speech on macOS, speech-vulkan otherwise), download a model GGUF, fill in features.speech in ~/.horizon/config.yaml (or via Settings), and verify the mic button appears on terminal panel title bars.
| Rust | Edition 2024, safe and fast |
| eframe / egui | Immediate-mode UI framework |
| wgpu | GPU rendering — Vulkan, Metal, DX12, OpenGL |
| alacritty_terminal | Battle-tested terminal emulation |
| Catppuccin | Terminal palettes (Mocha dark / Latte light) on a warm editorial UI chrome |
See AGENTS.md for development setup, architecture, coding standards, and CI requirements. Release instructions live in docs/release-flow.md. Manual smoke-test plans live under docs/testing, including the workspace close smoke test.
cargo fmt --all -- --check
cargo test --workspace
cargo clippy --all-targets --features speech,trace-profiling -- -D warningsMIT License
