Setting up a Neru config
Neru reads one TOML file, and every key in it has a documented default and a
platform column saying where it does anything. The work is choosing what to
change, then applying it correctly. Most keys reload on demand, a few need a
daemon restart, and a one-value change is faster through neru config set
than through the file. The reference is docs/CONFIGURATION.md in the repo,
man neru-config on any install, and neru docs config in a browser.
Where the reference lives
The user rarely has a checkout. Every install method ships the binary and man pages only. Resolve the docs in this order:
A checkout in the working directory:
docs/CONFIGURATION.mdexists.Man pages on macOS and Linux:
man neru-config-init,man neru-config-set,man neru-config-validate,man neru-services, andman neru-hintsor another mode page for the flags a hotkey may pass. On Windows,neru <command> --helpcarries the same text.The doc at the installed version, fetched from GitHub:
tag=$(neru --version | sed -n '1s/^Neru version //p' | cut -d- -f1) case $tag in v*.*.*) ;; *) tag=main ;; esac curl -fsSL "https://raw.githubusercontent.com/y3owk1n/neru/$tag/docs/CONFIGURATION.md"A release build prints its tag. A dev build prints
v1.2.3-14-gabcdef, which thecutmaps to the release it was built from.On Windows, where
man,sed, andcutare usually absent, the same fetch in PowerShell:$tag = (((neru --version)[0] -replace '^Neru version ', '') -split '-')[0] if ($tag -notmatch '^v\d+\.\d+\.\d+$') { $tag = 'main' } Invoke-RestMethod "https://raw.githubusercontent.com/y3owk1n/neru/$tag/docs/CONFIGURATION.md"neru docs configopens the same page in a browser on every platform, which is enough when the user rather than the agent will read it.
The file neru config init writes is fully commented and names every key,
so after step 2 below, the user's own file is the quickest reference.
docs/TIPS_TRICKS.md at the same URL has recipes to copy for the common
asks: Vimium-style click on select, auto-exit after click, restoring the
cursor after a mode, drag with any button, one key that cycles modes, and
handing all hotkeys to skhd or another daemon.
Steps
Check the install.
neru doctorruns without the daemon and reports config validity, socket health, permissions, and what this platform supports.neru statussays whether the daemon runs. On macOS, hints and actions need Accessibility, and thevisionandcontourstrategies need Screen Recording, both under System Settings, Privacy & Security. On Linux the user needs theinputgroup and a/dev/uinputudev rule, covered inLINUX_SETUP.md. Windows needs nothing beyond the install.Find or create the file. The first existing path wins:
--config,$XDG_CONFIG_HOME/neru/config.toml,%APPDATA%\neru\config.tomlon Windows,~/.config/neru/config.toml,~/.neru.toml, thenneru.tomlorconfig.tomlin the working directory. When none exists, runneru config init. It refuses to overwrite without--force, so never pass--forceover a file the user did not ask to replace.Look for
config.override.tomlbeside it.neru config setwrites there, and it wins over the base file on every start, so an edit to the base file that "does nothing" is often shadowed by an override.neru config reset <key>removes one, or delete the file and reload.Ask what they want, then edit only those sections. Leave everything else at defaults. Read the platform column before writing a key on Linux or Windows, and say so when a request needs a key the platform ignores.
- A global key.
[hotkeys], with a mode command and its flags as the value:"Primary+Shift+Space" = "hints --action left_click". The table merges over the defaults."<key>" = "__disabled__"drops one default, an empty[hotkeys]section drops them all, and--toggleon any mode makes the key open and close it. Overrides for one app go under a top-level[[app_configs]]entry with abundle_id. - A key inside a mode.
[hints.hotkeys],[grid.hotkeys], and the rest hold the keys that work while the mode is open, such as Shift+L for a click or Tab to cycle. Same merging rules. - Hints that miss elements. Run
neru hints --debugin the app first. A role missing from the sample goes intohints.clickable_roles, checked againstneru roles --explain. An app whose tree is empty getsstrategy = "vision"or"contour", globally or under[[hints.app_configs]]with itsbundle_id. The per-app table also takesadditional_clickable_roles,ignore_clickable_check, andlabel_direction, and it is the supported way to make one app behave, not a request for a code change. - Sequences and custom modes.
[macros]names a list of commands to run withneru macro <name>or from a hotkey.[modes]declares a mode with its own hotkey table and indicator. A built-in mode's name and the wordmodeare refused as names. - Look and feel.
[hints.ui],[grid.ui],[theme],[mode_indicator],[smooth_cursor],[held_repeat], and[sticky_modifiers]. Colours take#AARRGGBBor#RRGGBB.
For a single value, prefer
neru config set <key> <value>over editing. It applies immediately, persists to the override file, and needs the daemon. Chain interdependent keys with--no-reload, thenneru config reloadonce.neru config dumplists every dotted key. The per-app tables cannot be set this way, so those go in the file.- A global key.
Validate.
neru config validatemust pass before anything else. It runs without the daemon and parses the mode commands inside hotkeys, so a mistyped flag is caught here. Read its warnings: a flag the mode does not accept, a role the platform cannot name, or a key inert on this platform all load and then do nothing, which is the failure the user will report later.Apply it. With the daemon running,
neru config reloadre-reads the file. Nearly everything reloads, including hotkeys, modes, macros, and theme.[systray]needs a full restart:neru services restartfor the installed service, or quit andneru launchotherwise. Thenneru config dump | jq '.hints'or the section in question shows what the daemon holds, which confirms the key took effect.Prove it. Press the hotkey and watch the mode open. Run the macro. Open the app and check the hints appear. Do not declare done on a green validate alone.
What the reference does not make obvious
- Colours are alpha first.
#AARRGGBBor#RRGGBB, never#RRGGBBAA. - Hotkey mistakes split into warn and refuse. A bad flag, an orphaned
flag such as
--repeatwithout--action, or an inert key warns and loads. An unknown mode name, a reserved mode name under[modes], or a non-ASCII hint character refuses the whole file. - A bad config is rejected whole on reload. The previous config stays in place and the failure is logged. Validate before reloading so the user never runs on a stale config without knowing.
- There is no log file by default.
logging.disable_file_loggingistrue. To read why a reload failed or a hotkey did nothing, set it tofalse, restart, and read~/Library/Logs/neru/app.logon macOS,~/.local/state/neru/log/app.logon Linux, or%LOCALAPPDATA%\neru\log\app.logon Windows.log_level = "debug"shows per-keypress detail. - Linux has no default global hotkeys. A fresh Linux config binds
nothing on purpose, to avoid colliding with the compositor. Either write
[hotkeys]or bindneru hintsand friends in the compositor's keymap. bundle_idworks on every platform. Per-app tables match a bundle identifier on macOS, a window class on Linux, and an executable name on Windows, under the same key. The reference names how to find each.- Runtime toggles are not config.
neru toggle-scroll-invertand its siblings last until restart. The lasting version is the matching key. - A stale Accessibility grant looks like a broken hotkey. After an update on macOS, remove Neru from the Accessibility list and add it back before touching the config.
Installing the service
When the user wants Neru at login, neru services install writes a launchd
agent on macOS, a systemd user unit on Linux, or a Task Scheduler task on
Windows, and starts it. neru services status confirms. It refuses when
nix-darwin or home-manager already manages Neru, and those users configure
the service in their Nix module instead.