# 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_idlabel" 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._-' '_' }