From 5fb19f10389b70ee2389755106092a9ef6c64598 Mon Sep 17 00:00:00 2001 From: David Moc Date: Sun, 24 May 2026 11:48:07 +0200 Subject: Go --- kitty/ssh_picker.sh | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100755 kitty/ssh_picker.sh (limited to 'kitty/ssh_picker.sh') diff --git a/kitty/ssh_picker.sh b/kitty/ssh_picker.sh new file mode 100755 index 0000000..6ad6e3a --- /dev/null +++ b/kitty/ssh_picker.sh @@ -0,0 +1,202 @@ +#!/usr/bin/env bash +# ~/.config/kitty/ssh_picker.sh +# Launched by kitty as an overlay window. +# Reads servers from ~/.ssh/config (Host entries) and lets +# you fuzzy-filter with arrow keys, then opens kitten ssh. +# +# Dependencies: none beyond bash + standard coreutils. +# Optional: fzf — if present it takes over the picker UI. + +set -euo pipefail + +# ------------------------------------------------------------ +# config — edit these or let them fall through to ~/.ssh/config +# ------------------------------------------------------------ +SSH_CONFIG="${HOME}/.ssh/config" +TERM_FALLBACK="xterm-256color" + +# ------------------------------------------------------------ +# gruvbox dark ANSI helpers +# ------------------------------------------------------------ +RESET=$'\e[0m' +BOLD=$'\e[1m' +DIM=$'\e[2m' + +GB_BG=$'\e[48;2;40;40;40m' # #282828 +GB_BG1=$'\e[48;2;60;56;54m' # #3c3836 +GB_BG2=$'\e[48;2;80;73;69m' # #504945 +GB_YELLOW=$'\e[38;2;250;189;47m' # #fabd2f +GB_BLUE=$'\e[38;2;131;165;152m' # #83a598 +GB_GREEN=$'\e[38;2;184;187;38m' # #b8bb26 +GB_RED=$'\e[38;2;251;73;52m' # #fb4934 +GB_ORANGE=$'\e[38;2;254;128;25m' # #fe8019 +GB_FG=$'\e[38;2;235;219;178m' # #ebdbb2 +GB_DIM=$'\e[38;2;168;153;132m' # #a89984 + +# ------------------------------------------------------------ +# parse servers +# ------------------------------------------------------------ +get_servers() { + local servers=() + + # pull Host entries from ~/.ssh/config (skip wildcards) + if [[ -f "$SSH_CONFIG" ]]; then + while IFS= read -r line; do + if [[ "$line" =~ ^[Hh]ost[[:space:]]+([^*?]+)$ ]]; then + local host="${BASH_REMATCH[1]// /}" + [[ -n "$host" ]] && servers+=("$host") + fi + done < "$SSH_CONFIG" + fi + + # fallback examples if config is empty + if [[ ${#servers[@]} -eq 0 ]]; then + servers=( + "contabo server" + ) + fi + + printf '%s\n' "${servers[@]}" +} + +# ------------------------------------------------------------ +# fzf path (preferred) +# ------------------------------------------------------------ +if command -v fzf &>/dev/null; then + selected=$(get_servers | fzf \ + --prompt=" ssh " \ + --pointer=">" \ + --marker="*" \ + --height=40% \ + --layout=reverse \ + --border=rounded \ + --info=inline \ + --color="dark,\ +bg:#282828,\ +bg+:#3c3836,\ +fg:#ebdbb2,\ +fg+:#fbf1c7,\ +hl:#fabd2f,\ +hl+:#fabd2f,\ +border:#504945,\ +prompt:#83a598,\ +pointer:#fe8019,\ +marker:#b8bb26,\ +spinner:#d3869b,\ +header:#a89984,\ +info:#a89984" \ + --header=" ctrl+c to cancel" \ + --preview="echo {} | xargs -I% sh -c 'grep -A10 \"^Host %$\" ~/.ssh/config 2>/dev/null || echo \"no config entry\"'" \ + --preview-window="right:40%:wrap" \ + 2>/dev/null) || exit 0 + + [[ -z "$selected" ]] && exit 0 + TERM="$TERM_FALLBACK" kitten ssh "$selected" + exit 0 +fi + +# ------------------------------------------------------------ +# built-in picker (no fzf) +# ------------------------------------------------------------ +mapfile -t SERVERS < <(get_servers) +TOTAL=${#SERVERS[@]} +SELECTED=0 +QUERY="" +FILTERED=("${SERVERS[@]}") + +draw() { + clear + printf '%s' "${GB_BG}" + printf '\n' + printf ' %s%s ssh picker%s\n' "${GB_YELLOW}${BOLD}" "" "${RESET}" + printf ' %s%s%s\n\n' "${GB_DIM}" "arrow keys to navigate, enter to connect, esc to quit" "${RESET}" + + # search bar + printf ' %ssearch:%s %s%s%s\n\n' \ + "${GB_DIM}" "${RESET}" \ + "${GB_FG}${BOLD}" "${QUERY}" "${RESET}" + + # server list + local i=0 + for srv in "${FILTERED[@]}"; do + if [[ $i -eq $SELECTED ]]; then + printf ' %s%s > %s%s\n' "${GB_BG1}${GB_ORANGE}${BOLD}" "" "$srv" "${RESET}" + else + printf ' %s %s%s\n' "${GB_DIM}" "$srv" "${RESET}" + fi + (( i++ )) + done + + printf '\n %s%s hosts found • install fzf for preview support%s\n' \ + "${GB_DIM}" "${#FILTERED[@]}" "${RESET}" +} + +filter() { + FILTERED=() + SELECTED=0 + for srv in "${SERVERS[@]}"; do + if [[ -z "$QUERY" || "${srv,,}" == *"${QUERY,,}"* ]]; then + FILTERED+=("$srv") + fi + done +} + +# hide cursor +tput civis +trap 'tput cnorm; clear' EXIT + +while true; do + draw + + # read a single keypress (handles escape sequences) + IFS= read -r -s -N1 key + + # check for escape sequences (arrow keys) + if [[ "$key" == $'\e' ]]; then + IFS= read -r -s -N1 -t 0.1 k2 || true + if [[ "$k2" == "[" ]]; then + IFS= read -r -s -N1 -t 0.1 k3 || true + case "$k3" in + A) # up + (( SELECTED > 0 )) && (( SELECTED-- )) + ;; + B) # down + (( SELECTED < ${#FILTERED[@]} - 1 )) && (( SELECTED++ )) + ;; + esac + else + # plain escape — quit + exit 0 + fi + continue + fi + + case "$key" in + $'\n'|$'\r') + [[ ${#FILTERED[@]} -eq 0 ]] && continue + tput cnorm + clear + host="${FILTERED[$SELECTED]}" + printf '%sconnecting to %s%s%s...\n\n' \ + "${GB_DIM}" "${GB_BLUE}${BOLD}" "$host" "${RESET}" + TERM="$TERM_FALLBACK" kitten ssh "$host" + exit 0 + ;; + $'\x7f'|$'\b') + # backspace + QUERY="${QUERY%?}" + filter + ;; + $'\x03') + # ctrl+c + exit 0 + ;; + *) + # printable character — append to query + if [[ "$key" =~ ^[[:print:]]$ ]]; then + QUERY+="$key" + filter + fi + ;; + esac +done -- cgit v1.2.3