Files
dotfiles/dot_local/bin/executable_niri-launch
T

111 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
APP=$1
declare -a INSTANCES
# Per-class launch commands for kitty-* (class -> command run via `kitty -e`).
# Unmapped classes just open a plain kitty with that class.
declare -A KITTY_CMDS=(
# [kitty-lazygit]="lazygit"
# [kitty-btop]="btop"
)
# Handle webapp cycling and launching
if [[ "$APP" == "webapp" ]]; then
# Cycle all WebApps: Firefox SSAs (WebApp-*) and Chromium/Brave SSAs (chrome-*-Default).
# Falls back to launching YouTube if none are open. Excludes real browsers (chromium/brave-*).
INSTANCES=($(niri msg -j windows | jq -r '.[] | select((.app_id // "") | ascii_downcase | test("^(webapp-|chrome-)")) | .id'))
FALLBACK_APP="webapp-youtube"
elif [[ "$APP" == webapp-* ]]; then
# Extract the webapp name (e.g., "webapp-claude" -> "Claude")
WEBAPP_NAME=$(echo "$APP" | sed 's/webapp-//' | sed 's/.*/\u&/')
# Find matching WebApp by prefix (case-insensitive match)
APP_ID=$(niri msg -j windows | jq -r ".[] | select(.app_id | test(\"WebApp-${WEBAPP_NAME}\"; \"i\")) | .app_id" | head -1)
if [ -z "$APP_ID" ]; then
# No instance running, launch via gtk-launch
DESKTOP_FILE=$(find ~/.local/share/applications -iname "WebApp-${WEBAPP_NAME}*.desktop" -print -quit 2>/dev/null)
if [ -n "$DESKTOP_FILE" ]; then
gtk-launch "$(basename "$DESKTOP_FILE")"
else
echo "No desktop file found for WebApp-${WEBAPP_NAME}"
fi
exit 0
fi
INSTANCES=($(niri msg -j windows | jq -r ".[] | select(.app_id | test(\"WebApp-${WEBAPP_NAME}\"; \"i\")) | .id"))
elif [[ "$APP" == "kitty" ]]; then
# Cycle through ALL kitty windows (any class: kitty, kitty-nvim, ...)
INSTANCES=($(niri msg -j windows | jq -r '.[] | select((.app_id // "") | ascii_downcase | startswith("kitty")) | .id'))
elif [[ "$APP" == kitty-* ]]; then
# Specific kitty class, e.g. kitty-nvim -> app_id "kitty-nvim"
INSTANCES=($(niri msg -j windows | jq -r --arg cls "$APP" \
'.[] | select((.app_id // "") | ascii_downcase == ($cls | ascii_downcase)) | .id'))
if [ ${#INSTANCES[@]} -eq 0 ]; then
cmd="${KITTY_CMDS[$APP]:-}"
if [ -n "$cmd" ]; then
niri msg action spawn -- kitty --class "$APP" -e $cmd
else
niri msg action spawn -- kitty --class "$APP"
fi
exit 0
fi
elif [[ "$APP" == "ghostty" ]]; then
# Cycle through Ghostty terminals (case-insensitive app_id match)
INSTANCES=($(niri msg -j windows | jq -r '.[] | select(.app_id | test("ghostty"; "i")) | .id'))
elif [[ "$APP" == "browser" ]]; then
# Cycle through all browsers (zen/brave/chromium/firefox), launch default if none
INSTANCES=($(niri msg -j windows | jq -r '.[] | select((.app_id // "") | ascii_downcase | test("^(zen|brave|chromium|firefox)")) | .id'))
DEFAULT_SPAWN="zen"
elif [[ "$APP" == "terminal" ]]; then
# Cycle through all terminals (ghostty/kitty/alacritty), launch the OS
# default (xdg-terminal-exec, ~/.config/xdg-terminals.list) if none
INSTANCES=($(niri msg -j windows | jq -r '.[] | select((.app_id // "") | ascii_downcase | test("kitty|alacritty|ghostty")) | .id'))
DEFAULT_SPAWN="xdg-terminal-exec"
elif [[ "$APP" == "ai" ]]; then
# Cycle through AI desktop apps (Claude/Hermes), launch Hermes if none
INSTANCES=($(niri msg -j windows | jq -r '.[] | select((.app_id // "") | ascii_downcase | test("claude|hermes")) | .id'))
DEFAULT_SPAWN="hermes desktop"
else
INSTANCES=($(niri msg -j windows | jq -r ".[] | select(.app_id==\"${APP}\") | .id"))
fi
# Launch if no instances
if [ ${#INSTANCES[@]} -eq 0 ]; then
if [ -n "${DEFAULT_SPAWN:-}" ]; then
niri msg action spawn -- $DEFAULT_SPAWN
exit 0
fi
if [ -n "${FALLBACK_APP:-}" ]; then
exec "$0" "$FALLBACK_APP"
fi
# Removed "kitty" from this block so it falls through to the spawn command
if [[ "$APP" == "webapp" || "$APP" == "ghostty" ]]; then
echo "No ${APP} windows running"
exit 0
fi
niri msg action spawn -- $APP
exit 0
fi
# Focus logic
if [ ${#INSTANCES[@]} -eq 1 ]; then
niri msg action focus-window --id ${INSTANCES[0]}
else
ISORTED=($(printf "%s\n" "${INSTANCES[@]}" | sort -n))
FOCUSED=$(niri msg -j focused-window | jq '.id')
if [[ ! " ${ISORTED[@]} " =~ ${FOCUSED} ]]; then
niri msg action focus-window --id ${ISORTED[0]}
else
new_index=0
for i in "${!ISORTED[@]}"; do
if [ "${ISORTED[$i]}" -eq "${FOCUSED}" ]; then
new_index=$(( (i + 1) % ${#ISORTED[@]} ))
break
fi
done
niri msg action focus-window --id ${ISORTED[$new_index]}
fi
fi