diff --git a/README.md b/README.md index 0faf9b9..c73cb51 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,28 @@ dotfiles. ;) ### Options ### +### macOS-like Copy/Paste ### + +To address keyboard shortcut conflicts between operating systems, this environment +now supports using `Alt+C` for copy and `Alt+V` for paste, similar to macOS. +This functionality is context-aware: it will automatically use `Ctrl+Shift+C/V` +in terminals and `Ctrl+C/V` in all other applications. + +This feature requires the following packages to be installed: + +- `xbindkeys`: To listen for the keyboard shortcuts. +- `xdotool`: To send the appropriate keypresses. + +On Debian-based systems (like Ubuntu or Kali), you can install them with: + +```bash +sudo apt-get update +sudo apt-get install xbindkeys xdotool +``` + +After installation, the functionality will be enabled automatically on your +next login. + ``` BASEDIR: Where the skel framework is installed. Defaults to $HOME/.skel MINIMAL: Don't do things that require git clones or installation of anything diff --git a/bin/smart-copy-paste b/bin/smart-copy-paste new file mode 100755 index 0000000..8b88a6d --- /dev/null +++ b/bin/smart-copy-paste @@ -0,0 +1,36 @@ +#!/bin/sh +# +# smart-copy-paste +# +# This script provides context-aware copy and paste operations, mimicking +# macOS behavior (Alt+C/V) while correctly handling terminals that require +# the Shift key. + +# Exit silently if xdotool is not installed. +if ! command -v xdotool > /dev/null; then + exit 1 +fi + +# Get the class name of the currently focused window. +# We need to get the window on focus, to avoid issues with transparent terminals. +class=$(xdotool getwindowclassname "$(xdotool getwindowfocus)") + +# Semicolon-separated list of terminal class names. +terminals='Gnome-terminal;Xfce4-terminal;konsole;xterm;URxvt;Terminator;Alacritty;kitty;wezterm' + +# Determine the keystroke based on the window type and the argument passed. +if echo "$terminals" | grep -q "$class"; then + # This is a terminal, so use Shift. + if [ "$1" = "copy" ]; then + xdotool key --clearmodifiers ctrl+shift+c + elif [ "$1" = "paste" ]; then + xdotool key --clearmodifiers ctrl+shift+v + fi +else + # This is a standard GUI app. + if [ "$1" = "copy" ]; then + xdotool key --clearmodifiers ctrl+c + elif [ "$1" = "paste" ]; then + xdotool key --clearmodifiers ctrl+v + fi +fi diff --git a/dotfiles/aliases b/dotfiles/aliases index bd6bb7e..cd678ba 100755 --- a/dotfiles/aliases +++ b/dotfiles/aliases @@ -1,22 +1,12 @@ # General aliases, should only be sourced in interactive shells -# Add an "alert" alias for long running commands. Use like so: -# sleep 10; alert -alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' + # Cryptsetup alias alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64' -# Colors -if ls --version >/dev/null 2>&1 ; then - alias ls='ls --color=auto' -fi -if [ `uname` != 'Darwin' -a `uname` != 'NetBSD' -a `uname` != 'FreeBSD' -a `uname` != 'OpenBSD' ] ; then - # Should have a better way to check for GNU versions - alias grep='grep --color=auto' - alias egrep='egrep --color=auto' - alias fgrep='fgrep --color=auto' -fi + + # Easy upgrade alias dist-upgrade="sudo sh -c 'apt-get update && apt-get -y dist-upgrade'" @@ -30,8 +20,7 @@ alias mdcode="sed 's/^/ /'" # Intel format plz alias objdump="command objdump -M intel" -# Useful directory utilities -alias dircount="for d in * ; do find \$d -type d | wc -l | tr -d '\n' ; echo ' ' \$d ; done | sort -n" + # Drop caches for swap issues alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches" @@ -41,7 +30,7 @@ alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e- # get git working directory alias gitroot="git rev-parse --show-toplevel" -alias cdgr='cd $(git rev-parse --show-toplevel || echo .)' + # SSH without host key checking alias sshanon="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no" diff --git a/dotfiles/config/fish/conf.d/aliases.fish b/dotfiles/config/fish/conf.d/aliases.fish new file mode 100644 index 0000000..8a08930 --- /dev/null +++ b/dotfiles/config/fish/conf.d/aliases.fish @@ -0,0 +1,65 @@ +# This script reads a bash-formatted alias file (~/.aliases) and creates +# equivalent fish aliases. This allows for sharing aliases between bash and fish. + +# Path to the bash alias file +set bash_alias_file ~/.aliases + +# Check if the alias file exists +if test -f "$bash_alias_file" + # Read the file line by line + while read -l line + # Skip comments and empty lines + if string match -q -r '^\s*#' "$line" || test -z "$line" + continue + end + + # Check if the line defines an alias + if string match -q -r '^\s*alias\s+' "$line" + # Remove the 'alias ' prefix + set -l definition (string replace -r '^\s*alias\s+' '' "$line") + + # Split the definition into name and value at the first '=' + set -l parts (string split -m 1 '=' "$definition") + set -l alias_name $parts[1] + set -l alias_value $parts[2] + + # Remove leading/trailing quotes from the value + set -l alias_value (string trim -c "'"" "$alias_value") + + # Define the fish alias + alias $alias_name "$alias_value" + end + end < "$bash_alias_file" +end + +# Specific aliases that are not in the bash file + +# On some systems, bat is batcat +if not command -v bat >/dev/null 2>&1 + if command -v batcat >/dev/null 2>&1 + alias bat (command -v batcat) + end +end + +# FFUF aliases +if command -v ffuf >/dev/null 2>&1 + if test -d $HOME/tools/seclists + alias ffuf-files "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/raft-large-files.txt" + alias ffuf-dirs "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/raft-large-directories.txt" + alias ffuf-quick "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/quickhits.txt" + end +end + +if grep --help 2>/dev/null | grep -q 'color' + # Should have a better way to check for GNU versions + alias grep 'grep --color=auto' + alias egrep 'egrep --color=auto' + alias fgrep 'fgrep --color=auto' +end + +# Detect which `ls` flavor is in use and use the right flag for colors. +if ls --help 2>&1 | grep -q -- '--color' + alias ls 'ls --color=auto' # GNU `ls` +else if test (uname) = "Darwin" + alias ls 'ls -G' # macOS `ls` +end \ No newline at end of file diff --git a/dotfiles/config/fish/config.fish b/dotfiles/config/fish/config.fish index f681987..972eb5b 100644 --- a/dotfiles/config/fish/config.fish +++ b/dotfiles/config/fish/config.fish @@ -7,6 +7,11 @@ if test -x /opt/homebrew/bin/brew if test -d (brew --prefix)"/share/fish/vendor_completions.d" set -p fish_complete_path (brew --prefix)/share/fish/vendor_completions.d end + + # mise, if installed + if type -q mise + mise hook fish | source + end end if command -q starship diff --git a/dotfiles/config/fish/functions/cdgr.fish b/dotfiles/config/fish/functions/cdgr.fish new file mode 100644 index 0000000..fc0ea87 --- /dev/null +++ b/dotfiles/config/fish/functions/cdgr.fish @@ -0,0 +1,8 @@ +# Change to the root of the git repository. +# If not in a git repo, do nothing. +function cdgr + set git_root (git rev-parse --show-toplevel 2>/dev/null) + if test -n "$git_root" + cd "$git_root" + end +end diff --git a/dotfiles/config/mise/config.toml b/dotfiles/config/mise/config.toml new file mode 100644 index 0000000..4ef4de3 --- /dev/null +++ b/dotfiles/config/mise/config.toml @@ -0,0 +1,14 @@ +[settings] +experimental = true + +[settings.pipx] +uvx = true + +[settings.python] +uv_venv_auto = true + +[tools] +uv = "latest" + +[hooks] +postinstall = "mise sync python --uv" diff --git a/dotfiles/xbindkeysrc b/dotfiles/xbindkeysrc new file mode 100644 index 0000000..68d5004 --- /dev/null +++ b/dotfiles/xbindkeysrc @@ -0,0 +1,8 @@ +# Custom copy/paste mapping for Alt+C and Alt+V to feel more like macOS. +# Uses a helper script to send Shift for terminal applications. + +"smart-copy-paste copy" + alt + c + +"smart-copy-paste paste" + alt + v diff --git a/dotfiles/xsessionrc b/dotfiles/xsessionrc index f25d487..2b42ba9 100644 --- a/dotfiles/xsessionrc +++ b/dotfiles/xsessionrc @@ -2,4 +2,5 @@ setxkbmap -option ctrl:nocaps -option compose:ralt test -x /usr/bin/xsettingsd && /usr/bin/xsettingsd & test -f "$HOME/.env" && "$HOME/.env" test -f "$HOME/.shenv" && "$HOME/.shenv" +command -v xbindkeys >/dev/null && xbindkeys & test -f "$HOME/.profile" && . "$HOME/.profile" diff --git a/dotfiles/zlogin b/dotfiles/zlogin index 72ca9b0..7d61ed4 100644 --- a/dotfiles/zlogin +++ b/dotfiles/zlogin @@ -1,8 +1,3 @@ -# Execute code that does not affect the current session in the background. -{ - # Compile the completion dump to increase startup speed. - zcompdump="${ZDOTDIR:-$HOME}/.zcompdump" - if [[ -s "$zcompdump" && (! -s "${zcompdump}.zwc" || "$zcompdump" -nt "${zcompdump}.zwc") ]]; then - zcompile "$zcompdump" - fi -} &! +# This file is sourced only for login shells (e.g., SSH sessions, initial TTY logins). +# For most interactive shells on macOS (like those in Terminal.app or iTerm2), it is NOT sourced. +# All active Zsh startup and completion logic has been moved to .zshrc and .zshenv for better consistency and performance. \ No newline at end of file diff --git a/dotfiles/zshrc b/dotfiles/zshrc index da8040f..b3b8478 100755 --- a/dotfiles/zshrc +++ b/dotfiles/zshrc @@ -1,4 +1,8 @@ # For interactive shells +[[ -n "$ZSH_PROFILE" ]] && { + zshrc_start_time=$(date +%s.%N) + zmodload zsh/zprof +} HISTFILE=~/.zhistory HISTSIZE=10000 SAVEHIST=10000 @@ -159,7 +163,7 @@ bindkey '^r' history-incremental-search-backward bindkey "^[[3~" delete-char source_if_existing() { - if test -e "${1}" ; then source "${1}" ; else false ; fi + [[ -f "${1}" ]] && source "${1}" } source_first_existing() { @@ -196,7 +200,20 @@ if [[ $- == *i* ]] ; then zstyle ':compinstall' filename "${HOME}/.zshrc" zstyle ':completion:*' users root ${USER} # Modules after fpath - autoload -Uz compinit && compinit -i + autoload -Uz compinit + + # Regenerate zcompdump if it's older than any file in fpath + DUMPFILE="${ZDOTDIR:-$HOME}/.zcompdump" + updated_files=(${^fpath}(N.om[1])) + + if [[ ! -f "$DUMPFILE" || ( ${#updated_files} -gt 0 && "$updated_files[1]" -nt "$DUMPFILE" ) ]]; then + compinit -i -D "$DUMPFILE" + # Asynchronously compile the dump file + { zcompile "$DUMPFILE" } &! + else + compinit -C -i -D "$DUMPFILE" + fi + unset DUMPFILE updated_files autoload -Uz promptinit && promptinit # Virtualenvwrapper source_first_existing \ @@ -274,3 +291,11 @@ fi # Cleanup PATH typeset -U PATH + +if [[ -n "$ZSH_PROFILE" ]]; then + zshrc_end_time=$(date +%s.%N) + elapsed_seconds=$(echo "$zshrc_end_time - $zshrc_start_time" | bc -l) + elapsed_ms=$(printf "%.0f" "$(echo "$elapsed_seconds * 1000" | bc -l)") + echo "zshrc done: ${elapsed_ms}ms" + zprof +fi diff --git a/dotfiles/zshrc.d/alert.zsh b/dotfiles/zshrc.d/alert.zsh new file mode 100644 index 0000000..7eab354 --- /dev/null +++ b/dotfiles/zshrc.d/alert.zsh @@ -0,0 +1,26 @@ +#!/usr/bin/env zsh + +# A function to run a command and send a notification when it's done. +# Usage: alert sleep 10 + +alert() { + # Run the command passed as arguments + "$@" + + # Capture the exit code + local ret=$? + + # Determine the icon based on success or failure + local icon + if [ $ret -eq 0 ]; then + icon="terminal" + else + icon="error" + fi + + # Send the notification with the executed command + notify-send --urgency=low -i "$icon" "Finished: '$@'" + + # Return the original exit code + return $ret +} diff --git a/dotfiles/zshrc.d/aliases.zsh b/dotfiles/zshrc.d/aliases.zsh index 14e86e2..0b8c453 100644 --- a/dotfiles/zshrc.d/aliases.zsh +++ b/dotfiles/zshrc.d/aliases.zsh @@ -15,3 +15,17 @@ if command -v ffuf >/dev/null 2>&1 ; then alias ffuf-quick="ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/quickhits.txt" fi fi + +if grep --help 2>/dev/null | grep -q 'color'; then + # Should have a better way to check for GNU versions + alias grep='grep --color=auto' + alias egrep='egrep --color=auto' + alias fgrep='fgrep --color=auto' +fi + +# Detect which `ls` flavor is in use and use the right flag for colors. +if ls --help 2>&1 | grep -q -- '--color'; then + alias ls='ls --color=auto' # GNU `ls` +elif [ "$(uname)" = "Darwin" ]; then + alias ls='ls -G' # macOS `ls` +fi diff --git a/dotfiles/zshrc.d/cdgr.zsh b/dotfiles/zshrc.d/cdgr.zsh new file mode 100644 index 0000000..d414165 --- /dev/null +++ b/dotfiles/zshrc.d/cdgr.zsh @@ -0,0 +1,9 @@ +# Change to the root of the git repository. +# If not in a git repo, do nothing. +cdgr() { + local git_root + git_root=$(git rev-parse --show-toplevel 2>/dev/null) + if [ -n "$git_root" ]; then + cd "$git_root" + fi +}