mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 11:32:42 +00:00
mise ~/.config/mise/config.toml tools: crush@0.91.0
feat(cli): add omarchy-cycle scripts for window cycling
This commit is contained in:
1 parent
e1f47d903a
commit
03a03980de
4 files changed
+187
No files matched your search
Executable
+177
@@ -0,0 +1,177 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Cycle through the windows of an app, or launch it if none are open
|
||||||
|
# omarchy:args=<app|browser|terminal|ai|webapp|webapp-NAME|kitty-CLASS>
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
APP="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z $APP ]]; then
|
||||||
|
echo "Usage: omarchy-cycle <app|browser|terminal|ai|webapp|webapp-NAME|kitty-CLASS>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Hyprland helpers --------------------------------------------------------
|
||||||
|
|
||||||
|
# Print the addresses of all windows whose class or initialClass matches the
|
||||||
|
# regex in $1. Windows are sorted by workspace, then by address, so the cycle
|
||||||
|
# order stays the same between calls.
|
||||||
|
addresses_matching() {
|
||||||
|
hyprctl clients -j | jq -r --arg re "$1" '
|
||||||
|
[ .[]
|
||||||
|
| select(((.class // "") | test($re; "i")) or ((.initialClass // "") | test($re; "i")))
|
||||||
|
]
|
||||||
|
| sort_by(.workspace.id, .address)
|
||||||
|
| .[].address'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Same, but drops site-specific app (SSA) windows, so real browsers stay apart
|
||||||
|
# from web apps that share a class prefix.
|
||||||
|
addresses_matching_no_ssa() {
|
||||||
|
hyprctl clients -j | jq -r --arg re "$1" '
|
||||||
|
[ .[]
|
||||||
|
| select(((.class // "") | test($re; "i")) or ((.initialClass // "") | test($re; "i")))
|
||||||
|
| select(((.class // "") | test("^(chrome-|webapp-)"; "i")) | not)
|
||||||
|
]
|
||||||
|
| sort_by(.workspace.id, .address)
|
||||||
|
| .[].address'
|
||||||
|
}
|
||||||
|
|
||||||
|
focused_address() {
|
||||||
|
hyprctl activewindow -j 2>/dev/null | jq -r '.address // empty'
|
||||||
|
}
|
||||||
|
|
||||||
|
focus_window() {
|
||||||
|
hyprctl dispatch "hl.dsp.focus({ window = \"address:$1\" })" >/dev/null 2>&1 ||
|
||||||
|
hyprctl dispatch focuswindow "address:$1" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn() {
|
||||||
|
exec setsid uwsm-app -- $1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Find the windows --------------------------------------------------------
|
||||||
|
|
||||||
|
declare -a INSTANCES
|
||||||
|
|
||||||
|
if [[ $APP == "webapp" ]]; then
|
||||||
|
# Cycle all web apps: Firefox SSAs (WebApp-*) and Chromium/Brave SSAs (chrome-*).
|
||||||
|
# Real browsers are excluded, because their class has no SSA prefix.
|
||||||
|
INSTANCES=($(addresses_matching '^(webapp-|chrome-)'))
|
||||||
|
FALLBACK_APP="webapp-youtube"
|
||||||
|
|
||||||
|
elif [[ $APP == webapp-* ]]; then
|
||||||
|
# Extract the web app name (e.g. "webapp-claude" -> "claude").
|
||||||
|
WEBAPP_NAME="${APP#webapp-}"
|
||||||
|
# Firefox SSAs use the class WebApp-<Name>. Chromium SSAs use
|
||||||
|
# chrome-<host>__-Default, so the name is matched anywhere in the class.
|
||||||
|
INSTANCES=($(addresses_matching "webapp-${WEBAPP_NAME}|^chrome-.*${WEBAPP_NAME}"))
|
||||||
|
|
||||||
|
if ((${#INSTANCES[@]} == 0)); then
|
||||||
|
# No window is open. Launch from the desktop entry.
|
||||||
|
DESKTOP_FILE=$(find ~/.local/share/applications -iname "WebApp-${WEBAPP_NAME}*.desktop" -print -quit 2>/dev/null)
|
||||||
|
[[ -n $DESKTOP_FILE ]] ||
|
||||||
|
DESKTOP_FILE=$(find ~/.local/share/applications -iname "*${WEBAPP_NAME}*.desktop" -print -quit 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ -n $DESKTOP_FILE ]]; then
|
||||||
|
exec setsid gtk-launch "$(basename "$DESKTOP_FILE")"
|
||||||
|
else
|
||||||
|
echo "No desktop file found for web app: ${WEBAPP_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ $APP == "kitty" ]]; then
|
||||||
|
# Cycle all kitty windows, whatever the class (kitty, kitty-nvim, ...).
|
||||||
|
INSTANCES=($(addresses_matching '^kitty'))
|
||||||
|
DEFAULT_SPAWN="kitty"
|
||||||
|
|
||||||
|
elif [[ $APP == kitty-* ]]; then
|
||||||
|
# One kitty class, e.g. kitty-nvim -> class "kitty-nvim".
|
||||||
|
INSTANCES=($(addresses_matching "^${APP}$"))
|
||||||
|
|
||||||
|
if ((${#INSTANCES[@]} == 0)); then
|
||||||
|
cmd="${KITTY_CMDS[$APP]:-}"
|
||||||
|
if [[ -n $cmd ]]; then
|
||||||
|
spawn "kitty --class $APP -e $cmd"
|
||||||
|
else
|
||||||
|
spawn "kitty --class $APP"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ $APP == "ghostty" ]]; then
|
||||||
|
# Cycle Ghostty terminals (class com.mitchellh.ghostty).
|
||||||
|
INSTANCES=($(addresses_matching 'ghostty'))
|
||||||
|
DEFAULT_SPAWN="ghostty"
|
||||||
|
|
||||||
|
elif [[ $APP == "browser" ]]; then
|
||||||
|
# Cycle all browsers. Web apps are left out.
|
||||||
|
INSTANCES=($(addresses_matching_no_ssa '^(zen|brave|chromium|google-chrome|firefox|librewolf|vivaldi|microsoft-edge|helium)'))
|
||||||
|
DEFAULT_LAUNCHER="omarchy-launch-browser"
|
||||||
|
|
||||||
|
elif [[ $APP == "terminal" ]]; then
|
||||||
|
# Cycle all terminals.
|
||||||
|
INSTANCES=($(addresses_matching '(kitty|alacritty|ghostty|foot|wezterm)'))
|
||||||
|
DEFAULT_LAUNCHER="omarchy-launch-terminal"
|
||||||
|
|
||||||
|
elif [[ $APP == "ai" ]]; then
|
||||||
|
# Cycle the AI desktop apps (Claude/Hermes).
|
||||||
|
INSTANCES=($(addresses_matching '(claude|hermes)'))
|
||||||
|
DEFAULT_SPAWN="hermes desktop"
|
||||||
|
|
||||||
|
else
|
||||||
|
# Any other app, matched on the full class.
|
||||||
|
INSTANCES=($(addresses_matching "^${APP}$"))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Launch if nothing is open -----------------------------------------------
|
||||||
|
|
||||||
|
if ((${#INSTANCES[@]} == 0)); then
|
||||||
|
if [[ -n ${DEFAULT_LAUNCHER:-} ]]; then
|
||||||
|
exec setsid "$DEFAULT_LAUNCHER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n ${DEFAULT_SPAWN:-} ]]; then
|
||||||
|
spawn "$DEFAULT_SPAWN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n ${FALLBACK_APP:-} ]]; then
|
||||||
|
exec "$0" "$FALLBACK_APP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $APP == "webapp" ]]; then
|
||||||
|
echo "No ${APP} windows running"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
spawn "$APP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Focus, or cycle to the next window --------------------------------------
|
||||||
|
|
||||||
|
if ((${#INSTANCES[@]} == 1)); then
|
||||||
|
focus_window "${INSTANCES[0]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
FOCUSED=$(focused_address)
|
||||||
|
|
||||||
|
# If the focused window is one of ours, go to the next one. If it is not,
|
||||||
|
# go to the first one.
|
||||||
|
next_index=0
|
||||||
|
for i in "${!INSTANCES[@]}"; do
|
||||||
|
if [[ -n $FOCUSED && ${INSTANCES[$i]} == "$FOCUSED" ]]; then
|
||||||
|
next_index=$(((i + 1) % ${#INSTANCES[@]}))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
focus_window "${INSTANCES[$next_index]}"
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Cycle Brave Origin Nightly windows; launch brave-origin-nightly if none are open.
|
||||||
|
|
||||||
|
exec "$(dirname "$(readlink -f "$0")")/omarchy-cycle" brave-origin-nightly
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Cycle through all terminals (ghostty/kitty/alacritty/foot/wezterm); launch the default if none are open.
|
||||||
|
|
||||||
|
exec "$(dirname "$(readlink -f "$0")")/omarchy-cycle" terminal
|
||||||
Regular → Executable
File mode changed.
Reference in new issue
Block a user