feat(sh): niri spawning script for stacking two windows of same app vertically

This commit is contained in:
2025-09-19 20:03:11 +02:00
parent 3b7e566545
commit 1c8ccb6405
7 changed files with 170 additions and 141 deletions

View File

@@ -0,0 +1,77 @@
# Ghostty spawn-or-focus logic for Niri:
# If no Ghostty windows exist, spawn one.
# If focused window is not Ghostty, focus the first Ghostty instance.
# If Ghostty is focused and only one instance exists, spawn + consume into stack.
# If Ghostty is focused and two instances already exist, spawn a new separate one.
# Cycle repeats: focus first > allow stack of 2 > reset on 3rd.
APP_ID="com.mitchellh.ghostty"
APP_CMD="ghostty"
WINDOW_DATA=$(niri msg -j windows)
readarray -t GHOSTTY_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=${#GHOSTTY_IDS[@]}
FOCUSED_IS_GHOSTTY=$(
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_GHOSTTY" != "true" ]; then
niri msg action focus-window --id "${GHOSTTY_IDS[0]}"
exit 0
fi
if ((COUNT % 2 == 0)); then
spawn_normal
else
spawn_and_consume "${GHOSTTY_IDS[@]}"
fi

82
scripts/bin/spawn.sh Normal file
View File

@@ -0,0 +1,82 @@
# 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_ID> [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

View File

@@ -1,41 +0,0 @@
# Log file location
LOGFILE="/home/$USER/.cache/tuirun/tuirun-toggle.log"
# Redirect all output and errors to the log file
exec >>"$LOGFILE" 2>&1
# Enable command tracing
set -x
echo "Script started at $(date)"
# Log the environment variables
echo "Environment variables:"
env
# Define TERMINAL if not set
TERMINAL="${TERMINAL:-foot}"
echo "TERMINAL is set to: $TERMINAL"
# Ensure USER is set
USER="${USER:-$(whoami)}"
echo "USER is set to: $USER"
# Path to the tuirun executable
TUIRUN_PATH="/etc/profiles/per-user/$USER/bin/tuirun"
echo "TUIRUN_PATH is set to: $TUIRUN_PATH"
# Use absolute paths for commands
PGREP="/run/current-system/sw/bin/pgrep"
PKILL="/run/current-system/sw/bin/pkill"
HYPRCTL="/etc/profiles/per-user/$USER/bin/hyprctl"
echo "Checking if tuirun is already running..."
if "$PGREP" -f "$TERMINAL --title tuirun" >/dev/null; then
echo "Found existing tuirun process. Terminating..."
"$PKILL" -f "$TERMINAL --title tuirun"
else
echo "No existing tuirun process. Starting a new one..."
"$HYPRCTL" dispatch exec "$TERMINAL --title tuirun -e $TUIRUN_PATH"
fi
echo "Script finished at $(date)"

View File

@@ -1,37 +0,0 @@
# Define TERMINAL if not set
TERMINAL="${TERMINAL:-foot}"
# Use absolute paths for commands
PGREP="/run/current-system/sw/bin/pgrep"
PKILL="/run/current-system/sw/bin/pkill"
UWSM="/run/current-system/sw/bin/uwsm"
TUIRUN_PATH="/etc/profiles/per-user/$USER/bin/tuirun"
# Determine OPTIONS based on TERMINAL
if [ "$TERMINAL" = "foot" ]; then
OPTIONS="--override=main.pad=0x0"
elif [ "$TERMINAL" = "alacritty" ]; then
OPTIONS="--option window.padding.x=0 --option window.padding.y=0"
else
OPTIONS=""
fi
# Matching pattern for the process
MATCH_PATTERN="$TERMINAL --title tuirun"
if "$PGREP" -f "$MATCH_PATTERN" >/dev/null; then
echo "$(date): Killing existing process"
"$PKILL" -f "$MATCH_PATTERN"
else
# Log the environment for debugging
env >/tmp/script_env.txt
# Construct the command as an array for proper argument handling
CMD=("$TERMINAL" "--title" "tuirun")
if [ -n "$OPTIONS" ]; then
CMD+=("$OPTIONS")
fi
CMD+=("-e" "$TUIRUN_PATH")
echo "$(date): Executing command: ${CMD[*]}"
# Use eval to expand the command or pass the arguments directly
"$UWSM" app -- "${CMD[@]}"
fi

View File

@@ -1,33 +0,0 @@
# Define TERMINAL if not set
TERMINAL="${TERMINAL:-foot}"
# Use absolute paths for commands
PGREP="/run/current-system/sw/bin/pgrep"
PKILL="/run/current-system/sw/bin/pkill"
HYPRCTL="/etc/profiles/per-user/$USER/bin/hyprctl"
TUIRUN_PATH="/etc/profiles/per-user/$USER/bin/tuirun"
# Determine OPTIONS based on TERMINAL
if [ "$TERMINAL" = "foot" ]; then
OPTIONS="--override=main.pad=0x0"
elif [ "$TERMINAL" = "alacritty" ]; then
OPTIONS="--option window.padding.x=0 --option window.padding.y=0"
else
OPTIONS=""
fi
# Matching pattern for the process
MATCH_PATTERN="$TERMINAL --title tuirun"
if "$PGREP" -f "$MATCH_PATTERN" >/dev/null; then
"$PKILL" -f "$MATCH_PATTERN"
else
# Construct the command
CMD="$TERMINAL --title tuirun"
if [ -n "$OPTIONS" ]; then
CMD="$CMD $OPTIONS"
fi
# Use login shell to ensure proper environment
CMD="$CMD -e $SHELL -l -c '$TUIRUN_PATH'"
# Launch the terminal with OPTIONS
"$HYPRCTL" dispatch exec "$CMD"
fi