Skip to content

Instantly share code, notes, and snippets.

@HackingGate
Last active July 30, 2026 15:33
Show Gist options
  • Select an option

  • Save HackingGate/ebc5bd4d474d6aaffa05bfb84cc1e60f to your computer and use it in GitHub Desktop.

Select an option

Save HackingGate/ebc5bd4d474d6aaffa05bfb84cc1e60f to your computer and use it in GitHub Desktop.
Emacs-friendly tmux setup with clipboard copy, auto light/dark mode, and longer message display time
#!/usr/bin/env bash
set -euo pipefail
echo "Starting tmux and shell environment setup..."
# Detect OS/session and configure clipboard behavior
clip_cmd=""
copy_target="tmux OSC 52 host clipboard"
os_name="$(uname -s 2>/dev/null || true)"
is_wayland=0
is_ssh=0
has_display=0
if [[ "${XDG_SESSION_TYPE:-}" == "wayland" ]] || [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
is_wayland=1
fi
if [[ -n "${SSH_CONNECTION:-}" ]] || [[ -n "${SSH_CLIENT:-}" ]]; then
is_ssh=1
fi
if [[ -n "${DISPLAY:-}" ]] || [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
has_display=1
fi
# For SSH sessions, prefer tmux's OSC 52 clipboard path so copied text lands on
# the local machine connected to the SSH client instead of the remote host.
use_tmux_clipboard=1
if [[ "${is_ssh}" -eq 0 ]] && [[ "${has_display}" -eq 1 ]]; then
use_tmux_clipboard=0
fi
case "${os_name}" in
Linux)
if [[ "${use_tmux_clipboard}" -eq 0 ]]; then
if [[ "${is_wayland}" -eq 1 ]]; then
if ! command -v wl-copy >/dev/null 2>&1; then
if command -v apt >/dev/null 2>&1; then
sudo apt update >/dev/null 2>&1
sudo apt install -y wl-clipboard >/dev/null 2>&1
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install -y wl-clipboard >/dev/null 2>&1
fi
fi
if command -v wl-copy >/dev/null 2>&1; then
clip_cmd="wl-copy"
fi
else
if ! command -v xclip >/dev/null 2>&1; then
if command -v apt >/dev/null 2>&1; then
sudo apt update >/dev/null 2>&1
sudo apt install -y xclip >/dev/null 2>&1
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install -y xclip >/dev/null 2>&1
fi
fi
if command -v xclip >/dev/null 2>&1; then
clip_cmd="xclip -in -selection clipboard"
fi
fi
fi
;;
Darwin)
if [[ "${use_tmux_clipboard}" -eq 0 ]]; then
clip_cmd="pbcopy"
fi
;;
esac
# Define the final copy action
if [[ -n "${clip_cmd}" ]]; then
copy_action="copy-pipe-and-cancel \"${clip_cmd}\""
copy_target="local desktop clipboard helper (${clip_cmd})"
else
copy_action="copy-selection-and-cancel"
fi
# Version-gated features. An older tmux treats an unknown terminal feature or
# option value as an error and aborts the rest of the config, so these lines are
# emitted only when the running tmux understands them.
#
# hyperlinks feature -> tmux 3.4+
# extkeys feature and "extended-keys always" -> tmux 3.2+
hyperlinks_line="# OSC 8 hyperlinks need tmux 3.4+; this tmux is older, feature omitted"
extkeys_lines="# Extended keys need tmux 3.2+; this tmux is older, feature omitted"
tmux_major=""
tmux_minor=""
if command -v tmux >/dev/null 2>&1; then
tmux_ver="$(tmux -V 2>/dev/null | awk '{print $2}')"
tmux_major="${tmux_ver%%.*}"
tmux_minor="${tmux_ver#*.}"
tmux_minor="${tmux_minor%%[!0-9]*}"
if [[ ! "${tmux_major}" =~ ^[0-9]+$ ]] || [[ ! "${tmux_minor}" =~ ^[0-9]+$ ]]; then
tmux_major=""
tmux_minor=""
fi
fi
if [[ -n "${tmux_major}" ]]; then
if (( tmux_major > 3 || (tmux_major == 3 && tmux_minor >= 4) )); then
hyperlinks_line="# OSC 8 hyperlink passthrough (tmux 3.4+); without it tmux strips the
# escape and clickable link text renders as plain text
set -as terminal-features ',*:hyperlinks'"
fi
if (( tmux_major > 3 || (tmux_major == 3 && tmux_minor >= 2) )); then
extkeys_lines="# Extended keys (tmux 3.2+): lets an app inside the pane tell Shift+Enter
# apart from Enter, Ctrl+Enter apart from Ctrl+J, and so on.
#
# 'always' rather than 'on' is deliberate. With 'on', tmux forwards extended
# keys only to a pane that asks for them first; several TUIs -- Claude Code
# among them -- never send that request, so Shift+Enter silently arrives as
# plain Enter and submits instead of inserting a newline.
#
# The default extended-keys-format is xterm (CSI 27~), which Emacs 27+ and
# recent Vim decode natively. Switch to extended-keys-format csi-u only if an
# app you use wants the kitty-style CSI u encoding instead.
#
# This cannot rescue a terminal that never sends the distinct key in the first
# place: VTE terminals (Ptyxis, GNOME Terminal, Tilix, Terminator, Guake) have
# no released kitty-keyboard-protocol support, and JetBrains IDE terminals do
# not either. There, Shift+Enter stays indistinguishable from Enter no matter
# what tmux does -- use Ctrl+J for a newline. Works: kitty, Ghostty, WezTerm,
# iTerm2, Alacritty, foot, Windows Terminal.
set -s extended-keys always
set -as terminal-features ',xterm*:extkeys'"
fi
fi
# Create tmux configuration
cat > "${HOME}/.tmux.conf" <<EOF
# --- 1. Colors & Terminal ---
set -g default-terminal "screen-256color"
set -as terminal-overrides ',xterm*:Tc:smcup@:rmcup@'
set -as terminal-features ',xterm*:clipboard'
${hyperlinks_line}
${extkeys_lines}
set -s set-clipboard on
set -g allow-passthrough on
set -g focus-events on
set -ag update-environment "DISPLAY WAYLAND_DISPLAY XAUTHORITY SSH_AUTH_SOCK SSH_CONNECTION SSH_TTY XDG_SESSION_TYPE"
# --- 2. Visibility & History ---
set -g mouse on
set -g history-limit 1000000
# High-visibility highlight (Bright Cyan)
set -g mode-style "bg=brightcyan,fg=black,bold"
# --- 3. Keybindings ---
set -g mode-keys emacs
setw -g mode-keys emacs
set -g status-keys emacs
# --- 4. System Clipboard & Mouse Selection ---
bind -n MouseDrag1Pane if-shell -F "#{mouse_any_flag}" "send-keys -M" "copy-mode -M"
bind -T copy-mode-emacs MouseDragEnd1Pane send-keys -X ${copy_action}
bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X ${copy_action}
# Copy keys
bind -T copy-mode-emacs Enter send-keys -X ${copy_action}
bind -T copy-mode-emacs M-w send-keys -X ${copy_action}
bind -T copy-mode-emacs C-w send-keys -X ${copy_action}
bind -T copy-mode-vi Enter send-keys -X ${copy_action}
# Selection starting keys
unbind -T copy-mode-emacs C-@
bind -T copy-mode-emacs C-@ send-keys -X begin-selection
# Optional: double-click word, triple-click line
bind -T copy-mode-emacs DoubleClick1Pane select-pane \; send-keys -X select-word \; send-keys -X ${copy_action}
bind -T copy-mode-emacs TripleClick1Pane select-pane \; send-keys -X select-line \; send-keys -X ${copy_action}
bind -T copy-mode-vi DoubleClick1Pane select-pane \; send-keys -X select-word \; send-keys -X ${copy_action}
bind -T copy-mode-vi TripleClick1Pane select-pane \; send-keys -X select-line \; send-keys -X ${copy_action}
# --- 5. Quality of Life ---
set -g base-index 1
setw -g pane-base-index 1
set -g display-time 4000
bind r source-file ~/.tmux.conf \; display-message "Tmux config reloaded!"
EOF
# --- Force apply tmux changes to ALL sessions ---
if command -v tmux >/dev/null 2>&1 && tmux has-session >/dev/null 2>&1; then
tmux list-sessions -F '#S' > /tmp/tmux_sessions.txt
while read -r s; do
tmux set-option -t "$s" -g mode-keys emacs >/dev/null 2>&1 || true
tmux set-window-option -t "$s" -g mode-keys emacs >/dev/null 2>&1 || true
tmux source-file -t "$s" "${HOME}/.tmux.conf" >/dev/null 2>&1 || true
done < /tmp/tmux_sessions.txt
rm /tmp/tmux_sessions.txt
fi
echo "---"
echo "=== HOW TO COPY (Shift+Number Compatible) ==="
echo ""
echo "[Method A: With Mouse / Trackpad]"
echo " • Click & Drag -> Release to auto-copy."
echo ""
echo "[Method B: Keyboard Selection]"
echo " 1. Enter Copy Mode: Ctrl+b ["
echo " 2. Start Selection: Press 'Ctrl+@'"
echo " 3. Copy & Exit: Press 'Enter' (or 'Alt+w' / 'Ctrl+w')"
echo ""
echo "[Method C: Bypass Tmux]"
echo " • Hold SHIFT while dragging with the mouse."
echo "==================="
echo "Reload all sessions: Press Ctrl+b, then 'r'"
echo ""
echo "=== CLICKABLE LINKS (OSC 8) ==="
echo " • OPEN A LINK WITH CTRL+CLICK. With 'mouse on', tmux takes plain"
echo " clicks and Shift+click; only Ctrl reaches the terminal. A link that"
echo " looks like plain text is usually the wrong modifier, not a broken"
echo " config -- try Ctrl+click before changing anything."
echo " • tmux reads terminal-features when a client ATTACHES, so sourcing the"
echo " config is not enough: detach and reattach ('tmux detach', then"
echo " 'tmux attach') before hyperlinks work."
echo " • Some CLIs refuse to emit OSC 8 when TERM is screen-*, and"
echo " FORCE_HYPERLINK=1 overrides that -- but do NOT set it globally."
echo " Inside a full-screen app that grabs the mouse (check with"
echo " 'tmux display -p \"#{mouse_any_flag}\"'), clicks go to the app, so an"
echo " OSC 8 link cannot be activated at all -- while a PLAIN-TEXT URL"
echo " stays Ctrl+clickable via the terminal's own URL matching. Forcing"
echo " hyperlinks there turns working URLs into dead ones."
echo " • Verify the whole chain without involving any CLI:"
echo " printf '\\e]8;;https://example.com\\e\\\\LINK\\e]8;;\\e\\\\\\n'"
echo " Run it in a PLAIN pane. Piping it through a tool that captures and"
echo " re-renders output proves nothing -- escapes never reach the tty."
echo " • Your terminal emulator must render OSC 8: Ptyxis, GNOME Terminal and"
echo " other VTE terminals do, as do kitty, WezTerm, Ghostty, iTerm2 and"
echo " Windows Terminal. Plain URLs stay clickable either way."
echo ""
echo "=== TUI CODING AGENTS (Claude Code, Codex, and friends) ==="
echo " • SHIFT+ENTER for a newline needs 'extended-keys always', set above."
echo " 'on' is not enough: tmux then waits for the pane to request extended"
echo " keys and these apps never do. Applies on source-file, but the"
echo " matching extkeys terminal-feature is read at ATTACH time -- detach and"
echo " reattach. On a VTE terminal (Ptyxis, GNOME Terminal) the key never"
echo " leaves the emulator, so use Ctrl+J instead; no tmux setting fixes it."
echo " • DESKTOP NOTIFICATIONS and progress-bar escapes reach the outer"
echo " terminal through 'allow-passthrough on', set above."
echo " • OSC 8 LINKS printed by these apps are usually NOT clickable, and this"
echo " config is not the reason. They hold the mouse (mouse_any_flag=1) and"
echo " re-render output through their own layer, so the escape either never"
echo " reaches the tty or the click never reaches the terminal. Plain-text"
echo " URLs stay Ctrl+clickable via the terminal's own URL matching, which"
echo " is why links look fine until you try a labelled one."
echo " • PASTING AN IMAGE from the clipboard does not work through tmux: tmux"
echo " forwards text only. Save the image to a file and pass the path."
@HackingGate

HackingGate commented Mar 25, 2026

Copy link
Copy Markdown
Author
bash <(curl -fsSL https://gist.githubusercontent.com/HackingGate/ebc5bd4d474d6aaffa05bfb84cc1e60f/raw/tmux.sh)

Clipboard copy, automatic light/dark theme switching, and a longer tmux message display time are included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment