1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
# Shared by the commands in bin/. POSIX sh -- macOS /bin/sh is bash 3.2.
# Sourced, never run. Every command acts through the herdr CLI, or the raw
# socket for the one method the CLI does not wrap (pane.focus by id).
set -u
H="${HERDR_BIN_PATH:-herdr}"
BIN="$(cd "$(dirname "$0")" && pwd)"
# tmux shows messages and errors ("can't find window: 0") on its status line;
# here they are herdr toasts.
say() {
"$H" notification show "$1" --sound none >/dev/null 2>&1 \
|| printf '%s\n' "$1" >&2
}
fail() {
say "$1"
exit 1
}
# One newline-delimited JSON request on the session's socket.
api() {
printf '%s\n' "$1" | nc -U -w 2 "${HERDR_SOCKET_PATH:?not run by herdr}"
}
# The pane the key was pressed in. Keybinding commands and popups get
# HERDR_ACTIVE_PANE_ID; plugin actions get it in their context JSON; anything
# else asks the server which pane is focused.
active_pane() {
_p="${HERDR_ACTIVE_PANE_ID:-}"
if [ -z "$_p" ] && [ -n "${HERDR_PLUGIN_CONTEXT_JSON:-}" ]; then
_p="$(printf '%s' "$HERDR_PLUGIN_CONTEXT_JSON" \
| jq -r '.focused_pane_id // empty')"
fi
if [ -z "$_p" ]; then
_p="$("$H" pane current | jq -r '.result.pane.pane_id // empty')"
fi
[ -n "$_p" ] || fail "no current pane"
printf '%s' "$_p"
}
# pane_info PANE FIELD -- e.g. workspace_id, tab_id.
pane_info() {
"$H" pane get "$1" | jq -r --arg k "$2" '.result.pane[$k] // empty'
}
# Panes of the tab holding PANE, in layout order: tmux's pane index order.
tab_panes() {
"$H" pane layout --pane "$1" | jq -r '.result.layout.panes[].pane_id'
}
# 0-based position of PANE in its tab: tmux's #P (pane-base-index 0).
pane_index() {
_i="$(tab_panes "$1" | grep -n -x -F "$1" | cut -d: -f1)"
printf '%s' "$((${_i:-1} - 1))"
}
# Tabs of WORKSPACE in tab-bar order, one "tab_id<TAB>label" per line.
workspace_tabs() {
"$H" tab list --workspace "$1" | jq -r '.result.tabs[] | [.tab_id, .label] | @tsv'
}
focus_pane() {
api "{\"id\":\"tmux-mode\",\"method\":\"pane.focus\",\"params\":{\"pane_id\":\"$1\"}}" >/dev/null
}
swap() {
"$H" pane swap --source-pane "$1" --target-pane "$2" >/dev/null
}
# One keypress, unbuffered, for (y/n) prompts.
read_key() {
_tty="$(stty -g)"
stty -icanon -echo min 1 time 0
_k="$(dd bs=1 count=1 2>/dev/null)"
stty "$_tty"
printf '%s' "$_k"
}
# Plugin state (marks, focus history) lives where herdr puts this plugin's,
# one directory per session: herdr shares that directory across sessions,
# and ids repeat between them -- every named session has its own w1. The
# session is the directory holding its socket (herdr for the default one).
state_dir() {
_d="${HERDR_PLUGIN_STATE_DIR:?run as a herdr plugin action}"
_d="$_d/$(basename "$(dirname "${HERDR_SOCKET_PATH:?not run by herdr}")")"
mkdir -p "$_d"
printf '%s' "$_d"
}
# Tab and workspace ids hold colons; keep file names plain.
key_of() {
printf '%s' "$1" | tr -c 'A-Za-z0-9._-' '_'
}
|