179 lines
5.6 KiB
Bash
Executable File
179 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# IPC remote control for pikl-menu.
|
|
#
|
|
# Connects to a running pikl instance over its Unix socket and
|
|
# lets you send commands interactively. You see the effects in
|
|
# the pikl terminal in real time.
|
|
#
|
|
# Usage:
|
|
# Terminal 1: echo -e "alpha\nbeta\ngamma\ndelta" | pikl --ipc --session demo
|
|
# Terminal 2: ./examples/ipc-remote.sh demo
|
|
#
|
|
# Requires: socat (for socket I/O)
|
|
#
|
|
# The session name is optional. Without it, you'll need to pass
|
|
# the full socket path as the first argument.
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Socket path ──────────────────────────────────────────
|
|
|
|
resolve_socket() {
|
|
local arg="${1:-}"
|
|
if [[ -z "$arg" ]]; then
|
|
echo "usage: ipc-remote.sh <session-name | socket-path>" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# If it looks like a path, use it directly
|
|
if [[ "$arg" == */* || "$arg" == *.sock ]]; then
|
|
echo "$arg"
|
|
return
|
|
fi
|
|
|
|
# Otherwise treat it as a session name
|
|
local uid
|
|
uid=$(id -u)
|
|
echo "/run/user/${uid}/pikl-${arg}.sock"
|
|
}
|
|
|
|
SOCK=$(resolve_socket "${1:-}")
|
|
|
|
if [[ ! -S "$SOCK" ]]; then
|
|
echo "socket not found: $SOCK" >&2
|
|
echo "" >&2
|
|
echo "make sure pikl is running with --ipc:" >&2
|
|
echo " echo -e 'alpha\nbeta\ngamma' | pikl --ipc --session demo" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v socat >/dev/null 2>&1; then
|
|
echo "socat is required but not installed." >&2
|
|
echo "install it with your package manager (pacman -S socat, brew install socat, etc.)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── IPC helpers ──────────────────────────────────────────
|
|
|
|
ipc_send() {
|
|
echo "$1" | socat - "UNIX-CONNECT:${SOCK}" 2>/dev/null
|
|
}
|
|
|
|
ipc_fire() {
|
|
# Fire-and-forget: send command, don't wait for response
|
|
echo "$1" | socat -t 0.1 - "UNIX-CONNECT:${SOCK}" 2>/dev/null || true
|
|
}
|
|
|
|
# ── Commands ─────────────────────────────────────────────
|
|
|
|
cmd_state() {
|
|
local resp
|
|
resp=$(ipc_send '{"action":"get_state","id":"q"}')
|
|
echo "$resp" | python3 -m json.tool 2>/dev/null || echo "$resp"
|
|
}
|
|
|
|
cmd_selection() {
|
|
local resp
|
|
resp=$(ipc_send '{"action":"get_selection","id":"q"}')
|
|
echo "$resp" | python3 -m json.tool 2>/dev/null || echo "$resp"
|
|
}
|
|
|
|
cmd_move_down() { ipc_fire '{"action":"move_down"}'; }
|
|
cmd_move_up() { ipc_fire '{"action":"move_up"}'; }
|
|
cmd_top() { ipc_fire '{"action":"move_to_top"}'; }
|
|
cmd_bottom() { ipc_fire '{"action":"move_to_bottom"}'; }
|
|
cmd_page_down() { ipc_fire '{"action":"page_down"}'; }
|
|
cmd_page_up() { ipc_fire '{"action":"page_up"}'; }
|
|
cmd_toggle() { ipc_fire '{"action":"toggle_select"}'; }
|
|
cmd_select_all() { ipc_fire '{"action":"select_all"}'; }
|
|
cmd_clear() { ipc_fire '{"action":"clear_selections"}'; }
|
|
cmd_confirm() { ipc_fire '{"action":"confirm"}'; }
|
|
cmd_cancel() { ipc_fire '{"action":"cancel"}'; }
|
|
|
|
cmd_filter() {
|
|
local text="${1:-}"
|
|
if [[ -z "$text" ]]; then
|
|
read -rp "filter text: " text
|
|
fi
|
|
ipc_fire "{\"action\":\"set_filter\",\"text\":\"${text}\"}"
|
|
}
|
|
|
|
cmd_add() {
|
|
local items="${1:-}"
|
|
if [[ -z "$items" ]]; then
|
|
echo "enter items to add (one per line, empty line to finish):"
|
|
local lines=()
|
|
while IFS= read -rp "> " line; do
|
|
[[ -z "$line" ]] && break
|
|
lines+=("\"${line}\"")
|
|
done
|
|
items=$(IFS=,; echo "${lines[*]}")
|
|
fi
|
|
ipc_fire "{\"action\":\"add_items\",\"items\":[${items}]}"
|
|
}
|
|
|
|
cmd_replace() {
|
|
echo "enter new items (one per line, empty line to finish):"
|
|
local lines=()
|
|
while IFS= read -rp "> " line; do
|
|
[[ -z "$line" ]] && break
|
|
lines+=("\"${line}\"")
|
|
done
|
|
local items
|
|
items=$(IFS=,; echo "${lines[*]}")
|
|
ipc_fire "{\"action\":\"replace_items\",\"items\":[${items}]}"
|
|
}
|
|
|
|
# ── Interactive loop ─────────────────────────────────────
|
|
|
|
show_help() {
|
|
cat <<'HELP'
|
|
|
|
pikl IPC remote control
|
|
───────────────────────
|
|
j / down move cursor down k / up move cursor up
|
|
g jump to top G jump to bottom
|
|
f <text> set filter F clear filter
|
|
space toggle select a select all
|
|
c clear selections s get state
|
|
S get selection
|
|
+ add items r replace all items
|
|
enter confirm selection q cancel menu
|
|
? show this help x exit remote
|
|
|
|
HELP
|
|
}
|
|
|
|
main() {
|
|
echo "connected to: $SOCK"
|
|
show_help
|
|
|
|
while true; do
|
|
read -rp "ipc> " -n1 key || break
|
|
echo ""
|
|
|
|
case "$key" in
|
|
j) cmd_move_down ;;
|
|
k) cmd_move_up ;;
|
|
g) cmd_top ;;
|
|
G) cmd_bottom ;;
|
|
f) read -rp "filter: " text; cmd_filter "$text" ;;
|
|
F) cmd_filter "" ;;
|
|
" ") cmd_toggle ;;
|
|
a) cmd_select_all ;;
|
|
c) cmd_clear ;;
|
|
s) cmd_state ;;
|
|
S) cmd_selection ;;
|
|
+) cmd_add ;;
|
|
r) cmd_replace ;;
|
|
"") cmd_confirm; echo "confirmed."; break ;;
|
|
q) cmd_cancel; echo "cancelled."; break ;;
|
|
x) echo "bye."; break ;;
|
|
"?") show_help ;;
|
|
*) echo "unknown key '$key'. press ? for help." ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
main
|