https://github.com/z20240/yabai-dockstack
Visual enhancement suite for yabai: stack indicators, cross-space window menu, and Dock window previews.
https://github.com/z20240/yabai-dockstack
dock dockview macos menu-bar screencapturekit stackline swift tiling window-manager window-switcher yabai
Last synced: 5 days ago
JSON representation
Visual enhancement suite for yabai: stack indicators, cross-space window menu, and Dock window previews.
- Host: GitHub
- URL: https://github.com/z20240/yabai-dockstack
- Owner: z20240
- License: mit
- Created: 2026-06-28T15:39:35.000Z (6 days ago)
- Default Branch: main
- Last Pushed: 2026-06-28T18:53:19.000Z (6 days ago)
- Last Synced: 2026-06-28T19:24:08.911Z (6 days ago)
- Topics: dock, dockview, macos, menu-bar, screencapturekit, stackline, swift, tiling, window-manager, window-switcher, yabai
- Language: Swift
- Size: 13.7 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

[](https://github.com/z20240/yabai-dockstack/releases)
[](https://github.com/z20240/yabai-dockstack/stargazers)
[](LICENSE)

English · [繁體中文](README.zh-Hant.md)
Inspired by [stackline](https://github.com/AdamWagner/stackline) and DockView ·
keywords: yabai, stackline, dockview, macOS window manager, tiling, stack indicator,
dock preview, window switcher, menu bar, Mission Control alternative
---
`yabai-dockstack` adds the visual layer that yabai lacks:
- **Stack indicators** — a floating indicator next to each window stack so you see, at
a glance, which apps are stacked and in what order (inspired by **stackline**).
- **Cross-space window menu** — every window grouped by Display → Space in the menu
bar; click to jump and focus.
- **Dock window previews** — hover a Dock icon to peek an app's windows across all
spaces with thumbnails; click to jump (inspired by **DockView**).
It is a clean Swift rewrite — **not** a fork of stackline (which required Hammerspoon).
## Demo
**Stack indicators** — see at a glance which apps are stacked and where.

**Cross-space window menu** — every window grouped by Display → Space; click to jump.

**Jump & focus across spaces** — one click brings you to the window's space.

**Dock window previews** — hover a Dock icon to peek an app's windows; click to jump.

## Features
- 🚦 One floating indicator per stack, placed at the window's outer screen edge.
- 🅰️ **Icon mode** (default): app icons stacked top→bottom in stack-index order;
focused window highlighted. **Flag mode**: slim minimal markers. Toggle from
the menu bar.
- 🔎 **Hover** an indicator → popover with that window's full title.
- 🖱️ **Click** an indicator → focus that window (via yabai).
- 🖥️ Multi-monitor, all visible spaces.
- 🪶 No Hammerspoon. Minimal permissions (no Accessibility, no Screen Recording —
focus is delegated to yabai).
## Requirements
- macOS 14+
- **[yabai](https://github.com/koekeishiya/yabai) — required.** yabai-dockstack is a
companion for yabai and does nothing on its own: every feature reads window/stack
state from `yabai -m query`. Without it the menu bar just shows
**"yabai: not found"**. yabai must be installed, **running**, and set up enough to
create stacks (see yabai's install/configuration guide).
## Install
### Homebrew (recommended)
```sh
brew install --cask z20240/tap/yabai-dockstack
```
Universal (Apple Silicon + Intel). The app is ad-hoc signed but **not
Apple-notarized**, so Gatekeeper would normally block it — the cask strips the
quarantine attribute on install (postflight) so it opens without a prompt. If you
download the release zip manually instead, run once:
`xattr -dr com.apple.quarantine /Applications/yabai-dockstack.app` (or System
Settings → Privacy & Security → **Open Anyway**).
**yabai is required and is NOT installed automatically** (it lives in a third-party
tap that a cask can't auto-tap). Install and start it:
```sh
brew tap koekeishiya/formulae
brew install yabai
yabai --start-service # see yabai's docs for full setup (incl. SIP)
open -a yabai-dockstack
```
If yabai isn't found on launch, the app shows a **setup guide** (Install yabai via
Terminal · Open install guide · Set yabai path) and a red **"yabai not found — set
up…"** item in the menu bar. It starts working automatically once yabai is running.
### Build from source
```sh
./scripts/bundle.sh # produces yabai-dockstack.app
open yabai-dockstack.app
```
> **Toolchain note.** Builds with full Xcode or standalone Command Line Tools. If
> `swift build` complains about the Xcode license, run `sudo xcodebuild -license
> accept`, or build against CLT:
> `DEVELOPER_DIR=/Library/Developer/CommandLineTools ./scripts/bundle.sh`.
> The XCTest suite needs full Xcode; a toolchain-independent check is
> `swift run yst-selftest`.
### First launch
On launch the app:
- **auto-detects** the yabai binary (Homebrew Apple Silicon / Intel / nix, then `which`);
- **auto-registers** the yabai signals it needs (pointing at its own path), so you
never edit `~/.yabairc`. This is idempotent — re-running is safe.
The menu-bar menu shows **yabai: connected ✓** when it found yabai, then a live
list of every window grouped by **Display → Space** (the focused window is
checked). Spaces show their custom yabai label if set (`yabai -m space --label`),
otherwise "Space N". Clicking a window jumps to its space and focuses it. It also
offers:
- **Settings…** — a window to adjust style (icon/flag), indicator size, focused/
unfocused opacity, flag color, background pill + color, "keep inside window gap",
full-width side, **Debounce (ms)** / **Poll interval (ms)**, **yabai path**
(blank = auto-detect; a manual escape hatch if detection fails), and **Start at
login**. Changes apply live and are saved.
- **Re-register yabai signals** — re-applies the signals if yabai was restarted.
> First launch of an unsigned app: macOS Gatekeeper will block it once. Right-click
> the app → **Open**, or run `xattr -dr com.apple.quarantine yabai-dockstack.app`.
> To remove this step entirely you'd need an Apple Developer ID signature +
> notarization.
> Move `yabai-dockstack.app` to `/Applications` before enabling **Start at login**
> so the login item points at a stable location.
### Manual setup (optional / fallback)
If you prefer to manage signals yourself (e.g. persisted in your dotfiles), the
legacy helpers are still provided: `examples/yabairc-signals.sh` (set `BIN`, source
from `~/.yabairc`) and `examples/com.yabai-dockstack.agent.plist` (a LaunchAgent).
You don't need these for the default flow.
## Configuration
Optional config file at `~/.config/yabai-dockstack/config.json`. Any omitted key
falls back to its default. All keys:
```json
{
"yabaiPath": "/opt/homebrew/bin/yabai",
"socketPath": "/tmp/yabai-dockstack.sock",
"style": "icon",
"cellSize": 32,
"offset": 4,
"focusedAlpha": 1.0,
"unfocusedAlpha": 0.4,
"debounceSeconds": 0.05,
"pollSeconds": 3.0,
"fullWidthSide": "left",
"edgeInset": 6,
"flagColor": "#4C8DFF"
}
```
- `fullWidthSide`: which edge the indicator goes to for a near-full-width window
(≥90% of the screen), where there's no clear left/right bias. `"left"` (default)
or `"right"`. Narrower windows still follow their on-screen position.
- `edgeInset`: extra pixels to keep the indicator off the very screen edge when it
would otherwise clamp there (so it doesn't sit on a window's rounded corner).
- `flagColor`: flag-mode bar color, `"#RRGGBB"` or `"#RRGGBBAA"`.
- `showBackground`: draw a rounded backing pill behind the indicators so they read
as a floating chip (helpful over full-width windows). `true`/`false`.
- `backgroundColor`: backing pill color, `"#RRGGBB"` / `"#RRGGBBAA"`.
Most of these are adjustable from **Settings…** in the menu bar (no file editing
needed). The config file is still created automatically on first launch at
`~/.config/yabai-dockstack/config.json` for anyone who prefers editing by hand.
- `debounceSeconds`: lower = snappier focus highlight, higher = fewer redraws
during window drags.
- `style`: `"icon"` or `"flag"`. The menu-bar "Toggle icon/flag" item flips this
and writes it back to the config file.
- `debounceSeconds`: minimum interval between redraws when events arrive in a
burst.
- `pollSeconds`: low-frequency fallback refresh, in case a signal is missed.
Use **Reload config** in the menu after editing the file by hand.
## How it works
```
yabai window event ──▶ ~/.yabairc signal runs: yabai-dockstack --refresh
│ (pokes unix socket)
▼
SignalListener ──▶ RefreshCoordinator (debounced)
│
yabai -m query --windows ▼
StackBuilder ──▶ IndicatorLayout + CoordinateMapper
│
OverlayRenderer (one NSPanel per stack)
```
A low-frequency timer also triggers a refresh as a safety net. Focusing a window
is delegated to `yabai -m window --focus`, so the app itself needs no
Accessibility permission.
## Testing
- **Toolchain-independent self-test** (works with Command Line Tools), covering
decode, stack grouping, layout, coordinate mapping, config, diffing, and the
socket round-trip:
```sh
swift run yst-selftest
```
Pass a captured query to exercise the live decode path:
```sh
yabai -m query --windows > /tmp/q.json && swift run yst-selftest /tmp/q.json
```
- **Full XCTest suite** (requires Xcode):
```sh
swift test
```
- **Replay mode** — render indicators from a static JSON dump without live yabai,
for visual checks:
```sh
.build/debug/yabai-dockstack --replay Tests/YabaiDockstackKitTests/Fixtures/query-sample.json
```
## Acceptance checklist
1. `./scripts/bundle.sh` produces `yabai-dockstack.app`.
2. `open yabai-dockstack.app` → a `▦` item appears in the menu bar; the menu shows
**yabai: connected ✓** (signals were auto-registered — verify with
`yabai -m signal --list | grep yabai-dockstack`).
3. Create a stack: `yabai -m window --stack next`.
5. Icons appear beside the stacked window, top→bottom in stack order; the focused
window's icon is brightest.
6. Hover an icon → a tooltip shows that window's title (multiple VSCode windows
show distinct project names).
7. Click a non-focused icon → that window comes to front; highlight updates.
8. Menu bar → **Toggle icon/flag** switches appearance and persists it.
9. On a second display, indicators appear correctly placed on that screen.
10. LaunchAgent makes it start at login.
## Dock window previews
Hover a **Dock app icon** to pop up that app's windows across all spaces; click one
to jump to its space and focus it (like a Windows-taskbar peek / DockView).
- **Thumbnails:** a live thumbnail is shown for windows on a currently-visible
space. For windows on **other spaces** macOS cannot produce a live image
(verified: ScreenCaptureKit returns `-3811` for off-screen windows), so the app
shows the **last cached thumbnail** (captured while the window was on-screen) or,
if none, the **app icon + title**. Either way the entry is clickable.
- **Permissions:** this feature needs **Accessibility** (to detect the hovered
Dock icon) and **Screen Recording** (to capture thumbnails). Open **Settings →
Permissions**: it shows each permission's live status (✓ / ✗) with a **Grant…**
button that requests the permission and opens the exact System Settings pane —
one at a time, so the two prompts don't collide. Once Accessibility is granted
the feature activates automatically (no relaunch). If a permission is missing it
stays dormant — the core stack indicators need no permissions at all.
- **Toggle:** Settings → **Dock window previews** (on by default).
## Is it safe? (signing & permissions)
yabai-dockstack is open-source (MIT) and free. It is **intentionally not signed
with a paid Apple Developer ID nor notarized** — that costs $99/yr and this is a
community project, not a paid product. Consequences and what that means for you:
- **Gatekeeper warns on first open.** Open it once via right-click → **Open**,
or System Settings → Privacy & Security → **Open Anyway**, or
`xattr -dr com.apple.quarantine /Applications/yabai-dockstack.app`. The Homebrew
cask strips quarantine for you, so `brew install --cask` just opens.
- **Don't want to trust a prebuilt binary?** Build it yourself:
`./scripts/bundle.sh`. It's a few hundred lines of Swift you can read end to end.
- **No network, no analytics, no phone-home.** It only shells out to your local
`yabai` and (for previews) captures windows locally.
**Permissions — only what each feature needs:**
| Feature | Permissions |
|---|---|
| Stack indicators + cross-space window menu | **none** (just `yabai -m query` / `--focus`) |
| Dock window previews | **Accessibility** (detect the hovered Dock icon) + **Screen Recording** (window thumbnails) |
If you don't use Dock previews, you can grant nothing at all. Permissions are
requested only when that feature is enabled, and the app stays functional without
them.
## App icon
The icon lives in `assets/`. Two variants are generated from
`assets/icon-source.png`:
- `AppIcon-native.icns` — content padded on a transparent canvas with rounded
corners (macOS HIG style). **This is the bundled default** (`AppIcon.icns`).
- `AppIcon-fullbleed.icns` — the source as-is, square corners.
See `assets/icon-preview.png` for a side-by-side. To switch the bundled icon to
full-bleed, copy `assets/AppIcon-fullbleed.icns` over `assets/AppIcon.icns` and
rebuild. To regenerate everything after editing the source:
```sh
python3 scripts/make-icons.py # requires Pillow + macOS iconutil
```
> Note: the name/logo use "yabai". This is an **unofficial** community tool and is
> not affiliated with the yabai project.
## License
MIT.