doc: Add project design, dev plan, coding standards, and use cases.
This commit is contained in:
269
docs/DEVPLAN.md
Normal file
269
docs/DEVPLAN.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user