README
ยถ
Neru (็ทดใ)
Master your keyboard, refine your workflow
Navigate macOS without touching your mouse - keyboard-driven productivity at its finest ๐ฑ๏ธโจ๏ธ
Installation โข Quick Start โข Features โข Configuration โข CLI Usage
https://github.com/user-attachments/assets/4fc33733-d7de-4787-a4c8-c6cc2070e87e
https://github.com/user-attachments/assets/d49813e2-045e-4ccb-8120-39e7fbb22ef9
https://github.com/user-attachments/assets/ff6305a5-250a-465b-9fc2-45a08c172c4e
https://github.com/user-attachments/assets/53e9d63e-d578-45d1-a6b8-d6aec6f39a24
https://github.com/user-attachments/assets/bed2f3eb-aa41-4cbd-a18f-eac3916cf076
https://github.com/user-attachments/assets/57e9c87c-6b00-4c56-b657-54addf9032cd
https://github.com/user-attachments/assets/24b0de50-d17b-4c9b-8eae-41cf3e839dea
About Neru
Neru (็ทดใ) - A Japanese word meaning "to refine, polish, and master through practice" - embodies the philosophy of this tool: mastering keyboard-driven navigation to refine your macOS workflow.
Neru is a free, open-source alternative to Homerow, Shortcat, and Vimacโand it always will be. Built with Objective-C and Go, it's a working MVP that's proven itself capable of replacing Homerow in daily use for myself.
This project thrives on community contributions. I'm happy to merge pull requests that align with the project's goals, and I hope Neru stays current through collective effort rather than solo maintenance, and slowly dies off with hundreds of issues.
No GUI settings panel. Neru embraces configuration files over UI settings because:
- โ Config files are faster and more powerful, plus easier for dotfile management
- โ Easy to version control and share
- โ No maintenance burden for UI code
- โ Menubar provides quick access to essential commands
This is an intentional design choice to keep the project lean, maintainable, and focused on what matters: keyboard-driven productivity.
Key Advantages:
- ๐ Always free - No paywalls, no subscriptions, no "upgrade to pro"
- ๐ ๏ธ Power-user friendly - Text-based config is version-controllable and shareable
- ๐ค Community-owned - Your contributions shape the project's direction
- ๐ง Scriptable - CLI commands enable automation and integration
[!NOTE] This is a personal project maintained on a best-effort basis. Pull requests are more likely to be reviewed than feature requests or issues, unless I'm experiencing the same problem.
โจ Features
- ๐ฏ Hint Mode - Click any UI element using keyboard shortcuts with visual labels
- ๐ฌ Action Mode - Choose specific click actions (left, right, double, middle click)
- ๐ Scroll Mode - Vim-style scrolling (
j/k,gg/G, etc.) in any application - ๐ Universal Support - Works with native macOS apps, Electron apps, and Chrome/Firefox
- โก Performance - Built with native macOS APIs for instant response
- ๐ ๏ธ Highly Customizable - Configure hints, hotkeys, and behaviors via TOML
- ๐ซ App Exclusion - Exclude specific apps where Neru shouldn't activate
- ๐ฌ IPC Control - Control the daemon via CLI commands
Consideration (NOT roadmap)
- Find a way to auto deduplicate hints that are targeting the same point
- Homerow supports
continuous clicks, is that something important? - Test suites, but am lazy for it
- Implements launch agent with
start-serviceandstop-service? Though I am fine just doing it in my nix config directly - Better app icon and menubar icon
- Maybe make it a full-fledged vim mode? Just like Vimnav.spoon
- Hold and unhold action does not work in Finder.app... sobs
- Implement a text selection mode that focuses on AXStaticText elements
๐ Installation
Homebrew (Recommended)
brew tap y3owk1n/tap
brew install --cask y3owk1n/tap/neru
Nix Darwin
Click to expand Nix configuration
Add the following file to your overlay:
{
fetchzip,
gitUpdater,
installShellFiles,
stdenv,
versionCheckHook,
lib,
}:
let
appName = "Neru.app";
version = "1.5.0";
in
stdenv.mkDerivation {
pname = "neru";
inherit version;
src = fetchzip {
url = "https://github.com/y3owk1n/neru/releases/download/v${version}/neru-darwin-arm64.zip";
sha256 = "sha256-TcurBgkj3/vmOd40rBJzoVr2+rcOspR0QZ1zv9sN6O4=";
stripRoot = false;
};
nativeBuildInputs = [ installShellFiles ];
installPhase = ''
runHook preInstall
mkdir -p $out/Applications
mv ${appName} $out/Applications
cp -R bin $out
mkdir -p $out/share
runHook postInstall
'';
postInstall = ''
if ${lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) "true"}; then
installShellCompletion --cmd neru \
--bash <($out/bin/neru completion bash) \
--fish <($out/bin/neru completion fish) \
--zsh <($out/bin/neru completion zsh)
fi
'';
doInstallCheck = true;
nativeInstallCheckInputs = [
versionCheckHook
];
passthru.updateScript = gitUpdater {
url = "https://github.com/y3owk1n/neru.git";
rev-prefix = "v";
};
meta = {
mainProgram = "neru";
};
}
Create a custom module:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.neru;
configFile = pkgs.writeScript "neru.toml" cfg.config;
in
{
options = {
neru = with lib.types; {
enable = lib.mkEnableOption "Neru keyboard navigation";
package = lib.mkPackageOption pkgs "neru" { };
config = lib.mkOption {
type = types.lines;
default = ''
# Your config here
'';
description = "Config to use for {file} `neru.toml`.";
};
};
};
config = (
lib.mkIf (cfg.enable) {
environment.systemPackages = [ cfg.package ];
launchd.user.agents.neru = {
command =
"${cfg.package}/Applications/Neru.app/Contents/MacOS/Neru launch"
+ (lib.optionalString (cfg.config != "") " --config ${configFile}");
serviceConfig = {
KeepAlive = false;
RunAtLoad = true;
};
};
}
);
}
Enable in your configuration:
{
imports = [ ./path-to-neru-module.nix ];
neru.enable = true;
}
Manual Build
# Clone and build
git clone https://github.com/y3owk1n/neru.git
cd neru
just release # if you prefer cli only
just bundle # if you prefer app bundle
# You can then move to where you want
mv ./bin/neru /usr/local/bin/neru # if you prefer cli only (bin path depends on your setup)
mv ./build/Neru.app /Applications/Neru.app # if you prefer app bundle
# Run
open -a Neru # if you prefer app bundle
neru launch # if you prefer cli only
โ ๏ธ Required Permissions
Grant Accessibility permissions:
- Open System Settings
- Navigate to Privacy & Security โ Accessibility
- Enable Neru
๐ฏ Quick Start
After installation and granting permissions:
# Start Neru
open -a Neru # if you prefer app bundle
neru launch # if you prefer cli only
# Try hint mode: Press Cmd+Shift+Space
# Try scroll mode: Press Cmd+Shift+J
๐ฎ Usage
The following are the default hotkeys without needing to configure anything. You can literally bind any key to any action with the support of IPC.
Hint Mode (Direct Click)
Default hotkey: Cmd+Shift+Space
- Press the hotkey
- Clickable elements display hint labels (e.g., "AA", "AB", "AC") - You can also use
deletejust like in vimium if you type something wrong, instead of needing to reactivate the hint again. - Type the label to left click that element
- Restore cursor to previous position (if enabled)
Press esc anytime to quit the hint selection.
Hint Mode (Context Menu)
Default hotkey: Cmd+Shift+A
- Press the hotkey
- Type the hint label
- Choose your action:
- Left click (default)
- Right click (context menu)
- Double click (useful for opening files in finder)
- Triple click
- Middle click (new tab)
- Go to a position (move mouse)
- Hold mouse (start dragging)
- Release mouse (stop dragging)
- Restore cursor to previous position (if enabled)
Keys:
- l = left click (restoreable cursor)
- r = right click (restoreable cursor)
- d = double click (restoreable cursor)
- t = triple click (restoreable cursor)
- m = middle click (restoreable cursor)
- g = go to position
- h = hold mouse
- H = release mouse
Press esc anytime to quit the hint selection.
Scroll Mode
Default hotkey: Cmd+Shift+J
- Press the hotkey
- In the hint selection mode, type the hint label (will move the cursor to the position selected)
- At the same time, press
tabto enter scroll mode directly without selecting a hint - Start scrolling
Press esc anytime to quit the scroll mode.
Press tab to toggle between direct scroll and hint selection to move your cursor.
Vim-style navigation keys:
j/k- Scroll down/uph/l- Scroll left/rightCtrl+d/Ctrl+u- Page down/upgg- Jump to topG- Jump to bottomEsc- Exit scroll mode
Menu Bar
The โจ๏ธ icon in your menu bar provides quick access to:
- Version information
- Current Version
- Copy Version to Clipboard
- Status information
- Current Status
- Enable/Disable
- Actions
- Left Click
- Right Click
- Double Click
- Triple Click
- Mouse Up
- Mouse Down
- Middle Click
- Move Mouse
- Scroll
- Context Menu
- Quit Neru
โ๏ธ Configuration
Config File Location
Neru looks for configuration in:
- macOS Convention:
~/Library/Application Support/neru/config.toml - XDG Standard:
~/.config/neru/config.toml(Prefer this more so that we can put this in our dotfile) - Custom Path: Use
--configflag:neru launch --config /path/to/config.toml(Useful for nix users)
[!NOTE] If theres no config file, Neru will use the defaults.
Default Configuration
See configs/default-config.toml for all available options.
Common Configurations
Change the default keybindings
[hotkeys]
# all hotkeys can be disabled by either setting the key to "" or just commenting it out
"Ctrl+F" = "hints left_click"
"Ctrl+G" = "hints context_menu"
"Ctrl+S" = "hints scroll"
Add more bindings
"Ctrl+R" = "hints right_click"
"Ctrl+D" = "hints double_click"
"Ctrl+T" = "hints triple_click"
"Ctrl+H" = "hints middle_click"
"Ctrl+U" = "hints mouse_up"
"Ctrl+I" = "hints mouse_down"
"Ctrl+M" = "hints move_mouse"
Execute shell commands
# To run a shell command from a hotkey, prefix the value with `exec `:
"Ctrl+Alt+T" = "exec open -a Terminal"
"Ctrl+Alt+F" = "exec osascript -e 'display notification \"This is a test notification!\" with title \"Hello from Terminal\" subtitle \"Just testing ๐\"'"
# Note that you need to properly escape the quotes else it will break.
You should be also able to just clear the keybind and bind it with something like skhd or any similar tools, since we exposes commands in the cli through IPC. For example in skhd:
ctrl - f : neru hints left_click
ctrl - g : neru hints context_menu
ctrl - s : neru hints scroll
Read more about CLI Usage.
Enable Menu Bar Dock and Notification Center Hints
Default is false, set to true to show these additional hints.
[general]
include_menubar_hints = true
# Do nothing if `include_menubar_hints` is false
additional_menubar_hints_targets = [
"com.apple.TextInputMenuAgent", # TextInput menu bar
"com.apple.controlcenter", # Control Center
"com.apple.systemuiserver", # SystemUIServer (e.g. Siri in menubar)
"com.somebundle.example", # You can add more here for the apps that you use and shows in menubar (e.g. Aerospace, Orbstack, ...)
]
include_dock_hints = true # Also includes Mission Control
include_nc_hints = true # Notification popups
Restore cursor after click action
Default is false, set to true to restore the cursor position after clicking.
[hints]
[hints.left_click_hints]
restore_cursor = true
[hints.right_click_hints]
restore_cursor = true
[hints.middle_click_hints]
restore_cursor = true
[hints.double_click_hints]
restore_cursor = true
[hints.triple_click_hints]
restore_cursor = true
App Exclusion
Exclude specific apps where Neru shouldn't activate. This will also unregister all the binded hotkeys when the app is focused so that in specific app, you can use the hotkey to do something else.
[general]
excluded_apps = [
"com.apple.Terminal",
"com.googlecode.iterm2",
"com.microsoft.rdc.macos",
"com.vmware.fusion"
]
Find an app's bundle ID:
osascript -e 'id of app "AppName"'
Global Role Configuration
You can customize clickable/scrollable roles for all apps.
[accessibility]
# These are the defaults, feel free to remove or add more as you see fit
clickable_roles = [
"AXButton",
"AXComboBox",
"AXCheckBox",
"AXRadioButton",
"AXLink",
"AXPopUpButton",
"AXTextField",
"AXSlider",
"AXTabButton",
"AXSwitch",
"AXDisclosureTriangle",
"AXTextArea",
"AXMenuButton",
"AXMenuItem",
"AXCell",
"AXRow",
]
# There are predefined heuristics checks for if an element should be clickable or not.
# Skip role checking for clickable elements (all elements will be considered clickable)
# Enable this if you know what you're doing
ignore_clickable_check = false
# These are the defaults, feel free to remove or add more as you see fit
scrollable_roles = [
"AXWebArea",
"AXScrollArea",
"AXTable",
"AXRow",
"AXColumn",
"AXOutline",
"AXList",
"AXGroup",
]
Per-App Role Configuration
Customize clickable/scrollable roles for specific apps. This is useful when certain apps use non-standard UI elements.
[accessibility]
[[accessibility.app_configs]]
bundle_id = "com.example.bundle"
additional_clickable_roles = ["AXStaticText"]
additional_scrollable_roles = ["AXGroup"]
ignore_clickable_check = true # This will override the global ignore_clickable_check
Example: in Chrome, we can add support for tabgroups:
[[accessibility.app_configs]]
bundle_id = "com.google.Chrome"
additional_clickable_roles = ["AXTabGroup"]
Global roles apply to all apps, while per-app roles are merged with global roles for specific applications.
Electron & Chrome Support
Neru includes built-in support for Electron apps and Chromium browsers. (Note that this is off by default)
Supported Electron apps (automatic):
- VS Code
- Windsurf
- Cursor
- Slack
- Spotify
- Obsidian
Supported Chromium browsers (automatic):
- Chrome
- Brave
- Helium
- Arc
Supported Firefox browsers (automatic):
- Firefox
- Zen
[!NOTE] You can still add your browsers to
additional_*_bundlesif they aren't detected automatically. Right now, chromium, firefox and electron apps are all under the same key, in future we might split them out.
Configuration:
[accessibility.additional_ax_support]
enable = true # Enable additional AX support (off by default)
# Add additional Electron apps (AXManualAccessibility)
additional_electron_bundles = [
"com.example.app", # Exact bundle ID
]
# Add additional Chromium apps (AXEnhancedUserInterface)
additional_chromium_bundles = [
"com.example.app", # Exact bundle ID
]
# Add additional Firefox apps (AXEnhancedUserInterface)
additional_firefox_bundles = [
"com.example.app", # Exact bundle ID
]
Look and feel for hints
You can configure how you want those hints
[hints]
# Characters used to build hint labels for alphabet style.
# - At least 2 characters; choose distinct, easy-to-type ones.
hint_characters = "asdfghjkl"
# Font size for hint labels
# Valid range: 6โ72
font_size = 12
# Font family (leave empty for system default)
font_family = ""
# Border radius (pixels)
# Non-negative integer
border_radius = 4
# Padding (pixels)
# Non-negative integer
padding = 4
# Border width (pixels)
# Non-negative integer
border_width = 1
# Opacity (0.0 to 1.0)
# Controls hint translucency; 1.0 is fully opaque
opacity = 0.95
# Example for left click hints
# There are other hints for right, middle, double, triple, mouse up, mouse down, move mouse, scroll, and context menu
# See the default configuration for more details
[hints.left_click_hints]
background_color = "#FFD700"
text_color = "#000000"
matched_text_color = "#737373" # Matched text color - color for characters that have been typed
border_color = "#000000"
restore_cursor = false
Scroll configuration
Note that this is a simple implementation for scrolling, not exactly working fine yet, help would be appreciated (especially in electron and chromium applications)
[scroll]
# Base scroll amount for j/k keys in pixels
scroll_step = 50
# Half-page scroll amount for Ctrl+D/U in pixels
scroll_step_half = 500
# Full-page scroll amount for gg/G in pixels
# Normally this will be an extremely large number
scroll_step_full = 1000000
# Highlight the active scroll area with a border
# When true, draws a border around the active app
# This is just for visual feedback, it doesn't affect the actual scrolling
highlight_scroll_area = true
# Highlight border color (hex format)
highlight_color = "#FF0000"
# Highlight border width in pixels
highlight_width = 2
๐ฅ๏ธ CLI Usage
Neru provides comprehensive CLI commands with IPC (Inter-Process Communication) for controlling the daemon.
Launch Daemon
neru launch # Use default config location
neru launch --config /path/to/config.toml # Use custom config
Control Commands
These require Neru to be running:
neru start # Resume Neru if paused
neru stop # Pause Neru (doesn't quit, just disables functionality)
neru status # Show current status (running/paused, current mode, config path)
Mode Activation
neru hints --help # Show help for hints mode
# Available modes:
neru hints left_click # Show hints and perform left click action
neru hints right_click # Show hints and perform right click action
neru hints double_click # Show hints and perform double click action
neru hints triple_click # Show hints and perform triple click action
# Note that you probably will never use `mouse_up` directly, just including in the CLI for completeness
# `mouse_up` will be called after selected a hint from `mouse_down`.
neru hints mouse_up # Show hints and perform mouse up action
neru hints mouse_down # Show hints and perform a mouse down action. After selected a hint, it will call mouse up to select a target to perform the `mouse_up`
neru hints middle_click # Show hints and perform middle click action
neru hints move_mouse # Show hints and move the mouse to the position, no clicking
neru hints scroll # Enter scroll mode
neru hints context_menu # Show hints and trigger context menu action
# Return to idle state
neru idle # Return to idle state
Example Workflow
# Start Neru
neru launch
# Check status in another terminal
neru status
# Output:
# Neru Status:
# Status: running
# Mode: idle
# Config: /Users/you/Library/Application Support/neru/config.toml
# Activate hints via CLI instead of hotkey
neru hints left_click
# Pause functionality temporarily
neru stop
# Resume
neru start
IPC Architecture
The CLI uses Unix domain sockets (/tmp/neru.sock) for:
- Fast, reliable communication between CLI and daemon
- Multiple concurrent CLI commands
- Proper error handling when daemon isn't running
๐ง Troubleshooting
Is App X Supported?
Most app should work out of the box (especially native apps), if it doesn't, probably adding to the list of accessibility.additional_ax_support might work.
Apps that work for sure (tested by me)
- Native macOS apps should all work, the following are known to work:
- Finder
- Safari
- Activity Monitor
- System Settings
- Notes
- Password
- VS Code
- Windsurf
- Helium Browser
- Firefox Browser
- Adobe Illustrator 2025
- Adobe Photoshop 2025
- Orbstack
Accessibility Permissions Not Working
- Open System Settings โ Privacy & Security โ Accessibility
- Remove Neru from the list if present
- Re-add Neru
- Restart Neru
Hints Not Appearing in Electron Apps
If hints don't appear in Electron app content (VS Code, Windsurf, etc.):
-
Verify Electron support is enabled (it's on by default):
[accessibility.additional_ax_support] enable = true -
Check logs for these messages:
"App requires Electron support""Enabled AXManualAccessibility"
-
If your app isn't in the built-in list, add it manually:
[accessibility.additional_ax_support] additional_electron_bundles = ["com.your.app"] additional_chromium_bundles = ["com.your.app"] additional_firefox_bundles = ["com.your.app"]
Viewing Logs
Log location: ~/Library/Logs/neru/app.log
Enable debug logging:
[logging]
log_level = "debug"
Neru Not Responding
# Check if daemon is running
neru status
# If not running, restart
neru launch
# If stuck, force quit and restart
pkill neru
neru launch
๐๏ธ Architecture
Neru is built with:
- Go - Core application logic, configuration, and CLI
- CGo/Objective-C - macOS Accessibility API integration
- Native macOS APIs - Overlay rendering and hotkey management
Project Structure
neru/
โโโ cmd/neru/ # Main entry point
โโโ internal/
โ โโโ accessibility/ # Accessibility API wrappers
โ โโโ appwatcher/ # App watcher with callbacks
โ โโโ electron/ # Electron manager (includes Chromium and Firefox)
โ โโโ hints/ # Hint generation and display logic
โ โโโ scroll/ # Scroll mode implementation
โ โโโ hotkeys/ # Global hotkey management
โ โโโ config/ # TOML configuration parsing
โ โโโ cli/ # CLI commands (Cobra-based)
โ โโโ ipc/ # IPC server/client for daemon control
โ โโโ bridge/ # CGo/Objective-C bridges
โโโ configs/ # Default configuration files
โโโ scripts/ # Build and packaging scripts
๐ป Development
Prerequisites
- Go 1.25+
- macOS development tools
- Just (command runner)
Building
just build # Development build (auto-detects version from git)
just release # Optimized release build
just build-version v1.0.0 # Build with custom version
just test # Run tests
just test-race # Run tests with race detection
just lint # Run linter
Version information is automatically injected at build time from git tags.
Manual build with custom version:
go build -ldflags="-s -w -X github.com/y3owk1n/neru/internal/cli.Version=v1.0.0" \
-o bin/neru ./cmd/neru
Testing
just test # Run unit tests
just test-race # Run tests with race detection
just lint # Run golangci-lint
๐ค Contributing
Contributions are welcome! Here's how to contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Test your changes (
just test && just lint) - Commit (
git commit -m 'Add amazing feature') - Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
Contribution Guidelines
- Write clear commit messages
- Add tests for new features
- Update documentation as needed
- Follow existing code style
- Keep PRs focused on a single change
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
Neru is inspired by these excellent projects:
- Homerow - Modern keyboard navigation for macOS
- Vimac - Vim-style keyboard navigation
- Shortcat - Keyboard productivity tool
- Vimium - Vim bindings for browsers
- Vifari - Vimium/Vimari for Safari without browser extension in pure Lua
๐ Star History
If you find Neru useful, consider giving it a star on GitHub!
Made with โค๏ธ by y3owk1n