Skip to content
z0mbixPublic

About

A focused Neovim desktop frontend built with GPUI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Zvim

A focused native frontend for Neovim, written in Rust with GPUI. Neovim owns editing, splits, plugins, LSP, and configuration. Zvim draws the screen and integrates input, clipboard, windows, and file opening with the desktop.

Zvim showing Neovim with split editors and three native terminal panes

Platforms: macOS and Linux only. macOS is exercised locally; Linux remains experimental. The native Ghostty terminal requires Wayland on Linux; the editor can still run on X11. See validation.

Run on macOS

Requires macOS 13 or newer, Rust 1.98.1, Xcode 26.3 (including its macOS SDK), Python 3, curl, and Zig 0.16.0. GPUI's runtime Metal shader compilation is enabled, so the separate Xcode Metal compiler component is not required.

python3 scripts/bundle-neovim.py
cargo build --locked
python3 scripts/bundle-ghostty.py
cargo run --locked -- --clean
cargo run --locked -- path/to/file.rs

The first command downloads Neovim 0.12.5 from its official release and verifies the SHA-256 in packaging/neovim.json. No system Neovim is required. The entire runtime is bundled, not just the executable.

cargo build --release --locked
python3 scripts/package.py
open dist/zvim-macos-arm64/Zvim.app

For local macOS testing, quit Zvim and run just install-dev. This builds a fresh release package, replaces /Applications/Zvim.app, and removes its quarantine attribute. Then launch it with open /Applications/Zvim.app.

On an Intel Mac the directory is dist/zvim-macos-x86_64. The .app is signed ad hoc for local use; it is not notarised for public distribution. Signing identities and notarisation credentials are deliberately not part of the repository.

Command-line launcher

After moving Zvim to its permanent location, open Settings → Command-line launcher:

  1. Keep the default ~/.local/bin, or use Choose folder… to select your bin directory.
  2. Click Install / update zvim.

The app installs the launcher directly, with no Python or additional tools required. The selected directory is remembered, missing default directories are created, and unrelated commands or symlinks are never replaced. Choose a writable location; the app does not request administrator privileges. If you move the app, repeat installation. Choosing a different directory leaves the previous launcher in place.

Keep that directory on your shell's PATH. For fish, run fish_add_path ~/.local/bin once when using the default location. For zsh, add export PATH="$HOME/.local/bin:$PATH" to ~/.zshrc and open a new terminal. The app does not edit shell configuration.

The Python installer remains available for development or scripted setup (python3 scripts/install-cli.py --app /Applications/Zvim.app --bin-dir ~/.local/bin), but is not needed when installing from Settings.

zvim .
zvim ~/Projects/foo
zvim -O left.rs right.rs
zvim -o2 a.txt b.txt
zvim -p a.txt b.txt
zvim -R file.txt
zvim -d before.txt after.txt
zvim +42 src/main.rs
zvim --clean .
zvim --wait .

Each launch opens a new window in one shared application process, giving Zvim a single Dock icon. The installed launcher returns once the window is accepted; --wait stays attached until that specific window closes. Each window owns an independent Neovim process and inherits its caller’s environment. Terminal shells inherit the running application’s environment. If the first file operand is an existing directory, it sets Neovim's working directory before configuration loads. All remaining relative paths, including -u and -S values, resolve relative to that directory. Otherwise the shell's working directory is retained. Neovim configuration supplies project navigation.

GUI-compatible Neovim options are forwarded in their original order, including split/tab counts, readonly/diff modes, -u, -S, --cmd, -c, +command, and --listen. Use -- before filenames beginning with - or +. --help and --version print in the terminal. Headless, Ex/batch, standalone Lua, remote-client, and stdin-input modes conflict with the embedded GUI connection and produce an error; use Neovim directly for those. Detached startup errors are recorded in launcher.log in Zvim's configuration directory.

Choose Zvim → About Zvim to see the running app’s version. The same version is printed by zvim --version.

App settings

Open Zvim → Settings… (⌘, on macOS; Ctrl+Shift+, elsewhere). General preferences and Keybindings have separate tabs. Compact switches are green when enabled and grey when disabled; changes save automatically.

  • Appearance: System / Light / Dark styles Zvim controls. System responds to desktop appearance changes; native titlebars continue to follow the OS.
  • Sync editor appearance, off by default, also sets Neovim's background option. Your colourscheme must support both appearances. NvChad/Base46 palettes are preserved unchanged because changing background resets their custom highlights; Zvim does not choose a replacement theme. Disabling sync restores the background from before sync was enabled unless you changed it yourself in Neovim. No configuration files are edited.
  • Focus follows mouse, off by default, focuses editor or terminal content when the pointer moves into it. Tab controls, dividers, selection drags and close dialogs do not switch focus. Keyboard focus stays put until the pointer moves again. This affects Zvim panes, not Neovim’s internal splits.
  • Remember window size and position, on by default, reuses the last saved editor-window placement. Moves and resizes are saved after a brief pause, as well as on normal close; fullscreen preserves the previous windowed placement. Turning it off opens new windows centred at the default size and stops saving geometry. This does not restore files, sessions, or multiple window layouts.
  • Application icon previews the bundled Neovim mark. The saved app_icon ID is neovim; this release has one choice. Unknown IDs fall back to it without losing other settings. Additional artwork and platform switching can be added later. The macOS bundle embeds the icon for Finder, the Dock and the app switcher.
  • Open diagnostics folder reveals the directory containing settings and startup/editor logs.

Preferences live in preferences.json, separate from window.json. On macOS, both are in ~/Library/Application Support/dev.zvim.Zvim/, shared by just run and packaged app launches. Changes apply to all open windows in the shared application. Font and plugin settings remain in Neovim.

Behaviour

  • zvim [files...] loads your normal Neovim configuration. zvim --clean [files...] starts without it. Use -- before a filename beginning with -.
  • Each window owns one bundled editor process. g:zvim is true before your configuration loads; ZVIM=1 is also available to child tools.
  • Set vim.opt.guifont = 'Menlo:h16' (or your installed monospace font) and vim.opt.linespace in your configuration. Highlight colours and cursor shapes come from Neovim.
  • On macOS: Command-O opens files, Command-V pastes, Command-W requests close (or closes the focused terminal tab), Command-N opens a window (or a terminal tab when the terminal is focused), and Command-Q requests closing all windows. Control keys, including Ctrl+H/J/K/L for split navigation, retain Neovim/terminal behaviour unless explicitly assigned in Settings. Menus also expose Open, Paste, New Window, and Close.
  • Mouse clicking, selection, dragging, and scrolling require Neovim's mouse option, for example set mouse=a.
  • The + and * registers use the system clipboard. Neovim's own register commands provide copy/cut; Zvim does not replace Ctrl-C.
  • File → Open, Finder's Open With, and file drops open files in the current session. Opening files never forces away unsaved edits.
  • Close requests use :confirm qall. Choose Yes/No/Cancel in Neovim. No automatic discard or forced shutdown on ordinary close.
  • Window bounds, neovim.log, and the latest startup.log are stored in the platform's Zvim configuration directory. Neovim's configuration is not edited.
  • Desktop launches preserve PATH and append common Unix package-manager locations. Tools in custom shell-only paths still need to be on the desktop environment's PATH or configured in Neovim.

Your existing plugins remain responsible for their own installations, external programs, and network activity. Zvim adds no AI, accounts, collaboration, telemetry, or automatic updates.

Native terminal (experimental)

The terminal supports nested right/down splits, each with its own shell tabs and compact tab strip. The dock divider and the dividers between terminal panes are draggable. The first opening and each new tab start $SHELL -l in Neovim's current window-local directory. Hiding or switching tabs keeps every shell, scrollback and running program alive. Exiting a shell closes its tab automatically; create another with + or Cmd+N.

Defaults mirror the terminal actions in the maintainer's Zed keymap, with Zed's standard previous/next-tab shortcuts:

Action macOS shortcut Context
Focus terminal / return to editor Cmd+Shift+, (Cmd+<) Either pane
Show / hide terminal Cmd+Shift+. (Cmd+>) Either pane
Zoom focused terminal pane / restore layout Cmd+Shift+Enter Either pane
New tab in focused terminal pane Cmd+N Terminal focused
Close active terminal tab Cmd+W Terminal focused
Split focused terminal right Cmd+D Terminal focused
Split focused terminal down Cmd+Shift+D Terminal focused
Focus terminal pane left / right Cmd+[ / ] Terminal focused
Previous / next terminal tab Cmd+Shift+[ / ] or Cmd+Alt+Left / Right Terminal focused
Select tab 1–9 in focused pane Cmd+1–9 Terminal focused
Hide terminal Cmd+? Terminal focused
Close window Cmd+Shift+W Either pane

Option+, / Option+. also focus/toggle the terminal from Neovim normal/visual mode. Returning focus to the editor keeps the terminal visible and restores the split if it was maximised. Cmd+[ / ] moves focus spatially between left/right terminal panes. Click a stacked pane or use Terminal → Focus Pane Above / Below to focus it. Tab switching and numbered selection affect only the focused pane.

From Neovim, :ZvimTerminal still toggles visibility. Drag the divider to resize; the split starts at 40% and retains its proportion while that window stays open, including after hiding or maximising.

Under Settings → Keybindings, record replacements for show/hide, focus, zoom, new/close/previous/next tab, split right/down and focus pane left/right. Escape cancels recording; Reset shortcuts restores these defaults. Changes save automatically and apply immediately. Old default backtick shortcuts migrate to the new defaults; customised shortcuts are preserved. There is no runtime dependency on Zed or automatic rewriting of its keymap.

The pane uses Ghostty's native Metal renderer on macOS and OpenGL renderer on Linux/Wayland. It loads your Ghostty configuration for fonts and keybindings. Terminal colours follow Neovim live by default, including NvChad/Base46 themes; disable Follow Neovim terminal colours under Settings → General to opt out. Zvim's editor font remains controlled by Neovim. Ghostty application actions such as opening tabs/windows and settings are not implemented by this embedder. The terminal provides multiple panes and shell tabs in one resizable bottom dock, with native scrollback search and no session restoration. Built-in :terminal and existing terminal plugins retain Neovim's behaviour.

Terminal search opens with Cmd+F on macOS (Ctrl+Shift+F on Linux). Type to search scrollback; Enter / Shift+Enter and the arrow buttons move through matches, and Escape closes the search bar. Cmd+G / Cmd+Shift+G also navigate matches (Linux: Ctrl+Shift+G / Ctrl+Alt+G). Each terminal tab keeps its own search.

On macOS, the Window menu lists open windows with the current one checked. Minimise (Cmd+M), Bring All to Front, and selecting a listed window use native macOS behaviour. Cmd+ / Cmd+Shift+ cycle windows.

Copy/paste use Ghostty bindings (normally Command-C/V on macOS, Ctrl-Shift-C/V on Linux). Terminal → Close Terminal Tab asks before ending a running command. Exiting a shell automatically closes its tab. Closing the last tab in a split removes that split and expands its neighbour; closing the final terminal hides the dock. Window close and Quit check all terminal tabs across every split, including hidden ones, before closing Neovim; Cancel preserves the editor and shell. Idle prompts detected by Ghostty close quietly. If Neovim is quit directly, Keep Terminal expands the remaining session to fill the window.

The native dependency and its patched Ghostty source are pinned in Cargo. The initial build compiles Ghostty using Zig and downloads its dependencies; no standalone Ghostty installation is required. The integration is a trial, not a claim of complete standalone Ghostty parity. See docs/TERMINAL.md for limitations and verification.

Development checks

Install prek and enable the pre-commit hook once per checkout:

prek install
prek run --all-files

The hook runs cargo fmt --all using the rustfmt version pinned in rust-toolchain.toml, covering the application, examples, tests, and local Ghostty crate. If formatting changes files, review and stage those changes before retrying the commit. To format everything manually, run cargo fmt --all.

cargo fmt --all --check
cargo test --locked --all-targets
cargo clippy --locked --all-targets -- -D warnings

Run the bundling step before tests. The integration tests use real bundled Neovim with isolated state. --no-default-features runs the protocol/process tests without compiling a GUI. just check, just run, and just package are optional shortcuts.

Linux

CI builds macOS arm64/x86-64 and Linux x86-64. Releases currently publish macOS app bundles; Linux archives remain experimental build artifacts.

  • Linux: install the GPUI development dependencies listed in docs/VALIDATION.md; use python3 scripts/bundle-neovim.py, then cargo build --release --locked and python3 scripts/package.py. The archive contains zvim, neovim/, and a desktop entry. Keep the runtime beside the binary; add that directory to PATH before installing the desktop entry under ~/.local/share/applications. Wayland and X11 editor backends are enabled. The Ghostty pane requires Wayland, EGL, OpenGL 4.3, libxml2 and libc++ 21 or newer.

Architecture and limitations

See architecture and validation. In particular, cell-based text rendering does not currently provide cross-cell ligatures, screen-reader document semantics, images, or externalised graphical widgets. Native input-method hooks and composition display are implemented, but comprehensive CJK/IME and mixed-DPI QA remains required.

Licences

Original Zvim code is Apache-2.0. GPUI is used as a pinned library; no Zed editor/application code is imported. Packaging preserves the upstream Neovim licence/runtime and collects available licence files and declared licence expressions for resolved Rust dependencies. Release maintainers must review that inventory when updating dependencies. See third-party notes.

Releases

Download builds from GitHub Releases. Versions follow SemVer, starting at 0.0.1. To publish a release:

  1. Update the package version in Cargo.toml and refresh Cargo.lock with cargo check.

  2. Update docs/RELEASE_NOTES.md, run the checks, and commit the changes.

  3. Push the commit and a matching annotated tag, for example:

    git push origin main
    git tag -a v0.0.1 -m 'Zvim v0.0.1'
    git push origin v0.0.1

GitHub Actions rejects tags that do not match the package version. The Apple Silicon macOS and Linux builds must pass before it publishes versioned archives and SHA256SUMS.txt. SemVer tags containing a prerelease suffix publish as GitHub prereleases. The release is staged as a draft until all assets upload; failed jobs can be rerun. The macOS bundle version is derived from Cargo's version. Public signing and notarisation are not configured.

For an optimised development launch, use just run-release. See performance measurements and reproduction for the rendering/cache benchmarks.

Use just run-clean to try stock Neovim without your custom configuration or plugins. It builds an optimised Zvim, prepares the bundled runtime, and opens a --clean editor with temporary Neovim config, data, cache and state directories. Close that window to finish the recipe and delete its temporary state. Your normal Neovim configuration and history are untouched; files you explicitly edit are still real files. Optional arguments work as usual, for example just run-clean ~/Projects/example or just run-clean "file with spaces.txt". Terminal shell configuration is not reset. On macOS, Zvim GUI preferences remain shared with your normal app; on Linux, the temporary XDG config directory also isolates Zvim preferences. Quit any existing Zvim instance first if you also want to test the newly built application binary.

Zvim uses the same running application for CLI, direct executable, and just run launches. Quit the existing app before trying a rebuilt binary or an updated app. Older releases without launch handoff must be closed once when switching to this version. The app currently exits after its last window closes. See application instance handling.

About

A focused Neovim desktop frontend built with GPUI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages