1.49.0 (2026-07-27)
Overview
This release is mostly about making runtime config stick and making the Linux and Windows ports behave like first-class citizens. neru config set is now persistent, the recursive grid can jump straight to a depth, Linux/Wayland input handling got a large stability pass (hotplug, suspend/resume, stuck keys, XKB options), and Windows finally ships a single binary that works as both CLI and daemon — with a proper uninstaller on all three platforms.
Migration Guide
Warning
This release changes Linux default hotkeys and the Windows binary layout. Both need a one-time action.
Linux: default global hotkeys are now empty (breaking)
Neru no longer ships default global hotkeys on Linux, because the old defaults collided with terminal and application shortcuts (Ctrl+Shift+C / copy, Ctrl+Shift+V / paste, …). neru config init no longer writes them either. You now have two options:
- Recommended — bind
neru hints,neru grid, etc. in your desktop environment / window manager keybindings - Or explicitly add a
[hotkeys]section back to your own config
Windows: reinstall to get the unified binary (breaking)
Windows previously shipped a GUI-subsystem binary, so no subcommand could print to the terminal it was typed in. It is now built for the console subsystem and releases the console when launched from a shortcut, so a single binary serves as both CLI and daemon. Re-run the installer to pick this up — typing neru now works, while the Start Menu and autostart entries still open no window.
New: persistent config set writes an override file
neru config set now persists across restarts by writing to a *.override.toml file next to your active config (config.toml → config.override.toml). The new load order is:
Defaults → config.toml → config.override.toml → Runtime (in-memory)
Nothing to do if you don't use neru config set — but if you have been using it, be aware that changes now survive restarts. Undo them with neru config reset <key>, or delete the override file.
Configuration & CLI
- Persistent
neru config set— runtime config changes are now written to a*.override.tomlfile alongside your config, so they survive daemon restarts. Your hand-written config is never touched neru config reset <key>— new command that removes a key from the override file, restoring your configured (or default) value--no-reloadfor bothsetandreset— defer the reload when you are changing several fields that validate against each other, then commit all of them at once:This makes toggleable grid layouts a single hotkey:neru config set --no-reload recursive_grid.grid_cols 3 neru config set --no-reload recursive_grid.grid_rows 3 neru config set recursive_grid.keys gcrhtnmwv # commits and reloads
[hotkeys] "Cmd+8" = [ "config set recursive_grid.grid_cols 3 --no-reload", "config set recursive_grid.grid_rows 3 --no-reload", "config set recursive_grid.keys gcrhtnmwv", ] "Cmd+9" = [ "config reset recursive_grid.grid_cols --no-reload", "config reset recursive_grid.grid_rows --no-reload", "config reset recursive_grid.keys", ]
- Friendlier startup notice — the scary Linux/Windows startup warning is replaced by a short, dynamic capability line, e.g.
⚠️ Neru is running on linux/wayland-wlroots. Run 'neru doctor' for platform capabilities.
Recursive Grid
--zoom-to-depthflag — drill straight down to a given depth at the current cursor position instead of stepping there key by key. Zero-based (0is the root), negative values are rejected, and values beyond the maximum depth are clamped instead of erroring:To bind it from inside the recursive grid,neru recursive_grid --zoom-to-depth 2
idlefirst:[recursive_grid.hotkeys] "Cmd+1" = [ "idle", "recursive_grid --zoom-to-depth 2" ]
Linux
Note
Linux implementations are mainly contributor driven, feel free to help improving it!
- Recursive grid animations — transition animations are now implemented on Linux for both Wayland and X11, matching the macOS behavior
- Persistent evdev capture with hotplug support — devices under
/dev/input/event*were being re-scanned on every mode activation, adding roughly 0.5s of input delay. The capture is now created once and reused, with an inotify watcher on/dev/input/keeping the device list in sync when keyboards are plugged or unplugged - No more stuck keys after grab — keys held across a grab could split their state between file descriptors, leaving libinput permanently convinced a key was down. Neru now waits (up to 5s) for held keys to be released before grabbing, suppresses the replayed press events for keys still held, and injects synthetic releases on disable
- XKB options respected — evdev scan codes are resolved through the compositor's
libxkbcommonkeymap instead of a hardcoded table, so options likecaps:swapescapeconfigured in your compositor are honored - Global hotkeys recover after sleep — hotkeys could become permanently unresponsive after suspend, because
/dev/input/event*had not been re-enumerated at the momentPrepareForSleep(false)fired. Capture creation now succeeds with zero devices so the hotplug watcher can pick them up as udev populates them, wake-up retries are bounded (5 attempts, 500ms backoff), and the health check no longer reports a failed listener as healthy - No more hangs or repeated permission prompts on resume — hotkey teardown now times out and recovers instead of hanging the daemon after deep sleep, and the RemoteDesktop session is no longer reset on every sleep cycle, so you are not re-prompted for Remote Control permission (input actions retry once if they hit a stale connection)
- 100% CPU busy-loop fixed — the evdev hotplug inotify watcher was created with
IN_NONBLOCK, so reads returnedEAGAINimmediately and spun the loop - Cursor synced before mode activation — the Wayland cursor-position cache is refreshed before a mode activates, so overlays and indicators anchor to the real pointer position instead of a stale one (often screen center) after you move the mouse manually
- AT-SPI init is now gated — accessibility activation is conditional on your hints configuration, and the state is always reset on startup and shutdown
Windows
- One binary for CLI and daemon — built for the console subsystem, releasing the console when started from a shortcut rather than a terminal.
neru <subcommand>now prints to the terminal you typed it in, while the Start Menu and autostart entries still open no window. The embedded icon is preserved - Proper Windows paths everywhere — PATH entries, shortcuts and registry values are now real Windows paths instead of mixed or POSIX-style ones
- PATH written through the registry — replaces
setx, which truncates at 1024 characters and flattens other tools' expandable entries - Optional Start Menu shortcut — what actually makes the app searchable from the taskbar; PATH alone does not
~/.config/neruis now discovered — in addition to%APPDATA%andXDG_CONFIG_HOME, so a config carried over from a Unix dotfiles repo works unchanged.%APPDATA%remains the write location
Install & Uninstall
just uninstallon all three platforms — undoes each install step in reverse. Config and logs are kept unless--purgeis passed, so-yalone can never delete a hand-tuned config;--purgelists the fully resolved directories before deleting anything. It deliberately leaves alone what it did not install — a binary on PATH that is not our symlink, and the Linuxinputgroup, which other tools may depend on
Docs & Internals
- Cross-platform feature parity tables —
docs/CROSS_PLATFORM.mdnow spells out per-feature support across macOS, Linux (X11 / Wayland variants) and Windows in much more detail - macOS integration tests — the main run loop is now serviced during integration tests, fixing flakiness in run-loop-dependent tests
Features
- config: add
--no-reloadforsetand addreset(#1083) (2920864) - config: persistent
config setviaoverridefiles (#1082) (6398fa5) - install: unify the Windows binary, add cross-platform uninstall (#1103) (fc8e1f0)
- linux: support animation for recursive grid (#1089) (b800247)
- recursive_grid: add
--zoom-to-depthflag (#1077) (3dfe895)
Bug Fixes
- linux,wayland: make evdev capture persistent with kb hotplug support (#1086) (e345661)
- linux,wayland: prevent hotpulgLoop busy wait (#1088) (c519875)
- linux,wayland: prevent stale evdev state from causing stuck keys after grab (#1087) (cdd466a)
- linux,wayland: sync cursor before mode activation (#1091) (f94f72a)
- linux,wayland: use compositor keymap to respect XKB options (#1097) (fb4fe8e)
- linux: disable default global hotkeys to avoid collisions (#1081) (3625f39)
- linux: gate AT-SPI accessibility init and ensure reset on quit (#1085) (986f064)
- linux: recover evdev global hotkeys post-sleep and fix health check (#1092) (154ae6c)
- linux: resolve evdev hotkey deadlock post-suspend and preserve RemoteDesktop session (#1094) (16be957)
- replace startup warning on launch with dynamic capability notice (#1084) (03b2d5c)