batipanel 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +603 -0
- package/VERSION +1 -0
- package/bin/cli.sh +15 -0
- package/bin/start.sh +131 -0
- package/completions/_batipanel.zsh +88 -0
- package/completions/batipanel.bash +77 -0
- package/config/tmux.conf +141 -0
- package/examples/config.sh +18 -0
- package/examples/custom-layout.sh +40 -0
- package/examples/multi-project.sh +26 -0
- package/examples/project.sh +9 -0
- package/install.sh +538 -0
- package/layouts/4panel.sh +37 -0
- package/layouts/5panel.sh +39 -0
- package/layouts/6panel.sh +38 -0
- package/layouts/7panel.sh +47 -0
- package/layouts/7panel_log.sh +46 -0
- package/layouts/8panel.sh +46 -0
- package/layouts/devops.sh +46 -0
- package/layouts/dual-claude.sh +44 -0
- package/lib/common.sh +32 -0
- package/lib/core.sh +92 -0
- package/lib/doctor.sh +149 -0
- package/lib/layout.sh +174 -0
- package/lib/project.sh +125 -0
- package/lib/server-docker.sh +159 -0
- package/lib/server-init.sh +178 -0
- package/lib/server.sh +260 -0
- package/lib/session.sh +98 -0
- package/lib/shell-setup.sh +254 -0
- package/lib/themes.sh +354 -0
- package/lib/validate.sh +66 -0
- package/lib/wizard.sh +100 -0
- package/package.json +48 -0
- package/uninstall.sh +112 -0
package/lib/themes.sh
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel themes - color theme system
|
|
3
|
+
|
|
4
|
+
# available themes
|
|
5
|
+
BATIPANEL_THEMES="default dracula nord gruvbox tokyo-night catppuccin rose-pine kanagawa"
|
|
6
|
+
|
|
7
|
+
# get all theme colors as space-separated values
|
|
8
|
+
# usage: _get_theme_colors <theme>
|
|
9
|
+
# returns: STATUS_BG STATUS_FG ACCENT ACCENT_FG WINDOW_BG WINDOW_FG
|
|
10
|
+
# WINDOW_ACTIVE_BG WINDOW_ACTIVE_FG BORDER MESSAGE_BG MESSAGE_FG
|
|
11
|
+
# PROMPT_USER_BG PROMPT_DIR_BG PROMPT_GIT_BG PROMPT_ERR_BG
|
|
12
|
+
_get_theme_colors() {
|
|
13
|
+
local theme="$1"
|
|
14
|
+
case "$theme" in
|
|
15
|
+
default)
|
|
16
|
+
echo "colour234 colour137 colour2 colour232 colour238 colour249 colour33 colour255 colour240 colour33 colour255 44 240 42 41"
|
|
17
|
+
;;
|
|
18
|
+
dracula)
|
|
19
|
+
echo "colour236 colour253 colour141 colour232 colour238 colour253 colour212 colour255 colour61 colour141 colour232 105 238 212 203"
|
|
20
|
+
;;
|
|
21
|
+
nord)
|
|
22
|
+
echo "colour236 colour253 colour110 colour232 colour238 colour253 colour67 colour255 colour240 colour67 colour255 67 238 110 131"
|
|
23
|
+
;;
|
|
24
|
+
gruvbox)
|
|
25
|
+
echo "colour235 colour223 colour208 colour232 colour237 colour223 colour214 colour235 colour239 colour208 colour235 172 239 142 167"
|
|
26
|
+
;;
|
|
27
|
+
tokyo-night)
|
|
28
|
+
echo "colour234 colour253 colour111 colour232 colour238 colour253 colour141 colour255 colour240 colour111 colour232 111 238 141 203"
|
|
29
|
+
;;
|
|
30
|
+
catppuccin)
|
|
31
|
+
echo "colour234 colour189 colour183 colour233 colour237 colour146 colour183 colour233 colour239 colour237 colour189 183 237 151 211"
|
|
32
|
+
;;
|
|
33
|
+
rose-pine)
|
|
34
|
+
echo "colour234 colour189 colour181 colour234 colour236 colour103 colour181 colour234 colour238 colour235 colour189 182 238 152 168"
|
|
35
|
+
;;
|
|
36
|
+
kanagawa)
|
|
37
|
+
echo "colour235 colour187 colour110 colour234 colour236 colour242 colour110 colour234 colour59 colour236 colour187 103 236 107 203"
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
return 1
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# theme description for display
|
|
46
|
+
_get_theme_desc() {
|
|
47
|
+
case "$1" in
|
|
48
|
+
default) echo "Green/blue (original)" ;;
|
|
49
|
+
dracula) echo "Purple/pink dark theme" ;;
|
|
50
|
+
nord) echo "Arctic blue palette" ;;
|
|
51
|
+
gruvbox) echo "Retro warm colors" ;;
|
|
52
|
+
tokyo-night) echo "Blue/purple night theme" ;;
|
|
53
|
+
catppuccin) echo "Pastel warm dark (Mocha)" ;;
|
|
54
|
+
rose-pine) echo "Soho vibes, warm rose" ;;
|
|
55
|
+
kanagawa) echo "Japanese ink painting" ;;
|
|
56
|
+
esac
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# list available themes
|
|
60
|
+
_list_themes() {
|
|
61
|
+
local current="${BATIPANEL_THEME:-default}"
|
|
62
|
+
echo ""
|
|
63
|
+
echo -e " ${BLUE}Available themes:${NC}"
|
|
64
|
+
echo ""
|
|
65
|
+
local name desc marker
|
|
66
|
+
for name in $BATIPANEL_THEMES; do
|
|
67
|
+
desc=$(_get_theme_desc "$name")
|
|
68
|
+
# pad name to 14 chars for alignment
|
|
69
|
+
local padded
|
|
70
|
+
padded=$(printf '%-14s' "$name")
|
|
71
|
+
if [ "$name" = "$current" ]; then
|
|
72
|
+
marker="${GREEN}*${NC}"
|
|
73
|
+
echo -e " ${marker} ${GREEN}${padded}${NC}${desc}"
|
|
74
|
+
else
|
|
75
|
+
echo -e " ${padded}${desc}"
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
78
|
+
echo ""
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# generate tmux theme overlay file
|
|
82
|
+
# shellcheck disable=SC2153 # BATIPANEL_HOME is set by core.sh
|
|
83
|
+
_generate_theme_conf() {
|
|
84
|
+
local theme="$1"
|
|
85
|
+
local conf_file="$BATIPANEL_HOME/config/theme.conf"
|
|
86
|
+
|
|
87
|
+
local colors
|
|
88
|
+
colors=$(_get_theme_colors "$theme") || return 1
|
|
89
|
+
|
|
90
|
+
# parse color values
|
|
91
|
+
local status_bg status_fg accent accent_fg win_bg win_fg
|
|
92
|
+
local win_active_bg win_active_fg border msg_bg msg_fg
|
|
93
|
+
# shellcheck disable=SC2086
|
|
94
|
+
read -r status_bg status_fg accent accent_fg win_bg win_fg \
|
|
95
|
+
win_active_bg win_active_fg border msg_bg msg_fg \
|
|
96
|
+
_prompt_user _prompt_dir _prompt_git _prompt_err <<< $colors
|
|
97
|
+
|
|
98
|
+
mkdir -p "$BATIPANEL_HOME/config"
|
|
99
|
+
cat > "$conf_file" << CONF
|
|
100
|
+
# batipanel theme: ${theme} (auto-generated — do not edit)
|
|
101
|
+
|
|
102
|
+
# status bar
|
|
103
|
+
set -g status-style "bg=${status_bg},fg=${status_fg}"
|
|
104
|
+
set -g status-left '#[fg=${accent_fg},bg=${accent},bold] #S #[fg=${accent},bg=${status_bg},nobold] '
|
|
105
|
+
set -g status-right '#[fg=${border},bg=${status_bg}]#[fg=${win_fg},bg=${border}] #{session_windows}W #{window_panes}P #[fg=${accent},bg=${border}]#[fg=${accent_fg},bg=${accent},bold] %H:%M %m-%d '
|
|
106
|
+
|
|
107
|
+
# window tabs
|
|
108
|
+
setw -g window-status-format '#[fg=${status_bg},bg=${win_bg}]#[fg=${win_fg},bg=${win_bg}] #I #W #[fg=${win_bg},bg=${status_bg}]'
|
|
109
|
+
setw -g window-status-current-format '#[fg=${status_bg},bg=${win_active_bg}]#[fg=${win_active_fg},bg=${win_active_bg},bold] #I #W #[fg=${win_active_bg},bg=${status_bg}]'
|
|
110
|
+
|
|
111
|
+
# pane borders
|
|
112
|
+
set -g pane-border-style "fg=${border}"
|
|
113
|
+
set -g pane-active-border-style "fg=${accent},bold"
|
|
114
|
+
set -g pane-border-format "#[fg=${border}] #{pane_index}:#{pane_title} "
|
|
115
|
+
|
|
116
|
+
# messages
|
|
117
|
+
set -g message-style "bg=${msg_bg},fg=${msg_fg},bold"
|
|
118
|
+
set -g message-command-style "bg=${msg_bg},fg=${msg_fg}"
|
|
119
|
+
CONF
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# generate bash prompt with theme colors
|
|
123
|
+
_generate_themed_prompt() {
|
|
124
|
+
local theme="$1"
|
|
125
|
+
local prompt_file="$BATIPANEL_HOME/config/bash-prompt.sh"
|
|
126
|
+
|
|
127
|
+
local colors
|
|
128
|
+
colors=$(_get_theme_colors "$theme") || return 1
|
|
129
|
+
|
|
130
|
+
local prompt_user prompt_dir prompt_git prompt_err
|
|
131
|
+
# shellcheck disable=SC2086
|
|
132
|
+
read -r _ _ _ _ _ _ _ _ _ _ _ \
|
|
133
|
+
prompt_user prompt_dir prompt_git prompt_err <<< $colors
|
|
134
|
+
|
|
135
|
+
# map ANSI code numbers to fg transition codes
|
|
136
|
+
# user bg → dir transition
|
|
137
|
+
local t_user_fg
|
|
138
|
+
case "$prompt_user" in
|
|
139
|
+
44) t_user_fg="34" ;; # blue
|
|
140
|
+
105) t_user_fg="105" ;; # purple (256-color)
|
|
141
|
+
67) t_user_fg="67" ;; # nord blue (256-color)
|
|
142
|
+
172) t_user_fg="172" ;; # gruvbox orange (256-color)
|
|
143
|
+
111) t_user_fg="111" ;; # tokyo blue (256-color)
|
|
144
|
+
183) t_user_fg="183" ;; # catppuccin mauve (256-color)
|
|
145
|
+
182) t_user_fg="182" ;; # rose-pine iris (256-color)
|
|
146
|
+
103) t_user_fg="103" ;; # kanagawa violet (256-color)
|
|
147
|
+
*) t_user_fg="34" ;; # fallback blue
|
|
148
|
+
esac
|
|
149
|
+
|
|
150
|
+
# git bg → end transition
|
|
151
|
+
local t_git_fg
|
|
152
|
+
case "$prompt_git" in
|
|
153
|
+
42) t_git_fg="32" ;; # green
|
|
154
|
+
212) t_git_fg="212" ;; # pink (256-color)
|
|
155
|
+
110) t_git_fg="110" ;; # frost (256-color)
|
|
156
|
+
142) t_git_fg="142" ;; # gruvbox green (256-color)
|
|
157
|
+
141) t_git_fg="141" ;; # purple (256-color)
|
|
158
|
+
151) t_git_fg="151" ;; # catppuccin green (256-color)
|
|
159
|
+
152) t_git_fg="152" ;; # rose-pine foam (256-color)
|
|
160
|
+
107) t_git_fg="107" ;; # kanagawa spring green (256-color)
|
|
161
|
+
*) t_git_fg="32" ;; # fallback green
|
|
162
|
+
esac
|
|
163
|
+
|
|
164
|
+
# error bg → dir transition
|
|
165
|
+
local t_err_fg
|
|
166
|
+
case "$prompt_err" in
|
|
167
|
+
41) t_err_fg="31" ;; # red
|
|
168
|
+
203) t_err_fg="203" ;; # salmon (256-color)
|
|
169
|
+
131) t_err_fg="131" ;; # nord red (256-color)
|
|
170
|
+
167) t_err_fg="167" ;; # gruvbox red (256-color)
|
|
171
|
+
211) t_err_fg="211" ;; # catppuccin red (256-color)
|
|
172
|
+
168) t_err_fg="168" ;; # rose-pine/kanagawa red (256-color)
|
|
173
|
+
*) t_err_fg="31" ;; # fallback red
|
|
174
|
+
esac
|
|
175
|
+
|
|
176
|
+
# determine if we need 256-color or basic ANSI for bg/fg
|
|
177
|
+
local bg_user bg_dir bg_git fg_git bg_err t_user_dir t_dir_git t_dir_end t_git_end t_err_dir
|
|
178
|
+
|
|
179
|
+
# user segment bg
|
|
180
|
+
if [ "$prompt_user" -le 47 ] 2>/dev/null; then
|
|
181
|
+
bg_user="\\[\\e[${prompt_user}m\\]"
|
|
182
|
+
else
|
|
183
|
+
bg_user="\\[\\e[48;5;${prompt_user}m\\]"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# dir segment bg (always 256-color)
|
|
187
|
+
bg_dir="\\[\\e[48;5;${prompt_dir}m\\]"
|
|
188
|
+
|
|
189
|
+
# git segment bg
|
|
190
|
+
if [ "$prompt_git" -le 47 ] 2>/dev/null; then
|
|
191
|
+
bg_git="\\[\\e[${prompt_git}m\\]"
|
|
192
|
+
else
|
|
193
|
+
bg_git="\\[\\e[48;5;${prompt_git}m\\]"
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# error segment bg
|
|
197
|
+
if [ "$prompt_err" -le 47 ] 2>/dev/null; then
|
|
198
|
+
bg_err="\\[\\e[${prompt_err}m\\]"
|
|
199
|
+
else
|
|
200
|
+
bg_err="\\[\\e[48;5;${prompt_err}m\\]"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# git fg (text on git bg) — use black for light bg, white for dark
|
|
204
|
+
case "$prompt_git" in
|
|
205
|
+
42|142|151|152|107|214) fg_git="\\[\\e[30m\\]" ;; # black text
|
|
206
|
+
*) fg_git="\\[\\e[97m\\]" ;; # white text
|
|
207
|
+
esac
|
|
208
|
+
|
|
209
|
+
# transition: user → dir
|
|
210
|
+
if [ "$t_user_fg" -le 37 ] 2>/dev/null; then
|
|
211
|
+
t_user_dir="\\[\\e[${t_user_fg};48;5;${prompt_dir}m\\]"
|
|
212
|
+
else
|
|
213
|
+
t_user_dir="\\[\\e[38;5;${t_user_fg};48;5;${prompt_dir}m\\]"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# transition: dir → git
|
|
217
|
+
t_dir_git="\\[\\e[38;5;${prompt_dir};48;5;${prompt_git}m\\]"
|
|
218
|
+
if [ "$prompt_git" -le 47 ] 2>/dev/null; then
|
|
219
|
+
t_dir_git="\\[\\e[38;5;${prompt_dir};${prompt_git}m\\]"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# transition: dir → end
|
|
223
|
+
t_dir_end="\\[\\e[38;5;${prompt_dir}m\\]"
|
|
224
|
+
|
|
225
|
+
# transition: git → end
|
|
226
|
+
if [ "$t_git_fg" -le 37 ] 2>/dev/null; then
|
|
227
|
+
t_git_end="\\[\\e[${t_git_fg}m\\]"
|
|
228
|
+
else
|
|
229
|
+
t_git_end="\\[\\e[38;5;${t_git_fg}m\\]"
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# transition: err → dir
|
|
233
|
+
if [ "$t_err_fg" -le 37 ] 2>/dev/null; then
|
|
234
|
+
t_err_dir="\\[\\e[${t_err_fg};48;5;${prompt_dir}m\\]"
|
|
235
|
+
else
|
|
236
|
+
t_err_dir="\\[\\e[38;5;${t_err_fg};48;5;${prompt_dir}m\\]"
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
mkdir -p "$BATIPANEL_HOME/config"
|
|
240
|
+
cat > "$prompt_file" << PROMPT_EOF
|
|
241
|
+
#!/usr/bin/env bash
|
|
242
|
+
# batipanel bash prompt - theme: ${theme} (auto-generated)
|
|
243
|
+
|
|
244
|
+
__batipanel_prompt() {
|
|
245
|
+
local exit_code=\$?
|
|
246
|
+
|
|
247
|
+
# powerline arrow symbols (U+E0B0, U+E0B1)
|
|
248
|
+
local sep=\$'\\uE0B0'
|
|
249
|
+
|
|
250
|
+
# colors (generated from theme: ${theme})
|
|
251
|
+
local bg_user="${bg_user}"
|
|
252
|
+
local fg_user="\\[\\e[97m\\]"
|
|
253
|
+
local bg_dir="${bg_dir}"
|
|
254
|
+
local fg_dir="\\[\\e[97m\\]"
|
|
255
|
+
local bg_git="${bg_git}"
|
|
256
|
+
local fg_git="${fg_git}"
|
|
257
|
+
local bg_err="${bg_err}"
|
|
258
|
+
local fg_err="\\[\\e[97m\\]"
|
|
259
|
+
local reset="\\[\\e[0m\\]"
|
|
260
|
+
|
|
261
|
+
# transition colors
|
|
262
|
+
local t_user_dir="${t_user_dir}"
|
|
263
|
+
local t_dir_git="${t_dir_git}"
|
|
264
|
+
local t_dir_end="${t_dir_end}"
|
|
265
|
+
local t_git_end="${t_git_end}"
|
|
266
|
+
local t_err_dir="${t_err_dir}"
|
|
267
|
+
|
|
268
|
+
# segment 1: username (no hostname)
|
|
269
|
+
local ps=""
|
|
270
|
+
if [ "\$exit_code" -ne 0 ]; then
|
|
271
|
+
ps+="\${bg_err}\${fg_err} ✘ \${exit_code} "
|
|
272
|
+
ps+="\${t_err_dir}\${sep}"
|
|
273
|
+
fi
|
|
274
|
+
ps+="\${bg_user}\${fg_user} \\\\u "
|
|
275
|
+
|
|
276
|
+
# segment 2: working directory
|
|
277
|
+
ps+="\${t_user_dir}\${sep}"
|
|
278
|
+
ps+="\${bg_dir}\${fg_dir} \\\\w "
|
|
279
|
+
|
|
280
|
+
# segment 3: git branch (if in a repo)
|
|
281
|
+
local git_branch=""
|
|
282
|
+
if command -v git &>/dev/null; then
|
|
283
|
+
git_branch="\$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)"
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if [ -n "\$git_branch" ]; then
|
|
287
|
+
ps+="\${t_dir_git}\${sep}"
|
|
288
|
+
local git_icon=\$'\\uE0A0'
|
|
289
|
+
ps+="\${bg_git}\${fg_git} \${git_icon} \${git_branch} "
|
|
290
|
+
ps+="\${reset}\${t_git_end}\${sep}\${reset} "
|
|
291
|
+
else
|
|
292
|
+
ps+="\${reset}\${t_dir_end}\${sep}\${reset} "
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
PS1="\$ps"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
PROMPT_COMMAND="__batipanel_prompt"
|
|
299
|
+
PROMPT_EOF
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
# apply theme: generate files, persist config, live reload
|
|
303
|
+
_apply_theme() {
|
|
304
|
+
local theme="$1"
|
|
305
|
+
|
|
306
|
+
# validate theme exists
|
|
307
|
+
if ! _get_theme_colors "$theme" >/dev/null 2>&1; then
|
|
308
|
+
echo -e "${RED}Unknown theme: ${theme}${NC}"
|
|
309
|
+
_list_themes
|
|
310
|
+
return 1
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
# generate tmux theme overlay
|
|
314
|
+
_generate_theme_conf "$theme"
|
|
315
|
+
|
|
316
|
+
# generate themed bash prompt
|
|
317
|
+
_generate_themed_prompt "$theme"
|
|
318
|
+
|
|
319
|
+
# persist to config.sh
|
|
320
|
+
if [ -f "$TMUX_CONFIG" ]; then
|
|
321
|
+
if grep -qF "BATIPANEL_THEME=" "$TMUX_CONFIG"; then
|
|
322
|
+
_sed_i "s|BATIPANEL_THEME=.*|BATIPANEL_THEME=\"$theme\"|" "$TMUX_CONFIG"
|
|
323
|
+
else
|
|
324
|
+
echo "BATIPANEL_THEME=\"$theme\"" >> "$TMUX_CONFIG"
|
|
325
|
+
fi
|
|
326
|
+
else
|
|
327
|
+
echo "BATIPANEL_THEME=\"$theme\"" > "$TMUX_CONFIG"
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
# live reload tmux if running
|
|
331
|
+
if command -v tmux &>/dev/null && tmux list-sessions &>/dev/null 2>&1; then
|
|
332
|
+
tmux source-file "$BATIPANEL_HOME/config/tmux.conf" 2>/dev/null || true
|
|
333
|
+
tmux source-file "$BATIPANEL_HOME/config/theme.conf" 2>/dev/null || true
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
BATIPANEL_THEME="$theme"
|
|
337
|
+
echo -e "${GREEN}Theme applied: ${theme}${NC}"
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# CLI entry point: b theme [name]
|
|
341
|
+
tmux_theme() {
|
|
342
|
+
local name="${1:-}"
|
|
343
|
+
|
|
344
|
+
case "$name" in
|
|
345
|
+
""|list)
|
|
346
|
+
echo -e " Current theme: ${GREEN}${BATIPANEL_THEME:-default}${NC}"
|
|
347
|
+
_list_themes
|
|
348
|
+
echo " Change: b theme <name>"
|
|
349
|
+
;;
|
|
350
|
+
*)
|
|
351
|
+
_apply_theme "$name"
|
|
352
|
+
;;
|
|
353
|
+
esac
|
|
354
|
+
}
|
package/lib/validate.sh
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel validation - tmux version, terminal size, input validation
|
|
3
|
+
|
|
4
|
+
# tmux version check (2.6+ required)
|
|
5
|
+
check_tmux_version() {
|
|
6
|
+
if ! command -v tmux &>/dev/null; then
|
|
7
|
+
echo -e "${RED}tmux is not installed${NC}"
|
|
8
|
+
case "$(uname -s)" in
|
|
9
|
+
Darwin) echo " Install: brew install tmux" ;;
|
|
10
|
+
Linux)
|
|
11
|
+
if command -v apt-get &>/dev/null; then
|
|
12
|
+
echo " Install: sudo apt-get install tmux"
|
|
13
|
+
elif command -v dnf &>/dev/null; then
|
|
14
|
+
echo " Install: sudo dnf install tmux"
|
|
15
|
+
elif command -v pacman &>/dev/null; then
|
|
16
|
+
echo " Install: sudo pacman -S tmux"
|
|
17
|
+
else
|
|
18
|
+
echo " Install tmux using your package manager"
|
|
19
|
+
fi
|
|
20
|
+
;;
|
|
21
|
+
*) echo " Install tmux for your OS" ;;
|
|
22
|
+
esac
|
|
23
|
+
return 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
local ver
|
|
27
|
+
ver=$(tmux -V | grep -oE '[0-9]+\.[0-9]+' | head -1)
|
|
28
|
+
if [[ -z "$ver" ]]; then
|
|
29
|
+
debug_log "Could not parse tmux version, continuing anyway"
|
|
30
|
+
return 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
local major minor
|
|
34
|
+
major="${ver%%.*}"
|
|
35
|
+
minor="${ver#*.}"
|
|
36
|
+
if (( major < 2 || (major == 2 && minor < 6) )); then
|
|
37
|
+
echo -e "${RED}tmux $ver is too old (2.6+ required)${NC}"
|
|
38
|
+
case "$(uname -s)" in
|
|
39
|
+
Darwin) echo " Upgrade: brew install tmux" ;;
|
|
40
|
+
*)
|
|
41
|
+
if command -v apt-get &>/dev/null; then
|
|
42
|
+
echo " Upgrade: sudo apt-get install --only-upgrade tmux"
|
|
43
|
+
elif command -v dnf &>/dev/null; then
|
|
44
|
+
echo " Upgrade: sudo dnf upgrade tmux"
|
|
45
|
+
else
|
|
46
|
+
echo " Upgrade tmux using your package manager"
|
|
47
|
+
fi
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
return 1
|
|
51
|
+
fi
|
|
52
|
+
debug_log "tmux version $ver OK"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# session name validation
|
|
56
|
+
validate_session_name() {
|
|
57
|
+
local name="$1"
|
|
58
|
+
if [[ -z "$name" ]]; then
|
|
59
|
+
echo -e "${RED}Session name is required${NC}"
|
|
60
|
+
return 1
|
|
61
|
+
fi
|
|
62
|
+
if ! [[ "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
63
|
+
echo -e "${RED}Session name may only contain letters, numbers, _ and -: $name${NC}"
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
}
|
package/lib/wizard.sh
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel setup - first-run detection and interactive wizard
|
|
3
|
+
|
|
4
|
+
is_first_run() {
|
|
5
|
+
# First run if no config.sh AND no registered projects
|
|
6
|
+
[[ ! -f "$TMUX_CONFIG" ]] || return 1
|
|
7
|
+
local found=0
|
|
8
|
+
for f in "$BATIPANEL_HOME"/projects/*.sh; do
|
|
9
|
+
[ -f "$f" ] && found=1 && break
|
|
10
|
+
done
|
|
11
|
+
(( found == 0 ))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run_wizard() {
|
|
15
|
+
echo ""
|
|
16
|
+
echo -e "${BLUE}=== Welcome to batipanel! ===${NC}"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Let's set up your workspace in 2 quick steps."
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
local screen_choice screen workflow_choice workflow layout
|
|
22
|
+
|
|
23
|
+
# Step 1: Screen size
|
|
24
|
+
echo -e "${GREEN}Step 1/2: What is your screen size?${NC}"
|
|
25
|
+
echo " 1) Small (laptop 13-14\")"
|
|
26
|
+
echo " 2) Large (external monitor) [default]"
|
|
27
|
+
echo " 3) Ultrawide"
|
|
28
|
+
echo ""
|
|
29
|
+
printf "Choose [1-3]: "
|
|
30
|
+
read -r screen_choice
|
|
31
|
+
screen_choice="${screen_choice:-2}"
|
|
32
|
+
case "$screen_choice" in
|
|
33
|
+
1) screen="small" ;;
|
|
34
|
+
3) screen="ultrawide" ;;
|
|
35
|
+
*) screen="large" ;;
|
|
36
|
+
esac
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
# Step 2: Workflow
|
|
40
|
+
echo -e "${GREEN}Step 2/2: What is your primary workflow?${NC}"
|
|
41
|
+
echo " 1) AI coding (Claude Code) [default]"
|
|
42
|
+
echo " 2) General development"
|
|
43
|
+
echo " 3) DevOps / infrastructure"
|
|
44
|
+
echo ""
|
|
45
|
+
printf "Choose [1-3]: "
|
|
46
|
+
read -r workflow_choice
|
|
47
|
+
workflow_choice="${workflow_choice:-1}"
|
|
48
|
+
case "$workflow_choice" in
|
|
49
|
+
2) workflow="general" ;;
|
|
50
|
+
3) workflow="devops" ;;
|
|
51
|
+
*) workflow="ai" ;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
# Layout mapping
|
|
55
|
+
case "${screen}:${workflow}" in
|
|
56
|
+
small:ai) layout="4panel" ;;
|
|
57
|
+
small:general) layout="4panel" ;;
|
|
58
|
+
small:devops) layout="devops" ;;
|
|
59
|
+
large:ai) layout="7panel" ;;
|
|
60
|
+
large:general) layout="6panel" ;;
|
|
61
|
+
large:devops) layout="devops" ;;
|
|
62
|
+
ultrawide:ai) layout="dual-claude" ;;
|
|
63
|
+
ultrawide:general) layout="7panel_log" ;;
|
|
64
|
+
ultrawide:devops) layout="devops" ;;
|
|
65
|
+
*) layout="7panel" ;;
|
|
66
|
+
esac
|
|
67
|
+
|
|
68
|
+
echo ""
|
|
69
|
+
echo -e "Selected layout: ${GREEN}${layout}${NC}"
|
|
70
|
+
echo ""
|
|
71
|
+
|
|
72
|
+
# Save config
|
|
73
|
+
mkdir -p "$BATIPANEL_HOME"
|
|
74
|
+
echo "DEFAULT_LAYOUT=\"$layout\"" > "$TMUX_CONFIG"
|
|
75
|
+
# shellcheck disable=SC2034
|
|
76
|
+
DEFAULT_LAYOUT="$layout"
|
|
77
|
+
echo -e "${GREEN}Configuration saved.${NC}"
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# Offer to register current directory as project
|
|
81
|
+
local cwd proj_name reg_answer
|
|
82
|
+
cwd=$(pwd)
|
|
83
|
+
proj_name=$(basename "$cwd" | tr -c 'a-zA-Z0-9_-' '-' | sed 's/-*$//')
|
|
84
|
+
|
|
85
|
+
echo -e "Register ${BLUE}${cwd}${NC} as project '${GREEN}${proj_name}${NC}'?"
|
|
86
|
+
printf "[Y/n] "
|
|
87
|
+
read -r reg_answer
|
|
88
|
+
if [[ "$reg_answer" != [nN] ]]; then
|
|
89
|
+
tmux_new "$proj_name" "$cwd"
|
|
90
|
+
echo ""
|
|
91
|
+
echo -e "${GREEN}Starting ${proj_name}...${NC}"
|
|
92
|
+
tmux_start "$proj_name" ""
|
|
93
|
+
else
|
|
94
|
+
echo ""
|
|
95
|
+
echo "No problem! Here's how to get started:"
|
|
96
|
+
echo " b new <name> <path> Register a project"
|
|
97
|
+
echo " b <project> Start a session"
|
|
98
|
+
echo " b help Show full help"
|
|
99
|
+
fi
|
|
100
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "batipanel",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "AI-powered terminal workspace manager — multi-panel tmux layouts with Claude Code, git, monitoring, and more",
|
|
5
|
+
"bin": {
|
|
6
|
+
"batipanel": "./bin/cli.sh"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "bash install.sh"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"tmux",
|
|
13
|
+
"claude",
|
|
14
|
+
"ai",
|
|
15
|
+
"terminal",
|
|
16
|
+
"workspace",
|
|
17
|
+
"multi-panel",
|
|
18
|
+
"developer-tools",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"author": "bati.ai (https://bati.ai)",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/batiai/batipanel.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/batiai/batipanel",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/batiai/batipanel/issues"
|
|
30
|
+
},
|
|
31
|
+
"os": ["darwin", "linux"],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=16"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"bin/",
|
|
37
|
+
"lib/",
|
|
38
|
+
"layouts/",
|
|
39
|
+
"config/",
|
|
40
|
+
"completions/",
|
|
41
|
+
"examples/",
|
|
42
|
+
"install.sh",
|
|
43
|
+
"uninstall.sh",
|
|
44
|
+
"VERSION",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"README.md"
|
|
47
|
+
]
|
|
48
|
+
}
|
package/uninstall.sh
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# uninstall.sh - remove batipanel configuration and aliases
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
BATIPANEL_HOME="${BATIPANEL_HOME:-$HOME/.batipanel}"
|
|
7
|
+
|
|
8
|
+
if [ -t 1 ]; then
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
NC='\033[0m'
|
|
13
|
+
else
|
|
14
|
+
RED='' GREEN='' YELLOW='' NC=''
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
echo ""
|
|
18
|
+
echo -e "${RED}batipanel uninstaller${NC}"
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
# portable sed -i
|
|
22
|
+
_sed_i() {
|
|
23
|
+
if [ "$(uname -s)" = "Darwin" ]; then
|
|
24
|
+
sed -i '' "$@"
|
|
25
|
+
else
|
|
26
|
+
sed -i "$@"
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# 1. stop active sessions
|
|
31
|
+
if command -v tmux &>/dev/null; then
|
|
32
|
+
for f in "$BATIPANEL_HOME"/projects/*.sh; do
|
|
33
|
+
[ -f "$f" ] || continue
|
|
34
|
+
name=$(basename "$f" .sh)
|
|
35
|
+
if tmux has-session -t "$name" 2>/dev/null; then
|
|
36
|
+
tmux kill-session -t "$name" 2>/dev/null || true
|
|
37
|
+
echo " Stopped session: $name"
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 2. remove tmux.conf source line
|
|
43
|
+
if [ -f "$HOME/.tmux.conf" ]; then
|
|
44
|
+
if grep -q "batipanel" "$HOME/.tmux.conf" 2>/dev/null; then
|
|
45
|
+
_sed_i '/# batipanel/d' "$HOME/.tmux.conf"
|
|
46
|
+
_sed_i '/batipanel/d' "$HOME/.tmux.conf"
|
|
47
|
+
echo " Cleaned ~/.tmux.conf"
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 3. remove shell aliases and completion source lines
|
|
52
|
+
for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
|
|
53
|
+
[ -f "$rc" ] || continue
|
|
54
|
+
if grep -q "batipanel" "$rc" 2>/dev/null; then
|
|
55
|
+
_sed_i '/# batipanel/d' "$rc"
|
|
56
|
+
_sed_i '/alias batipanel=/d' "$rc"
|
|
57
|
+
_sed_i '/alias b=.*batipanel/d' "$rc"
|
|
58
|
+
_sed_i '/completions\/batipanel/d' "$rc"
|
|
59
|
+
echo " Cleaned aliases and completions from $(basename "$rc")"
|
|
60
|
+
fi
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
# remove zsh completion from fpath
|
|
64
|
+
local_zsh_comp="${ZDOTDIR:-$HOME}/.zfunc"
|
|
65
|
+
if [ -f "$local_zsh_comp/_batipanel" ]; then
|
|
66
|
+
rm -f "$local_zsh_comp/_batipanel"
|
|
67
|
+
echo " Removed zsh completion"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# 4. stop server containers (if running)
|
|
71
|
+
if [ -f "$BATIPANEL_HOME/server/docker-compose.yml" ]; then
|
|
72
|
+
if command -v docker &>/dev/null && docker info &>/dev/null 2>&1; then
|
|
73
|
+
echo " Stopping server containers..."
|
|
74
|
+
if docker compose version &>/dev/null 2>&1; then
|
|
75
|
+
docker compose -f "$BATIPANEL_HOME/server/docker-compose.yml" down 2>/dev/null || true
|
|
76
|
+
elif command -v docker-compose &>/dev/null; then
|
|
77
|
+
docker-compose -f "$BATIPANEL_HOME/server/docker-compose.yml" down 2>/dev/null || true
|
|
78
|
+
fi
|
|
79
|
+
echo " Server containers stopped"
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# 5. remove ~/.batipanel/ (preserve projects if user wants)
|
|
84
|
+
if [ -d "$BATIPANEL_HOME" ]; then
|
|
85
|
+
project_count=0
|
|
86
|
+
for f in "$BATIPANEL_HOME"/projects/*.sh; do
|
|
87
|
+
[ -f "$f" ] && project_count=$((project_count + 1))
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
if (( project_count > 0 )) && [ -t 0 ]; then
|
|
91
|
+
echo ""
|
|
92
|
+
echo -e "${YELLOW}You have $project_count registered project(s).${NC}"
|
|
93
|
+
printf "Keep project configs in %s/projects/? [Y/n] " "$BATIPANEL_HOME"
|
|
94
|
+
read -r keep_projects
|
|
95
|
+
if [[ "$keep_projects" == [nN] ]]; then
|
|
96
|
+
rm -rf "$BATIPANEL_HOME"
|
|
97
|
+
echo " Removed $BATIPANEL_HOME (including projects)"
|
|
98
|
+
else
|
|
99
|
+
# remove everything except projects/
|
|
100
|
+
find "$BATIPANEL_HOME" -mindepth 1 -maxdepth 1 ! -name projects -exec rm -rf {} +
|
|
101
|
+
echo " Removed $BATIPANEL_HOME (kept projects/)"
|
|
102
|
+
fi
|
|
103
|
+
else
|
|
104
|
+
rm -rf "$BATIPANEL_HOME"
|
|
105
|
+
echo " Removed $BATIPANEL_HOME"
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
echo ""
|
|
110
|
+
echo -e "${GREEN}batipanel uninstalled.${NC}"
|
|
111
|
+
echo " Open a new terminal to apply changes."
|
|
112
|
+
echo ""
|