diff --git a/local-bin/.local/bin/dev-launcher b/local-bin/.local/bin/dev-launcher new file mode 100755 index 0000000..82fba9f --- /dev/null +++ b/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 </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" diff --git a/local-bin/.local/bin/launch-or-focus b/local-bin/.local/bin/launch-or-focus new file mode 100755 index 0000000..6956f30 --- /dev/null +++ b/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 [args...] + +if [ $# -eq 0 ]; then + echo "Usage: $0 [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 diff --git a/noctalia/.config/noctalia/colors.json b/noctalia/.config/noctalia/colors.json index 51e6f4f..18521c0 100644 --- a/noctalia/.config/noctalia/colors.json +++ b/noctalia/.config/noctalia/colors.json @@ -1,6 +1,8 @@ { "mError": "#eb6f92", + "mHover": "#524f67", "mOnError": "#191724", + "mOnHover": "#e0def4", "mOnPrimary": "#191724", "mOnSecondary": "#191724", "mOnSurface": "#e0def4", diff --git a/noctalia/.config/noctalia/settings.json b/noctalia/.config/noctalia/settings.json index da7ab2a..deb8e83 100644 --- a/noctalia/.config/noctalia/settings.json +++ b/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": { diff --git a/sway/.config/sway/config.d/autostart b/sway/.config/sway/config.d/autostart index 9704a2c..80bc007 100644 --- a/sway/.config/sway/config.d/autostart +++ b/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 } diff --git a/sway/.config/sway/config.d/keybindings b/sway/.config/sway/config.d/keybindings index 6334fb0..d9abf4d 100644 --- a/sway/.config/sway/config.d/keybindings +++ b/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 }