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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
|