From 9dca029a620cde4104231bb0fe02f2d76e835f75 Mon Sep 17 00:00:00 2001 From: "Mariano Z." Date: Sun, 16 Nov 2025 15:13:37 -0300 Subject: [PATCH] dev: automated commit - 2025-11-16 11:58:51 --- local-bin/.local/bin/dev-launcher | 8 +- local-bin/.local/bin/launch-or-focus | 231 +++++++++---------------- ssh/.ssh/config | Bin 573 -> 662 bytes sway/.config/sway/config.d/autostart | 19 +- sway/.config/sway/config.d/keybindings | 22 +-- 5 files changed, 102 insertions(+), 178 deletions(-) diff --git a/local-bin/.local/bin/dev-launcher b/local-bin/.local/bin/dev-launcher index ff4d54c..bcc6c57 100755 --- a/local-bin/.local/bin/dev-launcher +++ b/local-bin/.local/bin/dev-launcher @@ -5,7 +5,7 @@ set -euo pipefail CACHE_MAX_AGE=300 MRU_SIZE=20 -# Debug logging (set DEBUG=1 to enable) +# Debug logging (set DEBUG=1 to enable via environment variable) DEBUG="${DEBUG:-0}" debug() { @@ -87,7 +87,7 @@ find_git_repos() { find "$DEV_DIR" -maxdepth 3 -type d -name ".git" -print0 | xargs -0 -n1 dirname | sort -u >"$CACHE_FILE" fi - local repo_count=$(wc -l < "$CACHE_FILE") + local repo_count=$(wc -l <"$CACHE_FILE") debug "Found $repo_count repositories" cat "$CACHE_FILE" } @@ -200,9 +200,9 @@ debug "Class name: $CLASS_NAME" debug "Launching ghostty with launch-or-focus..." launch-or-focus ghostty \ + --class "$CLASS_NAME" \ + --title "$PROJECT_NAME" \ --working-directory="$SELECTED" \ - --title="$PROJECT_NAME" \ - --class="$CLASS_NAME" \ -e bash -c "if tmux has-session -t '$SESSION_NAME' 2>/dev/null; then \ tmux attach -t '$SESSION_NAME'; \ else \ diff --git a/local-bin/.local/bin/launch-or-focus b/local-bin/.local/bin/launch-or-focus index 720abf3..17e94cd 100755 --- a/local-bin/.local/bin/launch-or-focus +++ b/local-bin/.local/bin/launch-or-focus @@ -1,167 +1,96 @@ #!/usr/bin/env bash set -euo pipefail -# Script to launch an application or focus it if already running in sway -# Usage: launch-or-focus [args...] -# Set DEBUG=1 to enable debug logging +# Simple script to launch an application or focus it if already running in sway +# Usage: launch-or-focus [--class CLASS] [--title TITLE] [args...] +# +# Examples: +# launch-or-focus firefox +# launch-or-focus code --class Code --title "My Project" +# launch-or-focus kitty --class kitty -DEBUG="${DEBUG:-0}" +CLASS="" +TITLE="" +ARGS=() -debug() { - [ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2 || true -} +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --class) + CLASS="$2" + shift 2 + ;; + --title) + TITLE="$2" + shift 2 + ;; + *) + ARGS+=("$1") + shift + ;; + esac +done -if [ $# -eq 0 ]; then - echo "Usage: $0 [args...]" >&2 +if [ ${#ARGS[@]} -eq 0 ]; then + echo "Usage: $0 [--class CLASS] [--title TITLE] [args...]" >&2 exit 1 fi -COMMAND="$1" -shift -ARGS=("$@") - +COMMAND="${ARGS[0]}" APP_NAME=$(basename "$COMMAND") -debug "Command: $COMMAND" -debug "App name: $APP_NAME" -debug "Args: ${ARGS[*]}" +unset 'ARGS[0]' +ARGS=("${ARGS[@]}") -# Extract --class and --title values from args if present -# Handle both --class VALUE and --class=VALUE formats -CLASS_NAME="" -TITLE_NAME="" -for i in "${!ARGS[@]}"; do - if [[ "${ARGS[$i]}" =~ ^--class=(.+)$ ]]; then - CLASS_NAME="${BASH_REMATCH[1]}" - debug "Found class (format --class=VALUE): $CLASS_NAME" - elif [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then - CLASS_NAME="${ARGS[$((i+1))]}" - debug "Found class (format --class VALUE): $CLASS_NAME" - elif [[ "${ARGS[$i]}" =~ ^--title=(.+)$ ]]; then - TITLE_NAME="${BASH_REMATCH[1]}" - debug "Found title (format --title=VALUE): $TITLE_NAME" - elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then - TITLE_NAME="${ARGS[$((i+1))]}" - debug "Found title (format --title VALUE): $TITLE_NAME" - fi -done -debug "Extracted CLASS_NAME: '$CLASS_NAME'" -debug "Extracted TITLE_NAME: '$TITLE_NAME'" - -find_window_by_class() { - local class="$1" - debug "Searching for window by class: $class" - local result - result=$(swaymsg -t get_tree | jq -r --arg class "$class" ' - recurse(.nodes[]?, .floating_nodes[]?) | - select( - ((.window_properties.class | type) == "string" and .window_properties.class == $class) or - ((.app_id | type) == "string" and .app_id == $class) - ) | - .id - ' | head -n 1) - debug "find_window_by_class result: '$result'" - echo "$result" -} - -find_window_by_title() { - local title="$1" - local app="$2" - local result - - debug "Searching for window by title: '$title' and app: '$app'" - - # Search for windows matching both app and title (most specific) - result=$(swaymsg -t get_tree | jq -r --arg title "$title" --arg app "$app" ' - recurse(.nodes[]?, .floating_nodes[]?) | - select( - ( - ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or - ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i")))) - ) and - ((.name | type) == "string" and .name == $title) - ) | - .id - ' | head -n 1) - - debug "find_window_by_title (app+title) result: '$result'" - [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return - - # Fall back to title-only exact match - debug "Falling back to title-only exact match" - result=$(swaymsg -t get_tree | jq -r --arg title "$title" ' - recurse(.nodes[]?, .floating_nodes[]?) | - select((.name | type) == "string" and .name == $title) | - .id - ' | head -n 1) - - debug "find_window_by_title (title only) result: '$result'" - [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return - - # Final fallback: title-only case-insensitive match - debug "Falling back to title-only case-insensitive match" - result=$(swaymsg -t get_tree | jq -r --arg title "$title" ' - recurse(.nodes[]?, .floating_nodes[]?) | - select((.name | type) == "string" and (.name | test($title; "i"))) | - .id - ' | head -n 1) - debug "find_window_by_title (case-insensitive) result: '$result'" - echo "$result" -} +# If class/title were provided as flags, add them to command args (for apps that support them) +if [ -n "$CLASS" ]; then + ARGS=("--class=$CLASS" "${ARGS[@]}") +fi +if [ -n "$TITLE" ]; then + ARGS=("--title=$TITLE" "${ARGS[@]}") +fi +# Find window by app_id (sway uses app_id), title, or app name find_window() { - local app_name="$1" - debug "Searching for window by app name: $app_name" - local result - result=$(swaymsg -t get_tree | jq -r --arg app "$app_name" ' - recurse(.nodes[]?, .floating_nodes[]?) | - select( - ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or - ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i")))) or - ((.name | type) == "string" and (.name | test($app; "i"))) - ) | - .id - ' | head -n 1) - debug "find_window result: '$result'" - echo "$result" -} - -focus_window() { - local window_id="$1" - debug "Focusing window ID: $window_id" - swaymsg "[con_id=$window_id]" focus -} - -WINDOW_ID="" - -debug "Starting window search..." -# Only search by class/title if provided - don't fall back to generic app search -# This prevents matching the wrong window -if [ -n "$CLASS_NAME" ]; then - WINDOW_ID=$(find_window_by_class "$CLASS_NAME") - debug "After class search: WINDOW_ID='$WINDOW_ID'" -fi - -if ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ]; then - WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME") - debug "After title search: WINDOW_ID='$WINDOW_ID'" -fi - -# Only fall back to generic app search if we don't have class or title -# This means the user wants to focus any instance of the app -if ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -z "$CLASS_NAME" ] && [ -z "$TITLE_NAME" ]; then - WINDOW_ID=$(find_window "$APP_NAME") - debug "After app name search (no class/title): WINDOW_ID='$WINDOW_ID'" + local class="$1" + local title="$2" + local app="$3" - ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}") - debug "After lowercase app name search: WINDOW_ID='$WINDOW_ID'" + # If class provided, search by app_id first (sway uses app_id) + if [ -n "$class" ]; then + swaymsg -t get_tree | jq -r --arg class "$class" ' + recurse(.nodes[]?, .floating_nodes[]?) | + select((.app_id | type) == "string" and .app_id == $class) | + .id + ' | head -n 1 + return + fi + + # If only title provided, search by title + if [ -n "$title" ]; then + swaymsg -t get_tree | jq -r --arg title "$title" ' + recurse(.nodes[]?, .floating_nodes[]?) | + select((.name | type) == "string" and .name == $title) | + .id + ' | head -n 1 + return + fi + + # Fallback to app name (search by app_id) + swaymsg -t get_tree | jq -r --arg app "$app" ' + recurse(.nodes[]?, .floating_nodes[]?) | + select((.app_id | type) == "string" and .app_id == $app) | + .id + ' | head -n 1 +} + +# Try to find existing window +WINDOW_ID=$(find_window "$CLASS" "$TITLE" "$APP_NAME") + +# Focus if found, otherwise launch +if [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ]; then + swaymsg "[con_id=$WINDOW_ID]" focus +else + "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 & + disown fi -if [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null; then - debug "Window found! Focusing window ID: $WINDOW_ID" - focus_window "$WINDOW_ID" - exit 0 -fi - -debug "No matching window found, launching new instance" -"$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 & -disown diff --git a/ssh/.ssh/config b/ssh/.ssh/config index 21892347b7aeed0fc14bd07e41a7fb88dda44a20..809250a18c60c5ff7aeebc6cc538d0c6dfe8a858 100644 GIT binary patch literal 662 zcmV;H0%`pKM@dveQdv+`0LQt=kfOsj|g7MQ7y^Uyp4IsZX|2&`% zoSt*0Qj42)cie_6e!Zs#@-rcGXnrSvw$M{o$IH!syz7b#NP&hTO!I%2Takt#(- zBuV#OAs%w>A07P5JgXaaoNHm-PwC|?e)^KR32?#S^S(p82KRJ-BjO}nTy=mUhVJRF zy`418%~JB}hl@&OK}1JGv)7g6uo5)=qM?^!s56efJXn*mxZ0)ISt!9!@PEZTh#E5y zhp05z#geMBNyF1Tnch+GmRTq3VNX=Ilu|3hCw`M#G_&4eh!HqT?WJmX*)MwUbZIBF zw{TD~UTqnE`Qj^U%?AQ0IjcnOmV=g?ZeKhQORfSU8%R9fJtt@C27;n2D|^g?rg5oZpr-)(*R!ZfnqO zV&?C?$h81(WLvdAxVDP&Q=u#DzC{Ccroznjx{v)#V*yD(Qg#@3voeJvMB;jez<{yh zMFws;#c4Jf;gk9ADwhTbFQA+2z->TY&vP*3pedn_Xh098c3ZbJ53i-Q7^rdsiMq%G ztlz^{@u9}az|UAG2wtfnuj$!wu7$Lxi=*R-*m<0o3uLe>Av)kAv|wvH<1gR~oMS6~ z0cs>Z44vwB#D{Epx0suKTMzvlG5;m1ZE{pb?OM@dveQdv+`03&v_a>1t-t>*1#!YqwJWB19kqkpj1Rc$xrDT@RQx?s!< zYf(SOk!eD5tI4TTE0qI(RqV`c1s|u|XC^&qw@_s$v7iIC@&#nv2H-HqJdU<(?vbAu zIi3Y#0dq6p4BSMdgmm#VYzn5c1G%4ur|Rt$gFZ#VWccF}Q%%y!ZAXI2SJog7$ao=os_}cnlc*)VlB(&@9bZ`JcQY~8j zVJM_s9$l{)b1dmT8PZ;`9uyh%l+z@fB^(`Sep0Jj_twXr2GyBDmvR^*_zjZ#ih|;l zyd#nZX_h%BOR+R}L&TR%u5OxAz5EQuMJlnfp*m>!(|CGGL%mNmFWC^~3VQQag_90R zKb>fa^T|zzBZLY6y$;)&KdoQP7Y)d#5h>R4uK;gs(88o*OW@60Pb1sbe_BWDC7`qb z(llPyjZ(>@ysx|(p8^=w5``w3tHgnrzFWK40UidA7SPyvb1?jepbm;Fap;^cvEQ5C z1U57V>?-kk!S^A63!#92hC@39_U7x_l{^)A8o{$}w