diff --git a/sway/.config/sway/config.d/keybindings b/sway/.config/sway/config.d/keybindings index 03a9489..bd2cac7 100644 --- a/sway/.config/sway/config.d/keybindings +++ b/sway/.config/sway/config.d/keybindings @@ -121,3 +121,22 @@ bindsym { button3 floating toggle $mod+button3 floating toggle } + +# Presentation Mode +mode "present" { + # command starts mirroring + bindsym m mode "default"; exec wl-present mirror + # these commands modify an already running mirroring window + bindsym o mode "default"; exec wl-present set-output + bindsym r mode "default"; exec wl-present set-region + bindsym Shift+r mode "default"; exec wl-present unset-region + bindsym s mode "default"; exec wl-present set-scaling + bindsym f mode "default"; exec wl-present toggle-freeze + bindsym c mode "default"; exec wl-present custom + + # return to default mode + bindsym Return mode "default" + bindsym Escape mode "default" +} + +bindsym $mod+Shift+m mode "present" diff --git a/zsh/.config/zsh/.zprofile b/zsh/.config/zsh/.zprofile index 4fc56ac..0c33138 100644 --- a/zsh/.config/zsh/.zprofile +++ b/zsh/.config/zsh/.zprofile @@ -1,27 +1,4 @@ -# === Variables específicas para sesión gráfica (Wayland/Sway) === -export ZDOTDIR=~/.config/zsh -export MOZ_ENABLE_WAYLAND=1 -export QT_QPA_PLATFORM=wayland-egl -export QT_WAYLAND_DISABLE_WINDOWDECORATION=1 -export QT_QPA_PLATFORMTHEME=qt5ct -export QT_STYLE_OVERRIDE=kvantum -export CLUTTER_BACKEND=wayland -export GDK_BACKEND=wayland -export ECORE_EVAS_ENGINE=wayland_egl -export ELM_ENGINE=wayland_wgl -export SDL_VIDEODRIVER=wayland -export _JAVA_AWT_WM_NONREPARENTING=1 -export XDG_CURRENT_DESKTOP=sway -export XDG_SESSION_TYPE=wayland - -unset SSH_AGENT_PID -if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then - export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" -fi -export GPG_TTY=$(tty) -gpg-connect-agent updatestartuptty /bye >/dev/null - -if [ -z "$WAYLAND_DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then - exec sway -fi +source ~/.config/env +# If running from tty1 start sway +[ "$(tty)" = "/dev/tty1" ] && exec dbus-run-session sway diff --git a/zsh/.config/zsh/.zshrc b/zsh/.config/zsh/.zshrc index 57c0a01..1edae66 100644 --- a/zsh/.config/zsh/.zshrc +++ b/zsh/.config/zsh/.zshrc @@ -1,9 +1,12 @@ source $ZDOTDIR/init.zsh -# Initialize completions with defer +# Initialize completions immediately autoload -Uz compinit if [ "$(date +'%j')" != "$(stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null)" ]; then - zsh-defer compinit + compinit else - zsh-defer compinit -C + compinit -C fi + +# Load completion registrations +source $ZDOTDIR/completions.zsh diff --git a/zsh/.config/zsh/completions.zsh b/zsh/.config/zsh/completions.zsh new file mode 100644 index 0000000..0f87c6a --- /dev/null +++ b/zsh/.config/zsh/completions.zsh @@ -0,0 +1,48 @@ +#!/bin/zsh +# Completion registration file +# This file handles all completion registrations + +# Load custom completion files +for completion_file in ~/.local/share/zsh/*-autocomplete.zsh; do + if [ -f "$completion_file" ]; then + source "$completion_file" + fi +done + +# Lazy loading function for completions +_lazy_load_completion() { + local cmd="$1" + local completion_cmd="$2" + + eval "${cmd}() { + unfunction $cmd + eval \"\$($completion_cmd)\" + $cmd \"\$@\" + }" +} + +# Load lightweight completions immediately +if command -v eza &> /dev/null; then compdef eza=ls; fi + +# Defer heavy completions with lazy loading +if command -v kubefwd &> /dev/null; then _lazy_load_completion kubefwd "kubefwd completion zsh"; fi +if command -v bombadil &> /dev/null; then _lazy_load_completion bombadil "bombadil generate-completions zsh"; fi + +# Load remaining completions normally (they're lightweight) +if command -v rop &> /dev/null; then eval "$(rop completion zsh)"; fi +if command -v goq &> /dev/null; then eval "$(goq completion zsh)"; fi +if command -v drop &> /dev/null; then eval "$(drop completion zsh)"; fi + +# Custom completion for kf function using kubectx contexts +_kf_completion() { + local -a contexts + # Get all contexts and extract unique cluster names (remove -read and -security suffixes) + contexts=($(kubectx 2>/dev/null | sed 's/-read$//; s/-security$//' | sort -u)) + + if [[ ${#contexts[@]} -gt 0 ]]; then + _values 'cluster' "${contexts[@]}" + fi +} + +# Register the completion +compdef _kf_completion kf diff --git a/zsh/.config/zsh/functions.zsh b/zsh/.config/zsh/functions.zsh index 291227d..eab4e41 100644 --- a/zsh/.config/zsh/functions.zsh +++ b/zsh/.config/zsh/functions.zsh @@ -1,27 +1,64 @@ #!/bin/zsh +# ============================================================================= +# NAVIGATION UTILITIES +# ============================================================================= + eval "$(zoxide init zsh)" -zi() { z "$@" } -za() { z "$@" } -zrm() { zoxide remove "$@" } +# Zoxide aliases with better names +zcd() { z "$@" } +zadd() { z "$@" } +zremove() { zoxide remove "$@" } +# Legacy aliases for backward compatibility +alias zi=zcd +alias za=zadd +alias zrm=zremove + +# Open files with xdg-open (non-blocking) function open { - for i in $*; do - setsid nohup xdg-open $i > /dev/null 2> /dev/null + if [[ $# -eq 0 ]]; then + echo "Usage: open [file2] ..." + echo "Opens files using the system default application" + return 1 + fi + + for i in "$@"; do + if [[ ! -e "$i" ]]; then + echo "Warning: '$i' does not exist" >&2 + continue + fi + setsid nohup xdg-open "$i" > /dev/null 2> /dev/null & done } +# Find and open files with fzf function fopen() { + if ! command -v fd >/dev/null 2>&1; then + echo "Error: fd is not installed" >&2 + return 1 + fi + + if ! command -v fzf >/dev/null 2>&1; then + echo "Error: fzf is not installed" >&2 + return 1 + fi + local selected selected=$(fd "$@" | fzf --preview 'bat --color=always --style=numbers --line-range=:500 {}') [[ -n "$selected" ]] && setsid nohup xdg-open "$selected" >/dev/null 2>&1 & } +# ============================================================================= # DEVELOPMENT TOOLS -# Package manager detection with caching +# ============================================================================= + +# Smart package manager detection and execution function _package_manager { local pkg_manager="" + + # Detect package manager based on lock files if [[ -f bun.lockb ]]; then pkg_manager="bun" elif [[ -f pnpm-lock.yaml ]]; then @@ -31,15 +68,17 @@ function _package_manager { elif [[ -f package-lock.json ]]; then pkg_manager="npm" else - pkg_manager="pnpm" + pkg_manager="pnpm" # Default fallback fi + # Auto-detect npm scripts if command exists in package.json if [[ -f package.json ]] && [[ $1 != "run" ]] && [[ $1 != "install" ]] && [[ $1 != "add" ]] && [[ $1 != "remove" ]] && [[ $1 != "i" ]] && [[ $1 != "rm" ]]; then - if jq -e ".scripts[\"$1\"] != null" package.json >/dev/null 2>&1; then + if command -v jq >/dev/null 2>&1 && jq -e ".scripts[\"$1\"] != null" package.json >/dev/null 2>&1; then set -- "run" "$@" fi fi + # Execute with corepack if available, otherwise direct command if command -v corepack >/dev/null 2>&1; then corepack ${pkg_manager} "$@" else @@ -47,10 +86,18 @@ function _package_manager { fi } -# Improved development commit with optional message +# Quick development commit with auto-push function dc() { + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: dc [commit_message]" + echo "Quickly commit all changes and push to remote" + echo "If no message provided, uses timestamp-based message" + return 0 + fi + if ! git rev-parse --is-inside-work-tree &>/dev/null; then - echo "Error: Not in a git repository" + echo "Error: Not in a git repository" >&2 return 1 fi @@ -69,13 +116,35 @@ function dc() { commit_msg="dev: automated commit - ${timestamp}" fi - # pass other args - git commit -m "$commit_msg" --no-gpg-sign "$@" - git push &>/dev/null || { echo "Push failed"; return 1; } + # Commit with message and push + if git commit -m "$commit_msg" --no-gpg-sign; then + if git push &>/dev/null; then + echo "Committed and pushed successfully" + else + echo "Warning: Commit successful but push failed" >&2 + return 1 + fi + else + echo "Commit failed" >&2 + return 1 + fi } -# Git branch cleanup helper +# Clean up merged git branches function git-clean() { + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: git-clean" + echo "Safely delete merged branches (both local and optionally remote)" + echo "Protects main, master, and develop branches" + return 0 + fi + + if ! git rev-parse --is-inside-work-tree &>/dev/null; then + echo "Error: Not in a git repository" >&2 + return 1 + fi + local current_branch=$(git rev-parse --abbrev-ref HEAD) local branches_to_delete=$(git branch --merged | grep -v "^\*" | grep -v "master" | grep -v "main" | grep -v "develop") @@ -100,117 +169,136 @@ function git-clean() { echo "$branches_to_delete" | xargs -I{} git push origin --delete {} echo "Remote branches deleted" fi + else + echo "Operation cancelled" fi } -function sm { /opt/sublime_merge/sublime_merge $1; } +# Open Sublime Merge +function sm { + if [[ ! -f /opt/sublime_merge/sublime_merge ]]; then + echo "Error: Sublime Merge not found at /opt/sublime_merge/sublime_merge" >&2 + return 1 + fi + /opt/sublime_merge/sublime_merge "$@" +} +# Quick access to Neovim config function vimrc { - cd $XDG_CONFIG_HOME/nvim + if [[ -z "$XDG_CONFIG_HOME" ]]; then + echo "Error: XDG_CONFIG_HOME not set" >&2 + return 1 + fi + + local original_dir=$(pwd) + cd "$XDG_CONFIG_HOME/nvim" || { + echo "Error: Cannot access $XDG_CONFIG_HOME/nvim" >&2 + return 1 + } nvim - cd - >/dev/null + cd "$original_dir" } +# Quick access to Zsh config function zshrc { - cd $XDG_CONFIG_HOME/zsh + if [[ -z "$XDG_CONFIG_HOME" ]]; then + echo "Error: XDG_CONFIG_HOME not set" >&2 + return 1 + fi + + local original_dir=$(pwd) + cd "$XDG_CONFIG_HOME/zsh" || { + echo "Error: Cannot access $XDG_CONFIG_HOME/zsh" >&2 + return 1 + } nvim - cd - >/dev/null -} - -# FILE SHARING AND NETWORKING -function upload_file() { - local usage="Usage: $0 [-o] " - local one_time=true - - # Parse options - while getopts ":o" opt; do - case ${opt} in - o ) one_time=false ;; - \? ) - echo "Invalid option: -$OPTARG" >&2 - echo $usage - return 1 ;; - esac - done - shift $((OPTIND -1)) - - # Check file argument - if [[ -z "$1" ]]; then - echo $usage - return 1 - fi - if [[ ! -f "$1" ]]; then - echo "Error: File not found: $1" - return 1 - fi - - local url="https://drop.mz.uy" - echo "Uploading $1 to $url... (one-time: ${one_time})" - - # Prepare upload options - local form_data=("-F" "file=@$1") - if $one_time; then - form_data+=("-F" "one_time=") - fi - - # Try up to 3 times with a short delay between attempts - local max_attempts=3 - local attempt=1 - local success=false - - while (( attempt <= max_attempts )) && ! $success; do - if (( attempt > 1 )); then - echo "Retry attempt $attempt of $max_attempts..." - sleep 1 - fi - - local full_response=$(curl -i "${form_data[@]}" "$url" 2>/dev/null) - local curl_status=$? - - if [[ $curl_status -eq 0 ]]; then - local url_response=$(echo "$full_response" | tail -n 1) - local token=$(echo "$full_response" | grep -i "X-Token:" | awk '{print $2}' | tr -d '\r') - - if [[ -n "$url_response" ]]; then - echo -n "$url_response" | wl-copy - echo "✓ Uploaded: $url_response (copied to clipboard)" - - if [[ -n "$token" ]]; then - echo "Management token: $token" - fi - - success=true - break - fi - fi - - (( attempt++ )) - done - - if ! $success; then - echo "× Failed to upload file after $max_attempts attempts" - return 1 - fi + cd "$original_dir" } +# Expose local port through SSH tunnel function expose() { - if [ -z "$1" ]; then + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: expose " + echo "Create SSH tunnel to expose local port through remote server" + echo "Example: expose 3000 # Exposes localhost:3000" + return 0 + fi + + if [[ -z "$1" ]]; then + echo "Error: Port number required" >&2 + echo "Usage: expose " >&2 return 1 fi - ssh marianozunino@srv.us -R 1:localhost:$1 + + # Validate port number + if ! [[ "$1" =~ ^[0-9]+$ ]] || [[ "$1" -lt 1 ]] || [[ "$1" -gt 65535 ]]; then + echo "Error: Invalid port number. Must be between 1-65535" >&2 + return 1 + fi + + # Check if port is already in use + if lsof -i ":$1" >/dev/null 2>&1; then + echo "Warning: Port $1 is already in use" >&2 + echo "Current processes using port $1:" + lsof -i ":$1" + echo -n "Continue anyway? [y/N] " + read confirm + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Operation cancelled" + return 1 + fi + fi + + echo "Creating SSH tunnel for port $1..." + ssh marianozunino@srv.us -R 1:localhost:"$1" } +# Find process using a specific port function ppid { - lsof -i :$1 + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: ppid " + echo "Find processes using the specified port" + echo "Alias: pport" + return 0 + fi + + if [[ -z "$1" ]]; then + echo "Error: Port number required" >&2 + echo "Usage: ppid " >&2 + return 1 + fi + + # Validate port number + if ! [[ "$1" =~ ^[0-9]+$ ]] || [[ "$1" -lt 1 ]] || [[ "$1" -gt 65535 ]]; then + echo "Error: Invalid port number. Must be between 1-65535" >&2 + return 1 + fi + + lsof -i ":$1" } +# Alias for ppid function alias pport="ppid" +# ============================================================================= # KUBERNETES AND DEVOPS +# ============================================================================= + +# Kubernetes port forwarding with kubefwd function kf { - if [ -z "$1" ]; then + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: kf [service-name]" + echo "Forward Kubernetes services using kubefwd" + echo "Example: kf prod my-service" + return 0 + fi + + if [[ -z "$1" ]]; then + echo "Error: Cluster name required" >&2 + echo "Usage: kf [service-name]" >&2 return 1 fi @@ -218,14 +306,23 @@ function kf { local cluster="oc-$1-eks-cluster" local svc_filter="" - if [ ! -z "$2" ]; then + if [[ -n "$2" ]]; then svc_filter="-l app.kubernetes.io/instance=$2" fi - sudo -E kubefwd svc -n ${namespace} -x ${cluster} ${svc_filter} + if ! command -v kubefwd >/dev/null 2>&1; then + echo "Error: kubefwd is not installed" >&2 + return 1 + fi + + echo "Starting kubefwd for cluster: $cluster" + sudo -E kubefwd svc -n "${namespace}" -x "${cluster}" ${svc_filter} } +# ============================================================================= # SYSTEM AND UTILITY FUNCTIONS +# ============================================================================= + # Lazy-load handler for heavy tools function _lazy_load() { local cmd="$1" @@ -241,102 +338,181 @@ function _lazy_load() { # Example: lazy load kubectl completion _lazy_load kubectl "kubectl completion zsh" +# Configure Wacom tablet for current session function wacom { - if [ "$XDG_SESSION_TYPE" = "wayland" ]; then - systemctl --user enable opentabletdriver --now - otd loadsettings ~/Sync/wacom/wacom.json - else - xsetwacom --set "Wacom One by Wacom S Pen stylus" ResetArea - xsetwacom --set "Wacom One by Wacom S Pen stylus" MapToOutput DisplayPort-0 - xsetwacom --set "Wacom One by Wacom S Pen stylus" Rotate half - - xsetwacom --set "Wacom One by Wacom S Pen eraser" ResetArea - xsetwacom --set "Wacom One by Wacom S Pen eraser" MapToOutput DisplayPort-0 + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: wacom" + echo "Configure Wacom tablet for current display session" + echo "Supports both Wayland and X11 sessions" + return 0 + fi + + if [[ "$XDG_SESSION_TYPE" = "wayland" ]]; then + if ! command -v otd >/dev/null 2>&1; then + echo "Error: opentabletdriver (otd) not found" >&2 + return 1 fi + + systemctl --user enable opentabletdriver --now + + local config_file="$HOME/Sync/System/Configs/wacom/wacom.json" + if [[ -f "$config_file" ]]; then + otd loadsettings "$config_file" + echo "Wacom configured for Wayland" + else + echo "Warning: Config file not found at $config_file" >&2 + fi + else + if ! command -v xsetwacom >/dev/null 2>&1; then + echo "Error: xsetwacom not found" >&2 + return 1 + fi + + xsetwacom --set "Wacom One by Wacom S Pen stylus" ResetArea + xsetwacom --set "Wacom One by Wacom S Pen stylus" MapToOutput DisplayPort-0 + xsetwacom --set "Wacom One by Wacom S Pen stylus" Rotate half + + xsetwacom --set "Wacom One by Wacom S Pen eraser" ResetArea + xsetwacom --set "Wacom One by Wacom S Pen eraser" MapToOutput DisplayPort-0 + + echo "Wacom configured for X11" + fi } +# Enhanced cat with bat, context-aware display function cat { - if [ -n "$SSH_TTY" ] || [ -n "$SSH_CLIENT" ] || [ -n "$SSH_CONNECTION" ]; then - bat --plain --paging=never "$@" - else - bat "$@" - fi + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: cat [file1] [file2] ..." + echo "Enhanced cat using bat with syntax highlighting" + echo "Automatically adjusts output for SSH sessions" + return 0 + fi + + if ! command -v bat >/dev/null 2>&1; then + echo "Warning: bat not found, falling back to system cat" >&2 + command cat "$@" + return $? + fi + + if [[ -n "$SSH_TTY" ]] || [[ -n "$SSH_CLIENT" ]] || [[ -n "$SSH_CONNECTION" ]]; then + bat --plain --paging=never "$@" + else + bat "$@" + fi } +# Toggle display resolution and configuration function toggle_resolution() { - local mirror_mode=false + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: toggle_resolution [-m|--mirror]" + echo "Toggle between different display resolutions and modes" + echo "Options:" + echo " -m, --mirror Force mirror mode" + return 0 + fi + + local mirror_mode=false - # Verificar flags - while [[ $# -gt 0 ]]; do - case $1 in - -m|--mirror) - mirror_mode=true - shift - ;; - *) - shift - ;; - esac - done + # Parse flags + while [[ $# -gt 0 ]]; do + case $1 in + -m|--mirror) + mirror_mode=true + shift + ;; + *) + shift + ;; + esac + done - # Obtener estado actual del monitor interno - current_state=$(swaymsg -t get_outputs | jq -r '.[] | select(.name == "eDP-1") | "\(.current_mode.width)x\(.current_mode.height):\(.scale)"') + # Check if swaymsg is available + if ! command -v swaymsg >/dev/null 2>&1; then + echo "Error: swaymsg not found. This function requires Sway window manager" >&2 + return 1 + fi + + # Check if jq is available + if ! command -v jq >/dev/null 2>&1; then + echo "Error: jq not found. Required for JSON parsing" >&2 + return 1 + fi - # Detectar monitores externos conectados - external_monitor="" - if swaymsg -t get_outputs | jq -r '.[].name' | grep -q "DP-2"; then - external_monitor="DP-2" - elif swaymsg -t get_outputs | jq -r '.[].name' | grep -q "HDMI-A-1"; then - external_monitor="HDMI-A-1" - fi + # Get current state of internal monitor + local current_state + current_state=$(swaymsg -t get_outputs | jq -r '.[] | select(.name == "eDP-1") | "\(.current_mode.width)x\(.current_mode.height):\(.scale)"') - if [[ "$mirror_mode" == true ]] || [[ -n "$external_monitor" ]]; then - echo "Switching to mirror mode (1920x1080)..." + # Detect connected external monitors + local external_monitor="" + if swaymsg -t get_outputs | jq -r '.[].name' | grep -q "DP-2"; then + external_monitor="DP-2" + elif swaymsg -t get_outputs | jq -r '.[].name' | grep -q "HDMI-A-1"; then + external_monitor="HDMI-A-1" + fi - # Configurar monitor interno a 1920x1080 - swaymsg output eDP-1 resolution 1920x1080@120Hz scale 1.0 pos 0 0 + if [[ "$mirror_mode" == true ]] || [[ -n "$external_monitor" ]]; then + echo "Switching to mirror mode (1920x1080)..." - # Configurar monitor externo si está conectado - if [[ -n "$external_monitor" ]]; then - swaymsg output "$external_monitor" resolution 1920x1080@60Hz pos 0 0 - echo "Mirror mode enabled: eDP-1 + $external_monitor at 1920x1080" - else - echo "Monitor interno configurado a 1920x1080 (sin monitor externo detectado)" - fi - - elif [[ "$current_state" == "2880x1920:2.0" ]]; then - echo "Switching to 1920x1080 (16:9)..." - swaymsg output eDP-1 resolution 1920x1080@120Hz scale 1.0 - - # Deshabilitar mirroring si hay monitor externo - if [[ -n "$external_monitor" ]]; then - swaymsg output "$external_monitor" pos 1920 0 - echo "Extended mode: $external_monitor positioned to the right" - fi + # Configure internal monitor to 1920x1080 + swaymsg output eDP-1 resolution 1920x1080@120Hz scale 1.0 pos 0 0 + # Configure external monitor if connected + if [[ -n "$external_monitor" ]]; then + swaymsg output "$external_monitor" resolution 1920x1080@60Hz pos 0 0 + echo "Mirror mode enabled: eDP-1 + $external_monitor at 1920x1080" else - echo "Switching to 2880x1920 (3:2) native..." - swaymsg output eDP-1 resolution 2880x1920@120Hz scale 2.0 - - # Deshabilitar mirroring si hay monitor externo - if [[ -n "$external_monitor" ]]; then - swaymsg output "$external_monitor" pos 1440 0 - echo "Extended mode: $external_monitor positioned to the right" - fi + echo "Internal monitor configured to 1920x1080 (no external monitor detected)" fi - # Mostrar estado final - echo "Current configuration:" - swaymsg -t get_outputs | jq -r '.[] | select(.active == true) | " \(.name): \(.current_mode.width)x\(.current_mode.height)@\(.current_mode.refresh/1000)Hz scale:\(.scale) pos:(\(.rect.x),\(.rect.y))"' + elif [[ "$current_state" == "2880x1920:2.0" ]]; then + echo "Switching to 1920x1080 (16:9)..." + swaymsg output eDP-1 resolution 1920x1080@120Hz scale 1.0 + + # Disable mirroring if external monitor is present + if [[ -n "$external_monitor" ]]; then + swaymsg output "$external_monitor" pos 1920 0 + echo "Extended mode: $external_monitor positioned to the right" + fi + + else + echo "Switching to 2880x1920 (3:2) native..." + swaymsg output eDP-1 resolution 2880x1920@120Hz scale 2.0 + + # Disable mirroring if external monitor is present + if [[ -n "$external_monitor" ]]; then + swaymsg output "$external_monitor" pos 1440 0 + echo "Extended mode: $external_monitor positioned to the right" + fi + fi + + # Show final state + echo "Current configuration:" + swaymsg -t get_outputs | jq -r '.[] | select(.active == true) | " \(.name): \(.current_mode.width)x\(.current_mode.height)@\(.current_mode.refresh/1000)Hz scale:\(.scale) pos:(\(.rect.x),\(.rect.y))"' } +# Launch Code::Blocks with high contrast theme function cb { + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: cb [codeblocks-args]" + echo "Launch Code::Blocks with high contrast theme" + return 0 + fi + + if [[ ! -f /usr/bin/codeblocks ]]; then + echo "Error: Code::Blocks not found at /usr/bin/codeblocks" >&2 + return 1 + fi + export GTK_THEME=HighContrast export GDK_THEME=HighContrast export QT_STYLE_OVERRIDE=HighContrast export XDG_CURRENT_DESKTOP=GNOME - # Reiniciar configuración GTK + # Reset GTK configuration unset GTK2_RC_FILES unset GTK_RC_FILES @@ -344,9 +520,26 @@ function cb { } +# Create a new git repository on remote server function zrepo() { - if [ -z "$1" ]; then - echo "Usage: zrepo " + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: zrepo " + echo "Create a new bare git repository on remote server" + echo "Example: zrepo my-project" + return 0 + fi + + if [[ -z "$1" ]]; then + echo "Error: Repository name required" >&2 + echo "Usage: zrepo " >&2 + return 1 + fi + + # Validate repository name (basic check) + if [[ ! "$1" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "Error: Repository name contains invalid characters" >&2 + echo "Use only letters, numbers, hyphens, and underscores" >&2 return 1 fi @@ -354,40 +547,65 @@ function zrepo() { local SERVER="git@zvps" local PATH_ON_SERVER="/var/git/$REPO.git" - ssh $SERVER " + echo "Creating repository '$REPO' on $SERVER..." + + ssh "$SERVER" " if [ -d $PATH_ON_SERVER ]; then - echo 'Error: repo already exists' + echo 'Error: Repository '$REPO' already exists' exit 1 fi mkdir -p $PATH_ON_SERVER && git init --bare $PATH_ON_SERVER && chown -R git:git $PATH_ON_SERVER && chmod -R 755 $PATH_ON_SERVER - " || return 1 + " || { + echo "Failed to create repository" >&2 + return 1 + } - echo "Repo created on $SERVER:$PATH_ON_SERVER" + echo "Repository created on $SERVER:$PATH_ON_SERVER" if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then git remote add origin "$SERVER:$PATH_ON_SERVER" - echo "Remote 'origin' added to your local repo." + echo "Remote 'origin' added to your local repository" else - echo "Run 'git clone $SERVER:$PATH_ON_SERVER' to clone the repo." + echo "To clone this repository, run:" + echo " git clone $SERVER:$PATH_ON_SERVER" fi } +# Check if a command exists function _has { - return $( whence $1 >/dev/null ) + if [[ $# -eq 0 ]]; then + echo "Usage: _has " >&2 + return 1 + fi + + whence "$1" >/dev/null 2>&1 } -# History-based directory navigation +# History-based directory navigation with fzf function cdr { + # Show help if requested + if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: cdr" + echo "Navigate to recently visited directories using fzf" + return 0 + fi + + if ! command -v fzf >/dev/null 2>&1; then + echo "Error: fzf is not installed" >&2 + return 1 + fi + local dir dir=$(dirs -pl | awk '!x[$0]++' | fzf --height 40% --reverse) [[ -n "$dir" ]] && cd "$dir" } +# Fix swaymsg when running inside tmux if [[ -v TMUX ]]; then function swaymsg { export SWAYSOCK=$XDG_RUNTIME_DIR/sway-ipc.$UID.$(pgrep -x sway).sock @@ -395,24 +613,19 @@ if [[ -v TMUX ]]; then } fi -# COMPLETIONS -for completion_file in ~/.local/share/zsh/*-autocomplete.zsh; do - echo $completion_file - if [ -f "$completion_file" ]; then - source "$completion_file" - fi -done -if command -v rop &> /dev/null; then eval "$(rop completion zsh)"; fi -if command -v goq &> /dev/null; then eval "$(goq completion zsh)"; fi -if command -v kubefwd &> /dev/null; then eval "$(kubefwd completion zsh)"; fi -if command -v bombadil &> /dev/null; then eval "$(bombadil generate-completions zsh)"; fi -if command -v eza &> /dev/null; then compdef eza=ls; fi +# ============================================================================= +# ALIASES +# ============================================================================= +# Development aliases alias dev='~/Dev/marianozunino/dev/dev' alias p='_package_manager' alias fo='fopen' -alias drop='upload_file' -alias dump='upload_file' -alias pport=ppid -alias unChonk="echo '🧹 Starting the great node_modules purge...' && find . -name 'node_modules' -type d -prune -exec sh -c 'echo \"💥 Deleting {}\" && rm -rf \"{}\"' \;" +alias pport='ppid' + +# Node.js cleanup alias +alias unChonk="echo 'Starting the great node_modules purge...' && find . -name 'node_modules' -type d -prune -exec sh -c 'echo \"Deleting {}\" && rm -rf \"{}\"' \;" + + + diff --git a/zsh/.config/zsh/init.zsh b/zsh/.config/zsh/init.zsh index 74363c6..77c35ec 100644 --- a/zsh/.config/zsh/init.zsh +++ b/zsh/.config/zsh/init.zsh @@ -1,14 +1,7 @@ PLUGIN_DIR="$HOME/.local/share/zsh/plugins" -PLUGIN_LOCK="$PLUGIN_DIR/.plugins.lock" -[ -d "$PLUGIN_DIR" ] || mkdir -p "$PLUGIN_DIR" +mkdir -p "$PLUGIN_DIR" -# Reinstall function -update-plugins() { - rm -fr "$PLUGIN_DIR" - echo "Plugin lock removed. Restart your shell to reinstall plugins." -} - -# Define plugins: "github_repo name" +# Install plugins (only if missing) plugins=( "romkatv/zsh-defer" "zsh-users/zsh-autosuggestions" @@ -21,28 +14,64 @@ plugins=( "zsh-users/zsh-syntax-highlighting" ) -# Only install plugins if lock file doesn't exist -if [ ! -f "$PLUGIN_LOCK" ]; then +# Plugin management function +zap() { + echo "⚡ Updating plugins..." + + # Update plugins sequentially (clean output) for plugin in "${plugins[@]}"; do plugin_name=${plugin##*/} + if [ ! -d "$PLUGIN_DIR/$plugin_name" ]; then echo "Installing $plugin_name" git clone --quiet "https://github.com/$plugin" "$PLUGIN_DIR/$plugin_name" --depth=1 + else + old_hash=$(git -C "$PLUGIN_DIR/$plugin_name" rev-parse HEAD 2>/dev/null) + + git -C "$PLUGIN_DIR/$plugin_name" fetch --quiet + git -C "$PLUGIN_DIR/$plugin_name" reset --hard origin/HEAD --quiet + + new_hash=$(git -C "$PLUGIN_DIR/$plugin_name" rev-parse HEAD 2>/dev/null) + + if [ "$old_hash" = "$new_hash" ]; then + echo "✓ $plugin_name (up to date)" + else + echo "↑ $plugin_name (${old_hash:0:7} → ${new_hash:0:7})" + fi fi done - date > "$PLUGIN_LOCK" -fi + + for dir in "$PLUGIN_DIR"/*; do + if [ -d "$dir" ]; then + plugin_name=$(basename "$dir") + if [[ ! " ${plugins[@]##*/} " =~ " $plugin_name " ]]; then + echo "Removing unused plugin: $plugin_name" + rm -rf "$dir" + fi + fi + done + + echo "Plugin update complete!" +} + +for plugin in "${plugins[@]}"; do + plugin_name=${plugin##*/} + [ -d "$PLUGIN_DIR/$plugin_name" ] || git clone --quiet "https://github.com/$plugin" "$PLUGIN_DIR/$plugin_name" --depth=1 +done # Source plugins source "$PLUGIN_DIR/zsh-defer/zsh-defer.plugin.zsh" -source "$PLUGIN_DIR/zsh-autosuggestions/zsh-autosuggestions.zsh" -source "$PLUGIN_DIR/zsh-autopair/autopair.zsh" -source "$PLUGIN_DIR/zsh-history-substring-search/zsh-history-substring-search.zsh" -source "$PLUGIN_DIR/fzf-tab/fzf-tab.plugin.zsh" -source "$PLUGIN_DIR/fzf/shell/completion.zsh" -source "$PLUGIN_DIR/fzf/shell/key-bindings.zsh" + +# Defer heavy plugins for faster startup +zsh-defer source "$PLUGIN_DIR/zsh-autosuggestions/zsh-autosuggestions.zsh" +zsh-defer source "$PLUGIN_DIR/zsh-autopair/autopair.zsh" +zsh-defer source "$PLUGIN_DIR/zsh-history-substring-search/zsh-history-substring-search.zsh" +zsh-defer source "$PLUGIN_DIR/fzf-tab/fzf-tab.plugin.zsh" +zsh-defer source "$PLUGIN_DIR/fzf/shell/completion.zsh" +zsh-defer source "$PLUGIN_DIR/fzf/shell/key-bindings.zsh" +zsh-defer source "$PLUGIN_DIR/vim/vim.plugin.zsh" + source "$PLUGIN_DIR/minimal/minimal.zsh" -source "$PLUGIN_DIR/vim/vim.plugin.zsh" # Source config files source "$ZDOTDIR/opts.zsh" @@ -51,6 +80,6 @@ source "$ZDOTDIR/maintenance.zsh" # Defer loading zsh-defer source "$PLUGIN_DIR/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" -for config in mise.zsh tmux.zsh functions.zsh alias.zsh keymap.zsh path.zsh pnpm.zsh opts.zsh; do +for config in mise.zsh tmux.zsh functions.zsh alias.zsh keymap.zsh path.zsh pnpm.zsh; do zsh-defer source "$ZDOTDIR/$config" -done +done \ No newline at end of file diff --git a/zsh/.config/zsh/mise.zsh b/zsh/.config/zsh/mise.zsh index 57e94cc..19bc3a1 100644 --- a/zsh/.config/zsh/mise.zsh +++ b/zsh/.config/zsh/mise.zsh @@ -1,9 +1,8 @@ # Source it eval "$($HOME/.local/bin/mise activate zsh)" -# Install mise if not present -if ! command -v mise &> /dev/null -then +# Install mise if not present (with caching to avoid repeated checks) +if ! command -v mise &> /dev/null && [[ ! -f ~/.local/bin/mise ]]; then echo "Installing mise..." mkdir -p ~/.local/bin curl https://mise.jdx.dev/mise-latest-linux-x64 > ~/.local/bin/mise diff --git a/zsh/.config/zsh/opts.zsh b/zsh/.config/zsh/opts.zsh index d70c99e..1ed140f 100644 --- a/zsh/.config/zsh/opts.zsh +++ b/zsh/.config/zsh/opts.zsh @@ -13,6 +13,7 @@ setopt HIST_FCNTL_LOCK setopt SHARE_HISTORY setopt AUTO_CD +# History file configuration - per host HISTORY_IGNORE='(*.git/hooks*)' LESSHISTFILE="-" HISTFILE="$ZDOTDIR/zsh_history_$HOST" @@ -20,6 +21,21 @@ HISTFILESIZE=100000 HISTSIZE=100000 SAVEHIST=100000 +# Ensure history file exists and has proper permissions +if [[ ! -f "$HISTFILE" ]]; then + touch "$HISTFILE" + chmod 644 "$HISTFILE" # More permissive permissions +fi + +# Additional history safeguards +# Backup history on each session start (only if file is significantly large) +if [[ -f "$HISTFILE" && -w "$HISTFILE" ]]; then + # Create backup if history file is large enough (> 10KB) to avoid frequent small backups + if [[ $(stat -f%z "$HISTFILE" 2>/dev/null || stat -c%s "$HISTFILE" 2>/dev/null) -gt 10240 ]]; then + cp "$HISTFILE" "${HISTFILE}.bak" 2>/dev/null || true + fi +fi + MNML_INFOLN=() MNML_PROMPT=(mnml_ssh mnml_status 'mnml_cwd 2 0' mnml_git mnml_keymap ) MNML_RPROMPT=() diff --git a/zsh/.config/zsh/zsh_history_main b/zsh/.config/zsh/zsh_history_main index cd0deb3..da3e478 100644 Binary files a/zsh/.config/zsh/zsh_history_main and b/zsh/.config/zsh/zsh_history_main differ