Dotfiles update 2025-12-25 10:23
This commit is contained in:
@@ -1,380 +1,116 @@
|
||||
# ============================================================================
|
||||
# Tmux Workspace Manager - Project Templates & Layouts
|
||||
# ============================================================================
|
||||
# Quick project workspace setup with pre-configured tmux layouts
|
||||
# ============================================================================
|
||||
|
||||
# Source shared colors (with fallback)
|
||||
source "${0:A:h}/../lib/colors.zsh" 2>/dev/null || \
|
||||
source "$HOME/.dotfiles/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
typeset -g DF_GREEN=$'\033[0;32m' DF_BLUE=$'\033[0;34m'
|
||||
typeset -g DF_YELLOW=$'\033[1;33m' DF_CYAN=$'\033[0;36m'
|
||||
typeset -g DF_RED=$'\033[0;31m' DF_NC=$'\033[0m'
|
||||
source "${0:A:h}/../lib/utils.zsh" 2>/dev/null || \
|
||||
source "$HOME/.dotfiles/zsh/lib/utils.zsh" 2>/dev/null
|
||||
|
||||
typeset -g TW_TEMPLATES="${TW_TEMPLATES:-$HOME/.dotfiles/.tmux-templates}"
|
||||
typeset -g TW_PREFIX="${TW_PREFIX:-work}"
|
||||
typeset -g TW_DEFAULT="${TW_DEFAULT:-dev}"
|
||||
|
||||
_tw_check() { df_require_cmd tmux || return 1; }
|
||||
|
||||
_tw_init() {
|
||||
df_ensure_dir "$TW_TEMPLATES"
|
||||
[[ ! -f "$TW_TEMPLATES/dev.tmux" ]] && {
|
||||
echo -e "# Dev layout\nsplit-window -h -p 50\nsplit-window -v -p 50\nselect-pane -t 0" > "$TW_TEMPLATES/dev.tmux"
|
||||
echo -e "# Ops layout\nsplit-window -h\nsplit-window -v\nselect-pane -t 0\nsplit-window -v\nselect-pane -t 0" > "$TW_TEMPLATES/ops.tmux"
|
||||
echo "# Full\n" > "$TW_TEMPLATES/full.tmux"
|
||||
df_print_success "Created default templates"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 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}"
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
_tw_print_step() { echo -e "${DF_BLUE}==>${DF_NC} $1"; }
|
||||
_tw_print_success() { echo -e "${DF_GREEN}✓${DF_NC} $1"; }
|
||||
_tw_print_error() { echo -e "${DF_RED}✗${DF_NC} $1"; }
|
||||
_tw_print_info() { echo -e "${DF_CYAN}ℹ${DF_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"
|
||||
[[ ! -f "$TW_TEMPLATES_DIR/dev.tmux" ]] && _tw_create_default_templates
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Default Template Definitions
|
||||
# ============================================================================
|
||||
|
||||
_tw_create_default_templates() {
|
||||
_tw_print_step "Creating default templates..."
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/dev.tmux" << 'EOF'
|
||||
# Development workspace
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/ops.tmux" << 'EOF'
|
||||
# Operations workspace - 4 panes
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/ssh-multi.tmux" << 'EOF'
|
||||
# Multi-server SSH workspace
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/debug.tmux" << 'EOF'
|
||||
# Debug workspace
|
||||
split-window -h -p 30
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/full.tmux" << 'EOF'
|
||||
# Full workspace - single pane
|
||||
EOF
|
||||
|
||||
cat > "$TW_TEMPLATES_DIR/review.tmux" << 'EOF'
|
||||
# Code Review workspace
|
||||
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
|
||||
|
||||
df_print_func_name "Available Tmux Templates"
|
||||
|
||||
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 "${DF_GREEN}●${DF_NC} ${DF_CYAN}$name${DF_NC}"
|
||||
[[ -n "$description" ]] && echo " $description"
|
||||
_tw_init
|
||||
df_print_func_name "Tmux Templates"
|
||||
for t in "$TW_TEMPLATES"/*.tmux; do
|
||||
[[ -f "$t" ]] && df_print_indent "● $(basename "$t" .tmux)"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Create workspace: ${DF_CYAN}tw-create myproject dev${DF_NC}"
|
||||
echo "Quick attach: ${DF_CYAN}tw myproject${DF_NC}"
|
||||
echo ""
|
||||
df_print_info "Create: tw-create <name> <template>"
|
||||
}
|
||||
|
||||
tw-template-edit() {
|
||||
local template_name="$1"
|
||||
[[ -z "$template_name" ]] && { echo "Usage: tw-template-edit <template_name>"; tw-templates; return 1; }
|
||||
_tw_init_templates
|
||||
${EDITOR:-vim} "$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
_tw_print_success "Template edited: $template_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Workspace Management
|
||||
# ============================================================================
|
||||
|
||||
tw-create() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
local name="$1" tmpl="${2:-$TW_DEFAULT}"
|
||||
[[ -z "$name" ]] && { tw-templates; return 1; }
|
||||
_tw_check || return 1
|
||||
_tw_init
|
||||
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-create <workspace_name> [template]"; tw-templates; return 1; }
|
||||
local session="${TW_PREFIX}-${name}"
|
||||
tmux has-session -t "$session" 2>/dev/null && { df_print_error "'$name' exists"; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
_tw_init_templates
|
||||
local tfile="$TW_TEMPLATES/${tmpl}.tmux"
|
||||
[[ ! -f "$tfile" ]] && { df_print_error "Template '$tmpl' not found"; tw-templates; return 1; }
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
df_print_step "Creating: $name (template: $tmpl)"
|
||||
tmux new-session -d -s "$session"
|
||||
tmux source-file "$tfile" -t "$session"
|
||||
|
||||
if tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' already exists"
|
||||
echo "Use: ${DF_CYAN}tw $workspace_name${DF_NC} to attach"
|
||||
return 1
|
||||
fi
|
||||
df_in_git_repo && {
|
||||
local root=$(df_git_root)
|
||||
df_print_info "Git root: $root"
|
||||
tmux send-keys -t "$session:0" "cd $root" C-m
|
||||
}
|
||||
|
||||
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)"
|
||||
|
||||
tmux new-session -d -s "$session_name"
|
||||
_tw_print_step "Applying template: $template"
|
||||
tmux source-file "$template_file" -t "$session_name"
|
||||
|
||||
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"
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_step "Attaching to workspace..."
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
_tw_print_info "Switch with: ${DF_CYAN}tmux switch-client -t $session_name${DF_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-attach() {
|
||||
local workspace_name="$1"
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-attach <workspace_name>"; tw-list; return 1; }
|
||||
|
||||
_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 "Create it with: ${DF_CYAN}tw-create $workspace_name${DF_NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
tmux switch-client -t "$session_name"
|
||||
fi
|
||||
df_print_success "Created: $name"
|
||||
[[ -z "$TMUX" ]] && tmux attach -t "$session" || df_print_info "Switch: tmux switch -t $session"
|
||||
}
|
||||
|
||||
tw-list() {
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
df_print_func_name "Active Tmux Workspaces"
|
||||
|
||||
local has_workspaces=false
|
||||
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r session_full rest; do
|
||||
if [[ "$session_full" == ${TW_SESSION_PREFIX}-* ]]; then
|
||||
has_workspaces=true
|
||||
local workspace_name="${session_full#${TW_SESSION_PREFIX}-}"
|
||||
local attached=""
|
||||
|
||||
if [[ -n "$TMUX" ]]; then
|
||||
local current_session=$(tmux display-message -p '#S')
|
||||
[[ "$current_session" == "$session_full" ]] && attached=" ${DF_GREEN}(current)${DF_NC}"
|
||||
fi
|
||||
|
||||
echo -e "${DF_GREEN}●${DF_NC} ${DF_CYAN}$workspace_name${DF_NC}$attached"
|
||||
echo " Session: $session_full"
|
||||
fi
|
||||
_tw_check || return 1
|
||||
df_print_func_name "Tmux Workspaces"
|
||||
local found=false
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r sess rest; do
|
||||
[[ "$sess" == ${TW_PREFIX}-* ]] && { found=true; df_print_indent "● ${sess#${TW_PREFIX}-}"; }
|
||||
done
|
||||
|
||||
if [[ "$has_workspaces" != true ]]; then
|
||||
_tw_print_info "No active workspaces"
|
||||
echo "Create one with: ${DF_CYAN}tw-create myproject${DF_NC}"
|
||||
fi
|
||||
[[ "$found" != true ]] && df_print_info "No workspaces. Use: tw-create <name>"
|
||||
}
|
||||
|
||||
tw-attach() {
|
||||
local name="$1"
|
||||
[[ -z "$name" ]] && { tw-list; return 1; }
|
||||
_tw_check || return 1
|
||||
local session="${TW_PREFIX}-${name}"
|
||||
tmux has-session -t "$session" 2>/dev/null || { df_print_error "'$name' not found"; return 1; }
|
||||
[[ -z "$TMUX" ]] && tmux attach -t "$session" || tmux switch -t "$session"
|
||||
}
|
||||
|
||||
tw-delete() {
|
||||
local workspace_name="$1"
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-delete <workspace_name>"; tw-list; return 1; }
|
||||
|
||||
_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
|
||||
|
||||
tmux kill-session -t "$session_name"
|
||||
_tw_print_success "Deleted workspace: $workspace_name"
|
||||
}
|
||||
|
||||
tw-save() {
|
||||
local template_name="$1"
|
||||
[[ -z "$template_name" ]] && { echo "Usage: tw-save <template_name>"; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
[[ -z "$TMUX" ]] && { _tw_print_error "Must be run from inside tmux"; return 1; }
|
||||
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
[[ -f "$template_file" ]] && {
|
||||
read -q "REPLY?Template '$template_name' exists. Overwrite? [y/N]: "
|
||||
echo
|
||||
[[ ! "$REPLY" =~ ^[Yy]$ ]] && return 1
|
||||
}
|
||||
|
||||
_tw_print_step "Saving current layout as template: $template_name"
|
||||
local pane_count=$(tmux display-message -p '#{window_panes}')
|
||||
|
||||
cat > "$template_file" << EOF
|
||||
# Custom template: $template_name
|
||||
# Saved: $(date)
|
||||
# Panes: $pane_count
|
||||
EOF
|
||||
|
||||
if (( pane_count == 2 )); then
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
elif (( pane_count == 3 )); then
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
echo "split-window -v -p 50" >> "$template_file"
|
||||
elif (( pane_count == 4 )); then
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
echo "split-window -v -p 50" >> "$template_file"
|
||||
echo "select-pane -t 0" >> "$template_file"
|
||||
echo "split-window -v -p 50" >> "$template_file"
|
||||
fi
|
||||
|
||||
echo "" >> "$template_file"
|
||||
echo "select-pane -t 0" >> "$template_file"
|
||||
|
||||
_tw_print_success "Template saved: $template_name"
|
||||
echo " File: $template_file"
|
||||
echo " Edit: ${DF_CYAN}tw-template-edit $template_name${DF_NC}"
|
||||
[[ -z "$1" ]] && { tw-list; return 1; }
|
||||
_tw_check || return 1
|
||||
local session="${TW_PREFIX}-${1}"
|
||||
tmux has-session -t "$session" 2>/dev/null || { df_print_error "'$1' not found"; return 1; }
|
||||
tmux kill-session -t "$session"
|
||||
df_print_success "Deleted: $1"
|
||||
}
|
||||
|
||||
tw() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
|
||||
[[ -z "$workspace_name" ]] && { tw-list; return 0; }
|
||||
|
||||
_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-attach "$workspace_name"
|
||||
else
|
||||
_tw_print_info "Workspace doesn't exist. Creating with template: $template"
|
||||
tw-create "$workspace_name" "$template"
|
||||
fi
|
||||
local name="$1" tmpl="${2:-$TW_DEFAULT}"
|
||||
[[ -z "$name" ]] && { tw-list; return; }
|
||||
_tw_check || return 1
|
||||
local session="${TW_PREFIX}-${name}"
|
||||
tmux has-session -t "$session" 2>/dev/null && tw-attach "$name" || tw-create "$name" "$tmpl"
|
||||
}
|
||||
|
||||
twf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_tw_print_error "fzf not installed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
df_require_cmd fzf || return 1
|
||||
_tw_check || return 1
|
||||
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
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r sess rest; do
|
||||
[[ "$sess" == ${TW_PREFIX}-* ]] && sessions+=("${sess#${TW_PREFIX}-}")
|
||||
done
|
||||
|
||||
[[ ${#sessions[@]} -eq 0 ]] && { _tw_print_info "No workspaces found"; return 1; }
|
||||
|
||||
local selection=$(printf '%s\n' "${sessions[@]}" | \
|
||||
fzf --height=40% --layout=reverse --border=rounded --prompt='Workspace > ')
|
||||
|
||||
[[ -n "$selection" ]] && tw-attach "$selection"
|
||||
[[ ${#sessions[@]} -eq 0 ]] && { df_print_info "No workspaces"; return 1; }
|
||||
local sel=$(printf '%s\n' "${sessions[@]}" | fzf $(df_fzf_opts) --prompt='Workspace > ')
|
||||
[[ -n "$sel" ]] && tw-attach "$sel"
|
||||
}
|
||||
|
||||
tw-sync() {
|
||||
[[ -z "$TMUX" ]] && { _tw_print_error "Must be run from inside tmux"; return 1; }
|
||||
|
||||
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: ${DF_RED}OFF${DF_NC}"
|
||||
else
|
||||
tmux set-window-option synchronize-panes on
|
||||
_tw_print_info "Pane synchronization: ${DF_GREEN}ON${DF_NC}"
|
||||
fi
|
||||
[[ -z "$TMUX" ]] && { df_print_error "Must be in tmux"; return 1; }
|
||||
local cur=$(tmux show-window-option -v synchronize-panes 2>/dev/null)
|
||||
[[ "$cur" == "on" ]] && { tmux set-window-option synchronize-panes off; df_print_info "Sync: OFF"; } || \
|
||||
{ tmux set-window-option synchronize-panes on; df_print_info "Sync: ON"; }
|
||||
}
|
||||
|
||||
tw-rename() {
|
||||
local old_name="$1"
|
||||
local new_name="$2"
|
||||
|
||||
[[ -z "$old_name" || -z "$new_name" ]] && { echo "Usage: tw-rename <old_name> <new_name>"; return 1; }
|
||||
|
||||
_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
|
||||
alias twl='tw-list' twc='tw-create' twa='tw-attach' twd='tw-delete' twt='tw-templates'
|
||||
_tw_init
|
||||
|
||||
Reference in New Issue
Block a user