dev: automated commit - 2025-11-10 10:37:03
This commit is contained in:
parent
1ce29c0997
commit
5a71b7bdab
6 changed files with 289 additions and 6 deletions
167
local-bin/.local/bin/dev-launcher
Executable file
167
local-bin/.local/bin/dev-launcher
Executable file
|
|
@ -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
local-bin/.local/bin/launch-or-focus
Executable file
106
local-bin/.local/bin/launch-or-focus
Executable file
|
|
@ -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
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
"mError": "#eb6f92",
|
"mError": "#eb6f92",
|
||||||
|
"mHover": "#524f67",
|
||||||
"mOnError": "#191724",
|
"mOnError": "#191724",
|
||||||
|
"mOnHover": "#e0def4",
|
||||||
"mOnPrimary": "#191724",
|
"mOnPrimary": "#191724",
|
||||||
"mOnSecondary": "#191724",
|
"mOnSecondary": "#191724",
|
||||||
"mOnSurface": "#e0def4",
|
"mOnSurface": "#e0def4",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"mprisBlacklist": [
|
"mprisBlacklist": [
|
||||||
],
|
],
|
||||||
"preferredPlayer": "",
|
"preferredPlayer": "",
|
||||||
|
"visualizerQuality": "high",
|
||||||
"visualizerType": "linear",
|
"visualizerType": "linear",
|
||||||
"volumeOverdrive": false,
|
"volumeOverdrive": false,
|
||||||
"volumeStep": 5
|
"volumeStep": 5
|
||||||
|
|
@ -73,7 +74,8 @@
|
||||||
"showDiskUsage": false,
|
"showDiskUsage": false,
|
||||||
"showMemoryAsPercent": false,
|
"showMemoryAsPercent": false,
|
||||||
"showMemoryUsage": true,
|
"showMemoryUsage": true,
|
||||||
"showNetworkStats": false
|
"showNetworkStats": false,
|
||||||
|
"usePrimaryColor": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ScreenRecorder"
|
"id": "ScreenRecorder"
|
||||||
|
|
@ -96,6 +98,7 @@
|
||||||
"usePrimaryColor": true
|
"usePrimaryColor": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"colorizeDistroLogo": false,
|
||||||
"customIconPath": "",
|
"customIconPath": "",
|
||||||
"icon": "noctalia",
|
"icon": "noctalia",
|
||||||
"id": "ControlCenter",
|
"id": "ControlCenter",
|
||||||
|
|
@ -105,6 +108,7 @@
|
||||||
"blacklist": [
|
"blacklist": [
|
||||||
],
|
],
|
||||||
"colorizeIcons": false,
|
"colorizeIcons": false,
|
||||||
|
"drawerEnabled": true,
|
||||||
"favorites": [
|
"favorites": [
|
||||||
],
|
],
|
||||||
"id": "Tray"
|
"id": "Tray"
|
||||||
|
|
@ -248,6 +252,7 @@
|
||||||
"backgroundOpacity": 1,
|
"backgroundOpacity": 1,
|
||||||
"criticalUrgencyDuration": 15,
|
"criticalUrgencyDuration": 15,
|
||||||
"doNotDisturb": false,
|
"doNotDisturb": false,
|
||||||
|
"enabled": true,
|
||||||
"location": "top_right",
|
"location": "top_right",
|
||||||
"lowUrgencyDuration": 3,
|
"lowUrgencyDuration": 3,
|
||||||
"monitors": [
|
"monitors": [
|
||||||
|
|
@ -275,7 +280,7 @@
|
||||||
"videoCodec": "h264",
|
"videoCodec": "h264",
|
||||||
"videoSource": "portal"
|
"videoSource": "portal"
|
||||||
},
|
},
|
||||||
"settingsVersion": 18,
|
"settingsVersion": 21,
|
||||||
"setupCompleted": true,
|
"setupCompleted": true,
|
||||||
"templates": {
|
"templates": {
|
||||||
"alacritty": false,
|
"alacritty": false,
|
||||||
|
|
@ -296,6 +301,7 @@
|
||||||
"kitty": false,
|
"kitty": false,
|
||||||
"pywalfox": false,
|
"pywalfox": false,
|
||||||
"qt": false,
|
"qt": false,
|
||||||
|
"spicetify": false,
|
||||||
"vicinae": false,
|
"vicinae": false,
|
||||||
"walker": false,
|
"walker": false,
|
||||||
"wezterm": false
|
"wezterm": false
|
||||||
|
|
@ -306,7 +312,7 @@
|
||||||
"fontFixed": "DejaVu Sans Mono",
|
"fontFixed": "DejaVu Sans Mono",
|
||||||
"fontFixedScale": 1,
|
"fontFixedScale": 1,
|
||||||
"panelsAttachedToBar": true,
|
"panelsAttachedToBar": true,
|
||||||
"panelsOverlayLayer": false,
|
"settingsPanelAttachToBar": false,
|
||||||
"tooltipsEnabled": true
|
"tooltipsEnabled": true
|
||||||
},
|
},
|
||||||
"wallpaper": {
|
"wallpaper": {
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,6 @@ exec {
|
||||||
obsidian
|
obsidian
|
||||||
localsend_app
|
localsend_app
|
||||||
vesktop
|
vesktop
|
||||||
|
gtk-launch com.microsoft.Edge.flextop.msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default
|
||||||
|
gtk-launch com.microsoft.Edge.flextop.msedge-faolnafnngnfdaknnbpnkhgohbobgegn-Default
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ bindgesture swipe:down workspace prev
|
||||||
bindsym {
|
bindsym {
|
||||||
$mod+Return exec $term
|
$mod+Return exec $term
|
||||||
$mod+d exec ~/.config/tofi/scripts/drun.sh
|
$mod+d exec ~/.config/tofi/scripts/drun.sh
|
||||||
$mod+t exec ~/.local/bin/code.sh -s ~/.local/bin/rofi.yaml
|
$mod+t exec ~/.local/bin/dev-launcher ~/Dev
|
||||||
$mod+shift+t exec ~/.local/bin/present
|
$mod+Shift+t exec ~/.local/bin/dev-launcher --no-cache ~/Dev
|
||||||
$mod+n exec ~/.local/bin/sdm-ui.sh dmenu
|
$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+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+y exec clapboard
|
||||||
$mod+b exec rofi-rbw --action copy
|
$mod+b exec rofi-rbw --action copy
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue