summaryrefslogtreecommitdiff
path: root/dotfiles
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-26 16:57:37 -0600
committerCraig Jennings <c@cjennings.net>2026-01-26 16:57:37 -0600
commit7d490453085ae084ce0e3875952eae1d3ad7b1ab (patch)
treed6f76ded02d541107271dc2b04cac34435c81a26 /dotfiles
parentfeb8dfaae9b0172c9d24e7e0d115754a467b4627 (diff)
refactor(shell): reorganize shell config for proper separation
Restructure shell configuration to follow standard conventions: - .profile: Environment variables only (POSIX compatible) - .bash_profile: NEW - sources .profile and .bashrc for login shells - .bashrc: Bash-specific settings, sources .bashrc.d/ - .zshrc: Zsh-specific settings, sources .zshrc.d/ New modular directories: - .bashrc.d/: aliases, emacs, fzf, git, media, utilities - .zshrc.d/: same as bashrc.d plus arch-downgrade (zsh-only) - .profile.d/: reduced to env-only files (display, framework, auto-tmux) Fixes: - Remove duplicate .profile sourcing in .bashrc - Remove broken XDG_CURRENT_DESKTOP=GNOME line from display.sh - Move aliases/functions from .profile to appropriate .d/ directories - Shell-specific init (zoxide, fzf) now in .bashrc/.zshrc directly - FreeBSD bindkey fix now in .zshrc directly Also adds CLAUDE.md session context file. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'dotfiles')
-rw-r--r--dotfiles/system/.bash_profile11
-rw-r--r--dotfiles/system/.bashrc99
-rw-r--r--dotfiles/system/.bashrc.d/aliases.sh91
-rw-r--r--dotfiles/system/.bashrc.d/emacs.sh24
-rw-r--r--dotfiles/system/.bashrc.d/fzf.sh122
-rw-r--r--dotfiles/system/.bashrc.d/git.sh32
-rw-r--r--dotfiles/system/.bashrc.d/media.sh (renamed from dotfiles/system/.profile.d/media.sh)42
-rw-r--r--dotfiles/system/.bashrc.d/utilities.sh206
-rw-r--r--dotfiles/system/.profile160
-rwxr-xr-xdotfiles/system/.profile.d/arch-linux-downgrade.sh50
-rw-r--r--dotfiles/system/.profile.d/auto-tmux-session.sh12
-rw-r--r--dotfiles/system/.profile.d/chronographic.sh120
-rw-r--r--dotfiles/system/.profile.d/compress.sh75
-rw-r--r--dotfiles/system/.profile.d/dd.sh19
-rw-r--r--dotfiles/system/.profile.d/display.sh10
-rw-r--r--dotfiles/system/.profile.d/emacs.sh33
-rw-r--r--dotfiles/system/.profile.d/extract.sh27
-rw-r--r--dotfiles/system/.profile.d/framework.sh11
-rw-r--r--dotfiles/system/.profile.d/freebsd.sh10
-rw-r--r--dotfiles/system/.profile.d/fzf.sh123
-rw-r--r--dotfiles/system/.profile.d/git.sh24
-rwxr-xr-xdotfiles/system/.profile.d/zoxide.sh11
-rw-r--r--dotfiles/system/.zshrc259
-rw-r--r--dotfiles/system/.zshrc.d/aliases.sh91
-rw-r--r--dotfiles/system/.zshrc.d/arch-downgrade.sh47
-rw-r--r--dotfiles/system/.zshrc.d/emacs.sh24
-rw-r--r--dotfiles/system/.zshrc.d/fzf.sh122
-rw-r--r--dotfiles/system/.zshrc.d/git.sh32
-rw-r--r--dotfiles/system/.zshrc.d/media.sh41
-rw-r--r--dotfiles/system/.zshrc.d/utilities.sh206
30 files changed, 1332 insertions, 802 deletions
diff --git a/dotfiles/system/.bash_profile b/dotfiles/system/.bash_profile
new file mode 100644
index 0000000..b910a02
--- /dev/null
+++ b/dotfiles/system/.bash_profile
@@ -0,0 +1,11 @@
+# .bash_profile
+# Craig Jennings <c@cjennings.net>
+#
+# Sourced by bash login shells. Sources .profile for env vars
+# and .bashrc for interactive settings.
+
+# Environment variables
+[ -f "$HOME/.profile" ] && . "$HOME/.profile"
+
+# Interactive shell settings (aliases, functions, prompt, etc.)
+[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
diff --git a/dotfiles/system/.bashrc b/dotfiles/system/.bashrc
index b5290cd..b2177d9 100644
--- a/dotfiles/system/.bashrc
+++ b/dotfiles/system/.bashrc
@@ -1,59 +1,76 @@
#!/bin/bash
-# cjennings .bashrc
-
-# tells shellcheck not to follow references to other files
-# shellcheck source=/dev/null
+# .bashrc
+# Craig Jennings <c@cjennings.net>
+#
+# Bash-specific interactive shell settings.
+# Aliases, functions, prompt, completions, shell options.
# If not running interactively, don't do anything
case $- in
- *i*) ;;
- *) return;;
+ *i*) ;;
+ *) return;;
esac
-# env variables, aliases, and functions that are not bash specific
-source "$HOME"/.profile
-
-# don't put duplicate lines or lines starting with space in the history.
-HISTCONTROL=ignoreboth
-
-# infinite history
-HISTSIZE=HISTFILESIZE=
-
-# append and reload the history after each command
-PROMPT_COMMAND="history -a; history -n"
+# =============================================================================
+# Environment Variables (from .profile)
+# =============================================================================
+# Source .profile for env vars (needed for non-login interactive shells)
+[ -f "$HOME/.profile" ] && . "$HOME/.profile"
-# ignore the following commands from the history
-HISTIGNORE="ls:ll:cd:pwd:bg:fg:history"
+# =============================================================================
+# Shell Options
+# =============================================================================
+shopt -s histappend # append to history file, don't overwrite
+shopt -s checkwinsize # update LINES and COLUMNS after each command
+shopt -s autocd # cd to directory by typing its name
+shopt -s cdspell # correct minor spelling errors in cd
+shopt -s dirspell # correct spelling errors during tab-completion
-# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+# =============================================================================
+# History
+# =============================================================================
+HISTCONTROL=ignoreboth # ignore duplicates and lines starting with space
HISTSIZE=100000
HISTFILESIZE=10000000
+HISTIGNORE="ls:ll:cd:pwd:bg:fg:history:exit"
+PROMPT_COMMAND="history -a; history -n" # append and reload after each command
-# append to the history file, don't overwrite it
-shopt -s histappend
+# =============================================================================
+# Prompt
+# =============================================================================
+PS1='[\d, \t] \u@\H:\w \n$ '
-# cd to directory by typing its name
-shopt -s autocd
-
-# check window size after each command + update LINES and COLUMNS values.
-shopt -s checkwinsize
-
-# enable programmable completion features
+# =============================================================================
+# Completions
+# =============================================================================
if ! shopt -oq posix; then
- if [ -f /usr/share/bash-completion/bash_completion ]; then
- . /usr/share/bash-completion/bash_completion
- elif [ -f /etc/bash_completion ]; then
- . /etc/bash_completion
- fi
+ if [ -f /usr/share/bash-completion/bash_completion ]; then
+ . /usr/share/bash-completion/bash_completion
+ elif [ -f /etc/bash_completion ]; then
+ . /etc/bash_completion
+ fi
fi
-if [ -f /etc/bash_completion ]; then
- . /etc/bash_completion
+
+# =============================================================================
+# Source modular bash configs from .bashrc.d/
+# =============================================================================
+if [ -d "$HOME/.bashrc.d" ]; then
+ for file in "$HOME/.bashrc.d"/*.sh; do
+ [ -r "$file" ] && . "$file"
+ done
+ unset file
fi
-export PS1="[\d, \t] \u@\H:\w \n$ "
+# =============================================================================
+# Tool-specific initialization
+# =============================================================================
+# FZF
+[ -f "$HOME/.fzf.bash" ] && . "$HOME/.fzf.bash"
-source "$HOME"/.profile
+# Deno
+[ -f "$HOME/.deno/env" ] && . "$HOME/.deno/env"
+[ -f "$HOME/.local/share/bash-completion/completions/deno.bash" ] && \
+ . "$HOME/.local/share/bash-completion/completions/deno.bash"
-[ -f "$HOME"/.fzf.bash ] && source "$HOME"/.fzf.bash
-. "/home/cjennings/.deno/env"
-source /home/cjennings/.local/share/bash-completion/completions/deno.bash \ No newline at end of file
+# Zoxide (smart cd)
+command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init bash)"
diff --git a/dotfiles/system/.bashrc.d/aliases.sh b/dotfiles/system/.bashrc.d/aliases.sh
new file mode 100644
index 0000000..28c0f3f
--- /dev/null
+++ b/dotfiles/system/.bashrc.d/aliases.sh
@@ -0,0 +1,91 @@
+# aliases.sh
+# Craig Jennings <c@cjennings.net>
+# Shell aliases - works in both bash and zsh
+
+# =============================================================================
+# Directory Navigation
+# =============================================================================
+alias cdot="cd ~/code/archsetup/dotfiles"
+alias cdpf="cd ~/projects/finances/"
+alias cdpj="cd ~/projects/jr-estate/"
+alias cdpd="cd ~/projects/documents/"
+
+# =============================================================================
+# File Listing (exa)
+# =============================================================================
+alias ls="exa --group-directories-first"
+alias l="exa -lhF --group-directories-first"
+alias ll="exa -lhAF --group-directories-first"
+alias lt="exa -lthAF --group-directories-first"
+
+# =============================================================================
+# File Operations
+# =============================================================================
+alias mkd="mkdir -pv"
+alias open="xdg-open"
+alias linkdel="find . -type l ! -exec test -d {} \; -delete"
+alias linkfind="find . -type l ! -exec test -d {} \; -print"
+
+# =============================================================================
+# System Administration
+# =============================================================================
+alias df='dfc -p /dev/'
+alias ducks='du -cksh * | sort -rh | head -n11'
+alias ntop="sudo bandwhich"
+alias ptop="sudo powertop"
+alias running_services='systemctl list-units --type=service --state=running'
+alias ssn="sudo shutdown now"
+alias boot2bios="sudo systemctl reboot --firmware-setup"
+alias backup='sudo rsyncshot backup 1000'
+alias sysinfo='sudo inxi -v 8 -a -xxxA -xxxB -xxxC -xxxD -xxxG -xxxI -xxxm -xxxN -xxxR -xxxS -xxx --usb -d -I -pl -n -s --slots'
+alias timeshift='sudo timeshift-gtk'
+alias sysupgrade="topgrade"
+
+# =============================================================================
+# Network
+# =============================================================================
+alias myip='curl -4 https://chroot-me.in/ip/ 2>/dev/null || w3m -4 -dump https://chroot-me.in/ip'
+alias whereami="curl ipinfo.io"
+alias speedtest="speedtest-go"
+
+# =============================================================================
+# Applications
+# =============================================================================
+alias vim="nvim"
+alias et="emacs -nw"
+alias weather="wego"
+alias crm="tickrs -s CRM"
+alias handbrake="ghb"
+alias smerge="/usr/bin/smerge"
+alias stext="/opt/sublime_text/sublime_text"
+alias steam="flatpak run com.valvesoftware.Steam"
+alias xterm="xterm -ti 340"
+
+# =============================================================================
+# Stow (dotfiles management)
+# =============================================================================
+alias stow="stow --target=/home/cjennings"
+
+# =============================================================================
+# Ranger (file manager)
+# =============================================================================
+alias cdr='. ranger'
+alias r='. ranger'
+
+# =============================================================================
+# Programming
+# =============================================================================
+alias cc="gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversion -Wextra -std=c2x -pedantic"
+alias gdbx="gdb --batch --ex r --ex bt --ex q --args"
+
+# =============================================================================
+# Claude Code
+# =============================================================================
+alias hey='claude "Read ./docs/protocols.org and ./docs/NOTES.org, follow their instructions, then run session startup workflow."'
+
+# =============================================================================
+# Phenomenology RAG (ollama/deepseek)
+# =============================================================================
+phenom() {
+ aichat --rag phenom -m ollama:deepseek-r1:70b "$@"
+}
diff --git a/dotfiles/system/.bashrc.d/emacs.sh b/dotfiles/system/.bashrc.d/emacs.sh
new file mode 100644
index 0000000..0a8444b
--- /dev/null
+++ b/dotfiles/system/.bashrc.d/emacs.sh
@@ -0,0 +1,24 @@
+# emacs.sh
+# Craig Jennings <c@cjennings.net>
+# Emacs-specific settings and functions
+
+# GTK/Emacs accessibility bug workaround
+# https://unix.stackexchange.com/questions/230238/
+export NO_AT_BRIDGE=1
+
+# Wake emacs from elisp freeze
+alias emacswake='for i in $(seq 1 500); do killall -s USR2 emacs; done'
+
+# Vterm shell integration
+# Allows shell to send information to vterm via escape sequences
+vterm_printf() {
+ if [ -n "$TMUX" ] && { [ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ]; }; then
+ # Tell tmux to pass the escape sequences through
+ printf "\ePtmux;\e\e]%s\007\e\\" "$1"
+ elif [ "${TERM%%-*}" = "screen" ]; then
+ # GNU screen
+ printf "\eP\e]%s\007\e\\" "$1"
+ else
+ printf "\e]%s\e\\" "$1"
+ fi
+}
diff --git a/dotfiles/system/.bashrc.d/fzf.sh b/dotfiles/system/.bashrc.d/fzf.sh
new file mode 100644
index 0000000..9a5a9bd
--- /dev/null
+++ b/dotfiles/system/.bashrc.d/fzf.sh
@@ -0,0 +1,122 @@
+# fzf.sh
+# Craig Jennings <c@cjennings.net>
+# FZF settings and utility functions
+
+# =============================================================================
+# Settings
+# =============================================================================
+export FZF_DEFAULT_OPTS='--height=70%'
+export FZF_DEFAULT_COMMAND='rg --files --no-ignore-vcs --hidden'
+export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
+
+# Directory paths for convenience functions
+IF_GAMES_DIR="$HOME/sync/org/text.games"
+BOOKS_DIR="$HOME/sync/books"
+
+# =============================================================================
+# Navigation
+# =============================================================================
+
+# cdff - change directory to where a file resides
+cdff() {
+ file=$(fzf +m -q "$1")
+ dir=$(dirname "$file")
+ cd "$dir" || return 1
+}
+
+# cdd - cd to directory with fzf
+cdd() {
+ destdir=$(find "${1:-.}" -path '*/\.*' -prune \
+ -o -type d -print 2>/dev/null | fzf +m) &&
+ cd "$destdir"
+}
+
+# =============================================================================
+# System Admin
+# =============================================================================
+
+# Kill process with fzf
+kp() {
+ pid=$(ps -ef | sed 1d | eval "fzf ${FZF_DEFAULT_OPTS} -m --header='[kill:process]'" | awk '{print $2}')
+ if [ -n "$pid" ]; then
+ echo "$pid" | xargs kill -"${1:-9}"
+ kp
+ fi
+}
+
+# Install packages with fzf preview
+yinstall() {
+ yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm
+}
+
+yinstall-skipverify() {
+ yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm --mflags --skipinteg
+}
+
+# Remove packages with fzf preview
+yrm() {
+ yay -Qq | fzf --multi --preview 'yay -Qi {1}' | xargs -ro yay -Rns
+}
+
+# Find in file with fzf
+fif() {
+ if [ "$#" -eq 0 ]; then
+ echo "Need a string to search for!"
+ return 1
+ fi
+ rg --files-with-matches --no-messages "$1" | \
+ fzf --preview "highlight -O ansi -l {} 2>/dev/null | \
+ rg --colors 'match:bg:yellow' --ignore-case --pretty --context 10 '$1' || \
+ rg --ignore-case --pretty --context 10 '$1' {}"
+}
+
+# =============================================================================
+# Convenience
+# =============================================================================
+
+# Find and read epub book in terminal
+bk() {
+ bkfile=$(find "$BOOKS_DIR" -iname "*.epub" -print | fzf)
+ if [ -n "$bkfile" ]; then
+ epr "$bkfile"
+ fi
+}
+
+# Find and play interactive fiction game
+tg() {
+ gamefile=$(find "$IF_GAMES_DIR" -type f \( -iname "*.z[1-8]" -o -iname "*.zblorb" -o -iname "*.blorb" \) -print | fzf)
+ if [ -n "$gamefile" ]; then
+ frotz "$gamefile"
+ fi
+}
+
+# =============================================================================
+# WireGuard
+# =============================================================================
+
+wgup() {
+ # Shutdown existing connections first
+ output=$(sudo wg 2>/dev/null)
+ if [ -n "$output" ]; then
+ for iface in $(sudo wg show interfaces); do
+ sudo wg-quick down "${iface}"
+ done
+ fi
+ # Select and start new connection
+ wgfile=$(sudo find /etc/wireguard/ -iname "*.conf" -exec basename -s .conf {} \; | fzf)
+ if [ -n "$wgfile" ]; then
+ sudo wg-quick up "$wgfile"
+ sudo wg
+ fi
+}
+
+wgdown() {
+ output=$(sudo wg 2>/dev/null)
+ if [ -n "$output" ]; then
+ for iface in $(sudo wg show interfaces); do
+ sudo wg-quick down "${iface}"
+ done
+ fi
+}
+
+alias wg=wgup
diff --git a/dotfiles/system/.bashrc.d/git.sh b/dotfiles/system/.bashrc.d/git.sh
new file mode 100644
index 0000000..6c2b6ad
--- /dev/null
+++ b/dotfiles/system/.bashrc.d/git.sh
@@ -0,0 +1,32 @@
+# git.sh
+# Craig Jennings <c@cjennings.net>
+# Git aliases and convenience functions
+
+# =============================================================================
+# Aliases
+# =============================================================================
+alias gitlog="git log --graph --pretty=format:'%Cred%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relative"
+alias gitstatus='git status -sb'
+alias gitcom='git commit -m'
+alias gitpp='git pull --prune'
+alias gittagbydate="git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags"
+
+# =============================================================================
+# Functions
+# =============================================================================
+
+# Stash, pull, pop
+gitsp() {
+ git stash && git pull && git stash pop
+}
+
+# Checkout branch with fzf
+gitck() {
+ git checkout "$(git branch --all | fzf | tr -d '[:space:]')"
+}
+
+# Diff with fzf preview
+gitdiff() {
+ preview="git diff $@ --color=always -- {-1}"
+ git diff "$@" --name-only | fzf -m --ansi --preview "$preview"
+}
diff --git a/dotfiles/system/.profile.d/media.sh b/dotfiles/system/.bashrc.d/media.sh
index 3add213..92fe2ce 100644
--- a/dotfiles/system/.profile.d/media.sh
+++ b/dotfiles/system/.bashrc.d/media.sh
@@ -1,41 +1,41 @@
-#!/bin/sh
-
# media.sh
# Craig Jennings <c@cjennings.net>
-# utilities for working with media (music, video, books, etc.)
-
+# Media utilities - music, video, downloads
-### TERMINAL MPV ALIASES
+# =============================================================================
+# Terminal MPV
+# =============================================================================
alias play='mpv --no-video'
-alias shuffle='mpv --no-video --shuffle '
-
+alias shuffle='mpv --no-video --shuffle'
-### YOUTUBE AND TIDAL
-# Leverage task spooler to download url targets serially in the background.
-# example : % stdl "https://youtu.be/gv-3Y7CcUUo"
-# note that zshell urls must be quoted; not so for bash.
-
-# tidal-dl
+# =============================================================================
+# Tidal
+# =============================================================================
alias tdl="tidal-dl -l"
alias ttdl="tsp tidal-dl -l"
-# youtube-dl
+# =============================================================================
+# YouTube (yt-dlp)
+# =============================================================================
+# Video - single
alias yt="yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'"
alias tyt="tsp yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'"
+# Video - playlist
alias ytp="yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'"
alias tytp="tsp yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'"
+# Audio - single
alias yta="yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'"
alias tyta="tsp yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'"
+# Audio - playlist
alias ytap="yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'"
alias tytap="tsp yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'"
-
-### SOUND FILE CONVERSION
-# converts audible aax to other formats. Requires AAXtoMP3 script in dotfiles.
-
-alias aax2flac='AAXtoMP3 -f '
-alias aax2mp3='AAXtoMP3 -c -e:mp3 '
-alias aax2opus='AAXtoMP3 --opus -t . -c '
+# =============================================================================
+# Audible Conversion (AAXtoMP3)
+# =============================================================================
+alias aax2flac='AAXtoMP3 -f'
+alias aax2mp3='AAXtoMP3 -c -e:mp3'
+alias aax2opus='AAXtoMP3 --opus -t . -c'
diff --git a/dotfiles/system/.bashrc.d/utilities.sh b/dotfiles/system/.bashrc.d/utilities.sh
new file mode 100644
index 0000000..431cac0
--- /dev/null
+++ b/dotfiles/system/.bashrc.d/utilities.sh
@@ -0,0 +1,206 @@
+# utilities.sh
+# Craig Jennings <c@cjennings.net>
+# General utility functions
+
+# =============================================================================
+# Archive Extraction
+# =============================================================================
+extract() {
+ if [ -f "$1" ]; then
+ case "$1" in
+ *.tar.bz2) tar xjf "$1" ;;
+ *.tar.gz) tar xzf "$1" ;;
+ *.bz2) bunzip2 "$1" ;;
+ *.rar) rar x "$1" ;;
+ *.gz) gunzip "$1" ;;
+ *.tar) tar xf "$1" ;;
+ *.tbz2) tar xjf "$1" ;;
+ *.tgz) tar xzf "$1" ;;
+ *.zip) unzip "$1" ;;
+ *.Z) uncompress "$1" ;;
+ *) echo "$1 cannot be extracted via extract()" ;;
+ esac
+ else
+ echo "$1 is not a valid file"
+ fi
+}
+
+# =============================================================================
+# Archive Compression
+# =============================================================================
+compress() {
+ if [ $# -ne 2 ]; then
+ echo "Usage: compress <format> <file_or_directory>"
+ echo "Formats: tar.bz2, tar.gz, bz2, tar, tbz2, tgz, zip, gz, Z"
+ return 1
+ fi
+
+ format="$1"
+ target="$2"
+
+ if [ ! -e "$target" ]; then
+ echo "Error: '$target' does not exist"
+ return 1
+ fi
+
+ basename=$(basename "$target")
+
+ case "$format" in
+ tar.bz2|tbz2) output="${basename}.tar.bz2" ;;
+ tar.gz|tgz) output="${basename}.tar.gz" ;;
+ bz2) output="${target}.bz2" ;;
+ gz) output="${target}.gz" ;;
+ tar) output="${basename}.tar" ;;
+ zip) output="${basename}.zip" ;;
+ Z) output="${target}.Z" ;;
+ *)
+ echo "Error: Unknown format '$format'"
+ return 1
+ ;;
+ esac
+
+ if [ -e "$output" ]; then
+ printf "Warning: '%s' already exists. Overwrite? (y/N): " "$output"
+ read -r response
+ case "$response" in
+ [yY]|[yY][eE][sS]) rm -f "$output" ;;
+ *) echo "Aborted." && return 1 ;;
+ esac
+ fi
+
+ case "$format" in
+ tar.bz2|tbz2) tar -cjf "$output" "$target" ;;
+ tar.gz|tgz) tar -czf "$output" "$target" ;;
+ bz2)
+ [ -d "$target" ] && echo "Error: bz2 only works on files" && return 1
+ bzip2 -k "$target"
+ ;;
+ gz)
+ [ -d "$target" ] && echo "Error: gz only works on files" && return 1
+ gzip -k "$target"
+ ;;
+ tar) tar -cf "$output" "$target" ;;
+ zip)
+ [ -d "$target" ] && zip -r "$output" "$target" || zip "$output" "$target"
+ ;;
+ Z)
+ [ -d "$target" ] && echo "Error: Z only works on files" && return 1
+ command compress -c "$target" > "$output"
+ ;;
+ esac
+
+ [ $? -eq 0 ] && echo "Created $output" || echo "Compression failed"
+}
+
+# =============================================================================
+# DD Helper
+# =============================================================================
+dd_with_bs() {
+ OUT_DIR=$(dirname "$2")
+ if [ ! -e "$1" ] || [ ! -e "$OUT_DIR" ]; then
+ echo "$1 or $OUT_DIR doesn't exist"
+ return 1
+ fi
+ IN_BS=$(stat -c "%o" "$1")
+ OUT_BS=$(stat -c "%o" "$OUT_DIR")
+ echo dd \"if=$1\" \"of=$2\" \"ibs=$IN_BS\" \"obs=$OUT_BS\"
+}
+
+# =============================================================================
+# Clock, Timer, Alarm, Stopwatch
+# =============================================================================
+export BEEP="/usr/share/sounds/freedesktop/stereo/bell.oga"
+alias beep='paplay $BEEP'
+
+clock() {
+ while true; do
+ tput clear
+ echo ""
+ date +" %l : %M %p" | figlet -f roman -w 200
+ sleep 1
+ done
+}
+
+timer() {
+ if [ "$#" -lt 2 ]; then
+ echo "Pass the time and a notification. Example: timer 1h30m Parking expiring"
+ return 1
+ fi
+ message="${*:2}"
+ start_time=$(date +"%H:%M:%S %p")
+ printf "\nStarting %s timer at %s\n" "$1" "$start_time"
+ snore "$1" && paplay "$BEEP" && notify-send "Timer" "$message"
+}
+
+alarm() {
+ if [ "$#" -lt 2 ]; then
+ echo "Pass both the time and a message. Example: alarm 1:45pm Time to eat!"
+ return 1
+ fi
+
+ if ! date -d "$1" >/dev/null 2>&1; then
+ echo "Invalid time: $1"
+ return 1
+ fi
+
+ echo "paplay \$BEEP && notify-send \"Alarm\" \"$*\"" | at "$1" >/dev/null 2>&1
+ echo ""
+ echo "Alarm '${*:2}' is queued for $1."
+ echo "To see all alarms: atq"
+ echo "To remove an alarm: atrm <number>"
+ echo ""
+}
+
+# Stopwatch
+sw_start_time=0
+sw_started=0
+
+swreset() {
+ sw_start_time=0
+ sw_started=0
+ echo "Stopwatch reset"
+}
+
+swshow() {
+ if [ "$sw_started" = 0 ]; then
+ echo "Error: Stopwatch not started" >&2
+ return 1
+ fi
+
+ current_time=$(date +%s)
+ elapsed_time=$((current_time - sw_start_time))
+
+ if [ "$elapsed_time" -lt 60 ]; then
+ echo "Elapsed time: $elapsed_time seconds"
+ elif [ "$elapsed_time" -lt 3600 ]; then
+ minutes=$((elapsed_time / 60))
+ seconds=$((elapsed_time % 60))
+ echo "Elapsed time: $minutes minutes, $seconds seconds"
+ else
+ hours=$((elapsed_time / 3600))
+ minutes=$(((elapsed_time / 60) % 60))
+ seconds=$((elapsed_time % 60))
+ echo "Elapsed time: $hours hours, $minutes minutes, $seconds seconds"
+ fi
+}
+
+swstop() {
+ swshow
+ swreset
+}
+
+swstart() {
+ if [ "$sw_started" = 1 ]; then
+ printf "Stopwatch is already started. Reset? (y/n): "
+ read -r answer
+ case "$answer" in
+ [yY]) swreset ;;
+ [nN]) echo "Stopwatch not reset." && swshow && return ;;
+ *) echo "Error: Invalid input." >&2 && return 1 ;;
+ esac
+ fi
+
+ sw_started=1
+ sw_start_time=$(date +%s)
+ printf "Stopwatch started at %s\n\n" "$(date +"%H:%M:%S %p")"
+}
diff --git a/dotfiles/system/.profile b/dotfiles/system/.profile
index 8e7653c..b4da79d 100644
--- a/dotfiles/system/.profile
+++ b/dotfiles/system/.profile
@@ -1,137 +1,81 @@
# .profile
# Craig Jennings <c@cjennings.net>
+#
+# Environment variables only. POSIX sh compatible.
+# Sourced by login shells. Aliases and functions go in .bashrc/.zshrc.
-# if connecting via Emacs tramp, simplify prompt for easy identification.
-# keeping this statement at the top of the file prevent PS1 modifications
+# Tramp compatibility - simplify prompt for Emacs remote editing
if [ "$TERM" = "tramp" ] || [ "$TERM" = "dumb" ]; then
PS1='$ '
+ return 0 2>/dev/null || exit 0
fi
-##
-## ENVIRONMENT VARIABLES
-##
-
+# =============================================================================
# Locale
+# =============================================================================
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_CTYPE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
-# General
-export LANGUAGE=en_US
-export GPG_TTY="$(tty)"
-
+# =============================================================================
# Path
-[ -d "$HOME/.local/share/gem/ruby/3.4.0/bin" ]; PATH="$PATH:$HOME/.local/share/gem/ruby/3.4.0/bin"
-[ -d "$HOME/.cargo/bin" ]; PATH="$PATH:$HOME/.cargo/bin"
+# =============================================================================
+[ -d "$HOME/.local/share/gem/ruby/3.4.0/bin" ] && PATH="$PATH:$HOME/.local/share/gem/ruby/3.4.0/bin"
+[ -d "$HOME/.cargo/bin" ] && PATH="$PATH:$HOME/.cargo/bin"
+[ -d "$HOME/.deno/bin" ] && PATH="$PATH:$HOME/.deno/bin"
export PATH="$PATH:$HOME/.local/bin:/usr/sbin"
-# Editor
+# =============================================================================
+# Default Applications
+# =============================================================================
export ALTERNATE_EDITOR=""
export EDITOR="emacsclient -c -a ''"
-export SUDO_EDITOR="$(which vi)"
-
-# Browser
-export ALTBROWSER="$(which firefox)"
-export BROWSER="$(which google-chrome-stable)"
-# export BROWSER="$(which librewolf)"
+export VISUAL="emacsclient -c -a ''"
+export SUDO_EDITOR="vi"
+export BROWSER="google-chrome-stable"
+export ALTBROWSER="firefox"
-# Terminal
+# =============================================================================
+# Terminal (X11 defaults - overridden by hyprland.sh for Wayland)
+# =============================================================================
export COLORTERM=truecolor
-#export TERM="vt100"
export TERM="st-256color"
-export TERMINAL="$(which st)"
-export VISUAL="emacsclient -c -a ''"
+export TERMINAL="st"
-# XDG
-export XDG_CONFIG_HOME="$HOME/.config/"
-export XDG_CURRENT_DESKTOP=dwm
-export XDG_SESSION_TYPE=x11
-export XDG_DATA_DIRS="$XDG_DATA_DIRS:/var/lib/flatpak/exports/share:$HOME/.local/share/flatpak/exports/share:/usr/share"
+# =============================================================================
+# XDG Base Directories
+# =============================================================================
+export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}:/var/lib/flatpak/exports/share:$HOME/.local/share/flatpak/exports/share"
+
+# XDG_RUNTIME_DIR fallback (usually set by systemd/pam)
if [ -z "$XDG_RUNTIME_DIR" ]; then
export XDG_RUNTIME_DIR="$HOME/.local/xdg/runtime"
- if [ ! -d "$XDG_RUNTIME_DIR" ]; then
- mkdir -p "$XDG_RUNTIME_DIR"
- chmod 0700 "$XDG_RUNTIME_DIR"
- fi
+ mkdir -p "$XDG_RUNTIME_DIR"
+ chmod 0700 "$XDG_RUNTIME_DIR"
fi
-# INFO
-export INFOPATH=/home/cjennings/.emacs.d/assets/info:
-
-
-##
-## ALIASES
-##
-
-# change to specific directory
-alias cdot="cd ~/code/archsetup/dotfiles"
-alias cdpf="cd ~/projects/finances/"
-alias cdpj="cd ~/projects/jr-estate/"
-alias cdpd="cd ~/projects/documents/"
-
-# general software aliases
-alias crm="tickrs -s CRM"
-alias handbrake="ghb"
-alias linkdel="find . -type l ! -exec test -d {} \; -delete"
-alias linkfind="find . -type l ! -exec test -d {} \; -print"
-alias smerge="/usr/bin/smerge"
-alias steam="flatpak run com.valvesoftware.Steam"
-alias stext="/opt/sublime_text/sublime_text"
-alias stow="stow --target=/home/cjennings" # required as dotfiles are in non-standard location
-alias sysupgrade="topgrade"
-alias vim="nvim"
-alias et="emacs -nw"
-alias weather="wego"
-alias whereami="curl ipinfo.io"
-alias xterm="xterm -ti 340"
-
-# claude code
-alias hey='claude "Read ./docs/protocols.org and ./docs/NOTES.org, follow their instructions, then run session startup workflow."'
-
-# general utility
-# alias mount="sudo mount -o uid=$(id -u),gid=$(id -g),umask=0022 "
-alias backup='sudo rsyncshot backup 1000'
-alias boot2bios="sudo systemctl reboot --firmware-setup"
-alias df='dfc -p /dev/'
-alias ducks='du -cksh * | sort -rh | head -n11'
-alias ls="exa --group-directories-first "
-alias l="exa -lhF --group-directories-first "
-alias ll="exa -lhAF --group-directories-first "
-alias lt="exa -lthAF --group-directories-first "
-alias mkd="mkdir -pv"
-alias myip='curl -4 https://chroot-me.in/ip/ 2> /dev/null || w3m -4 -dump https://chroot-me.in/ip'
-alias open="xdg-open"
-alias ntop="sudo bandwhich"
-alias ptop="sudo powertop"
-alias running_services='systemctl list-units --type=service --state=running'
-alias speedtest="speedtest-go"
-alias ssn="sudo shutdown now"
-alias sysinfo='sudo inxi -v 8 -a -xxxA -xxxB -xxxC -xxxD -xxxG -xxxI -xxxm -xxxN -xxxR -xxxS -xxx --usb -d -I -pl -n -s --slots '
-alias timeshift='sudo timeshift-gtk'
-
-# programming
-alias cc="gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversion -Wextra -std=c2x -pedantic"
-alias gdbx="gdb --batch --ex r --ex bt --ex q --args"
+# Desktop defaults (X11 - overridden by hyprland.sh for Wayland)
+export XDG_CURRENT_DESKTOP=dwm
+export XDG_SESSION_TYPE=x11
-# ranger
-# when exiting ranger, leave the prompt at the destination
-alias cdr='. ranger'
-alias r='. ranger'
+# =============================================================================
+# GPG
+# =============================================================================
+export GPG_TTY="$(tty)"
-# source sh files in .profile.d
-PROFILE_D="$HOME/.profile.d/"
-if [ -d "$PROFILE_D" ]; then
- for file in $(find "$PROFILE_D" -name "*.sh"); do
- source "$file"
- done
+# =============================================================================
+# Emacs Info Path
+# =============================================================================
+export INFOPATH="$HOME/.config/emacs/info:/usr/share/info:/usr/local/share/info"
+
+# =============================================================================
+# Source modular environment files from .profile.d/
+# =============================================================================
+if [ -d "$HOME/.profile.d" ]; then
+ for file in "$HOME/.profile.d"/*.sh; do
+ [ -r "$file" ] && . "$file"
+ done
+ unset file
fi
-
-# launch x automatically
-# if [ -z "$DISPLAY" ] && [ "$(tty)" = /dev/tty1 ]; then
-# startx
-# fi
-# Phenomenology RAG alias - queries cogito with deepseek-r1:70b
-phenom() {
- aichat --rag phenom -m ollama:deepseek-r1:70b "$@"
-}
diff --git a/dotfiles/system/.profile.d/arch-linux-downgrade.sh b/dotfiles/system/.profile.d/arch-linux-downgrade.sh
deleted file mode 100755
index d97a2dc..0000000
--- a/dotfiles/system/.profile.d/arch-linux-downgrade.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-# arch-linux-downgrade.sh
-# Craig Jennings <c@cjennings.net>
-# Downgrade an explicitly installed Arch package, chosen via fzf.
-
-# Config:
-# export DG_HELPER=pacman # or yay, paru, etc. Must support -Qe, -Qi, -Si
-# export DG_FZF_OPTS="..." # optional extra fzf flags
-
-dg() {
- emulate -L zsh
- set -u
- set -o pipefail
-
- local helper="${DG_HELPER:-pacman}"
-
- # Check dependencies
- for cmd in "$helper" fzf sudo downgrade awk sort; do
- if ! command -v "${cmd%% *}" >/dev/null 2>&1; then
- print -u2 -- "Missing dependency: $cmd"
- return 1
- fi
- done
-
- # List explicitly installed packages
- local selection
- selection="$(
- "$helper" -Qe 2>/dev/null \
- | fzf --no-multi \
- --prompt="Pick package to downgrade > " \
- --height=80% \
- --reverse \
- --delimiter=' ' \
- --preview-window=right:60%:wrap \
- --preview "$helper -Qi {1} 2>/dev/null || $helper -Si {1}" \
- ${DG_FZF_OPTS:-}
- )"
-
- # If fzf or pacman fails, just return
- [[ $? -ne 0 || -z "$selection" ]] && return 0
-
- local pkg
- pkg="$(print -r -- "$selection" | head -n1 | awk '{print $1}')"
-
- if ! sudo downgrade "$pkg"; then
- print -u2 -- "Downgrade failed for $pkg"
- return 1
- fi
-}
diff --git a/dotfiles/system/.profile.d/auto-tmux-session.sh b/dotfiles/system/.profile.d/auto-tmux-session.sh
index 2f17085..cd39fab 100644
--- a/dotfiles/system/.profile.d/auto-tmux-session.sh
+++ b/dotfiles/system/.profile.d/auto-tmux-session.sh
@@ -1,12 +1,8 @@
-#!/bin/sh
-
# auto-tmux-session.sh
# Craig Jennings <c@cjennings.net>
-# if TRAMP (ssh dumb terminal), simplify prompt, and start tmux.
+# Start tmux automatically when connecting via SSH
-# start tmux when logging in via ssh
-if [[ -z "$TMUX" ]] && [ "$SSH_CONNECTION" != "" ]; then
- export TERM="xterm-mono"
- # if dumb terminal (i.e., tramp), then set a simple prompt, otherwise set monochrome TERM and start tmux
- tmux attach-session -t "$USER" || tmux new-session -s "$USER"
+if [ -z "$TMUX" ] && [ -n "$SSH_CONNECTION" ]; then
+ export TERM="xterm-mono"
+ tmux attach-session -t "$USER" || tmux new-session -s "$USER"
fi
diff --git a/dotfiles/system/.profile.d/chronographic.sh b/dotfiles/system/.profile.d/chronographic.sh
deleted file mode 100644
index 41c1860..0000000
--- a/dotfiles/system/.profile.d/chronographic.sh
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/bin/sh
-
-# chronographic.sh
-# Craig Jennings <c@cjennings.net>
-# quick and dirty terminal clock, timer, alarm and stopwatch functions
-
-alias beep='paplay $BEEP'
-export BEEP="/usr/share/sounds/freedesktop/stereo/bell.oga"
-
-##
-## CLOCK
-##
-
-clock() {
- while true; do tput clear; echo ""; date +" %l : %M %p" | figlet -f roman -w 200 ; sleep 1; done
-}
-
-##
-## TIMER
-##
-
-timer() {
- # Ensure we have at least two arguments
- if [ "$#" -lt 2 ]; then
- echo "Pass the time and a notification. Example: timer 1h30m Parking expiring"
- return 1
- fi
- message="${@:2}"
- start_time=$(date +"%H:%M:%S %p")
- printf "\nStarting %s timer at $start_time\n" "$1"
- snore "$1" && paplay "$BEEP" && notify-send "Timer" "$message" && echo ""
-}
-
-##
-## ALARM
-##
-
-alarm() {
- # Ensure we have two or more arguments
- if [ "$#" -lt 2 ]; then
- echo "Pass both the time and a message. Example: alarm 1:45pm Time to eat!"
- return 1
- fi
-
- # Validate the first argument is a valid time
- ! date -d "$1" >/dev/null 2>&1 && echo "Invalid time: $1" && return 1
-
- # silently schedule the command using 'at' command
- echo "paplay \$BEEP && notify-send \"Alarm\" \"$@\"" | at "$1" >> /dev/null 2>&1
- echo ""
- echo "Alarm '${@:2}' is queued for $1."
- echo "To see all alarms, issue the command: 'atq'"
- echo "To remove an alarm, issue the command: 'atrm' and the number of entry from atq"
- echo ""
-}
-
-##
-## STOPWATCH FUNCTIONS
-##
-
-sw_start_time=0
-sw_started=0
-
-swreset() {
- sw_start_time=0
- sw_started=0
- echo "Stopwatch reset"
-}
-
-swshow() {
- if [ "$sw_started" = 0 ] ; then
- echo "Error: Stopwatch not started" >&2 && return 1
- fi
-
- current_time=$(date +%s)
- elapsed_time=$((current_time - sw_start_time))
-
- if (( elapsed_time < 60 )); then
- # Display elapsed time in seconds
- echo "Elapsed time: $elapsed_time seconds"
- elif (( elapsed_time < 3600 )); then
- # Display elapsed time in minutes and seconds
- minutes=$((elapsed_time / 60))
- seconds=$((elapsed_time % 60))
- echo "Elapsed time: $minutes minutes, $seconds seconds"
- else
- # Display elapsed time in hours, minutes, and seconds
- hours=$((elapsed_time / 3600))
- minutes=$(((elapsed_time / 60) % 60))
- seconds=$((elapsed_time % 60))
- echo "Elapsed time: $hours hours, $minutes minutes, and $seconds seconds"
- fi
-}
-
-swstop() {
- swshow
- swreset
-}
-
-swstart() {
- if [ "$sw_started" = 1 ] ; then
- printf "Stopwatch is already started. Reset? (y/n): "
- read -r answer
- if [ "$answer" = "y" -o "$answer" = "Y" ]; then
- swreset
- # continue on to start the new timer
- elif [ "$answer" = "n" -o "$answer" = "N" ]; then
- echo "Stopwatch not reset."
- swshow
- # return to avoid restarting the timer
- return
- else
- echo "Error: Invalid input. Exiting." >&2 && return 1
- fi
- fi
-
- sw_started=1
- sw_start_time=$(date +%s)
- printf "Stopwatch started at %s\n\n" "$(date +"%H:%M:%S %p")"
-}
diff --git a/dotfiles/system/.profile.d/compress.sh b/dotfiles/system/.profile.d/compress.sh
deleted file mode 100644
index 0a9a7db..0000000
--- a/dotfiles/system/.profile.d/compress.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/sh
-# Craig Jennings <c@cjennings.net>
-# convenience compression function
-
-compress () {
- if [ $# -ne 2 ]; then
- echo "Usage: compress <format> <file_or_directory>"
- echo "Formats: tar.bz2, tar.gz, bz2, tar, tbz2, tgz, zip, gz, Z"
- return 1
- fi
-
- format="$1"
- target="$2"
-
- if [ ! -e "$target" ]; then
- echo "Error: '$target' does not exist"
- return 1
- fi
-
- basename=$(basename "$target")
-
- # Determine output filename first
- case "$format" in
- tar.bz2|tbz2) output="${basename}.tar.bz2" ;;
- tar.gz|tgz) output="${basename}.tar.gz" ;;
- bz2) output="${target}.bz2" ;;
- gz) output="${target}.gz" ;;
- tar) output="${basename}.tar" ;;
- zip) output="${basename}.zip" ;;
- Z) output="${target}.Z" ;;
- *)
- echo "Error: Unknown format '$format'"
- return 1
- ;;
- esac
-
- # Check if output file already exists
- if [ -e "$output" ]; then
- printf "Warning: '%s' already exists. Overwrite? (y/N): " "$output"
- read -r response
- case "$response" in
- [yY]|[yY][eE][sS])
- rm -f "$output"
- ;;
- *)
- echo "Aborted."
- return 1
- ;;
- esac
- fi
-
- # perform the compression
- case "$format" in
- tar.bz2|tbz2) tar -cjf "$output" "$target" ;;
- tar.gz|tgz) tar -czf "$output" "$target" ;;
- bz2)
- [ -d "$target" ] && echo "Error: bz2 only works on files" && return 1
- bzip2 -k "$target"
- ;;
- gz)
- [ -d "$target" ] && echo "Error: gz only works on files" && return 1
- gzip -k "$target"
- ;;
- tar) tar -cf "$output" "$target" ;;
- zip)
- [ -d "$target" ] && zip -r "$output" "$target" || zip "$output" "$target"
- ;;
- Z)
- [ -d "$target" ] && echo "Error: Z only works on files" && return 1
- compress -c "$target" > "$output"
- ;;
- esac
-
- [ $? -eq 0 ] && echo "✓ Created $output" || echo "✗ Compression failed"
-}
diff --git a/dotfiles/system/.profile.d/dd.sh b/dotfiles/system/.profile.d/dd.sh
deleted file mode 100644
index 5390a65..0000000
--- a/dotfiles/system/.profile.d/dd.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-
-# dd.sh
-# Craig Jennings <c@cjennings.net>
-# takes input and output files as arguments
-# prints parameters needed for dd
-
-
-dd_with_bs() {
- OUT_DIR=$(dirname "$2")
- if [ ! -e "$1" -o ! -e "$OUT_DIR" ]; then
- echo "$1 or $OUT_DIR doesn't exist"
- return 1
- fi
- IN_BS=$(stat -c "%o" "$1")
- OUT_BS=$(stat -c "%o" "$OUT_DIR")
-
- echo dd \"if=$1\" \"of=$2\" \"ibs=$IN_BS\" \"obs=$OUT_BS\"
-}
diff --git a/dotfiles/system/.profile.d/display.sh b/dotfiles/system/.profile.d/display.sh
index dbc3846..254fc53 100644
--- a/dotfiles/system/.profile.d/display.sh
+++ b/dotfiles/system/.profile.d/display.sh
@@ -1,15 +1,11 @@
-#!/bin/sh
# display.sh
# Craig Jennings <c@cjennings.net>
-# UI Appearance settings, sourced by .profile
+# UI appearance environment variables
-# Theme
+# GTK Theme
export GTK_THEME=Adwaita:dark
-# export GTK_THEME=Adwaita
-
-XDG_CURRENT_DESKTOP=GNOME
-# Qt theming - qt6ct for Qt6 apps, style override for consistent Adwaita-Dark
+# Qt theming - use qt6ct config with Adwaita-Dark style
export QT_QPA_PLATFORMTHEME=qt6ct
export QT_STYLE_OVERRIDE=Adwaita-Dark
export QT_SCALE_FACTOR=1
diff --git a/dotfiles/system/.profile.d/emacs.sh b/dotfiles/system/.profile.d/emacs.sh
deleted file mode 100644
index c70d928..0000000
--- a/dotfiles/system/.profile.d/emacs.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-# emacs.sh
-# Craig Jennings <c@cjennings.net>
-# Emacs specific settings, sourced by .profile
-
-### INFO FILES
-export INFOPATH="$HOME/.config/emacs/info:/usr/share/info:/usr/local/share/info"
-
-### WORKAROUND
-# https://unix.stackexchange.com/questions/230238/x-applications-warn-couldnt-connect-to-accessibility-bus-on-stderr
-# emacs/gtk interaction bug workaround
-export NO_AT_BRIDGE=1
-
-# WAKE UTILITY
-# useful when emacs or elisp doesn't timeout.
-alias emacswake='for i in `seq 1 500`; do killall -s USR2 emacs; done' # wake emacs from a freeze
-
-### VTERM
-# Vterm uses some features (e.g., directory-tracking and prompt-tracking or message passing) that require shell-side configurations.
-# This functions enables the shell to send information to vterm via properly escaped sequences.
-
-vterm_printf(){
- if [ -n "$TMUX" ] && ([ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ] ); then
- # Tell tmux to pass the escape sequences through
- printf "\ePtmux;\e\e]%s\007\e\\" "$1"
- elif [ "${TERM%%-*}" = "screen" ]; then
- # GNU screen (screen, screen-256color, screen-256color-bce)
- printf "\eP\e]%s\007\e\\" "$1"
- else
- printf "\e]%s\e\\" "$1"
- fi
-}
diff --git a/dotfiles/system/.profile.d/extract.sh b/dotfiles/system/.profile.d/extract.sh
deleted file mode 100644
index 5fce587..0000000
--- a/dotfiles/system/.profile.d/extract.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-# extract.sh
-# Craig Jennings <c@cjennings.net>
-# convenience extraction function
-
-## EXTRACT
-
-extract () {
- if [ -f "$1" ] ; then
- case "$1" in
- *.tar.bz2) tar xjf "$1" ;;
- *.tar.gz) tar xzf "$1" ;;
- *.bz2) bunzip2 "$1" ;;
- *.rar) rar x "$1" ;;
- *.gz) gunzip "$1" ;;
- *.tar) tar xf "$1" ;;
- *.tbz2) tar xjf "$1" ;;
- *.tgz) tar xzf "$1" ;;
- *.zip) unzip "$1" ;;
- *.Z) uncompress "$1" ;;
- *) echo "$1 cannot be extracted via extract()" ;;
- esac
- else
- echo "$1 is not a valid file"
- fi
-}
diff --git a/dotfiles/system/.profile.d/framework.sh b/dotfiles/system/.profile.d/framework.sh
index 2b8895e..5013e8d 100644
--- a/dotfiles/system/.profile.d/framework.sh
+++ b/dotfiles/system/.profile.d/framework.sh
@@ -1,10 +1,9 @@
-#!/bin/sh
-
# framework.sh
# Craig Jennings <c@cjennings.net>
-# settings for Framework 13 specific issues
+# Framework 13 laptop specific environment variables
-# HiDPI scale settings for Framework 13 Laptop
-export GDK_DPI_SCALE=1 # text scaling only.
-export GDK_SCALE=1 # this is an integer, 1 or 2 etc
+# HiDPI scaling (integer scaling for X11)
+# Note: Wayland handles scaling differently via compositor
+export GDK_DPI_SCALE=1
+export GDK_SCALE=1
export QT_AUTO_SCREEN_SCALE_FACTOR=1
diff --git a/dotfiles/system/.profile.d/freebsd.sh b/dotfiles/system/.profile.d/freebsd.sh
deleted file mode 100644
index 8d12031..0000000
--- a/dotfiles/system/.profile.d/freebsd.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-# freebsd.sh
-# Craig Jennings <c@cjennings.net>
-# Settings FreeBSD specific, sourced by .profile
-
-# make delete do the right thing for freebsd
-if echo "$(uname)" | grep -q "FreeBSD"; then
- bindkey "\e[3~" delete-char
-fi
diff --git a/dotfiles/system/.profile.d/fzf.sh b/dotfiles/system/.profile.d/fzf.sh
deleted file mode 100644
index 5fab752..0000000
--- a/dotfiles/system/.profile.d/fzf.sh
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/sh
-
-# fzf.sh
-# Craig Jennings <c@cjennings.net>
-# fuzzy find settings and utilities, sourced by .profile
-
-# otherwise ** doesn't expand
-source /usr/share/fzf/completion.zsh
-
-### SETTINGS
-export FZF_DEFAULT_OPTS='--height=70%'
-export FZF_DEFAULT_COMMAND='rg --files'
-export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
-export FZF_DEFAULT_COMMAND='rg --files --no-ignore-vcs --hidden'
-
-# Directory paths for convenience functions
-IF_GAMES_DIR="$HOME/sync/org/text.games"
-BOOKS_DIR="$HOME/sync/books"
-
-### NAVIGATION
-
-# cdff - change directory find file
-# change to the directory where the file resides.
-cdff() {
-
- file=$(fzf +m -q "$1")
- dir=$(dirname "$file")
- cd "$dir" || exit
-}
-
-# CD to a directory with fzf
-cdd () {
- destdir=$(find "${1:-.}" -path '*/\.*' -prune \
- -o -type d -print 2> /dev/null | fzf +m) &&
- cd "$destdir"
-}
-
-### SYSTEM ADMIN
-
-# Kill a process with fzf
-kp () {
- pid=$(ps -ef | sed 1d | eval "fzf ${FZF_DEFAULT_OPTS} -m --header='[kill:process]'" | awk '{print $2}')
-
- if [ "x$pid" != "x" ]
- then
- echo "$pid" | xargs kill -${1:-9}
- kp
- fi
-}
-
-# list available packages, show info in preview, and install selection
-yinstall() {
- yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm
-}
-
-
-yinstall-skipverify() {
- yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm --mflags --skipinteg
-}
-
-# list installed packages, show info in preview, and remove selection
-yrm() {
- yay -Qq | fzf --multi --preview 'yay -Qi {1}' | xargs -ro yay -Rns
-}
-
-# find-in-file - usage: fif <searchTerm>
-fif() {
- if [ ! "$#" -gt 0 ]; then echo "Need a string to search for!"; return 1; fi
- rg --files-with-matches --no-messages "$1" | fzf --preview "highlight -O ansi -l {} 2> /dev/null | rg --colors 'match:bg:yellow' --ignore-case --pretty --context 10 '$1' || rg --ignore-case --pretty --context 10 '$1' {}"
-}
-
-### CONVENIENCE
-
-# Find an epub book in the library and open it in epr terminal reader.
-# previously: find ~/books \( -iname \*.epub -o -iname \*.pdf -o -iname \*.djvu \) | fzf | xargs emacs
-bk() {
- bkfile=$(find "$BOOKS_DIR" -iname "*.epub" -print | fzf)
- if [ -n "$bkfile" ]; then
- epr "$bkfile"
- fi
-}
-
-# Find an interactive fiction game and open it in frotz.
-# Supports Z-machine files (.z1-.z8, .zblorb, .blorb)
-tg() {
- gamefile=$(find "$IF_GAMES_DIR" -type f \( -iname "*.z[1-8]" -o -iname "*.zblorb" -o -iname "*.blorb" \) -print | fzf)
- if [ -n "$gamefile" ]; then
- frotz "$gamefile"
- fi
-}
-
-
-
-# close wireguard connection first if already running, then
-# run wireguard, selecting the configuration file.
-wgup() {
- # Check if wireguard is running
- output=$(sudo wg)
- if [[ -n "$output" ]]; then
- # Shutdown all wg interfaces if WireGuard is currently running.
- for iface in $(sudo wg show interfaces); do
- sudo wg-quick down "${iface}"
- done
- fi
- # Get the list of config files
- wgfile=$(sudo find /etc/wireguard/ -iname "*.conf" -exec basename -s .conf {} \; | fzf)
-
- if [ -n "$wgfile" ]; then
- sudo wg-quick up $wgfile
- sudo wg
- fi
-}
-wgdown() {
- # Check if wireguard is running
- output=$(sudo wg)
- if [[ -n "$output" ]]; then
- # Shutdown all wg interfaces if WireGuard is currently running.
- for iface in $(sudo wg show interfaces); do
- sudo wg-quick down "${iface}"
- done
- fi
-}
-alias wg=wgup
diff --git a/dotfiles/system/.profile.d/git.sh b/dotfiles/system/.profile.d/git.sh
deleted file mode 100644
index 7332ba1..0000000
--- a/dotfiles/system/.profile.d/git.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-# git.sh
-# Craig Jennings <c@cjennings.net>
-# git settings and convenience aliases, sourced by .profile
-
-alias gitlog="git log --graph --pretty=format:'%Cred%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relative"
-alias gitstatus='git status -sb '
-alias gitcom='git commit -m '
-alias gitpp='git pull --prune ' # clean up any orphaned git objects
-alias gittagbydate="git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags"
-
-gitsp() {
- git stash && git pull && git stash pop
-}
-
-gitck() {
- git checkout "$(git branch --all | fzf| tr -d '[:space:]')"
-}
-
-gitdiff() {
- preview="git diff $@ --color=always -- {-1}"
- git diff "$@" --name-only | fzf -m --ansi --preview "$preview"
-}
diff --git a/dotfiles/system/.profile.d/zoxide.sh b/dotfiles/system/.profile.d/zoxide.sh
deleted file mode 100755
index 8bbb86b..0000000
--- a/dotfiles/system/.profile.d/zoxide.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-# zoxide.sh
-# Craig Jennings <c@cjennings.net>
-# zoxide initialization, sourced by .profile
-
-# Initialize zoxide for zsh
-eval "$(zoxide init zsh)"
-
-# Alias cd to use zoxide
-# alias cd="z"
diff --git a/dotfiles/system/.zshrc b/dotfiles/system/.zshrc
index 413d8b8..0e997ab 100644
--- a/dotfiles/system/.zshrc
+++ b/dotfiles/system/.zshrc
@@ -1,55 +1,66 @@
-# source appropriate dotfiles if they exist
-[ -f ~/.profile ] && source ~/.profile
-[ -f ~/.secrets ] && source ~/.secrets
-[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
-[ -f ~/.zsh/fzf-tab.zsh ] && source ~/.zsh/fzf-tab.zsh
-
-
-# GENERAL #####################
-setopt PROMPT_SUBST # allow env variables in prompt
-setopt AUTO_REMOVE_SLASH # self explicit
-setopt CHASE_LINKS # resolve symlinks
-setopt CORRECT # try to correct spelling of commands
-setopt EXTENDED_GLOB # activate complex pattern globbing
-setopt GLOB_DOTS # include dotfiles in globbing
-setopt PRINT_EXIT_VALUE # print return value if non-zero
-setopt CLOBBER # don't need to use >| to truncate existing files
-setopt interactivecomments # allow comments in command line like bash
-unsetopt BEEP # no bell on error
-unsetopt BG_NICE # no lower prio for background jobs
-unsetopt HIST_BEEP # no bell on error in history
-unsetopt HUP # no hup signal at shell exit
-unsetopt IGNORE_EOF # do not exit on end-of-file
-unsetopt LIST_BEEP # no bell on ambiguous completion
-unsetopt RM_STAR_SILENT # ask for confirmation for `rm *' or `rm path/*'
-autoload -U colors zsh-mime-setup select-word-style
-colors # colors
-zsh-mime-setup # run everything as if it's an executable
+# .zshrc
+# Craig Jennings <c@cjennings.net>
+#
+# Zsh-specific interactive shell settings.
+# Aliases, functions, prompt, completions, shell options.
+
+# =============================================================================
+# Environment Variables (from .profile)
+# =============================================================================
+[ -f "$HOME/.profile" ] && source "$HOME/.profile"
+[ -f "$HOME/.secrets" ] && source "$HOME/.secrets"
+
+# =============================================================================
+# General Options
+# =============================================================================
+setopt PROMPT_SUBST # allow variable substitution in prompt
+setopt AUTO_REMOVE_SLASH # remove trailing slash when completing
+setopt CHASE_LINKS # resolve symlinks
+setopt CORRECT # try to correct spelling of commands
+setopt EXTENDED_GLOB # activate complex pattern globbing
+setopt GLOB_DOTS # include dotfiles in globbing
+setopt PRINT_EXIT_VALUE # print return value if non-zero
+setopt CLOBBER # allow > to truncate existing files
+setopt INTERACTIVE_COMMENTS # allow comments in command line
+unsetopt BEEP # no bell on error
+unsetopt BG_NICE # no lower prio for background jobs
+unsetopt HIST_BEEP # no bell on error in history
+unsetopt HUP # no hup signal at shell exit
+unsetopt IGNORE_EOF # do not exit on end-of-file
+unsetopt LIST_BEEP # no bell on ambiguous completion
+unsetopt RM_STAR_SILENT # ask for confirmation for `rm *'
+autoload -U colors zsh-mime-setup select-word-style
+colors
+zsh-mime-setup
-# HISTORY #####################
+# =============================================================================
+# History
+# =============================================================================
HISTFILE="$HOME/.zsh_history"
HISTSIZE=10000000
SAVEHIST=$HISTSIZE
-setopt BANG_HIST # Treat the '!' character specially during expansion.
-setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format.
-setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits.
-setopt SHARE_HISTORY # Share history between all sessions.
-setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history.
-setopt HIST_IGNORE_DUPS # Don't record an entry that was just recorded again.
-setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate.
-setopt HIST_FIND_NO_DUPS # Do not display a line previously found.
-setopt HIST_IGNORE_SPACE # Don't record an entry starting with a space.
-setopt HIST_SAVE_NO_DUPS # Don't write duplicate entries in the history file.
-setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry.
-setopt HIST_VERIFY # Don't execute immediately upon history expansion.
-setopt HIST_BEEP # Beep when accessing nonexistent history.
-
-# KEYBOARD ##################
-# create a zkbd compatible hash;
-# to add other keys to this hash, see: man 5 terminfo
-typeset -g -A key
+setopt BANG_HIST # treat '!' specially during expansion
+setopt EXTENDED_HISTORY # write history with timestamps
+setopt INC_APPEND_HISTORY # write to history immediately
+setopt SHARE_HISTORY # share history between sessions
+setopt HIST_EXPIRE_DUPS_FIRST
+setopt HIST_IGNORE_DUPS
+setopt HIST_IGNORE_ALL_DUPS
+setopt HIST_FIND_NO_DUPS
+setopt HIST_IGNORE_SPACE # don't record entries starting with space
+setopt HIST_SAVE_NO_DUPS
+setopt HIST_REDUCE_BLANKS
+setopt HIST_VERIFY # don't execute immediately on history expansion
+
+# =============================================================================
+# Keyboard Bindings
+# =============================================================================
+bindkey -e # emacs keybindings
+
+# zkbd compatible hash for special keys
+typeset -g -A key
key[Home]="${terminfo[khome]}"
key[End]="${terminfo[kend]}"
key[Insert]="${terminfo[kich1]}"
@@ -63,7 +74,6 @@ key[PageUp]="${terminfo[kpp]}"
key[PageDown]="${terminfo[knp]}"
key[Shift-Tab]="${terminfo[kcbt]}"
-# setup key accordingly
[[ -n "${key[Home]}" ]] && bindkey -- "${key[Home]}" beginning-of-line
[[ -n "${key[End]}" ]] && bindkey -- "${key[End]}" end-of-line
[[ -n "${key[Insert]}" ]] && bindkey -- "${key[Insert]}" overwrite-mode
@@ -77,43 +87,43 @@ key[Shift-Tab]="${terminfo[kcbt]}"
[[ -n "${key[PageDown]}" ]] && bindkey -- "${key[PageDown]}" end-of-buffer-or-history
[[ -n "${key[Shift-Tab]}" ]] && bindkey -- "${key[Shift-Tab]}" reverse-menu-complete
-# cjennings additions #################
-bindkey -e # emacs keybindings
-bindkey '\e[1;5C' forward-word # C-Right
-bindkey '\e[1;5D' backward-word # C-Left
-bindkey -s "\e[24~" "" # stop F12 from spitting tildes
+bindkey '\e[1;5C' forward-word # Ctrl-Right
+bindkey '\e[1;5D' backward-word # Ctrl-Left
+bindkey -s "\e[24~" "" # stop F12 from outputting tildes
-# Finally, make sure the terminal is in application mode, when zle is
-# active. Only then are the values from $terminfo valid.
+# Terminal application mode for proper key handling
if (( ${+terminfo[smkx]} && ${+terminfo[rmkx]} )); then
- autoload -Uz add-zle-hook-widget
- function zle_application_mode_start { echoti smkx }
- function zle_application_mode_stop { echoti rmkx }
- add-zle-hook-widget -Uz zle-line-init zle_application_mode_start
- add-zle-hook-widget -Uz zle-line-finish zle_application_mode_stop
+ autoload -Uz add-zle-hook-widget
+ function zle_application_mode_start { echoti smkx }
+ function zle_application_mode_stop { echoti rmkx }
+ add-zle-hook-widget -Uz zle-line-init zle_application_mode_start
+ add-zle-hook-widget -Uz zle-line-finish zle_application_mode_stop
fi
-# COMPLETION ##################
-autoload -U compinit
-compinit
-zmodload -i zsh/complist
-setopt AUTO_LIST # list options on ambiguous completion
-setopt AUTO_MENU # show menu on second request for completion
-setopt ALWAYS_TO_END # when completing from middle of a word, move cursor to end of word
-setopt COMPLETE_IN_WORD # allow completion from within a word/phrase
-setopt COMPLETEALIASES # complete alisases
-setopt CORRECT # spelling correction for commands
-setopt HASH_LIST_ALL # hash everything before completion
-setopt LIST_AMBIGUOUS # complete as much of a completion until it gets
-
-zstyle ':completion::complete:*' use-cache on # completion caching, use rehash to clear
-zstyle ':completion:*' cache-path ~/.zsh/cache # cache path
-zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # ignore case
-zstyle ':completion:*' menu select=2 # menu if nb items > 2
-zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} # colorz !
-zstyle ':completion:*::::' completer _expand _complete _ignored _approximate # list of completers to use
-
-# sections completion
+# FreeBSD delete key fix
+[[ "$(uname)" == "FreeBSD" ]] && bindkey "\e[3~" delete-char
+
+# =============================================================================
+# Completion
+# =============================================================================
+autoload -U compinit && compinit
+zmodload -i zsh/complist
+
+setopt AUTO_LIST # list options on ambiguous completion
+setopt AUTO_MENU # show menu on second tab
+setopt ALWAYS_TO_END # move cursor to end after completion
+setopt COMPLETE_IN_WORD # allow completion from within a word
+setopt COMPLETE_ALIASES # complete aliases
+setopt HASH_LIST_ALL # hash everything before completion
+setopt LIST_AMBIGUOUS # complete as much as possible
+
+zstyle ':completion::complete:*' use-cache on
+zstyle ':completion:*' cache-path ~/.zsh/cache
+zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # case insensitive
+zstyle ':completion:*' menu select=2
+zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
+zstyle ':completion:*::::' completer _expand _complete _ignored _approximate
+
zstyle ':completion:*' verbose yes
zstyle ':completion:*:descriptions' format $'\e[00;34m%d'
zstyle ':completion:*:messages' format $'\e[00;31m%d'
@@ -126,54 +136,65 @@ zstyle ':completion:*:kill:*' force-list always
zstyle ':completion:*:*:kill:*:processes' list-colors "=(#b) #([0-9]#)*=29=34"
zstyle ':completion:*:*:killall:*' menu yes select
zstyle ':completion:*:killall:*' force-list always
-users=(cjennings root) # because I don't care about others
-zstyle ':completion:*' users $users
+zstyle ':completion:*' users cjennings root
-#generic completion with --help
compdef _gnu_generic gcc
compdef _gnu_generic gdb
-# DIRECTORY NAVIGATION ########
-setopt AUTO_CD # if typing directory name, auto cd there
-setopt AUTO_PUSHD # push old directories to stack (for use with popd)
-setopt PUSHD_IGNORE_DUPS # don’t push multiple copies of same directory onto stack
-setopt PUSHD_SILENT # don't print directory stack after pushd/popd
-setopt PUSHD_TO_HOME # pushd == `pushd $HOME`
-
-# VERSION CONTROL
-autoload -Uz vcs_info # Load version control information
-precmd() { vcs_info }
-
-# Styling the version control info
-zstyle ':vcs_info:*' enable git cvs svn # enable only the repo systems I use
-zstyle ':vcs_info:*' check-for-changes false # don't check for changes in local repo
-zstyle ':vcs_info:git*' formats "on %b" # format the vcs_info_msg_0_ variable
-
-# HISTORY ####################
-export HISTFILE=~/.zsh_history
-export HISTFILESIZE=1000000000
-export HISTSIZE=1000000000
-setopt INC_APPEND_HISTORY
-export HISTTIMEFORMAT="[%F %T] "
-setopt EXTENDED_HISTORY
-setopt HIST_IGNORE_ALL_DUPS
+# Python Poetry completions
+fpath+=~/.zfunc
-# PROMPT #####################
+# =============================================================================
+# Directory Navigation
+# =============================================================================
+setopt AUTO_CD # cd by typing directory name
+setopt AUTO_PUSHD # push old directories to stack
+setopt PUSHD_IGNORE_DUPS # don't push duplicates
+setopt PUSHD_SILENT # don't print stack after pushd/popd
+setopt PUSHD_TO_HOME # pushd with no args goes home
+
+# =============================================================================
+# Version Control Info (for prompt)
+# =============================================================================
+autoload -Uz vcs_info
+precmd() { vcs_info }
+zstyle ':vcs_info:*' enable git cvs svn
+zstyle ':vcs_info:*' check-for-changes false
+zstyle ':vcs_info:git*' formats "on %b"
+
+# =============================================================================
+# Prompt
+# =============================================================================
WHO='%n'
NEWLINE=$'\n'
-PROMPT='[%D %*] $WHO $HOST:${PWD/#$HOME/~} %(?..[%?] ) $NEWLINE%# '
+PROMPT='[%D %*] $WHO $HOST:${PWD/#$HOME/~} %(?..[%?] )$NEWLINE%# '
RPROMPT='${vcs_info_msg_0_}'
-# PYTHON POETRY #############
-fpath+=~/.zfunc
-autoload -Uz compinit && compinit
+# =============================================================================
+# Source modular zsh configs from .zshrc.d/
+# =============================================================================
+if [[ -d "$HOME/.zshrc.d" ]]; then
+ for file in "$HOME/.zshrc.d"/*.sh; do
+ [[ -r "$file" ]] && source "$file"
+ done
+ unset file
+fi
+
+# =============================================================================
+# Tool-specific initialization
+# =============================================================================
+# FZF
+[[ -f "$HOME/.fzf.zsh" ]] && source "$HOME/.fzf.zsh"
+[[ -f "$HOME/.zsh/fzf-tab.zsh" ]] && source "$HOME/.zsh/fzf-tab.zsh"
-# zoxide
-eval "$(zoxide init zsh)"
+# Dart CLI completion
+[[ -f "$HOME/.config/.dart-cli-completion/zsh-config.zsh" ]] && \
+ source "$HOME/.config/.dart-cli-completion/zsh-config.zsh"
-## [Completion]
-## Completion scripts setup. Remove the following line to uninstall
-[[ -f /home/cjennings/.config/.dart-cli-completion/zsh-config.zsh ]] && . /home/cjennings/.config/.dart-cli-completion/zsh-config.zsh || true
-## [/Completion]
+# Zoxide (smart cd)
+command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init zsh)"
-ulimit -c unlimited \ No newline at end of file
+# =============================================================================
+# Resource Limits
+# =============================================================================
+ulimit -c unlimited
diff --git a/dotfiles/system/.zshrc.d/aliases.sh b/dotfiles/system/.zshrc.d/aliases.sh
new file mode 100644
index 0000000..28c0f3f
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/aliases.sh
@@ -0,0 +1,91 @@
+# aliases.sh
+# Craig Jennings <c@cjennings.net>
+# Shell aliases - works in both bash and zsh
+
+# =============================================================================
+# Directory Navigation
+# =============================================================================
+alias cdot="cd ~/code/archsetup/dotfiles"
+alias cdpf="cd ~/projects/finances/"
+alias cdpj="cd ~/projects/jr-estate/"
+alias cdpd="cd ~/projects/documents/"
+
+# =============================================================================
+# File Listing (exa)
+# =============================================================================
+alias ls="exa --group-directories-first"
+alias l="exa -lhF --group-directories-first"
+alias ll="exa -lhAF --group-directories-first"
+alias lt="exa -lthAF --group-directories-first"
+
+# =============================================================================
+# File Operations
+# =============================================================================
+alias mkd="mkdir -pv"
+alias open="xdg-open"
+alias linkdel="find . -type l ! -exec test -d {} \; -delete"
+alias linkfind="find . -type l ! -exec test -d {} \; -print"
+
+# =============================================================================
+# System Administration
+# =============================================================================
+alias df='dfc -p /dev/'
+alias ducks='du -cksh * | sort -rh | head -n11'
+alias ntop="sudo bandwhich"
+alias ptop="sudo powertop"
+alias running_services='systemctl list-units --type=service --state=running'
+alias ssn="sudo shutdown now"
+alias boot2bios="sudo systemctl reboot --firmware-setup"
+alias backup='sudo rsyncshot backup 1000'
+alias sysinfo='sudo inxi -v 8 -a -xxxA -xxxB -xxxC -xxxD -xxxG -xxxI -xxxm -xxxN -xxxR -xxxS -xxx --usb -d -I -pl -n -s --slots'
+alias timeshift='sudo timeshift-gtk'
+alias sysupgrade="topgrade"
+
+# =============================================================================
+# Network
+# =============================================================================
+alias myip='curl -4 https://chroot-me.in/ip/ 2>/dev/null || w3m -4 -dump https://chroot-me.in/ip'
+alias whereami="curl ipinfo.io"
+alias speedtest="speedtest-go"
+
+# =============================================================================
+# Applications
+# =============================================================================
+alias vim="nvim"
+alias et="emacs -nw"
+alias weather="wego"
+alias crm="tickrs -s CRM"
+alias handbrake="ghb"
+alias smerge="/usr/bin/smerge"
+alias stext="/opt/sublime_text/sublime_text"
+alias steam="flatpak run com.valvesoftware.Steam"
+alias xterm="xterm -ti 340"
+
+# =============================================================================
+# Stow (dotfiles management)
+# =============================================================================
+alias stow="stow --target=/home/cjennings"
+
+# =============================================================================
+# Ranger (file manager)
+# =============================================================================
+alias cdr='. ranger'
+alias r='. ranger'
+
+# =============================================================================
+# Programming
+# =============================================================================
+alias cc="gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversion -Wextra -std=c2x -pedantic"
+alias gdbx="gdb --batch --ex r --ex bt --ex q --args"
+
+# =============================================================================
+# Claude Code
+# =============================================================================
+alias hey='claude "Read ./docs/protocols.org and ./docs/NOTES.org, follow their instructions, then run session startup workflow."'
+
+# =============================================================================
+# Phenomenology RAG (ollama/deepseek)
+# =============================================================================
+phenom() {
+ aichat --rag phenom -m ollama:deepseek-r1:70b "$@"
+}
diff --git a/dotfiles/system/.zshrc.d/arch-downgrade.sh b/dotfiles/system/.zshrc.d/arch-downgrade.sh
new file mode 100644
index 0000000..f1d01a7
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/arch-downgrade.sh
@@ -0,0 +1,47 @@
+# arch-downgrade.sh
+# Craig Jennings <c@cjennings.net>
+# Downgrade Arch packages with fzf (zsh only)
+
+# Config:
+# export DG_HELPER=pacman # or yay, paru, etc.
+# export DG_FZF_OPTS="..." # optional extra fzf flags
+
+dg() {
+ emulate -L zsh
+ set -u
+ set -o pipefail
+
+ local helper="${DG_HELPER:-pacman}"
+
+ # Check dependencies
+ for cmd in "$helper" fzf sudo downgrade awk sort; do
+ if ! command -v "${cmd%% *}" >/dev/null 2>&1; then
+ print -u2 -- "Missing dependency: $cmd"
+ return 1
+ fi
+ done
+
+ # List explicitly installed packages
+ local selection
+ selection="$(
+ "$helper" -Qe 2>/dev/null \
+ | fzf --no-multi \
+ --prompt="Pick package to downgrade > " \
+ --height=80% \
+ --reverse \
+ --delimiter=' ' \
+ --preview-window=right:60%:wrap \
+ --preview "$helper -Qi {1} 2>/dev/null || $helper -Si {1}" \
+ ${DG_FZF_OPTS:-}
+ )"
+
+ [[ $? -ne 0 || -z "$selection" ]] && return 0
+
+ local pkg
+ pkg="$(print -r -- "$selection" | head -n1 | awk '{print $1}')"
+
+ if ! sudo downgrade "$pkg"; then
+ print -u2 -- "Downgrade failed for $pkg"
+ return 1
+ fi
+}
diff --git a/dotfiles/system/.zshrc.d/emacs.sh b/dotfiles/system/.zshrc.d/emacs.sh
new file mode 100644
index 0000000..0a8444b
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/emacs.sh
@@ -0,0 +1,24 @@
+# emacs.sh
+# Craig Jennings <c@cjennings.net>
+# Emacs-specific settings and functions
+
+# GTK/Emacs accessibility bug workaround
+# https://unix.stackexchange.com/questions/230238/
+export NO_AT_BRIDGE=1
+
+# Wake emacs from elisp freeze
+alias emacswake='for i in $(seq 1 500); do killall -s USR2 emacs; done'
+
+# Vterm shell integration
+# Allows shell to send information to vterm via escape sequences
+vterm_printf() {
+ if [ -n "$TMUX" ] && { [ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ]; }; then
+ # Tell tmux to pass the escape sequences through
+ printf "\ePtmux;\e\e]%s\007\e\\" "$1"
+ elif [ "${TERM%%-*}" = "screen" ]; then
+ # GNU screen
+ printf "\eP\e]%s\007\e\\" "$1"
+ else
+ printf "\e]%s\e\\" "$1"
+ fi
+}
diff --git a/dotfiles/system/.zshrc.d/fzf.sh b/dotfiles/system/.zshrc.d/fzf.sh
new file mode 100644
index 0000000..9a5a9bd
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/fzf.sh
@@ -0,0 +1,122 @@
+# fzf.sh
+# Craig Jennings <c@cjennings.net>
+# FZF settings and utility functions
+
+# =============================================================================
+# Settings
+# =============================================================================
+export FZF_DEFAULT_OPTS='--height=70%'
+export FZF_DEFAULT_COMMAND='rg --files --no-ignore-vcs --hidden'
+export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
+
+# Directory paths for convenience functions
+IF_GAMES_DIR="$HOME/sync/org/text.games"
+BOOKS_DIR="$HOME/sync/books"
+
+# =============================================================================
+# Navigation
+# =============================================================================
+
+# cdff - change directory to where a file resides
+cdff() {
+ file=$(fzf +m -q "$1")
+ dir=$(dirname "$file")
+ cd "$dir" || return 1
+}
+
+# cdd - cd to directory with fzf
+cdd() {
+ destdir=$(find "${1:-.}" -path '*/\.*' -prune \
+ -o -type d -print 2>/dev/null | fzf +m) &&
+ cd "$destdir"
+}
+
+# =============================================================================
+# System Admin
+# =============================================================================
+
+# Kill process with fzf
+kp() {
+ pid=$(ps -ef | sed 1d | eval "fzf ${FZF_DEFAULT_OPTS} -m --header='[kill:process]'" | awk '{print $2}')
+ if [ -n "$pid" ]; then
+ echo "$pid" | xargs kill -"${1:-9}"
+ kp
+ fi
+}
+
+# Install packages with fzf preview
+yinstall() {
+ yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm
+}
+
+yinstall-skipverify() {
+ yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm --mflags --skipinteg
+}
+
+# Remove packages with fzf preview
+yrm() {
+ yay -Qq | fzf --multi --preview 'yay -Qi {1}' | xargs -ro yay -Rns
+}
+
+# Find in file with fzf
+fif() {
+ if [ "$#" -eq 0 ]; then
+ echo "Need a string to search for!"
+ return 1
+ fi
+ rg --files-with-matches --no-messages "$1" | \
+ fzf --preview "highlight -O ansi -l {} 2>/dev/null | \
+ rg --colors 'match:bg:yellow' --ignore-case --pretty --context 10 '$1' || \
+ rg --ignore-case --pretty --context 10 '$1' {}"
+}
+
+# =============================================================================
+# Convenience
+# =============================================================================
+
+# Find and read epub book in terminal
+bk() {
+ bkfile=$(find "$BOOKS_DIR" -iname "*.epub" -print | fzf)
+ if [ -n "$bkfile" ]; then
+ epr "$bkfile"
+ fi
+}
+
+# Find and play interactive fiction game
+tg() {
+ gamefile=$(find "$IF_GAMES_DIR" -type f \( -iname "*.z[1-8]" -o -iname "*.zblorb" -o -iname "*.blorb" \) -print | fzf)
+ if [ -n "$gamefile" ]; then
+ frotz "$gamefile"
+ fi
+}
+
+# =============================================================================
+# WireGuard
+# =============================================================================
+
+wgup() {
+ # Shutdown existing connections first
+ output=$(sudo wg 2>/dev/null)
+ if [ -n "$output" ]; then
+ for iface in $(sudo wg show interfaces); do
+ sudo wg-quick down "${iface}"
+ done
+ fi
+ # Select and start new connection
+ wgfile=$(sudo find /etc/wireguard/ -iname "*.conf" -exec basename -s .conf {} \; | fzf)
+ if [ -n "$wgfile" ]; then
+ sudo wg-quick up "$wgfile"
+ sudo wg
+ fi
+}
+
+wgdown() {
+ output=$(sudo wg 2>/dev/null)
+ if [ -n "$output" ]; then
+ for iface in $(sudo wg show interfaces); do
+ sudo wg-quick down "${iface}"
+ done
+ fi
+}
+
+alias wg=wgup
diff --git a/dotfiles/system/.zshrc.d/git.sh b/dotfiles/system/.zshrc.d/git.sh
new file mode 100644
index 0000000..6c2b6ad
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/git.sh
@@ -0,0 +1,32 @@
+# git.sh
+# Craig Jennings <c@cjennings.net>
+# Git aliases and convenience functions
+
+# =============================================================================
+# Aliases
+# =============================================================================
+alias gitlog="git log --graph --pretty=format:'%Cred%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relative"
+alias gitstatus='git status -sb'
+alias gitcom='git commit -m'
+alias gitpp='git pull --prune'
+alias gittagbydate="git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags"
+
+# =============================================================================
+# Functions
+# =============================================================================
+
+# Stash, pull, pop
+gitsp() {
+ git stash && git pull && git stash pop
+}
+
+# Checkout branch with fzf
+gitck() {
+ git checkout "$(git branch --all | fzf | tr -d '[:space:]')"
+}
+
+# Diff with fzf preview
+gitdiff() {
+ preview="git diff $@ --color=always -- {-1}"
+ git diff "$@" --name-only | fzf -m --ansi --preview "$preview"
+}
diff --git a/dotfiles/system/.zshrc.d/media.sh b/dotfiles/system/.zshrc.d/media.sh
new file mode 100644
index 0000000..92fe2ce
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/media.sh
@@ -0,0 +1,41 @@
+# media.sh
+# Craig Jennings <c@cjennings.net>
+# Media utilities - music, video, downloads
+
+# =============================================================================
+# Terminal MPV
+# =============================================================================
+alias play='mpv --no-video'
+alias shuffle='mpv --no-video --shuffle'
+
+# =============================================================================
+# Tidal
+# =============================================================================
+alias tdl="tidal-dl -l"
+alias ttdl="tsp tidal-dl -l"
+
+# =============================================================================
+# YouTube (yt-dlp)
+# =============================================================================
+# Video - single
+alias yt="yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'"
+alias tyt="tsp yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'"
+
+# Video - playlist
+alias ytp="yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'"
+alias tytp="tsp yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'"
+
+# Audio - single
+alias yta="yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'"
+alias tyta="tsp yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'"
+
+# Audio - playlist
+alias ytap="yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'"
+alias tytap="tsp yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'"
+
+# =============================================================================
+# Audible Conversion (AAXtoMP3)
+# =============================================================================
+alias aax2flac='AAXtoMP3 -f'
+alias aax2mp3='AAXtoMP3 -c -e:mp3'
+alias aax2opus='AAXtoMP3 --opus -t . -c'
diff --git a/dotfiles/system/.zshrc.d/utilities.sh b/dotfiles/system/.zshrc.d/utilities.sh
new file mode 100644
index 0000000..431cac0
--- /dev/null
+++ b/dotfiles/system/.zshrc.d/utilities.sh
@@ -0,0 +1,206 @@
+# utilities.sh
+# Craig Jennings <c@cjennings.net>
+# General utility functions
+
+# =============================================================================
+# Archive Extraction
+# =============================================================================
+extract() {
+ if [ -f "$1" ]; then
+ case "$1" in
+ *.tar.bz2) tar xjf "$1" ;;
+ *.tar.gz) tar xzf "$1" ;;
+ *.bz2) bunzip2 "$1" ;;
+ *.rar) rar x "$1" ;;
+ *.gz) gunzip "$1" ;;
+ *.tar) tar xf "$1" ;;
+ *.tbz2) tar xjf "$1" ;;
+ *.tgz) tar xzf "$1" ;;
+ *.zip) unzip "$1" ;;
+ *.Z) uncompress "$1" ;;
+ *) echo "$1 cannot be extracted via extract()" ;;
+ esac
+ else
+ echo "$1 is not a valid file"
+ fi
+}
+
+# =============================================================================
+# Archive Compression
+# =============================================================================
+compress() {
+ if [ $# -ne 2 ]; then
+ echo "Usage: compress <format> <file_or_directory>"
+ echo "Formats: tar.bz2, tar.gz, bz2, tar, tbz2, tgz, zip, gz, Z"
+ return 1
+ fi
+
+ format="$1"
+ target="$2"
+
+ if [ ! -e "$target" ]; then
+ echo "Error: '$target' does not exist"
+ return 1
+ fi
+
+ basename=$(basename "$target")
+
+ case "$format" in
+ tar.bz2|tbz2) output="${basename}.tar.bz2" ;;
+ tar.gz|tgz) output="${basename}.tar.gz" ;;
+ bz2) output="${target}.bz2" ;;
+ gz) output="${target}.gz" ;;
+ tar) output="${basename}.tar" ;;
+ zip) output="${basename}.zip" ;;
+ Z) output="${target}.Z" ;;
+ *)
+ echo "Error: Unknown format '$format'"
+ return 1
+ ;;
+ esac
+
+ if [ -e "$output" ]; then
+ printf "Warning: '%s' already exists. Overwrite? (y/N): " "$output"
+ read -r response
+ case "$response" in
+ [yY]|[yY][eE][sS]) rm -f "$output" ;;
+ *) echo "Aborted." && return 1 ;;
+ esac
+ fi
+
+ case "$format" in
+ tar.bz2|tbz2) tar -cjf "$output" "$target" ;;
+ tar.gz|tgz) tar -czf "$output" "$target" ;;
+ bz2)
+ [ -d "$target" ] && echo "Error: bz2 only works on files" && return 1
+ bzip2 -k "$target"
+ ;;
+ gz)
+ [ -d "$target" ] && echo "Error: gz only works on files" && return 1
+ gzip -k "$target"
+ ;;
+ tar) tar -cf "$output" "$target" ;;
+ zip)
+ [ -d "$target" ] && zip -r "$output" "$target" || zip "$output" "$target"
+ ;;
+ Z)
+ [ -d "$target" ] && echo "Error: Z only works on files" && return 1
+ command compress -c "$target" > "$output"
+ ;;
+ esac
+
+ [ $? -eq 0 ] && echo "Created $output" || echo "Compression failed"
+}
+
+# =============================================================================
+# DD Helper
+# =============================================================================
+dd_with_bs() {
+ OUT_DIR=$(dirname "$2")
+ if [ ! -e "$1" ] || [ ! -e "$OUT_DIR" ]; then
+ echo "$1 or $OUT_DIR doesn't exist"
+ return 1
+ fi
+ IN_BS=$(stat -c "%o" "$1")
+ OUT_BS=$(stat -c "%o" "$OUT_DIR")
+ echo dd \"if=$1\" \"of=$2\" \"ibs=$IN_BS\" \"obs=$OUT_BS\"
+}
+
+# =============================================================================
+# Clock, Timer, Alarm, Stopwatch
+# =============================================================================
+export BEEP="/usr/share/sounds/freedesktop/stereo/bell.oga"
+alias beep='paplay $BEEP'
+
+clock() {
+ while true; do
+ tput clear
+ echo ""
+ date +" %l : %M %p" | figlet -f roman -w 200
+ sleep 1
+ done
+}
+
+timer() {
+ if [ "$#" -lt 2 ]; then
+ echo "Pass the time and a notification. Example: timer 1h30m Parking expiring"
+ return 1
+ fi
+ message="${*:2}"
+ start_time=$(date +"%H:%M:%S %p")
+ printf "\nStarting %s timer at %s\n" "$1" "$start_time"
+ snore "$1" && paplay "$BEEP" && notify-send "Timer" "$message"
+}
+
+alarm() {
+ if [ "$#" -lt 2 ]; then
+ echo "Pass both the time and a message. Example: alarm 1:45pm Time to eat!"
+ return 1
+ fi
+
+ if ! date -d "$1" >/dev/null 2>&1; then
+ echo "Invalid time: $1"
+ return 1
+ fi
+
+ echo "paplay \$BEEP && notify-send \"Alarm\" \"$*\"" | at "$1" >/dev/null 2>&1
+ echo ""
+ echo "Alarm '${*:2}' is queued for $1."
+ echo "To see all alarms: atq"
+ echo "To remove an alarm: atrm <number>"
+ echo ""
+}
+
+# Stopwatch
+sw_start_time=0
+sw_started=0
+
+swreset() {
+ sw_start_time=0
+ sw_started=0
+ echo "Stopwatch reset"
+}
+
+swshow() {
+ if [ "$sw_started" = 0 ]; then
+ echo "Error: Stopwatch not started" >&2
+ return 1
+ fi
+
+ current_time=$(date +%s)
+ elapsed_time=$((current_time - sw_start_time))
+
+ if [ "$elapsed_time" -lt 60 ]; then
+ echo "Elapsed time: $elapsed_time seconds"
+ elif [ "$elapsed_time" -lt 3600 ]; then
+ minutes=$((elapsed_time / 60))
+ seconds=$((elapsed_time % 60))
+ echo "Elapsed time: $minutes minutes, $seconds seconds"
+ else
+ hours=$((elapsed_time / 3600))
+ minutes=$(((elapsed_time / 60) % 60))
+ seconds=$((elapsed_time % 60))
+ echo "Elapsed time: $hours hours, $minutes minutes, $seconds seconds"
+ fi
+}
+
+swstop() {
+ swshow
+ swreset
+}
+
+swstart() {
+ if [ "$sw_started" = 1 ]; then
+ printf "Stopwatch is already started. Reset? (y/n): "
+ read -r answer
+ case "$answer" in
+ [yY]) swreset ;;
+ [nN]) echo "Stopwatch not reset." && swshow && return ;;
+ *) echo "Error: Invalid input." >&2 && return 1 ;;
+ esac
+ fi
+
+ sw_started=1
+ sw_start_time=$(date +%s)
+ printf "Stopwatch started at %s\n\n" "$(date +"%H:%M:%S %p")"
+}