commit ad8b80ae0f9df494800c58bc44348733b6aaec61 Author: Nami Lauckner Date: Mon Mar 9 02:14:06 2026 +0100 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..475bc4d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +plugins/* diff --git a/functions.sh b/functions.sh new file mode 100755 index 0000000..6fdde28 --- /dev/null +++ b/functions.sh @@ -0,0 +1,11 @@ +function bring_pane() { + windows=$( + tmux list-windows -F "#{window_active} #{window_index} #{window_name}" | sort -r | awk '{print $2 " : " $3}' \ + | fzf-tmux -p --border-label="Bring pane" | awk '{print $1}' | sort -r + ) + if [ -n "$windows" ]; then + echo $windows | xargs -n1 -I {i} tmux join-pane -s {i} + fi +} + + diff --git a/tmux.conf b/tmux.conf new file mode 100644 index 0000000..4d74fd8 --- /dev/null +++ b/tmux.conf @@ -0,0 +1,77 @@ +# 24bit color fix +set-option -sa terminal-overrides ",xterm*:Tc" + +# Set tmux prefix +unbind C-b +set -g prefix C-Space +bind C-Space send-prefix + +# Shift+Alt vim nav keys +bind -n M-H previous-window +bind -n M-L next-window + +bind s run-shell "tmuxss" +bind -n C-s run-shell "tmuxss" +bind C-k run-shell "tmuxss -k" +bind C-r run-shell "tmuxss -r" +bind C-o display-popup -E "tmuxss -i" +bind C-t display-popup -E "fish" +unbind -n C-k +unbind Space +bind-key i run-shell "source ~/.config/tmux/functions.sh && bring_pane" +bind M-f move-pane -t '.-' +bind M-r move-pane -h -t '.-' + +# bind -n C-k run-shell 'tmux switch-client -t "$(tmux list-sessions | grep -o "^AP[0-9]*" | tail -n 1)"' + +# Vi copy-mode +setw -g mode-keys vi + +bind -T copy-mode-vi v send -X begin-selection +bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'pbcopy' +bind P paste-buffer +bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'pbcopy' + +set -g @plugin 'tmux-plugins/tpm' +# Fixes some tmux configuration quirks +set -g @plugin 'tmux-plugins/tmux-sensible' +set -g @plugin 'tmux-plugins/vim-tmux-navigator' +#set -g @plugin 'catppuccin/tmux' +# Temporary fix while window seperators are still broken +set -g @plugin 'mklbravo/catppuccin-tmux#feat/window-specific-separator-config' +set -g @plugin 'tmux-plugins/tmux-battery' + +set -g @plugin 'tmux-plugins/tmux-copycat' +set -g @plugin 'tmux-plugins/tmux-yank' +set -g @yank_with_mouse off +set -g @shell_mode 'vi' + +set -g @catppuccin_custom_plugin_dir "~/.config/tmux/plugins/custom" + +set -g @catppuccin_window_left_separator "" +set -g @catppuccin_window_right_separator " " +set -g @catppuccin_window_middle_separator " █" +set -g @catppuccin_window_default_middle_separator " █" +set -g @catppuccin_window_current_middle_separator " " +set -g @catppuccin_window_number_position "right" + +set -g @catppuccin_window_default_fill "number" +set -g @catppuccin_window_default_text "#W" + +set -g @catppuccin_window_current_fill "all" +set -g @catppuccin_window_current_text "#W" + +set -g @catppuccin_status_modules_right "directory battery application session" + +# set -g @catppuccin_status_left_separator " " +# set -g @catppuccin_status_right_separator "" +#set -g @catppuccin_status_left_separator ""#"" +#set -g @catppuccin_status_right_separator ""# " " +set -g @catppuccin_status_fill "icon" +set -g @catppuccin_status_connect_separator "yes" + +set -g @catppuccin_directory_text "#{pane_current_path}" +set -g @catppuccin_directory_icon " " +set -g status-interval 1 + +run '~/.tmux/plugins/tpm/tpm' diff --git a/tmuxss b/tmuxss new file mode 100755 index 0000000..312e97f --- /dev/null +++ b/tmuxss @@ -0,0 +1,589 @@ +#!/usr/bin/env bash + +# Define color variables +CSI="\e[" +RESET="${CSI}0m" +BOLD_BLUE="${CSI}1;34m" +YELLOW="${CSI}0;33m" +GREEN="${CSI}0;32m" +MAGENTA="${CSI}0;35m" +CYAN="${CSI}0;36m" +DIM="${CSI}2m" + +help() { + local content=`cat << EOF +${BOLD_BLUE}tmuxss${RESET} - a simple tmux multiview session manager + +${CYAN}Syntax:${RESET} tmuxss -<${YELLOW}k|a|i|c${RESET}> [-${YELLOW}g${RESET} group] [-${YELLOW}s${RESET} sid] [-${YELLOW}d${RESET}] [-${YELLOW}t${RESET} template] [-${YELLOW}p${RESET} path] + +${BOLD_BLUE}Commands:${RESET} + ${YELLOW}k${RESET} Kill sessions associated with a SID or group + ${YELLOW}a${RESET} Attach to a session group with the given root SID + ${YELLOW}i${RESET} Interactive open mode + ${YELLOW}c${RESET} Create a session group in the current directory or a provided one, optionally using a template + ${YELLOW}p${RESET} A directory path to create the session in + ${YELLOW}b${RESET} Initialize the main session + ${YELLOW}h${RESET} Print the entire help menu + +${BOLD_BLUE}Options:${RESET} + ${YELLOW}s${RESET} SID (Session ID) + ${YELLOW}g${RESET} Group name + ${YELLOW}t${RESET} Template name + ${YELLOW}f${RESET} Path to template config file + ${YELLOW}d${RESET} Stay detached from new session + +EOF +` +printf "$content" +echo +echo +} + +extended_help() { + content=`cat << EOF +${BOLD_BLUE}Config Format:${RESET} +${CYAN}{${RESET} + ${YELLOW}"default"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: \$PWD${RESET} + ${YELLOW}"envs"${RESET}: ${CYAN}{${RESET} + ${GREEN}""${RESET}: ${CYAN}{${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: \$PWD${RESET} + ${YELLOW}"group"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: basename \$PWD${RESET} + ${YELLOW}"focused"${RESET}: ${CYAN}${RESET}, ${DIM}# default: 0${RESET} + ${YELLOW}"windows"${RESET}: ${CYAN}[${RESET} + ${CYAN}{${RESET} + ${YELLOW}"name"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: index${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: \$PWD${RESET} + ${YELLOW}"command_run"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: ""${RESET} + ${YELLOW}"command_prepare"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: ""${RESET} + ${YELLOW}"read_only"${RESET}: ${MAGENTA}true${RESET} | ${MAGENTA}false${RESET}, ${DIM}# default: false${RESET} + ${YELLOW}"hist_file"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: std Hist${RESET} + ${YELLOW}"hsplit"${RESET}: ${CYAN}[ , ...]${RESET}, + ${YELLOW}"vsplit"${RESET}: ${CYAN}[ , ...]${RESET} + ${CYAN}}, ...]${RESET} + ${CYAN}}${RESET} + ${CYAN}}${RESET} +${CYAN}}${RESET} + +${BOLD_BLUE}Pane:${RESET} +${CYAN}{${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: \$PWD${RESET} + ${YELLOW}"command_run"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: ""${RESET} + ${YELLOW}"command_prepare"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: ""${RESET} + ${YELLOW}"read_only"${RESET}: ${MAGENTA}true${RESET} | ${MAGENTA}false${RESET}, ${DIM}# default: false${RESET} + ${YELLOW}"hist_file"${RESET}: ${GREEN}""${RESET}, ${DIM}# default: std Hist${RESET} + ${YELLOW}"hsplit"${RESET}?: ${CYAN}[ , ...]${RESET}, + ${YELLOW}"vsplit"${RESET}?: ${CYAN}[ , ...]${RESET} +${CYAN}}${RESET} + +${BOLD_BLUE}Notes:${RESET} + - All paths are relative to the environment path unless absolute + - Only either hsplit or vsplit may be defined + +${BOLD_BLUE}Automatic Template Resolution (resolve key):${RESET} +${CYAN}[${RESET} + ${CYAN}{${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}""${RESET}, ${DIM}# Path to search for this template${RESET} + ${YELLOW}"template"${RESET}: ${GREEN}""${RESET}, ${DIM}# Template to automatically select if path matches${RESET} + ${CYAN}}, ...${RESET} +${CYAN}]${RESET} + +${BOLD_BLUE}Notes:${RESET} + - Paths can be absolute or relative to \$HOME + - When creating a session, tmuxss will check if the current path matches any 'resolve' entry + - If a match is found, the corresponding template is selected automatically + - Multiple entries are checked in order; first match wins + +${BOLD_BLUE}Example:${RESET} +${CYAN}[${RESET} + ${CYAN}{${RESET} "path": "~/src/autopurger", "template": "autopurger" ${CYAN}},${RESET} + ${CYAN}{${RESET} "path": "~/src/project-x", "template": "projectx" ${CYAN}}${RESET} +${CYAN}]${RESET} + +${BOLD_BLUE}Examples:${RESET} + ${YELLOW}Basic layout:${RESET} + ${CYAN}{${RESET} + ${YELLOW}"default"${RESET}: ${GREEN}"default"${RESET}, + ${YELLOW}"envs"${RESET}: ${CYAN}{${RESET} + ${GREEN}"default"${RESET}: ${CYAN}{${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}"."${RESET}, + ${YELLOW}"focused"${RESET}: ${CYAN}0${RESET}, + ${YELLOW}"windows"${RESET}: ${CYAN}[${RESET} + ${CYAN}{${RESET} ${YELLOW}"name"${RESET}: ${GREEN}"code"${RESET}, ${YELLOW}"command_run"${RESET}: ${GREEN}"nvim"${RESET} ${CYAN}},${RESET} + ${CYAN}{${RESET} ${YELLOW}"name"${RESET}: ${GREEN}"util"${RESET}, ${YELLOW}"command_run"${RESET}: ${GREEN}"htop"${RESET} ${CYAN}}${RESET} + ${CYAN}]${RESET} + ${CYAN}}${RESET} + ${CYAN}}${RESET} + ${CYAN}}${RESET} + + ${YELLOW}Nested splits with preparation:${RESET} + ${CYAN}{${RESET} + ${YELLOW}"envs"${RESET}: ${CYAN}{${RESET} + ${GREEN}"dev"${RESET}: ${CYAN}{${RESET} + ${YELLOW}"path"${RESET}: ${GREEN}"~/src/project"${RESET}, + ${YELLOW}"windows"${RESET}: ${CYAN}[${RESET} + ${CYAN}{${RESET} ${YELLOW}"name"${RESET}: ${GREEN}"code"${RESET}, ${YELLOW}"command_run"${RESET}: ${GREEN}"nvim"${RESET} ${CYAN}},${RESET} + ${CYAN}{${RESET} + ${YELLOW}"name"${RESET}: ${GREEN}"runtime"${RESET}, + ${YELLOW}"hsplit"${RESET}: ${CYAN}[${RESET} + ${CYAN}{${RESET} + ${YELLOW}"vsplit"${RESET}: ${CYAN}[${RESET} + ${CYAN}{${RESET} ${YELLOW}"command_prepare"${RESET}: ${GREEN}"yarn start"${RESET}, ${YELLOW}"path"${RESET}: ${GREEN}"./frontend"${RESET} ${CYAN}},${RESET} + ${CYAN}{${RESET} ${YELLOW}"command_prepare"${RESET}: ${GREEN}"cargo run"${RESET}, ${YELLOW}"path"${RESET}: ${GREEN}"./backend"${RESET} ${CYAN}}${RESET} + ${CYAN}]${RESET} + ${CYAN}},${RESET} + ${CYAN}{${RESET} ${YELLOW}"command_run"${RESET}: ${GREEN}"htop"${RESET} ${CYAN}}${RESET} + ${CYAN}]${RESET} + ${CYAN}}${RESET} + ${CYAN}]${RESET} + ${CYAN}}${RESET} + ${CYAN}}${RESET} + ${CYAN}}${RESET} + +EOF +` +printf "$content" +echo +} + +resolve_path() { + local base=$1 + local pair=$2 + + [[ -z $pair ]] && pair="." + [[ $pair == ~* ]] && pair="${pair/#\~/$HOME}" + + if [[ $pair = /* ]]; then + realpath "$pair" + else + realpath "$base/$pair" + fi +} + +SID="" +GROUP="" +SUBCOMMAND="" +TEMPLATE="" +DETACH=0 +BASE_PATH=$PWD +YQ_AVAILABLE=$(command -v yq >/dev/null 2>&1 && echo true || echo false) +TEMPLATE_SOURCE="${XDG_DATA_HOME:-$HOME/.config}/tmux/tmuxss.json" + +while getopts "p:f:g:t:s:bhkaicd" option; do + case $option in + h) + help + extended_help + exit 0 + ;; + s) + if [[ $OPTARG =~ "#" ]]; then + echo "\"#\" is a reserved character" + exit 3 + fi + SID=$OPTARG ;; + g) + if [[ $OPTARG =~ "#" ]]; then + echo "\"#\" is a reserved character" + exit 3 + fi + GROUP=$OPTARG ;; + t) + if [[ ! $OPTARG =~ ^[a-zA-Z]+$ ]]; then + echo "Template names must be only upper and lowercase." + exit 3 + fi + # GROUP=$OPTARG + TEMPLATE=$OPTARG ;; + f) + if [[ ! $OPTARG =~ ^(/)?([^/\0]+(/)?)+$ ]]; then + echo "Template source must be a valid path." + exit 3 + fi + TEMPLATE_SOURCE=$OPTARG ;; + p) + if [[ ! $OPTARG =~ ^(/)?([^/\0]+(/)?)+$ ]]; then + echo "Path must be valid." + exit 3 + fi + BASE_PATH=$(resolve_path $BASE_PATH $OPTARG);; + i) + SUBCOMMAND="i" ;; + a) + SUBCOMMAND="a" ;; + k) + SUBCOMMAND="k" ;; + b) + SUBCOMMAND="b" ;; + c) + SUBCOMMAND="c" ;; + d) + DETACH=1 ;; + ?) + echo "Invalid option" + echo "Pull up the help menu with tmuxss -h" + exit 3 + ;; + esac +done + +ATTACHED_TO="" +SIDINFERED=0 + +infer() { +if [[ -z $SID ]]; then + if [[ -n "$TMUX" ]]; then + local SESSION=$(tmux display-message -p '#S') + + ATTACHED_TO=$SESSION + + SID=${SESSION#*#} + + if [[ -z $SID ]]; then + echo "No ID found in the current tmux session name: $SESSION" + exit 66 + fi + + SIDINFERED=1 + fi +fi +} +infer + +attachSession() { + local group=$1 + local sid=$2 + local session="$group#$sid" + + # Check if a session exists in the group with the current SID + if [[ -z $(tmux list-sessions -F "#{session_name}" | grep "^$session$") ]]; then + # If no session exists with the current SID, create a new one + tmux new-session -ds "$session" -t "$group" + fi + + if [[ $TMUX ]]; then + tmux switch-client -t "$session" + else + tmux attach -t "$session" + fi +} + +sanitize_path() { + local input=$1 + + local sanitized=$(echo "$input" | tr -d '.' | sed 's/[^a-zA-Z0-9]/_/g' | tr '[:upper:]' '[:lower:]') + + echo "$sanitized" +} + +resolve_template_from_path() { + # explicit -t always wins + [[ -n $TEMPLATE ]] && return + + local best_template="" + local best_len=0 + local i=0 + + while :; do + local path_var="resolve_${i}_path" + local tmpl_var="resolve_${i}_template" + + # stop when index no longer exists in parsed DATA + grep -q "^${path_var}=" <<< "$DATA" || break + + local raw_path=${!path_var} + local tmpl=${!tmpl_var} + + # normalize candidate path + local abs_path + abs_path=$(resolve_path "$HOME" "$raw_path") + + # prefix match + if [[ $BASE_PATH == "$abs_path"* ]]; then + local len=${#abs_path} + if (( len > best_len )); then + best_len=$len + best_template=$tmpl + fi + fi + + ((i++)) + done + + [[ -n $best_template ]] && TEMPLATE="$best_template" +} + +# This gets the last bit of performance +setup_pane() { + local key="$1" + local pane_id="$2" + + if [[ $SHELL != "/bin/fish" ]]; then + local hist_file="${key}_hist_file" + hist_file=${!hist_file} + # Set HISTFILE only if a specific file was provided + if [[ -n $hist_file ]]; then + hist_file=$(resolve_path "$BASE_PATH" "$hist_file") + tmux send-keys -t "$pane_id" "export HISTFILE=\"$hist_file\"; export PROMPT_COMMAND='history -a; history -c; history -r'; history -d \$(history 1)" C-m + fi + + local read_only="${key}_read_only" + read_only=${!read_only} + if [[ $read_only == "true" ]]; then + tmux send-keys -t "$pane_id" "shopt -ou history; history -d \$(history 1)" C-m + fi + + tmux send-keys -t "$pane_id" "clear; history -d \$(history 1)" C-m + tmux clear-history -t "$pane_id" + fi + + local command_run="${key}_command_run" + command_run=${!command_run} + [[ -n $command_run ]] && tmux send-keys -t "$pane_id" "$command_run" C-m + local command_prepare="${key}_command_prepare" + command_prepare=${!command_prepare} + [[ -n $command_prepare ]] && tmux send-keys -t "$pane_id" "$command_prepare" +} + +visit_pane() { + local key="$1" + local window_index="$2" + local pane_index="${PANE_INDEX[$window_index]:-0}" + + local path_var="${key}_path" + local path="${!path_var}" + path=$(resolve_path "$BASE_PATH" "$path") + + local pane_id="$GROUP:$window_index.$pane_index" + + local hsplit="${key}_hsplit" + local vsplit="${key}_vsplit" + + if grep -q "$hsplit" <<< "$DATA"; then + tmux split-window -h -t "$pane_id" -c "$path" + + local i=0 + while grep -q "${hsplit}_${i}" <<< "$DATA"; do + visit_pane "${hsplit}_${i}" "$window_index" "$pane_index" + ((i++)) + done + + elif grep -q "$vsplit" <<< "$DATA"; then + tmux split-window -v -t "$pane_id" -c "$path" + + local i=0 + while grep -q "${vsplit}_${i}" <<< "$DATA"; do + visit_pane "${vsplit}_${i}" "$window_index" "$pane_index" + ((i++)) + done + + else + # Ensure the pane exists at the correct cwd before setup + tmux respawn-pane -k -t "$pane_id" -c "$path" + + setup_pane "$key" "$pane_id" & + PANE_INDEX[$window_index]=$((pane_index + 1)) + fi +} + +DATA="" +declare -A PANE_INDEX + +build_template() { + local path="${template_key}_path" + path=${!path} + + BASE_PATH=$(resolve_path $BASE_PATH $path) + + cd "$BASE_PATH" && tmux new-session -ds "$GROUP" -c "$BASE_PATH" + + local i=0 + while :; do + local key="${template_key}_windows_${i}" + if [ $i -gt 0 ] && ! grep -q "$key" <<< "$DATA"; then break; fi + + local name="${key}_name" + name=${!name:-$i} + [[ $i -gt 0 ]] && tmux new-window -d -t "$GROUP" -c "$BASE_PATH" -n "$name" + + visit_pane "$key" "$i" & + i=$((i + 1)) + done + wait + + local focused_window="${template_key}_focused" + focused_window=${!focused_window} + [[ -z $focused_window ]] && focused_window="0" + + tmux select-window -t "$GROUP:$focused_window" +} + +case $SUBCOMMAND in +k) + if [[ -n $GROUP ]]; then + SESSION_LIST=$(tmux list-sessions -F "#{session_name}") + + for SESSION in $SESSION_LIST; do + if [[ $SESSION == "$GROUP#"* || $SESSION == $GROUP ]]; then + if [[ "$GROUP#$SID" == $ATTACHED_TO && $GROUP != "main" ]]; then + attachSession main $SID + fi + + tmux kill-session -t "$SESSION" + fi + done + fi + if [[ -n $SID && $SIDINFERED -eq 0 ]]; then + SESSION_LIST=$(tmux list-sessions -F "#{session_name}") + + for SESSION in $SESSION_LIST; do + if [[ $SESSION == *#$SID ]]; then + tmux kill-session -t "$SESSION" + fi + done + fi + + if [[ ( -n $SID && $SIDINFERED -eq 0 ) || -n $GROUP ]]; then + exit 0 + fi + ;; +a) + if [[ -z $GROUP ]]; then + echo "No group specified" + echo "Check out tmuxss -h" + exit 3 + fi + [[ -z $SID ]] && SID=$$ + attachSession $GROUP $SID + exit 0 + ;; +i) + file=$(<"$TEMPLATE_SOURCE") + if [[ -n $file ]] && $YQ_AVAILABLE; then + DATA=$(yq -p=json -o shell <<< "$file") || { + echo "Invalid template file" + exit 3 + } + fi + + eval "$DATA" + + SEARCH_PATHS=() + mapfile -t SEARCH_PATHS < <(yq -r '.paths[]?' <<< "$file") + + for i in "${!SEARCH_PATHS[@]}"; do + p="${SEARCH_PATHS[$i]}" + [[ $p == ~* ]] && p="${p/#\~/$HOME}" + [[ $p != /* ]] && p="$PWD/$p" + SEARCH_PATHS[$i]="$p" + done + + mapfile -d '' DIRS < <( + for path in "${SEARCH_PATHS[@]}"; do + [[ -d "$path" ]] || continue + find "$path" -mindepth 1 -maxdepth 1 -type d -print0 + done + ) + + [[ ${#DIRS[@]} -eq 0 ]] && { echo "No directories found in SEARCH_PATHS"; exit 0; } + + selected=$(printf '%s\n' "${DIRS[@]}" | fzf --height 40% --reverse) + + [[ -z "${selected:-}" ]] && exit 0 + + selected=${selected%$'\0'} + + exec tmuxss -c -p "$selected" + ;; +b) + [[ -z $SID ]] && SID=$$ + + tmuxss -c -d -t "main" -g "main" -s "$SID" + + trap "tmuxss -k -s $SID" EXIT SIGHUP && tmuxss -a -g "main" -s "$SID" + exit 0 + ;; +c) + file=$(<"$TEMPLATE_SOURCE") + if [[ -n $file ]] && $YQ_AVAILABLE; then + DATA=$(yq -p=json -o shell <<< "$file") || { + echo "Invalid template file" + exit 3 + } + fi + if [[ -z $DATA ]]; then + if [[ -n $TEMPLATE && $TEMPLATE != "main" ]]; then + ! $YQ_AVAILABLE && { echo "Using templates requires yq to be installed"; exit 1; } + echo "Could not locate config file" + exit 1 + fi + DATA="default='default' envs_default_path='.' envs_main_path='~' envs_main_group='main'" + fi + + eval "$DATA" + + resolve_template_from_path + + if [[ -z $TEMPLATE ]]; then + default_template="default" + default_template=${!default_template} + [[ -n $default_template ]] && TEMPLATE=$default_template + fi + + template_key="envs_${TEMPLATE}" + if ! grep -q "$template_key" <<< "$DATA"; then { echo "Session template not found"; exit 3; }; fi + + template_group="${template_key}_group" + template_group=${!template_group} + [[ -n $template_group ]] && GROUP=$template_group + + if [[ -z $GROUP ]]; then + GROUP=$(sanitize_path "$(basename "$BASE_PATH")") + + # If the current directory is root, set GROUP to "root" + if [[ $GROUP == "/" ]]; then + GROUP="root" + fi + fi + [[ -z $SID ]] && SID=$$ + + if [[ -z $(tmux list-sessions -F "#{session_name}" | grep "^$GROUP$") ]]; then + build_template + # nohup build_template >/dev/null 2>&1 & + fi + + if [[ $DETACH -eq 0 ]]; then + attachSession $GROUP $SID + fi + exit 0 + ;; +esac + + +if [[ -n $TMUX ]]; then + if [[ -z $SUBCOMMAND || $SUBCOMMAND == "a" ]]; then + SUBCOMMAND="a" + TITLE="Select to attach" + fi + + if [[ $SUBCOMMAND == "k" ]]; then + TITLE="Select to kill" + fi + + ITEMS="" + index=1 + SESSION_GROUPS=$(tmux list-sessions -F "#{session_name}" | awk -F '#' '{print $1}' | sort -u) + while read -r group; do + if [[ -n "$group" ]]; then + ITEMS+="$group $index 'run-shell \"tmuxss -$SUBCOMMAND -g \"$group\"\"' " + ((index++)) + fi + done <<< "$SESSION_GROUPS" + + if [[ -n $ITEMS ]]; then + eval "tmux display-menu -T \"$TITLE\" $ITEMS" + else + tmux display-message "No session groups found." + fi +else + help +fi diff --git a/tmuxss.json b/tmuxss.json new file mode 100644 index 0000000..156316f --- /dev/null +++ b/tmuxss.json @@ -0,0 +1,169 @@ +{ + "default": "default", + "paths": [ + "~/src", + "~/.config", + "~/.local", + "~" + ], + "resolve": [ + { + "path": "~/src/autopurger", + "template": "autopurger" + } + ], + "envs": { + "default": { + "path": ".", + "focused": 0, + "windows": [ + { + "name": "code", + "path": ".", + "command-run": "nvim" + }, + { + "name": "util", + "path": ".", + "command_run_old": "set +x && clear && echo -e \"\\033[1;32mHave fun! \\033[0m\\n\\n\\033[1;36mDate:\\033[0m \\033[1;33m$(date '+%A, %B %d, %Y')\\033[0m\\n\\033[1;36mTime:\\033[0m \\033[1;33m$(date '+%H:%M:%S')\\033[0m\\n\"; history -d $(history 1)" + } + ] + }, + "agdsn": { + "group": "agdsn", + "path": "~", + "focused": 0, + "windows": [ + { + "name": "proxy", + "path": ".", + "command_run": "proxy-dijkstra" + } + ] + }, + "main": { + "path": "~", + "focused": 0, + "windows": [ + { + "name": "main", + "path": ".", + "command_run_old": "alias clear='set +x; command clear; echo -e \"\\n\\033[1;32mWelcome, Nami! :p \\033[0m\\n\\n\\033[1;36mSystem:\\033[0m \\033[1;33m$(hostname)\\033[0m\\n\\033[1;36mDate:\\033[0m \\033[1;33m$(date \"+%A, %B %d, %Y\")\\033[0m\\n\\033[1;36mTime:\\033[0m \\033[1;33m$(date \"+%H:%M:%S\")\\033[0m\\n\"; history -d $(history 1)' && eval clear" + } + ] + }, + "autopurger": { + "path": "~/src/autopurger", + "group": "autopurger", + "focused": 0, + "windows": [ + { + "name": "code", + "path": ".", + "read_only": true, + "hist_file": "./.hist/code", + "command_run": "nvim" + }, + { + "name": "runtime", + "hsplit": [ + { + "vsplit": [ + { + "hsplit": [ + { + "command_prepare": "ENV=development bun --preload ./src/instrumentation.ts src/index", + "path": "./apps/gateway-connector", + "hist_file": "./.hist/gateway-connector", + "read_only": false + }, + { + "command_prepare": "ENV=development bun --preload ./src/instrumentation.ts src/index", + "path": "./apps/event-handler", + "hist_file": "./.hist/event-handler", + "read_only": false + } + ] + }, + { + "hsplit": [ + { + "command_prepare": "ENV=development bun --preload ./src/instrumentation.ts src/index", + "path": "./apps/fetch-engine", + "hist_file": "./.hist/fetch-engine", + "read_only": false + }, + { + "command_prepare": "ENV=development bun src/index", + "path": "./apps/delete-engine", + "hist_file": "./.hist/delete-engine", + "read_only": false + } + ] + } + ] + }, + { + "vsplit": [ + { + "command_prepare": "pnpm run dev", + "path": "./apps/dashboard", + "hist_file": "./.hist/dashboard", + "read_only": false + }, + { + "hsplit": [ + { + "command_prepare": "ENV=development bun src/index", + "path": "./apps/interactions", + "hist_file": "./.hist/interactions", + "read_only": false + }, + { + "command_prepare": "ENV=development bun src/index", + "path": "./apps/api", + "hist_file": "./.hist/api", + "read_only": false + } + ] + } + ] + } + ] + }, + { + "name": "util", + "path": ".", + "hist_file": "./.hist/util" + }, + { + "name": "cloc", + "path": ".", + "read_only": true, + "hist_file": "./.hist/cloc", + "command_run": "./scripts/countLines" + }, + { + "name": "git", + "path": ".", + "hist_file": "./.hist/git", + "command_run": "git status" + }, + { + "name": "prod", + "path": ".", + "read_only": true, + "hist_file": "./.hist/prod", + "command_prepare": "ssh node-worker-01.funkloch.net" + }, + { + "name": "caddy", + "path": ".", + "read_only": true, + "hist_file": "./.hist/caddy", + "command_prepare": "scripts/caddy" + } + ] + } + } +}