diff --git a/README.md b/README.md index b1c9f5b..3a6cfb3 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,30 @@ or at all: overlay on Wayland (layer-shell) and X11. Auto-detects your environment. +## Install + +See [docs/guides/install.md](docs/guides/install.md) for full details. +The short version: + +```sh +cargo install pikl +``` + +Or from source: + +```sh +git clone https://github.com/maplecool/pikl-menu.git +cd pikl-menu +cargo install --path . +``` + +## Guides + +- **[Install](docs/guides/install.md):** cargo install, building from source +- **[App Launcher](docs/guides/app-launcher.md):** set up pikl as a + keyboard-driven app launcher on Hyprland, i3, macOS (Raycast), or + in a terminal + ## Building ```sh diff --git a/docs/DEVPLAN.md b/docs/DEVPLAN.md index c077ffa..5b6bb9e 100644 --- a/docs/DEVPLAN.md +++ b/docs/DEVPLAN.md @@ -275,6 +275,36 @@ navigate directories without spawning new processes. query, or the viewport to do a lookup after each filter pass. +- **Confirm-with-arguments (Shift+Enter).** Select an item + and also pass free-text arguments alongside it. Primary + use case: app launcher where you select `ls` and want to + pass `-la` to it. The output would include both the + selected item and the user-supplied arguments. + + Open questions: + - UX flow: does the filter text become the args on + Shift+Enter? Or does Shift+Enter open a second input + field for args after selection? The filter-as-args + approach is simpler but conflates filtering and + argument input. A two-step flow (select, then type + args) is cleaner but adds a mode. + - Output format: separate field in the JSON output + (`"args": "-la"`)? Second line on stdout? Appended to + the label? Needs to be unambiguous for scripts. + - Should regular Enter with a non-empty filter that + matches exactly one item just confirm that item (current + behaviour), or should it also treat any "extra" text + as args? Probably not, too implicit. + - Keybind: Shift+Enter is natural, but some terminals + don't distinguish it from Enter. May need a fallback + like Ctrl+Enter or a normal-mode keybind. + + This is a core feature (new keybind, new output field), + not just a launcher script concern. Fits naturally after + phase 4 (multi-select) since it's another selection + mode variant. The launcher script would assemble + `{selected} {args}` for execution. + ## Future Ideas (Unscheduled) These are things we've talked about or thought of. No @@ -306,3 +336,11 @@ commitment, no order. (optionally into a tmux session). Needs GUI frontend (phase 8) and frecency sorting. See `docs/use-cases/app-launcher.md`. + Setup guides: `docs/guides/app-launcher.md`. +- App description indexing: a tool or subcommand that + builds a local cache of binary descriptions from man + pages (`whatis`), .desktop file Comment fields, and + macOS Info.plist data. Solves the "whatis is too slow + to run per-keystroke" problem for the app launcher. + Could be a `pikl index` subcommand or a standalone + helper script. diff --git a/docs/guides/app-launcher.md b/docs/guides/app-launcher.md new file mode 100644 index 0000000..350c143 --- /dev/null +++ b/docs/guides/app-launcher.md @@ -0,0 +1,286 @@ +# App Launcher Setup + +Use pikl as a keyboard-driven application launcher. Bind +a hotkey, type a few characters, hit Enter, and your app +launches. This guide covers terminal, Hyprland, i3, and +macOS setups. + +For the design rationale and feature roadmap, see +[the app launcher use case](../use-cases/app-launcher.md). + +## Quick start: terminal + +The simplest version. No GUI, no hotkeys, just an alias: + +```sh +# ~/.bashrc or ~/.zshrc +alias launch='compgen -c | sort -u | pikl | xargs -I{} sh -c "{} &"' +``` + +Run `launch`, type to filter, Enter to run the selection +in the background. That's it. + +### With descriptions (experimental) + +You can pull one-line descriptions from man pages using +`whatis`. This is noticeably slow on systems with +thousands of binaries (it shells out per binary), so treat +it as a nice-to-have rather than the default: + +```sh +launch-rich() { + compgen -c | sort -u | while IFS= read -r cmd; do + desc=$(whatis "$cmd" 2>/dev/null | head -1 | sed 's/.*- //') + printf '{"label":"%s","sublabel":"%s"}\n' "$cmd" "$desc" + done | pikl --format '{label} {sublabel}' \ + | jq -r '.label' \ + | xargs -I{} sh -c '{} &' +} +``` + +This works, but it's slow. Caching the output of the +`whatis` loop to a file and refreshing it periodically +would make it usable day-to-day. A built-in indexing +solution is on the roadmap but not built yet. + +## Hyprland + +Hyprland is a first-class target. pikl uses +`iced_layershell` to render as a Wayland layer-shell +overlay, so it floats above your desktop like rofi does. + +### The launcher script + +Save this somewhere in your PATH (e.g. +`~/.local/bin/pikl-launch`): + +```sh +#!/bin/sh +# pikl-launch: open pikl as a GUI app launcher + +compgen -c | sort -u \ + | pikl --mode gui \ + | xargs -I{} sh -c '{} &' +``` + +```sh +chmod +x ~/.local/bin/pikl-launch +``` + +### Keybinding + +Add to `~/.config/hypr/hyprland.conf`: + +``` +bind = SUPER, SPACE, exec, pikl-launch +``` + +Reload with `hyprctl reload` or restart Hyprland. + +### With .desktop files (future) + +When pikl gains .desktop file parsing (or a helper +script emits structured JSON from XDG desktop entries), +you'll get proper app names, descriptions, and categories +instead of raw binary names. The keybinding and workflow +stay the same, only the input to pikl changes. + +## i3 + +i3 runs on X11, so pikl opens as an override-redirect +window rather than a layer-shell overlay. Same launcher +script, different keybinding syntax. + +### The launcher script + +Same `pikl-launch` script as Hyprland above. pikl +auto-detects X11 vs Wayland, so no changes needed. + +### Keybinding + +Add to `~/.config/i3/config`: + +``` +bindsym $mod+space exec --no-startup-id pikl-launch +``` + +Reload with `$mod+Shift+r`. + +### Notes + +- `--no-startup-id` prevents the i3 startup notification + cursor from spinning while pikl is open. +- If pikl doesn't grab focus automatically, you may need + to add an i3 rule: + ``` + for_window [class="pikl"] focus + ``` + +## macOS with Raycast + +Raycast is the best way to bind a global hotkey to pikl +on macOS. You create a script command that Raycast can +trigger from its search bar or a direct hotkey. + +### Prerequisites + +- [Raycast](https://raycast.com) installed +- pikl installed (`cargo install pikl`) +- pikl in your PATH (cargo's bin directory is usually + `~/.cargo/bin`, make sure it's in your shell PATH) + +### Create the script command + +Raycast script commands are shell scripts with a special +header. Create this file in your Raycast script commands +directory (usually `~/.config/raycast/scripts/` or +wherever you've configured it): + +**`pikl-launch.sh`:** + +```bash +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title App Launcher (pikl) +# @raycast.mode silent +# @raycast.packageName pikl + +# Optional parameters: +# @raycast.icon :rocket: + +# Scan /Applications for .app bundles, pipe to pikl, open the result +find /Applications ~/Applications -maxdepth 2 -name '*.app' 2>/dev/null \ + | sed 's|.*/||; s|\.app$||' \ + | sort -u \ + | pikl --mode gui \ + | xargs -I{} open -a "{}" +``` + +```sh +chmod +x pikl-launch.sh +``` + +The `silent` mode tells Raycast not to show any output +window. pikl handles its own GUI. + +### Assign a hotkey + +1. Open Raycast preferences (Cmd+,) +2. Go to Extensions +3. Find "App Launcher (pikl)" under Script Commands +4. Click the hotkey field and press your preferred combo + (e.g. Ctrl+Space, Opt+Space, etc.) + +### With structured JSON input + +For a richer experience with app metadata, you can parse +the `Info.plist` files inside .app bundles: + +```bash +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title App Launcher (pikl) +# @raycast.mode silent +# @raycast.packageName pikl + +for app in /Applications/*.app ~/Applications/*.app; do + [ -d "$app" ] || continue + name=$(basename "$app" .app) + # Pull the bundle identifier for metadata + bundle_id=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" \ + "$app/Contents/Info.plist" 2>/dev/null) + printf '{"label":"%s","meta":{"bundle":"%s","path":"%s"}}\n' \ + "$name" "$bundle_id" "$app" +done \ + | pikl --mode gui \ + | jq -r '.meta.path' \ + | xargs -I{} open "{}" +``` + +This version gives pikl structured data to work with. +When pikl gains frecency sorting, your most-launched apps +will float to the top automatically. + +### Alternative: skhd + +If you don't use Raycast, [skhd](https://github.com/koekeishiya/skhd) +is a standalone hotkey daemon: + +``` +# ~/.config/skhd/skhdrc +ctrl + space : pikl-launch-mac.sh +``` + +Where `pikl-launch-mac.sh` is the same script as above, +minus the Raycast header comments. + +## Customizing the launcher + +These tips apply to all platforms. + +### Filter fields + +If your input includes structured JSON with metadata, +tell pikl which fields to search: + +```sh +# Search both label and sublabel when filtering +pikl --filter-fields label,sublabel +``` + +### Hooks + +Run something when the launcher opens or when an item +is selected: + +```sh +pikl --mode gui \ + --on-select-exec 'jq -r .label >> ~/.local/state/pikl-launch-history' +``` + +This logs every launch to a history file, which could +feed into frecency sorting later. + +### Starting in normal mode + +If you prefer to land in vim normal mode (navigate first, +then `/` to filter): + +```sh +pikl --mode gui --start-mode normal +``` + +## What's not built yet + +A few things mentioned in this guide depend on features +that are still in development: + +- **GUI frontend** (phase 8): the `--mode gui` flag and + layer-shell/X11 rendering. Until this ships, you can use + the TUI versions in a drop-down terminal (like tdrop, + kitty's `--single-instance`, or a quake-mode terminal). +- **Frecency sorting:** tracking launch frequency and + boosting common picks. On the roadmap. +- **.desktop file parsing:** structured input from XDG + desktop entries with proper names, icons, and categories. + On the roadmap. +- **Description caching/indexing:** a way to build and + maintain a local index of binary descriptions so the + launcher can show what each app does without the slow + `whatis` loop. On the roadmap. + +## GNOME, KDE, and other desktops + +These aren't supported as app launcher environments right +now. Hyprland, i3, and macOS are the environments the dev +team uses daily, so that's where the effort goes. The +main blocker for GNOME on Wayland is the lack of +layer-shell support, which means pikl can't render as an +overlay the same way it does on wlroots-based compositors. + +If you use one of these environments and want to help, +PRs and discussion are welcome. diff --git a/docs/guides/install.md b/docs/guides/install.md new file mode 100644 index 0000000..f561c17 --- /dev/null +++ b/docs/guides/install.md @@ -0,0 +1,60 @@ +# Installing pikl + +## From crates.io (recommended) + +```sh +cargo install pikl +``` + +This builds the unified `pikl` binary with both TUI and +GUI frontends. You'll need a working Rust toolchain. If +you don't have one, [rustup](https://rustup.rs) is the +way to go. + +### TUI only + +If you only want the terminal interface and don't want to +pull in GUI dependencies: + +```sh +cargo install pikl --no-default-features --features tui +``` + +## From source + +```sh +git clone https://github.com/maplecool/pikl-menu.git +cd pikl-menu +cargo install --path . +``` + +This builds and installs the `pikl` binary into your +cargo bin directory (usually `~/.cargo/bin/`). + +## Package managers + +We'd like pikl to be available in package managers like +the AUR and Homebrew, but honestly haven't set that up +before and aren't sure when we'll get to it. TBD. + +If you package pikl for a distro or package manager, open +an issue and we'll link it here. + +## Verify it works + +```sh +echo -e "hello\nworld\ngoodbye" | pikl +``` + +You should see a filterable list in insert mode. Type to +filter, use arrow keys to navigate, Enter to select, +Escape to quit. The selected item prints to stdout. + +pikl starts in insert mode by default (type to filter +immediately). Press Ctrl+N to switch to normal mode for +vim-style navigation (j/k, gg, G, Ctrl+D/U). Ctrl+E +switches back to insert mode. You can also start in +normal mode with `--start-mode normal`. + +Note: Ctrl+I is not the same as Tab. pikl treats these +as distinct inputs.