Compare commits

...
1 Commits
Author SHA1 Message Date
jaredandClaude Opus 5.5 ab7e127f70 fix: v0.10.3 no tmux right-click menus; close split panes
build-deb / Deploy to CT 126 (push) Successful in 19s
build-deb / Lint, test, package (push) Successful in 3m9s
build-deb / Notify on failure (push) Skipped
Right-clicking a pane opened tmux's menu, whose Split entries left a stray
pane next to Claude (and whose Kill entries could end it). Unbind the
right-click menus; add --unsplit and a palette entry that closes extra
panes and keeps Claude's. Closes #52

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHRLCCdXYm41Qx5kNhehaP
2026-10-13 01:48:04 -04:00
6 changed files with 63 additions and 3 deletions
+1
View File
@@ -73,6 +73,7 @@ The tmux server runs in its own unit, so every Claude process lives in **that**
| Ctrl-b d | Detach (the session keeps running) |
| Drag | Selects text; it stays highlighted and is copied when you let go. Double-click selects a word, triple-click a line. Clicks on the tmux status line still go to tmux. |
| Ctrl+C | Copies the selection when there is one; otherwise interrupts as usual |
| Palette → Close split panes | Removes extra tmux panes (a stray `^b %` split) and keeps Claude. tmux's right-click menus are off. |
| Ctrl+Shift+C / V | Copy selection / paste |
| Ctrl+Shift+P | Command palette (also the ⌕ button) |
| Ctrl+Shift+E | Files pane (Esc: back, then close) |
+21 -2
View File
@@ -15,7 +15,7 @@
#
# The web UI calls `devdesk attach --list | --repo NAME | --alerts | --investigate SESSION UID |
# --upload SESSION FILE BYTES | --new NAME [PROJECT] [--worktree] | --rename OLD NEW | --end NAME |
# --last-reply NAME | --existing NAME`
# --last-reply NAME | --unsplit NAME | --existing NAME`
# through ttyd; those print JSON.
set -euo pipefail
@@ -182,6 +182,25 @@ dd_worktree_end() {
fi
}
# --unsplit NAME (devdesk#52): close the extra panes a split left in the session's
# windows. Each window keeps one pane: Claude's in the claude window, else the
# first. Prints {ok, closed}.
cmd_unsplit() {
[ $# -eq 1 ] || dd_die "usage: devdesk attach --unsplit NAME"
dd_valid_name "$1" || dd_die "invalid session name: $1"
{ dd_server_up && dd_tmux has-session -t "=$1" 2>/dev/null; } || dd_die "session '$1' isn't running"
local win keep pane closed=0
while read -r win; do
keep=$(dd_tmux list-panes -t "=$1:$win" -F '#{pane_id} #{pane_start_command}' | awk '/devdesk-claude-run/ { print $1; exit }')
[ -n "$keep" ] || keep=$(dd_tmux list-panes -t "=$1:$win" -F '#{pane_id}' | head -n 1)
while read -r pane; do
[ "$pane" = "$keep" ] && continue
dd_tmux kill-pane -t "$pane" && closed=$((closed + 1))
done < <(dd_tmux list-panes -t "=$1:$win" -F '#{pane_id}')
done < <(dd_tmux list-windows -t "=$1" -F '#{window_index}')
jq -cMn --argjson c "$closed" '{ok: true, closed: $c}'
}
cmd_rename() {
[ $# -eq 2 ] || dd_die "usage: devdesk rename OLD NEW"
local old=$1 new=$2 running=0
@@ -366,7 +385,7 @@ cmd_attach() {
dd_linger
return
;;
--peek | --search | --last-reply)
--peek | --search | --last-reply | --unsplit)
local action=${1#--}
shift
"cmd_${action//-/_}" "$@"
+9
View File
@@ -1,3 +1,12 @@
devdesk (0.10.3) bookworm; urgency=medium
* No tmux right-click menus (#52). A stray click on one split a pane, or
could kill Claude's window. "Close split panes (keeps Claude)" in the
command palette, backed by `--unsplit`, removes a split that happened
anyway.
-- Jared Vititoe <jjvititoe1@gmail.com> Tue, 13 Oct 2026 09:00:00 -0400
devdesk (0.10.2) bookworm; urgency=medium
* Selecting text in the terminal works like a normal terminal (#51). A
+7 -1
View File
@@ -8,7 +8,13 @@ set -s set-clipboard on # tmux copy-mode -> OSC 52 -> browser cli
set -s default-terminal "tmux-256color"
set -s terminal-features[90] "xterm-256color:RGB" # fixed index: re-sourcing must not append
set -g history-limit 50000
set -g mouse on # wheel scrolls history; Shift+drag selects natively
set -g mouse on # wheel scrolls history; a drag selects in the browser (devdesk#51)
# No right-click menus (devdesk#52): one stray click on them splits a pane or
# kills a window (Claude's). Splits and kills stay on the keyboard (^b % ^b x).
unbind -n MouseDown3Pane
unbind -n M-MouseDown3Pane
unbind -n MouseDown3Status
unbind -n MouseDown3StatusLeft
set -g allow-passthrough off # output may not bypass tmux (title/clipboard spoofing)
set -g renumber-windows on
+14
View File
@@ -1181,3 +1181,17 @@ JSONL
[ "$(posts)" -eq 1 ]
[ "$(cat "$DEVDESK_STATE_DIR/usage-warned")" = 2000018000 ]
}
@test "unsplit closes extra panes and keeps Claude's (#52)" {
start_server
devdesk new alpha
tmux -S "$DEVDESK_SOCKET" split-window -h -t =alpha:claude
tmux -S "$DEVDESK_SOCKET" split-window -v -t =alpha:shell
run devdesk attach --unsplit alpha
[ "$status" -eq 0 ]
[ "$(jq -r .closed <<<"$output")" = 2 ]
[ "$(tmux -S "$DEVDESK_SOCKET" list-panes -t =alpha:claude -F '#{pane_start_command}')" = "$REPO/libexec/devdesk-claude-run" ]
[ "$(tmux -S "$DEVDESK_SOCKET" list-panes -t =alpha:shell | wc -l)" = 1 ]
run devdesk attach --unsplit alpha
[ "$(jq -r .closed <<<"$output")" = 0 ]
}
+11
View File
@@ -750,6 +750,16 @@ async function compact(session) {
return undefined;
}
// A stray split (devdesk#52): close every extra pane, keeping Claude's.
async function unsplit(session) {
try {
const result = await sessionAction('unsplit', session);
toast.success(result?.closed ? `Closed ${result.closed} pane${result.closed === 1 ? '' : 's'}` : 'No split panes');
} catch (error) {
toast.error(error.message);
}
}
async function copyLink() {
const url = new URL(window.location.href);
url.search = state.session ? `?arg=${encodeURIComponent(state.session)}` : '';
@@ -801,6 +811,7 @@ function buildCommands() {
add({ id: 'win-shell', group: 'Terminal', icon: '1', label: 'Shell window', kbd: '^b 1', run: () => sendKeys('\x021') });
add({ id: 'scroll', group: 'Terminal', icon: '⇞', label: 'Scroll back (tmux copy mode, q to leave)', kbd: '^b [', keywords: 'history scrollback', run: () => sendKeys('\x02[') });
add({ id: 'detach', group: 'Terminal', icon: '⏏', label: 'Detach (the session keeps running)', kbd: '^b d', run: () => sendKeys('\x02d') });
add({ id: 'unsplit', group: 'Terminal', icon: '▣', label: 'Close split panes (keeps Claude)', keywords: 'pane split remove side by side layout', run: () => unsplit(state.session) });
}
if (state.session) {