Auto-sync from catchthesethighs
This commit is contained in:
575
zsh/functions/tmux-workspaces.zsh
Normal file
575
zsh/functions/tmux-workspaces.zsh
Normal file
@@ -0,0 +1,575 @@
|
||||
# ============================================================================
|
||||
# Tmux Workspace Manager - Project Templates & Layouts
|
||||
# ============================================================================
|
||||
# Quick project workspace setup with pre-configured tmux layouts
|
||||
#
|
||||
# Usage:
|
||||
# tw-create <name> [template] # Create workspace from template
|
||||
# tw-attach <name> # Attach to workspace
|
||||
# tw-list # List all workspaces
|
||||
# tw-delete <name> # Delete workspace
|
||||
# tw-save <name> # Save current layout as template
|
||||
# tw <name> # Quick attach (or create if not exists)
|
||||
#
|
||||
# Templates:
|
||||
# dev - Vim (50%) + terminal (25%) + logs (25%)
|
||||
# ops - 4 panes: htop, logs, shell, monitoring
|
||||
# ssh-multi - 4 panes for managing multiple servers
|
||||
# debug - 2 panes: main (70%) + helper (30%)
|
||||
# full - Just one full pane
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/tmux-workspaces.zsh
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
typeset -g TW_TEMPLATES_DIR="${TW_TEMPLATES_DIR:-$HOME/.dotfiles/.tmux-templates}"
|
||||
typeset -g TW_SESSION_PREFIX="${TW_SESSION_PREFIX:-work}"
|
||||
typeset -g TW_DEFAULT_TEMPLATE="${TW_DEFAULT_TEMPLATE:-dev}"
|
||||
|
||||
# Colors
|
||||
typeset -g TW_GREEN=$'\033[0;32m'
|
||||
typeset -g TW_BLUE=$'\033[0;34m'
|
||||
typeset -g TW_YELLOW=$'\033[1;33m'
|
||||
typeset -g TW_CYAN=$'\033[0;36m'
|
||||
typeset -g TW_RED=$'\033[0;31m'
|
||||
typeset -g TW_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
_tw_print_step() {
|
||||
echo -e "${TW_BLUE}==>${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_success() {
|
||||
echo -e "${TW_GREEN}✓${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_error() {
|
||||
echo -e "${TW_RED}✗${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_info() {
|
||||
echo -e "${TW_CYAN}ℹ${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_check_tmux() {
|
||||
if ! command -v tmux &>/dev/null; then
|
||||
_tw_print_error "tmux not installed"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
_tw_init_templates() {
|
||||
mkdir -p "$TW_TEMPLATES_DIR"
|
||||
|
||||
# Create default templates if they don't exist
|
||||
if [[ ! -f "$TW_TEMPLATES_DIR/dev.tmux" ]]; then
|
||||
_tw_create_default_templates
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Default Template Definitions
|
||||
# ============================================================================
|
||||
|
||||
_tw_create_default_templates() {
|
||||
_tw_print_step "Creating default templates..."
|
||||
|
||||
# Development template - vim + terminal + logs
|
||||
cat > "$TW_TEMPLATES_DIR/dev.tmux" << 'EOF'
|
||||
# Development workspace
|
||||
# Usage: tw-create myproject dev
|
||||
|
||||
# Split vertically (vim on left 50%, rest on right)
|
||||
split-window -h -p 50
|
||||
|
||||
# Split right pane horizontally (terminal top, logs bottom)
|
||||
split-window -v -p 50
|
||||
|
||||
# Select the first pane (vim)
|
||||
select-pane -t 0
|
||||
|
||||
# Optional: Start vim in first pane
|
||||
# send-keys -t 0 'vim' C-m
|
||||
|
||||
# Optional: Set pane titles
|
||||
# select-pane -t 0 -T "Editor"
|
||||
# select-pane -t 1 -T "Terminal"
|
||||
# select-pane -t 2 -T "Logs"
|
||||
EOF
|
||||
|
||||
# Operations template - 4 panes for monitoring
|
||||
cat > "$TW_TEMPLATES_DIR/ops.tmux" << 'EOF'
|
||||
# Operations workspace
|
||||
# 4-pane layout for system monitoring
|
||||
|
||||
# Create 2x2 grid
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
|
||||
# Optional: Auto-start monitoring tools
|
||||
# send-keys -t 0 'htop' C-m
|
||||
# send-keys -t 1 'docker ps' C-m
|
||||
# send-keys -t 2 '' C-m
|
||||
# send-keys -t 3 'tail -f /var/log/syslog' C-m
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# SSH multi-server template
|
||||
cat > "$TW_TEMPLATES_DIR/ssh-multi.tmux" << 'EOF'
|
||||
# Multi-server SSH workspace
|
||||
# 4 panes for managing multiple servers
|
||||
|
||||
# Create 2x2 grid
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
|
||||
# Enable pane synchronization (optional - uncomment to enable)
|
||||
# set-window-option synchronize-panes on
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# Debug template - main + helper pane
|
||||
cat > "$TW_TEMPLATES_DIR/debug.tmux" << 'EOF'
|
||||
# Debug workspace
|
||||
# Main pane (70%) + helper pane (30%)
|
||||
|
||||
split-window -h -p 30
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# Full template - single pane
|
||||
cat > "$TW_TEMPLATES_DIR/full.tmux" << 'EOF'
|
||||
# Full workspace
|
||||
# Single full-screen pane (default tmux behavior)
|
||||
EOF
|
||||
|
||||
# Code review template - side-by-side comparison
|
||||
cat > "$TW_TEMPLATES_DIR/review.tmux" << 'EOF'
|
||||
# Code Review workspace
|
||||
# Two equal panes side-by-side for comparison
|
||||
|
||||
split-window -h -p 50
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
_tw_print_success "Created default templates in: $TW_TEMPLATES_DIR"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Template Management
|
||||
# ============================================================================
|
||||
|
||||
tw-templates() {
|
||||
_tw_init_templates
|
||||
|
||||
echo -e "${TW_BLUE}╔════════════════════════════════════════════════════════════╗${TW_NC}"
|
||||
echo -e "${TW_BLUE}║${TW_NC} Available Tmux Templates ${TW_BLUE}║${TW_NC}"
|
||||
echo -e "${TW_BLUE}╚════════════════════════════════════════════════════════════╝${TW_NC}"
|
||||
echo
|
||||
|
||||
for template in "$TW_TEMPLATES_DIR"/*.tmux; do
|
||||
[[ ! -f "$template" ]] && continue
|
||||
|
||||
local name=$(basename "$template" .tmux)
|
||||
local description=$(grep "^#" "$template" | head -2 | tail -1 | sed 's/^# *//')
|
||||
|
||||
echo -e "${TW_GREEN}●${TW_NC} ${TW_CYAN}$name${TW_NC}"
|
||||
[[ -n "$description" ]] && echo " $description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Create workspace: ${TW_CYAN}tw-create myproject dev${TW_NC}"
|
||||
echo "Quick attach: ${TW_CYAN}tw myproject${TW_NC}"
|
||||
}
|
||||
|
||||
tw-template-edit() {
|
||||
local template_name="$1"
|
||||
|
||||
if [[ -z "$template_name" ]]; then
|
||||
echo "Usage: tw-template-edit <template_name>"
|
||||
echo
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
|
||||
${EDITOR:-vim} "$template_file"
|
||||
|
||||
_tw_print_success "Template edited: $template_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Workspace Management
|
||||
# ============================================================================
|
||||
|
||||
tw-create() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-create <workspace_name> [template]"
|
||||
echo
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
_tw_init_templates
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
# Check if session already exists
|
||||
if tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' already exists"
|
||||
echo "Use: ${TW_CYAN}tw $workspace_name${TW_NC} to attach"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if template exists
|
||||
local template_file="$TW_TEMPLATES_DIR/${template}.tmux"
|
||||
if [[ ! -f "$template_file" ]]; then
|
||||
_tw_print_error "Template '$template' not found"
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_print_step "Creating workspace: $workspace_name (template: $template)"
|
||||
|
||||
# Create new tmux session (detached)
|
||||
tmux new-session -d -s "$session_name"
|
||||
|
||||
# Apply template
|
||||
_tw_print_step "Applying template: $template"
|
||||
tmux source-file "$template_file" -t "$session_name"
|
||||
|
||||
# Set working directory if we're in a git repo or specific directory
|
||||
if git rev-parse --git-dir &>/dev/null 2>&1; then
|
||||
local git_root=$(git rev-parse --show-toplevel)
|
||||
_tw_print_info "Setting workspace directory to: $git_root"
|
||||
tmux send-keys -t "$session_name:0" "cd $git_root" C-m
|
||||
fi
|
||||
|
||||
_tw_print_success "Workspace created: $workspace_name"
|
||||
|
||||
# Attach if not already in tmux
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_step "Attaching to workspace..."
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
_tw_print_info "Switch with: ${TW_CYAN}tmux switch-client -t $session_name${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-attach() {
|
||||
local workspace_name="$1"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-attach <workspace_name>"
|
||||
echo
|
||||
tw-list
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
if ! tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' not found"
|
||||
echo
|
||||
echo "Create it with: ${TW_CYAN}tw-create $workspace_name${TW_NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Attach or switch
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
tmux switch-client -t "$session_name"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-list() {
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
echo -e "${TW_BLUE}╔════════════════════════════════════════════════════════════╗${TW_NC}"
|
||||
echo -e "${TW_BLUE}║${TW_NC} Active Tmux Workspaces ${TW_BLUE}║${TW_NC}"
|
||||
echo -e "${TW_BLUE}╚════════════════════════════════════════════════════════════╝${TW_NC}"
|
||||
echo
|
||||
|
||||
local has_workspaces=false
|
||||
|
||||
# List all tmux sessions
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r session_full rest; do
|
||||
# Only show sessions with our prefix
|
||||
if [[ "$session_full" == ${TW_SESSION_PREFIX}-* ]]; then
|
||||
has_workspaces=true
|
||||
local workspace_name="${session_full#${TW_SESSION_PREFIX}-}"
|
||||
local attached=""
|
||||
|
||||
# Check if currently attached
|
||||
if [[ -n "$TMUX" ]]; then
|
||||
local current_session=$(tmux display-message -p '#S')
|
||||
[[ "$current_session" == "$session_full" ]] && attached=" ${TW_GREEN}(current)${TW_NC}"
|
||||
fi
|
||||
|
||||
echo -e "${TW_GREEN}●${TW_NC} ${TW_CYAN}$workspace_name${TW_NC}$attached"
|
||||
echo " Session: $session_full"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$has_workspaces" != true ]]; then
|
||||
_tw_print_info "No active workspaces"
|
||||
echo
|
||||
echo "Create one with: ${TW_CYAN}tw-create myproject${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-delete() {
|
||||
local workspace_name="$1"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-delete <workspace_name>"
|
||||
echo
|
||||
tw-list
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
if ! tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Kill session
|
||||
tmux kill-session -t "$session_name"
|
||||
|
||||
_tw_print_success "Deleted workspace: $workspace_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Save Current Layout as Template
|
||||
# ============================================================================
|
||||
|
||||
tw-save() {
|
||||
local template_name="$1"
|
||||
|
||||
if [[ -z "$template_name" ]]; then
|
||||
echo "Usage: tw-save <template_name>"
|
||||
echo
|
||||
echo "Saves the current tmux window layout as a reusable template"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
|
||||
if [[ -f "$template_file" ]]; then
|
||||
read -q "REPLY?Template '$template_name' exists. Overwrite? [y/N]: "
|
||||
echo
|
||||
[[ ! "$REPLY" =~ ^[Yy]$ ]] && return 1
|
||||
fi
|
||||
|
||||
_tw_print_step "Saving current layout as template: $template_name"
|
||||
|
||||
# Get current window layout
|
||||
local layout=$(tmux display-message -p '#{window_layout}')
|
||||
local pane_count=$(tmux display-message -p '#{window_panes}')
|
||||
|
||||
# Create template with layout commands
|
||||
cat > "$template_file" << EOF
|
||||
# Custom template: $template_name
|
||||
# Saved: $(date)
|
||||
# Panes: $pane_count
|
||||
|
||||
# Note: This is a simplified layout recreation
|
||||
# You may need to adjust split percentages and commands
|
||||
|
||||
EOF
|
||||
|
||||
# Generate split commands based on pane count
|
||||
if (( pane_count == 2 )); then
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
elif (( pane_count == 3 )); then
|
||||
cat >> "$template_file" << 'EOF'
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
EOF
|
||||
elif (( pane_count == 4 )); then
|
||||
cat >> "$template_file" << 'EOF'
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "" >> "$template_file"
|
||||
echo "select-pane -t 0" >> "$template_file"
|
||||
|
||||
_tw_print_success "Template saved: $template_name"
|
||||
echo " File: $template_file"
|
||||
echo " Edit: ${TW_CYAN}tw-template-edit $template_name${TW_NC}"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Quick Workspace (attach or create)
|
||||
# ============================================================================
|
||||
|
||||
tw() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
tw-list
|
||||
return 0
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
# If session exists, attach. Otherwise create.
|
||||
if tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
tw-attach "$workspace_name"
|
||||
else
|
||||
_tw_print_info "Workspace doesn't exist. Creating with template: $template"
|
||||
tw-create "$workspace_name" "$template"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Fuzzy Search (requires fzf)
|
||||
# ============================================================================
|
||||
|
||||
twf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_tw_print_error "fzf not installed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
# Get list of sessions
|
||||
local sessions=()
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r session_full rest; do
|
||||
if [[ "$session_full" == ${TW_SESSION_PREFIX}-* ]]; then
|
||||
local workspace_name="${session_full#${TW_SESSION_PREFIX}-}"
|
||||
sessions+=("$workspace_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#sessions[@]} -eq 0 ]]; then
|
||||
_tw_print_info "No workspaces found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Fuzzy select
|
||||
local selection=$(printf '%s\n' "${sessions[@]}" | \
|
||||
fzf --height=40% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--prompt='Workspace > ' \
|
||||
--preview='tmux list-windows -t work-{} 2>/dev/null || echo "No preview"')
|
||||
|
||||
if [[ -n "$selection" ]]; then
|
||||
tw-attach "$selection"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Pane Synchronization Toggle
|
||||
# ============================================================================
|
||||
|
||||
tw-sync() {
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local current=$(tmux show-window-option -v synchronize-panes 2>/dev/null)
|
||||
|
||||
if [[ "$current" == "on" ]]; then
|
||||
tmux set-window-option synchronize-panes off
|
||||
_tw_print_info "Pane synchronization: ${TW_RED}OFF${TW_NC}"
|
||||
else
|
||||
tmux set-window-option synchronize-panes on
|
||||
_tw_print_info "Pane synchronization: ${TW_GREEN}ON${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Rename Workspace
|
||||
# ============================================================================
|
||||
|
||||
tw-rename() {
|
||||
local old_name="$1"
|
||||
local new_name="$2"
|
||||
|
||||
if [[ -z "$old_name" || -z "$new_name" ]]; then
|
||||
echo "Usage: tw-rename <old_name> <new_name>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local old_session="${TW_SESSION_PREFIX}-${old_name}"
|
||||
local new_session="${TW_SESSION_PREFIX}-${new_name}"
|
||||
|
||||
if ! tmux has-session -t "$old_session" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$old_name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
tmux rename-session -t "$old_session" "$new_session"
|
||||
|
||||
_tw_print_success "Renamed: $old_name → $new_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Aliases
|
||||
# ============================================================================
|
||||
|
||||
alias twl='tw-list'
|
||||
alias twc='tw-create'
|
||||
alias twa='tw-attach'
|
||||
alias twd='tw-delete'
|
||||
alias tws='tw-save'
|
||||
alias twt='tw-templates'
|
||||
alias twe='tw-template-edit'
|
||||
|
||||
# ============================================================================
|
||||
# Initialization
|
||||
# ============================================================================
|
||||
|
||||
_tw_init_templates
|
||||
Reference in New Issue
Block a user