# Spawn-or-focus logic for Niri: # If no "app" windows exist, spawn one. # If focused window is not "app", focus the first "app" instance. # If "app" is focused and only one instance exists, spawn + consume into stack. # If "app" is focused and two instances already exist, spawn a new separate one. # Cycle repeats: focus first > allow stack of 2 > reset on 3rd. if [ $# -lt 1 ]; then echo "Usage: $0 [APP_CMD]" >&2 exit 1 fi APP_ID="$1" APP_CMD="${2:-$1}" WINDOW_DATA=$(niri msg -j windows) readarray -t APP_IDS < <( echo "$WINDOW_DATA" | jq -r --arg app_id "$APP_ID" ' [ .[] | select(.app_id == $app_id) ] | sort_by(.layout.pos_in_scrolling_layout // [0,0]) | .[].id ' ) COUNT=${#APP_IDS[@]} FOCUSED_IS_APP=$( echo "$WINDOW_DATA" | jq -r --arg app_id "$APP_ID" ' any(.[]; .app_id == $app_id and .is_focused) ' ) spawn_normal() { "$APP_CMD" & } spawn_and_consume() { local initial_ids=("$@") "$APP_CMD" & local pid=$! for _ in {1..50}; do readarray -t after_ids < <( niri msg -j windows | jq -r --arg app_id "$APP_ID" ' [ .[] | select(.app_id == $app_id) ] | sort_by(.layout.pos_in_scrolling_layout // [0,0]) | .[].id ' ) NEW_ID="" for id in "${after_ids[@]}"; do [[ " ${initial_ids[*]} " == *" $id "* ]] || NEW_ID="$id" done if [ -n "$NEW_ID" ]; then niri msg action focus-window --id "${initial_ids[$((${#initial_ids[@]} - 1))]}" niri msg action consume-window-into-column break fi sleep 0.05 done wait "$pid" 2>/dev/null || true } if ((COUNT == 0)); then spawn_normal exit 0 fi if [ "$FOCUSED_IS_APP" != "true" ]; then niri msg action focus-window --id "${APP_IDS[0]}" exit 0 fi if ((COUNT % 2 == 0)); then spawn_normal else spawn_and_consume "${APP_IDS[@]}" fi