A cross-platform dmenu-like application launcher built with Zig and SDL3.
- Fast and lightweight menu selection interface
- Fuzzy matching - Type letters and they can match anywhere (e.g., "abc" matches "a_b_c")
- Case-insensitive filtering - Search without worrying about caps (ASCII only)
- UTF-8 safe - Handles multi-byte characters correctly in input and items
- Multi-line display - See up to 10 items at once with scrolling
- Keyboard-driven navigation with vim-style keybindings
- Cross-platform - Runs on Linux, Windows, and macOS without code changes
- Zero configuration - Works out of the box with sensible defaults
zmenu reads items from stdin and displays them in a menu:
# Simple example
echo -e "Apple\nBanana\nCherry\nDate\nEldberry" | zmenu
# From a file
cat items.txt | zmenu
# Use with find
find . -type f | zmenuThe selected item is written to stdout, making it easy to use in scripts:
selected=$(find ~/projects -maxdepth 1 -type d | zmenu)
cd "$selected"Navigation:
↑/k- Move selection up↓/j- Move selection downTab- Move to next itemShift+Tab- Move to previous itemPage Up- Jump up one page (10 items)Page Down- Jump down one page (10 items)Home- Jump to first itemEnd- Jump to last item
Input:
- Type any text - Fuzzy filter items (case-insensitive, UTF-8 safe)
Backspace- Delete last character (UTF-8 aware)Ctrl+U- Clear entire inputCtrl+W- Delete last word
Actions:
Enter- Select current item and output to stdoutEscape/Ctrl+C- Cancel without selection
- Top left: Input prompt with your query
- Top right: Filtered count / Total items
- Bottom right (if needed): Scroll indicator showing visible range
- Selected item has
>prefix and highlighted color
zmenu supports multiple color themes via the ZMENU_THEME environment variable:
# Use a specific theme (note: env var must be set for zmenu, not the input command)
echo -e "Apple\nBanana\nCherry" | ZMENU_THEME=dracula zmenu
# Works with any command
find . -type f | ZMENU_THEME=nord zmenu
# Or export first, then use normally
export ZMENU_THEME=gruvbox
seq 1 100 | zmenu
# Set as default in your shell config (~/.bashrc, ~/.zshrc, etc.)
export ZMENU_THEME=gruvboxCatppuccin Family (pastel themes):
- mocha (default) - Dark pastel with purple-gray background
- latte - Light pastel with lavender background
- frappe - Medium-dark pastel
- macchiato - Dark-medium pastel
Classic Themes:
- dracula - Popular dark theme with vibrant pink/cyan accents
- gruvbox - Retro warm dark theme with earthy tones
- nord - Cool arctic-inspired theme with blue accents
- solarized - Low-contrast dark theme
If ZMENU_THEME is not set or contains an invalid name, zmenu defaults to mocha.
Theme names are case-insensitive (NORD, nord, and NoRd all work).
- mise for version management
- No SDL3 system libraries required! - zig-sdl3 bundles everything
- Install mise (if not already installed):
curl https://mise.run | sh- Install Zig via mise:
mise install- Build the project:
mise run buildmise run build- Build the projectmise run run- Run the applicationmise run test- Run testsmise run clean- Clean build artifactsmise run check- Check code without building
zmenu/
├── .mise.toml # Mise configuration and tasks
├── build.zig # Build configuration
├── build.zig.zon # Dependencies (zig-sdl3)
├── src/
│ ├── main.zig # Main application (~740 lines)
│ └── theme.zig # Theme definitions (8 color themes)
└── README.md
The fuzzy matcher allows characters to appear in order but not necessarily consecutively:
Query: "abc"
Matches: "AaBbCc", "a_long_b_string_c", "AbsolutelyBigCat"
No Match: "cba", "acb"
Important: Case-insensitive matching only works for ASCII characters (a-z, A-Z). UTF-8 characters like é, ü, 日 are matched byte-for-byte:
- ✅ "Café" matches "caf" (ASCII part is case-insensitive)
- ❌ "café" does NOT match "cafe" (é ≠ e)
- ✅ "日本語" matches "日本" (exact UTF-8 bytes)
This design choice ensures UTF-8 safety without complex Unicode normalization.
This project follows a pragmatic approach:
- Simple, maintainable code - Single ~500-line file, easy to understand
- Minimal dependencies - Just zig-sdl3 (which bundles SDL3)
- Clear separation of concerns - Init, event handling, rendering, filtering
- Performance-conscious but readable - Optimized where it matters, clear elsewhere
- Helper methods - Navigation, text editing extracted to focused functions
The project includes automated tests covering critical functionality:
mise run test # Run all testsTest Coverage:
- Fuzzy matching (basic, case-insensitive, UTF-8 handling)
- UTF-8 boundary detection for safe truncation
- UTF-8 aware character deletion (backspace, word deletion)
- Color comparison
- Configuration validation
All tests use Zig's built-in testing framework and run on every build.
Supported Platforms: Linux, macOS, Windows
Building for different platforms:
# Native build
mise run build
# Cross-compile to Windows
zig build -Dtarget=x86_64-windows
# Cross-compile to macOS
zig build -Dtarget=x86_64-macos
# Cross-compile to Linux
zig build -Dtarget=x86_64-linux- No configuration file support yet
- No history/frecency tracking
- Window size is fixed (800x300)
Planned:
- Configuration file support (
~/.config/zmenu/config.toml) - History tracking with frecency scoring
- Multi-column layout option
- Preview pane for file paths
- Custom keybinding support
Maybe:
- Plugin system for custom filters
- Custom theme support (user-defined colors)
- Desktop file integration (.desktop files)
- Icon support
This is a learning project exploring Zig + SDL3.
Discussions, issues and PRs are welcomed!
This project is open source. Use it however you'd like.