A Neovim GUI built for efficiency, not for cool.
Your Neovim config in proper macOS windows, in the tradition of MacVim. Instant startup with near-zero overhead, fast Metal rendering, fast multi-tab session loading. Designed for focus. No visual noise, no distractions.
Veil brings the MacVim workflow to Neovim: multiple independent windows in a single app, Cmd+` to switch between projects, Cmd+1/2/3 to jump to tabs. As a native macOS app instead of a terminal process, nearly all key sequences reach Neovim without being intercepted.
Vim is a tool built for focused, efficient work. Animations consume attention. Cursor effects, scroll easing, transition flourishes all pull your eyes away from what really matters: the text. Veil chooses to keep things quiet.
- MacVim-tradition multi-window: each window runs an independent Neovim,
Cmd+Nto create,Cmd+`to cycle. - Tabs: Neovim drives the tabline by default; opt into native macOS tabs with
native_tabs = true. - Instant startup: sub-second cold start with fast multi-tab session loading.
- Pixel-perfect Metal rendering: GPU-accelerated, with continuous box-drawing lines and programming ligature support.
- Native macOS keyboard handling: most key sequences reach Neovim untouched; app shortcuts and Neovim keymaps both configurable. See KEYBOARD.md.
- Per-window profiles: pick a different
NVIM_APPNAMEper window withCmd+Shift+N. - Remote Neovim: connect to a remote Neovim over TCP, with local clipboard integration.
- CJK ready: auto-fallback to matching CJK font variants with full IME support.
- macOS 14+
- Neovim 0.10+ recommended (install via
brew install neovim)
Veil uses your system-installed Neovim. No bundled binary, ~1 MB download. Veil finds nvim anywhere your interactive shell can access it, and caches the resolved path so only the first launch pays the detection cost.
Veil communicates with Neovim via its stable msgpack-RPC and ext_linegrid UI protocol, and works with any recent Neovim version. The RPC layer is heavily optimized with event batching for responsive rendering.
Veil has two variants: the standard build, and a default-editor build that claims file associations on install (so double-clicking source files in Finder opens Veil).
brew tap 0x1b2c/veil
brew install --cask veil # standard
brew install --cask veil-default-editor # claims file associationsThis installs Veil.app to /Applications and symlinks the veil, gvim, and gvimdiff CLI commands to your Homebrew bin directory.
Download Veil.zip (standard) or Veil-default-editor.zip (claims file associations) from Releases, unzip, and move Veil.app to /Applications. Then remove the quarantine attribute so macOS doesn't block it:
xattr -cr /Applications/Veil.appOpen Veil.xcodeproj in Xcode and run, or:
make # Release build (arm-only, fast)
make debug # Debug build
make test # Run unit tests
make install # Build + install to /Applications
make install-polite # Build + install + strip LSHandlerRank from /Applications/Veil.app
make zip # Universal build + package as Veil-default-editor.zip
make zip-polite # Universal build + package as Veil.zip (polite variant)
make clean # Clean build artifacts
make lsp # Generate buildServer.json for SourceKit-LSP
For Neovim/editor LSP support, run make lsp after cloning to generate buildServer.json (requires xcode-build-server).
Veil reads your existing Neovim configuration (~/.config/nvim/) for editor behavior. Veil's own settings (font, keyboard shortcuts, remote bookmarks, and a few other options) live in ~/.config/veil/veil.toml, separate from nvim's config. All fields are optional and have sensible defaults; see veil.sample.toml for the full reference. Changes take effect on the next new window.
We recommend Maple Mono for its excellent CJK support and Nerd Font icons:
brew install font-maple-mono-nf-cn # or font-maple-mono-nf without CJKSet it in nvim:
vim.o.guifont = 'Maple Mono NF CN:h16' -- or 'Maple Mono NF:h16' without CJKOr in ~/.config/veil/veil.toml (recommended for remote nvim connections, where the remote machine's guifont rarely matches your local Mac):
[font]
family = "Maple Mono NF CN" # or "Maple Mono NF" without CJK
size = 16.0See FONT.md for precedence rules, the force option, and Nerd Font fallback details.
Veil's keybindings split into two categories: app shortcuts for Veil's own actions (new window, close tab, open settings, etc.) and Neovim keymaps for macOS-style shortcuts pre-bound to Neovim commands (cmd+s → :w, cmd+1-9 → switch tabs, etc.). Both are configurable, and the Neovim keymaps can be disabled wholesale to free those keys for your own nvim mappings. See KEYBOARD.md for the full list and string format.
Quick reference:
| Key | Action |
|---|---|
Cmd+N |
New window |
Cmd+Shift+N |
New window with profile picker |
Cmd+W |
Close tab (or window if only one tab) |
Cmd+Shift+W |
Close window |
Cmd+Q |
Quit Veil |
Cmd+` |
Cycle windows |
Cmd+1 – Cmd+9 |
Switch tab |
Cmd+, |
Open settings (veil.toml) |
Cmd+Ctrl+Shift+N |
Connect to remote nvim |
Each of these stays at the granularity macOS users expect: Cmd+W closes one tab, Cmd+Shift+W closes the window, Cmd+Q quits the entire app. Unsaved-buffer prompts route through Neovim's :confirm qa.
Veil can connect to a Neovim instance running on a remote machine over TCP. Start Neovim on the remote host listening on localhost:
nvim --headless --listen 127.0.0.1:6666Neovim's RPC protocol has no authentication, so always bind to 127.0.0.1 and use SSH tunneling. Never expose the listening port to the network directly.
Forward the port over SSH, then connect from Veil using Cmd+Ctrl+Shift+N:
# -L local_port:remote_host:remote_port
ssh -L 6666:127.0.0.1:6666 your-serverFor a persistent setup, add to ~/.ssh/config:
Host dev-nvim
HostName your-server
# local_port remote_host:remote_port
LocalForward 6666 127.0.0.1:6666Then ssh dev-nvim automatically forwards the port. Use ssh -N dev-nvim to forward without opening a shell.
You can save frequently used connections in veil.toml:
[[remote]]
name = "Dev Server"
address = "127.0.0.1:6666"
[[remote]]
name = "Staging"
address = "127.0.0.1:6667"I don't always code, but when I do, I code in
production.
Clipboard integrates seamlessly with your local Mac. Yank/paste with + and * registers in the remote session operates on your local pasteboard.
Veil registers these commands in Neovim:
:VeilAppVersionshow Veil and Neovim version info, and check for updates. Use:VeilAppVersion!to open in a scratch buffer for easy copying:VeilAppDebugToggleshow/hide an overlay with renderer info, frame time, grid size, font details, and more:VeilAppDebugCopycopy the debug overlay info to the system clipboard
Veil ships CLI commands inside the app bundle: veil, plus gvim and gvimdiff as traditional aliases (gvimdiff is equivalent to veil -d). Symlink them to your PATH:
ln -s /Applications/Veil.app/Contents/bin/veil ~/.local/bin/veil
ln -s /Applications/Veil.app/Contents/bin/gvim ~/.local/bin/gvim
ln -s /Applications/Veil.app/Contents/bin/gvimdiff ~/.local/bin/gvimdiffThen use it like nvim. All nvim flags and arguments are passed through transparently:
veil file.txt
gvim +42 file.txt # open at line 42
gvim -p file1.txt file2.txt # open in tabs
veil -d file1.txt file2.txt # diff mode
gvimdiff file1.txt file2.txt # same as veil -dIf Veil is already running, the CLI forwards arguments to the existing instance (opens a new window) instead of launching a second copy.
Each window can run a different Neovim configuration. Use NVIM_APPNAME from the CLI or Cmd+Shift+N from the GUI to select which config directory under ~/.config/ nvim uses:
NVIM_APPNAME=astronvim veil # launch Veil with astronvim config
NVIM_APPNAME=nvim-nvchad gvim file.txt # open file with NvChad configCreate shell aliases for configs you use often:
alias gvi='NVIM_APPNAME=nvim-nvchad gvim'
gvi file.txt # just works, fresh launch or new windowMacVim introduced a workflow that set it apart from gVim: multiple independent windows in a single app, each with its own tabs, switching between projects instantly with Cmd+`. You could have three codebases open, Cmd+` between them, and Cmd+1/2/3 to jump to specific tabs within each one.
In the Neovim era, most GUI frontends followed the gVim model: you can open multiple instances, but each is a separate process with no way to Cmd+` between them. VimR was the exception, bringing the MacVim multi-window experience to Neovim, but its Redux architecture made startup and multi-tab session loading noticeably slow.
Veil carries this tradition forward: a minimal, fast wrapper that gives Neovim first-class macOS citizenship. Multiple windows as independent workspaces, intuitive tab switching with Cmd+1/2/3, and as a native app instead of a terminal process, nearly all key sequences reach Neovim without being intercepted.
Note: Neovide v0.16.0 also shipped macOS multi-window support within one day of Veil's first release, a fun coincidence. Neovide is an excellent choice if you prefer a cross-platform solution with visual effects. Veil focuses on being a minimal native macOS wrapper (~1 MB).
Thanks to VimR by Tae Won Ha. Veil learned a great deal from its implementation of the Neovim UI protocol, input handling, and macOS integration.
Box-drawing and block element rendering algorithms are ported from Ghostty (MIT License).
Glyph overflow strategy inspired by WezTerm: when a glyph (e.g. Nerd Font icon) is wider than its allocated cell, Veil renders it at full natural width if followed by a space, or shrinks it to fit otherwise.
MIT
