diff options
| author | Craig Jennings <c@cjennings.net> | 2026-01-26 16:57:37 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-01-26 16:57:37 -0600 |
| commit | 7d490453085ae084ce0e3875952eae1d3ad7b1ab (patch) | |
| tree | d6f76ded02d541107271dc2b04cac34435c81a26 | |
| parent | feb8dfaae9b0172c9d24e7e0d115754a467b4627 (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>
31 files changed, 1379 insertions, 802 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4ab478e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,47 @@ +# Archsetup Project Context + +## Overview +Arch Linux installation and configuration scripts with dotfiles managed via GNU Stow. Supports both X11 (DWM) and Wayland (Hyprland) setups. + +## Project Structure +- `archsetup` - Main installation script with functions for packages, configs, services +- `dotfiles/system/` - Shared dotfiles (stowed to ~) +- `dotfiles/hyprland/` - Hyprland-specific dotfiles (stowed to ~) +- `dotfiles/dwm/` - DWM/X11-specific dotfiles +- `docs/` - Documentation and package lists +- `reference-repos/` - External dotfile references + +## Current Theme (Dupre) +- **GTK**: Adwaita-dark +- **Qt**: Adwaita-Dark (via adwaita-qt5/qt6) +- **Icons**: Papirus-Dark +- **Cursors**: capitaine-cursors-light (size 24) +- **Color palette**: bg #151311, gold #d7af5f, steel #969385, blue #67809c + +## Key Configuration Files +- `dotfiles/hyprland/.config/hypr/hyprland.conf` - Main Hyprland config +- `dotfiles/hyprland/.config/themes/dupre/` - Dupre theme files +- `dotfiles/system/.profile.d/` - Shell environment scripts +- `dotfiles/system/.config/qt5ct/qt5ct.conf` - Qt5 theming +- `dotfiles/system/.config/qt6ct/qt6ct.conf` - Qt6 theming +- `dotfiles/system/.local/bin/resetmimetypes` - MIME associations script + +## Pending Tasks +1. Review shell config files for Wayland compatibility +3. Theme Nautilus to match Hyprland/dupre theme (low priority - Adwaita-dark works) +4. Evaluate Euphonica MPD client (May 2026) + +## Recent Changes (Jan 2026) +- Added Qt theming with adwaita-qt5/qt6 +- Added font packages: cantarell-fonts, ttf-caladea, ttf-carlito, ttf-croscore +- Pruned ~90 desktop files from app menu with NoDisplay=true overrides +- Switched file manager from Thunar to Nautilus +- Cleaned up resetmimetypes script (296 -> 124 lines) +- Added satty screenshot annotation and hyprpicker +- Configured wlogout exit menu with dupre theme + +## Notes +- Desktop file overrides go in `dotfiles/hyprland/.local/share/applications/` +- Stow conflicts require manual symlinks: `ln -sf source target` +- MPD is used for music; mpv handles audio file associations +- BerkeleyMono Nerd Font is the primary monospace font 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")" +} |
