Add window switcher with Alt+Tab and snappy-switcher integration - #892
Add window switcher with Alt+Tab and snappy-switcher integration#892shunkakinoki wants to merge 3 commits into
Conversation
Add a focus-history-based Alt+Tab window switcher for Hyprland, inspired by the saneAspect tutorial. Uses hyprctl to cycle windows by recency (like Windows/macOS), with session-aware state persistence for continuous switching, reverse cycling (ALT+Shift+Tab), and temporary dim feedback. https://claude.ai/code/session_01NNjd5QrBK887SPA9wLsBZx
Use snappy-switcher (OpalAayan/snappy-switcher) for Super+Tab instead of hyprexpo. Adds daemon startup, Dracula-themed config, and Super+Shift+Tab for reverse cycling. https://claude.ai/code/session_01NNjd5QrBK887SPA9wLsBZx
|
You do not have enough credits to review this pull request. Please purchase more credits to continue. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughThis change integrates window switching functionality into Hyprland by adding snappy-switcher and a custom alt-tab script. Configuration files, keybindings, and package dependencies are updated across multiple files to support both switchers, with snappy-switcher pulled as a new flake input and included in desktop packages. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @shunkakinoki, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the window switching experience in Hyprland by introducing two complementary mechanisms. It provides a custom Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Mesa DescriptionTL;DRAdds two window switchers to Hyprland: a focus-history Alt+Tab script and a visual switcher via snappy-switcher on Super+Tab. This makes switching fast and familiar to Windows/macOS users. What changed?
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
1 issue found across 6 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="config/hyprland/scripts/alt-tab.sh">
<violation number="1" location="config/hyprland/scripts/alt-tab.sh:132">
P2: `local` is used outside a function, which causes a runtime error and stops the script. Declare the variable without `local` in this top-level block.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- Move snappy-switcher config from config/hyprland/ to config/snappy-switcher/ with its own default.nix module (matching repo convention) - Delete config/hyprland/scripts/alt-tab.sh (replaced by snappy-switcher) - Remove alt-tab.sh bindings and xdg.configFile entries from hyprland config https://claude.ai/code/session_01NNjd5QrBK887SPA9wLsBZx
There was a problem hiding this comment.
Code Review
This pull request introduces two new window switching mechanisms: a custom alt-tab.sh script for focus-history based switching, and integration with snappy-switcher for visual switching. While the overall integration is well-structured, a high-severity security vulnerability was identified in the alt-tab.sh script regarding insecure temporary file handling in /tmp. This could allow a local attacker to perform a symlink attack, leading to data destruction or a denial of service by killing arbitrary user processes. It is recommended to move the state directory to a user-specific, secure location like $XDG_RUNTIME_DIR. Additionally, the alt-tab.sh script could benefit from performance improvements by reducing redundant shell command executions, which can become noticeable with many open windows.
I am having trouble creating individual review comments. Click here to see my feedback.
config/hyprland/scripts/alt-tab.sh (3)
The script uses a predictable directory /tmp/hypr-alt-tab to store state and PID files. Since /tmp is world-writable, an attacker can pre-create this directory and place symlinks inside it. When the script (run by a user) writes to these files (e.g., echo $! > "$STATE_DIR/dim_timer" on line 259), it will follow the symlink and overwrite the target file with the user's privileges. This can be used to destroy user data or cause a Denial of Service by killing arbitrary processes (via the kill command on line 252).
To remediate this, use a more secure, user-specific location for temporary files, such as a directory under $XDG_RUNTIME_DIR. Additionally, ensure the directory is created with restricted permissions (e.g., by using mkdir -p -m 0700 "$STATE_DIR" on line 13).
STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}/hypr-alt-tab-${USER}"
config/hyprland/scripts/alt-tab.sh (102-114)
This function is inefficient because it calls window_exists for every window in a loop. The window_exists function in turn executes hyprctl clients -j, which is a relatively expensive operation. For a list of N windows, you are making N hyprctl calls just in this function.
This, and other parts of the script, can be significantly optimized by fetching the list of all clients once at the beginning of the script and using that cached data throughout.
Recommendation:
- At the start of the script, cache the client data:
CLIENTS_JSON=$(hyprctl clients -j) - Then, rewrite this function to use the cached data with a hash map for efficient lookups. This avoids calling
hyprctlin a loop:validate_window_list() { local windows=("$@") local valid_windows=() # Create a hash map of existing windows for O(1) lookups declare -A existing_windows_map while read -r addr; do existing_windows_map["$addr"]=1 done < <(echo "$CLIENTS_JSON" | jq -r '.[].address') for window in "${windows[@]}"; do if [[ -v existing_windows_map[$window] ]]; then valid_windows+=("$window") fi done printf '%s\n' "${valid_windows[@]}" }
Applying this pattern to all functions that call hyprctl clients -j will make the script much faster.
config/hyprland/scripts/alt-tab.sh (4)
The variable STATE_FILE is defined but never used in the script. It can be safely removed to improve code clarity.
config/hyprland/scripts/alt-tab.sh (132-140)
The nested loop to find the intersection of windows and same_type_windows has a time complexity of O(N*M), which can be slow if there are many windows. This can be optimized by using an associative array (hash map) for one of the lists to achieve more efficient O(N+M) complexity.
declare -A same_type_map
for w in "${same_type_windows[@]}"; do same_type_map["$w"]=1; done
local filtered_windows=()
for visible_window in "${windows[@]}"; do
if [[ -v same_type_map[$visible_window] ]]; then
filtered_windows+=("$visible_window")
fi
done
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
home-manager/packages/default.nix (1)
19-19:bcis only needed byalt-tab.sh, which is Linux-desktop only.Consider moving
bcinto thelib.optionals (stdenv.isLinux && isDesktop)block alongsidesnappy-switcher, since it's only used for floating-point arithmetic in the Hyprland alt-tab script.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@home-manager/packages/default.nix` at line 19, Move the bc dependency out of the global package list and include it only when building the Linux desktop profile: add "bc" inside the existing lib.optionals (stdenv.isLinux && isDesktop) block alongside "snappy-switcher" so it is only added when stdenv.isLinux and isDesktop are true (this keeps bc available for the Hyprland alt-tab.sh script but avoids installing it on non-desktop or non-Linux configurations).config/hyprland/scripts/alt-tab.sh (2)
34-44:get_window_titleandget_window_workspaceare defined but never called.These are dead code. Remove them to keep the script lean, or add a comment noting they're reserved for future use.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@config/hyprland/scripts/alt-tab.sh` around lines 34 - 44, The functions get_window_title and get_window_workspace are defined but never used; either remove these dead functions to slim the script or keep them but add a short comment above each (e.g., "reserved for future use" and a brief example of intended usage) so they're no longer misleading; update the function blocks named get_window_title and get_window_workspace accordingly and run a quick lint/test of the script to ensure no accidental references remain.
248-259: Dim timer PID kill is racy — could kill an unrelated process.If the previous background sleep (lines 255–258) has already exited by the time the next Alt+Tab invocation runs, the saved PID in
dim_timermay have been reused by an unrelated process. Thekillon line 252 would then signal that process.A safer approach is to use a dedicated cleanup mechanism or check the process before killing:
Proposed fix
# Clear any existing reset timer -[[ -f "$STATE_DIR/dim_timer" ]] && kill "$(<"$STATE_DIR/dim_timer")" 2>/dev/null || true +if [[ -f "$STATE_DIR/dim_timer" ]]; then + old_pid=$(<"$STATE_DIR/dim_timer") + # Only kill if PID is still our background sleep + if [[ -d "/proc/$old_pid" ]] && grep -q "sleep" "/proc/$old_pid/comm" 2>/dev/null; then + kill "$old_pid" 2>/dev/null || true + fi +fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@config/hyprland/scripts/alt-tab.sh` around lines 248 - 259, The current dim-timer management is racy because the saved PID in "$STATE_DIR/dim_timer" might have exited and been reused, so the kill "$( <"$STATE_DIR/dim_timer")" in the cleanup step can signal an unrelated process; modify the cleanup to validate the PID before killing by reading the PID from "$STATE_DIR/dim_timer", verify it is a running process and that its command line or start time matches the expected sleep/alt-tab helper (e.g., check /proc/<pid>/cmdline or /proc/<pid>/comm contains "sleep" or the script name), and only then send the kill; alternatively, create a more robust token (e.g., write the PID plus a UUID or use a dedicated lockfile/flock) and compare that token when deciding to kill so you never accidentally kill a different process when clearing dim_timer in the functions that set/clear the dim timer (references: "$STATE_DIR/dim_timer", the kill invocation, and the background sleep block).config/hyprland/hyprland.conf (1)
442-451: Hyprexpo plugin is still loaded and configured but no longer bound to any key.Lines 261–262 now use snappy-switcher instead of
hyprexpo:expo, and the plugin is still loaded indefault.nix(line 17). The gesture binding (3-finger swipe at line 448–449) still works through the plugin, so it may be intentionally kept. If hyprexpo is no longer needed, consider removing the plugin load and this config block to reduce overhead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@config/hyprland/hyprland.conf` around lines 442 - 451, The hyprexpo plugin is still loaded/configured via the plugin { hyprexpo { ... } } block but is no longer bound to hyprexpo:expo (you now use snappy-switcher), so either remove the plugin block and its load entry from default.nix to drop unused overhead, or keep it if you rely on the 3-finger gesture—if keeping, explicitly document the gesture dependency or rebind hyprexpo:expo to a key; locate the plugin block named "hyprexpo" in hyprland.conf and the corresponding load line in default.nix and apply one of these two changes.flake.nix (1)
67-69: Addinputs.nixpkgs.followsto snappy-switcher input.Most other inputs in this flake pin
inputs.nixpkgs.follows = "nixpkgs"to share a single nixpkgs instance. Without it,snappy-switcherwill pull its own copy of nixpkgs/nixos-unstable, bloating the flake lock and evaluation time.Proposed fix
snappy-switcher = { url = "github:OpalAayan/snappy-switcher"; + inputs.nixpkgs.follows = "nixpkgs"; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flake.nix` around lines 67 - 69, The snappy-switcher input is missing inputs.nixpkgs.follows so it pulls its own nixpkgs; update the snappy-switcher input block (the entry named "snappy-switcher") to include inputs.nixpkgs.follows = "nixpkgs" so it follows the top-level nixpkgs and shares a single nixpkgs instance with the other inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@config/hyprland/scripts/alt-tab.sh`:
- Around line 96-114: The script makes repeated hyprctl clients -j calls (e.g.,
inside window_exists and used by validate_window_list), causing many IPC
round-trips; fix by fetching the clients JSON once (e.g., into a variable
CLIENTS_JSON) at the start of the workflow and modify functions like
window_exists, validate_window_list, get_window_class, get_windows_by_focus,
get_visible_windows, and get_same_type_windows to accept or read that cached
JSON instead of invoking hyprctl each time, then update their jq/grep
invocations to pipe from the cached CLIENTS_JSON so a single hyprctl call
services the whole Alt+Tab operation.
- Line 132: The script uses "local filtered_windows=()" at top-level which is
invalid in bash; change the declaration of filtered_windows so it isn't using
local outside a function—either move the declaration into the function where
it's used (e.g., the alt-tab handling function) or replace "local" with a
top-level-safe declaration such as "declare -a filtered_windows=()" or
"filtered_windows=()" so the variable is properly initialized; update any
references to filtered_windows accordingly.
- Around line 80-89: The get_same_type_windows function returns windows in
hyprctl's default order; update it to sort by focus history so the
most-recent-first behavior is preserved: when building the jq pipeline in
get_same_type_windows, apply sort_by(-.focusHistoryID) before selecting by
.class and outputting .address (i.e., sort the clients by negative
.focusHistoryID then filter by $class).
---
Nitpick comments:
In `@config/hyprland/hyprland.conf`:
- Around line 442-451: The hyprexpo plugin is still loaded/configured via the
plugin { hyprexpo { ... } } block but is no longer bound to hyprexpo:expo (you
now use snappy-switcher), so either remove the plugin block and its load entry
from default.nix to drop unused overhead, or keep it if you rely on the 3-finger
gesture—if keeping, explicitly document the gesture dependency or rebind
hyprexpo:expo to a key; locate the plugin block named "hyprexpo" in
hyprland.conf and the corresponding load line in default.nix and apply one of
these two changes.
In `@config/hyprland/scripts/alt-tab.sh`:
- Around line 34-44: The functions get_window_title and get_window_workspace are
defined but never used; either remove these dead functions to slim the script or
keep them but add a short comment above each (e.g., "reserved for future use"
and a brief example of intended usage) so they're no longer misleading; update
the function blocks named get_window_title and get_window_workspace accordingly
and run a quick lint/test of the script to ensure no accidental references
remain.
- Around line 248-259: The current dim-timer management is racy because the
saved PID in "$STATE_DIR/dim_timer" might have exited and been reused, so the
kill "$( <"$STATE_DIR/dim_timer")" in the cleanup step can signal an unrelated
process; modify the cleanup to validate the PID before killing by reading the
PID from "$STATE_DIR/dim_timer", verify it is a running process and that its
command line or start time matches the expected sleep/alt-tab helper (e.g.,
check /proc/<pid>/cmdline or /proc/<pid>/comm contains "sleep" or the script
name), and only then send the kill; alternatively, create a more robust token
(e.g., write the PID plus a UUID or use a dedicated lockfile/flock) and compare
that token when deciding to kill so you never accidentally kill a different
process when clearing dim_timer in the functions that set/clear the dim timer
(references: "$STATE_DIR/dim_timer", the kill invocation, and the background
sleep block).
In `@flake.nix`:
- Around line 67-69: The snappy-switcher input is missing inputs.nixpkgs.follows
so it pulls its own nixpkgs; update the snappy-switcher input block (the entry
named "snappy-switcher") to include inputs.nixpkgs.follows = "nixpkgs" so it
follows the top-level nixpkgs and shares a single nixpkgs instance with the
other inputs.
In `@home-manager/packages/default.nix`:
- Line 19: Move the bc dependency out of the global package list and include it
only when building the Linux desktop profile: add "bc" inside the existing
lib.optionals (stdenv.isLinux && isDesktop) block alongside "snappy-switcher" so
it is only added when stdenv.isLinux and isDesktop are true (this keeps bc
available for the Hyprland alt-tab.sh script but avoids installing it on
non-desktop or non-Linux configurations).
| # Function to get windows filtered by type (same class) | ||
| get_same_type_windows() { | ||
| local current_window="$1" | ||
| local current_class | ||
| current_class=$(get_window_class "$current_window") | ||
|
|
||
| # Get all windows with same class | ||
| hyprctl clients -j | jq -r --arg class "$current_class" \ | ||
| '.[] | select(.class == $class) | .address' | ||
| } |
There was a problem hiding this comment.
get_same_type_windows doesn't sort by focus history.
When --same is used without --visible, the returned window list comes from get_same_type_windows which uses hyprctl's default order rather than sort_by(-.focusHistoryID). This breaks the "most-recent-first" behavior for same-class switching.
Proposed fix
get_same_type_windows() {
local current_window="$1"
local current_class
current_class=$(get_window_class "$current_window")
# Get all windows with same class
hyprctl clients -j | jq -r --arg class "$current_class" \
- '.[] | select(.class == $class) | .address'
+ '[.[] | select(.class == $class)] | sort_by(-.focusHistoryID) | .[].address'
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Function to get windows filtered by type (same class) | |
| get_same_type_windows() { | |
| local current_window="$1" | |
| local current_class | |
| current_class=$(get_window_class "$current_window") | |
| # Get all windows with same class | |
| hyprctl clients -j | jq -r --arg class "$current_class" \ | |
| '.[] | select(.class == $class) | .address' | |
| } | |
| # Function to get windows filtered by type (same class) | |
| get_same_type_windows() { | |
| local current_window="$1" | |
| local current_class | |
| current_class=$(get_window_class "$current_window") | |
| # Get all windows with same class | |
| hyprctl clients -j | jq -r --arg class "$current_class" \ | |
| '[.[] | select(.class == $class)] | sort_by(-.focusHistoryID) | .[].address' | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/hyprland/scripts/alt-tab.sh` around lines 80 - 89, The
get_same_type_windows function returns windows in hyprctl's default order;
update it to sort by focus history so the most-recent-first behavior is
preserved: when building the jq pipeline in get_same_type_windows, apply
sort_by(-.focusHistoryID) before selecting by .class and outputting .address
(i.e., sort the clients by negative .focusHistoryID then filter by $class).
| # Function to check if window exists | ||
| window_exists() { | ||
| local window="$1" | ||
| [[ -n "$window" ]] && hyprctl clients -j | jq -r '.[].address' | grep -q "^${window}$" | ||
| } | ||
|
|
||
| # Function to validate and refresh window list | ||
| validate_window_list() { | ||
| local windows=("$@") | ||
| local valid_windows=() | ||
|
|
||
| for window in "${windows[@]}"; do | ||
| if window_exists "$window"; then | ||
| valid_windows+=("$window") | ||
| fi | ||
| done | ||
|
|
||
| printf '%s\n' "${valid_windows[@]}" | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Repeated hyprctl clients -j calls — O(n) IPC round-trips in validate_window_list.
window_exists calls hyprctl clients -j per window, and validate_window_list invokes it for every window in the list. Combined with earlier calls in get_windows_by_focus, get_same_type_windows, etc., a single Alt+Tab press can issue many IPC calls. Consider fetching the client list once and passing it through.
Proposed approach — cache clients JSON once
+# Fetch clients once and reuse
+CLIENTS_JSON=$(hyprctl clients -j)
+
# Function to check if window exists
window_exists() {
local window="$1"
- [[ -n "$window" ]] && hyprctl clients -j | jq -r '.[].address' | grep -q "^${window}$"
+ [[ -n "$window" ]] && echo "$CLIENTS_JSON" | jq -r '.[].address' | grep -q "^${window}$"
}Apply the same pattern to get_window_class, get_windows_by_focus, get_visible_windows, get_same_type_windows, etc., piping from $CLIENTS_JSON instead of calling hyprctl clients -j each time.
Also applies to: 157-158
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/hyprland/scripts/alt-tab.sh` around lines 96 - 114, The script makes
repeated hyprctl clients -j calls (e.g., inside window_exists and used by
validate_window_list), causing many IPC round-trips; fix by fetching the clients
JSON once (e.g., into a variable CLIENTS_JSON) at the start of the workflow and
modify functions like window_exists, validate_window_list, get_window_class,
get_windows_by_focus, get_visible_windows, and get_same_type_windows to accept
or read that cached JSON instead of invoking hyprctl each time, then update
their jq/grep invocations to pipe from the cached CLIENTS_JSON so a single
hyprctl call services the whole Alt+Tab operation.
| if [[ "$same_type" == "true" ]] && [[ -n "$current_focused" ]]; then | ||
| mapfile -t same_type_windows < <(get_same_type_windows "$current_focused") | ||
|
|
||
| local filtered_windows=() |
There was a problem hiding this comment.
local used outside a function — bash will error here.
local is only valid inside a function body in bash. At script top-level (lines 120–155 are not inside any function), this will emit bash: local: can only be used in a function and filtered_windows won't be declared as intended.
Proposed fix
- local filtered_windows=()
+ filtered_windows=()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| local filtered_windows=() | |
| filtered_windows=() |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/hyprland/scripts/alt-tab.sh` at line 132, The script uses "local
filtered_windows=()" at top-level which is invalid in bash; change the
declaration of filtered_windows so it isn't using local outside a
function—either move the declaration into the function where it's used (e.g.,
the alt-tab handling function) or replace "local" with a top-level-safe
declaration such as "declare -a filtered_windows=()" or "filtered_windows=()" so
the variable is properly initialized; update any references to filtered_windows
accordingly.
There was a problem hiding this comment.
Pull request overview
Adds two window-switching workflows to the Hyprland setup: a focus-history-based Alt+Tab script and a visual Super+Tab switcher via snappy-switcher, wired into the Nix flake + Home Manager configuration.
Changes:
- Add
alt-tab.shand Hyprland keybinds for Alt+Tab / Alt+Shift+Tab window cycling. - Add snappy-switcher flake input, package installation, daemon startup, and config file; bind Super+Tab / Super+Shift+Tab to it.
- Add
bcdependency for the Alt+Tab script’s timing logic.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| home-manager/packages/default.nix | Adds bc and installs snappy-switcher on Linux desktop systems. |
| flake.nix | Adds snappy-switcher as a flake input. |
| config/hyprland/snappy-switcher-config.ini | Introduces snappy-switcher configuration (theme/layout/font). |
| config/hyprland/scripts/alt-tab.sh | New focus-history-based Alt+Tab implementation with session state + dimming feedback. |
| config/hyprland/hyprland.conf | Rebinds Super+Tab to snappy-switcher and adds Alt+Tab bindings. |
| config/hyprland/default.nix | Starts snappy-switcher daemon and installs new config/scripts via xdg.configFile. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| bind = $mod, TAB, exec, snappy-switcher next | ||
| bind = $mod SHIFT, TAB, exec, snappy-switcher prev |
There was a problem hiding this comment.
$mod+TAB is switched to snappy-switcher, but Hyprland is still loading/configuring the hyprexpo plugin (see plugin = ...libhyprexpo.so in config/hyprland/default.nix and the plugin { hyprexpo { ... } } block later in this config). If hyprexpo is no longer used anywhere, consider removing the plugin load/config to avoid unnecessary plugin overhead and keep the config consistent.
| extraConfig = '' | ||
| plugin = ${hyprexpoPlugin}/lib/libhyprexpo.so | ||
| exec-once = ${pkgs.hyprpanel}/bin/hyprpanel | ||
| exec-once = ${snappySwitcher}/bin/snappy-switcher --daemon | ||
| '' |
There was a problem hiding this comment.
Hyprland still loads the hyprexpo plugin (plugin = ...libhyprexpo.so) even though the PR replaces the only visible hyprexpo:expo keybind with snappy-switcher. If hyprexpo is no longer invoked, removing this plugin load (and its config block in hyprland.conf) would reduce startup work and avoid carrying unused plugin configuration.
Summary
This PR adds two complementary window switching mechanisms to Hyprland: a custom Alt+Tab script that mimics Windows/macOS focus-history behavior, and integration with snappy-switcher for Super+Tab visual window switching.
Key Changes
Added
alt-tab.shscript: A comprehensive bash script that implements focus-history-based window switching with the following features:--sameflag to filter windows by class type--visibleflag to limit switching to visible workspacesAdded snappy-switcher integration:
snappy-switcher-config.iniwith Dracula theme and layout customizationUpdated Hyprland keybindings:
Super+Tab(hyprexpo) with snappy-switcher for window switchingAlt+TabandAlt+Shift+Tabfor focus-history-based switchingSuper+Shift+Tabfor reverse direction in snappy-switcherAdded dependencies:
bcpackage for floating-point arithmetic in the alt-tab scriptImplementation Details
The alt-tab script uses temporary state files to track:
This allows it to distinguish between a user holding Alt and repeatedly pressing Tab (continuing session) versus pressing Alt+Tab after a delay (new session). The script also validates that windows still exist before focusing them, preventing errors when windows are closed during switching.
https://claude.ai/code/session_01NNjd5QrBK887SPA9wLsBZx
Summary by cubic
Replaces hyprexpo and the old Alt+Tab script with snappy-switcher for Super+Tab window switching. Adds a Dracula-themed config, starts a daemon at login, and moves the config to its own module.
New Features
Dependencies
Written for commit 02988f9. Summary will update on new commits.