Explorar o código

dev: automated commit - 2025-11-28 18:32:11

Mariano Z. hai 2 meses
pai
achega
d6e088278f

+ 272 - 0
local-bin/.local/bin/niri-dev-launcher

@@ -0,0 +1,272 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Configuration
+CACHE_MAX_AGE=300
+MRU_SIZE=5
+
+# Debug logging (set DEBUG=1 to enable via environment variable)
+DEBUG="${DEBUG:-0}"
+
+debug() {
+    [ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2 || true
+}
+
+# Parse arguments - flags first, then DEV_DIR
+NO_CACHE=false
+CLEAR_CACHE=false
+EDITOR="${EDITOR:-nvim}"
+DEV_DIR=""
+
+debug "Starting niri-dev-launcher with args: $*"
+
+for arg in "$@"; do
+    case "$arg" in
+    --help | -h)
+        cat <<EOF
+Usage: $0 [DEV_DIR] [OPTIONS]
+
+Options:
+  --no-cache      Bypass cache and rescan repositories
+  --clear-cache   Clear cache and MRU files
+  --help, -h      Show this help message
+EOF
+        exit 0
+        ;;
+    --no-cache)
+        NO_CACHE=true
+        ;;
+    --clear-cache)
+        CLEAR_CACHE=true
+        ;;
+    *)
+        [ -z "$DEV_DIR" ] && DEV_DIR="$arg"
+        ;;
+    esac
+done
+
+DEV_DIR="${DEV_DIR:-$HOME/Dev}"
+debug "DEV_DIR: $DEV_DIR"
+debug "NO_CACHE: $NO_CACHE"
+debug "CLEAR_CACHE: $CLEAR_CACHE"
+
+# Setup cache files (unique per dev directory)
+DEV_DIR_HASH=$(echo -n "$DEV_DIR" | sha256sum | cut -d' ' -f1 | head -c 16)
+CACHE_FILE="$HOME/.cache/niri-dev-launcher-cache-${DEV_DIR_HASH}"
+MRU_FILE="$HOME/.cache/niri-dev-launcher-mru-${DEV_DIR_HASH}"
+debug "CACHE_FILE: $CACHE_FILE"
+debug "MRU_FILE: $MRU_FILE"
+
+# Handle --clear-cache
+if [ "$CLEAR_CACHE" = true ]; then
+    rm -f "$CACHE_FILE" "$MRU_FILE"
+    notify-send "Niri Dev Launcher" "Cache cleared" 2>/dev/null || true
+    exit 0
+fi
+
+# Find git repositories
+find_git_repos() {
+    debug "find_git_repos: checking cache..."
+    if [ "$NO_CACHE" = false ] && [ -f "$CACHE_FILE" ]; then
+        local cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
+        debug "Cache age: ${cache_age}s (max: ${CACHE_MAX_AGE}s)"
+        if [ $cache_age -lt $CACHE_MAX_AGE ]; then
+            debug "Using cached repos"
+            cat "$CACHE_FILE"
+            return
+        fi
+        debug "Cache expired, rescanning..."
+    fi
+
+    debug "Scanning for git repositories in $DEV_DIR..."
+    if command -v fd >/dev/null 2>&1; then
+        debug "Using fd for scanning"
+        fd -H -t d "^\.git$" "$DEV_DIR" -d 3 -0 | xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
+    else
+        debug "Using find for scanning"
+        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")
+    debug "Found $repo_count repositories"
+    cat "$CACHE_FILE"
+}
+
+# Get sanitized project name for tmux session
+get_project_name() {
+    basename "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g'
+}
+
+# Update MRU list
+update_mru() {
+    local selected="$1"
+    local temp_file
+    temp_file=$(mktemp)
+
+    echo "$selected" >"$temp_file"
+    [ -f "$MRU_FILE" ] && grep -vFx "$selected" "$MRU_FILE" >>"$temp_file" || true
+    head -n "$MRU_SIZE" "$temp_file" >"$MRU_FILE"
+    rm -f "$temp_file"
+}
+
+# Sort by MRU (MRU items first with ⭐, then others)
+sort_by_mru() {
+    local all_repos="$1"
+    local mru_list
+    local temp_file
+
+    [ -f "$MRU_FILE" ] && mru_list=$(grep -v '^$' "$MRU_FILE") || mru_list=""
+
+    # If MRU list is empty, just return sorted repos
+    if [ -z "$mru_list" ]; then
+        echo "$all_repos" | sort
+        return
+    fi
+
+    # Output MRU items first with indicator
+    echo "$mru_list" | sed 's/^/⭐ /'
+
+    # Output non-MRU items using comm (fast set difference)
+    temp_file=$(mktemp)
+    echo "$all_repos" | sort >"$temp_file.all"
+    echo "$mru_list" | sort >"$temp_file.mru"
+    comm -23 "$temp_file.all" "$temp_file.mru" 2>/dev/null || cat "$temp_file.all"
+    rm -f "$temp_file.all" "$temp_file.mru"
+}
+
+# Find window by app-id in niri
+find_window_by_app_id() {
+    local app_id="$1"
+    debug "Searching for window with app-id: $app_id"
+
+    # Check if jq is available
+    if ! command -v jq >/dev/null 2>&1; then
+        debug "jq not available, falling back to grep method"
+        # Get windows list and search for matching app-id
+        niri msg windows 2>/dev/null | grep -A 1 "App ID: \"$app_id\"" | head -1 | grep -q "Window ID" || return 1
+        return 0
+    fi
+
+    # Get windows list from niri as JSON
+    local windows_json
+    windows_json=$(niri msg -j windows 2>/dev/null || echo "[]")
+
+    if [ -z "$windows_json" ] || [ "$windows_json" = "[]" ]; then
+        return 1
+    fi
+
+    # Use jq to find matching window
+    local window_id
+    window_id=$(echo "$windows_json" | jq -r --arg app_id "$app_id" '
+        .[] | select(.app_id | ascii_downcase == ($app_id | ascii_downcase)) | .id
+    ' | head -1)
+
+    if [ -n "$window_id" ] && [ "$window_id" != "null" ]; then
+        echo "$window_id"
+        return 0
+    fi
+
+    return 1
+}
+
+# Focus or launch alacritty window
+focus_or_launch_alacritty() {
+    local class_name="$1"
+    local title="$2"
+    local working_dir="$3"
+    local session_name="$4"
+
+    debug "focus_or_launch_alacritty: class=$class_name, title=$title, dir=$working_dir"
+
+    # Try to find existing window
+    local window_id
+    window_id=$(find_window_by_app_id "$class_name" || echo "")
+
+    if [ -n "$window_id" ]; then
+        debug "Found existing window, focusing..."
+        niri msg action focus-window --id "$window_id" >/dev/null 2>&1
+    else
+        debug "No existing window found, launching alacritty..."
+        alacritty \
+            --class="$class_name" \
+            --title="$title" \
+            --working-directory="$working_dir" \
+            -e bash -c "if tmux has-session -t '$session_name' 2>/dev/null; then \
+                tmux attach -t '$session_name'; \
+            else \
+                tmux new-session -c '$working_dir' -s '$session_name' -d '$EDITOR'; \
+                tmux split-window -h -c '$working_dir' -t '$session_name'; \
+                tmux select-pane -t '$session_name:0.0'; \
+                tmux attach -t '$session_name'; \
+            fi" >/dev/null 2>&1 &
+        disown
+    fi
+}
+
+# Main execution
+REPOS=$(find_git_repos)
+
+if [ -z "$REPOS" ]; then
+    debug "No repositories found!"
+    notify-send "Niri Dev Launcher" "No git repositories found in $DEV_DIR" 2>/dev/null || true
+    exit 1
+fi
+debug "Found repositories, proceeding..."
+
+# Build display list (relative paths) and mapping
+DISPLAY_LIST=$(echo "$REPOS" | sed "s|^${DEV_DIR}/||")
+declare -A DISPLAY_TO_PATH
+
+debug "Building display mapping..."
+while IFS=$'\t' read -r repo display; do
+    DISPLAY_TO_PATH["$display"]="$repo"
+done < <(paste <(echo "$REPOS") <(echo "$DISPLAY_LIST"))
+debug "Mapped ${#DISPLAY_TO_PATH[@]} projects"
+
+# Sort by MRU and present in fuzzel
+debug "Sorting by MRU..."
+SORTED_LIST=$(sort_by_mru "$DISPLAY_LIST")
+debug "Presenting in fuzzel..."
+
+# Configure fuzzel with similar style to original tofi
+SELECTED_DISPLAY=$(echo "$SORTED_LIST" | fuzzel \
+    --prompt "💀 Poison: " \
+    --dmenu \
+    --width 30 \
+    --lines 20 \
+    --border-width 2 \
+    --font "$(fc-match -f '%{family}:size=16' 2>/dev/null || echo 'sans:size=16')" \
+    --background-color '#191724ff' \
+    --text-color '#e0def4ff' \
+    --match-color '#31748fff' \
+    --selection-color '#1f1d2eff' \
+    --selection-text-color '#31748fff' \
+    --selection-match-color '#31748fff' \
+    --prompt-color '#f6c177ff')
+
+debug "Selected display: '$SELECTED_DISPLAY'"
+[ -z "$SELECTED_DISPLAY" ] && debug "No selection, exiting" && exit 0
+
+# Remove star indicator and lookup path
+SELECTED_DISPLAY_CLEAN=$(echo "$SELECTED_DISPLAY" | sed 's/^⭐ //')
+SELECTED="${DISPLAY_TO_PATH[$SELECTED_DISPLAY_CLEAN]:-}"
+
+debug "Selected display (clean): '$SELECTED_DISPLAY_CLEAN'"
+debug "Selected path: '$SELECTED'"
+
+[ -z "$SELECTED" ] || [ ! -d "$SELECTED" ] && debug "Invalid selection, exiting" && exit 0
+
+# Update MRU and launch
+debug "Updating MRU..."
+update_mru "$SELECTED_DISPLAY_CLEAN"
+
+PROJECT_NAME=$(get_project_name "$SELECTED")
+SESSION_NAME="dev-${PROJECT_NAME}"
+CLASS_NAME="com.mzunino.dev.${PROJECT_NAME}"
+
+debug "Project name: $PROJECT_NAME"
+debug "Session name: $SESSION_NAME"
+debug "Class name: $CLASS_NAME"
+debug "Focusing or launching alacritty..."
+
+focus_or_launch_alacritty "$CLASS_NAME" "$PROJECT_NAME" "$SELECTED" "$SESSION_NAME"

+ 109 - 0
local-bin/.local/bin/niri-launch-or-focus

@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Simple script to launch an application or focus it if already running in niri
+# Usage: niri-launch-or-focus <command> [--class CLASS] [--title TITLE] [args...]
+#
+# Examples:
+#   niri-launch-or-focus firefox
+#   niri-launch-or-focus code --class Code --title "My Project"
+#   niri-launch-or-focus kitty --class kitty
+
+CLASS=""
+TITLE=""
+ARGS=()
+
+# 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 [ ${#ARGS[@]} -eq 0 ]; then
+    echo "Usage: $0 <command> [--class CLASS] [--title TITLE] [args...]" >&2
+    exit 1
+fi
+
+COMMAND="${ARGS[0]}"
+APP_NAME=$(basename "$COMMAND")
+unset 'ARGS[0]'
+ARGS=("${ARGS[@]}")
+
+# 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 (niri uses app-id), title, or app name
+find_window() {
+    local class="$1"
+    local title="$2"
+    local app="$3"
+
+    # Check if jq is available
+    if ! command -v jq >/dev/null 2>&1; then
+        echo "Error: jq is required but not found. Please install jq." >&2
+        return 1
+    fi
+
+    # Get windows list from niri as JSON
+    local windows_json
+    windows_json=$(niri msg -j windows 2>/dev/null || echo "[]")
+
+    if [ -z "$windows_json" ] || [ "$windows_json" = "[]" ]; then
+        return 1
+    fi
+
+    # Use jq to find matching window
+    local window_id
+
+    if [ -n "$class" ]; then
+        # Match by class (app_id)
+        window_id=$(echo "$windows_json" | jq -r --arg class "$class" '
+            .[] | select(.app_id | ascii_downcase == ($class | ascii_downcase)) | .id
+        ' | head -1)
+    elif [ -n "$title" ]; then
+        # Match by title
+        window_id=$(echo "$windows_json" | jq -r --arg title "$title" '
+            .[] | select(.title == $title) | .id
+        ' | head -1)
+    else
+        # Match by app name (basename of command)
+        window_id=$(echo "$windows_json" | jq -r --arg app "$app" '
+            .[] | select(.app_id | ascii_downcase == ($app | ascii_downcase)) | .id
+        ' | head -1)
+    fi
+
+    if [ -n "$window_id" ] && [ "$window_id" != "null" ]; then
+        echo "$window_id"
+        return 0
+    fi
+
+    return 1
+}
+
+# Try to find existing window
+WINDOW_ID=$(find_window "$CLASS" "$TITLE" "$APP_NAME" || echo "")
+
+# Focus if found, otherwise launch
+if [ -n "$WINDOW_ID" ]; then
+    niri msg action focus-window --id "$WINDOW_ID" >/dev/null 2>&1
+else
+    "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
+    disown
+fi

+ 18 - 0
local-bin/.local/bin/niri-scratchpad.sh

@@ -0,0 +1,18 @@
+#!/run/current-system/sw/bin/bash
+
+# Check if scratchpad terminal is already running
+if pgrep -f "kitty.*scratchpad" > /dev/null; then
+    # Get the window ID of the scratchpad terminal
+    WINDOW_ID=$(niri msg windows | grep -A 10 'app-id="scratchpad"' | head -1 | grep 'Window ID' | cut -d' ' -f3 | cut -d: -f1)
+    
+    if [ -n "$WINDOW_ID" ]; then
+        # Focus the existing scratchpad window
+        niri msg action focus-window "$WINDOW_ID"
+    else
+        # If we can't find the window ID, spawn a new one
+        kitty --class scratchpad --title scratchpad &
+    fi
+else
+    # Spawn a new scratchpad terminal
+    kitty --class scratchpad --title scratchpad &
+fi