How the Vorssaint Utils App Switcher Captures and Displays Window Thumbnails

The app switcher leverages ScreenCaptureKit to enumerate windows, stores 512‑pixel edge thumbnails in a 160 MiB LRU NSCache, and renders them through SwiftUI’s SwitcherGridCard with automatic fallback to application icons.

The vorssaint-utils repository provides a macOS utility suite featuring an App Switcher that generates live window previews. Understanding how this component captures and displays window thumbnails requires examining its three‑tier architecture: window enumeration, bitmap caching, and SwiftUI rendering.

The Thumbnail Pipeline Architecture

The implementation spans three core services that handle distinct stages of the preview lifecycle.

Window Enumeration with ScreenCaptureKit

In Sources/Vorssaint/Services/Switcher/WindowEnumerator.swift, the WindowEnumerator class interfaces with ScreenCaptureKit to produce the list of capturable windows. It queries the system for all SCWindow objects, filters out non‑interactive or system‑level windows, and returns a lightweight WindowItem struct for each candidate. This enumeration runs whenever the switcher opens, ensuring the UI reflects the current window state.

Thumbnail Capture and Caching

Sources/Vorssaint/Services/Switcher/WindowPreviewProvider.swift contains the capture logic. For each WindowItem, the provider invokes SCWindow.captureThumbnail(maxEdge: 512) to generate a high‑resolution bitmap suitable for Retina displays. The resulting CGImage is wrapped in an NSImage and stored in an NSCache instance keyed by the window’s UUID string. The cache is configured with a totalCostLimit of 160 MiB; each entry calculates its cost as bytesPerRow × height, and the cache automatically evicts the least‑recently‑used thumbnails when this budget is exceeded. If a window becomes unavailable or capture fails, the provider emits a nil value, signaling the UI to use a fallback.

SwiftUI Rendering and Fallback Handling

The presentation layer lives in Sources/Vorssaint/UI/Switcher/SwitcherView.swift. The SwitcherView iterates over the window items, passing each to a SwitcherGridCard. This card inspects the cached thumbnail via the provider’s API; if present, it draws the image inside a rounded rectangle using layout constants defined in Sources/Vorssaint/Services/Switcher/SwitcherSupport.swift. When the cache returns no image, the card renders the window’s application icon instead, ensuring the grid remains visually consistent even for protected or minimized windows.

Working with the Thumbnail Provider

Developers can interact with the thumbnail pipeline directly through the public APIs exposed in the switcher service layer.

Manual Thumbnail Capture

To capture a thumbnail outside the standard provider flow, use ScreenCaptureKit directly:

import ScreenCaptureKit

func fetchThumbnail(for window: SCWindow) async -> NSImage? {
    guard let cgImage = await window.captureThumbnail(maxEdge: 512) else {
        return nil
    }
    return NSImage(cgImage: cgImage, size: .zero)
}

Observing Thumbnail Updates

The WindowPreviewProvider exposes an onUpdate closure that fires whenever a new thumbnail enters the cache. Integrate this into a SwiftUI view as follows:

import Vorssaint

struct CustomSwitcher: View {
    @StateObject private var provider = WindowPreviewProvider()
    @State private var thumbnails: [UUID: NSImage] = [:]

    var body: some View {
        LazyVGrid(columns: [.adaptive(minimum: 200)]) {
            ForEach(provider.items) { item in
                SwitcherGridCard(item: item, image: thumbnails[item.id])
            }
        }
        .onAppear {
            provider.onUpdate = { windowID, image in
                thumbnails[windowID] = image
            }
            provider.startUpdating()
        }
    }
}

Inspecting the Cache Directly

Access cached images using the window identifier as the key:

let key = window.id.uuidString as NSString
if let image = provider.thumbnailCache.object(forKey: key) {
    // Use cached NSImage
    imageView.image = image
}

Key Source Files

Summary

  • WindowEnumerator uses ScreenCaptureKit to enumerate live windows and produce WindowItem descriptors.
  • WindowPreviewProvider captures 512‑pixel edge thumbnails via SCWindow.captureThumbnail() and stores them in a 160 MiB LRU NSCache to balance visual quality with memory constraints.
  • SwitcherGridCard renders cached thumbnails inside rounded rectangles, falling back to application icons when captures fail or windows are inaccessible.
  • The pipeline is reactive; the provider’s onUpdate closure triggers SwiftUI view refreshes whenever new thumbnails arrive.

Frequently Asked Questions

How does the app switcher handle windows that cannot be captured?

When SCWindow.captureThumbnail() returns nil or throws an error, WindowPreviewProvider skips the cache update and signals the UI layer to display the window’s application icon instead. This ensures the switcher grid remains populated and usable even for protected, minimized, or full‑screen windows that ScreenCaptureKit cannot access.

What is the maximum resolution of the window thumbnails?

The provider requests thumbnails with a maxEdge of 512 points, which renders as 1024 pixels on Retina displays. This value is defined in SwitcherSupport.swift and provides sufficient detail for the grid view while keeping the per‑image memory footprint small enough to fit within the 160 MiB cache budget.

How does the thumbnail cache prevent memory leaks?

The NSCache in WindowPreviewProvider.swift is initialized with a totalCostLimit of approximately 160 megabytes. Each cached image reports its size in bytes as its cost; when the cumulative cost exceeds the limit, the cache automatically discards the least‑recently‑used entries. This LRU eviction policy ensures that long‑running sessions do not consume unbounded memory.

Can the thumbnail provider be used independently of the switcher UI?

Yes, WindowPreviewProvider is designed as a standalone service. Any view controller or SwiftUI view can instantiate the class, assign a closure to onUpdate, and call startUpdating() to receive thumbnail updates without importing the full SwitcherView hierarchy, making it suitable for custom window managers or accessibility tools.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →