Skip to content

Commit ccb5f58

Browse files
D3OXYclaude
andcommitted
Release v1.3.4 - Instant app hiding with event-driven detection
- Replace 2-second polling timer with NSWorkspace notifications - Apps now hide instantly (~300ms) when launched - Zero CPU usage when idle - only updates on app events - Observe app launch, activate, and unhide events 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7020c1f commit ccb5f58

3 files changed

Lines changed: 63 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.3.4] - 2024-12-21
11+
12+
### Changed
13+
- **Instant app hiding**: Excluded apps now hide instantly when launched (was 2-second delay)
14+
- **Event-driven detection**: Replaced 2-second polling timer with NSWorkspace notifications
15+
- **Zero idle CPU**: No more background polling - only updates when apps actually launch/activate
16+
17+
### Performance
18+
- Removed continuous 2-second timer (saves CPU when idle)
19+
- Uses system notifications for immediate response (~300ms after app launch)
20+
- More efficient than polling - only runs when needed
21+
1022
## [1.3.3] - 2024-12-21
1123

1224
### Fixed
@@ -108,6 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
108120

109121
| Version | Date | Highlights |
110122
|---------|------|------------|
123+
| 1.3.4 | 2024-12-21 | Instant app hiding, event-driven detection, zero idle CPU |
111124
| 1.3.3 | 2024-12-21 | HUD above fullscreen, green menu icon, faster hotkeys |
112125
| 1.3.2 | 2024-12-21 | Dynamic aspect ratio, smart visibility (hidden until sharing) |
113126
| 1.3.1 | 2024-12-21 | Fix window sharing visibility, self-hide toggle |

Cloak/AppDelegate.swift

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,14 +1416,60 @@ class ScreenCaptureEngine: NSObject {
14161416
private var windowsToExclude: [NSWindow]
14171417
private var appNamesToExclude: [String]
14181418
private var currentDisplay: SCDisplay?
1419-
private var refreshTimer: Timer?
1419+
private var workspaceObservers: [NSObjectProtocol] = []
14201420

14211421
init(previewView: PreviewView, excludingWindows: [NSWindow] = [], excludingApps: [String] = []) {
14221422
self.previewView = previewView
14231423
self.windowsToExclude = excludingWindows
14241424
self.appNamesToExclude = excludingApps
14251425
super.init()
14261426
loadSettings()
1427+
setupWorkspaceObservers()
1428+
}
1429+
1430+
private func setupWorkspaceObservers() {
1431+
let center = NSWorkspace.shared.notificationCenter
1432+
1433+
// Observe app launches - refresh filter when any app launches
1434+
let launchObserver = center.addObserver(
1435+
forName: NSWorkspace.didLaunchApplicationNotification,
1436+
object: nil,
1437+
queue: .main
1438+
) { [weak self] notification in
1439+
// Small delay to let the window appear
1440+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
1441+
self?.refreshExcludedApps()
1442+
}
1443+
}
1444+
workspaceObservers.append(launchObserver)
1445+
1446+
// Also observe when apps become active (window might appear)
1447+
let activateObserver = center.addObserver(
1448+
forName: NSWorkspace.didActivateApplicationNotification,
1449+
object: nil,
1450+
queue: .main
1451+
) { [weak self] _ in
1452+
self?.refreshExcludedApps()
1453+
}
1454+
workspaceObservers.append(activateObserver)
1455+
1456+
// Observe when windows might have changed
1457+
let unhideObserver = center.addObserver(
1458+
forName: NSWorkspace.didUnhideApplicationNotification,
1459+
object: nil,
1460+
queue: .main
1461+
) { [weak self] _ in
1462+
self?.refreshExcludedApps()
1463+
}
1464+
workspaceObservers.append(unhideObserver)
1465+
}
1466+
1467+
private func removeWorkspaceObservers() {
1468+
let center = NSWorkspace.shared.notificationCenter
1469+
for observer in workspaceObservers {
1470+
center.removeObserver(observer)
1471+
}
1472+
workspaceObservers.removeAll()
14271473
}
14281474

14291475
func loadSettings() {
@@ -1460,8 +1506,7 @@ class ScreenCaptureEngine: NSObject {
14601506
}
14611507

14621508
func stopCapture() {
1463-
refreshTimer?.invalidate()
1464-
refreshTimer = nil
1509+
removeWorkspaceObservers()
14651510
Task {
14661511
try? await stream?.stopCapture()
14671512
stream = nil
@@ -1508,13 +1553,6 @@ class ScreenCaptureEngine: NSObject {
15081553

15091554
try stream?.addStreamOutput(self, type: .screen, sampleHandlerQueue: .main)
15101555
try await stream?.startCapture()
1511-
1512-
// Start periodic refresh to catch new windows from excluded apps
1513-
DispatchQueue.main.async {
1514-
self.refreshTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in
1515-
self?.refreshExcludedApps()
1516-
}
1517-
}
15181556
}
15191557

15201558
private func buildContentFilter(display: SCDisplay) async throws -> SCContentFilter {

Cloak/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
<plist version="1.0">
44
<dict>
55
<key>CFBundleShortVersionString</key>
6-
<string>1.3.3</string>
6+
<string>1.3.4</string>
77
<key>CFBundleVersion</key>
8-
<string>7</string>
8+
<string>8</string>
99
<key>NSScreenCaptureUsageDescription</key>
1010
<string>Cloak needs to capture your screen to create a preview window that you can share.</string>
1111
</dict>

0 commit comments

Comments
 (0)