GPU-accelerated terminal board that puts all your sessions on an infinite canvas
$ npx -y skills add peters/horizon --agent claude-code
Run the curl in your terminal, the rest in Claude Code.
Repo: peters/horizon
What's inside
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 .md file onto the canvas or create one from the command palette. Split view with syntax highlighting and live preview, saved with Ctrl+Shift+S.
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/horizon
If you prefer to add the tap explicitly first:
brew tap peters/horizon
brew install horizon
To update or remove it later:
brew upgrade horizon
brew uninstall horizon
brew untap peters/horizon
Stable 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.Horizon
Stable 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-ui
Classic 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 --release
Requires 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:
| 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: false
Use 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 instant
The 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: F3
Profile 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: transcribe
Two 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 warnings
.cargo/
config.toml
.gitattributes
.github/
dependabot.yml
workflows/
ci.yml
release.yml
.gitignore
.idea/
horizon.iml
modules.xml
runConfigurations/
Run_horizon.xml
Test_horizon.xml
vcs.xml
.surge/
surge.yml
AGENTS.md
assets/
demo.gif
hero-banner.svg
icons/
icon-128.png
icon-256.png
icon-512.png
icon-64.png
logo.svg
plugins/
claude-code/
.claude-plugin/
plugin.json
skills/
horizon-notify/
SKILL.md
codex/
skills/
horizon-notify/
SKILL.md
Cargo.lock
Cargo.toml
CLAUDE.md
crates/
horizon-core/
Cargo.toml
src/
agents.rs
attention.rs
board/
board.rs
arrangement.rs
attention.rs
geometry.rs
shutdown.rs
tests/
core.rs
layout.rs
mod.rs
workspace.rs
workspaces.rs
config_migration.rs
config.rs
dir_search.rs
editor.rs
error.rs
git_changes.rs
git_status.rs
git_watcher.rs
horizon_home.rs
layout.rs
lib.rs
managed_install.rs
opencode_paths.rs
panel/
panel.rs
spawn.rs
remote_hosts.rs
runtime_state/
runtime_state.rs
agent_sessions.rs
claude_live_sessions.rs
search.rs
session_store/
session_store.rs
model.rs
tests.rs
shortcuts.rs
speech_config.rs
speech_model.rs
ssh/
config_parser.rs
connection_status.rs
mod.rs
terminal/
terminal.rs
content.rs
events.rs
lifecycle.rs
replay.rs
resize.rs
selection.rs
support.rs
transcript.rs
usage_dashboard.rs
usage_stats/
usage_stats.rs
claude.rs
view.rs
workspace.rs
tests/
session_startup.rs
support/
mod.rs
transcript_capture.rs
transcript_capture_incompatible_script.rs
transcript_capture_missing_script.rs
workspace_bounds.rs
horizon-cursor/
Cargo.toml
src/
lib.rs
horizon-ui/
assets/
fonts/
InterVariable.ttf
JetBrainsMono-Regular.ttf
NotoSansCJKsc-Regular.otf
NotoSansSymbols2-Regular.ttf
OFL-1.1.txt
THIRD_PARTY_NOTICES.md
build.rs
Cargo.toml
examples/
surge-update-smoke.rs
src/
app/
actions/
actions.rs
command_palette.rs
interaction.rs
layout.rs
panels.rs
pickers.rs
search.rs
support.rs
attention_feed.rs
canvas.rs
detached_viewports/
detached_viewports.rs
shortcut_actions.rs
file_drop_highlight.rs
file_drop.rs
frame_stats.rs
lifecycle.rs
minimap/
minimap.rs
interaction.rs
mod.rs
panel_chrome.rs
panels/
panels.rs
tests.rs
persistence.rs
remote_hosts.rs
root_chrome.rs
session_manager.rs
session.rs
settings/
bar.rs
general.rs
mod.rs
presets.rs
shortcuts.rs
speech/
speech.rs
model_info.rs
yaml_editor.rs
shortcut_inventory.rs
shortcuts.rs
sidebar/
sidebar.rs
toolbar.rs
speech/
capture.rs
engine/
engine.rs
tests.rs
mod.rs
stub.rs
worker.rs
ssh_upload/
ssh_upload.rs
destination_picker.rs
render.rs
worker/
worker.rs
ssh.rs
startup_session.rs
updates.rs
util.rs
view.rs
workspace/
workspace.rs
paint.rs
render.rs
toolbar.rs
yaml_highlight.rs
branding.rs
command_palette/
mod.rs
render.rs
command_registry.rs
dir_picker/
local.rs
mod.rs
modal.rs
editor_widget.rs
git_changes_widget.rs
input/
ime_commit.rs
keyboard.rs
mod.rs
mouse.rs
sequence.rs
winit_keyboard.rs
loading_spinner.rs
main.rs
native_app.rs
plugin_install.rs
primary_selection.rs
remote_hosts_overlay/
remote_hosts_overlay.rs
layout.rs
paint.rs
query.rs
search_overlay/
mod.rs
render.rs
terminal_widget/
ime.rs
input/
input.rs
frame_events.rs
keyboard.rs
mouse_tests.rs
routing.rs
selection_drag.rs
selection_tests.rs
viewport_scroll.rs
layout.rs
mod.rs
render.rs
scrollbar.rs
theme.rs
usage_widget.rs
docs/
architecture/
maintainability.md
pi-rpc-follow-up.md
release-flow.md
testing/
2026-03-22-ssh-upload-smoke.md
2026-03-24-surge-installer-update-smoke.md
2026-04-04-macos-workspace-drag-sidebar-smoke.md
2026-05-28-macos-pi-agent-panel-smoke.md
2026-05-29-macos-shell-panel-cwd-restore-smoke.md
windows-space-pan-smoke-test.md
LICENSE
packaging/
linux/
horizon.desktop
install.sh
macos/
make_app_bundle.sh
README.md
rust-toolchain.toml
rustfmt.toml
scripts/
build-surge-toolchain.sh
check-maintainability.sh
check-version-sync.sh
next-version.sh
package-release-asset.sh
profile-vulkan.sh
profile.sh
render-homebrew-formula.sh
render-surge-manifest.sh
render-winget-manifests.ps1
render-winget-manifests.sh
repro-issue-19-redraw.sh
run-surge-azure-smoke.sh
run-surge-filesystem-smoke.sh
run-winget-azure-smoke.sh
set-release-version.sh
smoke-winget-package.ps1
stage-surge-artifacts.sh
update-homebrew-tap.sh
update-winget-pkgs.sh
version-lib.sh
snap/
local/
launch.sh
snapcraft.yamlFAQ
horizon is a Claude Code plugin with 2 hand-picked skills for development work, indexed on Flowy. Install it with the command on its page. It includes horizon-notify, horizon-notify. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.