betterterm 1.0.2 → 2.0.1
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/README.md +20 -1
- package/bin/cli.js +12 -0
- package/configs/ghostty.conf +29 -0
- package/configs/tmux.conf +31 -6
- package/package.json +1 -1
- package/scripts/betterterm-panes.sh +32 -0
- package/scripts/betterterm.sh +19 -7
- package/scripts/help-menu.sh +37 -0
- package/scripts/layout.sh +62 -0
- package/scripts/session-load-exec.sh +81 -0
- package/scripts/session-load.sh +19 -0
- package/scripts/session-save-exec.sh +48 -0
- package/scripts/session-save.sh +3 -0
- package/scripts/swap-pane.sh +23 -0
- package/scripts/toggle-yazi.sh +13 -7
package/README.md
CHANGED
|
@@ -22,6 +22,10 @@ The installer handles everything else (tmux, yazi, carbonyl).
|
|
|
22
22
|
- Sets up yazi as a toggleable file explorer with cd-to-terminal sync
|
|
23
23
|
- Adds an in-terminal browser (carbonyl) toggled via hotkey
|
|
24
24
|
- Patches your Ghostty config with Cmd-key shortcuts that map to tmux
|
|
25
|
+
- Layout presets (1-4 panes) with auto-launch of Claude Code
|
|
26
|
+
- Agent-aware status bar showing running AI agents per pane
|
|
27
|
+
- Project session save/restore
|
|
28
|
+
- Broadcast mode, pane zoom, and directional pane swapping
|
|
25
29
|
|
|
26
30
|
## Keybindings
|
|
27
31
|
|
|
@@ -40,6 +44,21 @@ The installer handles everything else (tmux, yazi, carbonyl).
|
|
|
40
44
|
| `Cmd+R` | Reload tmux config |
|
|
41
45
|
| `Ctrl+B, Tab` | Toggle between panes |
|
|
42
46
|
|
|
47
|
+
### Layout & Agents
|
|
48
|
+
|
|
49
|
+
| Key | Action |
|
|
50
|
+
|-----|--------|
|
|
51
|
+
| `Cmd+1` | Focus mode (1 pane + claude) |
|
|
52
|
+
| `Cmd+2` | Dual mode (2 panes + claude) |
|
|
53
|
+
| `Cmd+3` | Triple mode (3 panes + claude) |
|
|
54
|
+
| `Cmd+4` | Quad mode (4 panes + claude) |
|
|
55
|
+
| `Cmd+Z` | Zoom pane toggle |
|
|
56
|
+
| `Cmd+Shift+B` | Broadcast toggle (type in all panes) |
|
|
57
|
+
| `Cmd+S` | Save session |
|
|
58
|
+
| `Cmd+O` | Load session |
|
|
59
|
+
| `Cmd+Shift+Arrow` | Swap pane position |
|
|
60
|
+
| `Cmd+?` | Help menu (clickable) |
|
|
61
|
+
|
|
43
62
|
## Uninstall
|
|
44
63
|
|
|
45
64
|
The installer backs up any existing configs to `*.backup` files. To revert:
|
|
@@ -52,7 +71,7 @@ mv ~/.config/yazi/keymap.toml.backup ~/.config/yazi/keymap.toml
|
|
|
52
71
|
|
|
53
72
|
# Remove betterterm block from Ghostty config (between the marker comments)
|
|
54
73
|
# Remove scripts
|
|
55
|
-
rm ~/.local/bin/betterterm*.sh ~/.local/bin/toggle-*.sh ~/.local/bin/open-browser.sh ~/.local/bin/close-browser.sh ~/.local/bin/browser-nav.sh
|
|
74
|
+
rm ~/.local/bin/betterterm*.sh ~/.local/bin/toggle-*.sh ~/.local/bin/open-browser.sh ~/.local/bin/close-browser.sh ~/.local/bin/browser-nav.sh ~/.local/bin/layout.sh ~/.local/bin/session-*.sh ~/.local/bin/swap-pane.sh
|
|
56
75
|
```
|
|
57
76
|
|
|
58
77
|
## License
|
package/bin/cli.js
CHANGED
|
@@ -239,4 +239,16 @@ console.log(`
|
|
|
239
239
|
Cmd+R Reload tmux config
|
|
240
240
|
Ctrl+B, Tab Toggle between panes
|
|
241
241
|
Opt+Enter Send Enter to terminal
|
|
242
|
+
|
|
243
|
+
\x1b[1mLayout & Agents:\x1b[0m
|
|
244
|
+
Cmd+1 Focus mode (1 pane + claude)
|
|
245
|
+
Cmd+2 Dual mode (2 panes + claude)
|
|
246
|
+
Cmd+3 Triple mode (3 panes + claude)
|
|
247
|
+
Cmd+4 Quad mode (4 panes + claude)
|
|
248
|
+
Cmd+Z Zoom pane toggle
|
|
249
|
+
Cmd+Shift+B Broadcast toggle (type in all panes)
|
|
250
|
+
Cmd+S Save session
|
|
251
|
+
Cmd+O Load session
|
|
252
|
+
Cmd+Shift+Arrow Swap pane position
|
|
253
|
+
Cmd+? Help menu (clickable)
|
|
242
254
|
`);
|
package/configs/ghostty.conf
CHANGED
|
@@ -21,4 +21,33 @@ keybind = cmd+t=text:\x02c
|
|
|
21
21
|
keybind = cmd+]=text:\x02n
|
|
22
22
|
keybind = cmd+[=text:\x02p
|
|
23
23
|
|
|
24
|
+
# Layout presets (unbind Ghostty tab-switching defaults first)
|
|
25
|
+
keybind = cmd+1=unbind
|
|
26
|
+
keybind = cmd+2=unbind
|
|
27
|
+
keybind = cmd+3=unbind
|
|
28
|
+
keybind = cmd+4=unbind
|
|
29
|
+
keybind = cmd+1=text:\x021
|
|
30
|
+
keybind = cmd+2=text:\x022
|
|
31
|
+
keybind = cmd+3=text:\x023
|
|
32
|
+
keybind = cmd+4=text:\x024
|
|
33
|
+
|
|
34
|
+
# Project sessions
|
|
35
|
+
keybind = cmd+s=text:\x02S
|
|
36
|
+
keybind = cmd+o=text:\x02O
|
|
37
|
+
|
|
38
|
+
# Pane zoom toggle
|
|
39
|
+
keybind = cmd+z=text:\x02z
|
|
40
|
+
|
|
41
|
+
# Broadcast toggle (type in all panes)
|
|
42
|
+
keybind = cmd+shift+b=text:\x02a
|
|
43
|
+
|
|
44
|
+
# Help menu
|
|
45
|
+
keybind = cmd+shift+slash=text:\x02?
|
|
46
|
+
|
|
47
|
+
# Pane swap
|
|
48
|
+
keybind = cmd+shift+up=text:\x02K
|
|
49
|
+
keybind = cmd+shift+down=text:\x02J
|
|
50
|
+
keybind = cmd+shift+left=text:\x02H
|
|
51
|
+
keybind = cmd+shift+right=text:\x02L
|
|
52
|
+
|
|
24
53
|
# --- betterterm end ---
|
package/configs/tmux.conf
CHANGED
|
@@ -34,6 +34,31 @@ bind -r Right resize-pane -R 5
|
|
|
34
34
|
# Reload config with prefix + r
|
|
35
35
|
bind r source-file ~/.tmux.conf \; display "Config reloaded"
|
|
36
36
|
|
|
37
|
+
# Layout presets (auto-launch claude)
|
|
38
|
+
bind 1 run-shell "~/.local/bin/layout.sh 1"
|
|
39
|
+
bind 2 run-shell "~/.local/bin/layout.sh 2"
|
|
40
|
+
bind 3 run-shell "~/.local/bin/layout.sh 3"
|
|
41
|
+
bind 4 run-shell "~/.local/bin/layout.sh 4"
|
|
42
|
+
|
|
43
|
+
# Project sessions
|
|
44
|
+
bind S run-shell "~/.local/bin/session-save.sh"
|
|
45
|
+
bind O run-shell "~/.local/bin/session-load.sh"
|
|
46
|
+
|
|
47
|
+
# Pane zoom (built-in toggle)
|
|
48
|
+
bind z resize-pane -Z
|
|
49
|
+
|
|
50
|
+
# Broadcast toggle (type in all panes simultaneously)
|
|
51
|
+
bind a setw synchronize-panes
|
|
52
|
+
|
|
53
|
+
# Help menu (Cmd+?)
|
|
54
|
+
bind ? run-shell "~/.local/bin/help-menu.sh"
|
|
55
|
+
|
|
56
|
+
# Pane swap
|
|
57
|
+
bind K swap-pane -U
|
|
58
|
+
bind J swap-pane -D
|
|
59
|
+
bind H run-shell "~/.local/bin/swap-pane.sh left"
|
|
60
|
+
bind L run-shell "~/.local/bin/swap-pane.sh right"
|
|
61
|
+
|
|
37
62
|
# Better split keybindings
|
|
38
63
|
bind | split-window -h -c "#{pane_current_path}"
|
|
39
64
|
bind - split-window -v -c "#{pane_current_path}"
|
|
@@ -45,17 +70,17 @@ set -g allow-rename off
|
|
|
45
70
|
set -g status-position bottom
|
|
46
71
|
set -g status-style "bg=#1a1a1a,fg=#888888"
|
|
47
72
|
|
|
48
|
-
# Left: status
|
|
49
|
-
set -g status-left-length
|
|
50
|
-
set -g status-left "#
|
|
73
|
+
# Left: agent-aware pane status (or hint bar when single pane)
|
|
74
|
+
set -g status-left-length 80
|
|
75
|
+
set -g status-left "#(~/.local/bin/betterterm-panes.sh)"
|
|
51
76
|
|
|
52
77
|
# Window list
|
|
53
78
|
setw -g window-status-format "#[fg=#555555]#I:#W"
|
|
54
79
|
setw -g window-status-current-format "#[fg=#cccccc,bold]#I:#W"
|
|
55
80
|
|
|
56
|
-
# Right: system stats + time
|
|
57
|
-
set -g status-right-length
|
|
58
|
-
set -g status-right "#[fg=#555555]#(~/.local/bin/betterterm-stats.sh) #[fg=#888888]%H:%M"
|
|
81
|
+
# Right: broadcast indicator + system stats + time
|
|
82
|
+
set -g status-right-length 80
|
|
83
|
+
set -g status-right "#{?synchronize-panes,#[fg=#1a1a1a,bg=#ff5555,bold] BROADCAST #[default] ,}#[fg=#555555]#(~/.local/bin/betterterm-stats.sh) #[fg=#888888]%H:%M"
|
|
59
84
|
set -g status-interval 5
|
|
60
85
|
|
|
61
86
|
set -g pane-border-style "fg=#333333"
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Agent-aware pane status for the tmux status bar
|
|
3
|
+
|
|
4
|
+
PANE_COUNT=$(tmux list-panes -F "#{pane_id}" | wc -l | tr -d ' ')
|
|
5
|
+
|
|
6
|
+
# Single pane: show original hint bar
|
|
7
|
+
if [ "$PANE_COUNT" -le 1 ]; then
|
|
8
|
+
echo "#[fg=#888888,bg=#2a2a2a] Files:Cmd+E #[default] #[fg=#888888,bg=#2a2a2a] Browser:Cmd+Y #[default] "
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Multi-pane: show per-pane agent status
|
|
13
|
+
AGENTS="claude|aider|codex"
|
|
14
|
+
OUTPUT=""
|
|
15
|
+
|
|
16
|
+
while read -r idx cmd; do
|
|
17
|
+
label="$cmd"
|
|
18
|
+
|
|
19
|
+
# Detect known agents
|
|
20
|
+
if echo "$cmd" | grep -qiE "$AGENTS"; then
|
|
21
|
+
label=$(echo "$cmd" | grep -oiE "$AGENTS" | head -1 | tr '[:upper:]' '[:lower:]')
|
|
22
|
+
OUTPUT="${OUTPUT}#[fg=#1a1a1a,bg=#5af78e] ${idx}:${label} #[default] "
|
|
23
|
+
else
|
|
24
|
+
# Check if pane is idle (shell with no foreground process)
|
|
25
|
+
if echo "$cmd" | grep -qE "^(bash|zsh|sh|fish)$"; then
|
|
26
|
+
label="idle"
|
|
27
|
+
fi
|
|
28
|
+
OUTPUT="${OUTPUT}#[fg=#888888,bg=#2a2a2a] ${idx}:${label} #[default] "
|
|
29
|
+
fi
|
|
30
|
+
done < <(tmux list-panes -F "#{pane_index} #{pane_current_command}")
|
|
31
|
+
|
|
32
|
+
echo -n "$OUTPUT"
|
package/scripts/betterterm.sh
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Start
|
|
2
|
+
# Start or create a betterterm tmux session
|
|
3
3
|
export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH"
|
|
4
4
|
TMUX=$(command -v tmux)
|
|
5
5
|
|
|
6
6
|
# Use first argument as starting dir, or current dir, or home
|
|
7
7
|
DIR="${1:-${PWD:-$HOME}}"
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
|
|
9
|
+
# If already inside tmux, do nothing (let Ghostty handle new window)
|
|
10
|
+
if [ -n "$TMUX_PANE" ]; then
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
11
13
|
|
|
12
|
-
#
|
|
13
|
-
$TMUX
|
|
14
|
+
# Find an existing unattached session to attach to
|
|
15
|
+
UNATTACHED=$($TMUX list-sessions -F "#{session_name}|#{session_attached}" 2>/dev/null | grep "|0$" | head -1 | cut -d'|' -f1)
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
$TMUX attach-session -t
|
|
17
|
+
if [ -n "$UNATTACHED" ]; then
|
|
18
|
+
$TMUX attach-session -t "$UNATTACHED"
|
|
19
|
+
else
|
|
20
|
+
# Create a new session with a unique name
|
|
21
|
+
SESSION="main-$(date +%s)"
|
|
22
|
+
# Use "main" for the first session
|
|
23
|
+
if ! $TMUX has-session -t main 2>/dev/null; then
|
|
24
|
+
SESSION="main"
|
|
25
|
+
fi
|
|
26
|
+
$TMUX new-session -d -s "$SESSION" -c "$DIR"
|
|
27
|
+
$TMUX attach-session -t "$SESSION"
|
|
28
|
+
fi
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Interactive help menu with clickable actions
|
|
3
|
+
|
|
4
|
+
tmux display-menu -T "#[align=centre,bold] betterterm " -x C -y C \
|
|
5
|
+
"" \
|
|
6
|
+
"#[bold,align=centre]── Files & Browser ──" "" "" \
|
|
7
|
+
"#{?#{==:#{pane_current_command},yazi},Close,Open} File Explorer Cmd+E" e "run-shell '~/.local/bin/toggle-yazi.sh'" \
|
|
8
|
+
"Toggle Browser Cmd+Y" b "run-shell '~/.local/bin/toggle-browser.sh'" \
|
|
9
|
+
"Close Browser Cmd+Sh+Y" B "run-shell '~/.local/bin/close-browser.sh'" \
|
|
10
|
+
"Browser URL Bar Cmd+L" l "run-shell '~/.local/bin/browser-nav.sh'" \
|
|
11
|
+
"" \
|
|
12
|
+
"#[bold,align=centre]── Layout Presets ──" "" "" \
|
|
13
|
+
"Focus (1 pane) Cmd+1" 1 "run-shell '~/.local/bin/layout.sh 1'" \
|
|
14
|
+
"Dual (2 panes) Cmd+2" 2 "run-shell '~/.local/bin/layout.sh 2'" \
|
|
15
|
+
"Triple (3 panes) Cmd+3" 3 "run-shell '~/.local/bin/layout.sh 3'" \
|
|
16
|
+
"Quad (4 panes) Cmd+4" 4 "run-shell '~/.local/bin/layout.sh 4'" \
|
|
17
|
+
"" \
|
|
18
|
+
"#[bold,align=centre]── Panes ──" "" "" \
|
|
19
|
+
"Zoom Pane Toggle Cmd+Z" z "resize-pane -Z" \
|
|
20
|
+
"Broadcast Toggle Cmd+Sh+B" a "setw synchronize-panes" \
|
|
21
|
+
"Swap Pane Up Cmd+Sh+Up" K "swap-pane -U" \
|
|
22
|
+
"Swap Pane Down Cmd+Sh+Down" J "swap-pane -D" \
|
|
23
|
+
"Swap Pane Left Cmd+Sh+Left" H "run-shell '~/.local/bin/swap-pane.sh left'" \
|
|
24
|
+
"Swap Pane Right Cmd+Sh+Right" L "run-shell '~/.local/bin/swap-pane.sh right'" \
|
|
25
|
+
"" \
|
|
26
|
+
"#[bold,align=centre]── Windows ──" "" "" \
|
|
27
|
+
"New Window Cmd+T" c "new-window" \
|
|
28
|
+
"Next Window Cmd+]" n "next-window" \
|
|
29
|
+
"Previous Window Cmd+[" p "previous-window" \
|
|
30
|
+
"Split Vertical Cmd+\\" "|" "split-window -h -c '#{pane_current_path}'" \
|
|
31
|
+
"Split Horizontal Cmd+-" - "split-window -v -c '#{pane_current_path}'" \
|
|
32
|
+
"" \
|
|
33
|
+
"#[bold,align=centre]── Sessions ──" "" "" \
|
|
34
|
+
"Save Session Cmd+S" S "run-shell '~/.local/bin/session-save.sh'" \
|
|
35
|
+
"Load Session Cmd+O" O "run-shell '~/.local/bin/session-load.sh'" \
|
|
36
|
+
"" \
|
|
37
|
+
"Reload Config Cmd+R" r "source-file ~/.tmux.conf ; display 'Config reloaded'"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Layout presets: 1-4 panes with optional auto-launch of claude
|
|
3
|
+
|
|
4
|
+
COUNT="${1:-1}"
|
|
5
|
+
|
|
6
|
+
# Capture current working directory before killing panes
|
|
7
|
+
PWD=$(tmux display-message -p '#{pane_current_path}')
|
|
8
|
+
|
|
9
|
+
# Kill all panes except pane 1, then reset it
|
|
10
|
+
tmux kill-pane -a -t "{top-left}" 2>/dev/null
|
|
11
|
+
tmux respawn-pane -k -c "$PWD" -t "{top-left}"
|
|
12
|
+
|
|
13
|
+
# Detect if claude is available
|
|
14
|
+
HAS_CLAUDE=0
|
|
15
|
+
if command -v claude >/dev/null 2>&1; then
|
|
16
|
+
HAS_CLAUDE=1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
launch_claude() {
|
|
20
|
+
local pane="$1"
|
|
21
|
+
if [ "$HAS_CLAUDE" -eq 1 ]; then
|
|
22
|
+
tmux send-keys -t "$pane" "claude --dangerously-skip-permissions" Enter
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
case "$COUNT" in
|
|
27
|
+
1)
|
|
28
|
+
# Focus mode: single pane
|
|
29
|
+
launch_claude 1
|
|
30
|
+
;;
|
|
31
|
+
2)
|
|
32
|
+
# Dual mode: side-by-side
|
|
33
|
+
tmux split-window -h -c "$PWD"
|
|
34
|
+
tmux select-pane -t 1
|
|
35
|
+
launch_claude 1
|
|
36
|
+
launch_claude 2
|
|
37
|
+
;;
|
|
38
|
+
3)
|
|
39
|
+
# Triple: big left + 2 stacked right
|
|
40
|
+
tmux split-window -h -p 40 -c "$PWD"
|
|
41
|
+
tmux split-window -v -c "$PWD"
|
|
42
|
+
tmux select-pane -t 1
|
|
43
|
+
launch_claude 1
|
|
44
|
+
launch_claude 2
|
|
45
|
+
launch_claude 3
|
|
46
|
+
;;
|
|
47
|
+
4)
|
|
48
|
+
# Quad grid
|
|
49
|
+
tmux split-window -h -c "$PWD"
|
|
50
|
+
tmux split-window -v -c "$PWD"
|
|
51
|
+
tmux select-pane -t 1
|
|
52
|
+
tmux split-window -v -c "$PWD"
|
|
53
|
+
tmux select-pane -t 1
|
|
54
|
+
launch_claude 1
|
|
55
|
+
launch_claude 2
|
|
56
|
+
launch_claude 3
|
|
57
|
+
launch_claude 4
|
|
58
|
+
;;
|
|
59
|
+
*)
|
|
60
|
+
tmux display-message "Usage: layout.sh [1-4]"
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Restore a saved session: all windows, panes, layouts, directories, agents
|
|
3
|
+
|
|
4
|
+
NAME="$1"
|
|
5
|
+
if [ -z "$NAME" ]; then
|
|
6
|
+
tmux display-message "Session name required"
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
SESSION_FILE="$HOME/.local/share/betterterm/sessions/$NAME.session"
|
|
11
|
+
|
|
12
|
+
if [ ! -f "$SESSION_FILE" ]; then
|
|
13
|
+
tmux display-message "Session not found: $NAME"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
ACTIVE_WIN=$(grep "^ACTIVE_WINDOW=" "$SESSION_FILE" | cut -d= -f2-)
|
|
18
|
+
|
|
19
|
+
# Parse windows and their panes
|
|
20
|
+
CURRENT_WIN=""
|
|
21
|
+
FIRST_WINDOW=1
|
|
22
|
+
|
|
23
|
+
while IFS= read -r line; do
|
|
24
|
+
[[ "$line" == ACTIVE_WINDOW=* ]] && continue
|
|
25
|
+
[[ -z "$line" ]] && continue
|
|
26
|
+
|
|
27
|
+
if [[ "$line" == WINDOW=* ]]; then
|
|
28
|
+
DATA="${line#WINDOW=}"
|
|
29
|
+
WIN_IDX=$(echo "$DATA" | cut -d'|' -f1)
|
|
30
|
+
WIN_NAME=$(echo "$DATA" | cut -d'|' -f2)
|
|
31
|
+
WIN_LAYOUT=$(echo "$DATA" | cut -d'|' -f3)
|
|
32
|
+
CURRENT_WIN="$WIN_IDX"
|
|
33
|
+
PANE_NUM=0
|
|
34
|
+
|
|
35
|
+
if [ "$FIRST_WINDOW" -eq 1 ]; then
|
|
36
|
+
# Reuse the existing first window
|
|
37
|
+
tmux kill-pane -a -t "{top-left}" 2>/dev/null
|
|
38
|
+
tmux rename-window "$WIN_NAME"
|
|
39
|
+
FIRST_WINDOW=0
|
|
40
|
+
else
|
|
41
|
+
tmux new-window -n "$WIN_NAME"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
elif [[ "$line" == PANE=* ]]; then
|
|
45
|
+
DATA="${line#PANE=}"
|
|
46
|
+
PANE_IDX=$(echo "$DATA" | cut -d'|' -f1)
|
|
47
|
+
PANE_DIR=$(echo "$DATA" | cut -d'|' -f2)
|
|
48
|
+
PANE_CMD=$(echo "$DATA" | cut -d'|' -f3-)
|
|
49
|
+
|
|
50
|
+
if [ "$PANE_NUM" -eq 0 ]; then
|
|
51
|
+
# First pane in window — respawn existing pane
|
|
52
|
+
tmux respawn-pane -k -c "${PANE_DIR:-$HOME}"
|
|
53
|
+
else
|
|
54
|
+
# Additional panes
|
|
55
|
+
tmux split-window -c "${PANE_DIR:-$HOME}"
|
|
56
|
+
fi
|
|
57
|
+
PANE_NUM=$((PANE_NUM + 1))
|
|
58
|
+
|
|
59
|
+
# Re-launch agents
|
|
60
|
+
if echo "$PANE_CMD" | grep -qiE "claude|aider|codex"; then
|
|
61
|
+
tmux send-keys "$PANE_CMD" Enter
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
done < "$SESSION_FILE"
|
|
65
|
+
|
|
66
|
+
# Apply layouts per window (second pass since all panes need to exist first)
|
|
67
|
+
while IFS= read -r line; do
|
|
68
|
+
if [[ "$line" == WINDOW=* ]]; then
|
|
69
|
+
DATA="${line#WINDOW=}"
|
|
70
|
+
WIN_IDX=$(echo "$DATA" | cut -d'|' -f1)
|
|
71
|
+
WIN_LAYOUT=$(echo "$DATA" | cut -d'|' -f3)
|
|
72
|
+
tmux select-layout -t ":$WIN_IDX" "$WIN_LAYOUT" 2>/dev/null
|
|
73
|
+
fi
|
|
74
|
+
done < "$SESSION_FILE"
|
|
75
|
+
|
|
76
|
+
# Switch to the previously active window
|
|
77
|
+
tmux select-window -t ":${ACTIVE_WIN:-1}" 2>/dev/null
|
|
78
|
+
tmux select-pane -t 1
|
|
79
|
+
|
|
80
|
+
WIN_COUNT=$(grep -c "^WINDOW=" "$SESSION_FILE")
|
|
81
|
+
tmux display-message "Session loaded: $NAME ($WIN_COUNT tabs)"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Prompt user to pick a saved session, then restore it
|
|
3
|
+
|
|
4
|
+
SESSION_DIR="$HOME/.local/share/betterterm/sessions"
|
|
5
|
+
|
|
6
|
+
if [ ! -d "$SESSION_DIR" ] || [ -z "$(ls -A "$SESSION_DIR" 2>/dev/null)" ]; then
|
|
7
|
+
tmux display-message "No saved sessions found"
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
# Show available sessions as a tmux menu instead of a prompt
|
|
12
|
+
MENU_ARGS=(-T "#[align=centre,bold] Load Session ")
|
|
13
|
+
|
|
14
|
+
for f in "$SESSION_DIR"/*.session; do
|
|
15
|
+
NAME=$(basename "$f" .session)
|
|
16
|
+
MENU_ARGS+=("$NAME" "" "run-shell '~/.local/bin/session-load-exec.sh \"$NAME\"'")
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
tmux display-menu -x C -y C "${MENU_ARGS[@]}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Save current tmux session: all windows, panes, layouts, directories, agents
|
|
3
|
+
|
|
4
|
+
NAME="$1"
|
|
5
|
+
if [ -z "$NAME" ]; then
|
|
6
|
+
tmux display-message "Session name required"
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
SESSION_DIR="$HOME/.local/share/betterterm/sessions"
|
|
11
|
+
mkdir -p "$SESSION_DIR"
|
|
12
|
+
|
|
13
|
+
SESSION_FILE="$SESSION_DIR/$NAME.session"
|
|
14
|
+
> "$SESSION_FILE"
|
|
15
|
+
|
|
16
|
+
# Save active window index
|
|
17
|
+
ACTIVE_WIN=$(tmux display-message -p '#{window_index}')
|
|
18
|
+
echo "ACTIVE_WINDOW=$ACTIVE_WIN" >> "$SESSION_FILE"
|
|
19
|
+
|
|
20
|
+
# Iterate all windows
|
|
21
|
+
tmux list-windows -F "#{window_index}|#{window_name}|#{window_layout}" | while read -r win_line; do
|
|
22
|
+
WIN_IDX=$(echo "$win_line" | cut -d'|' -f1)
|
|
23
|
+
WIN_NAME=$(echo "$win_line" | cut -d'|' -f2)
|
|
24
|
+
WIN_LAYOUT=$(echo "$win_line" | cut -d'|' -f3)
|
|
25
|
+
|
|
26
|
+
echo "WINDOW=$WIN_IDX|$WIN_NAME|$WIN_LAYOUT" >> "$SESSION_FILE"
|
|
27
|
+
|
|
28
|
+
# Save each pane in this window
|
|
29
|
+
tmux list-panes -t ":$WIN_IDX" -F "#{pane_index}|#{pane_current_path}|#{pane_current_command}|#{pane_pid}" | while read -r pane_line; do
|
|
30
|
+
PANE_IDX=$(echo "$pane_line" | cut -d'|' -f1)
|
|
31
|
+
PANE_DIR=$(echo "$pane_line" | cut -d'|' -f2)
|
|
32
|
+
PANE_PID=$(echo "$pane_line" | cut -d'|' -f4)
|
|
33
|
+
|
|
34
|
+
# Check process tree for agents
|
|
35
|
+
AGENT_CMD=""
|
|
36
|
+
if ps -o command= -g "$PANE_PID" 2>/dev/null | grep -qiE "claude|aider|codex"; then
|
|
37
|
+
AGENT_CMD=$(ps -o command= -g "$PANE_PID" 2>/dev/null | grep -iE "claude|aider|codex" | head -1)
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [ -n "$AGENT_CMD" ]; then
|
|
41
|
+
echo "PANE=$PANE_IDX|$PANE_DIR|$AGENT_CMD" >> "$SESSION_FILE"
|
|
42
|
+
else
|
|
43
|
+
echo "PANE=$PANE_IDX|$PANE_DIR|$(echo "$pane_line" | cut -d'|' -f3)" >> "$SESSION_FILE"
|
|
44
|
+
fi
|
|
45
|
+
done
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
tmux display-message "Session saved: $NAME ($(tmux list-windows | wc -l | tr -d ' ') tabs)"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Swap pane in the given direction (left or right)
|
|
3
|
+
|
|
4
|
+
DIRECTION="${1:-left}"
|
|
5
|
+
|
|
6
|
+
case "$DIRECTION" in
|
|
7
|
+
left)
|
|
8
|
+
TARGET=$(tmux display-message -p -t "{left-of}" "#{pane_id}" 2>/dev/null)
|
|
9
|
+
;;
|
|
10
|
+
right)
|
|
11
|
+
TARGET=$(tmux display-message -p -t "{right-of}" "#{pane_id}" 2>/dev/null)
|
|
12
|
+
;;
|
|
13
|
+
*)
|
|
14
|
+
tmux display-message "Usage: swap-pane.sh [left|right]"
|
|
15
|
+
exit 1
|
|
16
|
+
;;
|
|
17
|
+
esac
|
|
18
|
+
|
|
19
|
+
if [ -n "$TARGET" ]; then
|
|
20
|
+
tmux swap-pane -t "$TARGET"
|
|
21
|
+
else
|
|
22
|
+
tmux display-message "No pane ${DIRECTION}"
|
|
23
|
+
fi
|
package/scripts/toggle-yazi.sh
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Toggle yazi file explorer pane
|
|
2
|
+
# Toggle yazi file explorer as a left-side pane without disrupting layout
|
|
3
3
|
export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH"
|
|
4
4
|
T=$(command -v tmux)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
DIR=$($T display-message -p '#{pane_current_path}')
|
|
6
|
+
|
|
7
|
+
# Check if yazi is already running in any pane
|
|
8
|
+
YAZI_PANE=$($T list-panes -F "#{pane_id} #{pane_current_command}" | grep -i yazi | head -1 | awk '{print $1}')
|
|
9
|
+
|
|
10
|
+
if [ -n "$YAZI_PANE" ]; then
|
|
11
|
+
# Yazi is open — kill just that pane
|
|
12
|
+
$T kill-pane -t "$YAZI_PANE"
|
|
8
13
|
else
|
|
9
|
-
|
|
10
|
-
$T
|
|
11
|
-
$T
|
|
14
|
+
# Open yazi as a left-side pane (25% width), keep focus on original pane
|
|
15
|
+
CURRENT=$($T display-message -p '#{pane_id}')
|
|
16
|
+
$T split-window -hf -l 25% -c "$DIR" "env TERM=xterm-256color $(command -v yazi) $DIR"
|
|
17
|
+
$T select-pane -t "$CURRENT"
|
|
12
18
|
fi
|
|
13
19
|
exit 0
|