From 9fe3975fea6cdcc526e0490e1d80ff2438f865a4 Mon Sep 17 00:00:00 2001 From: Brett Kinny Date: Mon, 20 Jul 2026 11:06:06 +1000 Subject: [PATCH 1/2] fix: zellij session must detach, not quit, when the client dies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit on_force_close "quit" killed the whole session (and every process in it) whenever the ssh/mosh chain dropped — the opposite of what a multiplexer is for. Default to "detach" and self-heal existing configs on upgraded containers, mirroring the tmux mouse self-heal. Also bind ? to a floating configuration plugin as a keybinding help popup (clear-defaults=true leaves no discoverable help otherwise). Co-Authored-By: Claude Fable 5 --- setup.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/setup.sh b/setup.sh index b87f08b..12466d0 100755 --- a/setup.sh +++ b/setup.sh @@ -1023,7 +1023,9 @@ _install_zellij_inner() { scroll_buffer_size 50000 pane_frames false auto_layout true - on_force_close "quit" + // "detach" not "quit" — a dropped client (ssh/mosh dying) must not kill + // the session and everything running in it + on_force_close "detach" simplified_ui true session_serialization true support_kitty_keyboard_protocol true @@ -1161,6 +1163,9 @@ _install_zellij_inner() { // Enter scroll/copy mode (vi-style — like tmux [ ) bind "[" { SwitchToMode "Scroll"; } + + // Keybinding help — floating configuration plugin + bind "?" { LaunchOrFocusPlugin "configuration" { floating true; }; SwitchToMode "Normal"; } } // ── Scroll mode (vi-style copy mode) ──────────────────────── @@ -1236,9 +1241,19 @@ _install_zellij_inner() { fi } +_ensure_zellij_defaults() { + local conf="$HOME/.config/zellij/config.kdl" + [ -f "$conf" ] || return 0 + sed -i 's/^on_force_close "quit"/on_force_close "detach"/' "$conf" + if ! grep -q 'bind "?"' "$conf"; then + sed -i '/bind "\[" { SwitchToMode "Scroll"; }/a\ bind "?" { LaunchOrFocusPlugin "configuration" { floating true; }; SwitchToMode "Normal"; }' "$conf" + fi +} + install_zellij() { - if command -v zellij &>/dev/null; then echo "Zellij already installed, skipping."; return 0; fi + if command -v zellij &>/dev/null; then echo "Zellij already installed, skipping."; _ensure_zellij_defaults; return 0; fi run_with_spinner "Installing Zellij..." _install_zellij_inner + _ensure_zellij_defaults } for mux in $(echo "$mux_list" | tr ',' ' '); do From bf7c31f05fb6462568b84a2ece672f09a47f0204 Mon Sep 17 00:00:00 2001 From: Brett Kinny Date: Mon, 20 Jul 2026 11:14:39 +1000 Subject: [PATCH 2/2] feat: Ctrl+b as a second zellij leader alongside Ctrl+Space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ctrl+Space is transmitted as a NUL byte that some client stacks never deliver (iPadOS grabs it for input-source switching before Blink sees it). Mirror every Ctrl+Space mode bind with Ctrl+b — the classic tmux prefix — in the shipped config and the self-heal. Scroll/search modes keep their explicit Ctrl+b page-up binds, which take precedence over the shared exit bind. Co-Authored-By: Claude Fable 5 --- setup.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 12466d0..6921f8e 100755 --- a/setup.sh +++ b/setup.sh @@ -1105,16 +1105,21 @@ _install_zellij_inner() { }; } - // Prefix-style bindings via Ctrl+Space (tmux-like leader) + // Prefix-style bindings via Ctrl+Space (tmux-like leader). + // Ctrl+b as well: Ctrl+Space is a NUL byte some clients never + // deliver (iPadOS grabs it for input-source switching) bind "Ctrl Space" { SwitchToMode "Tmux"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } } locked { bind "Ctrl Space" { SwitchToMode "Normal"; } + bind "Ctrl b" { SwitchToMode "Normal"; } } tmux { bind "Ctrl Space" { SwitchToMode "Normal"; } + bind "Ctrl b" { SwitchToMode "Normal"; } bind "Esc" { SwitchToMode "Normal"; } // Pane splitting (h=horizontal, v=vertical — matching tmux) @@ -1222,6 +1227,7 @@ _install_zellij_inner() { // Allow Ctrl+Space to return to normal from any non-normal mode shared_except "normal" "locked" "tmux" { bind "Ctrl Space" { SwitchToMode "Normal"; } + bind "Ctrl b" { SwitchToMode "Normal"; } } } ZELLIJCONF @@ -1248,6 +1254,10 @@ _ensure_zellij_defaults() { if ! grep -q 'bind "?"' "$conf"; then sed -i '/bind "\[" { SwitchToMode "Scroll"; }/a\ bind "?" { LaunchOrFocusPlugin "configuration" { floating true; }; SwitchToMode "Normal"; }' "$conf" fi + if ! grep -q 'bind "Ctrl b" { SwitchToMode "Tmux"; }' "$conf"; then + sed -i 's|^\(\s*\)bind "Ctrl Space" { SwitchToMode "Tmux"; }$|&\n\1bind "Ctrl b" { SwitchToMode "Tmux"; }|' "$conf" + sed -i 's|^\(\s*\)bind "Ctrl Space" { SwitchToMode "Normal"; }$|&\n\1bind "Ctrl b" { SwitchToMode "Normal"; }|' "$conf" + fi } install_zellij() {