pikl-menu

Pipe stuff in, pick stuff out.

pikl is an interactive menu that runs in your terminal or as a desktop overlay. You give it a list of things. It lets you filter, navigate, and select from that list. Then it tells you what was picked.

That's it. That's the whole idea. The power comes from what you connect it to.

What's a menu tool for?

You've got a folder of 400 wallpapers and you want to pick one. You've got 30 running processes and you want to kill a few. You've got a list of SSH hosts, git branches, emoji, colour schemes, docker containers, tmux sessions, browser bookmarks. Anything that's fundamentally "here's a list of stuff, let me search through it and pick something."

Normally you'd write a little script for each of these. Or scroll through terminal output. Or memorize names. A menu tool replaces all of that with one interaction pattern: a searchable, keyboard-driven list that feeds your choice back into whatever script or keybinding kicked it off.

Some things pikl can do:

# pick a file
ls ~/wallpapers/*.jpg | pikl

# pick a process to kill
ps aux | pikl | awk '{print $2}' | xargs kill

# browse git branches and check one out
git branch --list | pikl | xargs git checkout

# pick from structured data - understands JSON
cat bookmarks.json | pikl --format '{title} ({url})'

Every one of those is just a pipe. Your shell already knows how to glue these together. pikl is the interactive step in the middle.

Coming from rofi or wofi?

If you already use rofi, wofi, dmenu, or fzf, you know the pattern. pikl is in the same family, with a few things those tools don't do well or at all:

  • Structured items. Input is JSON lines, not just flat strings. Each item can have a label, sublabel, metadata fields, and an icon. Output includes the full item with selection context, so downstream tools don't have to re-parse anything. Plain text still works for simple cases.

  • Event hooks. Shell commands that fire on lifecycle events. When the cursor moves to a new item, when the user confirms, when they cancel. A wallpaper picker is just pikl with an on-hover hook that sets the wallpaper live as you scroll. Hooks are debounced and can feed data back into the menu.

  • Vim keybindings. Not just j/k. Normal mode with gg, G, H/M/L, Ctrl+D/U, marks, named registers, visual line select. Insert mode for filtering. Tab to switch modes, Escape to close.

  • Streaming. Items can arrive over time. The list populates progressively as a slow command produces output. Useful for recursive finds, network queries, or anything that takes a while.

  • Multi-select with registers. Select multiple items (Space to toggle, V for visual line mode). Group selections into named registers like vim yanking.

  • Filtering that goes beyond fuzzy. Fuzzy by default, but also exact ('term), regex (/pattern/), inverse (!term), and field-scoped (meta.size:2.4MB). Chain filters with |. Full PCRE2-style regex with lookaround and capture groups.

  • Table mode. Display items as aligned columns. Pipe in CSV or TSV and get a sortable, filterable table. Handy for process lists, log entries, or anything columnar.

  • Sessions. --session name remembers your filter, scroll position, and selections across invocations. Close pikl, reopen it, pick up where you left off.

  • IPC. While pikl is running, external scripts can push or remove items, change the filter, or read the current selection over a Unix socket. Makes live-updating dashboards possible.

  • Watched sources. Point pikl at a directory and the list updates as files are added or removed. No restart needed.

  • TUI and GUI. Runs in your terminal (ratatui) or as a floating overlay on Wayland (layer-shell) and X11. Auto-detects your environment.

Building

cargo build --workspace
cargo test --workspace

Requires Rust stable. The repo includes a rust-toolchain.toml that pins the version and pulls in rust-analyzer + clippy.

Current Status

Phases 1 through 3 are complete. pikl works as a TUI menu with:

  • Fuzzy, exact, regex, and inverse filtering with | pipeline chaining
  • Vim navigation (j/k, gg/G, Ctrl+D/U, Ctrl+F/B, modes)
  • Structured JSON I/O with sublabel, icon, group, and arbitrary metadata
  • Lifecycle hooks: exec (fire-and-forget) and handler (bidirectional)
  • Debounce and cancel-stale for rapid events
  • Format templates (--format '{label} - {sublabel}')
  • Field-scoped filtering (--filter-fields, meta.res:3840 syntax)
  • Headless scripting via --action-fd

Platform Support

Platform Frontend Status
Linux (Wayland) GUI (layer-shell overlay) Planned
Linux (X11) GUI Planned
Linux TUI Working
macOS GUI Planned
macOS TUI Working
Windows GUI Low Priority

License

MIT

Description
No description provided
Readme MIT 283 KiB
Languages
Rust 100%