Vorssaint App Switcher Window Thumbnail Capture Mechanism Explained

The Vorssaint app switcher captures window thumbnails using ScreenCaptureKit’s SCWindow.captureThumbnail(maxEdge:) method, caches them in a 160 MiB LRU-backed NSCache, and renders them through SwitcherGridCard with automatic fallback to application icons when capture fails.

The vorssaint/vorssaint-utils repository implements a high-performance window preview pipeline that balances visual fidelity with memory constraints. The Vorssaint app switcher window thumbnail capture mechanism relies on three coordinated components—window enumeration, image caching, and reactive UI rendering—to deliver fluid previews without blocking the main thread.

How WindowEnumerator Discovers Windows

The pipeline begins in Sources/Vorssaint/Services/Switcher/WindowEnumerator.swift, where the WindowEnumerator class queries macOS ScreenCaptureKit to list every SCWindow object visible to the current user session. It filters out system‑level windows that cannot be previewed (such as menu bars and hidden daemons) and constructs an array of WindowItem structs. Each WindowItem carries a persistent identifier (window.id.uuidString) required later for cache lookups, along with metadata such as window title and owning application.

Thumbnail Capture and Caching Strategy

Once windows are enumerated, Sources/Vorssaint/Services/Switcher/WindowPreviewProvider.swift orchestrates the actual capture and storage logic.

ScreenCaptureKit Integration

The provider invokes SCWindow.captureThumbnail(maxEdge: 512) for each visible window. The maxEdge parameter defaults to 512 points (1024 pixels on Retina displays), ensuring thumbnails remain crisp while minimizing memory footprint. The raw CGImage returned by ScreenCaptureKit is immediately wrapped in an NSImage and passed to the caching layer.

LRU Cache Implementation

WindowPreviewProvider maintains an in‑memory NSCache configured with a totalCostLimit of 160 MiB. The cache uses the window’s UUID string as its key and calculates each entry’s cost as bytesPerRow × height, preventing unbounded growth. When the cache exceeds its budget, the least‑recently‑used thumbnail is evicted automatically. This design guarantees that the switcher never holds stale images for inactive windows while keeping the most recently viewed windows instantly accessible.

Rendering the Thumbnail Grid

Sources/Vorssaint/UI/Switcher/SwitcherView.swift consumes the cached images via SwitcherGridCard. Each card draws the thumbnail at dimensions defined in Sources/Vorssaint/Services/Switcher/SwitcherSupport.swift (thumbnailWidth and thumbnailHeight), applying rounded corners and padding. If WindowPreviewProvider reports a missing or failed capture, the card falls back to the application’s icon (fallbackIconSize) so the grid never shows empty placeholders. The view updates reactively when the provider’s onUpdate closure fires, triggering a SwiftUI diff and re‑render only for changed windows.

Practical Code Examples

1. Requesting a Thumbnail Manually

You can replicate the provider’s capture logic for custom UI components:

import ScreenCaptureKit
import AppKit

func captureWindowThumbnail(window: SCWindow) async -> NSImage? {
    // Capture at 512pt (1024px @2x) resolution
    guard let cgImage = await window.captureThumbnail(maxEdge: 512) else {
        return nil
    }
    return NSImage(cgImage: cgImage, size: .zero)
}

This mirrors the internal call made by WindowPreviewProvider.refreshThumbnails().

2. Observing Thumbnail Updates in SwiftUI

Integrate the provider into a view to receive automatic updates:

struct WindowGrid: View {
    @StateObject private var provider = WindowPreviewProvider()

    var body: some View {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 200))]) {
            ForEach(provider.items) { item in
                SwitcherGridCard(item: item)
            }
        }
        .onAppear { provider.startUpdating() }
    }
}

startUpdating() begins the enumeration‑to‑capture cycle described above.

3. Accessing the Cache Directly

For advanced use cases, you can query the cache without triggering a new capture:

let cacheKey = window.id.uuidString
if let cachedImage = WindowPreviewProvider.thumbnailCache.object(forKey: cacheKey) {
    // Use the cached NSImage immediately
    imageView.image = cachedImage
}

The cache key matches the window’s persistent identifier assigned during enumeration.

Summary

  • WindowEnumeration: WindowEnumerator.swift uses ScreenCaptureKit to list valid SCWindow objects and expose them as WindowItem structs.
  • Capture & Cache: WindowPreviewProvider.swift calls captureThumbnail(maxEdge: 512), wraps the result in an NSImage, and stores it in a 160 MiB NSCache keyed by window UUID.
  • UI Rendering: SwitcherView.swift and SwitcherGridCard display cached thumbnails at sizes defined in SwitcherSupport.swift, falling back to application icons when capture fails.
  • Memory Safety: The LRU cache evicts least‑recently‑used items automatically, ensuring the switcher remains responsive even with dozens of open windows.

Frequently Asked Questions

What API does Vorssaint use to capture window thumbnails?

Vorssaint uses ScreenCaptureKit’s SCWindow.captureThumbnail(maxEdge:) method, available on macOS 12.3 and later. This API provides hardware‑accelerated captures without requiring window server injection.

How does the switcher prevent excessive memory usage?

The mechanism enforces a hard 160 MiB limit via NSCache.totalCostLimit. Each thumbnail’s cost is calculated as bytesPerRow × height, and the cache automatically evicts the oldest entries when this threshold is exceeded.

What happens when a window cannot be captured?

If captureThumbnail(maxEdge:) returns nil—for example, due to security restrictions or minimized windows—the SwitcherGridCard falls back to the owning application’s icon. The UI remains populated; users see the icon instead of a blank space.

Why is the thumbnail size fixed at 512 points?

The 512‑point edge (1024 pixels on Retina) provides sufficient resolution for high‑DPI displays while keeping the uncompressed image under 2 MiB per thumbnail. This size balances visual clarity with the 160 MiB cache budget, allowing roughly 80 high‑resolution previews simultaneously.

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 →