diff --git a/Sources/Kapture/CaptureController.swift b/Sources/Kapture/CaptureController.swift
index e2cae29..df653d2 100644
--- a/Sources/Kapture/CaptureController.swift
+++ b/Sources/Kapture/CaptureController.swift
@@ -84,21 +84,26 @@ final class CaptureController: NSObject {
}
isSelecting = true
previousApp = NSWorkspace.shared.frontmostApplication
- // Activation is async on macOS 26; wait a tick so the app is active
- // before the overlays appear (otherwise clicks pass through).
- NSApp.activate(ignoringOtherApps: true)
- CaptureController.log("begin: activate issued, active=\(NSApp.isActive)")
+ // With the event tap driving input we must NOT activate: stealing
+ // focus turns the target app into background mode (dimmed chrome),
+ // and that is what would end up in the screenshot. Activate only on
+ // the fallback path, where panel events are the only input.
+ let usesTap = AXIsProcessTrusted()
+ if !usesTap {
+ NSApp.activate(ignoringOtherApps: true)
+ }
+ CaptureController.log("begin: usesTap=\(usesTap) active=\(NSApp.isActive)")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
guard let self, self.isSelecting else { return }
- CaptureController.log("begin: active=\(NSApp.isActive) — showing overlays")
self.overlays = NSScreen.screens.map { screen in
let panel = OverlayWindow(screen: screen, controller: self)
(panel.contentView as? OverlayView)?.controller = self
return panel
}
- self.overlays.forEach { $0.makeKeyAndOrderFront(nil) }
+ // orderFrontRegardless: show without taking key/focus.
+ self.overlays.forEach { $0.orderFrontRegardless() }
self.overlayIDs = self.overlays.map { $0.windowNumber }
- CaptureController.log("begin: \(self.overlays.count) overlays, ids=\(self.overlayIDs), active=\(NSApp.isActive)")
+ CaptureController.log("begin: \(self.overlays.count) overlays, ids=\(self.overlayIDs) — prior app stays focused")
self.installEventTap()
}
}
diff --git a/Sources/Kapture/OverlayView.swift b/Sources/Kapture/OverlayView.swift
index 8a59387..1e9192c 100644
--- a/Sources/Kapture/OverlayView.swift
+++ b/Sources/Kapture/OverlayView.swift
@@ -6,8 +6,11 @@ final class OverlayWindow: NSPanel {
init(screen: NSScreen, controller: CaptureController) {
let content = OverlayView()
super.init(
+ // Non-activating: the overlay must NOT steal focus, so the app
+ // being captured keeps its active appearance (undimmed chrome).
+ // Input comes from the CGEventTap, not panel events.
contentRect: screen.frame,
- styleMask: [.borderless],
+ styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: false
)
diff --git a/build-app.sh b/build-app.sh
index 9b3f421..a7510d9 100755
--- a/build-app.sh
+++ b/build-app.sh
@@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST'
CFBundleIconFile
Icon
CFBundleShortVersionString
- 1.8.7
+ 1.9.0
CFBundleVersion
- 87
+ 90
LSUIElement
NSHighResolutionCapable