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
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
- ๐จ Minimal UI - Non-intrusive hints that stay out of your way
- ๐ฌ IPC Control - Control the daemon via CLI commands
Consideration (NOT roadmap)
- Consider zIndex for hints on different layers? There might be multiple layers when we are considering
Menubar,DockandNotification bar - Better UI representation for action menu (maybe auto edge detection like tooltip in browser, that will place itself around the element based on the space available around it)
- Find a way to auto deduplicate hints that are targeting the same point
- Scroll areas are hardcoded from 1-9, do we ever need more than that and make it dynamic upon query? We can still use to cycle next tho
- Homerow supports
continuous clicks, is that something important? - Maybe forces to certain input source? I normally just use English, but maybe it's useful for others
- Electron apps does not show hints on the first activation. Maybe there's some manual work need to be done after setting up electron support?
- Cant actually make scroll works in Electron apps, what is the best approach? There are tons of random
AXGroupwhere some is scrollable and some is not... - Sort of working in Mision Control, but it still shows hints from the frontmost app. How can we know that we are in mission control and ignore the frontmost app?
- Firefox seems fine but still the first activation shows very minimal hint after setting it up, same as electron apps
- Research if there's a good way to implemet visual hint mode where we can select text? Doing this with accessibility seems a little hard and vague
- Add more actions to the menubar like
status,stop,start,current version - 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 - Macos bundle with
Neru.appfor easier installation? But we'll need a nice app icon for it tho...
๐ Installation
Homebrew (Recommended)
brew tap y3owk1n/tap
brew install y3owk1n/tap/neru
Nix Darwin
Click to expand Nix configuration
Add the following file to your overlay:
{
stdenv,
buildGoModule,
fetchFromGitHub,
installShellFiles,
writableTmpDirAsHomeHook,
lib,
nix-update-script,
}:
buildGoModule (finalAttrs: {
pname = "neru";
version = "1.2.0";
src = fetchFromGitHub {
owner = "y3owk1n";
repo = "neru";
tag = "v${finalAttrs.version}";
hash = "sha256-dzcpdVbl2eVU4L9x/qnXC5DOWbMCgt9/wEKroOedXoY=";
};
vendorHash = "sha256-x5NB18fP8ERIB5qeMAMyMnSoDEF2+g+NoJKrC+kIj+k=";
ldflags = [
"-s"
"-w"
"-X github.com/y3owk1n/neru/internal/cli.Version=${finalAttrs.version}"
];
# Completions
nativeBuildInputs = [
installShellFiles
writableTmpDirAsHomeHook
];
postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
installShellCompletion --cmd neru \
--bash <($out/bin/neru completion bash) \
--fish <($out/bin/neru completion fish) \
--zsh <($out/bin/neru completion zsh)
'';
passthru = {
updateScript = nix-update-script { };
};
})
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}/bin/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 build
# Install
mv ./bin/neru /usr/local/bin/neru
# Run
neru launch
โ ๏ธ Required Permissions
Grant Accessibility permissions:
- Open System Settings
- Navigate to Privacy & Security โ Accessibility
- Enable Neru
๐ฏ Quick Start
After installation and granting permissions:
# Start Neru
neru launch
# Try hint mode: Press Cmd+Shift+Space
# Try scroll mode: Press Cmd+Shift+J
๐ฎ Usage
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 (Action Selection)
Default hotkey: Cmd+Shift+A
- Press the hotkey
- Type the hint label
- Choose your action:
- Left click (default)
- Right click (context menu)
- Double click
- Middle click
- Go to a position (move mouse)
- Restore cursor to previous position (if enabled)
Press esc anytime to quit the hint selection.
Scroll Mode
Default hotkey: Cmd+Shift+J
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:
- 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)
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
activate_hint_mode = "Ctrl+F"
activate_hint_mode_with_actions = "Ctrl+G"
activate_scroll_mode = "Ctrl+S"
You shoul 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
ctrl - g : neru hints_action
ctrl - s : scroll
Read more about CLI Usage.
Enable Menu Bar Dock and Notification Center 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)
]
include_dock_hints = true # Also includes Mission Control
include_nc_hints = true # Notification popups
Restore cursor after click action
[general]
restore_pos_after_left_click = true
restore_pos_after_right_click = true
restore_pos_after_middle_click = true
restore_pos_after_double_click = 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"'
Per-App Role Configuration
Customize clickable/scrollable roles for specific apps. This is useful when certain apps use non-standard UI elements.
Example: In Mail.app, many AXStaticText elements should be clickable:
[accessibility]
[[accessibility.app_configs]]
bundle_id = "com.apple.mail"
additional_clickable_roles = ["AXStaticText"]
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.
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.electron_support]
enable = true # Enable Electron support (default)
# Add additional Electron apps
additional_bundles = [
"com.example.app", # Exact bundle ID
"com.company.prefix*" # Wildcard for multiple apps
]
Look and feel for hints
You can configure how you want those hints
[hints]
# Font size for hint labels
# Valid range: 6โ72
font_size = 12
# Font family (leave empty for system default)
font_family = ""
# Background color (hex format)
background_color = "#FFD700"
# Text color (hex format)
text_color = "#000000"
# Matched text color - color for characters that have been typed (hex format)
matched_text_color = "#0066CC"
# 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
# Border color (hex format)
border_color = "#000000"
# Opacity (0.0 to 1.0)
# Controls hint translucency; 1.0 is fully opaque
opacity = 0.95
# Action overlay colors (used when selecting click type)
# Background color (hex format)
action_background_color = "#66CCFF"
# Text color (hex format)
action_text_color = "#000000"
# Matched text color (hex format)
action_matched_text_color = "#003366"
# Border color (hex format)
action_border_color = "#000000"
# Opacity (0.0 to 1.0)
action_opacity = 0.95
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
# Minimum: 1. Increase for faster per-press scrolling.
scroll_speed = 50
# Highlight the active scroll area with a border
# When true, draws a border around the detected scroll container.
highlight_scroll_area = true
# Highlight border color (hex format)
highlight_color = "#FF0000"
# Highlight border width in pixels
highlight_width = 2
# Estimated page height in pixels (used for calculating Ctrl+D/U scroll distance)
# Increase if your app windows are taller; decrease for short panes.
page_height = 1200
# Half-page scroll multiplier for Ctrl+D/U (0.5 = 600px with default page_height)
# Valid range: (0, 1]
half_page_multiplier = 0.5
# Full-page scroll multiplier (not currently used)
# Valid range: (0, 1]
full_page_multiplier = 0.9
# Number of scroll events to send for gg/G commands
# Higher values create longer smooth scrolls to edges.
scroll_to_edge_iterations = 20
# Pixels to scroll per iteration for gg/G (total = iterations * delta)
# Increase for faster edge scrolling; tune with iterations above.
scroll_to_edge_delta = 5000
๐ฅ๏ธ 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 # Activate hint mode (direct click)
neru hints_action # Activate hint mode with action selection
neru scroll # Activate scroll mode
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
# 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
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.electron_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.electron_support] additional_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
โ โโโ 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/main.go
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
๐ Star History
If you find Neru useful, consider giving it a star on GitHub!
Made with โค๏ธ by y3owk1n