浏览代码

dev: automated commit - 2025-11-10 10:37:03

Mariano Z. 3 月之前
父节点
当前提交
5a71b7bdab

+ 167 - 0
local-bin/.local/bin/dev-launcher

@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Configuration
+CACHE_MAX_AGE=300
+MRU_SIZE=20
+
+# Parse arguments - flags first, then DEV_DIR
+NO_CACHE=false
+CLEAR_CACHE=false
+EDITOR="${EDITOR:-nvim}"
+DEV_DIR=""
+
+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}"
+
+# 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/dev-launcher-cache-${DEV_DIR_HASH}"
+MRU_FILE="$HOME/.cache/dev-launcher-mru-${DEV_DIR_HASH}"
+
+# Handle --clear-cache
+if [ "$CLEAR_CACHE" = true ]; then
+    rm -f "$CACHE_FILE" "$MRU_FILE"
+    notify-send "Dev Launcher" "Cache cleared" 2>/dev/null || true
+    exit 0
+fi
+
+# Find git repositories
+find_git_repos() {
+    if [ "$NO_CACHE" = false ] && [ -f "$CACHE_FILE" ]; then
+        local cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
+        [ $cache_age -lt $CACHE_MAX_AGE ] && cat "$CACHE_FILE" && return
+    fi
+    
+    if command -v fd >/dev/null 2>&1; then
+        fd -H -t d "^\.git$" "$DEV_DIR" -d 3 -0 | xargs -0 -n1 dirname | sort -u > "$CACHE_FILE"
+    else
+        find "$DEV_DIR" -maxdepth 3 -type d -name ".git" -print0 | \
+            xargs -0 -n1 dirname | sort -u > "$CACHE_FILE"
+    fi
+    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
+    temp_file=$(mktemp)
+    
+    [ -f "$MRU_FILE" ] && mru_list=$(cat "$MRU_FILE" | grep -v '^$') || mru_list=""
+    
+    # Output MRU items first with indicator
+    echo "$mru_list" | sed 's/^/⭐ /'
+    
+    # Output non-MRU items using comm (fast set difference)
+    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"
+}
+
+# Main execution
+REPOS=$(find_git_repos)
+
+[ -z "$REPOS" ] && notify-send "Dev Launcher" "No git repositories found in $DEV_DIR" 2>/dev/null && exit 1
+
+# Build display list (relative paths) and mapping
+DISPLAY_LIST=$(echo "$REPOS" | sed "s|^${DEV_DIR}/||")
+declare -A DISPLAY_TO_PATH
+
+while IFS=$'\t' read -r repo display; do
+    DISPLAY_TO_PATH["$display"]="$repo"
+done < <(paste <(echo "$REPOS") <(echo "$DISPLAY_LIST"))
+
+# Sort by MRU and present in tofi
+SORTED_LIST=$(sort_by_mru "$DISPLAY_LIST")
+SELECTED_DISPLAY=$(echo "$SORTED_LIST" | tofi \
+    --prompt-text "💀 Poison: " \
+    --fuzzy-match true \
+    --width 30% \
+    --height 50% \
+    --anchor center \
+    --padding-left 20 \
+    --padding-right 20 \
+    --padding-top 15 \
+    --padding-bottom 15 \
+    --border-width 2 \
+    --outline-width 0 \
+    --font "$(fc-match -f '%{family}' 2>/dev/null || echo 'sans')" \
+    --font-size 14 \
+    --background-color '#191724' \
+    --text-color '#e0def4' \
+    --selection-color '#31748f' \
+    --selection-background '#1f1d2e' \
+    --border-color '#31748f' \
+    --prompt-color '#f6c177')
+
+[ -z "$SELECTED_DISPLAY" ] && exit 0
+
+# Remove star indicator and lookup path
+SELECTED_DISPLAY_CLEAN=$(echo "$SELECTED_DISPLAY" | sed 's/^⭐ //')
+SELECTED="${DISPLAY_TO_PATH[$SELECTED_DISPLAY_CLEAN]:-}"
+
+[ -z "$SELECTED" ] || [ ! -d "$SELECTED" ] && exit 0
+
+# Update MRU and launch
+update_mru "$SELECTED_DISPLAY_CLEAN"
+
+PROJECT_NAME=$(get_project_name "$SELECTED")
+SESSION_NAME="dev-${PROJECT_NAME}"
+
+launch-or-focus ghostty \
+    --working-directory="$SELECTED" \
+    --title="$PROJECT_NAME" \
+    --class="$PROJECT_NAME" \
+    -e bash -c "if tmux has-session -t '$SESSION_NAME' 2>/dev/null; then \
+        tmux attach -t '$SESSION_NAME'; \
+    else \
+        tmux new-session -c '$SELECTED' -s '$SESSION_NAME' -d '$EDITOR'; \
+        tmux split-window -h -c '$SELECTED' -t '$SESSION_NAME'; \
+        tmux select-pane -t '$SESSION_NAME:0.0'; \
+        tmux attach -t '$SESSION_NAME'; \
+    fi"

+ 106 - 0
local-bin/.local/bin/launch-or-focus

@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Script to launch an application or focus it if already running in sway
+# Usage: launch-or-focus <command> [args...]
+
+if [ $# -eq 0 ]; then
+    echo "Usage: $0 <command> [args...]" >&2
+    exit 1
+fi
+
+COMMAND="$1"
+shift
+ARGS=("$@")
+
+APP_NAME=$(basename "$COMMAND")
+
+# Extract --class and --title values from args if present
+CLASS_NAME=""
+TITLE_NAME=""
+for i in "${!ARGS[@]}"; do
+    if [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
+        CLASS_NAME="${ARGS[$((i+1))]}"
+    elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
+        TITLE_NAME="${ARGS[$((i+1))]}"
+    fi
+done
+
+find_window_by_class() {
+    local class="$1"
+    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
+}
+
+find_window_by_title() {
+    local title="$1"
+    local app="$2"
+    local result
+    
+    # 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)
+    
+    [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
+    
+    # Fall 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)
+    
+    [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
+    
+    # Final fallback: title-only case-insensitive match
+    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
+}
+
+find_window() {
+    local app_name="$1"
+    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
+}
+
+focus_window() {
+    local window_id="$1"
+    swaymsg "[con_id=$window_id]" focus
+}
+
+WINDOW_ID=""
+
+[ -n "$CLASS_NAME" ] && WINDOW_ID=$(find_window_by_class "$CLASS_NAME")
+([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ] && WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME")
+([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "$APP_NAME")
+([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}")
+
+[ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null && focus_window "$WINDOW_ID" && exit 0
+
+"$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
+disown

+ 2 - 0
noctalia/.config/noctalia/colors.json

@@ -1,6 +1,8 @@
 {
     "mError": "#eb6f92",
+    "mHover": "#524f67",
     "mOnError": "#191724",
+    "mOnHover": "#e0def4",
     "mOnPrimary": "#191724",
     "mOnSecondary": "#191724",
     "mOnSurface": "#e0def4",

+ 9 - 3
noctalia/.config/noctalia/settings.json

@@ -16,6 +16,7 @@
         "mprisBlacklist": [
         ],
         "preferredPlayer": "",
+        "visualizerQuality": "high",
         "visualizerType": "linear",
         "volumeOverdrive": false,
         "volumeStep": 5
@@ -73,7 +74,8 @@
                     "showDiskUsage": false,
                     "showMemoryAsPercent": false,
                     "showMemoryUsage": true,
-                    "showNetworkStats": false
+                    "showNetworkStats": false,
+                    "usePrimaryColor": true
                 },
                 {
                     "id": "ScreenRecorder"
@@ -96,6 +98,7 @@
                     "usePrimaryColor": true
                 },
                 {
+                    "colorizeDistroLogo": false,
                     "customIconPath": "",
                     "icon": "noctalia",
                     "id": "ControlCenter",
@@ -105,6 +108,7 @@
                     "blacklist": [
                     ],
                     "colorizeIcons": false,
+                    "drawerEnabled": true,
                     "favorites": [
                     ],
                     "id": "Tray"
@@ -248,6 +252,7 @@
         "backgroundOpacity": 1,
         "criticalUrgencyDuration": 15,
         "doNotDisturb": false,
+        "enabled": true,
         "location": "top_right",
         "lowUrgencyDuration": 3,
         "monitors": [
@@ -275,7 +280,7 @@
         "videoCodec": "h264",
         "videoSource": "portal"
     },
-    "settingsVersion": 18,
+    "settingsVersion": 21,
     "setupCompleted": true,
     "templates": {
         "alacritty": false,
@@ -296,6 +301,7 @@
         "kitty": false,
         "pywalfox": false,
         "qt": false,
+        "spicetify": false,
         "vicinae": false,
         "walker": false,
         "wezterm": false
@@ -306,7 +312,7 @@
         "fontFixed": "DejaVu Sans Mono",
         "fontFixedScale": 1,
         "panelsAttachedToBar": true,
-        "panelsOverlayLayer": false,
+        "settingsPanelAttachToBar": false,
         "tooltipsEnabled": true
     },
     "wallpaper": {

+ 2 - 0
sway/.config/sway/config.d/autostart

@@ -28,4 +28,6 @@ exec {
     obsidian
     localsend_app
     vesktop
+    gtk-launch com.microsoft.Edge.flextop.msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default
+    gtk-launch com.microsoft.Edge.flextop.msedge-faolnafnngnfdaknnbpnkhgohbobgegn-Default
 }

+ 3 - 3
sway/.config/sway/config.d/keybindings

@@ -16,11 +16,11 @@ bindgesture swipe:down workspace prev
 bindsym {
     $mod+Return   exec $term
     $mod+d        exec ~/.config/tofi/scripts/drun.sh
-    $mod+t        exec ~/.local/bin/code.sh -s ~/.local/bin/rofi.yaml
-    $mod+shift+t  exec ~/.local/bin/present
+    $mod+t        exec ~/.local/bin/dev-launcher ~/Dev
+    $mod+Shift+t  exec ~/.local/bin/dev-launcher --no-cache ~/Dev
     $mod+n        exec ~/.local/bin/sdm-ui.sh dmenu
     $mod+o        exec ~/.local/bin/launch-or-focus obsidian "cd /home/forbi/Documents/Vault && $term --class obsidian nvim"
-    $mod+e        exec pcmanfm
+    $mod+e        exec ~/.local/bin/launch-or-focus thunar
     $mod+y        exec clapboard
     $mod+b        exec rofi-rbw --action copy
 }