node-mac-recorder 2.16.17 โ†’ 2.16.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing per-screen window selector overlay system...'');\nconst WindowSelector = require(''./window-selector.js'');\nconst selector = new WindowSelector();\n\n// Test permission check first\nselector.checkPermissions().then(perms => {\n console.log(''Permissions:'', perms);\n \n // Start window selection to test per-screen overlays\n return selector.startSelection();\n}).then(() => {\n console.log(''โœ… Window selection started - testing per-screen overlays'');\n console.log(''โžก๏ธ Move cursor over windows on different displays to test overlay positioning'');\n \n // Auto-stop after 10 seconds for testing\n setTimeout(() => {\n selector.stopSelection().then(() => {\n console.log(''โœ… Window selection stopped - test complete'');\n });\n }, 10000);\n}).catch(console.error);\n\")"
4
+ "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing UI elements on ALL screens...'');\nconst WindowSelector = require(''./window-selector.js'');\nconst selector = new WindowSelector();\n\nselector.checkPermissions().then(perms => {\n console.log(''Permissions:'', perms);\n return selector.startSelection();\n}).then(() => {\n console.log(''โœ… Window selection started - TEST UI elements on BOTH screens'');\n console.log(''๐Ÿ–ฅ๏ธ Primary display: Move cursor over windows'');\n console.log(''๐Ÿ–ฅ๏ธ External display: Move cursor over windows'');\n console.log(''โžก๏ธ Check if Record/Cancel buttons + logo + title appear on BOTH displays'');\n \n setTimeout(() => {\n selector.stopSelection().then(() => {\n console.log(''โœ… Test complete'');\n });\n }, 20000);\n}).catch(console.error);\n\")"
5
5
  ],
6
6
  "deny": [],
7
7
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.17",
3
+ "version": "2.16.19",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -21,6 +21,10 @@ static NSMutableArray *g_perScreenOverlays = nil;
21
21
  static NSMutableArray *g_perScreenOverlayViews = nil;
22
22
  static NSInteger g_activeScreenIndex = -1;
23
23
 
24
+ // Forward declaration for delegate
25
+ @class WindowSelectorDelegate;
26
+ static WindowSelectorDelegate *g_delegate = nil;
27
+
24
28
  // Functions to hide/show main overlay window during recording
25
29
  void hideAllOverlayWindows() {
26
30
  if (g_overlayWindow && [g_overlayWindow isVisible]) {
@@ -73,6 +77,9 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo);
73
77
  bool hideScreenRecordingPreview();
74
78
  void updateScreenOverlays();
75
79
 
80
+ // Forward declarations
81
+ @class HoverButton;
82
+
76
83
  // Custom overlay view class
77
84
  @interface WindowSelectorOverlayView : NSView
78
85
  @property (nonatomic, strong) NSDictionary *windowInfo;
@@ -80,7 +87,14 @@ void updateScreenOverlays();
80
87
  @property (nonatomic) BOOL isToggled;
81
88
  @property (nonatomic) NSRect highlightFrame;
82
89
  @property (nonatomic, strong) NSValue *globalOriginOffset; // Store global coordinate offset
90
+ @property (nonatomic, strong) HoverButton *recordButton;
91
+ @property (nonatomic, strong) HoverButton *cancelButton;
92
+ @property (nonatomic, strong) NSImageView *appIconView;
93
+ @property (nonatomic, strong) NSTextField *infoLabel;
83
94
  - (void)setHighlightFrame:(NSRect)frame;
95
+ - (void)setupUIElements;
96
+ - (void)updateUIElements;
97
+ - (void)positionUIElements;
84
98
  @end
85
99
 
86
100
  // Custom button with hover effects
@@ -115,15 +129,14 @@ void updateScreenOverlays();
115
129
  // Disable focus ring completely
116
130
  [self setFocusRingType:NSFocusRingTypeNone];
117
131
 
118
- // Window selector overlay view created
132
+ // Setup UI elements (button, icon, label)
133
+ [self setupUIElements];
134
+
135
+ NSLog(@"๐ŸŽฏ WindowSelectorOverlayView created with UI elements integrated");
119
136
  }
120
137
  return self;
121
138
  }
122
139
 
123
- - (void)setHighlightFrame:(NSRect)frame {
124
- _highlightFrame = frame;
125
- [self setNeedsDisplay:YES]; // Trigger redraw
126
- }
127
140
 
128
141
  - (void)drawRect:(NSRect)dirtyRect {
129
142
  [super drawRect:dirtyRect];
@@ -218,6 +231,178 @@ void updateScreenOverlays();
218
231
  NSLog(@"๐ŸŽฏ Global toggle state: %s", g_hasToggledWindow ? "HAS_TOGGLED" : "NO_TOGGLE");
219
232
  }
220
233
 
234
+ - (void)setupUIElements {
235
+ NSLog(@"๐Ÿ”ง Setting up integrated UI elements");
236
+
237
+ // Ensure delegate exists
238
+ if (!g_delegate) {
239
+ NSLog(@"โš ๏ธ No delegate found in setupUIElements, this may cause button click issues");
240
+ }
241
+
242
+ // Create record button
243
+ self.recordButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 200, 60)];
244
+ [self.recordButton setTitle:@"Start Recording"];
245
+ [self.recordButton setFont:[NSFont systemFontOfSize:18.0 weight:NSFontWeightMedium]];
246
+ [self.recordButton setBezelStyle:NSBezelStyleRounded];
247
+ [self.recordButton setTarget:g_delegate];
248
+ [self.recordButton setAction:@selector(selectButtonClicked:)];
249
+
250
+ // Style the button
251
+ self.recordButton.wantsLayer = YES;
252
+ self.recordButton.layer.backgroundColor = [[NSColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:0.9] CGColor];
253
+ self.recordButton.layer.cornerRadius = 8.0;
254
+ self.recordButton.layer.borderWidth = 0.0;
255
+
256
+ [self addSubview:self.recordButton];
257
+
258
+ // Create cancel button
259
+ self.cancelButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
260
+ [self.cancelButton setTitle:@"Cancel"];
261
+ [self.cancelButton setFont:[NSFont systemFontOfSize:14.0 weight:NSFontWeightMedium]];
262
+ [self.cancelButton setBezelStyle:NSBezelStyleRounded];
263
+ [self.cancelButton setTarget:g_delegate];
264
+ [self.cancelButton setAction:@selector(cancelButtonClicked:)];
265
+
266
+ // Style the cancel button
267
+ self.cancelButton.wantsLayer = YES;
268
+ self.cancelButton.layer.backgroundColor = [[NSColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:0.9] CGColor];
269
+ self.cancelButton.layer.cornerRadius = 6.0;
270
+ self.cancelButton.layer.borderWidth = 0.0;
271
+
272
+ [self addSubview:self.cancelButton];
273
+
274
+ // Create app icon view
275
+ self.appIconView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 96, 96)];
276
+ [self.appIconView setImageScaling:NSImageScaleProportionallyUpOrDown];
277
+ [self.appIconView setWantsLayer:YES];
278
+ [self.appIconView.layer setCornerRadius:8.0];
279
+ [self.appIconView.layer setMasksToBounds:YES];
280
+ [self addSubview:self.appIconView];
281
+
282
+ // Create info label
283
+ self.infoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 400, 60)];
284
+ [self.infoLabel setEditable:NO];
285
+ [self.infoLabel setSelectable:NO];
286
+ [self.infoLabel setBezeled:NO];
287
+ [self.infoLabel setDrawsBackground:NO];
288
+ [self.infoLabel setAlignment:NSTextAlignmentCenter];
289
+ [self.infoLabel setFont:[NSFont systemFontOfSize:23.4 weight:NSFontWeightMedium]];
290
+ [self.infoLabel setTextColor:[NSColor whiteColor]];
291
+ [self addSubview:self.infoLabel];
292
+
293
+ NSLog(@"โœ… UI elements created and added to overlay view");
294
+ }
295
+
296
+ - (void)updateUIElements {
297
+ if (!self.windowInfo) return;
298
+
299
+ NSLog(@"๐Ÿ”„ Updating UI elements for window: %@", [self.windowInfo objectForKey:@"title"]);
300
+
301
+ // Update info label
302
+ NSString *windowTitle = [self.windowInfo objectForKey:@"title"] ?: @"Unknown Window";
303
+ NSString *appName = [self.windowInfo objectForKey:@"appName"] ?: @"Unknown App";
304
+ [self.infoLabel setStringValue:[NSString stringWithFormat:@"%@\n%@", appName, windowTitle]];
305
+
306
+ // Update app icon
307
+ NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
308
+ NSArray *runningApps = [workspace runningApplications];
309
+ NSImage *appIcon = nil;
310
+
311
+ for (NSRunningApplication *app in runningApps) {
312
+ if ([[app localizedName] isEqualToString:appName] || [[app bundleIdentifier] containsString:appName]) {
313
+ appIcon = [app icon];
314
+ break;
315
+ }
316
+ }
317
+
318
+ if (!appIcon) {
319
+ appIcon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
320
+ }
321
+
322
+ [self.appIconView setImage:appIcon];
323
+
324
+ // Position elements
325
+ [self positionUIElements];
326
+ }
327
+
328
+ - (void)positionUIElements {
329
+ if (NSEqualRects(self.highlightFrame, NSZeroRect)) {
330
+ // Hide elements when no window is highlighted
331
+ [self.recordButton setHidden:YES];
332
+ [self.cancelButton setHidden:YES];
333
+ [self.appIconView setHidden:YES];
334
+ [self.infoLabel setHidden:YES];
335
+ return;
336
+ }
337
+
338
+ // Show elements
339
+ [self.recordButton setHidden:NO];
340
+ [self.cancelButton setHidden:NO];
341
+ [self.appIconView setHidden:NO];
342
+ [self.infoLabel setHidden:NO];
343
+
344
+ // Calculate window center
345
+ CGFloat windowCenterX = self.highlightFrame.origin.x + (self.highlightFrame.size.width / 2);
346
+ CGFloat windowCenterY = self.highlightFrame.origin.y + (self.highlightFrame.size.height / 2);
347
+
348
+ // Position record button at window center
349
+ NSSize buttonSize = self.recordButton.frame.size;
350
+ [self.recordButton setFrameOrigin:NSMakePoint(
351
+ windowCenterX - (buttonSize.width / 2),
352
+ windowCenterY - (buttonSize.height / 2)
353
+ )];
354
+
355
+ // Position app icon above window center
356
+ [self.appIconView setFrameOrigin:NSMakePoint(
357
+ windowCenterX - 48, // Center horizontally (96/2 = 48)
358
+ windowCenterY + 120 // 120px above window center
359
+ )];
360
+
361
+ // Add floating animation to app icon
362
+ [self.appIconView.layer removeAnimationForKey:@"floatAnimationX"];
363
+ CABasicAnimation *floatAnimationX = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
364
+ floatAnimationX.fromValue = @(-4.0);
365
+ floatAnimationX.toValue = @(4.0);
366
+ floatAnimationX.duration = 1.0;
367
+ floatAnimationX.repeatCount = HUGE_VALF;
368
+ floatAnimationX.autoreverses = YES;
369
+ floatAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
370
+ [self.appIconView.layer addAnimation:floatAnimationX forKey:@"floatAnimationX"];
371
+
372
+ // Position info label between icon and button
373
+ NSSize labelSize = self.infoLabel.frame.size;
374
+ [self.infoLabel setFrameOrigin:NSMakePoint(
375
+ windowCenterX - (labelSize.width / 2),
376
+ windowCenterY + 50 // 50px above window center
377
+ )];
378
+
379
+ // Position cancel button below record button
380
+ NSSize cancelButtonSize = self.cancelButton.frame.size;
381
+ [self.cancelButton setFrameOrigin:NSMakePoint(
382
+ windowCenterX - (cancelButtonSize.width / 2),
383
+ windowCenterY - 80 // 80px below window center
384
+ )];
385
+
386
+ NSLog(@"๐Ÿ“ UI elements positioned - Button:(%.0f,%.0f) Cancel:(%.0f,%.0f) Icon:(%.0f,%.0f) Label:(%.0f,%.0f)",
387
+ self.recordButton.frame.origin.x, self.recordButton.frame.origin.y,
388
+ self.cancelButton.frame.origin.x, self.cancelButton.frame.origin.y,
389
+ self.appIconView.frame.origin.x, self.appIconView.frame.origin.y,
390
+ self.infoLabel.frame.origin.x, self.infoLabel.frame.origin.y);
391
+ }
392
+
393
+ // Override setHighlightFrame to trigger UI updates
394
+ - (void)setHighlightFrame:(NSRect)frame {
395
+ _highlightFrame = frame;
396
+ [self positionUIElements]; // Position UI elements when highlight changes
397
+ [self setNeedsDisplay:YES]; // Trigger redraw
398
+ }
399
+
400
+ // Override setWindowInfo to trigger UI updates
401
+ - (void)setWindowInfo:(NSDictionary *)windowInfo {
402
+ _windowInfo = windowInfo;
403
+ [self updateUIElements]; // Update UI elements when window info changes
404
+ }
405
+
221
406
  // Layer-based approach, no custom drawing needed
222
407
 
223
408
  @end
@@ -513,8 +698,6 @@ void updateScreenOverlays();
513
698
  }
514
699
  @end
515
700
 
516
- static WindowSelectorDelegate *g_delegate = nil;
517
-
518
701
  // Bring window to front using Accessibility API
519
702
  bool bringWindowToFront(int windowId) {
520
703
  @autoreleasepool {
@@ -926,191 +1109,19 @@ void updateOverlay() {
926
1109
 
927
1110
  // Only reset toggle state when switching to different window (not for position updates)
928
1111
  if (!g_hasToggledWindow) {
929
- [(WindowSelectorOverlayView *)g_overlayView setIsToggled:NO];
1112
+ [targetOverlayView setIsToggled:NO];
930
1113
  } else {
931
1114
  // Keep toggle state for locked window
932
- [(WindowSelectorOverlayView *)g_overlayView setIsToggled:YES];
933
- }
934
-
935
- // Add/update info label above button
936
- NSTextField *infoLabel = nil;
937
- for (NSView *subview in [g_overlayWindow.contentView subviews]) {
938
- if ([subview isKindOfClass:[NSTextField class]]) {
939
- infoLabel = (NSTextField*)subview;
940
- break;
941
- }
942
- }
943
-
944
- if (!infoLabel) {
945
- infoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, width - 40, 60)];
946
- [infoLabel setEditable:NO];
947
- [infoLabel setSelectable:NO];
948
- [infoLabel setBezeled:NO];
949
- [infoLabel setDrawsBackground:NO];
950
- [infoLabel setAlignment:NSTextAlignmentCenter];
951
- [infoLabel setFont:[NSFont systemFontOfSize:23.4 weight:NSFontWeightMedium]]; // 18 * 1.3 = 23.4
952
- [infoLabel setTextColor:[NSColor whiteColor]];
953
-
954
- // Force no borders on info label
955
- [infoLabel setWantsLayer:YES];
956
- infoLabel.layer.borderWidth = 1.0;
957
- infoLabel.layer.borderColor = [[NSColor clearColor] CGColor];
958
- infoLabel.layer.cornerRadius = 0.0;
959
- infoLabel.layer.masksToBounds = YES;
960
-
961
- [g_overlayWindow.contentView addSubview:infoLabel];
1115
+ [targetOverlayView setIsToggled:YES];
962
1116
  }
963
1117
 
964
- // Add/update app icon
965
- NSImageView *appIconView = nil;
966
- for (NSView *subview in [g_overlayWindow.contentView subviews]) {
967
- if ([subview isKindOfClass:[NSImageView class]]) {
968
- appIconView = (NSImageView*)subview;
969
- break;
970
- }
971
- }
972
-
973
- if (!appIconView) {
974
- appIconView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 96, 96)];
975
- [appIconView setImageScaling:NSImageScaleProportionallyUpOrDown];
976
- [appIconView setWantsLayer:YES];
977
- [appIconView.layer setCornerRadius:8.0];
978
- [appIconView.layer setMasksToBounds:YES];
979
- [appIconView.layer setBackgroundColor:[[NSColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:0.3] CGColor]]; // Debug background
980
-
981
- // Force no borders on app icon view
982
- appIconView.layer.borderWidth = 1.0;
983
- appIconView.layer.borderColor = [[NSColor clearColor] CGColor];
984
- appIconView.layer.shadowOpacity = 0.0;
985
- appIconView.layer.shadowRadius = 0.0;
986
- appIconView.layer.shadowOffset = NSMakeSize(0, 0);
987
-
988
- [g_overlayWindow.contentView addSubview:appIconView];
989
- NSLog(@"๐Ÿ–ผ๏ธ Created app icon view at frame: (%.0f, %.0f, %.0f, %.0f)",
990
- appIconView.frame.origin.x, appIconView.frame.origin.y,
991
- appIconView.frame.size.width, appIconView.frame.size.height);
992
- }
993
-
994
- // Get app icon using NSWorkspace
995
- NSString *iconAppName = [windowUnderCursor objectForKey:@"appName"] ?: @"Unknown";
996
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
997
- NSArray *runningApps = [workspace runningApplications];
998
- NSImage *appIcon = nil;
999
-
1000
- for (NSRunningApplication *app in runningApps) {
1001
- if ([[app localizedName] isEqualToString:iconAppName] || [[app bundleIdentifier] containsString:iconAppName]) {
1002
- appIcon = [app icon];
1003
- break;
1004
- }
1005
- }
1006
-
1007
- // Fallback to generic app icon if not found
1008
- if (!appIcon) {
1009
- appIcon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
1010
- NSLog(@"โš ๏ธ Using fallback icon for app: %@", iconAppName);
1011
- } else {
1012
- NSLog(@"โœ… Found app icon for: %@", iconAppName);
1013
- }
1014
-
1015
- [appIconView setImage:appIcon];
1016
- NSLog(@"๐Ÿ–ผ๏ธ Set icon image, size: %.0fx%.0f", [appIcon size].width, [appIcon size].height);
1017
-
1018
- // Update label text
1019
- NSString *labelWindowTitle = [windowUnderCursor objectForKey:@"title"] ?: @"Unknown Window";
1020
- NSString *labelAppName = [windowUnderCursor objectForKey:@"appName"] ?: @"Unknown App";
1021
- [infoLabel setStringValue:[NSString stringWithFormat:@"%@\n%@", labelAppName, labelWindowTitle]];
1022
-
1023
- // Position buttons - Start Record in center of selected window
1024
- if (g_selectButton) {
1025
- NSSize buttonSize = [g_selectButton frame].size;
1026
- // Use local window center for positioning
1027
- CGFloat localWindowCenterX = localX + (width / 2);
1028
- CGFloat localWindowCenterY = localY + (height / 2);
1029
- NSPoint buttonCenter = NSMakePoint(
1030
- localWindowCenterX - (buttonSize.width / 2),
1031
- localWindowCenterY - (buttonSize.height / 2) // Perfect center of window
1032
- );
1033
-
1034
- NSLog(@" ButtonCalc: WindowCenter(%.0f, %.0f) -> Button(%.0f, %.0f)",
1035
- localWindowCenterX, localWindowCenterY, buttonCenter.x, buttonCenter.y);
1036
-
1037
- [g_selectButton setFrameOrigin:buttonCenter];
1038
-
1039
- // Position app icon above window center
1040
- NSPoint iconCenter = NSMakePoint(
1041
- localWindowCenterX - (96 / 2), // Center horizontally on window
1042
- localWindowCenterY + 120 // 120px above window center
1043
- );
1044
- [appIconView setFrameOrigin:iconCenter];
1045
- NSLog(@"๐ŸŽฏ Positioning app icon at: (%.0f, %.0f) for window size: (%.0f, %.0f)",
1046
- iconCenter.x, iconCenter.y, (float)width, (float)height);
1047
-
1048
- // Add fast horizontal floating animation after positioning
1049
- [appIconView.layer removeAnimationForKey:@"floatAnimationX"];
1050
- [appIconView.layer removeAnimationForKey:@"floatAnimationY"];
1051
-
1052
- // Faster horizontal float animation only
1053
- CABasicAnimation *floatAnimationX = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
1054
- floatAnimationX.fromValue = @(-4.0);
1055
- floatAnimationX.toValue = @(4.0);
1056
- floatAnimationX.duration = 1.0; // Much faster animation
1057
- floatAnimationX.repeatCount = HUGE_VALF;
1058
- floatAnimationX.autoreverses = YES;
1059
- floatAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
1060
- [appIconView.layer addAnimation:floatAnimationX forKey:@"floatAnimationX"];
1061
-
1062
- // Position info label between icon and button relative to window center
1063
- NSPoint labelCenter = NSMakePoint(
1064
- localWindowCenterX - ([infoLabel frame].size.width / 2), // Center horizontally on window
1065
- localWindowCenterY + 50 // 50px above window center, below icon
1066
- );
1067
- [infoLabel setFrameOrigin:labelCenter];
1068
-
1069
- // Position cancel button below the main button
1070
- NSButton *cancelButton = nil;
1071
- for (NSView *subview in [g_overlayWindow.contentView subviews]) {
1072
- if ([subview isKindOfClass:[NSButton class]] &&
1073
- [[(NSButton*)subview title] isEqualToString:@"Cancel"]) {
1074
- cancelButton = (NSButton*)subview;
1075
- break;
1076
- }
1077
- }
1078
-
1079
- if (cancelButton) {
1080
- NSSize cancelButtonSize = [cancelButton frame].size;
1081
- NSPoint cancelButtonCenter = NSMakePoint(
1082
- localWindowCenterX - (cancelButtonSize.width / 2), // Center horizontally on window
1083
- localWindowCenterY - 80 // 80px below window center
1084
- );
1085
- [cancelButton setFrameOrigin:cancelButtonCenter];
1086
- }
1087
- }
1118
+ NSLog(@"โœ… Updated overlay view with integrated UI elements");
1088
1119
 
1089
- [g_overlayWindow orderFront:nil];
1090
- // DON'T make key - prevents focus ring
1091
- // [g_overlayWindow makeKeyAndOrderFront:nil];
1120
+ [targetOverlay orderFront:nil];
1092
1121
 
1093
- // Ensure subviews (except overlay view itself) have no borders after positioning
1094
- for (NSView *subview in [g_overlayWindow.contentView subviews]) {
1095
- // Skip the main overlay view - it handles its own borders
1096
- if ([subview isKindOfClass:[WindowSelectorOverlayView class]]) continue;
1097
-
1098
- if ([subview respondsToSelector:@selector(setWantsLayer:)]) {
1099
- [subview setWantsLayer:YES];
1100
- if (subview.layer) {
1101
- subview.layer.borderWidth = 1.0;
1102
- subview.layer.borderColor = [[NSColor clearColor] CGColor];
1103
- subview.layer.masksToBounds = YES;
1104
- subview.layer.shadowOpacity = 0.0;
1105
- subview.layer.shadowRadius = 0.0;
1106
- subview.layer.shadowOffset = NSMakeSize(0, 0);
1107
- }
1108
- }
1109
- }
1110
-
1111
- NSLog(@" โœ… Overlay Status: Level=%ld, Alpha=%.1f, Visible=%s, Frame Set=YES",
1112
- [g_overlayWindow level], [g_overlayWindow alphaValue],
1113
- [g_overlayWindow isVisible] ? "YES" : "NO");
1122
+ NSLog(@" โœ… Overlay Status: Level=%ld, Alpha=%.1f, Visible=%s",
1123
+ [targetOverlay level], [targetOverlay alphaValue],
1124
+ [targetOverlay isVisible] ? "YES" : "NO");
1114
1125
  } else if (!windowUnderCursor && g_currentWindowUnderCursor) {
1115
1126
  // No window under cursor and no toggle active, hide overlay
1116
1127
  NSString *leftWindowTitle = [g_currentWindowUnderCursor objectForKey:@"title"] ?: @"Untitled";
@@ -1889,6 +1900,12 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1889
1900
 
1890
1901
  NSLog(@"๐Ÿ–ฅ๏ธ Creating per-screen overlays for %lu displays", [allScreens count]);
1891
1902
 
1903
+ // Ensure delegate exists for all overlays
1904
+ if (!g_delegate) {
1905
+ g_delegate = [[WindowSelectorDelegate alloc] init];
1906
+ NSLog(@"๐ŸŽ›๏ธ Created global delegate for all overlays");
1907
+ }
1908
+
1892
1909
  // Create overlay for each screen
1893
1910
  for (NSInteger i = 0; i < [allScreens count]; i++) {
1894
1911
  NSScreen *screen = [allScreens objectAtIndex:i];