Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

202 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cyclist

A keyboard-driven app switcher for macOS. No thumbnails, no window screenshots - just a list of app icons, names, and window titles you switch between with Cmd+Tab.

Beta: Cyclist is pre-1.0. Backward compatibility is not guaranteed until version 1.0.0 is reached.

Hold Cmd to open a vertical text list where every window is its own row, some tagged 'other space'; move the selection to a Safari window that lives in a fullscreen Space, release Cmd, and the display jumps straight to that Space.

What it does

  • Replaces the native Cmd+Tab switcher with a vertical, text-only list in most-recently-used app order. Every window gets its own row (App - Window title), so two Safari windows are two entries, and within an app the rows are ordered by when you last used each window.
  • A quick Cmd+Tab tap returns to the previous window, wherever it lives - even between two windows of the same app, and even across Spaces or workspaces.
  • Windows in other Spaces (including native fullscreen) get their own rows too. With Screen Recording permission granted their titles are live; without it, each row shows the last title Cyclist saw while that window was visible. Selecting a row jumps straight to that Space.
  • A separate binding (Cmd+`) switches among the windows of the frontmost app in most-recently-used order, including minimized ones and windows in other Spaces (native fullscreen included) - things the native window switcher skips. A quick tap bounces between the app's last two windows.
  • Ctrl+Left/Right walks the native Spaces of the active display (the one holding the menu bar) in Mission Control order - user desktops and fullscreen Spaces alike - instantly and without animation. Arriving on a desktop focuses its top window, so leaving a fullscreen app always lands somewhere concrete.
  • The trackpad's Spaces swipe (three or four fingers, per System Settings > Trackpad > More Gestures) drives that same navigation: Cyclist intercepts the gesture before the Dock sees it and steps instantly instead of playing the animated transition. The system gesture must stay enabled - it is what makes macOS emit the gesture events at all. A Settings toggle ("Trackpad swipe navigation") hands the gesture back to macOS at any time.
  • With AeroSpace running, its workspaces join that ring in place of the desktop hosting them, so Ctrl+Left/Right walks workspace 1 ... workspace N, fullscreen Spaces seamlessly - workspace steps go over AeroSpace's socket, and crossing from a fullscreen Space lands on the ring-adjacent workspace. Windows parked in hidden workspaces appear in the switcher as workspace N rows; selecting one switches there. A workspace whose windows all went native-fullscreen is hollow - its windows display on their own Spaces and visiting it shows a bare desktop - so the ring skips it by default and the fullscreen Space itself is the stop; the showHollowWorkspaces setting restores those stops. The integration is opt-in (Settings > AeroSpace, or the aerospaceIntegration default) and everything falls back to plain native behavior the moment AeroSpace is absent or disabled.
  • Every piece is optional: independent Settings toggles turn off the app switcher, the window switcher, keyboard Space navigation, and the trackpad swipe. A disabled binding passes through to macOS immediately, so Cyclist can run as just a Spaces/workspace navigator - or just a switcher.
  • Independent settings control what shows up. Two govern which windows are eligible in both the app switcher and the window switcher:
    • include minimized windows (the ones parked in the Dock)
    • include windows in other Spaces (including native fullscreen)
  • Two more apply to the app switcher only:
    • include hidden apps (Cmd+H)
    • include running apps with no windows at all (off by default; selecting one behaves like clicking its Dock icon, so the app reopens a window)

Keybindings

The four global shortcuts below are the defaults - rebind them in Settings (click the shortcut, press the new keys) or with defaults write (see Configuration).

Global - work anytime:

Keys Action
Cmd+Tab (quick tap) Switch to the previous window (any app)
Cmd+Tab (hold Cmd) Open the switcher list (Shift reverses)
Cmd+` Switch windows of the frontmost app
Ctrl+Left / Right Previous / next workspace or fullscreen Space
Trackpad swipe Previous / next workspace or fullscreen Space

While the switcher is open (Cmd held):

Keys Action
Tab / Shift+Tab Advance / reverse the selection
Up/Down, K/J Move the selection
Q Quit the selected app
W Close the selected window
, (Cmd+,) Open the Settings window
Esc Cancel
Release Cmd Switch to the selected item

In the app switcher, Quit and Close keep the list open: the affected rows leave and the selection moves to a neighbor. In the window switcher, quitting the app ends the session (every row belonged to it).

Requirements

  • macOS 26 (Tahoe)
  • Accessibility permission (System Settings > Privacy & Security > Accessibility) - required for the global Cmd+Tab hook and for reading window state. While it is missing, Settings shows a warning with a jump straight to the pane
  • Screen Recording permission, optional and off by default - macOS gates the titles of windows in other Spaces behind it, but titles seen while a window was visible are cached, so day-to-day the cache covers those rows; the permission only adds titles for windows not seen since boot. Enable "Live titles from other Spaces" in Settings to use it (Cyclist never requests the permission while the toggle is off); the actual grant is the + button in the Screen Recording pane, and Settings links straight there while the grant is missing

Install

Homebrew

brew install --cask pszypowicz/tap/cyclist

Release builds are signed with a Developer ID certificate and notarized, so Gatekeeper accepts them as-is; the cask launches the app after install.

Direct download

Grab Cyclist.dmg from the latest release, open it, and drag Cyclist into Applications.

Build from source

scripts/build-app.sh
cp -R build/Cyclist.app /Applications/
open /Applications/Cyclist.app

The build script signs with your "Developer ID Application" certificate - the same identity releases use, so the Accessibility grant survives rebuilds and release installs alike. Without one, pass --identity "Apple Development" or --identity adhoc explicitly; a missing identity is a hard error, never a silent downgrade. See scripts/build-app.sh --help for options.

On first launch Cyclist prompts for Accessibility permission and activates itself once granted. It lives in the menu bar (no Dock icon); the menu holds Settings, About, and Quit - quitting is how every hook releases and the native shortcuts return. The Settings window holds the list options, trackpad swipe navigation, and a native Launch at Login toggle (registers with System Settings > General > Login Items). The menu bar icon itself is optional (Settings > General): with it hidden, hold the switcher open and press , to reach Settings, or just relaunch Cyclist - reopening the app always presents the Settings window (where Cmd+Q still quits).

Configuration

Every setting lives in standard user defaults under the cz.szypowi.cyclist domain - the Settings window and defaults write are the same mechanism, and external writes apply to a running Cyclist immediately:

defaults write cz.szypowi.cyclist aerospaceIntegration -bool true
defaults write cz.szypowi.cyclist switchAppsShortcut "alt+tab"
Key Type Default
appSwitcher bool true
windowSwitcher bool true
showHiddenApps bool true
showMinimizedWindows bool true
showWindowsInOtherSpaces bool true
showAppsWithNoWindow bool false
liveOtherSpaceTitles bool false
trackpadSwipe bool true
keyboardSpaceNavigation bool true
showMenuBarIcon bool true
aerospaceIntegration bool false
showHollowWorkspaces bool false
switcherSize string default
demoHud bool false
switchAppsShortcut string cmd+tab
switchWindowsShortcut string cmd+backtick
previousSpaceShortcut string ctrl+left
nextSpaceShortcut string ctrl+right

switcherSize scales the switcher overlay - its text, icons, and spacing together - as one preset: small, default, large, or extraLarge. Larger reads more easily on a big or high-resolution display; smaller fits more windows on screen. A new value takes effect the next time the switcher opens. For system-wide magnification beyond this, macOS Zoom (System Settings > Accessibility > Zoom) enlarges any app.

demoHud is for screen recordings: every hotkey press and trackpad swipe Cyclist acts on flashes briefly at the bottom of the screen (⌃→, Swipe →), and chain navigation adds the transition it performs as a second line (workspace 2 → Safari (fullscreen)). Keystroke visualizers cannot show these inputs - Cyclist consumes them before other apps see them.

Shortcut strings are modifiers and a key joined with +: cmd, alt, ctrl, shift plus a key name (tab, backtick, left, right, up, down, space, return, a letter, a digit, ...). A binding needs at least one non-shift modifier (shift is the reverse key); a string that fails these rules is a hard error - Cyclist quits rather than silently reverting to a default, whether the bad write happens before launch or while it runs.

Known limitations

  • Cyclist consumes the Previous/Next Space shortcuts (Ctrl+Left/Right by default) for chain navigation; disable the equivalent Mission Control shortcuts if you do not want both meanings, or turn off "Keyboard Space navigation" in Settings. Quitting Cyclist brings the native behavior back.
  • Cyclist also consumes the trackpad Spaces-swipe gesture while "Trackpad swipe navigation" is on; flip the Settings toggle to get the native animated swipe back without quitting. One swipe is one step - a long swipe does not scrub across several Spaces.
  • While a password field has secure input enabled, macOS withholds keystrokes from event taps, so Cmd+Tab temporarily falls through to the native switcher.
  • Same-app window rows for other Spaces rely on the window-server list; their titles need Screen Recording permission or a previous sighting of the window (same rule as the app switcher's other-Space rows).
  • The AeroSpace bridge speaks the server's socket protocol (version 1) and tracks the workspaces of AeroSpace's focused monitor. With several native desktops the ring only expands the current one.
  • The list is keyboard-only; the panel ignores mouse clicks.
  • Recording a shortcut captures through the same event tap the shortcuts use, so it needs the Accessibility grant.

Acknowledgements

Cyclist relies on techniques that other projects worked out first:

  • Near-instant cross-Space jumps (Ctrl+Left/Right and jumping to another Space's window) post the synthetic Dock-swipe gesture encoding from Space Rabbit - the same approach seen in InstantSpaceSwitcher and Spaceman. macOS performs no Space transition for a plain app activation, so the switch is driven by posting the gesture a trackpad would.
  • Window and focus detection follows AltTab and yabai: reading focus changes, Space membership, and real-window state from the WindowServer's own notification stream and window records instead of the Accessibility API, and the tag/attribute predicate that filters out a fullscreen Space's companion windows.
  • The AeroSpace bridge talks directly to AeroSpace's socket.

About

Text-only alt-tab style app switcher for macOS

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages