dotfiles/zsh/.zshrc
2026-02-19 22:51:56 -07:00

305 lines
9.5 KiB
Bash

# Guard against re-sourcing (prevents slowdown on multiple sources)
# But allow aliases and exports to be reloaded when manually sourced
if [[ -n "$ZSHRC_LOADED" ]]; then
# If manually sourcing (not initial shell startup), just reload configs
[ -f ~/.aliases ] && source ~/.aliases
[ -f ~/.exports ] && source ~/.exports
return 0
fi
# Don't export - each shell instance should load fresh
ZSHRC_LOADED=1
# Import X11/Wayland display vars from systemd (for KDE Wayland)
# XAUTHORITY has a randomized filename each session, so pull from systemd
# if [ -z "$DISPLAY" ] || [ -z "$XAUTHORITY" ]; then
# eval $(systemctl --user show-environment | grep -E '^DISPLAY=|^XAUTHORITY=' 2>/dev/null)
# fi
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
# source ~/powerlevel10k/powerlevel10k.zsh-theme
# Instant prompt = Quiet
POWERLEVEL9K_INSTANT_PROMPT=quiet
if [[ -f "/opt/homebrew/bin/brew" ]]; then
# If you're using macOS, you'll want this enabled
eval "$(/opt/homebrew/bin/brew shellenv)"
fi
# Kill conflicting alias if it exists
unalias z 2>/dev/null
unalias zi 2>/dev/null
# Clone zcomet if necessary
if [[ ! -f ${ZDOTDIR:-${HOME}}/.zcomet/bin/zcomet.zsh ]]; then
command git clone https://github.com/agkozak/zcomet.git ${ZDOTDIR:-${HOME}}/.zcomet/bin
fi
source ${ZDOTDIR:-${HOME}}/.zcomet/bin/zcomet.zsh
# Load a code snippet - no need to download an entire repository
zcomet snippet https://github.com/jreese/zsh-titles/blob/master/titles.plugin.zsh
# Lazy-load Prezto's archive module without downloading all of Prezto's
# submodules
zcomet trigger --no-submodules archive unarchive lsarchive \
sorin-ionescu/prezto modules/archive
# Run compinit and compile its cache
zcomet compinit
# Load completions
# autoload -Uz compinit && compinit
# Themes and UI
zcomet load romkatv/powerlevel10k
# Completions and plugins
zcomet load zsh-users/zsh-syntax-highlighting
zcomet load zsh-users/zsh-completions
zcomet load zsh-users/zsh-autosuggestions
zcomet load Aloxaf/fzf-tab
# Oh My Zsh snippets
# zcomet load ohmyzsh/ohmyzsh lib/git.zsh
# zcomet load ohmyzsh/ohmyzsh plugins/git
# zcomet load ohmyzsh/ohmyzsh plugins/sudo
# zcomet load ohmyzsh/ohmyzsh plugins/archlinux
# zcomet load ohmyzsh/ohmyzsh plugins/aws
# zcomet load ohmyzsh/ohmyzsh plugins/kubectl
# zcomet load ohmyzsh/ohmyzsh plugins/kubectx
# zcomet load ohmyzsh/ohmyzsh plugins/command-not-found
# Keybindings
bindkey -e
bindkey '^p' history-search-backward
bindkey '^n' history-search-forward
bindkey '^[w' kill-region
bindkey "^[[1;5C" forward-word # Ctrl+Right
bindkey "^[[1;5D" backward-word # Ctrl+Left
# History
HISTSIZE=99999
HISTFILE=~/.zsh_history
SAVEHIST=$HISTSIZE
HISTDUP=erase
setopt appendhistory
# setopt sharehistory
setopt hist_ignore_space
setopt hist_ignore_all_dups
setopt hist_save_no_dups
setopt hist_ignore_dups
setopt hist_find_no_dups
# Completion styling
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*' menu no
# Enhanced fzf-tab completions with better previews
zstyle ':fzf-tab:complete:cd:*' fzf-preview 'eza -la --color=always $realpath 2>/dev/null || ls --color=always $realpath'
zstyle ':fzf-tab:complete:__zoxide_z:*' fzf-preview 'eza -la --color=always $realpath 2>/dev/null || ls --color=always $realpath'
# Preview for other common commands
zstyle ':fzf-tab:complete:cat:*' fzf-preview 'bat --color=always $realpath 2>/dev/null || cat $realpath'
zstyle ':fzf-tab:complete:bat:*' fzf-preview 'bat --color=always $realpath'
zstyle ':fzf-tab:complete:less:*' fzf-preview 'bat --color=always $realpath 2>/dev/null || cat $realpath'
zstyle ':fzf-tab:complete:more:*' fzf-preview 'bat --color=always $realpath 2>/dev/null || cat $realpath'
# Shell integrations
# eval "$(fzf --zsh)"
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
eval "$(zoxide init zsh)"
# Atuin setup - smart history search
eval "$(atuin init zsh --disable-up-arrow)"
# Sourcing
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
[ -f ~/.aliases ] && source ~/.aliases
[ -f ~/.exports ] && source ~/.exports
# source /usr/share/doc/pkgfile/command-not-found.zsh
# source /usr/lib/command-not-found
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
# __conda_setup="$('/home/e/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
# if [ $? -eq 0 ]; then
# eval "$__conda_setup"
# else
# if [ -f "/home/e/miniconda3/etc/profile.d/conda.sh" ]; then
# . "/home/e/miniconda3/etc/profile.d/conda.sh"
# else
# export PATH="/home/e/miniconda3/bin:$PATH"
# fi
# fi
# unset __conda_setup
# <<< conda initialize <<<
# NVM Lazy Loading (saves ~400ms on shell startup)
# Instead of loading NVM immediately, we create placeholder functions
# that load NVM only when first used
export NVM_DIR="$HOME/.nvm"
# Add NVM's default Node to PATH manually (fast)
if [ -d "$NVM_DIR/versions/node" ]; then
# Get the default/current Node version
NVM_DEFAULT_NODE=$(find "$NVM_DIR/versions/node" -maxdepth 1 -type d | sort -V | tail -1)
if [ -n "$NVM_DEFAULT_NODE" ]; then
export PATH="$NVM_DEFAULT_NODE/bin:$PATH"
fi
fi
# Lazy load function - loads NVM on first use
lazy_load_nvm() {
unset -f nvm node npm npx
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
}
# Create placeholder functions that trigger lazy loading
nvm() {
lazy_load_nvm
nvm "$@"
}
node() {
# If node is in PATH (from our manual add above), use it directly
if command -v node >/dev/null 2>&1; then
command node "$@"
else
lazy_load_nvm
node "$@"
fi
}
npm() {
if command -v npm >/dev/null 2>&1; then
command npm "$@"
else
lazy_load_nvm
npm "$@"
fi
}
npx() {
if command -v npx >/dev/null 2>&1; then
command npx "$@"
else
lazy_load_nvm
npx "$@"
fi
}
# Generated for pdtm. Do not edit.
export PATH=$PATH:/home/e/.pdtm/go/bin
# opencode
export PATH=/home/e/.opencode/bin:$PATH
# bun completions
[ -s "/home/e/.bun/_bun" ] && source "/home/e/.bun/_bun"
# bun
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
# Removed redundant .local/bin addition (already in ~/.exports)
# SSH Agent for persistent tunnel (Baserow, etc.)
# TEMPORARILY DISABLED - Testing if this breaks session during login
# export SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket
# Reminder to add SSH key after reboot
# TEMPORARILY DISABLED - Testing if this breaks session during login
# if ! ssh-add -l &>/dev/null; then
# echo "🔑 Reminder: Add your Vultr SSH key for blog automation:"
# echo " ssh-add ~/.ssh/id_ed25519_vultr"
# fi
# Deduplicate PATH at the end (after all PATH modifications)
export PATH=$(echo "$PATH" | tr ':' '\n' | awk '!seen[$0]++' | paste -sd:)
# Make PATH available to GUI applications (Ulauncher, etc.)
# This ensures apps launched outside terminal sessions can find bun, custom bins, etc.
# TEMPORARILY DISABLED - Testing if this breaks systemd user session during login
# systemctl --user import-environment PATH
[ -f ~/.env ] && source ~/.env
# Port reference quick access
# VPN Switcher - alias to private script (not in git)
alias vpn-switch='/home/e/.claude/context/personal/scripts/vpn-switch'
# GPG-based SSH authentication
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
# Foundry (Ethereum development toolkit)
export PATH="$HOME/.foundry/bin:$PATH"
# SSH key status check - run `ssh-keys` to see what's loaded
ssh-keys() {
local -A KEYS=(
[x1]="$HOME/.ssh/id_ed25519_x1"
[forge]="$HOME/.ssh/id_ed25519"
[contabo]="$HOME/.ssh/vps2"
[vps3]="$HOME/.ssh/id_ed25519"
)
local loaded=$(ssh-add -l 2>/dev/null)
echo "SSH Keys:"
for host in x1 forge contabo vps3; do
local keyfile="${KEYS[$host]}"
local fp=$(ssh-keygen -lf "$keyfile" 2>/dev/null | awk '{print $2}')
if echo "$loaded" | grep -q "$fp" 2>/dev/null; then
echo " $host ✓ loaded"
else
echo " $host ✗ ssh-add $keyfile"
fi
done
}
# Show missing SSH keys on new shell (quiet if all loaded)
_ssh_key_check() {
local loaded=$(ssh-add -l 2>/dev/null)
local missing=()
local -A KEYS=(
[x1]="$HOME/.ssh/id_ed25519_x1"
[forge/vps3]="$HOME/.ssh/id_ed25519"
[contabo]="$HOME/.ssh/vps2"
)
for label in x1 "forge/vps3" contabo; do
local keyfile="${KEYS[$label]}"
local fp=$(ssh-keygen -lf "$keyfile" 2>/dev/null | awk '{print $2}')
if ! echo "$loaded" | grep -q "$fp" 2>/dev/null; then
missing+=("$label → ssh-add $keyfile")
fi
done
if (( ${#missing[@]} > 0 )); then
echo "🔑 Missing SSH keys:"
for m in "${missing[@]}"; do
echo " $m"
done
fi
}
_ssh_key_check
# Post-reboot: remind to refresh security baseline
_reboot_baseline_check() {
local stamp="$HOME/.cache/.last-baseline-boot-id"
local current_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)
local last_boot=$(cat "$stamp" 2>/dev/null)
if [[ "$current_boot" != "$last_boot" ]]; then
echo "🛡️ New boot detected — security baseline may be stale"
echo " Run: ~/.claude-user/scripts/upgrade-pai.sh --manifest"
mkdir -p "$(dirname "$stamp")"
echo "$current_boot" > "$stamp"
fi
}
_reboot_baseline_check