A fast, fuzzy-filtered window-switching pie menu for Hyprland, powered by Kando.
⚠️ AI DISCLOSURE This application was almost entirely vibecoded using deepseek-v4-flash. If desired, I can package my entire session history with the application as a reference. At this point, the application does most of what I want in an ALT+TAB switcher, so I will be focusing on refactors to make menu rebuilding, overflow queueing, and other mechanisms more elegant. The application is essentially one monolithic node js script that needs to be rearranged into a more appropriate code friendly format.
- Add functionality to scrape the xdg desktop directory and allow for fast filter access to all desktop apps in the users system
-
Kando — the pie menu renderer. KAATWS connects to Kando's IPC WebSocket server, sends it a dynamic menu tree built from your running windows, and listens for selection/cancel events. Without Kando, there is no pie menu.
-
Hyprland — the Wayland compositor. KAATWS talks to Hyprland via
hyprctlto enumerate windows (hyprctl clients -j), detect the currently focused app (hyprctl activewindow -j), and focus specific windows (hyprctl dispatch). Without Hyprland, there are no windows to switch between.
These are hard dependencies. This is not a general-purpose window switcher. This is a Kando pie menu that queries Hyprland's state.
KAATWS is a standalone Node.js script (kaatws.js) that:
- Collects all mapped windows from Hyprland via
hyprctl clients -j. - Groups them by window class (app name).
- Builds a hierarchical pie menu — one slice per app, with submenus for apps that have multiple windows.
- Sends the menu to Kando's IPC server, which renders it as a radial menu under your cursor.
- Lets you navigate with
Tab/Shift+Tabto cycle selection, type letters to fuzzy-filter, press1–6to jump to a slot, orEnterto select. Submenus auto-expand when there's only one match. - Focuses the selected window (or launches a default app if it wasn't running).
Edit KAATWS.json in this directory:
| Field | Default | Description |
|---|---|---|
defaultApps |
[] |
Apps to show as launchable shortcuts even when not running |
hiddenWindows |
[] |
Window matchers ({ class?, title? }) to exclude from the switcher |
iconTheme |
"fireshark" |
Name of the Kando icon theme to use |
iconThemeDirectory |
~/.config/kando/icon-themes |
Where Kando icon themes live |
maxItems |
8 |
Maximum items visible on one page (overflow creates submenus) |
submenuThreshold |
2 |
Minimum windows needed before apps get a submenu |
truncationLimit |
30 |
Max chars before window titles are truncated with … |
node kaatws.jsOr, if you assigned a keybind (e.g. via Hyprland's bind = $mainMod, Tab, exec, node ~/.config/kaatws/kaatws.js), just press it.
| Key | Action |
|---|---|
Tab / Shift+Tab |
Cycle selection clockwise / counter-clockwise |
ALT+1–ALT+0 |
Select / expand the slot at that position (ALT+1=1st, …, ALT+9=9th, ALT+0=10th) |
| Letter keys | Incremental fuzzy filter by app/window name |
Backspace |
Remove last character from filter |
Enter |
Select focused item (expand submenu or focus/launch) |
Ctrl+H |
Return to root level |
Escape |
Close and exit |
Hyprland ──hyprctl──▶ KAATWS ──WebSocket IPC──▶ Kando (pie menu)
▲ │ │
└──hyprctl focus───────┘ │
│ │
└── user clicks/presses key ────────────┘
- KAATWS calls
hyprctl clients -jto enumerate all visible windows. - Windows are grouped by class, sorted, and converted into a Kando menu tree.
- KAATWS connects to Kando's IPC WebSocket and sends the tree via
show-menu. - The user navigates the pie menu; selection events flow back over the WebSocket.
- On selection, KAATWS calls
hyprctl dispatchto focus the target window (orexecto launch a default app).
- Hyprland (Wayland compositor) — the
hyprctlCLI must be available. - Kando (pie menu application) — must expose its IPC WebSocket (it does by default on the latest releases).
- Node.js ≥ 14 — the
wspackage is the only runtime dependency. inputgroup membership — KAATWS reads/dev/input/event*for keyboard capture. Add your user to theinputgroup if you haven't already:sudo usermod -aG input $USER && reboot.
MIT