Files
pikl/docs/DEVPLAN.md

270 lines
8.6 KiB
Markdown

# 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`.