Skip to content

zmenu

zmenu is a native macOS fuzzy menu for the command line, written in Zig. Pipe in a list, search it in a popup window and get the choice back on stdout.

Features

  • Reads items from stdin or streams them from a running process over a local socket
  • Fuzzy search with typo tolerant suggestions when few items match
  • Native AppKit window with translucent background, themeable colors, sizes and optional icons
  • Keyboard-first: Enter to accept, Esc to cancel, 1-9 shortcuts when few items match, Tab and arrow keys to move
  • Remembers the last query and selection for each menu
  • Settings by command-line flag, GMENU_* environment variable or config file
  • Minimal terminal mode for use without the window

Who it's for

Shell and script authors who want a dmenu or choose style picker on macOS, and tools that need a quick selection UI. It is the menu behind ComboSwitcher.

From GMenu to zmenu

zmenu started in 2023 as GMenu, a menu written in Go, and was rewritten in Zig as a native AppKit app. GMenu and zmenu are one product: zmenu still reads GMENU_* environment variables and the GMenu config locations, so existing setups keep working.

Install

Requires macOS, Zig 0.17.0 and Xcode Command Line Tools. Clone the repository, run just install to copy zmenu and zmenuctl to ~/.local/bin, then try printf "alpha\nbravo\n" | zmenu.

Source code on GitHub

FAQ

Is there a Homebrew formula or prebuilt download for zmenu?

No. The GitHub repository publishes no release binaries, so you build it from source with Zig 0.17.0 or newer. The build produces two programs: zmenu, the menu, and zmenuctl, which sends items to a running menu.

Does zmenu work on Linux or Windows?

No, zmenu is macOS only. The program links Apple's AppKit and Foundation frameworks, and terminal mode is part of the same program, so it needs a Mac too.

How does my script know whether the user picked something or cancelled?

When the user presses Enter, zmenu prints the chosen line to stdout and exits with code 0. Pressing Esc, or clicking away from the window, exits with code 2 and prints nothing, and empty stdin exits with code 1. If nothing matches, Enter prints the typed text unless you set GMENU_ACCEPT_CUSTOM_SELECTION=false.

Can I update the list while the menu is open?

Yes. Start zmenu with a --menu-id, then use zmenuctl --menu-id <id> set, append or prepend to change the items over a local Unix socket. You can also pass --follow-stdin to keep reading new lines from stdin, or --ipc-only to print the whole JSON item, including its id, when the user picks it.

Where does zmenu look for its config file, and does my old GMenu config still work?

It reads config.yaml from ~/.config/gmenu, ~/.gmenu or ~/Library/Application Support/gmenu, with an optional per-menu file in a subfolder named after the menu id. Run zmenu --init-config to write a default file to ~/.config/gmenu/config.yaml. Configs from the Go version work in the same locations, but the exact and regex search methods are gone, so switch to direct or fuzzy.

Questions or feedback about zmenu (Fuzzy Menu Selector for macOS)? Get in touch.