node-mac-recorder 2.6.6 → 2.6.9

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,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+ const MacRecorder = require('./index');
5
+
6
+ async function debugScreenSelection() {
7
+ console.log('šŸ” Screen Selection Debug Test');
8
+ console.log('================================\n');
9
+
10
+ const selector = new WindowSelector();
11
+ const recorder = new MacRecorder();
12
+
13
+ try {
14
+ // First, let's see what displays MacRecorder sees
15
+ console.log('šŸ“ŗ MacRecorder displays:');
16
+ const macDisplays = await recorder.getDisplays();
17
+ macDisplays.forEach((display, i) => {
18
+ console.log(` ${i}: ID=${display.id}, Name="${display.name}", Resolution=${display.resolution}, Primary=${display.isPrimary}`);
19
+ });
20
+ console.log('');
21
+
22
+ // Start screen selection
23
+ console.log('šŸ–„ļø Starting screen selection...');
24
+ console.log(' Move mouse to different screens and click "Start Record" on one of them');
25
+ console.log('');
26
+
27
+ const selectedScreen = await selector.selectScreen();
28
+
29
+ console.log('āœ… Screen selected!');
30
+ console.log('šŸ“Š Selected screen data:');
31
+ console.log(JSON.stringify(selectedScreen, null, 2));
32
+
33
+ // Check if this ID exists in MacRecorder displays
34
+ const matchingDisplay = macDisplays.find(d => d.id === selectedScreen.id);
35
+
36
+ if (matchingDisplay) {
37
+ console.log('\nāœ… MATCH FOUND in MacRecorder displays:');
38
+ console.log(` Selected: ${selectedScreen.name} (ID: ${selectedScreen.id})`);
39
+ console.log(` MacRecorder: ${matchingDisplay.name} (ID: ${matchingDisplay.id})`);
40
+ } else {
41
+ console.log('\nāŒ NO MATCH found in MacRecorder displays!');
42
+ console.log(` Selected ID: ${selectedScreen.id}`);
43
+ console.log(` Available IDs: ${macDisplays.map(d => d.id).join(', ')}`);
44
+ }
45
+
46
+ console.log('\nšŸŽ¬ Testing actual recording...');
47
+ console.log(' Setting displayId option and starting short recording');
48
+
49
+ // Set the display ID from screen selection
50
+ recorder.setOptions({
51
+ displayId: selectedScreen.id,
52
+ includeSystemAudio: false,
53
+ includeMicrophone: false
54
+ });
55
+
56
+ const testFile = `./test-output/screen-selection-test-${Date.now()}.mov`;
57
+ console.log(` Recording file: ${testFile}`);
58
+
59
+ await recorder.startRecording(testFile);
60
+ console.log(' Recording started...');
61
+
62
+ // Record for 3 seconds
63
+ await new Promise(resolve => setTimeout(resolve, 3000));
64
+
65
+ await recorder.stopRecording();
66
+ console.log(' Recording stopped');
67
+
68
+ console.log('\nāœ… Test completed! Check the recording to see if it captured the correct screen.');
69
+
70
+ } catch (error) {
71
+ console.error('\nāŒ Error during test:', error.message);
72
+ if (error.stack) {
73
+ console.error('Stack:', error.stack);
74
+ }
75
+ process.exit(1);
76
+ }
77
+ }
78
+
79
+ if (require.main === module) {
80
+ debugScreenSelection();
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.6.6",
3
+ "version": "2.6.9",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -42,7 +42,8 @@
42
42
  "example:window-selector": "node examples/window-selector-example.js"
43
43
  },
44
44
  "dependencies": {
45
- "node-addon-api": "^7.0.0"
45
+ "node-addon-api": "^7.0.0",
46
+ "node-mac-recorder": "^2.6.7"
46
47
  },
47
48
  "devDependencies": {
48
49
  "node-gyp": "^10.0.0"
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ console.log('Starting screen selection...');
6
+ console.log('Click "Start Record" on ANY screen then this will show the result:');
7
+
8
+ const selector = new WindowSelector();
9
+
10
+ selector.startScreenSelection().then(() => {
11
+ console.log('Screen selection UI shown');
12
+
13
+ // Check every 500ms for selection
14
+ const checkInterval = setInterval(() => {
15
+ const selected = selector.getSelectedScreen();
16
+ if (selected) {
17
+ console.log('\nšŸŽ‰ SCREEN SELECTED!');
18
+ console.log('Selected data:', JSON.stringify(selected, null, 2));
19
+ clearInterval(checkInterval);
20
+ process.exit(0);
21
+ }
22
+ }, 500);
23
+
24
+ // Timeout after 15 seconds
25
+ setTimeout(() => {
26
+ console.log('\nā° Timeout - no screen selected');
27
+ clearInterval(checkInterval);
28
+ process.exit(1);
29
+ }, 15000);
30
+
31
+ }).catch(error => {
32
+ console.error('Error:', error.message);
33
+ process.exit(1);
34
+ });
@@ -53,6 +53,7 @@ void updateScreenOverlays();
53
53
  // Custom overlay view class
54
54
  @interface WindowSelectorOverlayView : NSView
55
55
  @property (nonatomic, strong) NSDictionary *windowInfo;
56
+ @property (nonatomic) BOOL isActiveWindow;
56
57
  @end
57
58
 
58
59
  @implementation WindowSelectorOverlayView
@@ -60,25 +61,44 @@ void updateScreenOverlays();
60
61
  - (instancetype)initWithFrame:(NSRect)frameRect {
61
62
  self = [super initWithFrame:frameRect];
62
63
  if (self) {
64
+ // Use layer for background instead of custom drawing
63
65
  self.wantsLayer = YES;
64
- self.layer.backgroundColor = [[NSColor clearColor] CGColor];
65
- // Ensure no borders or decorations
66
- self.layer.borderWidth = 0.0;
67
- self.layer.cornerRadius = 0.0;
66
+ self.isActiveWindow = YES; // Default to active for current window under mouse
67
+
68
+ // Set purple background with border using layer
69
+ [self updateAppearance];
70
+
71
+ // Window selector overlay view created
68
72
  }
69
73
  return self;
70
74
  }
71
75
 
72
- - (void)drawRect:(NSRect)dirtyRect {
73
- [super drawRect:dirtyRect];
74
-
75
- if (!self.windowInfo) return;
76
+ - (void)updateAppearance {
77
+ if (self.isActiveWindow) {
78
+ // Active window: brighter background
79
+ self.layer.backgroundColor = [[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] CGColor];
80
+ // Active window appearance set
81
+ } else {
82
+ // Inactive window: dimmer background
83
+ self.layer.backgroundColor = [[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25] CGColor];
84
+ // Inactive window appearance set
85
+ }
76
86
 
77
- // Background with transparency - purple tone (no border)
78
- [[NSColor colorWithRed:0.5 green:0.3 blue:0.8 alpha:0.25] setFill];
79
- NSRectFill(self.bounds);
87
+ // Purple border (same as screen selector)
88
+ self.layer.borderColor = [[NSColor colorWithRed:0.7 green:0.5 blue:1.0 alpha:0.6] CGColor];
89
+ self.layer.borderWidth = 4.0;
90
+ self.layer.cornerRadius = 0.0;
91
+ }
92
+
93
+ - (void)setIsActiveWindow:(BOOL)isActiveWindow {
94
+ if (_isActiveWindow != isActiveWindow) {
95
+ _isActiveWindow = isActiveWindow;
96
+ [self updateAppearance];
97
+ }
80
98
  }
81
99
 
100
+ // Layer-based approach, no custom drawing needed
101
+
82
102
  @end
83
103
 
84
104
  // Recording preview overlay view - full screen with cutout
@@ -202,9 +222,9 @@ void updateScreenOverlays();
202
222
  NSDictionary *screenInfo = [g_allScreens objectAtIndex:screenIndex];
203
223
  g_selectedScreenInfo = [screenInfo retain];
204
224
 
205
- NSLog(@"šŸ–„ļø SCREEN BUTTON CLICKED: %@ (%@)",
225
+ NSLog(@"šŸ–„ļø SCREEN SELECTED: %@ (ID: %@)",
206
226
  [screenInfo objectForKey:@"name"],
207
- [screenInfo objectForKey:@"resolution"]);
227
+ [screenInfo objectForKey:@"id"]);
208
228
 
209
229
  cleanupScreenSelector();
210
230
  }
@@ -463,10 +483,13 @@ void updateOverlay() {
463
483
  if (!windowScreen) windowScreen = [NSScreen mainScreen];
464
484
 
465
485
  // Convert coordinates from CGWindow (top-left) to NSWindow (bottom-left) for the specific screen
466
- CGFloat screenHeight = [windowScreen frame].size.height;
486
+ NSRect screenFrame = [windowScreen frame];
487
+ CGFloat screenHeight = screenFrame.size.height;
467
488
  CGFloat adjustedY = screenHeight - y - height;
468
489
 
469
- // Use actual window coordinates without clamping to preserve overlay accuracy
490
+ // Window coordinates are in global space, overlay frame should be screen-relative
491
+ // Keep X coordinate as-is (already in global space which is what we want)
492
+ // Only convert Y from top-left to bottom-left coordinate system
470
493
  NSRect overlayFrame = NSMakeRect(x, adjustedY, width, height);
471
494
 
472
495
  NSString *windowTitle = [windowUnderCursor objectForKey:@"title"] ?: @"Untitled";
@@ -495,7 +518,6 @@ void updateOverlay() {
495
518
 
496
519
  // Update overlay view window info
497
520
  [(WindowSelectorOverlayView *)g_overlayView setWindowInfo:windowUnderCursor];
498
- [g_overlayView setNeedsDisplay:YES];
499
521
 
500
522
  // Add/update info label above button
501
523
  NSTextField *infoLabel = nil;
@@ -917,20 +939,57 @@ bool startScreenSelection() {
917
939
  NSMutableArray *screenInfoArray = [[NSMutableArray alloc] init];
918
940
  g_screenOverlayWindows = [[NSMutableArray alloc] init];
919
941
 
942
+ // Get real display IDs like MacRecorder does
943
+ CGDirectDisplayID activeDisplays[32];
944
+ uint32_t displayCount;
945
+ CGError err = CGGetActiveDisplayList(32, activeDisplays, &displayCount);
946
+
947
+ if (err != kCGErrorSuccess) {
948
+ NSLog(@"āŒ Failed to get active display list: %d", err);
949
+ return false;
950
+ }
951
+
920
952
  for (NSInteger i = 0; i < [screens count]; i++) {
921
953
  NSScreen *screen = [screens objectAtIndex:i];
922
954
  NSRect screenFrame = [screen frame];
923
955
 
924
- // Create screen info dictionary
956
+ // Get the real CGDirectDisplayID for this screen by matching frame
957
+ CGDirectDisplayID displayID = 0;
958
+
959
+ // Find matching display by comparing bounds
960
+ for (uint32_t j = 0; j < displayCount; j++) {
961
+ CGDirectDisplayID candidateID = activeDisplays[j];
962
+ CGRect displayBounds = CGDisplayBounds(candidateID);
963
+
964
+ // Compare screen frame with display bounds
965
+ if (fabs(screenFrame.origin.x - displayBounds.origin.x) < 1.0 &&
966
+ fabs(screenFrame.origin.y - displayBounds.origin.y) < 1.0 &&
967
+ fabs(screenFrame.size.width - displayBounds.size.width) < 1.0 &&
968
+ fabs(screenFrame.size.height - displayBounds.size.height) < 1.0) {
969
+ displayID = candidateID;
970
+ // Screen matched to display ID
971
+ break;
972
+ }
973
+ }
974
+
975
+ // Fallback: use array index if no match found
976
+ if (displayID == 0 && i < displayCount) {
977
+ displayID = activeDisplays[i];
978
+ // Used fallback display ID
979
+ } else if (displayID == 0) {
980
+ NSLog(@"āŒ Screen %ld could not get Display ID", (long)i);
981
+ }
982
+
983
+ // Create screen info dictionary with real display ID
925
984
  NSMutableDictionary *screenInfo = [[NSMutableDictionary alloc] init];
926
- [screenInfo setObject:[NSNumber numberWithInteger:i] forKey:@"id"];
985
+ [screenInfo setObject:[NSNumber numberWithUnsignedInt:displayID] forKey:@"id"]; // Real display ID
927
986
  [screenInfo setObject:[NSString stringWithFormat:@"Display %ld", (long)(i + 1)] forKey:@"name"];
928
987
  [screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.origin.x] forKey:@"x"];
929
988
  [screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.origin.y] forKey:@"y"];
930
989
  [screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.size.width] forKey:@"width"];
931
990
  [screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.size.height] forKey:@"height"];
932
991
  [screenInfo setObject:[NSString stringWithFormat:@"%.0fx%.0f", screenFrame.size.width, screenFrame.size.height] forKey:@"resolution"];
933
- [screenInfo setObject:[NSNumber numberWithBool:(i == 0)] forKey:@"isPrimary"]; // First screen is primary
992
+ [screenInfo setObject:[NSNumber numberWithBool:(displayID == CGMainDisplayID())] forKey:@"isPrimary"]; // Real primary check
934
993
  [screenInfoArray addObject:screenInfo];
935
994
 
936
995
  // Create overlay window for this screen (FULL screen including menu bar)
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function testRealScreenIDs() {
6
+ console.log('Testing real screen ID generation...\n');
7
+
8
+ const selector = new WindowSelector();
9
+
10
+ // Start screen selection to generate screen info
11
+ console.log('Starting screen selection (will timeout in 3 seconds)...');
12
+
13
+ const startPromise = selector.startScreenSelection();
14
+
15
+ // Wait a bit for screen info to be generated
16
+ await new Promise(resolve => setTimeout(resolve, 1000));
17
+
18
+ // Try to get selected screen info (should be null since nothing selected)
19
+ const selectedInfo = selector.getSelectedScreen();
20
+ console.log('Selected screen info (should be null):', selectedInfo);
21
+
22
+ // Clean up
23
+ try {
24
+ await selector.stopScreenSelection();
25
+ } catch (e) {
26
+ // Ignore
27
+ }
28
+
29
+ console.log('\nTest completed. Check logs above for screen creation details.');
30
+ }
31
+
32
+ testRealScreenIDs().catch(console.error);