From 2c22432253633a1647d34bb9091f0fe852e72cc4 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 10 Jan 2026 02:17:41 +0100 Subject: [PATCH 1/2] feat(tmux): add worktree attach and extract tmux config - Add tmux-worktree-attach script with fzf picker (prefix+w) - Extract all tmux config to common_tmux.nix - Fix worktree scripts to work from inside worktrees (use --git-common-dir to find main repo root) --- homeConfigurations/profiles/common.nix | 132 +------------ homeConfigurations/profiles/common_tmux.nix | 196 ++++++++++++++++++++ 2 files changed, 197 insertions(+), 131 deletions(-) create mode 100644 homeConfigurations/profiles/common_tmux.nix diff --git a/homeConfigurations/profiles/common.nix b/homeConfigurations/profiles/common.nix index d9b577d..101feae 100644 --- a/homeConfigurations/profiles/common.nix +++ b/homeConfigurations/profiles/common.nix @@ -7,6 +7,7 @@ { imports = [ (import ./common_neovim.nix) + (import ./common_tmux.nix) (import ./modules/onepassword-secrets.nix) ]; @@ -48,55 +49,6 @@ _1password-cli bash-language-server - - # Custom scripts - (writeShellScriptBin "tmux-worktree" '' - # tmux-worktree: Create git worktree and open tmux window with claude - set -euo pipefail - - NAME="$1" - - if [ -z "$NAME" ]; then - tmux display-message "Error: No worktree name provided" - exit 1 - fi - - # Get the git root from the current pane's directory - PANE_PATH=$(tmux display-message -p '#{pane_current_path}') - REPO_ROOT=$(cd "$PANE_PATH" && ${git}/bin/git rev-parse --show-toplevel 2>/dev/null) - - if [ -z "$REPO_ROOT" ]; then - tmux display-message "Error: Not in a git repository" - exit 1 - fi - - WORKTREE_PATH="$REPO_ROOT/w/$NAME" - - # Check if worktree already exists - if [ -d "$WORKTREE_PATH" ]; then - tmux display-message "Worktree already exists, opening: $WORKTREE_PATH" - else - # Create the w/ directory if it doesn't exist - mkdir -p "$REPO_ROOT/w" - - # Create worktree with new branch - if ! ${git}/bin/git -C "$REPO_ROOT" worktree add "$WORKTREE_PATH" -b "$NAME" 2>&1; then - tmux display-message "Error: Failed to create worktree" - exit 1 - fi - fi - - # Create new window in the worktree directory - tmux new-window -c "$WORKTREE_PATH" -n "$NAME" - - # Split horizontally (left/right) and run claude on the right - tmux split-window -h -c "$WORKTREE_PATH" "claude" - - # Focus the left pane (the shell) - tmux select-pane -L - - tmux display-message "Worktree ready: w/$NAME" - '') ]; # So happy when home manager is almost having Ghostty support hours after release: @@ -272,88 +224,6 @@ HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 ''; - programs.tmux.enable = true; - programs.tmux.clock24 = true; - programs.tmux.keyMode = "vi"; - programs.tmux.historyLimit = 10000; - programs.tmux.newSession = true; - programs.tmux.prefix = "C-Space"; - programs.tmux.shortcut = "Space"; - programs.tmux.baseIndex = 1; - programs.tmux.mouse = true; - programs.tmux.shell = "${pkgs.zsh}/bin/zsh"; - programs.tmux.plugins = with pkgs.tmuxPlugins; [ - catppuccin - tmux-fzf - ]; - programs.tmux.extraConfig = '' - set-option -g set-clipboard on - - # Auto-rename window to current folder name - set-option -g status-interval 1 - set-option -g automatic-rename on - set-option -g automatic-rename-format '#{b:pane_current_path}' - - # allow terminal scrolling - set-option -g terminal-overrides 'xterm*:smcup@:rmcup@' - - # New windows start from home directory - bind c new-window -c "~" - - # Splits stay in current directory - bind | split-window -h -c "#{pane_current_path}" - bind - split-window -v -c "#{pane_current_path}" - unbind '"' - unbind % - - # allow to use mouse - set -g mouse on - - # panes - set -g pane-border-style fg=black - set -g pane-active-border-style fg=brightred - - # moving between panes vim style - bind h select-pane -L - bind j select-pane -D - bind k select-pane -U - bind l select-pane -R - - # resize the pane - bind-key -r H resize-pane -L 3 - bind-key -r J resize-pane -D 3 - bind-key -r K resize-pane -U 3 - bind-key -r L resize-pane -R 3 - - # Activity monitoring for attention notifications - # This will highlight windows when they have activity (output) or bell signals - set-option -g monitor-activity on - set-option -g monitor-bell on - set-option -g activity-action none # Don't switch windows automatically - set-option -g bell-action any # Monitor bells in any window - set-option -g visual-activity off # Don't show "Activity in window X" message - set-option -g visual-bell off # Don't show bell message - - set -g status-left-length 100 - set -g status-left "#h " - set-option -g @catppuccin_window_text " #W" - set-option -g @catppuccin_window_current_text " #W" - set-option -g status-right "#{E:@catppuccin_status_date_time}" - - # Override catppuccin formats to use window name (#W) instead of pane title (#T) - # Normal window (no activity) - set-option -g window-status-format "#[fg=#11111b,bg=#{@thm_overlay_2}] #I #[fg=#cdd6f4,bg=#{@thm_surface_0}] #W " - # Current window - set-option -g window-status-current-format "#[fg=#11111b,bg=#{@thm_mauve}] #I #[fg=#cdd6f4,bg=#{@thm_surface_1}] #W " - # Window with activity/bell - using Catppuccin yellow (#f9e2af) - set-option -g window-status-activity-style "fg=#11111b,bg=#f9e2af,bold" - set-option -g window-status-bell-style "fg=#11111b,bg=#f9e2af,bold" - - # Worktree workflow: prefix + W prompts for name, creates worktree, opens split with claude - bind W command-prompt -p "Worktree name:" "run-shell 'tmux-worktree \"%%\"'" - - ''; - # XXX: this are UI programs #programs.vscode.enable = true; diff --git a/homeConfigurations/profiles/common_tmux.nix b/homeConfigurations/profiles/common_tmux.nix new file mode 100644 index 0000000..669f77b --- /dev/null +++ b/homeConfigurations/profiles/common_tmux.nix @@ -0,0 +1,196 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + # tmux-worktree: Create git worktree and open tmux window with claude + (writeShellScriptBin "tmux-worktree" '' + set -euo pipefail + + NAME="$1" + + if [ -z "$NAME" ]; then + tmux display-message "Error: No worktree name provided" + exit 1 + fi + + # Get the MAIN repo root (not worktree root) from the current pane's directory + # Using --git-common-dir to handle being inside a worktree + PANE_PATH=$(tmux display-message -p '#{pane_current_path}') + GIT_COMMON_DIR=$(cd "$PANE_PATH" && ${git}/bin/git rev-parse --git-common-dir 2>/dev/null) + + if [ -z "$GIT_COMMON_DIR" ]; then + tmux display-message "Error: Not in a git repository" + exit 1 + fi + + # Get the main repo root (parent of .git directory) + REPO_ROOT=$(dirname "$GIT_COMMON_DIR") + + WORKTREE_PATH="$REPO_ROOT/w/$NAME" + + # Check if worktree already exists + if [ -d "$WORKTREE_PATH" ]; then + tmux display-message "Worktree already exists, opening: $WORKTREE_PATH" + else + # Create the w/ directory if it doesn't exist + mkdir -p "$REPO_ROOT/w" + + # Create worktree with new branch + if ! ${git}/bin/git -C "$REPO_ROOT" worktree add "$WORKTREE_PATH" -b "$NAME" 2>&1; then + tmux display-message "Error: Failed to create worktree" + exit 1 + fi + fi + + # Create new window in the worktree directory + tmux new-window -c "$WORKTREE_PATH" -n "$NAME" + + # Split horizontally (left/right) and run claude on the right + tmux split-window -h -c "$WORKTREE_PATH" "claude" + + # Focus the left pane (the shell) + tmux select-pane -L + + tmux display-message "Worktree ready: w/$NAME" + '') + + # tmux-worktree-attach: Select and attach to existing worktree + (writeShellScriptBin "tmux-worktree-attach" '' + set -euo pipefail + + # Get the MAIN repo root (not worktree root) from the current pane's directory + # Using --git-common-dir to handle being inside a worktree + PANE_PATH=$(tmux display-message -p '#{pane_current_path}') + GIT_COMMON_DIR=$(cd "$PANE_PATH" && ${git}/bin/git rev-parse --git-common-dir 2>/dev/null) + + if [ -z "$GIT_COMMON_DIR" ]; then + tmux display-message "Error: Not in a git repository" + exit 1 + fi + + # Get the main repo root (parent of .git directory) + REPO_ROOT=$(dirname "$GIT_COMMON_DIR") + + WORKTREE_DIR="$REPO_ROOT/w" + + # Check if worktree directory exists + if [ ! -d "$WORKTREE_DIR" ]; then + tmux display-message "No worktrees found (w/ directory doesn't exist)" + exit 1 + fi + + # List worktree directories + WORKTREES=$(find "$WORKTREE_DIR" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort) + + if [ -z "$WORKTREES" ]; then + tmux display-message "No worktrees found in w/ directory" + exit 1 + fi + + # Use fzf-tmux for selection + SELECTED=$(echo "$WORKTREES" | ${fzf}/bin/fzf-tmux -p --reverse --header="Select worktree to attach:") + + if [ -z "$SELECTED" ]; then + # User cancelled + exit 0 + fi + + WORKTREE_PATH="$WORKTREE_DIR/$SELECTED" + + # Create new window in the worktree directory + tmux new-window -c "$WORKTREE_PATH" -n "$SELECTED" + + # Split horizontally (left/right) and run claude on the right + tmux split-window -h -c "$WORKTREE_PATH" "claude" + + # Focus the left pane (the shell) + tmux select-pane -L + + tmux display-message "Attached to worktree: w/$SELECTED" + '') + ]; + + programs.tmux.enable = true; + programs.tmux.clock24 = true; + programs.tmux.keyMode = "vi"; + programs.tmux.historyLimit = 10000; + programs.tmux.newSession = true; + programs.tmux.prefix = "C-Space"; + programs.tmux.shortcut = "Space"; + programs.tmux.baseIndex = 1; + programs.tmux.mouse = true; + programs.tmux.shell = "${pkgs.zsh}/bin/zsh"; + programs.tmux.plugins = with pkgs.tmuxPlugins; [ + catppuccin + tmux-fzf + ]; + programs.tmux.extraConfig = '' + set-option -g set-clipboard on + + # Auto-rename window to current folder name + set-option -g status-interval 1 + set-option -g automatic-rename on + set-option -g automatic-rename-format '#{b:pane_current_path}' + + # allow terminal scrolling + set-option -g terminal-overrides 'xterm*:smcup@:rmcup@' + + # New windows start from home directory + bind c new-window -c "~" + + # Splits stay in current directory + bind | split-window -h -c "#{pane_current_path}" + bind - split-window -v -c "#{pane_current_path}" + unbind '"' + unbind % + + # allow to use mouse + set -g mouse on + + # panes + set -g pane-border-style fg=black + set -g pane-active-border-style fg=brightred + + # moving between panes vim style + bind h select-pane -L + bind j select-pane -D + bind k select-pane -U + bind l select-pane -R + + # resize the pane + bind-key -r H resize-pane -L 3 + bind-key -r J resize-pane -D 3 + bind-key -r K resize-pane -U 3 + bind-key -r L resize-pane -R 3 + + # Activity monitoring for attention notifications + # This will highlight windows when they have activity (output) or bell signals + set-option -g monitor-activity on + set-option -g monitor-bell on + set-option -g activity-action none # Don't switch windows automatically + set-option -g bell-action any # Monitor bells in any window + set-option -g visual-activity off # Don't show "Activity in window X" message + set-option -g visual-bell off # Don't show bell message + + set -g status-left-length 100 + set -g status-left "#h " + set-option -g @catppuccin_window_text " #W" + set-option -g @catppuccin_window_current_text " #W" + set-option -g status-right "#{E:@catppuccin_status_date_time}" + + # Override catppuccin formats to use window name (#W) instead of pane title (#T) + # Normal window (no activity) + set-option -g window-status-format "#[fg=#11111b,bg=#{@thm_overlay_2}] #I #[fg=#cdd6f4,bg=#{@thm_surface_0}] #W " + # Current window + set-option -g window-status-current-format "#[fg=#11111b,bg=#{@thm_mauve}] #I #[fg=#cdd6f4,bg=#{@thm_surface_1}] #W " + # Window with activity/bell - using Catppuccin yellow (#f9e2af) + set-option -g window-status-activity-style "fg=#11111b,bg=#f9e2af,bold" + set-option -g window-status-bell-style "fg=#11111b,bg=#f9e2af,bold" + + # Worktree workflow: prefix + W prompts for name, creates worktree, opens split with claude + bind W command-prompt -p "Worktree name:" "run-shell 'tmux-worktree \"%%\"'" + + # Worktree attach: prefix + w opens fzf picker to select existing worktree + bind w run-shell "tmux-worktree-attach" + + ''; +} From cb14dab7cae316b2354c8c272a27fa34cdec722f Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 10 Jan 2026 02:20:25 +0100 Subject: [PATCH 2/2] feat(tmux): dim inactive panes for better focus --- homeConfigurations/profiles/common_tmux.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeConfigurations/profiles/common_tmux.nix b/homeConfigurations/profiles/common_tmux.nix index 669f77b..b1e9c5e 100644 --- a/homeConfigurations/profiles/common_tmux.nix +++ b/homeConfigurations/profiles/common_tmux.nix @@ -150,6 +150,10 @@ set -g pane-border-style fg=black set -g pane-active-border-style fg=brightred + # Dim inactive panes (brighter background for inactive, keeps text colors intact) + set -g window-style 'bg=#313244,fg=#cdd6f4' + set -g window-active-style 'bg=#1e1e2e,fg=#cdd6f4' + # moving between panes vim style bind h select-pane -L bind j select-pane -D