Dotfiles update 2025-12-22 12:00
This commit is contained in:
@@ -2,63 +2,38 @@
|
||||
# ============================================================================
|
||||
# Dotfiles Compile - Pre-compile zsh files for faster loading
|
||||
# ============================================================================
|
||||
# Compiles .zsh and .zshrc files to .zwc bytecode format
|
||||
# This can speed up shell startup by 20-50ms
|
||||
#
|
||||
# Usage:
|
||||
# dotfiles-compile.sh # Compile all
|
||||
# dotfiles-compile.sh --clean # Remove compiled files
|
||||
#
|
||||
# Aliases: dfc-compile
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
DOTFILES_DIR="${DOTFILES_DIR:-$HOME/.dotfiles}"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
# Source shared colors
|
||||
source "$DOTFILES_DIR/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m' DF_CYAN=$'\033[0;36m'
|
||||
DF_NC=$'\033[0m' DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOST:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-compile"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-compile"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOST:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-compile${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
compile_file() {
|
||||
@@ -66,10 +41,10 @@ compile_file() {
|
||||
if [[ -f "$file" ]]; then
|
||||
if [[ ! -f "${file}.zwc" ]] || [[ "$file" -nt "${file}.zwc" ]]; then
|
||||
zcompile "$file" 2>/dev/null && \
|
||||
echo -e "${GREEN}✓${NC} Compiled: ${file##*/}" || \
|
||||
echo -e "${YELLOW}⚠${NC} Skipped: ${file##*/}"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Compiled: ${file##*/}" || \
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Skipped: ${file##*/}"
|
||||
else
|
||||
echo -e "${CYAN}○${NC} Current: ${file##*/}"
|
||||
echo -e "${DF_CYAN}○${DF_NC} Current: ${file##*/}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -79,57 +54,46 @@ clean_compiled() {
|
||||
|
||||
local count=0
|
||||
|
||||
# Dotfiles
|
||||
for zwc in "$DOTFILES_DIR"/**/*.zwc(N); do
|
||||
rm -f "$zwc"
|
||||
((count++))
|
||||
done
|
||||
|
||||
# Home zsh files
|
||||
rm -f ~/.zshrc.zwc ~/.zshenv.zwc ~/.zprofile.zwc 2>/dev/null
|
||||
|
||||
# Oh-my-zsh (optional)
|
||||
# rm -f ~/.oh-my-zsh/**/*.zwc(N) 2>/dev/null
|
||||
|
||||
echo -e "${GREEN}✓${NC} Removed $count compiled files"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Removed $count compiled files"
|
||||
}
|
||||
|
||||
compile_all() {
|
||||
echo -e "${CYAN}Compiling zsh files for faster startup...${NC}"
|
||||
echo -e "${DF_CYAN}Compiling zsh files for faster startup...${DF_NC}"
|
||||
echo
|
||||
|
||||
# Core files
|
||||
echo "Core files:"
|
||||
compile_file ~/.zshrc
|
||||
compile_file ~/.zshenv
|
||||
compile_file ~/.zprofile
|
||||
echo
|
||||
|
||||
# Dotfiles zsh files
|
||||
echo "Dotfiles:"
|
||||
compile_file "$DOTFILES_DIR/zsh/.zshrc"
|
||||
compile_file "$DOTFILES_DIR/zsh/aliases.zsh"
|
||||
|
||||
# Function files
|
||||
for file in "$DOTFILES_DIR/zsh/functions"/*.zsh(N); do
|
||||
compile_file "$file"
|
||||
done
|
||||
|
||||
# Theme
|
||||
for file in "$DOTFILES_DIR/zsh/themes"/*.zsh-theme(N); do
|
||||
compile_file "$file"
|
||||
done
|
||||
echo
|
||||
|
||||
# Oh-my-zsh core (optional, can save ~10ms)
|
||||
if [[ -d ~/.oh-my-zsh ]]; then
|
||||
echo "Oh-My-Zsh (optional):"
|
||||
compile_file ~/.oh-my-zsh/oh-my-zsh.sh
|
||||
# compile_file ~/.oh-my-zsh/lib/*.zsh # Uncomment for more speed
|
||||
echo
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Compilation complete"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Compilation complete"
|
||||
echo
|
||||
echo "To measure startup time:"
|
||||
echo " time zsh -i -c exit"
|
||||
@@ -145,9 +109,6 @@ show_help() {
|
||||
echo " (none) Compile all zsh files"
|
||||
echo " --clean Remove all compiled (.zwc) files"
|
||||
echo " --help Show this help"
|
||||
echo
|
||||
echo "The compiled files (.zwc) are automatically used by zsh"
|
||||
echo "and can speed up shell startup by 20-50ms."
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -157,13 +118,7 @@ show_help() {
|
||||
print_header
|
||||
|
||||
case "${1:-}" in
|
||||
--clean|-c)
|
||||
clean_compiled
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
compile_all
|
||||
;;
|
||||
--clean|-c) clean_compiled ;;
|
||||
--help|-h) show_help ;;
|
||||
*) compile_all ;;
|
||||
esac
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
|
||||
set -e
|
||||
|
||||
readonly DOTFILES_HOME="${DOTFILES_HOME:-.}"
|
||||
readonly DOTFILES_HOME="${DOTFILES_HOME:-$HOME/.dotfiles}"
|
||||
readonly DOTFILES_VERSION="3.0.0"
|
||||
|
||||
# Color codes
|
||||
readonly RED='\033[0;31m'
|
||||
readonly GREEN='\033[0;32m'
|
||||
readonly YELLOW='\033[1;33m'
|
||||
readonly BLUE='\033[0;34m'
|
||||
readonly CYAN='\033[0;36m'
|
||||
readonly NC='\033[0m'
|
||||
# Source shared colors
|
||||
source "$DOTFILES_HOME/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
# Fallback if colors.zsh not found
|
||||
DF_RED=$'\033[0;31m' DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m'
|
||||
DF_BLUE=$'\033[0;34m' DF_CYAN=$'\033[0;36m' DF_NC=$'\033[0m'
|
||||
DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# Track results
|
||||
TOTAL_CHECKS=0
|
||||
@@ -26,39 +27,22 @@ WARNING_CHECKS=0
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-doctor"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo -e "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo -e "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-doctor"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-doctor${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -66,25 +50,25 @@ print_header() {
|
||||
# ============================================================================
|
||||
|
||||
print_section() {
|
||||
echo -e "\n${BLUE}▶${NC} $1"
|
||||
echo -e "\n${DF_BLUE}▶${DF_NC} $1"
|
||||
}
|
||||
|
||||
check_pass() {
|
||||
((PASSED_CHECKS++))
|
||||
((TOTAL_CHECKS++))
|
||||
echo -e " ${GREEN}✓${NC} $1"
|
||||
echo -e " ${DF_GREEN}✓${DF_NC} $1"
|
||||
}
|
||||
|
||||
check_fail() {
|
||||
((FAILED_CHECKS++))
|
||||
((TOTAL_CHECKS++))
|
||||
echo -e " ${RED}✗${NC} $1"
|
||||
echo -e " ${DF_RED}✗${DF_NC} $1"
|
||||
}
|
||||
|
||||
check_warn() {
|
||||
((WARNING_CHECKS++))
|
||||
((TOTAL_CHECKS++))
|
||||
echo -e " ${YELLOW}⚠${NC} $1"
|
||||
echo -e " ${DF_YELLOW}⚠${DF_NC} $1"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -314,23 +298,23 @@ check_dotfiles_dir() {
|
||||
|
||||
print_summary() {
|
||||
echo ""
|
||||
printf "${CYAN}─%.0s${NC}" {1..70}; echo ""
|
||||
printf "${DF_CYAN}─%.0s${DF_NC}" {1..70}; echo ""
|
||||
|
||||
if [[ $FAILED_CHECKS -eq 0 ]]; then
|
||||
echo -e "${GREEN}✓${NC} All checks passed ($PASSED_CHECKS/$TOTAL_CHECKS)"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} All checks passed ($PASSED_CHECKS/$TOTAL_CHECKS)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Some checks failed"
|
||||
echo -e " ${GREEN}Passed:${NC} $PASSED_CHECKS"
|
||||
echo -e " ${RED}Failed:${NC} $FAILED_CHECKS"
|
||||
echo -e "${DF_RED}✗${DF_NC} Some checks failed"
|
||||
echo -e " ${DF_GREEN}Passed:${DF_NC} $PASSED_CHECKS"
|
||||
echo -e " ${DF_RED}Failed:${DF_NC} $FAILED_CHECKS"
|
||||
if [[ $WARNING_CHECKS -gt 0 ]]; then
|
||||
echo -e " ${YELLOW}Warnings:${NC} $WARNING_CHECKS"
|
||||
echo -e " ${DF_YELLOW}Warnings:${DF_NC} $WARNING_CHECKS"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [[ $FAILED_CHECKS -gt 0 ]]; then
|
||||
echo -e "${YELLOW}💡 Tip:${NC} Run 'dotfiles-doctor.sh --fix' to attempt automatic fixes"
|
||||
echo -e "${DF_YELLOW}💡 Tip:${DF_NC} Run 'dotfiles-doctor.sh --fix' to attempt automatic fixes"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -5,52 +5,36 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes
|
||||
readonly RED='\033[0;31m'
|
||||
readonly GREEN='\033[0;32m'
|
||||
readonly YELLOW='\033[1;33m'
|
||||
readonly BLUE='\033[0;34m'
|
||||
readonly CYAN='\033[0;36m'
|
||||
readonly MAGENTA='\033[0;35m'
|
||||
readonly NC='\033[0m'
|
||||
readonly DOTFILES_HOME="${DOTFILES_HOME:-$HOME/.dotfiles}"
|
||||
|
||||
# Source shared colors
|
||||
source "$DOTFILES_HOME/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
DF_RED=$'\033[0;31m' DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m'
|
||||
DF_BLUE=$'\033[0;34m' DF_CYAN=$'\033[0;36m' DF_MAGENTA=$'\033[0;35m'
|
||||
DF_NC=$'\033[0m' DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-stats"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo -e "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo -e "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-stats"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-stats${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -59,8 +43,8 @@ print_header() {
|
||||
|
||||
print_section() {
|
||||
echo ""
|
||||
echo -e "${BLUE}▶${NC} $1"
|
||||
echo -e "${CYAN}─────────────────────────────────────────────────────────────${NC}"
|
||||
echo -e "${DF_BLUE}▶${DF_NC} $1"
|
||||
echo -e "${DF_CYAN}─────────────────────────────────────────────────────────────${DF_NC}"
|
||||
}
|
||||
|
||||
# Get command history
|
||||
@@ -83,8 +67,8 @@ show_dashboard() {
|
||||
local unique=$(get_history | sort | uniq | wc -l)
|
||||
|
||||
echo ""
|
||||
echo -e " ${CYAN}Total Commands:${NC} $total"
|
||||
echo -e " ${CYAN}Unique Commands:${NC} $unique"
|
||||
echo -e " ${DF_CYAN}Total Commands:${DF_NC} $total"
|
||||
echo -e " ${DF_CYAN}Unique Commands:${DF_NC} $unique"
|
||||
echo ""
|
||||
|
||||
print_section "Top 15 Commands"
|
||||
@@ -93,7 +77,7 @@ show_dashboard() {
|
||||
local percent=$((count * 100 / total))
|
||||
local bar_length=$((percent / 5))
|
||||
local bar=$(printf '█%.0s' $(seq 1 $bar_length))
|
||||
printf " %-20s ${GREEN}%5d${NC} ${MAGENTA}%3d%%${NC} ${bar}\n" "$cmd" "$count" "$percent"
|
||||
printf " %-20s ${DF_GREEN}%5d${DF_NC} ${DF_MAGENTA}%3d%%${DF_NC} ${bar}\n" "$cmd" "$count" "$percent"
|
||||
done
|
||||
|
||||
echo ""
|
||||
@@ -109,7 +93,7 @@ show_top_n() {
|
||||
get_history | awk '{print $1}' | sort | uniq -c | sort -rn | head -"$n" | \
|
||||
while read count cmd; do
|
||||
local percent=$((count * 100 / total))
|
||||
printf " ${YELLOW}%4d${NC} %-30s ${CYAN}%3d%%${NC}\n" "$count" "$cmd" "$percent"
|
||||
printf " ${DF_YELLOW}%4d${DF_NC} %-30s ${DF_CYAN}%3d%%${DF_NC}\n" "$count" "$cmd" "$percent"
|
||||
done
|
||||
|
||||
echo ""
|
||||
@@ -124,7 +108,7 @@ show_suggestions() {
|
||||
get_history | awk '{print $1}' | sort | uniq -c | sort -rn | head -20 | \
|
||||
while read count cmd; do
|
||||
if [[ $count -gt 50 ]]; then
|
||||
printf " ${YELLOW}Suggestion:${NC} ${GREEN}alias ${cmd:0:2}='$cmd'${NC} (used $count times)\n"
|
||||
printf " ${DF_YELLOW}Suggestion:${DF_NC} ${DF_GREEN}alias ${cmd:0:2}='$cmd'${DF_NC} (used $count times)\n"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -135,22 +119,22 @@ show_breakdown() {
|
||||
print_section "Command Breakdown"
|
||||
|
||||
echo ""
|
||||
echo -e " ${CYAN}Git Commands:${NC}"
|
||||
echo -e " ${DF_CYAN}Git Commands:${DF_NC}"
|
||||
get_history | grep "^git" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo -e " ${CYAN}Navigation (cd):${NC}"
|
||||
echo -e " ${DF_CYAN}Navigation (cd):${DF_NC}"
|
||||
get_history | grep "^cd" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo -e " ${CYAN}File Operations (ls):${NC}"
|
||||
echo -e " ${DF_CYAN}File Operations (ls):${DF_NC}"
|
||||
get_history | grep "^ls" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo -e " ${CYAN}Package Management (pacman/paru/yay):${NC}"
|
||||
echo -e " ${DF_CYAN}Package Management (pacman/paru/yay):${DF_NC}"
|
||||
get_history | grep -E "^(pacman|paru|yay)" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo -e " ${CYAN}Editing (vim/nvim):${NC}"
|
||||
echo -e " ${DF_CYAN}Editing (vim/nvim):${DF_NC}"
|
||||
get_history | grep -E "^(vim|nvim)" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo -e " ${CYAN}Dotfiles Commands (dotfiles-):${NC}"
|
||||
echo -e " ${DF_CYAN}Dotfiles Commands (dotfiles-):${DF_NC}"
|
||||
get_history | grep "^dotfiles-" | wc -l | xargs printf " %d\n"
|
||||
|
||||
echo ""
|
||||
@@ -161,15 +145,14 @@ show_heatmap() {
|
||||
|
||||
echo ""
|
||||
if [[ -f "$HOME/.zsh_history" ]]; then
|
||||
# Extract hour from zsh history timestamp
|
||||
grep "^:" "$HOME/.zsh_history" | awk -F'[: ]' '{print $2}' | \
|
||||
date -f - "+%H" 2>/dev/null | sort | uniq -c | sort -k2n | while read count hour; do
|
||||
local bar_length=$((count / 5))
|
||||
local bar=$(printf '█%.0s' $(seq 1 $bar_length))
|
||||
printf " ${CYAN}%02d:00${NC} ${MAGENTA}%5d${NC} ${GREEN}${bar}${NC}\n" "$hour" "$count"
|
||||
printf " ${DF_CYAN}%02d:00${DF_NC} ${DF_MAGENTA}%5d${DF_NC} ${DF_GREEN}${bar}${DF_NC}\n" "$hour" "$count"
|
||||
done
|
||||
else
|
||||
echo " ${YELLOW}⚠${NC} Zsh history file required for hourly breakdown"
|
||||
echo " ${DF_YELLOW}⚠${DF_NC} Zsh history file required for hourly breakdown"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -182,10 +165,10 @@ show_dirs() {
|
||||
if [[ -f "$HOME/.zsh_history" ]]; then
|
||||
grep "cd " "$HOME/.zsh_history" | awk '{print $NF}' | sort | uniq -c | \
|
||||
sort -rn | head -15 | while read count dir; do
|
||||
printf " ${CYAN}%4d${NC} ${YELLOW}%s${NC}\n" "$count" "$dir"
|
||||
printf " ${DF_CYAN}%4d${DF_NC} ${DF_YELLOW}%s${DF_NC}\n" "$count" "$dir"
|
||||
done
|
||||
else
|
||||
echo " ${YELLOW}⚠${NC} Zsh history file required"
|
||||
echo " ${DF_YELLOW}⚠${DF_NC} Zsh history file required"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -198,14 +181,14 @@ show_git_breakdown() {
|
||||
local total=$(get_history | grep "^git" | wc -l)
|
||||
|
||||
if [[ $total -eq 0 ]]; then
|
||||
echo " ${YELLOW}No git commands found${NC}"
|
||||
echo " ${DF_YELLOW}No git commands found${DF_NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
get_history | grep "^git " | awk '{print $2}' | sort | uniq -c | sort -rn | \
|
||||
head -10 | while read count subcmd; do
|
||||
local percent=$((count * 100 / total))
|
||||
printf " ${YELLOW}git %-15s${NC} ${CYAN}%4d${NC} (${MAGENTA}%3d%%${NC})\n" \
|
||||
printf " ${DF_YELLOW}git %-15s${DF_NC} ${DF_CYAN}%4d${DF_NC} (${DF_MAGENTA}%3d%%${DF_NC})\n" \
|
||||
"$subcmd" "$count" "$percent"
|
||||
done
|
||||
|
||||
@@ -242,7 +225,6 @@ main() {
|
||||
show_git_breakdown
|
||||
;;
|
||||
export)
|
||||
# Export as JSON
|
||||
echo "{"
|
||||
echo " \"total_commands\": $(get_history | wc -l),"
|
||||
echo " \"unique_commands\": $(get_history | sort | uniq | wc -l),"
|
||||
|
||||
@@ -2,57 +2,31 @@
|
||||
# ============================================================================
|
||||
# Update Dotfiles Script
|
||||
# ============================================================================
|
||||
# Updates dotfiles from the git repository and relinks files
|
||||
#
|
||||
# Usage:
|
||||
# dotfiles-update.sh # Pull and re-run install
|
||||
# dotfiles-update.sh --skip-deps # Pull and re-run install without deps
|
||||
# dotfiles-update.sh --pull-only # Only git pull, don't re-run install
|
||||
#
|
||||
# Aliases: dfu, dfupdate
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# ============================================================================
|
||||
# Options
|
||||
# ============================================================================
|
||||
|
||||
SKIP_DEPS=true # Default to skipping deps on updates
|
||||
SKIP_DEPS=true
|
||||
PULL_ONLY=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--skip-deps)
|
||||
SKIP_DEPS=true
|
||||
;;
|
||||
--with-deps)
|
||||
SKIP_DEPS=false
|
||||
;;
|
||||
--pull-only)
|
||||
PULL_ONLY=true
|
||||
;;
|
||||
--skip-deps) SKIP_DEPS=true ;;
|
||||
--with-deps) SKIP_DEPS=false ;;
|
||||
--pull-only) PULL_ONLY=true ;;
|
||||
--help|-h)
|
||||
echo "Usage: dotfiles-update.sh [OPTIONS]"
|
||||
echo
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --skip-deps Skip dependency check (default for updates)"
|
||||
echo " --with-deps Run full dependency check"
|
||||
echo " --pull-only Only git pull, don't re-run install script"
|
||||
echo " --help Show this help message"
|
||||
echo
|
||||
echo "Aliases:"
|
||||
echo " dfu, dfupdate Update dotfiles"
|
||||
echo
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# Load Configuration
|
||||
# ============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOTFILES_CONF="${SCRIPT_DIR}/../dotfiles.conf"
|
||||
[[ -f "$DOTFILES_CONF" ]] || DOTFILES_CONF="$HOME/.dotfiles/dotfiles.conf"
|
||||
@@ -60,78 +34,55 @@ DOTFILES_CONF="${SCRIPT_DIR}/../dotfiles.conf"
|
||||
if [[ -f "$DOTFILES_CONF" ]]; then
|
||||
source "$DOTFILES_CONF"
|
||||
else
|
||||
# Fallback defaults
|
||||
DOTFILES_DIR="$HOME/.dotfiles"
|
||||
DOTFILES_BRANCH="main"
|
||||
DOTFILES_GITHUB_USER="adlee-was-taken"
|
||||
DOTFILES_REPO_NAME="dotfiles"
|
||||
DOTFILES_RAW_URL="https://raw.githubusercontent.com/${DOTFILES_GITHUB_USER}/${DOTFILES_REPO_NAME}/${DOTFILES_BRANCH}"
|
||||
DOTFILES_RAW_URL="https://raw.githubusercontent.com/adlee-was-taken/dotfiles/main"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Colors
|
||||
# ============================================================================
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
# Source shared colors
|
||||
source "$DOTFILES_DIR/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m' DF_RED=$'\033[0;31m'
|
||||
DF_BLUE=$'\033[0;34m' DF_CYAN=$'\033[0;36m' DF_NC=$'\033[0m'
|
||||
DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-update"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo -e "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo -e "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-update"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-update${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
echo -e "${DF_RED}✗${DF_NC} $1"
|
||||
}
|
||||
|
||||
print_step() {
|
||||
echo -e "${GREEN}==>${NC} $1"
|
||||
echo -e "${DF_GREEN}==>${DF_NC} $1"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -178,7 +129,7 @@ if [ $? -eq 0 ]; then
|
||||
|
||||
echo
|
||||
print_success "Update complete!"
|
||||
echo -e "Reload your shell: ${CYAN}reload${NC} or ${CYAN}source ~/.zshrc${NC}"
|
||||
echo -e "Reload your shell: ${DF_CYAN}reload${DF_NC} or ${DF_CYAN}source ~/.zshrc${DF_NC}"
|
||||
else
|
||||
print_error "Failed to update dotfiles"
|
||||
exit 1
|
||||
|
||||
@@ -5,54 +5,38 @@
|
||||
|
||||
set -e
|
||||
|
||||
readonly DOTFILES_HOME="${DOTFILES_HOME:-$HOME/.dotfiles}"
|
||||
readonly VAULT_DIR="${HOME}/.dotfiles/vault"
|
||||
readonly VAULT_FILE="${VAULT_DIR}/secrets.enc"
|
||||
|
||||
# Color codes
|
||||
readonly RED='\033[0;31m'
|
||||
readonly GREEN='\033[0;32m'
|
||||
readonly YELLOW='\033[1;33m'
|
||||
readonly BLUE='\033[0;34m'
|
||||
readonly CYAN='\033[0;36m'
|
||||
readonly NC='\033[0m'
|
||||
# Source shared colors
|
||||
source "$DOTFILES_HOME/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
DF_RED=$'\033[0;31m' DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m'
|
||||
DF_BLUE=$'\033[0;34m' DF_CYAN=$'\033[0;36m' DF_NC=$'\033[0m'
|
||||
DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-vault"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo -e "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo -e "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-vault"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-vault${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -60,16 +44,16 @@ print_header() {
|
||||
# ============================================================================
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1" >&2
|
||||
echo -e "${DF_RED}✗${DF_NC} $1" >&2
|
||||
}
|
||||
|
||||
print_section() {
|
||||
echo ""
|
||||
echo -e "${BLUE}▶${NC} $1"
|
||||
echo -e "${DF_BLUE}▶${DF_NC} $1"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -94,7 +78,6 @@ init_vault() {
|
||||
chmod 700 "$VAULT_DIR"
|
||||
|
||||
if [[ ! -f "$VAULT_FILE" ]]; then
|
||||
# Create empty encrypted file
|
||||
echo "{}" | $(get_cipher) > "$VAULT_FILE"
|
||||
print_success "Vault initialized"
|
||||
else
|
||||
@@ -147,24 +130,19 @@ vault_set() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get value from stdin if not provided
|
||||
if [[ -z "$value" ]]; then
|
||||
read -s -p "Enter value for $key: " value
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Decrypt current vault
|
||||
local current=$(decrypt_vault)
|
||||
|
||||
# Add new key-value pair (using jq if available, otherwise simple replacement)
|
||||
if command -v jq &> /dev/null; then
|
||||
local updated=$(echo "$current" | jq --arg k "$key" --arg v "$value" '.[$k] = $v')
|
||||
else
|
||||
# Simple fallback without jq
|
||||
local updated="{\"$key\": \"$value\"}"
|
||||
fi
|
||||
|
||||
# Encrypt and save
|
||||
encrypt_vault "$updated"
|
||||
print_success "Secret stored: $key"
|
||||
}
|
||||
@@ -182,7 +160,6 @@ vault_get() {
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "$vault" | jq -r ".\"$key\" // \"\"" | grep -v "^$"
|
||||
else
|
||||
# Simple grep fallback
|
||||
echo "$vault" | grep "\"$key\"" | cut -d'"' -f4
|
||||
fi
|
||||
}
|
||||
@@ -194,12 +171,11 @@ vault_list() {
|
||||
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "$vault" | jq -r 'keys[]' | while read key; do
|
||||
echo -e " ${CYAN}•${NC} $key"
|
||||
echo -e " ${DF_CYAN}•${DF_NC} $key"
|
||||
done
|
||||
else
|
||||
# Simple fallback
|
||||
echo "$vault" | grep -o '"[^"]*":' | sed 's/"//g' | sed 's/:$//' | while read key; do
|
||||
echo -e " ${CYAN}•${NC} $key"
|
||||
echo -e " ${DF_CYAN}•${DF_NC} $key"
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -280,23 +256,23 @@ vault_status() {
|
||||
print_section "Vault Status"
|
||||
|
||||
if [[ ! -d "$VAULT_DIR" ]]; then
|
||||
echo -e " ${YELLOW}⚠${NC} Vault not initialized"
|
||||
echo -e " ${DF_YELLOW}⚠${DF_NC} Vault not initialized"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ ! -f "$VAULT_FILE" ]]; then
|
||||
echo -e " ${YELLOW}⚠${NC} Vault file not found"
|
||||
echo -e " ${DF_YELLOW}⚠${DF_NC} Vault file not found"
|
||||
return
|
||||
fi
|
||||
|
||||
local size=$(du -h "$VAULT_FILE" | cut -f1)
|
||||
local modified=$(stat -c %y "$VAULT_FILE" 2>/dev/null | cut -d' ' -f1 || stat -f '%Sm' "$VAULT_FILE" 2>/dev/null)
|
||||
|
||||
echo -e " ${CYAN}Location:${NC} $VAULT_FILE"
|
||||
echo -e " ${CYAN}Size:${NC} $size"
|
||||
echo -e " ${CYAN}Modified:${NC} $modified"
|
||||
echo -e " ${CYAN}Encryption:${NC} $(get_cipher)"
|
||||
echo -e " ${CYAN}Permissions:${NC} $(stat -c '%a' $VAULT_FILE 2>/dev/null || stat -f '%a' "$VAULT_FILE")"
|
||||
echo -e " ${DF_CYAN}Location:${DF_NC} $VAULT_FILE"
|
||||
echo -e " ${DF_CYAN}Size:${DF_NC} $size"
|
||||
echo -e " ${DF_CYAN}Modified:${DF_NC} $modified"
|
||||
echo -e " ${DF_CYAN}Encryption:${DF_NC} $(get_cipher)"
|
||||
echo -e " ${DF_CYAN}Permissions:${DF_NC} $(stat -c '%a' $VAULT_FILE 2>/dev/null || stat -f '%a' "$VAULT_FILE")"
|
||||
|
||||
echo ""
|
||||
}
|
||||
@@ -308,7 +284,6 @@ vault_status() {
|
||||
main() {
|
||||
print_header
|
||||
|
||||
# Initialize vault if not exists
|
||||
if [[ ! -d "$VAULT_DIR" ]]; then
|
||||
init_vault
|
||||
fi
|
||||
|
||||
@@ -2,17 +2,8 @@
|
||||
# ============================================================================
|
||||
# Dotfiles Version Checker
|
||||
# ============================================================================
|
||||
# Shows current and remote version info
|
||||
#
|
||||
# Usage:
|
||||
# dotfiles-version.sh # Show version info
|
||||
# dotfiles-version.sh --check # Check for updates (exit 1 if behind)
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# Load Configuration
|
||||
# ============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOTFILES_CONF="${SCRIPT_DIR}/../dotfiles.conf"
|
||||
[[ -f "$DOTFILES_CONF" ]] || DOTFILES_CONF="${SCRIPT_DIR}/dotfiles.conf"
|
||||
@@ -27,82 +18,51 @@ else
|
||||
DOTFILES_RAW_URL="https://raw.githubusercontent.com/adlee-was-taken/dotfiles/main"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Colors
|
||||
# ============================================================================
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
|
||||
print_header() {
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local script_name="dotfiles-version"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
|
||||
# Colors
|
||||
local _M_RESET=$'\033[0m'
|
||||
local _M_BOLD=$'\033[1m'
|
||||
local _M_DIM=$'\033[2m'
|
||||
local _M_BLUE=$'\033[38;5;39m'
|
||||
local _M_GREY=$'\033[38;5;242m'
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<_M_WIDTH; i++)); do hline+="═"; done
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo -e "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_DIM}${h_center}${h_spaces}${h_right}${_M_RESET} ${_M_GREY}│${_M_RESET}"
|
||||
echo -e "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo ""
|
||||
# Source shared colors
|
||||
source "$DOTFILES_DIR/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m' DF_CYAN=$'\033[0;36m'
|
||||
DF_NC=$'\033[0m' DF_GREY=$'\033[38;5;242m' DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Options
|
||||
# ============================================================================
|
||||
|
||||
CHECK_ONLY=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--check|-c)
|
||||
CHECK_ONLY=true
|
||||
;;
|
||||
--check|-c) CHECK_ONLY=true ;;
|
||||
--help|-h)
|
||||
echo "Usage: dotfiles-version.sh [OPTIONS]"
|
||||
echo
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --check Only check for updates (exit 1 if behind)"
|
||||
echo " --help Show this help message"
|
||||
echo
|
||||
echo "Aliases:"
|
||||
echo " dfv, dfversion Show version info"
|
||||
echo
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-style header
|
||||
# ============================================================================
|
||||
|
||||
print_header() {
|
||||
if declare -f df_print_header &>/dev/null; then
|
||||
df_print_header "dotfiles-version"
|
||||
else
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOSTNAME:-$(hostname -s 2>/dev/null)}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
local hline="" && for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}✦ ${user}@${hostname}${DF_NC} ${DF_DIM}dotfiles-version${DF_NC} ${datetime} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Functions
|
||||
# ============================================================================
|
||||
@@ -132,7 +92,6 @@ get_local_date() {
|
||||
}
|
||||
|
||||
get_remote_version() {
|
||||
# Try to get version from remote dotfiles.conf
|
||||
local remote_conf=$(curl -fsSL "${DOTFILES_RAW_URL}/dotfiles.conf" 2>/dev/null)
|
||||
if [[ -n "$remote_conf" ]]; then
|
||||
echo "$remote_conf" | grep -oP 'DOTFILES_VERSION="\K[^"]+' || echo "unknown"
|
||||
@@ -176,7 +135,6 @@ compare_versions() {
|
||||
if [[ "$local_v" == "$remote_v" ]]; then
|
||||
echo "current"
|
||||
else
|
||||
# Simple semver comparison
|
||||
local local_parts=(${local_v//./ })
|
||||
local remote_parts=(${remote_v//./ })
|
||||
|
||||
@@ -220,44 +178,44 @@ main() {
|
||||
|
||||
print_header
|
||||
|
||||
echo -e "${CYAN}Local:${NC}"
|
||||
echo -e " Version: ${GREEN}${local_version}${NC}"
|
||||
echo -e "${DF_CYAN}Local:${DF_NC}"
|
||||
echo -e " Version: ${DF_GREEN}${local_version}${DF_NC}"
|
||||
echo -e " Commit: ${local_commit}"
|
||||
echo -e " Date: ${local_date}"
|
||||
echo -e " Path: ${DOTFILES_DIR}"
|
||||
echo
|
||||
|
||||
echo -e "${CYAN}Remote:${NC}"
|
||||
echo -e "${DF_CYAN}Remote:${DF_NC}"
|
||||
echo -e " Version: ${remote_version}"
|
||||
echo -e " Commit: ${remote_commit}"
|
||||
echo -e " Branch: ${DOTFILES_BRANCH}"
|
||||
echo
|
||||
|
||||
echo -e "${CYAN}Status:${NC}"
|
||||
echo -e "${DF_CYAN}Status:${DF_NC}"
|
||||
|
||||
case "$version_status" in
|
||||
current)
|
||||
echo -e " Version: ${GREEN}✓ Up to date${NC}"
|
||||
echo -e " Version: ${DF_GREEN}✓ Up to date${DF_NC}"
|
||||
;;
|
||||
behind)
|
||||
echo -e " Version: ${YELLOW}⚠ New version available: ${remote_version}${NC}"
|
||||
echo -e " Version: ${DF_YELLOW}⚠ New version available: ${remote_version}${DF_NC}"
|
||||
;;
|
||||
ahead)
|
||||
echo -e " Version: ${CYAN}ℹ Local is ahead of remote${NC}"
|
||||
echo -e " Version: ${DF_CYAN}ℹ Local is ahead of remote${DF_NC}"
|
||||
;;
|
||||
*)
|
||||
echo -e " Version: ${YELLOW}? Cannot determine${NC}"
|
||||
echo -e " Version: ${DF_YELLOW}? Cannot determine${DF_NC}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$commits_behind" -gt 0 ]]; then
|
||||
echo -e " Commits: ${YELLOW}⚠ ${commits_behind} commit(s) behind${NC}"
|
||||
echo -e " Commits: ${DF_YELLOW}⚠ ${commits_behind} commit(s) behind${DF_NC}"
|
||||
echo
|
||||
echo -e "${YELLOW}To update:${NC}"
|
||||
echo -e "${DF_YELLOW}To update:${DF_NC}"
|
||||
echo " dfu # Alias"
|
||||
echo " dotfiles-update.sh # Full command"
|
||||
elif [[ "$commits_behind" == "0" ]]; then
|
||||
echo -e " Commits: ${GREEN}✓ Up to date${NC}"
|
||||
echo -e " Commits: ${DF_GREEN}✓ Up to date${DF_NC}"
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
@@ -12,24 +12,20 @@
|
||||
# Only run in interactive shells
|
||||
[[ -o interactive ]] || return 0
|
||||
|
||||
# ============================================================================
|
||||
# MOTD Width, adjust if needed.
|
||||
# ============================================================================
|
||||
|
||||
_M_WIDTH=66
|
||||
# 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_RESET=$'\033[0m' DF_BOLD=$'\033[1m' DF_DIM=$'\033[2m'
|
||||
typeset -g DF_BLUE=$'\033[38;5;39m' DF_CYAN=$'\033[38;5;51m'
|
||||
typeset -g DF_GREEN=$'\033[38;5;82m' DF_YELLOW=$'\033[38;5;220m'
|
||||
typeset -g DF_GREY=$'\033[38;5;242m' DF_NC=$'\033[0m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Colors (ANSI escape codes)
|
||||
# MOTD Width
|
||||
# ============================================================================
|
||||
|
||||
_M_RESET=$'\033[0m'
|
||||
_M_BOLD=$'\033[1m'
|
||||
_M_DIM=$'\033[2m'
|
||||
_M_BLUE=$'\033[38;5;39m'
|
||||
_M_CYAN=$'\033[38;5;51m'
|
||||
_M_GREEN=$'\033[38;5;82m'
|
||||
_M_YELLOW=$'\033[38;5;220m'
|
||||
_M_GREY=$'\033[38;5;242m'
|
||||
typeset -g _M_WIDTH=66
|
||||
|
||||
# ============================================================================
|
||||
# Info Gathering
|
||||
@@ -64,7 +60,6 @@ _motd_disk() {
|
||||
# Box Drawing - Fixed Width
|
||||
# ============================================================================
|
||||
|
||||
|
||||
_motd_line() {
|
||||
local char="$1"
|
||||
local i
|
||||
@@ -76,7 +71,6 @@ _motd_line() {
|
||||
}
|
||||
|
||||
_motd_pad() {
|
||||
# Pad a plain string to exact width
|
||||
local str="$1"
|
||||
local width="$2"
|
||||
local len=${#str}
|
||||
@@ -101,14 +95,14 @@ show_motd() {
|
||||
local load=$(_motd_load)
|
||||
local mem=$(_motd_mem)
|
||||
local disk=$(_motd_disk)
|
||||
local local_ip=$(hostname -i | awk -F" " '{print $1}')
|
||||
local local_ip=$(hostname -i 2>/dev/null | awk -F" " '{print $1}' || echo "N/A")
|
||||
local hline=$(_motd_line '═')
|
||||
local inner=$((_M_WIDTH - 2))
|
||||
|
||||
echo ""
|
||||
|
||||
# Top border
|
||||
echo "${_M_GREY}╒${hline}╕${_M_RESET}"
|
||||
echo "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
|
||||
# Header: hostname + datetime
|
||||
local h_left="✦ ${hostname}"
|
||||
@@ -117,21 +111,17 @@ show_motd() {
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2 ))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
echo "${_M_GREY}│${_M_RESET} ${_M_BOLD}${_M_BLUE}${h_left}${_M_RESET}${h_spaces}${_M_YELLOW}${h_center}${h_spaces}${_M_RESET}${_M_BOLD}${h_right}${_M_RESET}${_M_GREY} │${_M_RESET}"
|
||||
echo "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_BLUE}${h_left}${DF_NC}${h_spaces}${DF_YELLOW}${h_center}${h_spaces}${DF_NC}${DF_BOLD}${h_right}${DF_NC}${DF_GREY} │${DF_NC}"
|
||||
|
||||
# Separator
|
||||
echo "${_M_GREY}╘${hline}╛${_M_RESET}"
|
||||
echo "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
|
||||
# Stats line - build with exact spacing
|
||||
local s1="${_M_YELLOW}▲ up:${_M_RESET}${uptime}"
|
||||
local s2="${_M_CYAN}◆ load:${_M_RESET}${load}"
|
||||
local s3="${_M_GREEN}◇ mem:${_M_RESET}${mem}"
|
||||
local s4="${_M_BLUE}⊡${_M_RESET} ${disk}"
|
||||
echo "${_M_GREY}${_M_DIM} 〘${_M_RESET}${s1}${_M_GREY}${_M_DIM}〙⎯〘${s2}${_M_GREY}${_M_DIM}〙⎯〘${s3}${_M_GREY}${_M_DIM}〙⎯〘${s4}${_M_GREY}${_M_DIM}〙 ${_M_RESET}"
|
||||
|
||||
|
||||
## Bottom border
|
||||
#echo "${_M_GREY}╘${hline}${_M_RESET}"
|
||||
# Stats line
|
||||
local s1="${DF_YELLOW}▲ up:${DF_NC}${uptime}"
|
||||
local s2="${DF_CYAN}◆ load:${DF_NC}${load}"
|
||||
local s3="${DF_GREEN}◇ mem:${DF_NC}${mem}"
|
||||
local s4="${DF_BLUE}⊡${DF_NC} ${disk}"
|
||||
echo "${DF_GREY}${DF_DIM} 〘${DF_NC}${s1}${DF_GREY}${DF_DIM}〙⎯〘${s2}${DF_GREY}${DF_DIM}〙⎯〘${s3}${DF_GREY}${DF_DIM}〙⎯〘${s4}${DF_GREY}${DF_DIM}〙 ${DF_NC}"
|
||||
|
||||
echo ""
|
||||
}
|
||||
@@ -148,7 +138,7 @@ show_motd_mini() {
|
||||
local uptime=$(_motd_uptime)
|
||||
local mem=$(_motd_mem)
|
||||
|
||||
echo "${_M_DIM}──${_M_RESET} ${_M_BOLD}${hostname}${_M_RESET} ${_M_DIM}│${_M_RESET} up:${uptime} ${_M_DIM}│${_M_RESET} mem:${mem} ${_M_DIM}──${_M_RESET}"
|
||||
echo "${DF_DIM}──${DF_NC} ${DF_BOLD}${hostname}${DF_NC} ${DF_DIM}│${DF_NC} up:${uptime} ${DF_DIM}│${DF_NC} mem:${mem} ${DF_DIM}──${DF_NC}"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -9,24 +9,15 @@
|
||||
# pw otp <item> # Get OTP/TOTP code
|
||||
# pw search <query> # Search items
|
||||
# pw copy <item> # Copy password to clipboard
|
||||
#
|
||||
# Supported: LastPass (lpass)
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/password-manager.zsh
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Colors
|
||||
typeset -g PW_GREEN=$'\033[0;32m'
|
||||
typeset -g PW_BLUE=$'\033[0;34m'
|
||||
typeset -g PW_YELLOW=$'\033[1;33m'
|
||||
typeset -g PW_CYAN=$'\033[0;36m'
|
||||
typeset -g PW_RED=$'\033[0;31m'
|
||||
typeset -g PW_NC=$'\033[0m'
|
||||
# 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'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LastPass Functions
|
||||
@@ -78,9 +69,8 @@ pw() {
|
||||
local cmd="${1:-help}"
|
||||
shift
|
||||
|
||||
# Check if lastpass is installed
|
||||
if ! command -v lpass &>/dev/null; then
|
||||
echo -e "${PW_RED}✗${PW_NC} LastPass CLI (lpass) not installed"
|
||||
echo -e "${DF_RED}✗${DF_NC} LastPass CLI (lpass) not installed"
|
||||
echo "Install with: yay -S lastpass-cli"
|
||||
return 1
|
||||
fi
|
||||
@@ -133,7 +123,7 @@ pw() {
|
||||
;;
|
||||
|
||||
help|--help|-h|*)
|
||||
echo -e "${PW_BLUE}Password Manager CLI (LastPass)${PW_NC}"
|
||||
echo -e "${DF_BLUE}Password Manager CLI (LastPass)${DF_NC}"
|
||||
echo
|
||||
echo "Usage: pw <command> [args]"
|
||||
echo
|
||||
@@ -174,7 +164,6 @@ alias pws='pw search'
|
||||
# ============================================================================
|
||||
|
||||
if command -v fzf &>/dev/null; then
|
||||
# Interactive password selection and copy
|
||||
pwf() {
|
||||
if ! command -v lpass &>/dev/null; then
|
||||
echo "LastPass CLI not installed"
|
||||
@@ -188,7 +177,6 @@ if command -v fzf &>/dev/null; then
|
||||
fi
|
||||
}
|
||||
|
||||
# Interactive OTP selection and copy
|
||||
pwof() {
|
||||
if ! command -v lpass &>/dev/null; then
|
||||
echo "LastPass CLI not installed"
|
||||
|
||||
@@ -8,164 +8,73 @@
|
||||
# - Suggests existing aliases for frequently typed commands
|
||||
# - "Did you mean?" for unknown commands
|
||||
# - Package installation suggestions for missing commands
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/smart-suggest.zsh
|
||||
# ============================================================================
|
||||
|
||||
# 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_CYAN=$'\033[0;36m' DF_YELLOW=$'\033[1;33m'
|
||||
typeset -g DF_GREEN=$'\033[0;32m' DF_RED=$'\033[0;31m'
|
||||
typeset -g DF_DIM=$'\033[2m' DF_NC=$'\033[0m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Enable/disable features
|
||||
typeset -g SMART_SUGGEST_ENABLED=true
|
||||
typeset -g SMART_SUGGEST_TYPOS=true
|
||||
typeset -g SMART_SUGGEST_ALIASES=true
|
||||
typeset -g SMART_SUGGEST_PACKAGES=true
|
||||
typeset -g SMART_SUGGEST_HISTORY=true
|
||||
|
||||
# Tracking file for alias suggestions
|
||||
typeset -g SMART_SUGGEST_TRACK_FILE="$HOME/.cache/smart-suggest-track"
|
||||
|
||||
# Colors
|
||||
typeset -g SS_CYAN=$'\033[0;36m'
|
||||
typeset -g SS_YELLOW=$'\033[1;33m'
|
||||
typeset -g SS_GREEN=$'\033[0;32m'
|
||||
typeset -g SS_RED=$'\033[0;31m'
|
||||
typeset -g SS_DIM=$'\033[2m'
|
||||
typeset -g SS_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Common Typo Database
|
||||
# ============================================================================
|
||||
|
||||
typeset -gA TYPO_CORRECTIONS=(
|
||||
# Git typos
|
||||
[gti]="git"
|
||||
[gitt]="git"
|
||||
[got]="git"
|
||||
[gut]="git"
|
||||
[gi]="git"
|
||||
[giit]="git"
|
||||
[ggit]="git"
|
||||
[gitst]="git st"
|
||||
[gits]="git s"
|
||||
[gitl]="git l"
|
||||
[gitd]="git d"
|
||||
[gitp]="git p"
|
||||
[psuh]="push"
|
||||
[psull]="pull"
|
||||
[pul]="pull"
|
||||
[puhs]="push"
|
||||
[stauts]="status"
|
||||
[statis]="status"
|
||||
[statuus]="status"
|
||||
[comit]="commit"
|
||||
[commti]="commit"
|
||||
[commt]="commit"
|
||||
[chekcout]="checkout"
|
||||
[chekout]="checkout"
|
||||
[checkou]="checkout"
|
||||
[branhc]="branch"
|
||||
[barnch]="branch"
|
||||
[bracnh]="branch"
|
||||
[marge]="merge"
|
||||
[merg]="merge"
|
||||
[stsh]="stash"
|
||||
[stahs]="stash"
|
||||
[gti]="git" [gitt]="git" [got]="git" [gut]="git" [gi]="git"
|
||||
[giit]="git" [ggit]="git" [gitst]="git st" [gits]="git s"
|
||||
[gitl]="git l" [gitd]="git d" [gitp]="git p"
|
||||
[psuh]="push" [psull]="pull" [pul]="pull" [puhs]="push"
|
||||
[stauts]="status" [statis]="status" [statuus]="status"
|
||||
[comit]="commit" [commti]="commit" [commt]="commit"
|
||||
[chekcout]="checkout" [chekout]="checkout" [checkou]="checkout"
|
||||
[branhc]="branch" [barnch]="branch" [bracnh]="branch"
|
||||
[marge]="merge" [merg]="merge" [stsh]="stash" [stahs]="stash"
|
||||
|
||||
# Docker typos
|
||||
[dokcer]="docker"
|
||||
[doker]="docker"
|
||||
[docekr]="docker"
|
||||
[dcoker]="docker"
|
||||
[dockr]="docker"
|
||||
[docke]="docker"
|
||||
[docker-compoes]="docker-compose"
|
||||
[docker-compsoe]="docker-compose"
|
||||
[dokcer]="docker" [doker]="docker" [docekr]="docker"
|
||||
[dcoker]="docker" [dockr]="docker" [docke]="docker"
|
||||
[docker-compoes]="docker-compose" [docker-compsoe]="docker-compose"
|
||||
[dokcer-compose]="docker-compose"
|
||||
|
||||
# Common command typos
|
||||
[sl]="ls"
|
||||
[l]="ls"
|
||||
[sls]="ls"
|
||||
[lss]="ls"
|
||||
[cta]="cat"
|
||||
[catt]="cat"
|
||||
[caat]="cat"
|
||||
[grpe]="grep"
|
||||
[gerp]="grep"
|
||||
[gre]="grep"
|
||||
[grepp]="grep"
|
||||
[mkdri]="mkdir"
|
||||
[mkdr]="mkdir"
|
||||
[mdkir]="mkdir"
|
||||
[mdir]="mkdir"
|
||||
[rn]="rm"
|
||||
[rmm]="rm"
|
||||
[chmdo]="chmod"
|
||||
[chomd]="chmod"
|
||||
[chonw]="chown"
|
||||
[cown]="chown"
|
||||
[tarr]="tar"
|
||||
[tart]="tar"
|
||||
[wegt]="wget"
|
||||
[wgte]="wget"
|
||||
[weget]="wget"
|
||||
[crul]="curl"
|
||||
[crul]="curl"
|
||||
[curll]="curl"
|
||||
[pytohn]="python"
|
||||
[pyhton]="python"
|
||||
[pythn]="python"
|
||||
[pyton]="python"
|
||||
[pthon]="python"
|
||||
[pytho]="python"
|
||||
[ndoe]="node"
|
||||
[noed]="node"
|
||||
[noode]="node"
|
||||
[npn]="npm"
|
||||
[nmpm]="npm"
|
||||
[nppm]="npm"
|
||||
[yran]="yarn"
|
||||
[yaarn]="yarn"
|
||||
[yanr]="yarn"
|
||||
[suod]="sudo"
|
||||
[sudi]="sudo"
|
||||
[sduo]="sudo"
|
||||
[sudoo]="sudo"
|
||||
[sssh]="ssh"
|
||||
[shh]="ssh"
|
||||
[sssh]="ssh"
|
||||
[scpp]="scp"
|
||||
[spcp]="scp"
|
||||
[vmi]="vim"
|
||||
[imv]="vim"
|
||||
[viim]="vim"
|
||||
[cde]="code"
|
||||
[cdoe]="code"
|
||||
[cod]="code"
|
||||
[clera]="clear"
|
||||
[cler]="clear"
|
||||
[claer]="clear"
|
||||
[ecoh]="echo"
|
||||
[ehco]="echo"
|
||||
[echoo]="echo"
|
||||
[exti]="exit"
|
||||
[ext]="exit"
|
||||
[exitt]="exit"
|
||||
[eixt]="exit"
|
||||
[histroy]="history"
|
||||
[hisotry]="history"
|
||||
[hsitory]="history"
|
||||
[histrory]="history"
|
||||
[maek]="make"
|
||||
[mkae]="make"
|
||||
[amke]="make"
|
||||
[makee]="make"
|
||||
[ccd]="cd"
|
||||
[cdd]="cd"
|
||||
[ccd]="cd"
|
||||
[sl]="ls" [l]="ls" [sls]="ls" [lss]="ls"
|
||||
[cta]="cat" [catt]="cat" [caat]="cat"
|
||||
[grpe]="grep" [gerp]="grep" [gre]="grep" [grepp]="grep"
|
||||
[mkdri]="mkdir" [mkdr]="mkdir" [mdkir]="mkdir" [mdir]="mkdir"
|
||||
[rn]="rm" [rmm]="rm" [chmdo]="chmod" [chomd]="chmod"
|
||||
[chonw]="chown" [cown]="chown" [tarr]="tar" [tart]="tar"
|
||||
[wegt]="wget" [wgte]="wget" [weget]="wget"
|
||||
[crul]="curl" [curll]="curl"
|
||||
[pytohn]="python" [pyhton]="python" [pythn]="python"
|
||||
[pyton]="python" [pthon]="python" [pytho]="python"
|
||||
[ndoe]="node" [noed]="node" [noode]="node"
|
||||
[npn]="npm" [nmpm]="npm" [nppm]="npm"
|
||||
[yran]="yarn" [yaarn]="yarn" [yanr]="yarn"
|
||||
[suod]="sudo" [sudi]="sudo" [sduo]="sudo" [sudoo]="sudo"
|
||||
[sssh]="ssh" [shh]="ssh" [scpp]="scp" [spcp]="scp"
|
||||
[vmi]="vim" [imv]="vim" [viim]="vim"
|
||||
[cde]="code" [cdoe]="code" [cod]="code"
|
||||
[clera]="clear" [cler]="clear" [claer]="clear"
|
||||
[ecoh]="echo" [ehco]="echo" [echoo]="echo"
|
||||
[exti]="exit" [ext]="exit" [exitt]="exit" [eixt]="exit"
|
||||
[histroy]="history" [hisotry]="history" [hsitory]="history"
|
||||
[histrory]="history" [maek]="make" [mkae]="make"
|
||||
[amke]="make" [makee]="make" [ccd]="cd" [cdd]="cd"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
@@ -173,46 +82,25 @@ typeset -gA TYPO_CORRECTIONS=(
|
||||
# ============================================================================
|
||||
|
||||
_ss_get_package_manager() {
|
||||
if command -v apt-get &>/dev/null; then
|
||||
echo "apt"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
echo "dnf"
|
||||
elif command -v pacman &>/dev/null; then
|
||||
echo "pacman"
|
||||
elif command -v brew &>/dev/null; then
|
||||
echo "brew"
|
||||
else
|
||||
echo ""
|
||||
if command -v apt-get &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v pacman &>/dev/null; then echo "pacman"
|
||||
elif command -v brew &>/dev/null; then echo "brew"
|
||||
else echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Common commands and their packages
|
||||
typeset -gA COMMAND_PACKAGES=(
|
||||
[htop]="htop"
|
||||
[tree]="tree"
|
||||
[jq]="jq"
|
||||
[fd]="fd-find:apt fd:pacman fd:brew"
|
||||
[rg]="ripgrep"
|
||||
[bat]="bat"
|
||||
[eza]="eza"
|
||||
[exa]="exa"
|
||||
[fzf]="fzf"
|
||||
[tldr]="tldr"
|
||||
[ncdu]="ncdu"
|
||||
[duf]="duf"
|
||||
[dust]="dust"
|
||||
[procs]="procs"
|
||||
[bottom]="bottom"
|
||||
[btm]="bottom"
|
||||
[lazygit]="lazygit"
|
||||
[lazydocker]="lazydocker"
|
||||
[neofetch]="neofetch"
|
||||
[fastfetch]="fastfetch"
|
||||
[httpie]="httpie"
|
||||
[http]="httpie"
|
||||
[htop]="htop" [tree]="tree" [jq]="jq"
|
||||
[fd]="fd-find:apt fd:pacman fd:brew" [rg]="ripgrep"
|
||||
[bat]="bat" [eza]="eza" [exa]="exa" [fzf]="fzf"
|
||||
[tldr]="tldr" [ncdu]="ncdu" [duf]="duf" [dust]="dust"
|
||||
[procs]="procs" [bottom]="bottom" [btm]="bottom"
|
||||
[lazygit]="lazygit" [lazydocker]="lazydocker"
|
||||
[neofetch]="neofetch" [fastfetch]="fastfetch"
|
||||
[httpie]="httpie" [http]="httpie"
|
||||
[delta]="git-delta:apt delta:pacman git-delta:brew"
|
||||
[glow]="glow"
|
||||
[navi]="navi"
|
||||
[glow]="glow" [navi]="navi"
|
||||
)
|
||||
|
||||
_ss_suggest_package() {
|
||||
@@ -226,9 +114,7 @@ _ss_suggest_package() {
|
||||
|
||||
local pkg=""
|
||||
|
||||
# Check for PM-specific package name
|
||||
if [[ "$pkg_info" == *":"* ]]; then
|
||||
# Format: "pkg1:pm1 pkg2:pm2"
|
||||
for entry in ${(s: :)pkg_info}; do
|
||||
local p="${entry%%:*}"
|
||||
local m="${entry##*:}"
|
||||
@@ -237,7 +123,6 @@ _ss_suggest_package() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Fallback to first package
|
||||
[[ -z "$pkg" ]] && pkg="${${(s: :)pkg_info}[1]%%:*}"
|
||||
else
|
||||
pkg="$pkg_info"
|
||||
@@ -264,12 +149,11 @@ _ss_track_command() {
|
||||
[[ "$SMART_SUGGEST_ALIASES" != true ]] && return
|
||||
|
||||
local cmd="$1"
|
||||
[[ ${#cmd} -lt 8 ]] && return # Skip short commands
|
||||
[[ ${#cmd} -lt 8 ]] && return
|
||||
|
||||
mkdir -p "$(dirname "$SMART_SUGGEST_TRACK_FILE")"
|
||||
echo "$cmd" >> "$SMART_SUGGEST_TRACK_FILE"
|
||||
|
||||
# Periodically check for alias suggestions
|
||||
local count=$(grep -Fc "$cmd" "$SMART_SUGGEST_TRACK_FILE" 2>/dev/null || echo 0)
|
||||
|
||||
if [[ $count -ge 10 && $((count % 10)) -eq 0 ]]; then
|
||||
@@ -281,23 +165,21 @@ _ss_suggest_alias_for() {
|
||||
local cmd="$1"
|
||||
local count="$2"
|
||||
|
||||
# Check if an alias already exists for this command
|
||||
local existing=$(alias | grep -F "='$cmd'" | head -1 | cut -d= -f1)
|
||||
|
||||
if [[ -n "$existing" ]]; then
|
||||
echo
|
||||
echo -e "${SS_CYAN}💡 Tip:${SS_NC} You've typed '${SS_YELLOW}$cmd${SS_NC}' $count times"
|
||||
echo -e " You already have an alias: ${SS_GREEN}$existing${SS_NC}"
|
||||
echo -e "${DF_CYAN}💡 Tip:${DF_NC} You've typed '${DF_YELLOW}$cmd${DF_NC}' $count times"
|
||||
echo -e " You already have an alias: ${DF_GREEN}$existing${DF_NC}"
|
||||
else
|
||||
# Generate suggested alias name
|
||||
local suggested=$(echo "$cmd" | awk '{
|
||||
for(i=1; i<=NF && i<=3; i++)
|
||||
printf substr($i,1,1)
|
||||
}')
|
||||
|
||||
echo
|
||||
echo -e "${SS_CYAN}💡 Tip:${SS_NC} You've typed '${SS_YELLOW}$cmd${SS_NC}' $count times"
|
||||
echo -e " Consider adding: ${SS_GREEN}alias $suggested='$cmd'${SS_NC}"
|
||||
echo -e "${DF_CYAN}💡 Tip:${DF_NC} You've typed '${DF_YELLOW}$cmd${DF_NC}' $count times"
|
||||
echo -e " Consider adding: ${DF_GREEN}alias $suggested='$cmd'${DF_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -315,34 +197,31 @@ command_not_found_handler() {
|
||||
return 127
|
||||
}
|
||||
|
||||
echo -e "${SS_RED}✗${SS_NC} Command not found: ${SS_YELLOW}$cmd${SS_NC}"
|
||||
echo -e "${DF_RED}✗${DF_NC} Command not found: ${DF_YELLOW}$cmd${DF_NC}"
|
||||
|
||||
local suggestion_made=false
|
||||
|
||||
# Check for typo
|
||||
if [[ "$SMART_SUGGEST_TYPOS" == true ]]; then
|
||||
local correction="${TYPO_CORRECTIONS[$cmd]}"
|
||||
if [[ -n "$correction" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Did you mean: ${SS_GREEN}$correction${SS_NC}?"
|
||||
echo -e " ${SS_DIM}Run: $correction $args${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Did you mean: ${DF_GREEN}$correction${DF_NC}?"
|
||||
echo -e " ${DF_DIM}Run: $correction $args${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for similar commands
|
||||
if [[ "$suggestion_made" != true ]]; then
|
||||
local similar=$(compgen -c 2>/dev/null | grep -i "^${cmd:0:3}" | head -3 | tr '\n' ', ' | sed 's/,$//')
|
||||
if [[ -n "$similar" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Similar commands: ${SS_GREEN}$similar${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Similar commands: ${DF_GREEN}$similar${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Suggest package installation
|
||||
if [[ "$SMART_SUGGEST_PACKAGES" == true ]]; then
|
||||
local install_cmd=$(_ss_suggest_package "$cmd")
|
||||
if [[ -n "$install_cmd" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} To install: ${SS_GREEN}$install_cmd${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} To install: ${DF_GREEN}$install_cmd${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
@@ -351,42 +230,30 @@ command_not_found_handler() {
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Pre-exec Hook for Tracking
|
||||
# Hooks
|
||||
# ============================================================================
|
||||
|
||||
_ss_preexec_hook() {
|
||||
local cmd="$1"
|
||||
|
||||
# Extract just the command (first word)
|
||||
local first_word="${cmd%% *}"
|
||||
|
||||
# Track full commands for alias suggestions
|
||||
_ss_track_command "$cmd"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Post-command Hook for Suggestions
|
||||
# ============================================================================
|
||||
|
||||
_ss_precmd_hook() {
|
||||
local exit_code=$?
|
||||
|
||||
# Only suggest if last command failed
|
||||
[[ $exit_code -eq 0 ]] && return
|
||||
|
||||
# Get last command
|
||||
local last_cmd=$(fc -ln -1 2>/dev/null | sed 's/^[[:space:]]*//')
|
||||
[[ -z "$last_cmd" ]] && return
|
||||
|
||||
local first_word="${last_cmd%% *}"
|
||||
|
||||
# Check if it's a git command with typo
|
||||
if [[ "$first_word" == "git" && $exit_code -ne 0 ]]; then
|
||||
local git_subcmd=$(echo "$last_cmd" | awk '{print $2}')
|
||||
local correction="${TYPO_CORRECTIONS[$git_subcmd]}"
|
||||
|
||||
if [[ -n "$correction" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Did you mean: ${SS_GREEN}git $correction${SS_NC}?"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Did you mean: ${DF_GREEN}git $correction${DF_NC}?"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -395,18 +262,15 @@ _ss_precmd_hook() {
|
||||
# Quick Fix Function
|
||||
# ============================================================================
|
||||
|
||||
# Run the suggested correction
|
||||
# Usage: !! <correction> or just press up and edit
|
||||
fuck() {
|
||||
local last_cmd=$(fc -ln -1 2>/dev/null | sed 's/^[[:space:]]*//')
|
||||
local first_word="${last_cmd%% *}"
|
||||
|
||||
# Check for typo correction
|
||||
local correction="${TYPO_CORRECTIONS[$first_word]}"
|
||||
|
||||
if [[ -n "$correction" ]]; then
|
||||
local fixed_cmd="${last_cmd/$first_word/$correction}"
|
||||
echo -e "${SS_GREEN}Running:${SS_NC} $fixed_cmd"
|
||||
echo -e "${DF_GREEN}Running:${DF_NC} $fixed_cmd"
|
||||
eval "$fixed_cmd"
|
||||
else
|
||||
echo "No automatic fix available"
|
||||
@@ -419,28 +283,9 @@ fuck() {
|
||||
# ============================================================================
|
||||
|
||||
_ss_setup() {
|
||||
# Add preexec hook
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook preexec _ss_preexec_hook
|
||||
add-zsh-hook precmd _ss_precmd_hook
|
||||
}
|
||||
|
||||
# Initialize
|
||||
[[ "$SMART_SUGGEST_ENABLED" == true ]] && _ss_setup
|
||||
|
||||
# ============================================================================
|
||||
# Usage Examples (commented)
|
||||
# ============================================================================
|
||||
|
||||
# $ gti status
|
||||
# ✗ Command not found: gti
|
||||
# → Did you mean: git?
|
||||
# Run: git status
|
||||
|
||||
# $ dokcer ps
|
||||
# ✗ Command not found: dokcer
|
||||
# → Did you mean: docker?
|
||||
|
||||
# After typing "docker-compose up -d" 10 times:
|
||||
# 💡 Tip: You've typed 'docker-compose up -d' 10 times
|
||||
# Consider adding: alias dcu='docker-compose up -d'
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# ============================================================================
|
||||
# Snapper Snapshot Functions for CachyOS/Arch with limine-snapper-sync
|
||||
# ============================================================================
|
||||
# Add these functions to your ~/.zshrc or ~/.dotfiles/zsh/.zshrc
|
||||
|
||||
# Colors for output
|
||||
SNAP_GREEN='\033[0;32m'
|
||||
SNAP_YELLOW='\033[1;33m'
|
||||
SNAP_RED='\033[0;31m'
|
||||
SNAP_BLUE='\033[0;34m'
|
||||
SNAP_NC='\033[0m' # No Color
|
||||
# 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_YELLOW=$'\033[1;33m'
|
||||
typeset -g DF_RED=$'\033[0;31m' DF_BLUE=$'\033[0;34m' DF_NC=$'\033[0m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Snapshot Function with Limine Validation
|
||||
@@ -19,110 +18,81 @@ snap-create() {
|
||||
local snap_config="root"
|
||||
local limine_conf="/boot/limine.conf"
|
||||
|
||||
# Print header
|
||||
echo -e "\n${SNAP_BLUE}╔════════════════════════════════════════════════════════════╗${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}║${SNAP_NC} Snapper Snapshot Creation & Validation ${SNAP_BLUE}║${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}╚════════════════════════════════════════════════════════════╝${SNAP_NC}\n"
|
||||
echo -e "\n${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Snapper Snapshot Creation & Validation ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n"
|
||||
|
||||
# Check if description was provided
|
||||
if [[ -z "$description" ]]; then
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} No description provided"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} No description provided"
|
||||
echo -n "Enter snapshot description: "
|
||||
read description
|
||||
|
||||
if [[ -z "$description" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Description required. Aborting."
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$description" ]] && { echo -e "${DF_RED}✗${DF_NC} Description required. Aborting."; return 1; }
|
||||
fi
|
||||
|
||||
# Check if limine.conf exists
|
||||
if [[ ! -f "$limine_conf" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Limine config not found: $limine_conf"
|
||||
return 1
|
||||
fi
|
||||
[[ ! -f "$limine_conf" ]] && { echo -e "${DF_RED}✗${DF_NC} Limine config not found: $limine_conf"; return 1; }
|
||||
|
||||
# Get limine.conf state before snapshot
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Checking limine.conf state before snapshot"
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Checking limine.conf state before snapshot"
|
||||
local before_checksum=$(sudo md5sum "$limine_conf" | awk '{print $1}')
|
||||
local before_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0")
|
||||
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Before: $before_entries snapshot entries"
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Before checksum: $before_checksum"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Before: $before_entries snapshot entries"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Before checksum: $before_checksum"
|
||||
|
||||
# Create the snapshot
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Creating snapshot: \"$description\""
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Creating snapshot: \"$description\""
|
||||
|
||||
local snapshot_num=$(sudo snapper -c "$snap_config" create \
|
||||
--description "$description" \
|
||||
--print-number)
|
||||
local snapshot_num=$(sudo snapper -c "$snap_config" create --description "$description" --print-number)
|
||||
|
||||
if [[ -z "$snapshot_num" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Failed to create snapshot"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$snapshot_num" ]] && { echo -e "${DF_RED}✗${DF_NC} Failed to create snapshot"; return 1; }
|
||||
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Snapshot created: #$snapshot_num"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Snapshot created: #$snapshot_num"
|
||||
|
||||
# Trigger limine-snapper-sync service
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Triggering limine-snapper-sync service..."
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Triggering limine-snapper-sync service..."
|
||||
|
||||
if sudo systemctl start limine-snapper-sync.service; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Service triggered successfully"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Service triggered successfully"
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} Failed to trigger service (may run automatically)"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Failed to trigger service (may run automatically)"
|
||||
fi
|
||||
|
||||
# Wait a moment for the service to complete
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Waiting for limine-snapper-sync to update limine.conf..."
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Waiting for limine-snapper-sync to update limine.conf..."
|
||||
sleep 2
|
||||
|
||||
# Get limine.conf state after snapshot
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Validating limine.conf update"
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Validating limine.conf update"
|
||||
local after_checksum=$(sudo md5sum "$limine_conf" | awk '{print $1}')
|
||||
local after_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0")
|
||||
|
||||
# Validate the update
|
||||
local validation_passed=true
|
||||
|
||||
if [[ "$before_checksum" == "$after_checksum" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} limine.conf was NOT updated (checksum unchanged)"
|
||||
echo -e "${DF_RED}✗${DF_NC} limine.conf was NOT updated (checksum unchanged)"
|
||||
validation_passed=false
|
||||
else
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} limine.conf was updated"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} limine.conf was updated"
|
||||
fi
|
||||
|
||||
if [[ "$after_entries" -le "$before_entries" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} No new snapshot entry added to limine.conf"
|
||||
echo -e "${DF_RED}✗${DF_NC} No new snapshot entry added to limine.conf"
|
||||
validation_passed=false
|
||||
else
|
||||
local new_entries=$((after_entries - before_entries))
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Added $new_entries new snapshot entry/entries"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Added $new_entries new snapshot entry/entries"
|
||||
fi
|
||||
|
||||
# Check for the specific snapshot in limine.conf
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Searching for snapshot #$snapshot_num in limine.conf"
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Searching for snapshot #$snapshot_num in limine.conf"
|
||||
|
||||
# Format in limine.conf is: ///[number] │ [date]
|
||||
if sudo grep -qP "^\\s*///$snapshot_num\\s*│" "$limine_conf"; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Found snapshot #$snapshot_num in limine.conf"
|
||||
|
||||
# Show the entry with description
|
||||
echo -e "\n${SNAP_BLUE}Snapshot entry:${SNAP_NC}"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Found snapshot #$snapshot_num in limine.conf"
|
||||
echo -e "\n${DF_BLUE}Snapshot entry:${DF_NC}"
|
||||
local entry_line=$(sudo grep -nP "^\\s*///$snapshot_num\\s*│" "$limine_conf" | head -n 1 | cut -d: -f1)
|
||||
if [[ -n "$entry_line" ]]; then
|
||||
# Show the snapshot header and description
|
||||
sudo sed -n "${entry_line}p; $((entry_line+1))p" "$limine_conf" | sed 's/^/ /'
|
||||
fi
|
||||
[[ -n "$entry_line" ]] && sudo sed -n "${entry_line}p; $((entry_line+1))p" "$limine_conf" | sed 's/^/ /'
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Snapshot #$snapshot_num NOT found in limine.conf"
|
||||
echo -e "${DF_RED}✗${DF_NC} Snapshot #$snapshot_num NOT found in limine.conf"
|
||||
validation_passed=false
|
||||
fi
|
||||
|
||||
# Print summary
|
||||
echo -e "\n${SNAP_BLUE}╔════════════════════════════════════════════════════════════╗${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}║${SNAP_NC} Summary ${SNAP_BLUE}║${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}╚════════════════════════════════════════════════════════════╝${SNAP_NC}"
|
||||
echo -e "\n${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Summary ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}"
|
||||
echo -e "Snapshot Number: #$snapshot_num"
|
||||
echo -e "Description: \"$description\""
|
||||
echo -e "Config: $snap_config"
|
||||
@@ -130,14 +100,14 @@ snap-create() {
|
||||
echo -e "After entries: $after_entries"
|
||||
|
||||
if [[ "$validation_passed" == true ]]; then
|
||||
echo -e "Status: ${SNAP_GREEN}✓ VALIDATED${SNAP_NC}"
|
||||
echo -e "\n${SNAP_GREEN}✓${SNAP_NC} Snapshot created and limine.conf successfully updated!"
|
||||
echo -e "Status: ${DF_GREEN}✓ VALIDATED${DF_NC}"
|
||||
echo -e "\n${DF_GREEN}✓${DF_NC} Snapshot created and limine.conf successfully updated!"
|
||||
return 0
|
||||
else
|
||||
echo -e "Status: ${SNAP_RED}✗ VALIDATION FAILED${SNAP_NC}"
|
||||
echo -e "\n${SNAP_RED}✗${SNAP_NC} Snapshot created but limine.conf validation failed!"
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} Check if limine-snapper-sync service is running properly"
|
||||
echo -e "${SNAP_YELLOW}Run:${SNAP_NC} sudo systemctl status limine-snapper-sync.service"
|
||||
echo -e "Status: ${DF_RED}✗ VALIDATION FAILED${DF_NC}"
|
||||
echo -e "\n${DF_RED}✗${DF_NC} Snapshot created but limine.conf validation failed!"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Check if limine-snapper-sync service is running properly"
|
||||
echo -e "${DF_YELLOW}Run:${DF_NC} sudo systemctl status limine-snapper-sync.service"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -146,343 +116,135 @@ snap-create() {
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
# List recent snapshots
|
||||
snap-list() {
|
||||
local count="${1:-10}"
|
||||
echo -e "${SNAP_BLUE}Recent $count snapshots:${SNAP_NC}\n"
|
||||
echo -e "${DF_BLUE}Recent $count snapshots:${DF_NC}\n"
|
||||
sudo snapper -c root list | tail -n "$((count + 1))"
|
||||
}
|
||||
|
||||
# Show snapshot details
|
||||
snap-show() {
|
||||
if [[ -z "$1" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Usage: snap-show <snapshot_number>"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$1" ]] && { echo -e "${DF_RED}✗${DF_NC} Usage: snap-show <snapshot_number>"; return 1; }
|
||||
|
||||
echo -e "${SNAP_BLUE}Snapshot #$1 details:${SNAP_NC}\n"
|
||||
echo -e "${DF_BLUE}Snapshot #$1 details:${DF_NC}\n"
|
||||
sudo snapper -c root list | grep "^\s*$1\s"
|
||||
|
||||
echo -e "\n${SNAP_BLUE}In limine.conf:${SNAP_NC}"
|
||||
echo -e "\n${DF_BLUE}In limine.conf:${DF_NC}"
|
||||
if sudo grep -qP "^\\s*///$1\\s*│" /boot/limine.conf; then
|
||||
local entry_line=$(sudo grep -nP "^\\s*///$1\\s*│" /boot/limine.conf | head -n 1 | cut -d: -f1)
|
||||
if [[ -n "$entry_line" ]]; then
|
||||
sudo sed -n "${entry_line}p; $((entry_line+1))p" /boot/limine.conf
|
||||
fi
|
||||
[[ -n "$entry_line" ]] && sudo sed -n "${entry_line}p; $((entry_line+1))p" /boot/limine.conf
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} Not found in limine.conf"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Not found in limine.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
# Delete snapshot with limine validation
|
||||
snap-delete() {
|
||||
if [[ -z "$1" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Usage: snap-delete <snapshot_number>"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$1" ]] && { echo -e "${DF_RED}✗${DF_NC} Usage: snap-delete <snapshot_number>"; return 1; }
|
||||
|
||||
local snapshot_num="$1"
|
||||
local limine_conf="/boot/limine.conf"
|
||||
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Deleting snapshot #$snapshot_num"
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Deleting snapshot #$snapshot_num"
|
||||
|
||||
# Check before deletion (count snapshot entries using correct format)
|
||||
local before_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0")
|
||||
|
||||
# Delete the snapshot
|
||||
sudo snapper -c root delete "$snapshot_num"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Snapshot #$snapshot_num deleted"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Snapshot #$snapshot_num deleted"
|
||||
|
||||
# Trigger sync service
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Triggering limine-snapper-sync..."
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Triggering limine-snapper-sync..."
|
||||
sudo systemctl start limine-snapper-sync.service
|
||||
|
||||
# Wait for service to complete
|
||||
sleep 2
|
||||
|
||||
# Check after deletion (use correct format)
|
||||
local after_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0")
|
||||
|
||||
if [[ "$after_entries" -lt "$before_entries" ]]; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} limine.conf updated (removed entry)"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} limine.conf updated (removed entry)"
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} limine.conf may not have been updated"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} limine.conf may not have been updated"
|
||||
fi
|
||||
|
||||
# Verify snapshot is gone from limine.conf (use correct format)
|
||||
if ! sudo grep -qP "^\\s*///$snapshot_num\\s*│" "$limine_conf"; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Snapshot #$snapshot_num removed from limine.conf"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Snapshot #$snapshot_num removed from limine.conf"
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Snapshot #$snapshot_num still in limine.conf!"
|
||||
echo -e "${DF_RED}✗${DF_NC} Snapshot #$snapshot_num still in limine.conf!"
|
||||
fi
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Failed to delete snapshot #$snapshot_num"
|
||||
echo -e "${DF_RED}✗${DF_NC} Failed to delete snapshot #$snapshot_num"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check limine.conf for all snapshots
|
||||
snap-check-limine() {
|
||||
local limine_conf="/boot/limine.conf"
|
||||
|
||||
echo -e "${SNAP_BLUE}╔════════════════════════════════════════════════════════════╗${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}║${SNAP_NC} Limine Snapshot Entries ${SNAP_BLUE}║${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}╚════════════════════════════════════════════════════════════╝${SNAP_NC}\n"
|
||||
echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Limine Snapshot Entries ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n"
|
||||
|
||||
if [[ ! -f "$limine_conf" ]]; then
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Limine config not found: $limine_conf"
|
||||
return 1
|
||||
fi
|
||||
[[ ! -f "$limine_conf" ]] && { echo -e "${DF_RED}✗${DF_NC} Limine config not found: $limine_conf"; return 1; }
|
||||
|
||||
# Get latest snapshot number (excluding snapshot 0)
|
||||
local latest_snapshot=$(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | tail -n 1 | awk '{print $1}')
|
||||
[[ -z "$latest_snapshot" ]] && { echo -e "${DF_YELLOW}⚠${DF_NC} No snapshots found in snapper"; return 1; }
|
||||
|
||||
if [[ -z "$latest_snapshot" ]]; then
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} No snapshots found in snapper"
|
||||
return 1
|
||||
fi
|
||||
echo -e "${DF_BLUE}Latest snapshot:${DF_NC} #$latest_snapshot"
|
||||
|
||||
echo -e "${SNAP_BLUE}Latest snapshot:${SNAP_NC} #$latest_snapshot"
|
||||
|
||||
# Check if latest snapshot is in limine.conf
|
||||
# Format in limine.conf is: ///49 │ 2025-12-14 01:15:33
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Checking if latest snapshot is in limine.conf"
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Checking if latest snapshot is in limine.conf"
|
||||
|
||||
if sudo grep -qP "^\\s*///$latest_snapshot\s*│" "$limine_conf"; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Latest snapshot #$latest_snapshot is present in limine.conf"
|
||||
|
||||
# Show the entry with description
|
||||
echo -e "\n${SNAP_BLUE}Latest snapshot entry:${SNAP_NC}"
|
||||
local entry_line=$(sudo grep -nP "^\\s*///$latest_snapshot\s*│" "$limine_conf" | head -n 1 | cut -d: -f1)
|
||||
if [[ -n "$entry_line" ]]; then
|
||||
# Show the snapshot header and description (next line)
|
||||
sudo sed -n "${entry_line}p; $((entry_line+1))p" "$limine_conf" | sed 's/^/ /'
|
||||
fi
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Latest snapshot #$latest_snapshot is present in limine.conf"
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Latest snapshot #$latest_snapshot is NOT in limine.conf"
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} This may indicate the sync service hasn't run yet"
|
||||
echo -e "${DF_RED}✗${DF_NC} Latest snapshot #$latest_snapshot is NOT in limine.conf"
|
||||
fi
|
||||
|
||||
# Count snapshot entries (lines matching ///[number] │)
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Counting snapshot entries"
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Counting snapshot entries"
|
||||
local entry_count=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0")
|
||||
echo -e "${SNAP_BLUE}Total snapshot entries:${SNAP_NC} $entry_count\n"
|
||||
|
||||
# Show all snapshot entries
|
||||
if [[ "$entry_count" -gt 0 ]]; then
|
||||
echo -e "${SNAP_BLUE}Snapshot boot entries:${SNAP_NC}\n"
|
||||
|
||||
# Extract snapshot entries with their descriptions
|
||||
local snap_nums=($(sudo grep -oP "^\\s*///\\K\\d+(?=\\s*│)" "$limine_conf" | sort -n))
|
||||
|
||||
local i=1
|
||||
for snap_num in "${snap_nums[@]}"; do
|
||||
local snap_line=$(sudo grep -nP "^\\s*///$snap_num\s*│" "$limine_conf" | head -n 1)
|
||||
local line_num=$(echo "$snap_line" | cut -d: -f1)
|
||||
local date_time=$(echo "$snap_line" | cut -d: -f2- | grep -oP "│\\s*\\K.*" || echo "")
|
||||
|
||||
# Get description from next line (starts with "comment:")
|
||||
local desc=""
|
||||
if [[ -n "$line_num" ]]; then
|
||||
desc=$(sudo sed -n "$((line_num+1))p" "$limine_conf" | grep -oP "comment:\\s*\\K.*" || echo "")
|
||||
fi
|
||||
|
||||
printf "%2d. Snapshot #%-3s %s %s\n" "$i" "$snap_num" "$date_time" "${desc:-(no description)}"
|
||||
((i++))
|
||||
done
|
||||
|
||||
# Show snapshot range
|
||||
if [[ ${#snap_nums[@]} -gt 0 ]]; then
|
||||
echo -e "\n${SNAP_BLUE}Snapshot range in boot menu:${SNAP_NC}"
|
||||
echo -e " Oldest: #${snap_nums[1]}"
|
||||
echo -e " Newest: #${snap_nums[-1]}"
|
||||
fi
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} No snapshot entries found in limine.conf"
|
||||
fi
|
||||
|
||||
# Compare with actual snapshots
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Comparing with snapper list"
|
||||
echo ""
|
||||
|
||||
# Count all snapshots except snapshot 0 (current system)
|
||||
local snapper_count=$(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | wc -l)
|
||||
|
||||
# Get oldest and newest snapshots
|
||||
local oldest_snapshot=$(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | head -n 1 | awk '{print $1}')
|
||||
|
||||
echo -e "Snapshots in snapper: $snapper_count (range: #$oldest_snapshot to #$latest_snapshot)"
|
||||
echo -e "Entries in limine.conf: $entry_count"
|
||||
|
||||
if [[ "$snapper_count" -eq "$entry_count" ]]; then
|
||||
echo -e "Status: ${SNAP_GREEN}✓ FULLY SYNCED${SNAP_NC}"
|
||||
else
|
||||
local diff=$((snapper_count - entry_count))
|
||||
echo -e "Status: ${SNAP_YELLOW}⚠ PARTIALLY SYNCED${SNAP_NC}"
|
||||
echo -e "Missing from boot menu: $diff snapshot(s)"
|
||||
|
||||
# Check if latest is present (most important)
|
||||
if sudo grep -qP "^\\s*///$latest_snapshot\s*│" "$limine_conf"; then
|
||||
echo -e "\n${SNAP_GREEN}✓${SNAP_NC} Latest snapshot IS available for boot (this is what matters most)"
|
||||
else
|
||||
echo -e "\n${SNAP_RED}✗${SNAP_NC} Latest snapshot NOT available for boot (run: snap-sync)"
|
||||
fi
|
||||
|
||||
echo -e "\n${SNAP_BLUE}Note:${SNAP_NC} limine-snapper-sync typically limits boot entries to recent snapshots"
|
||||
echo -e " This is normal and prevents boot menu clutter"
|
||||
|
||||
# Check the limit from comment line
|
||||
local limit_comment=$(sudo grep -oP "comment:\\s*\\K\\d+\\s*/\\s*\\d+" "$limine_conf" | head -n 1)
|
||||
if [[ -n "$limit_comment" ]]; then
|
||||
echo -e " Current limit: $limit_comment snapshots"
|
||||
fi
|
||||
|
||||
# Show which snapshots are missing if there aren't too many
|
||||
if [[ $diff -le 20 ]]; then
|
||||
echo -e "\n${SNAP_BLUE}Snapshots NOT in boot menu:${SNAP_NC}"
|
||||
|
||||
local all_snapshots=($(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | awk '{print $1}'))
|
||||
local limine_snapshots=($(sudo grep -oP "^\\s*///\\K\\d+(?=\\s*│)" "$limine_conf"))
|
||||
local missing_count=0
|
||||
|
||||
for snap_num in "${all_snapshots[@]}"; do
|
||||
# Check if this snapshot is in limine.conf
|
||||
local found=false
|
||||
for limine_snap in "${limine_snapshots[@]}"; do
|
||||
if [[ "$snap_num" == "$limine_snap" ]]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$found" == false ]]; then
|
||||
if [[ $missing_count -lt 10 ]]; then
|
||||
local snap_info=$(sudo snapper -c root list | grep "^\s*$snap_num\s")
|
||||
local snap_type=$(echo "$snap_info" | awk '{print $2}')
|
||||
local snap_desc=$(echo "$snap_info" | awk -F'|' '{print $5}' | xargs)
|
||||
echo -e " #$snap_num ($snap_type) - $snap_desc"
|
||||
fi
|
||||
((missing_count++))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $missing_count -gt 10 ]]; then
|
||||
echo -e " ... and $((missing_count - 10)) more"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo -e "${DF_BLUE}Total snapshot entries:${DF_NC} $entry_count\n"
|
||||
}
|
||||
|
||||
# Show detailed snapshot information with types
|
||||
snap-info() {
|
||||
echo -e "${SNAP_BLUE}╔════════════════════════════════════════════════════════════╗${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}║${SNAP_NC} Detailed Snapshot Information ${SNAP_BLUE}║${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}╚════════════════════════════════════════════════════════════╝${SNAP_NC}\n"
|
||||
|
||||
echo -e "${SNAP_BLUE}All snapshots with types:${SNAP_NC}\n"
|
||||
sudo snapper -c root list
|
||||
|
||||
echo -e "\n${SNAP_BLUE}Breakdown by type:${SNAP_NC}\n"
|
||||
|
||||
local single_count=$(sudo snapper -c root list | grep -c "single" || echo "0")
|
||||
local pre_count=$(sudo snapper -c root list | grep -c "pre" || echo "0")
|
||||
local post_count=$(sudo snapper -c root list | grep -c "post" || echo "0")
|
||||
local total=$(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | wc -l)
|
||||
|
||||
echo -e "Single snapshots: $single_count"
|
||||
echo -e "Pre snapshots: $pre_count"
|
||||
echo -e "Post snapshots: $post_count"
|
||||
echo -e "Total snapshots: $total"
|
||||
|
||||
echo -e "\n${SNAP_BLUE}Snapshot types explained:${SNAP_NC}"
|
||||
echo -e " ${SNAP_GREEN}single${SNAP_NC} - Manual snapshots (created by you)"
|
||||
echo -e " ${SNAP_GREEN}pre${SNAP_NC} - Before system changes (e.g., package updates)"
|
||||
echo -e " ${SNAP_GREEN}post${SNAP_NC} - After system changes"
|
||||
}
|
||||
|
||||
# Manually trigger sync service
|
||||
snap-sync() {
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Manually triggering limine-snapper-sync..."
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Manually triggering limine-snapper-sync..."
|
||||
|
||||
if sudo systemctl start limine-snapper-sync.service; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Service triggered successfully"
|
||||
|
||||
# Wait for completion
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Service triggered successfully"
|
||||
sleep 2
|
||||
|
||||
# Show status
|
||||
echo -e "\n${SNAP_BLUE}Service status:${SNAP_NC}"
|
||||
echo -e "\n${DF_BLUE}Service status:${DF_NC}"
|
||||
sudo systemctl status limine-snapper-sync.service --no-pager -l | tail -n 10
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Failed to trigger service"
|
||||
echo -e "${DF_RED}✗${DF_NC} Failed to trigger service"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate limine-snapper-sync service is working
|
||||
snap-validate-service() {
|
||||
echo -e "${SNAP_BLUE}╔════════════════════════════════════════════════════════════╗${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}║${SNAP_NC} Limine-Snapper-Sync Service Validation ${SNAP_BLUE}║${SNAP_NC}"
|
||||
echo -e "${SNAP_BLUE}╚════════════════════════════════════════════════════════════╝${SNAP_NC}\n"
|
||||
echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Limine-Snapper-Sync Service Validation ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n"
|
||||
|
||||
# Check if service unit exists
|
||||
echo -e "${SNAP_BLUE}==>${SNAP_NC} Checking service unit"
|
||||
echo -e "${DF_BLUE}==>${DF_NC} Checking service unit"
|
||||
|
||||
if systemctl list-unit-files | grep -q "limine-snapper-sync.service"; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} limine-snapper-sync.service unit exists"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} limine-snapper-sync.service unit exists"
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} limine-snapper-sync.service unit NOT found"
|
||||
echo -e "\n${SNAP_YELLOW}Install with:${SNAP_NC} paru -S limine-snapper-sync"
|
||||
echo -e "${DF_RED}✗${DF_NC} limine-snapper-sync.service unit NOT found"
|
||||
echo -e "\n${DF_YELLOW}Install with:${DF_NC} paru -S limine-snapper-sync"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if service is enabled
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Checking if service is enabled"
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Checking if service is enabled"
|
||||
|
||||
if systemctl is-enabled limine-snapper-sync.service &>/dev/null; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Service is enabled"
|
||||
echo -e "${DF_GREEN}✓${DF_NC} Service is enabled"
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} Service is NOT enabled"
|
||||
echo -e "${SNAP_YELLOW}Enable with:${SNAP_NC} sudo systemctl enable limine-snapper-sync.service"
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Service is NOT enabled"
|
||||
echo -e "${DF_YELLOW}Enable with:${DF_NC} sudo systemctl enable limine-snapper-sync.service"
|
||||
fi
|
||||
|
||||
# Check service status
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Checking service status"
|
||||
|
||||
if systemctl is-active limine-snapper-sync.service &>/dev/null; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Service is active"
|
||||
else
|
||||
echo -e "${SNAP_YELLOW}⚠${SNAP_NC} Service is inactive (this is normal for oneshot services)"
|
||||
fi
|
||||
|
||||
# Show recent service logs
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Recent service logs (last 10 lines)"
|
||||
echo -e "\n${DF_BLUE}==>${DF_NC} Recent service logs (last 10 lines)"
|
||||
echo ""
|
||||
sudo journalctl -u limine-snapper-sync.service -n 10 --no-pager | sed 's/^/ /'
|
||||
|
||||
# Check snapper config
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Checking snapper configuration"
|
||||
|
||||
if [[ -f "/etc/snapper/configs/root" ]]; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} Snapper root config exists"
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} Snapper root config not found"
|
||||
fi
|
||||
|
||||
# Check limine.conf
|
||||
echo -e "\n${SNAP_BLUE}==>${SNAP_NC} Checking limine.conf"
|
||||
|
||||
if [[ -f "/boot/limine.conf" ]]; then
|
||||
echo -e "${SNAP_GREEN}✓${SNAP_NC} limine.conf exists"
|
||||
local snap_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" /boot/limine.conf || echo "0")
|
||||
echo -e " Snapshot entries: $snap_entries"
|
||||
else
|
||||
echo -e "${SNAP_RED}✗${SNAP_NC} limine.conf not found"
|
||||
fi
|
||||
|
||||
echo -e "\n${SNAP_GREEN}✓${SNAP_NC} Validation complete"
|
||||
echo -e "\n${DF_GREEN}✓${DF_NC} Validation complete"
|
||||
}
|
||||
|
||||
# Quick snapshot aliases
|
||||
@@ -492,16 +254,3 @@ alias snaprm='snap-delete'
|
||||
alias snapshow='snap-show'
|
||||
alias snapcheck='snap-check-limine'
|
||||
alias snapsync='snap-sync'
|
||||
alias snapinfo='snap-info'
|
||||
|
||||
# ============================================================================
|
||||
# Usage Examples (commented out - uncomment to see examples)
|
||||
# ============================================================================
|
||||
|
||||
# snap-create "Before system update"
|
||||
# snap-list 20
|
||||
# snap-show 42
|
||||
# snap-delete 42
|
||||
# snap-check-limine
|
||||
# snap-sync
|
||||
# snap-validate-service
|
||||
|
||||
@@ -2,26 +2,16 @@
|
||||
# SSH Session Manager with Tmux Integration
|
||||
# ============================================================================
|
||||
# Manage SSH connections with automatic tmux session handling
|
||||
#
|
||||
# Usage:
|
||||
# ssh-save <name> <connection> # Save SSH connection
|
||||
# ssh-connect <name> # Connect and attach/create tmux session
|
||||
# ssh-list # List all saved connections
|
||||
# ssh-delete <name> # Delete saved connection
|
||||
# ssh-edit <name> # Edit connection details
|
||||
# sshf # Fuzzy search and connect
|
||||
#
|
||||
# Features:
|
||||
# - Automatic tmux session attach/create on remote host
|
||||
# - Named sessions per connection
|
||||
# - Connection profiles with SSH options
|
||||
# - Auto-reconnect support
|
||||
# - Dotfiles sync to remote (optional)
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/ssh-manager.zsh
|
||||
# ============================================================================
|
||||
|
||||
# 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'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
@@ -31,33 +21,14 @@ typeset -g SSH_AUTO_TMUX="${SSH_AUTO_TMUX:-true}"
|
||||
typeset -g SSH_TMUX_SESSION_PREFIX="${SSH_TMUX_SESSION_PREFIX:-ssh}"
|
||||
typeset -g SSH_SYNC_DOTFILES="${SSH_SYNC_DOTFILES:-ask}"
|
||||
|
||||
# Colors
|
||||
typeset -g SSH_GREEN=$'\033[0;32m'
|
||||
typeset -g SSH_BLUE=$'\033[0;34m'
|
||||
typeset -g SSH_YELLOW=$'\033[1;33m'
|
||||
typeset -g SSH_CYAN=$'\033[0;36m'
|
||||
typeset -g SSH_RED=$'\033[0;31m'
|
||||
typeset -g SSH_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
_ssh_print_step() {
|
||||
echo -e "${SSH_BLUE}==>${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_success() {
|
||||
echo -e "${SSH_GREEN}✓${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_error() {
|
||||
echo -e "${SSH_RED}✗${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_info() {
|
||||
echo -e "${SSH_CYAN}ℹ${SSH_NC} $1"
|
||||
}
|
||||
_ssh_print_step() { echo -e "${DF_BLUE}==>${DF_NC} $1"; }
|
||||
_ssh_print_success() { echo -e "${DF_GREEN}✓${DF_NC} $1"; }
|
||||
_ssh_print_error() { echo -e "${DF_RED}✗${DF_NC} $1"; }
|
||||
_ssh_print_info() { echo -e "${DF_CYAN}ℹ${DF_NC} $1"; }
|
||||
|
||||
_ssh_init_profiles() {
|
||||
if [[ ! -f "$SSH_PROFILES_FILE" ]]; then
|
||||
@@ -65,10 +36,6 @@ _ssh_init_profiles() {
|
||||
cat > "$SSH_PROFILES_FILE" << 'EOF'
|
||||
# SSH Connection Profiles
|
||||
# Format: name|user@host|port|key_file|options|description
|
||||
#
|
||||
# Example:
|
||||
# prod|user@prod.example.com|22|~/.ssh/prod_key|-L 8080:localhost:80|Production server
|
||||
# dev|user@dev.example.com|2222||ForwardAgent=yes|Development server
|
||||
EOF
|
||||
_ssh_print_success "Created SSH profiles file: $SSH_PROFILES_FILE"
|
||||
fi
|
||||
@@ -77,14 +44,8 @@ EOF
|
||||
_ssh_parse_profile() {
|
||||
local name="$1"
|
||||
local line=$(grep "^${name}|" "$SSH_PROFILES_FILE" 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$line" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse: name|connection|port|key|options|description
|
||||
[[ -z "$line" ]] && return 1
|
||||
IFS='|' read -r profile_name connection port key_file ssh_opts description <<< "$line"
|
||||
|
||||
echo "$connection|$port|$key_file|$ssh_opts|$description"
|
||||
}
|
||||
|
||||
@@ -93,88 +54,62 @@ _ssh_parse_profile() {
|
||||
# ============================================================================
|
||||
|
||||
ssh-save() {
|
||||
local name="$1"
|
||||
local connection="$2"
|
||||
local port="${3:-22}"
|
||||
local key_file="${4:-}"
|
||||
local options="${5:-}"
|
||||
local description="${6:-}"
|
||||
local name="$1" connection="$2" port="${3:-22}" key_file="${4:-}" options="${5:-}" description="${6:-}"
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
if [[ -z "$name" || -z "$connection" ]]; then
|
||||
[[ -z "$name" || -z "$connection" ]] && {
|
||||
echo "Usage: ssh-save <name> <user@host> [port] [key_file] [options] [description]"
|
||||
echo
|
||||
echo "Examples:"
|
||||
echo " ssh-save prod user@prod.com"
|
||||
echo " ssh-save dev user@dev.com 2222 ~/.ssh/dev_key"
|
||||
echo " ssh-save vpn user@vpn.com 22 '' '-D 9090' 'VPN server'"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if profile exists
|
||||
if grep -q "^${name}|" "$SSH_PROFILES_FILE" 2>/dev/null; then
|
||||
echo -e "${SSH_YELLOW}⚠${SSH_NC} Profile '$name' already exists"
|
||||
read -q "REPLY?Overwrite? [y/N]: "
|
||||
echo
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} Profile '$name' already exists"
|
||||
read -q "REPLY?Overwrite? [y/N]: "; echo
|
||||
[[ ! "$REPLY" =~ ^[Yy]$ ]] && return 1
|
||||
|
||||
# Remove old entry
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
fi
|
||||
|
||||
# Save new profile
|
||||
echo "${name}|${connection}|${port}|${key_file}|${options}|${description}" >> "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Saved SSH profile: $name"
|
||||
echo " Connection: $connection"
|
||||
[[ "$port" != "22" ]] && echo " Port: $port"
|
||||
[[ -n "$key_file" ]] && echo " Key: $key_file"
|
||||
[[ -n "$options" ]] && echo " Options: $options"
|
||||
[[ -n "$description" ]] && echo " Description: $description"
|
||||
}
|
||||
|
||||
ssh-list() {
|
||||
_ssh_init_profiles
|
||||
|
||||
echo -e "${SSH_BLUE}╔════════════════════════════════════════════════════════════╗${SSH_NC}"
|
||||
echo -e "${SSH_BLUE}║${SSH_NC} SSH Connection Profiles ${SSH_BLUE}║${SSH_NC}"
|
||||
echo -e "${SSH_BLUE}╚════════════════════════════════════════════════════════════╝${SSH_NC}"
|
||||
echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} SSH Connection Profiles ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}"
|
||||
echo
|
||||
|
||||
local has_profiles=false
|
||||
while IFS='|' read -r name connection port key options description; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
has_profiles=true
|
||||
|
||||
echo -e "${SSH_GREEN}●${SSH_NC} ${SSH_CYAN}$name${SSH_NC}"
|
||||
echo -e "${DF_GREEN}●${DF_NC} ${DF_CYAN}$name${DF_NC}"
|
||||
echo " Connection: $connection"
|
||||
[[ "$port" != "22" && -n "$port" ]] && echo " Port: $port"
|
||||
[[ -n "$key" ]] && echo " Key: $key"
|
||||
[[ -n "$options" ]] && echo " Options: $options"
|
||||
[[ -n "$description" ]] && echo " Description: $description"
|
||||
echo
|
||||
done < "$SSH_PROFILES_FILE"
|
||||
|
||||
if [[ "$has_profiles" != true ]]; then
|
||||
[[ "$has_profiles" != true ]] && {
|
||||
_ssh_print_info "No profiles saved yet"
|
||||
echo
|
||||
echo "Create a profile with:"
|
||||
echo " ssh-save myserver user@example.com"
|
||||
fi
|
||||
echo "Create a profile with: ssh-save myserver user@example.com"
|
||||
}
|
||||
}
|
||||
|
||||
ssh-delete() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-delete <name>"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$name" ]] && { echo "Usage: ssh-delete <name>"; return 1; }
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
@@ -183,182 +118,74 @@ ssh-delete() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Remove profile
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Deleted profile: $name"
|
||||
}
|
||||
|
||||
ssh-edit() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
# Edit entire file
|
||||
${EDITOR:-vim} "$SSH_PROFILES_FILE"
|
||||
return
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
echo -e "${SSH_CYAN}Editing profile: $name${SSH_NC}"
|
||||
echo
|
||||
|
||||
read "new_connection?Connection [$connection]: "
|
||||
new_connection="${new_connection:-$connection}"
|
||||
|
||||
read "new_port?Port [$port]: "
|
||||
new_port="${new_port:-$port}"
|
||||
|
||||
read "new_key?Key file [$key_file]: "
|
||||
new_key="${new_key:-$key_file}"
|
||||
|
||||
read "new_opts?SSH options [$ssh_opts]: "
|
||||
new_opts="${new_opts:-$ssh_opts}"
|
||||
|
||||
read "new_desc?Description [$description]: "
|
||||
new_desc="${new_desc:-$description}"
|
||||
|
||||
# Remove old and add new
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
echo "${name}|${new_connection}|${new_port}|${new_key}|${new_opts}|${new_desc}" >> "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Updated profile: $name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SSH Connection with Tmux Integration
|
||||
# ============================================================================
|
||||
|
||||
ssh-connect() {
|
||||
local name="$1"
|
||||
local session_name="${2:-${SSH_TMUX_SESSION_PREFIX}-${name}}"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-connect <profile_name> [tmux_session_name]"
|
||||
echo
|
||||
echo "Saved profiles:"
|
||||
ssh-list
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$name" ]] && { echo "Usage: ssh-connect <profile_name>"; ssh-list; return 1; }
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
# Parse profile
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
echo "Use 'ssh-save $name user@host' to create it"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$profile_data" ]] && { _ssh_print_error "Profile '$name' not found"; return 1; }
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
_ssh_print_step "Connecting to: $name"
|
||||
[[ -n "$description" ]] && echo " $description"
|
||||
|
||||
# Build SSH command
|
||||
local ssh_cmd="ssh"
|
||||
|
||||
# Add port
|
||||
[[ -n "$port" && "$port" != "22" ]] && ssh_cmd="$ssh_cmd -p $port"
|
||||
|
||||
# Add key file
|
||||
[[ -n "$key_file" ]] && ssh_cmd="$ssh_cmd -i $key_file"
|
||||
|
||||
# Add custom options
|
||||
[[ -n "$ssh_opts" ]] && ssh_cmd="$ssh_cmd $ssh_opts"
|
||||
|
||||
# Add connection
|
||||
ssh_cmd="$ssh_cmd $connection"
|
||||
|
||||
# Tmux integration
|
||||
if [[ "$SSH_AUTO_TMUX" == "true" ]]; then
|
||||
_ssh_print_info "Attaching to tmux session: $session_name"
|
||||
|
||||
# SSH with tmux attach or create
|
||||
local tmux_cmd="tmux attach-session -t $session_name 2>/dev/null || tmux new-session -s $session_name"
|
||||
|
||||
# Execute
|
||||
eval "$ssh_cmd -t '$tmux_cmd'"
|
||||
else
|
||||
# Direct SSH without tmux
|
||||
eval "$ssh_cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Fuzzy Search Integration (requires fzf)
|
||||
# ============================================================================
|
||||
|
||||
sshf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_ssh_print_error "fzf not installed"
|
||||
echo "Install: git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && ~/.fzf/install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
# Build selection list
|
||||
local profiles=()
|
||||
while IFS='|' read -r name connection port key options description; do
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
local display="$name → $connection"
|
||||
[[ -n "$description" ]] && display="$display ($description)"
|
||||
|
||||
profiles+=("$name|$display")
|
||||
done < "$SSH_PROFILES_FILE"
|
||||
|
||||
if [[ ${#profiles[@]} -eq 0 ]]; then
|
||||
_ssh_print_info "No profiles saved"
|
||||
return 1
|
||||
fi
|
||||
[[ ${#profiles[@]} -eq 0 ]] && { _ssh_print_info "No profiles saved"; return 1; }
|
||||
|
||||
# Fuzzy select
|
||||
local selection=$(printf '%s\n' "${profiles[@]}" | \
|
||||
fzf --height=50% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--prompt='SSH > ' \
|
||||
--preview='echo {}' \
|
||||
--preview-window=hidden \
|
||||
--delimiter='|' \
|
||||
--with-nth=2)
|
||||
fzf --height=50% --layout=reverse --border=rounded --prompt='SSH > ' \
|
||||
--delimiter='|' --with-nth=2)
|
||||
|
||||
if [[ -n "$selection" ]]; then
|
||||
local profile_name="${selection%%|*}"
|
||||
ssh-connect "$profile_name"
|
||||
fi
|
||||
[[ -n "$selection" ]] && ssh-connect "${selection%%|*}"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Quick Reconnect
|
||||
# ============================================================================
|
||||
|
||||
ssh-reconnect() {
|
||||
local name="${1:-last}"
|
||||
|
||||
if [[ "$name" == "last" ]]; then
|
||||
# Get last connected profile from history
|
||||
local last_profile=$(grep "ssh-connect" "$HISTFILE" 2>/dev/null | tail -1 | awk '{print $2}')
|
||||
|
||||
if [[ -z "$last_profile" ]]; then
|
||||
_ssh_print_error "No previous connection found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
[[ -z "$last_profile" ]] && { _ssh_print_error "No previous connection found"; return 1; }
|
||||
name="$last_profile"
|
||||
fi
|
||||
|
||||
@@ -366,58 +193,29 @@ ssh-reconnect() {
|
||||
ssh-connect "$name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Dotfiles Sync to Remote
|
||||
# ============================================================================
|
||||
|
||||
ssh-sync-dotfiles() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-sync-dotfiles <profile_name>"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$name" ]] && { echo "Usage: ssh-sync-dotfiles <profile_name>"; return 1; }
|
||||
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
return 1
|
||||
fi
|
||||
[[ -z "$profile_data" ]] && { _ssh_print_error "Profile '$name' not found"; return 1; }
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
local dotfiles_dir="${DOTFILES_DIR:-$HOME/.dotfiles}"
|
||||
|
||||
if [[ ! -d "$dotfiles_dir" ]]; then
|
||||
_ssh_print_error "Dotfiles directory not found: $dotfiles_dir"
|
||||
return 1
|
||||
fi
|
||||
[[ ! -d "$dotfiles_dir" ]] && { _ssh_print_error "Dotfiles directory not found"; return 1; }
|
||||
|
||||
_ssh_print_step "Syncing dotfiles to: $connection"
|
||||
|
||||
# Build rsync command
|
||||
local rsync_cmd="rsync -avz --exclude='.git' --exclude='*.local'"
|
||||
|
||||
[[ -n "$port" && "$port" != "22" ]] && rsync_cmd="$rsync_cmd -e 'ssh -p $port'"
|
||||
[[ -n "$key_file" ]] && rsync_cmd="$rsync_cmd -e 'ssh -i $key_file'"
|
||||
|
||||
rsync_cmd="$rsync_cmd $dotfiles_dir/ $connection:.dotfiles/"
|
||||
|
||||
_ssh_print_info "Running: $rsync_cmd"
|
||||
|
||||
if eval "$rsync_cmd"; then
|
||||
_ssh_print_success "Dotfiles synced successfully"
|
||||
|
||||
# Optionally run install script on remote
|
||||
read -q "REPLY?Run install script on remote? [y/N]: "
|
||||
echo
|
||||
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
|
||||
local ssh_cmd="ssh"
|
||||
[[ -n "$port" && "$port" != "22" ]] && ssh_cmd="$ssh_cmd -p $port"
|
||||
[[ -n "$key_file" ]] && ssh_cmd="$ssh_cmd -i $key_file"
|
||||
|
||||
eval "$ssh_cmd $connection 'cd .dotfiles && ./install.sh --skip-deps'"
|
||||
fi
|
||||
else
|
||||
_ssh_print_error "Failed to sync dotfiles"
|
||||
return 1
|
||||
@@ -435,26 +233,6 @@ alias sshd='ssh-delete'
|
||||
alias sshr='ssh-reconnect'
|
||||
alias sshsync='ssh-sync-dotfiles'
|
||||
|
||||
# ============================================================================
|
||||
# Completion Helper
|
||||
# ============================================================================
|
||||
|
||||
_ssh_manager_profiles() {
|
||||
local profiles=()
|
||||
while IFS='|' read -r name rest; do
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
profiles+=("$name")
|
||||
done < "$SSH_PROFILES_FILE" 2>/dev/null
|
||||
|
||||
echo "${profiles[@]}"
|
||||
}
|
||||
|
||||
# ZSH completion (if you want to add it)
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-connect
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-delete
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-edit
|
||||
|
||||
# ============================================================================
|
||||
# Initialization
|
||||
# ============================================================================
|
||||
|
||||
@@ -2,26 +2,16 @@
|
||||
# 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
|
||||
# ============================================================================
|
||||
|
||||
# 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'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
@@ -30,33 +20,14 @@ 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_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
|
||||
@@ -68,11 +39,7 @@ _tw_check_tmux() {
|
||||
|
||||
_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
|
||||
[[ ! -f "$TW_TEMPLATES_DIR/dev.tmux" ]] && _tw_create_default_templates
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -82,89 +49,44 @@ _tw_init_templates() {
|
||||
_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
|
||||
# Operations workspace - 4 panes
|
||||
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)
|
||||
# Full workspace - single pane
|
||||
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
|
||||
|
||||
@@ -178,42 +100,29 @@ EOF
|
||||
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 -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Available Tmux Templates ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_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}"
|
||||
echo -e "${DF_GREEN}●${DF_NC} ${DF_CYAN}$name${DF_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}"
|
||||
echo "Create workspace: ${DF_CYAN}tw-create myproject dev${DF_NC}"
|
||||
echo "Quick attach: ${DF_CYAN}tw myproject${DF_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
|
||||
|
||||
[[ -z "$template_name" ]] && { echo "Usage: tw-template-edit <template_name>"; tw-templates; return 1; }
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
|
||||
${EDITOR:-vim} "$template_file"
|
||||
|
||||
${EDITOR:-vim} "$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
_tw_print_success "Template edited: $template_name"
|
||||
}
|
||||
|
||||
@@ -225,26 +134,19 @@ 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
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-create <workspace_name> [template]"; tw-templates; return 1; }
|
||||
|
||||
_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"
|
||||
echo "Use: ${DF_CYAN}tw $workspace_name${DF_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"
|
||||
@@ -254,14 +156,10 @@ tw-create() {
|
||||
|
||||
_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"
|
||||
@@ -270,24 +168,17 @@ tw-create() {
|
||||
|
||||
_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}"
|
||||
_tw_print_info "Switch with: ${DF_CYAN}tmux switch-client -t $session_name${DF_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
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-attach <workspace_name>"; tw-list; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
@@ -295,12 +186,10 @@ tw-attach() {
|
||||
|
||||
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}"
|
||||
echo "Create it with: ${DF_CYAN}tw-create $workspace_name${DF_NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Attach or switch
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
@@ -311,48 +200,38 @@ tw-attach() {
|
||||
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 -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}"
|
||||
echo -e "${DF_BLUE}║${DF_NC} Active Tmux Workspaces ${DF_BLUE}║${DF_NC}"
|
||||
echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_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}"
|
||||
[[ "$current_session" == "$session_full" ]] && attached=" ${DF_GREEN}(current)${DF_NC}"
|
||||
fi
|
||||
|
||||
echo -e "${TW_GREEN}●${TW_NC} ${TW_CYAN}$workspace_name${TW_NC}$attached"
|
||||
echo -e "${DF_GREEN}●${DF_NC} ${DF_CYAN}$workspace_name${DF_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}"
|
||||
echo "Create one with: ${DF_CYAN}tw-create myproject${DF_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
|
||||
[[ -z "$workspace_name" ]] && { echo "Usage: tw-delete <workspace_name>"; tw-list; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
@@ -363,75 +242,45 @@ tw-delete() {
|
||||
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
|
||||
[[ -z "$template_name" ]] && { echo "Usage: tw-save <template_name>"; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
[[ -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"
|
||||
|
||||
if [[ -f "$template_file" ]]; then
|
||||
[[ -f "$template_file" ]] && {
|
||||
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
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
echo "split-window -v -p 50" >> "$template_file"
|
||||
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
|
||||
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"
|
||||
@@ -439,27 +288,19 @@ EOF
|
||||
|
||||
_tw_print_success "Template saved: $template_name"
|
||||
echo " File: $template_file"
|
||||
echo " Edit: ${TW_CYAN}tw-template-edit $template_name${TW_NC}"
|
||||
echo " Edit: ${DF_CYAN}tw-template-edit $template_name${DF_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
|
||||
[[ -z "$workspace_name" ]] && { tw-list; return 0; }
|
||||
|
||||
_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
|
||||
@@ -468,10 +309,6 @@ tw() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Fuzzy Search (requires fzf)
|
||||
# ============================================================================
|
||||
|
||||
twf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_tw_print_error "fzf not installed"
|
||||
@@ -480,7 +317,6 @@ twf() {
|
||||
|
||||
_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
|
||||
@@ -489,57 +325,33 @@ twf() {
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#sessions[@]} -eq 0 ]]; then
|
||||
_tw_print_info "No workspaces found"
|
||||
return 1
|
||||
fi
|
||||
[[ ${#sessions[@]} -eq 0 ]] && { _tw_print_info "No workspaces found"; return 1; }
|
||||
|
||||
# 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"')
|
||||
fzf --height=40% --layout=reverse --border=rounded --prompt='Workspace > ')
|
||||
|
||||
if [[ -n "$selection" ]]; then
|
||||
tw-attach "$selection"
|
||||
fi
|
||||
[[ -n "$selection" ]] && tw-attach "$selection"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Pane Synchronization Toggle
|
||||
# ============================================================================
|
||||
|
||||
tw-sync() {
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
[[ -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: ${TW_RED}OFF${TW_NC}"
|
||||
_tw_print_info "Pane synchronization: ${DF_RED}OFF${DF_NC}"
|
||||
else
|
||||
tmux set-window-option synchronize-panes on
|
||||
_tw_print_info "Pane synchronization: ${TW_GREEN}ON${TW_NC}"
|
||||
_tw_print_info "Pane synchronization: ${DF_GREEN}ON${DF_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
|
||||
[[ -z "$old_name" || -z "$new_name" ]] && { echo "Usage: tw-rename <old_name> <new_name>"; return 1; }
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
@@ -552,7 +364,6 @@ tw-rename() {
|
||||
fi
|
||||
|
||||
tmux rename-session -t "$old_session" "$new_session"
|
||||
|
||||
_tw_print_success "Renamed: $old_name → $new_name"
|
||||
}
|
||||
|
||||
|
||||
155
zsh/lib/colors.zsh
Normal file
155
zsh/lib/colors.zsh
Normal file
@@ -0,0 +1,155 @@
|
||||
# ============================================================================
|
||||
# Shared Color Definitions for Dotfiles
|
||||
# ============================================================================
|
||||
# Source this file in scripts and functions to get consistent color support.
|
||||
#
|
||||
# Usage in zsh functions:
|
||||
# source "${0:A:h}/../lib/colors.zsh" 2>/dev/null || source "$HOME/.dotfiles/zsh/lib/colors.zsh"
|
||||
#
|
||||
# Usage in bash scripts:
|
||||
# source "$HOME/.dotfiles/zsh/lib/colors.zsh"
|
||||
#
|
||||
# All variables are prefixed with DF_ (dotfiles) to avoid conflicts.
|
||||
# ============================================================================
|
||||
|
||||
# Prevent double-sourcing
|
||||
[[ -n "$_DF_COLORS_LOADED" ]] && return 0
|
||||
typeset -g _DF_COLORS_LOADED=1
|
||||
|
||||
# ============================================================================
|
||||
# Standard Colors (ANSI escape codes)
|
||||
# ============================================================================
|
||||
|
||||
typeset -g DF_RED=$'\033[0;31m'
|
||||
typeset -g DF_GREEN=$'\033[0;32m'
|
||||
typeset -g DF_YELLOW=$'\033[1;33m'
|
||||
typeset -g DF_BLUE=$'\033[0;34m'
|
||||
typeset -g DF_MAGENTA=$'\033[0;35m'
|
||||
typeset -g DF_CYAN=$'\033[0;36m'
|
||||
typeset -g DF_WHITE=$'\033[0;37m'
|
||||
|
||||
# Bold variants
|
||||
typeset -g DF_BOLD_RED=$'\033[1;31m'
|
||||
typeset -g DF_BOLD_GREEN=$'\033[1;32m'
|
||||
typeset -g DF_BOLD_YELLOW=$'\033[1;33m'
|
||||
typeset -g DF_BOLD_BLUE=$'\033[1;34m'
|
||||
typeset -g DF_BOLD_MAGENTA=$'\033[1;35m'
|
||||
typeset -g DF_BOLD_CYAN=$'\033[1;36m'
|
||||
typeset -g DF_BOLD_WHITE=$'\033[1;37m'
|
||||
|
||||
# Text styles
|
||||
typeset -g DF_BOLD=$'\033[1m'
|
||||
typeset -g DF_DIM=$'\033[2m'
|
||||
typeset -g DF_ITALIC=$'\033[3m'
|
||||
typeset -g DF_UNDERLINE=$'\033[4m'
|
||||
typeset -g DF_RESET=$'\033[0m'
|
||||
typeset -g DF_NC=$'\033[0m' # Alias for reset (No Color)
|
||||
|
||||
# ============================================================================
|
||||
# 256-Color Palette (used in theme and MOTD)
|
||||
# ============================================================================
|
||||
|
||||
typeset -g DF_GREY=$'\033[38;5;242m'
|
||||
typeset -g DF_LIGHT_GREY=$'\033[38;5;248m'
|
||||
typeset -g DF_DARK_GREY=$'\033[38;5;239m'
|
||||
typeset -g DF_ORANGE=$'\033[38;5;208m'
|
||||
typeset -g DF_LIGHT_ORANGE=$'\033[38;5;220m'
|
||||
typeset -g DF_PINK=$'\033[38;5;213m'
|
||||
typeset -g DF_PURPLE=$'\033[38;5;141m'
|
||||
typeset -g DF_LIGHT_BLUE=$'\033[38;5;39m'
|
||||
typeset -g DF_LIGHT_GREEN=$'\033[38;5;82m'
|
||||
typeset -g DF_BRIGHT_GREEN=$'\033[38;5;118m'
|
||||
typeset -g DF_TEAL=$'\033[38;5;51m'
|
||||
|
||||
# ============================================================================
|
||||
# Semantic Colors (for consistent UI)
|
||||
# ============================================================================
|
||||
|
||||
typeset -g DF_SUCCESS="$DF_GREEN"
|
||||
typeset -g DF_ERROR="$DF_RED"
|
||||
typeset -g DF_WARNING="$DF_YELLOW"
|
||||
typeset -g DF_INFO="$DF_CYAN"
|
||||
typeset -g DF_HINT="$DF_DIM"
|
||||
typeset -g DF_ACCENT="$DF_BLUE"
|
||||
typeset -g DF_MUTED="$DF_GREY"
|
||||
|
||||
# ============================================================================
|
||||
# Common Print Functions
|
||||
# ============================================================================
|
||||
|
||||
# Print a step/section header
|
||||
df_print_step() {
|
||||
echo -e "${DF_GREEN}==>${DF_NC} $1"
|
||||
}
|
||||
|
||||
# Print success message
|
||||
df_print_success() {
|
||||
echo -e "${DF_GREEN}✓${DF_NC} $1"
|
||||
}
|
||||
|
||||
# Print error message (to stderr)
|
||||
df_print_error() {
|
||||
echo -e "${DF_RED}✗${DF_NC} $1" >&2
|
||||
}
|
||||
|
||||
# Print warning message
|
||||
df_print_warning() {
|
||||
echo -e "${DF_YELLOW}⚠${DF_NC} $1"
|
||||
}
|
||||
|
||||
# Print info message
|
||||
df_print_info() {
|
||||
echo -e "${DF_CYAN}ℹ${DF_NC} $1"
|
||||
}
|
||||
|
||||
# Print a section divider
|
||||
df_print_section() {
|
||||
echo ""
|
||||
echo -e "${DF_BLUE}▶${DF_NC} $1"
|
||||
echo -e "${DF_CYAN}─────────────────────────────────────────────────────────────${DF_NC}"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MOTD-Style Header Function
|
||||
# ============================================================================
|
||||
|
||||
# Prints a standardized header box for scripts
|
||||
# Usage: df_print_header "script-name"
|
||||
df_print_header() {
|
||||
local script_name="${1:-script}"
|
||||
local user="${USER:-root}"
|
||||
local hostname="${HOST:-${HOSTNAME:-$(hostname -s 2>/dev/null)}}"
|
||||
local datetime=$(date '+%a %b %d %H:%M')
|
||||
local width=66
|
||||
|
||||
# Build horizontal line
|
||||
local hline=""
|
||||
for ((i=0; i<width; i++)); do hline+="═"; done
|
||||
local inner=$((width - 2))
|
||||
|
||||
# Header content
|
||||
local h_left="✦ ${user}@${hostname}"
|
||||
local h_center="${script_name}"
|
||||
local h_right="${datetime}"
|
||||
local h_pad=$(((inner - ${#h_left} - ${#h_center} - ${#h_right}) / 2))
|
||||
local h_spaces=""
|
||||
for ((i=0; i<h_pad; i++)); do h_spaces+=" "; done
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_GREY}╒${hline}╕${DF_NC}"
|
||||
echo -e "${DF_GREY}│${DF_NC} ${DF_BOLD}${DF_LIGHT_BLUE}${h_left}${DF_NC}${h_spaces}${DF_DIM}${h_center}${h_spaces}${h_right}${DF_NC} ${DF_GREY}│${DF_NC}"
|
||||
echo -e "${DF_GREY}╘${hline}╛${DF_NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Bash Compatibility
|
||||
# ============================================================================
|
||||
|
||||
# For bash scripts, export as regular variables too
|
||||
if [[ -n "$BASH_VERSION" ]]; then
|
||||
export DF_RED DF_GREEN DF_YELLOW DF_BLUE DF_MAGENTA DF_CYAN DF_WHITE
|
||||
export DF_BOLD DF_DIM DF_RESET DF_NC
|
||||
export DF_GREY DF_LIGHT_BLUE DF_LIGHT_GREEN
|
||||
export DF_SUCCESS DF_ERROR DF_WARNING DF_INFO
|
||||
fi
|
||||
Reference in New Issue
Block a user