Switcher Service in vorssaint-utils: Features, Configuration, and Implementation Guide
The Switcher service in vorssaint-utils provides a highly configurable macOS window-switching panel with icon-row and grid layouts, customizable shortcuts, live window previews, and advanced session management.
The vorssaint-utils repository delivers a powerful App Switcher feature through its Switcher service, offering macOS users a customizable alternative to the native Command-Tab interface. This Swift-based implementation supports multiple display modes, per-app rules, and robust event handling for seamless window navigation. Whether you need a compact icon bar or a full thumbnail grid, the Switcher service adapts to your workflow with extensive configuration options.
Core Window-Switching Capabilities
The Switcher service operates as the central orchestrator for the App Switcher feature, handling everything from window enumeration to UI rendering. At its core, AppSwitcher.swift manages the session lifecycle, while SwitcherView.swift renders the actual panel using SwiftUI.
The service supports two primary visual modes controlled by the iconRowMode and simpleMode settings. In Sources/Vorssaint/UI/Switcher/SwitcherView.swift, the body property (lines 56-64) automatically selects between iconRowPanel and standardPanel based on these user preferences. This allows the switcher to present either a compact horizontal row of application icons or a comprehensive grid showing live window thumbnails.
Panel Layout Modes: Icon-Row vs. Grid
Icon-Row Layout
The icon-row layout displays applications as a compact horizontal strip with configurable spacing and geometry. The SwitcherIconRowLayout.compute(...) method in Sources/Vorssaint/Services/Switcher/SwitcherSupport.swift (lines 79-106) calculates:
- Visible icon count based on screen width
- Panel dimensions and positioning
- Horizontal spacing between icons
- Whether shortcut hint overlays appear
This layout supports hover-edge behavior: when the cursor remains on the last visible icon, the row automatically scrolls after a configurable interval. The timing constants iconRowEdgeHoverInterval, iconRowEdgeHoverRepeatInterval, and iconRowEdgeHoverAnimationDuration in SwitcherSupport.swift (lines 72-86) control this interaction.
Grid Layout with Live Previews
The standard grid layout provides a visual overview of all open windows using captured thumbnails. The SwitcherGrid struct dynamically calculates rows and columns based on screen real estate and window count. Preview resolution is controlled by captureAlphaGridSize in SwitcherSupport.swift (lines 69-71), allowing performance tuning based on hardware capabilities.
Users can enable minimal-preview mode by setting the minimalPreviews AppStorage key, which disables the thin outline stroke around each thumbnail for a cleaner aesthetic (handled in SwitcherView.swift, lines 80-84).
Advanced Configuration Options
Screen Placement Control
The SwitcherScreenPlacement enum in SwitcherSupport.swift (lines 93-104) defines three positioning strategies:
- Pointer screen: Opens on the display containing the cursor
- Menu-bar screen: Opens on the primary display with the menu bar
- Active window screen: Opens on the display containing the currently focused window
Appearance Delay
Users can configure the delay before the panel appears when holding the activation shortcut. The default value is stored in SwitcherSupport.defaultAppearanceDelayMilliseconds, with sanitizing helpers ensuring valid ranges (lines 33-38).
Session Scope Modes
The SwitcherSessionScope enum controls enumeration behavior:
allApps: Lists all applications with their windowsfrontmostApp: Shows only windows belonging to the currently active application
Keyboard Shortcuts and Navigation
Global Shortcut Handling
The service intercepts system shortcuts using SwitcherNativeSymbolicHotKey (lines 48-60 in SwitcherSupport.swift), mapping hot-key IDs for:
command-tabandcommand-shift-tabnavigation- Next/previous window selection
- Optional takeover of native macOS shortcuts
Shortcut Hint Overlays
When enabled via showsShortcutHints, the panel displays visual indicators for available keyboard shortcuts. The SwitcherShortcutHints struct (lines 28-32) stores the hint strings rendered in the UI.
Letter Actions
While the panel is visible, specific key presses trigger SwitcherLetterAction behaviors:
W: Close the selected windowQ: Quit the selected applicationS: Pin the search field
Search and Pinning
The switcher includes a real-time search field that filters the window list. The pinning feature (isSearchPinned) allows users to continue typing without holding the modifier key. In SwitcherView.swift (lines 24-32), a visual chip indicates the pinned state, and the searchQuery property drives the filtering logic.
Session Management and App Behavior
Windowless App Entries
The SwitcherWindowlessApps enum (lines 62-73) handles applications without open windows:
off: Hides windowless appsfinder: Shows only Finder when it has no windowsall: Displays all running apps regardless of window state
App-Specific Rules
Per-application overrides allow fine-grained control via the SwitcherAppRule enum (lines 17-27):
- Force apps to appear without windows
- Restrict apps to windows-only display
- Hide specific applications entirely
Hidden App Tracking
The service maintains state for hidden applications across sessions using functions like isConfirmedHiddenAppWindow (demonstrated in MetricsTests.swift, lines 2769-2777), ensuring hidden apps reappear correctly in subsequent switching sessions.
Merge Tabs Option
When mergeWindowsByApp is enabled (stored in AppStorage and honored in SwitcherView.swift, lines 51-52), multiple windows from the same application collapse into a single selectable entry, reducing panel clutter.
Robust Concurrency Handling
AppSwitcher.swift (lines 21-46) implements thread-safe session management using SwitcherPendingKeyDecision and SwitcherPendingSessionStart enums with lock-protected state. This prevents race conditions during concurrent event-tap threads and ensures graceful cancellation when users release modifier keys before the panel appears.
Implementation Architecture
The Switcher service relies on several coordinated components:
Sources/Vorssaint/Services/Switcher/SwitcherSupport.swift: Central definitions including enums, layout calculations, and timing constantsSources/Vorssaint/Services/Switcher/AppSwitcher.swift: Main orchestrator class managing enumeration, shortcuts, and UI stateSources/Vorssaint/UI/Switcher/SwitcherView.swift: SwiftUI view implementing the panel renderingSources/Vorssaint/Services/Switcher/SwitcherModels.swift: Data models forSwitcherItemand window identifiersSources/Vorssaint/UI/Settings/SwitcherAppRulesList.swift: Settings interface for per-app rule configuration
Code Examples
Activating the Switcher Programmatically
import Vorssaint
// Access the shared singleton instance
let switcher = AppSwitcher.shared
// Start a session listing all applications
switcher.startSession(scope: .allApps, reversed: false)
// Navigate using Tab / Shift-Tab or arrow keys
Querying Window State
let windows = switcher.windows // Array of SwitcherItem
let selected = switcher.selectedIndex // Currently highlighted index
let totalCount = switcher.totalWindowCount // Total window count
Configuring User Preferences
// Enable compact icon-row mode
UserDefaults.standard.set(true, forKey: DefaultsKey.switcherIconRowMode)
// Display shortcut hint overlays
UserDefaults.standard.set(true, forKey: DefaultsKey.switcherShowShortcutHints)
Customizing Appearance Timing
// Set 250ms delay before panel appears
UserDefaults.standard.set(250, forKey: DefaultsKey.switcherAppearanceDelay)
Defining App-Specific Rules
// Force Music app to appear even without windows
let rules: [String: SwitcherAppRule] = ["com.apple.Music": .showWithoutWindows]
UserDefaults.standard.set(SwitcherAppRule.storedValue(rules),
forKey: DefaultsKey.switcherAppRules)
Managing Windows and Search
// Close a specific window
if let window = switcher.windows.first {
switcher.closeWindow(window)
}
// Pin the search field to release modifier key
switcher.isSearchPinned = true
Summary
- The Switcher service provides dual layout modes (icon-row and grid) with automatic geometry calculations via
SwitcherIconRowLayout.compute(). - Configuration options include screen placement strategies, appearance delays, and session scopes (
allAppsvsfrontmostApp). - Advanced features encompass windowless app entries, per-app override rules via
SwitcherAppRule, and hidden app state preservation. - Navigation enhancements include global shortcut interception, letter actions (
Wfor close,Qfor quit), and a pinnable search field. - Thread-safe implementation uses
SwitcherPendingKeyDecisionand lock-protected state inAppSwitcher.swiftto handle concurrent event taps safely.
Frequently Asked Questions
How does the Switcher service handle apps without open windows?
The service uses the SwitcherWindowlessApps enum defined in SwitcherSupport.swift to control visibility of windowless applications. You can configure it to hide all windowless apps, show only Finder, or display all running applications regardless of window state. This setting integrates with the Accessibility-based window enumeration performed by WindowEnumerator.swift.
What is the difference between icon-row mode and simple mode?
Icon-row mode displays a horizontal strip of application icons with optional shortcut hints and hover-edge scrolling. Simple mode (controlled by the simpleMode setting) further strips down the interface by hiding live previews entirely, showing only the icon bar. Simple mode is ideal for low-resource environments or users who prefer minimal visual overhead, as implemented in SwitcherView.swift lines 94-100.
Can I customize which keyboard shortcuts activate the switcher?
Yes. The SwitcherNativeSymbolicHotKey enum maps symbolic hot-key IDs that the service registers with the WindowServer. While the default implementation uses Command-Tab bindings, the architecture supports custom shortcut takeovers. The AppSwitcher.swift class handles the global event tap and translates these into startSession calls with appropriate reversed parameters for backward navigation.
How does the search pinning feature work?
When you press the S key while the switcher is visible (as defined in SwitcherLetterAction), the isSearchPinned boolean toggles to true. This allows you to release the modifier key while continuing to type your search query. The UI displays a visual chip indicating the pinned state, and the searchQuery property filters the windows array in real-time without requiring held keys.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →