Full disclosure: I'm not a Swift dev, I mostly vibe-coded this
Lightweight macOS menu bar app that shows which apps are active on which macOS space. Relies on yabai as its backend.
1 [Chrome] [WhatsApp] 2 [VS Code] [iTerm2] 5 [LM Studio]
| Colors, including empty | |
| Colors, excluding empty | |
| Mono, including empty | |
| Mono, excluding empty |
- macOS 26.2 (Tahoe) or later
- Apple Silicon (arm64)
- yabai installed and running
brew tap alber70g/tap
brew install --cask spacesbar
# the app is unsigned so remove the quarantine attribute
xattr -dr com.apple.quarantine /Applications/SpacesBar.app-
Download the latest
SpacesBar-<version>-arm64.zipfrom Releases. -
Unzip and move
SpacesBar.appto/Applications. -
Because the build is unsigned, macOS Gatekeeper will block it on first launch. Remove the quarantine attribute:
- run this shell command
xattr -dr com.apple.quarantine /Applications/SpacesBar.app
- run this shell command
-
Open the app. It will appear in the menu bar with no Dock icon.
Every push to master republishes a rolling dev prerelease with an unsigned .app zip built by GitHub Actions. Versioned releases are cut from v* tags.
On first launch SpacesBar writes defaults to ~/.config/spacesbar.json:
{
"hideEmptySpaces": false,
"iconStyle": "colored",
"refreshFallbackSeconds": 5
}iconStyle:colored·monochromehideEmptySpaces: hide spaces with no appsrefreshFallbackSeconds: safety-net poll interval (event-driven refresh is primary)
The same options are available from the menu bar dropdown: icon style, hide empty spaces, copy current output, quit.
Logs: ~/Library/Logs/SpacesBar/spacesbar.log
SpacesBar listens for file-system events on ~/Library/Application Support/SpacesBar/refresh.signal. Wire yabai to touch that file so the menu bar updates instantly instead of waiting for the fallback timer.
Add to ~/.config/yabai/yabairc:
#!/usr/bin/env sh
SIGNAL="$HOME/Library/Application Support/SpacesBar/refresh.signal"
yabai -m signal --add event=space_changed action="touch \"$SIGNAL\""
yabai -m signal --add event=window_created action="touch \"$SIGNAL\""
yabai -m signal --add event=window_destroyed action="touch \"$SIGNAL\""
yabai -m signal --add event=window_moved action="touch \"$SIGNAL\""
yabai -m signal --add event=application_visible action="touch \"$SIGNAL\""
yabai -m signal --add event=application_hidden action="touch \"$SIGNAL\""Reload yabai (brew services restart yabai) and the menu bar will refresh on every yabai event.
Requires Xcode 26.4+ (matches MACOSX_DEPLOYMENT_TARGET = 26.2).
# Open in Xcode
open SpacesBar.xcodeproj
# Or build from CLI
xcodebuild \
-project SpacesBar.xcodeproj \
-scheme SpacesBar \
-configuration Release \
-derivedDataPath build \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
build
open build/Build/Products/Release/SpacesBar.app-
Bump
MARKETING_VERSIONinSpacesBar.xcodeproj/project.pbxproj(or let CI override it via the tag). -
Tag and push:
git tag v1.2.3 git push origin v1.2.3
-
The
releaseworkflow buildsSpacesBar-<version>-arm64.zip, attaches it to a GitHub Release, and — ifTAP_GITHUB_TOKENis configured — opens abump/spacesbar-<version>pull request againstalber70g/homebrew-tapupdatingCasks/spacesbar.rb. Merge the PR to publish the new version on the tap.
To set up the Homebrew tap the first time:
# In a separate repo named `homebrew-tap` under your GitHub account
mkdir -p Casks
cp /path/to/SpacesBar/Casks/spacesbar.rb Casks/spacesbar.rb
git add Casks/spacesbar.rb && git commit -m "Add spacesbar cask" && git pushThen create a fine-grained PAT scoped to alber70g/homebrew-tap with permissions Contents: Read and write and Pull requests: Read and write, and add it to this repo's Actions secrets as TAP_GITHUB_TOKEN. If the secret is missing the release still publishes — only the tap PR is skipped.