node-mac-recorder 1.13.0 ā 1.14.1
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/overlay-test.js +84 -0
- package/package.json +1 -1
- package/src/window_selector.mm +126 -5
package/overlay-test.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
async function testOverlayImprovements() {
|
|
4
|
+
console.log('šÆ Testing Overlay Improvements\n');
|
|
5
|
+
|
|
6
|
+
const recorder = new MacRecorder();
|
|
7
|
+
const WindowSelector = MacRecorder.WindowSelector;
|
|
8
|
+
const selector = new WindowSelector();
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
console.log('1ļøā£ Testing Window Selection with custom buttons...');
|
|
12
|
+
console.log(' - Custom "Start Record" and "Cancel" buttons should appear');
|
|
13
|
+
console.log(' - ESC key should cancel selection');
|
|
14
|
+
console.log(' - Cancel button should cancel selection');
|
|
15
|
+
console.log(' - Starting window selection...\n');
|
|
16
|
+
|
|
17
|
+
// Start window selection (non-blocking)
|
|
18
|
+
await selector.startSelection();
|
|
19
|
+
|
|
20
|
+
console.log('ā
Window selection started. Move mouse over windows to see overlay.');
|
|
21
|
+
console.log(' - Press ESC to cancel');
|
|
22
|
+
console.log(' - Click Cancel button to cancel');
|
|
23
|
+
console.log(' - Click Start Record to select window\n');
|
|
24
|
+
|
|
25
|
+
// Wait for selection or timeout
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
const timeout = 30000; // 30 seconds
|
|
28
|
+
|
|
29
|
+
while (selector.getStatus().isSelecting && (Date.now() - startTime) < timeout) {
|
|
30
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const selectedWindow = selector.getSelectedWindow();
|
|
34
|
+
|
|
35
|
+
if (selectedWindow) {
|
|
36
|
+
console.log('š¬ Window selected:', selectedWindow.appName, '-', selectedWindow.title);
|
|
37
|
+
console.log(' Size:', selectedWindow.width + 'x' + selectedWindow.height);
|
|
38
|
+
console.log(' Position: (' + selectedWindow.x + ', ' + selectedWindow.y + ')');
|
|
39
|
+
|
|
40
|
+
console.log('\n2ļøā£ Testing Recording Preview Overlay...');
|
|
41
|
+
console.log(' - Should show darkened overlay with window area transparent');
|
|
42
|
+
|
|
43
|
+
await selector.showRecordingPreview(selectedWindow);
|
|
44
|
+
console.log('ā
Recording preview shown. Check if window area is highlighted.');
|
|
45
|
+
|
|
46
|
+
// Wait 3 seconds
|
|
47
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
48
|
+
|
|
49
|
+
await selector.hideRecordingPreview();
|
|
50
|
+
console.log('ā
Recording preview hidden.');
|
|
51
|
+
|
|
52
|
+
} else {
|
|
53
|
+
console.log('š« No window selected (cancelled or timeout)');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log('\n3ļøā£ Testing Screen Selection with custom buttons...');
|
|
57
|
+
console.log(' - Custom "Start Record" and "Cancel" buttons should appear on each screen');
|
|
58
|
+
console.log(' - ESC key should cancel selection');
|
|
59
|
+
console.log(' - Cancel button should cancel selection');
|
|
60
|
+
|
|
61
|
+
const selectedScreen = await selector.selectScreen().catch(err => {
|
|
62
|
+
console.log('š« Screen selection cancelled:', err.message);
|
|
63
|
+
return null;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (selectedScreen) {
|
|
67
|
+
console.log('š„ļø Screen selected:', selectedScreen.name);
|
|
68
|
+
console.log(' Resolution:', selectedScreen.resolution);
|
|
69
|
+
console.log(' Position: (' + selectedScreen.x + ', ' + selectedScreen.y + ')');
|
|
70
|
+
} else {
|
|
71
|
+
console.log('š« No screen selected (cancelled or timeout)');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log('\nš Overlay improvements test completed!');
|
|
75
|
+
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('ā Test failed:', error.message);
|
|
78
|
+
} finally {
|
|
79
|
+
await selector.cleanup();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Run test
|
|
84
|
+
testOverlayImprovements().catch(console.error);
|
package/package.json
CHANGED
package/src/window_selector.mm
CHANGED
|
@@ -16,6 +16,7 @@ static NSDictionary *g_selectedWindowInfo = nil;
|
|
|
16
16
|
static NSMutableArray *g_allWindows = nil;
|
|
17
17
|
static NSDictionary *g_currentWindowUnderCursor = nil;
|
|
18
18
|
static bool g_bringToFrontEnabled = true; // Default enabled
|
|
19
|
+
static id g_windowKeyEventMonitor = nil;
|
|
19
20
|
|
|
20
21
|
// Recording preview overlay state
|
|
21
22
|
static NSWindow *g_recordingPreviewWindow = nil;
|
|
@@ -213,6 +214,7 @@ bool hideScreenRecordingPreview();
|
|
|
213
214
|
@interface WindowSelectorDelegate : NSObject
|
|
214
215
|
- (void)selectButtonClicked:(id)sender;
|
|
215
216
|
- (void)screenSelectButtonClicked:(id)sender;
|
|
217
|
+
- (void)cancelButtonClicked:(id)sender;
|
|
216
218
|
- (void)timerUpdate:(NSTimer *)timer;
|
|
217
219
|
@end
|
|
218
220
|
|
|
@@ -241,6 +243,16 @@ bool hideScreenRecordingPreview();
|
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
- (void)cancelButtonClicked:(id)sender {
|
|
247
|
+
NSLog(@"š« CANCEL BUTTON CLICKED: Selection cancelled");
|
|
248
|
+
// Clean up without selecting anything
|
|
249
|
+
if (g_isScreenSelecting) {
|
|
250
|
+
cleanupScreenSelector();
|
|
251
|
+
} else {
|
|
252
|
+
cleanupWindowSelector();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
244
256
|
- (void)timerUpdate:(NSTimer *)timer {
|
|
245
257
|
updateOverlay();
|
|
246
258
|
}
|
|
@@ -495,14 +507,33 @@ void updateOverlay() {
|
|
|
495
507
|
[(WindowSelectorOverlayView *)g_overlayView setWindowInfo:windowUnderCursor];
|
|
496
508
|
[g_overlayView setNeedsDisplay:YES];
|
|
497
509
|
|
|
498
|
-
// Position
|
|
510
|
+
// Position buttons - Start Record in center, Cancel below it
|
|
499
511
|
if (g_selectButton) {
|
|
500
512
|
NSSize buttonSize = [g_selectButton frame].size;
|
|
501
513
|
NSPoint buttonCenter = NSMakePoint(
|
|
502
514
|
(width - buttonSize.width) / 2,
|
|
503
|
-
(height - buttonSize.height) / 2
|
|
515
|
+
(height - buttonSize.height) / 2 + 30 // Slightly above center
|
|
504
516
|
);
|
|
505
517
|
[g_selectButton setFrameOrigin:buttonCenter];
|
|
518
|
+
|
|
519
|
+
// Position cancel button below the main button
|
|
520
|
+
NSButton *cancelButton = nil;
|
|
521
|
+
for (NSView *subview in [g_overlayWindow.contentView subviews]) {
|
|
522
|
+
if ([subview isKindOfClass:[NSButton class]] &&
|
|
523
|
+
[[(NSButton*)subview title] isEqualToString:@"Cancel"]) {
|
|
524
|
+
cancelButton = (NSButton*)subview;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (cancelButton) {
|
|
530
|
+
NSSize cancelButtonSize = [cancelButton frame].size;
|
|
531
|
+
NSPoint cancelButtonCenter = NSMakePoint(
|
|
532
|
+
(width - cancelButtonSize.width) / 2,
|
|
533
|
+
buttonCenter.y - buttonSize.height - 20 // 20px below main button
|
|
534
|
+
);
|
|
535
|
+
[cancelButton setFrameOrigin:cancelButtonCenter];
|
|
536
|
+
}
|
|
506
537
|
}
|
|
507
538
|
|
|
508
539
|
[g_overlayWindow orderFront:nil];
|
|
@@ -535,6 +566,12 @@ void cleanupWindowSelector() {
|
|
|
535
566
|
g_trackingTimer = nil;
|
|
536
567
|
}
|
|
537
568
|
|
|
569
|
+
// Remove key event monitor
|
|
570
|
+
if (g_windowKeyEventMonitor) {
|
|
571
|
+
[NSEvent removeMonitor:g_windowKeyEventMonitor];
|
|
572
|
+
g_windowKeyEventMonitor = nil;
|
|
573
|
+
}
|
|
574
|
+
|
|
538
575
|
// Close overlay window
|
|
539
576
|
if (g_overlayWindow) {
|
|
540
577
|
[g_overlayWindow close];
|
|
@@ -752,14 +789,52 @@ bool startScreenSelection() {
|
|
|
752
789
|
[selectButton setTarget:g_delegate];
|
|
753
790
|
[selectButton setAction:@selector(screenSelectButtonClicked:)];
|
|
754
791
|
|
|
755
|
-
//
|
|
792
|
+
// Create cancel button for screen selection
|
|
793
|
+
NSButton *screenCancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
794
|
+
[screenCancelButton setTitle:@"Cancel"];
|
|
795
|
+
[screenCancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
796
|
+
[screenCancelButton setBezelStyle:NSBezelStyleRounded];
|
|
797
|
+
[screenCancelButton setFont:[NSFont systemFontOfSize:14 weight:NSFontWeightMedium]];
|
|
798
|
+
|
|
799
|
+
// Gray cancel button styling
|
|
800
|
+
[screenCancelButton setWantsLayer:YES];
|
|
801
|
+
[screenCancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8] CGColor]];
|
|
802
|
+
[screenCancelButton.layer setCornerRadius:6.0];
|
|
803
|
+
[screenCancelButton.layer setBorderColor:[[NSColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1.0] CGColor]];
|
|
804
|
+
[screenCancelButton.layer setBorderWidth:1.0];
|
|
805
|
+
|
|
806
|
+
// White text for cancel button
|
|
807
|
+
NSMutableAttributedString *screenCancelTitleString = [[NSMutableAttributedString alloc]
|
|
808
|
+
initWithString:[screenCancelButton title]];
|
|
809
|
+
[screenCancelTitleString addAttribute:NSForegroundColorAttributeName
|
|
810
|
+
value:[NSColor whiteColor]
|
|
811
|
+
range:NSMakeRange(0, [screenCancelTitleString length])];
|
|
812
|
+
[screenCancelButton setAttributedTitle:screenCancelTitleString];
|
|
813
|
+
|
|
814
|
+
// Add shadow for cancel button
|
|
815
|
+
[screenCancelButton.layer setShadowColor:[[NSColor blackColor] CGColor]];
|
|
816
|
+
[screenCancelButton.layer setShadowOffset:NSMakeSize(0, -1)];
|
|
817
|
+
[screenCancelButton.layer setShadowRadius:2.0];
|
|
818
|
+
[screenCancelButton.layer setShadowOpacity:0.2];
|
|
819
|
+
|
|
820
|
+
[screenCancelButton setTarget:g_delegate];
|
|
821
|
+
[screenCancelButton setAction:@selector(cancelButtonClicked:)];
|
|
822
|
+
|
|
823
|
+
// Position buttons - Start Record in center, Cancel below it
|
|
756
824
|
NSPoint buttonCenter = NSMakePoint(
|
|
757
825
|
(screenFrame.size.width - [selectButton frame].size.width) / 2,
|
|
758
|
-
(screenFrame.size.height - [selectButton frame].size.height) / 2
|
|
826
|
+
(screenFrame.size.height - [selectButton frame].size.height) / 2 + 30 // Slightly above center
|
|
759
827
|
);
|
|
760
828
|
[selectButton setFrameOrigin:buttonCenter];
|
|
761
829
|
|
|
830
|
+
NSPoint cancelButtonCenter = NSMakePoint(
|
|
831
|
+
(screenFrame.size.width - [screenCancelButton frame].size.width) / 2,
|
|
832
|
+
buttonCenter.y - [selectButton frame].size.height - 20 // 20px below main button
|
|
833
|
+
);
|
|
834
|
+
[screenCancelButton setFrameOrigin:cancelButtonCenter];
|
|
835
|
+
|
|
762
836
|
[overlayView addSubview:selectButton];
|
|
837
|
+
[overlayView addSubview:screenCancelButton];
|
|
763
838
|
[overlayWindow orderFront:nil];
|
|
764
839
|
[overlayWindow makeKeyAndOrderFront:nil];
|
|
765
840
|
|
|
@@ -946,13 +1021,59 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
946
1021
|
[g_selectButton setTarget:g_delegate];
|
|
947
1022
|
[g_selectButton setAction:@selector(selectButtonClicked:)];
|
|
948
1023
|
|
|
949
|
-
|
|
1024
|
+
// Add select button directly to window (not view) for proper layering
|
|
1025
|
+
[g_overlayWindow.contentView addSubview:g_selectButton];
|
|
1026
|
+
|
|
1027
|
+
// Create cancel button
|
|
1028
|
+
NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
1029
|
+
[cancelButton setTitle:@"Cancel"];
|
|
1030
|
+
[cancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1031
|
+
[cancelButton setBezelStyle:NSBezelStyleRounded];
|
|
1032
|
+
[cancelButton setFont:[NSFont systemFontOfSize:14 weight:NSFontWeightMedium]];
|
|
1033
|
+
|
|
1034
|
+
// Gray cancel button styling
|
|
1035
|
+
[cancelButton setWantsLayer:YES];
|
|
1036
|
+
[cancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8] CGColor]];
|
|
1037
|
+
[cancelButton.layer setCornerRadius:6.0];
|
|
1038
|
+
[cancelButton.layer setBorderColor:[[NSColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1.0] CGColor]];
|
|
1039
|
+
[cancelButton.layer setBorderWidth:1.0];
|
|
1040
|
+
|
|
1041
|
+
// White text for cancel button
|
|
1042
|
+
NSMutableAttributedString *cancelTitleString = [[NSMutableAttributedString alloc]
|
|
1043
|
+
initWithString:[cancelButton title]];
|
|
1044
|
+
[cancelTitleString addAttribute:NSForegroundColorAttributeName
|
|
1045
|
+
value:[NSColor whiteColor]
|
|
1046
|
+
range:NSMakeRange(0, [cancelTitleString length])];
|
|
1047
|
+
[cancelButton setAttributedTitle:cancelTitleString];
|
|
1048
|
+
|
|
1049
|
+
// Add shadow for cancel button
|
|
1050
|
+
[cancelButton.layer setShadowColor:[[NSColor blackColor] CGColor]];
|
|
1051
|
+
[cancelButton.layer setShadowOffset:NSMakeSize(0, -1)];
|
|
1052
|
+
[cancelButton.layer setShadowRadius:2.0];
|
|
1053
|
+
[cancelButton.layer setShadowOpacity:0.2];
|
|
1054
|
+
|
|
1055
|
+
[cancelButton setTarget:g_delegate];
|
|
1056
|
+
[cancelButton setAction:@selector(cancelButtonClicked:)];
|
|
1057
|
+
|
|
1058
|
+
// Add cancel button to window
|
|
1059
|
+
[g_overlayWindow.contentView addSubview:cancelButton];
|
|
1060
|
+
|
|
1061
|
+
// Cancel button reference will be found dynamically in positioning code
|
|
950
1062
|
|
|
951
1063
|
// Timer approach doesn't work well with Node.js
|
|
952
1064
|
// Instead, we'll use JavaScript polling via getWindowSelectionStatus
|
|
953
1065
|
// The JS side will call this function repeatedly to trigger overlay updates
|
|
954
1066
|
g_trackingTimer = nil; // No timer for now
|
|
955
1067
|
|
|
1068
|
+
// Add ESC key event monitor to cancel selection
|
|
1069
|
+
g_windowKeyEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown
|
|
1070
|
+
handler:^(NSEvent *event) {
|
|
1071
|
+
if ([event keyCode] == 53) { // ESC key
|
|
1072
|
+
NSLog(@"šŖ WINDOW SELECTION: ESC pressed - cancelling selection");
|
|
1073
|
+
cleanupWindowSelector();
|
|
1074
|
+
}
|
|
1075
|
+
}];
|
|
1076
|
+
|
|
956
1077
|
g_isWindowSelecting = true;
|
|
957
1078
|
g_selectedWindowInfo = nil;
|
|
958
1079
|
|