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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "1.13.0",
3
+ "version": "1.14.1",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -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 select button in center
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
- // Position button in center of screen
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
- [g_overlayView addSubview:g_selectButton];
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