Files
pikl/docs/DEVPLAN.md

8.6 KiB

pikl-menu: Development Plan

Implementation order, phase definitions, and the running list of ideas. The DESIGN.md is the source of truth for what pikl-menu is. This doc is the plan for how and when we build it.

Principles

  • Get a working vertical slice before going wide on features
  • pikl-core stays rendering-agnostic. No GUI or TUI deps in the library.
  • Every feature gets tests in pikl-core before frontend work
  • Ship something usable early, iterate from real usage
  • Don't optimize until there's a reason to

Phase 1: Core Loop (TUI)

The minimum thing that works end-to-end.

Deliverables:

  • pikl-core: Item store (accepts JSON lines on stdin, plain text fallback)
  • pikl-core: Basic fuzzy filter on label
  • pikl-core: Single selection, Enter to confirm, Escape to cancel
  • pikl-core: Event bus with on-select and on-cancel hooks (shell commands)
  • pikl-tui: ratatui list renderer
  • pikl (CLI): Reads stdin, opens TUI, emits selected item as JSON on stdout
  • Exit codes: 0 = selected, 1 = cancelled, 2 = error
  • CI: Strict clippy, fmt, tests on Linux + macOS

Done when: ls | pikl works and prints the selected item.

Phase 1.5: Action-fd (Headless Mode)

Scriptable, non-interactive mode for integration tests and automation. Small enough to slot in before phase 2. It's about 100 lines of new code plus the binary orchestration for show-ui.

Deliverables:

  • --action-fd <N>: read action script from file descriptor N
  • Action protocol: plain text, one action per line (filter, move-up/down, confirm, cancel, etc.)
  • show-ui / show-tui / show-gui actions for headless-to-interactive handoff
  • Upfront validation pass: reject unknown actions, malformed args, actions after show-ui
  • --stdin-timeout <seconds>: default 30s in action-fd mode, 0 in interactive mode
  • Default viewport height of 50 in headless mode
  • Binary integration tests: spawn pikl with piped stdin + action-fd, assert stdout

Done when: echo -e "hello\nworld" | pikl --action-fd 3 with confirm on fd 3 prints "hello" to stdout.

Phase 2: Navigation & Filtering

Make it feel like home for a vim user. The filter system is the real star here: strategy prefixes, pipeline chaining, incremental caching.

Implementation order:

  1. Mode system in core (Insert/Normal, Ctrl+N/Ctrl+E to switch)
  2. Normal mode vim nav (j/k/gg/G/Ctrl+D/U/Ctrl+F/B)
  3. / in normal mode enters insert mode with filter focused
  4. Filter strategy engine (prefix parsing: ', !, !', /pattern/, !/pattern/)
  5. fancy-regex integration for the regex strategy
  6. Filter pipeline (| chaining between stages, incremental caching)

Deliverables:

  • Insert mode / normal mode (Ctrl+N to normal, Ctrl+E to insert)
  • Escape always cancels and exits, in any mode
  • Normal mode: j/k, gg, G, Ctrl+D/U, Ctrl+F/B
  • / in normal mode enters filter (insert) mode
  • --start-mode normal flag
  • Filter strategies via inline prefixes: fuzzy (default), exact ('term), inverse (!term, !'term), regex (/pattern/, !/pattern/)
  • fancy-regex integration (unlimited capture groups, lookaround)
  • Filter pipeline with |: each stage narrows the previous stage's output
  • Incremental filter caching: each stage caches item indices, only recomputes from the edited stage forward
  • Arrow keys to navigate within the filter text and edit earlier pipeline segments
  • \| escapes a literal pipe in the query
  • Case rules: fuzzy = smart case, exact = case-insensitive, regex = case-sensitive (use (?i) for insensitive)

Deferred to later:

  • H/M/L (viewport-relative jumps): nice-to-have, not essential
  • Filter syntax highlighting in the input field: deferred to theming work

Done when: You can navigate and filter a large list with vim muscle memory. 'log | !temp | /[0-9]+/ works as a pipeline.

Phase 3: Structured I/O & Hooks

The structured data pipeline.

Deliverables:

  • JSON line input parsing (label, sublabel, meta, icon, group)
  • JSON output with action context
  • Full hook lifecycle: on-open, on-close, on-hover, on-select, on-cancel, on-filter
  • Hook debouncing
  • Bidirectional hooks (hook stdout modifies menu state)
  • --format template strings for display
  • Field filters (meta.res:3840)
  • Filter scoping (--filter-fields)

Done when: The wallpaper picker use case works entirely through hooks and structured I/O.

Phase 4: Multi-Select & Registers

Power selection features.

Deliverables:

  • Space to toggle select, V for visual line mode
  • Multi-select output (multiple JSON lines)
  • Named registers ("a through "z)
  • Marks (m{a-z}, '{a-z})
  • --multi flag to enable multi-select mode

Done when: You can select multiple items, store them in registers, and get them all in the output.

Phase 5: Table Mode & CSV

Columnar data display.

Deliverables:

  • --columns flag for table layout
  • Auto-alignment
  • Column sorting keybinds
  • --input-format csv and --input-format tsv
  • Column-specific filtering

Done when: ps aux | pikl --input-format tsv --columns 1,10,2 renders a clean table.

Phase 6: Sessions & IPC

Persistence and external control.

Deliverables:

  • --session name for state persistence
  • Session state: filter, scroll position, selections, marks, registers
  • Session history log file
  • Unix socket IPC while running
  • IPC commands: push/remove/update items, set filter, read selection, close
  • Protocol: newline-delimited JSON

Done when: You can close and reopen a session and find your state intact. External scripts can push items into a running pikl instance.

Phase 7: Streaming & Watched Sources

Live, dynamic menus.

Deliverables:

  • Async/streaming stdin (items arrive over time, list updates progressively)
  • Streaming output (on-hover events emitted to stdout)
  • --watch path for file/directory watching
  • --watch-extensions filter
  • notify crate integration (inotify on Linux, FSEvents on macOS)

Done when: find / -name '*.log' | pikl populates progressively. A watched directory updates the list live.

Phase 8: GUI Frontend (Wayland + X11)

The graphical overlay.

Deliverables:

  • pikl-gui crate with iced
  • Wayland: layer-shell overlay via iced_layershell
  • X11: override-redirect window or EWMH hints
  • Auto-detection of Wayland vs X11 vs fallback to TUI
  • --mode gui / --mode tui override
  • Theming (TOML-based, a few built-in themes)
  • Image/icon rendering in item list
  • Preview pane (text + images)

Done when: pikl looks and feels like a native Wayland overlay with keyboard-first interaction.

Phase 9: Drill-Down & Groups

Hierarchical navigation.

Deliverables:

  • on-select hook returning {"action": "replace", "items": [...]} for drill-down
  • Backspace / h in normal mode to go back
  • Navigation history stack
  • Item groups with headers
  • Tab to cycle groups
  • za to collapse/expand groups

Done when: A file browser built on pikl-menu can navigate directories without spawning new processes.

Open Design Notes

  • Viewport cursor preservation on filter change. When the filter narrows and the highlighted item is still in the result set, keep it highlighted (like fzf/rofi). When it's gone, fall back to the top. Needs the filter to report whether a specific original index survived the query, or the viewport to do a lookup after each filter pass.

Future Ideas (Unscheduled)

These are things we've talked about or thought of. No commitment, no order.

  • Lua scripting frontend (mlua + LuaJIT): stateful/ conditional automation, natural follow-on to action-fd and IPC. Lua runtime is just another frontend pushing Actions and subscribing to MenuEvents. Deliberately deferred until after IPC (phase 6) so the event/action API is battle-tested before exposing it to a scripting language. See "Scripting Ladder" in DESIGN.md.
  • WASM plugin system for custom filter strategies
  • pcre2 feature flag for JIT-compiled regex
  • Frecency sorting (track selection frequency, boost common picks)
  • Manifest file format for reusable pikl configurations
  • Sixel / kitty graphics protocol support in TUI mode
  • Custom action keybinds (Ctrl+1 through Ctrl+9) with distinct exit codes
  • Accessibility / screen reader support
  • Sections as a first-class concept separate from groups
  • Network input sources (HTTP, WebSocket)
  • Shell completion generation (bash, zsh, fish)
  • Man page generation
  • Homebrew formula + AUR PKGBUILD
  • App launcher use case: global hotkey opens pikl as GUI overlay, fuzzy-filters PATH binaries, launches selection (optionally into a tmux session). Needs GUI frontend (phase 8) and frecency sorting. See docs/use-cases/app-launcher.md.