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.
- package/.claude/settings.local.json +1 -1
- package/package.json +1 -1
- package/src/window_selector.mm +203 -186
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐งช Testing
|
|
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
package/src/window_selector.mm
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
[
|
|
1112
|
+
[targetOverlayView setIsToggled:NO];
|
|
930
1113
|
} else {
|
|
931
1114
|
// Keep toggle state for locked window
|
|
932
|
-
[
|
|
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
|
-
|
|
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
|
-
[
|
|
1090
|
-
// DON'T make key - prevents focus ring
|
|
1091
|
-
// [g_overlayWindow makeKeyAndOrderFront:nil];
|
|
1120
|
+
[targetOverlay orderFront:nil];
|
|
1092
1121
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
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];
|