doc: Add project design, dev plan, coding standards, and use cases.
This commit is contained in:
137
docs/use-cases/app-launcher.md
Normal file
137
docs/use-cases/app-launcher.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Use Case: App Launcher
|
||||
|
||||
A global-hotkey application launcher that replaces
|
||||
Spotlight, Alfred, or rofi's drun mode. Bind a key
|
||||
combo to open pikl as a GUI overlay, fuzzy-filter
|
||||
binaries from PATH, hit Enter to launch.
|
||||
|
||||
## What It Looks Like
|
||||
|
||||
1. User presses a global hotkey (Super on Linux,
|
||||
Cmd+Space on macOS).
|
||||
2. pikl opens as a centered overlay.
|
||||
3. The list is pre-populated with binaries from PATH
|
||||
(or parsed .desktop entries).
|
||||
4. User types to fuzzy-filter. Frecency sorting puts
|
||||
frequently launched apps at the top.
|
||||
5. Enter launches the selection. Escape dismisses.
|
||||
|
||||
This should feel instant. Sub-100ms to first paint.
|
||||
|
||||
## How It Works With pikl
|
||||
|
||||
### Basic Version
|
||||
|
||||
```sh
|
||||
# Collect PATH binaries, open pikl, run selection
|
||||
compgen -c | sort -u \
|
||||
| pikl --mode gui \
|
||||
| xargs -I{} sh -c '{} &'
|
||||
```
|
||||
|
||||
### With tmux Integration
|
||||
|
||||
On select, create a new tmux session running the
|
||||
chosen binary, then switch to it. Keeps everything
|
||||
inside tmux for window management.
|
||||
|
||||
```sh
|
||||
compgen -c | sort -u \
|
||||
| pikl --mode gui \
|
||||
--on-select 'cmd=$(cat);
|
||||
tmux new-session -d -s "$cmd" "$cmd" &&
|
||||
tmux switch-client -t "$cmd"'
|
||||
```
|
||||
|
||||
### With .desktop Files
|
||||
|
||||
Parse XDG .desktop entries for proper app names and
|
||||
icons instead of raw binary names:
|
||||
|
||||
```sh
|
||||
# Hypothetical helper that emits JSON items
|
||||
desktop-entries --json \
|
||||
| pikl --mode gui \
|
||||
--format '{icon} {label} <dim>{sublabel}</dim>'
|
||||
```
|
||||
|
||||
Input would look like:
|
||||
|
||||
```jsonl
|
||||
{"label": "Firefox", "sublabel": "Web Browser",
|
||||
"meta": {"exec": "firefox", "desktop": "firefox.desktop"},
|
||||
"icon": "/usr/share/icons/.../firefox.png"}
|
||||
{"label": "Alacritty", "sublabel": "Terminal",
|
||||
"meta": {"exec": "alacritty"},
|
||||
"icon": "/usr/share/icons/.../alacritty.png"}
|
||||
```
|
||||
|
||||
### Hyprland / Sway Keybinding
|
||||
|
||||
```
|
||||
bind = SUPER, SPACE, exec, app-launcher.sh
|
||||
```
|
||||
|
||||
On macOS, use skhd or a similar hotkey daemon.
|
||||
|
||||
### Fallback: TUI Mode
|
||||
|
||||
Before the GUI frontend exists, this works today in
|
||||
a drop-down terminal:
|
||||
|
||||
```sh
|
||||
# Bind to a hotkey that opens a terminal running:
|
||||
compgen -c | sort -u | pikl | xargs -I{} sh -c '{} &'
|
||||
```
|
||||
|
||||
## What pikl Features This Exercises
|
||||
|
||||
| Feature | Phase | How It's Used |
|
||||
|---|---|---|
|
||||
| GUI overlay | 8 | Centered layer-shell popup |
|
||||
| Fuzzy filtering | 2 | Filter thousands of binaries |
|
||||
| Frecency sorting | future | Boost frequently launched apps |
|
||||
| on-select hook | 3 | Spawn or exec the selection |
|
||||
| Structured I/O | 3 | .desktop metadata, icons |
|
||||
| Fast startup | 1 | Must feel instant |
|
||||
| Icon rendering | 8 | App icons in the list |
|
||||
| Groups | 9 | Categories (dev, media, etc) |
|
||||
|
||||
## Stretch Goals
|
||||
|
||||
- **Frecency:** track launch counts per binary, sort
|
||||
by frequency so your top apps float to the top.
|
||||
This is the single biggest UX improvement over a
|
||||
plain sorted list.
|
||||
- **Categories:** group items by type. Dev tools,
|
||||
browsers, media, system. Parsed from .desktop
|
||||
Categories field or manually tagged.
|
||||
- **Recent files:** a second section below apps
|
||||
showing recently opened files. Needs a separate
|
||||
data source (zeitgeist, custom tracker, etc).
|
||||
- **Calculator / snippets:** if the query doesn't
|
||||
match any app, treat it as a math expression or
|
||||
snippet expansion. Scope creep, but it is what
|
||||
makes launchers sticky.
|
||||
|
||||
## Platform Notes
|
||||
|
||||
- **Linux (Wayland):** layer-shell overlay. Global
|
||||
hotkey via the compositor (Hyprland bind, Sway
|
||||
bindsym). This is the primary target.
|
||||
- **Linux (X11):** override-redirect window. Global
|
||||
hotkey via xbindkeys or similar.
|
||||
- **macOS:** no layer-shell. Needs a borderless
|
||||
window with proper focus handling. Global hotkey
|
||||
via skhd or a native Swift shim. Secondary target.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- How to handle apps that need a terminal (e.g.
|
||||
htop, vim). Detect from .desktop Terminal=true and
|
||||
wrap in the user's preferred terminal emulator?
|
||||
- Should the launcher persist as a background process
|
||||
for faster re-open, or cold-start each time?
|
||||
Background process is snappier but uses memory.
|
||||
- PATH scanning: rescan on every open, or cache with
|
||||
inotify/FSEvents on PATH directories?
|
||||
145
docs/use-cases/wallpaper-picker.md
Normal file
145
docs/use-cases/wallpaper-picker.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Use Case: Wallpaper Picker
|
||||
|
||||
A keyboard-driven wallpaper picker for Hyprland that
|
||||
previews wallpapers live on the desktop as you browse.
|
||||
This is a first-class use case for pikl-menu: it
|
||||
exercises structured I/O, lifecycle hooks, debouncing,
|
||||
and the GUI overlay.
|
||||
|
||||
## What It Looks Like
|
||||
|
||||
1. User presses a keybind. pikl opens as a layer-shell
|
||||
overlay on the focused monitor.
|
||||
2. A list of wallpapers appears, filterable by typing.
|
||||
3. Navigating the list applies each wallpaper live to
|
||||
the desktop behind the overlay (via hyprpaper IPC).
|
||||
4. Enter confirms. The wallpaper stays, overlay closes.
|
||||
5. Escape cancels. The wallpaper reverts to whatever
|
||||
was set before the picker opened.
|
||||
|
||||
The overlay is semi-transparent so the user sees the
|
||||
real wallpaper behind it. No preview pane needed.
|
||||
|
||||
## How It Works With pikl
|
||||
|
||||
The wallpaper picker is not a standalone app. It is a
|
||||
shell script (or a small wrapper) that pipes items into
|
||||
pikl and uses hooks for the live preview behaviour.
|
||||
|
||||
### Basic Version
|
||||
|
||||
```sh
|
||||
# List wallpapers as JSON, pipe to pikl with hooks
|
||||
find ~/Pictures/walls -type f \
|
||||
-name '*.jpg' -o -name '*.png' -o -name '*.webp' \
|
||||
| jq -Rc '{label: (. | split("/") | last),
|
||||
sublabel: (. | split("/") | .[:-1]
|
||||
| join("/")), meta: {path: .}}' \
|
||||
| pikl --mode gui \
|
||||
--on-open 'hyprctl hyprpaper listactive
|
||||
| head -1 > /tmp/pikl-wall-orig' \
|
||||
--on-hover 'jq -r .meta.path
|
||||
| xargs -I{} hyprctl hyprpaper
|
||||
wallpaper "DP-4, {}"' \
|
||||
--on-cancel 'cat /tmp/pikl-wall-orig
|
||||
| cut -d: -f2 | xargs -I{}
|
||||
hyprctl hyprpaper wallpaper "DP-4, {}"' \
|
||||
--on-hover-debounce 100
|
||||
```
|
||||
|
||||
### With a Manifest
|
||||
|
||||
For a reusable setup, use a pikl manifest file:
|
||||
|
||||
```toml
|
||||
# ~/.config/pikl/wallpaper.toml
|
||||
|
||||
[hooks]
|
||||
on-open = "save-current-wallpaper.sh"
|
||||
on-hover = '''
|
||||
jq -r .meta.path \
|
||||
| xargs -I{} hyprctl hyprpaper \
|
||||
wallpaper "DP-4, {}"
|
||||
'''
|
||||
on-cancel = "restore-wallpaper.sh"
|
||||
on-hover-debounce = 100
|
||||
|
||||
[display]
|
||||
format = "{label} <dim>{sublabel}</dim>"
|
||||
```
|
||||
|
||||
```sh
|
||||
find ~/Pictures/walls -type f \( \
|
||||
-name '*.jpg' -o -name '*.png' \) \
|
||||
| to-json-items \
|
||||
| pikl --manifest ~/.config/pikl/wallpaper.toml \
|
||||
--mode gui
|
||||
```
|
||||
|
||||
### Modes
|
||||
|
||||
Different invocations cover different workflows:
|
||||
|
||||
**Browse:** open the picker, filter and select.
|
||||
|
||||
```sh
|
||||
find ~/Pictures/walls -type f ... | pikl ...
|
||||
```
|
||||
|
||||
**History:** pipe wallpaper history instead of a
|
||||
directory scan. Same hooks, different input source.
|
||||
|
||||
```sh
|
||||
cat ~/.local/state/wallpaper-history.jsonl \
|
||||
| pikl --manifest ~/.config/pikl/wallpaper.toml \
|
||||
--mode gui
|
||||
```
|
||||
|
||||
**Random:** no pikl needed, just a shell one-liner.
|
||||
|
||||
```sh
|
||||
find ~/Pictures/walls -type f ... \
|
||||
| shuf -n1 \
|
||||
| xargs -I{} hyprctl hyprpaper wallpaper "DP-4, {}"
|
||||
```
|
||||
|
||||
**Restore:** read the last state file, apply it.
|
||||
Also just a shell script, no pikl.
|
||||
|
||||
### Hyprland Keybindings
|
||||
|
||||
```
|
||||
bind = $mod CTRL, B, exec, wallpicker browse
|
||||
bind = $mod SHIFT, B, exec, wallpicker random
|
||||
bind = $mod, B, exec, wallpicker copy
|
||||
```
|
||||
|
||||
## What pikl Features This Exercises
|
||||
|
||||
| Feature | Phase | How It's Used |
|
||||
|---|---|---|
|
||||
| GUI overlay | 8 | Layer-shell on Wayland |
|
||||
| Structured I/O | 3 | JSON items with metadata |
|
||||
| on-hover hook | 3 | Live wallpaper preview |
|
||||
| on-open hook | 3 | Save current wallpaper |
|
||||
| on-cancel hook | 3 | Revert wallpaper |
|
||||
| Hook debouncing | 3 | Don't flood hyprctl |
|
||||
| Fuzzy filtering | 2 | Filter by filename |
|
||||
| Manifest files | 3 | Reusable config |
|
||||
| Watched sources | 7 | Live update on new files |
|
||||
| Sessions | 6 | Remember last position |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should pikl support image thumbnails in the item
|
||||
list? Useful here but adds complexity. Could be a
|
||||
GUI-only feature using the icon field.
|
||||
- Debounce timing: 100ms feels right for hyprctl IPC.
|
||||
Might need tuning for slower wallpaper daemons.
|
||||
- Multi-monitor: the on-hover hook needs to know which
|
||||
monitor to target. Could come from a CLI flag or
|
||||
from hyprctl's focused monitor.
|
||||
- History management: pikl sessions handle state, but
|
||||
wallpaper history (which wallpapers were set when)
|
||||
is the wrapper script's responsibility. Keep it
|
||||
simple: append to a JSONL file.
|
||||
Reference in New Issue
Block a user