node-mac-recorder 2.15.8 → 2.15.10

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,69 @@
1
+ const MacRecorder = require('./index');
2
+
3
+ async function debugCoordinates() {
4
+ const recorder = new MacRecorder();
5
+ const displays = await recorder.getDisplays();
6
+
7
+ console.log('šŸ–„ļø Display coordinates analysis:');
8
+ displays.forEach((display, index) => {
9
+ console.log(`Display ${index}: ${display.name}`);
10
+ console.log(` ID: ${display.id}`);
11
+ console.log(` Position: (${display.x}, ${display.y})`);
12
+ console.log(` Size: ${display.width}x${display.height}`);
13
+ console.log(` isPrimary: ${display.isPrimary}`);
14
+ console.log('');
15
+ });
16
+
17
+ // Calculate combined frame like we do in native code
18
+ let combinedFrame = { x: 0, y: 0, width: 0, height: 0 };
19
+ let first = true;
20
+
21
+ for (const display of displays) {
22
+ if (first) {
23
+ combinedFrame = {
24
+ x: display.x,
25
+ y: display.y,
26
+ width: display.width,
27
+ height: display.height
28
+ };
29
+ first = false;
30
+ } else {
31
+ const minX = Math.min(combinedFrame.x, display.x);
32
+ const minY = Math.min(combinedFrame.y, display.y);
33
+ const maxX = Math.max(combinedFrame.x + combinedFrame.width, display.x + display.width);
34
+ const maxY = Math.max(combinedFrame.y + combinedFrame.height, display.y + display.height);
35
+
36
+ combinedFrame = {
37
+ x: minX,
38
+ y: minY,
39
+ width: maxX - minX,
40
+ height: maxY - minY
41
+ };
42
+ }
43
+ }
44
+
45
+ console.log('šŸ“ Combined frame calculation:');
46
+ console.log(` Origin: (${combinedFrame.x}, ${combinedFrame.y})`);
47
+ console.log(` Size: ${combinedFrame.width}x${combinedFrame.height}`);
48
+ console.log('');
49
+
50
+ console.log('šŸ”„ Coordinate conversion examples:');
51
+ displays.forEach((display, index) => {
52
+ console.log(`Display ${index} (${display.name}):`);
53
+ const localOriginX = display.x - combinedFrame.x;
54
+ const localOriginY = display.y - combinedFrame.y;
55
+ console.log(` Global offset from combined: (${localOriginX}, ${localOriginY})`);
56
+
57
+ // Test window at display origin
58
+ const testWindowGlobalX = display.x + 100;
59
+ const testWindowGlobalY = display.y + 100;
60
+ const localWindowX = testWindowGlobalX - combinedFrame.x;
61
+ const localWindowY = testWindowGlobalY - combinedFrame.y;
62
+
63
+ console.log(` Test window at (${testWindowGlobalX}, ${testWindowGlobalY}) global`);
64
+ console.log(` -> Local coordinates: (${localWindowX}, ${localWindowY})`);
65
+ console.log('');
66
+ });
67
+ }
68
+
69
+ debugCoordinates();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.15.8",
3
+ "version": "2.15.10",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -451,13 +451,47 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
451
451
  NSArray *screens = [NSScreen screens];
452
452
  NSMutableArray *displays = [NSMutableArray array];
453
453
 
454
+ // Get real CGDirectDisplayIDs first
455
+ CGDirectDisplayID activeDisplays[32];
456
+ uint32_t displayCount;
457
+ CGGetActiveDisplayList(32, activeDisplays, &displayCount);
458
+
454
459
  for (NSUInteger i = 0; i < [screens count]; i++) {
455
460
  NSScreen *screen = [screens objectAtIndex:i];
461
+ NSRect frame = [screen frame];
462
+
463
+ // Find matching CGDirectDisplayID
464
+ CGDirectDisplayID displayID = 0;
465
+ bool isPrimary = false;
466
+
467
+ for (uint32_t j = 0; j < displayCount; j++) {
468
+ CGDirectDisplayID candidateID = activeDisplays[j];
469
+ CGRect displayBounds = CGDisplayBounds(candidateID);
470
+
471
+ if (fabs(frame.origin.x - displayBounds.origin.x) < 1.0 &&
472
+ fabs(frame.origin.y - displayBounds.origin.y) < 1.0 &&
473
+ fabs(frame.size.width - displayBounds.size.width) < 1.0 &&
474
+ fabs(frame.size.height - displayBounds.size.height) < 1.0) {
475
+ displayID = candidateID;
476
+ isPrimary = (candidateID == CGMainDisplayID());
477
+ break;
478
+ }
479
+ }
480
+
481
+ // Fallback if no match found
482
+ if (displayID == 0 && i < displayCount) {
483
+ displayID = activeDisplays[i];
484
+ isPrimary = (displayID == CGMainDisplayID());
485
+ }
486
+
456
487
  NSDictionary *displayInfo = @{
457
- @"id": @(i + 1),
488
+ @"id": @(displayID), // Use real display ID
458
489
  @"name": [NSString stringWithFormat:@"Display %lu", (unsigned long)(i + 1)],
459
- @"width": @((int)screen.frame.size.width),
460
- @"height": @((int)screen.frame.size.height)
490
+ @"width": @((int)frame.size.width),
491
+ @"height": @((int)frame.size.height),
492
+ @"x": @((int)frame.origin.x),
493
+ @"y": @((int)frame.origin.y),
494
+ @"isPrimary": @(isPrimary)
461
495
  };
462
496
  [displays addObject:displayInfo];
463
497
  }
@@ -74,6 +74,7 @@ void updateScreenOverlays();
74
74
  @property (nonatomic) BOOL isActiveWindow;
75
75
  @property (nonatomic) BOOL isToggled;
76
76
  @property (nonatomic) NSRect highlightFrame;
77
+ @property (nonatomic, strong) NSValue *globalOriginOffset; // Store global coordinate offset
77
78
  - (void)setHighlightFrame:(NSRect)frame;
78
79
  @end
79
80
 
@@ -863,10 +864,21 @@ void updateOverlay() {
863
864
  }
864
865
  }
865
866
 
866
- // No need to resize window since it's full-screen, just update the highlight area
867
+ // Convert global window coordinates to local view coordinates
868
+ // Account for multi-display offset from global origin
869
+ WindowSelectorOverlayView *overlayView = (WindowSelectorOverlayView *)g_overlayView;
870
+ NSPoint globalOffset = NSZeroPoint;
871
+ if (overlayView.globalOriginOffset) {
872
+ globalOffset = [overlayView.globalOriginOffset pointValue];
873
+ }
874
+
875
+ // Convert global window position to local view position
876
+ CGFloat localX = x - globalOffset.x;
877
+ CGFloat localY = ([g_overlayView frame].size.height - (y - globalOffset.y)) - height;
878
+
867
879
  // Update overlay view window info for highlighting
868
- [(WindowSelectorOverlayView *)g_overlayView setWindowInfo:targetWindow];
869
- [(WindowSelectorOverlayView *)g_overlayView setHighlightFrame:NSMakeRect(x, [g_overlayView frame].size.height - y - height, width, height)];
880
+ [overlayView setWindowInfo:targetWindow];
881
+ [overlayView setHighlightFrame:NSMakeRect(localX, localY, width, height)];
870
882
 
871
883
  // Only reset toggle state when switching to different window (not for position updates)
872
884
  if (!g_hasToggledWindow) {
@@ -967,19 +979,19 @@ void updateOverlay() {
967
979
  // Position buttons - Start Record in center of selected window
968
980
  if (g_selectButton) {
969
981
  NSSize buttonSize = [g_selectButton frame].size;
970
- // Use window center for positioning
971
- CGFloat windowCenterX = x + (width / 2);
972
- CGFloat windowCenterY = y + (height / 2);
982
+ // Use local window center for positioning
983
+ CGFloat localWindowCenterX = localX + (width / 2);
984
+ CGFloat localWindowCenterY = localY + (height / 2);
973
985
  NSPoint buttonCenter = NSMakePoint(
974
- windowCenterX - (buttonSize.width / 2),
975
- windowCenterY - (buttonSize.height / 2) // Perfect center of window
986
+ localWindowCenterX - (buttonSize.width / 2),
987
+ localWindowCenterY - (buttonSize.height / 2) // Perfect center of window
976
988
  );
977
989
  [g_selectButton setFrameOrigin:buttonCenter];
978
990
 
979
991
  // Position app icon above window center
980
992
  NSPoint iconCenter = NSMakePoint(
981
- windowCenterX - (96 / 2), // Center horizontally on window
982
- windowCenterY + 120 // 120px above window center
993
+ localWindowCenterX - (96 / 2), // Center horizontally on window
994
+ localWindowCenterY + 120 // 120px above window center
983
995
  );
984
996
  [appIconView setFrameOrigin:iconCenter];
985
997
  NSLog(@"šŸŽÆ Positioning app icon at: (%.0f, %.0f) for window size: (%.0f, %.0f)",
@@ -1001,8 +1013,8 @@ void updateOverlay() {
1001
1013
 
1002
1014
  // Position info label between icon and button relative to window center
1003
1015
  NSPoint labelCenter = NSMakePoint(
1004
- windowCenterX - ([infoLabel frame].size.width / 2), // Center horizontally on window
1005
- windowCenterY + 50 // 50px above window center, below icon
1016
+ localWindowCenterX - ([infoLabel frame].size.width / 2), // Center horizontally on window
1017
+ localWindowCenterY + 50 // 50px above window center, below icon
1006
1018
  );
1007
1019
  [infoLabel setFrameOrigin:labelCenter];
1008
1020
 
@@ -1019,8 +1031,8 @@ void updateOverlay() {
1019
1031
  if (cancelButton) {
1020
1032
  NSSize cancelButtonSize = [cancelButton frame].size;
1021
1033
  NSPoint cancelButtonCenter = NSMakePoint(
1022
- windowCenterX - (cancelButtonSize.width / 2), // Center horizontally on window
1023
- windowCenterY - 80 // 80px below window center
1034
+ localWindowCenterX - (cancelButtonSize.width / 2), // Center horizontally on window
1035
+ localWindowCenterY - 80 // 80px below window center
1024
1036
  );
1025
1037
  [cancelButton setFrameOrigin:cancelButtonCenter];
1026
1038
  }
@@ -1436,8 +1448,9 @@ bool startScreenSelection() {
1436
1448
  [overlayWindow setOpaque:NO];
1437
1449
  [overlayWindow setBackgroundColor:[NSColor clearColor]];
1438
1450
 
1439
- // Create overlay view
1440
- ScreenSelectorOverlayView *overlayView = [[ScreenSelectorOverlayView alloc] initWithFrame:screenFrame];
1451
+ // Create overlay view - use local frame (0,0) not screen frame
1452
+ NSRect localFrame = NSMakeRect(0, 0, screenFrame.size.width, screenFrame.size.height);
1453
+ ScreenSelectorOverlayView *overlayView = [[ScreenSelectorOverlayView alloc] initWithFrame:localFrame];
1441
1454
  [overlayView setScreenInfo:screenInfo];
1442
1455
  [overlayWindow setContentView:overlayView];
1443
1456
 
@@ -1521,8 +1534,8 @@ bool startScreenSelection() {
1521
1534
  [screenCancelButton setFocusRingType:NSFocusRingTypeNone];
1522
1535
  [screenCancelButton setShowsBorderOnlyWhileMouseInside:NO];
1523
1536
 
1524
- // Create info label for screen
1525
- NSTextField *screenInfoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, screenFrame.size.width - 40, 60)];
1537
+ // Create info label for screen - use local coordinates
1538
+ NSTextField *screenInfoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, localFrame.size.width - 40, 60)];
1526
1539
  [screenInfoLabel setEditable:NO];
1527
1540
  [screenInfoLabel setSelectable:NO];
1528
1541
  [screenInfoLabel setBezeled:NO];
@@ -1548,17 +1561,17 @@ bool startScreenSelection() {
1548
1561
  NSString *resolution = [screenInfo objectForKey:@"resolution"] ?: @"Unknown Resolution";
1549
1562
  [screenInfoLabel setStringValue:[NSString stringWithFormat:@"%@\n%@", screenName, resolution]];
1550
1563
 
1551
- // Position buttons - Start Record in perfect center
1564
+ // Position buttons - Start Record in perfect center using local coordinates
1552
1565
  NSPoint buttonCenter = NSMakePoint(
1553
- (screenFrame.size.width - [selectButton frame].size.width) / 2,
1554
- (screenFrame.size.height - [selectButton frame].size.height) / 2 // Perfect center
1566
+ (localFrame.size.width - [selectButton frame].size.width) / 2,
1567
+ (localFrame.size.height - [selectButton frame].size.height) / 2 // Perfect center
1555
1568
  );
1556
1569
  [selectButton setFrameOrigin:buttonCenter];
1557
1570
 
1558
- // Position screen icon above center
1571
+ // Position screen icon above center using local coordinates
1559
1572
  NSPoint iconCenter = NSMakePoint(
1560
- (screenFrame.size.width - 96) / 2, // Center horizontally (icon is 96px wide)
1561
- (screenFrame.size.height / 2) + 120 // 120px above center
1573
+ (localFrame.size.width - 96) / 2, // Center horizontally (icon is 96px wide)
1574
+ (localFrame.size.height / 2) + 120 // 120px above center
1562
1575
  );
1563
1576
  [screenIconView setFrameOrigin:iconCenter];
1564
1577
 
@@ -1572,16 +1585,16 @@ bool startScreenSelection() {
1572
1585
  screenFloatAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
1573
1586
  [screenIconView.layer addAnimation:screenFloatAnimationX forKey:@"floatAnimationX"];
1574
1587
 
1575
- // Position info label between icon and button
1588
+ // Position info label between icon and button using local coordinates
1576
1589
  NSPoint labelCenter = NSMakePoint(
1577
- (screenFrame.size.width - [screenInfoLabel frame].size.width) / 2, // Center horizontally
1578
- (screenFrame.size.height / 2) + 50 // 50px above center, below icon
1590
+ (localFrame.size.width - [screenInfoLabel frame].size.width) / 2, // Center horizontally
1591
+ (localFrame.size.height / 2) + 50 // 50px above center, below icon
1579
1592
  );
1580
1593
  [screenInfoLabel setFrameOrigin:labelCenter];
1581
1594
 
1582
1595
  NSPoint cancelButtonCenter = NSMakePoint(
1583
- (screenFrame.size.width - [screenCancelButton frame].size.width) / 2,
1584
- (screenFrame.size.height / 2) - 80 // 80px below center
1596
+ (localFrame.size.width - [screenCancelButton frame].size.width) / 2,
1597
+ (localFrame.size.height / 2) - 80 // 80px below center
1585
1598
  );
1586
1599
  [screenCancelButton setFrameOrigin:cancelButtonCenter];
1587
1600
 
@@ -1801,9 +1814,21 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1801
1814
  return env.Null();
1802
1815
  }
1803
1816
 
1804
- // Create full-screen overlay window to prevent window dragging
1805
- NSScreen *mainScreen = [NSScreen mainScreen];
1806
- NSRect fullScreenFrame = [mainScreen frame];
1817
+ // Create multi-display overlay window to prevent window dragging
1818
+ NSArray *allScreens = [NSScreen screens];
1819
+ NSRect combinedFrame = NSZeroRect;
1820
+
1821
+ // Calculate combined frame that covers all displays
1822
+ for (NSScreen *screen in allScreens) {
1823
+ NSRect screenFrame = [screen frame];
1824
+ if (NSEqualRects(combinedFrame, NSZeroRect)) {
1825
+ combinedFrame = screenFrame;
1826
+ } else {
1827
+ combinedFrame = NSUnionRect(combinedFrame, screenFrame);
1828
+ }
1829
+ }
1830
+
1831
+ NSRect fullScreenFrame = combinedFrame;
1807
1832
  g_overlayWindow = [[NoFocusWindow alloc] initWithContentRect:fullScreenFrame
1808
1833
  styleMask:NSWindowStyleMaskBorderless
1809
1834
  backing:NSBackingStoreBuffered
@@ -1832,8 +1857,13 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1832
1857
  [g_overlayWindow setOpaque:NO];
1833
1858
  [g_overlayWindow setBackgroundColor:[NSColor clearColor]];
1834
1859
 
1835
- // Create overlay view covering full screen
1836
- g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:fullScreenFrame];
1860
+ // Create overlay view covering all displays
1861
+ // Convert global coordinates to local view coordinates (offset by origin)
1862
+ NSRect localViewFrame = NSMakeRect(0, 0, fullScreenFrame.size.width, fullScreenFrame.size.height);
1863
+ g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:localViewFrame];
1864
+
1865
+ // Store the global origin offset for coordinate conversion
1866
+ [(WindowSelectorOverlayView *)g_overlayView setValue:[NSValue valueWithPoint:fullScreenFrame.origin] forKey:@"globalOriginOffset"];
1837
1867
  [g_overlayWindow setContentView:g_overlayView];
1838
1868
 
1839
1869
  // Note: NSWindow doesn't have setWantsLayer method, only NSView does
@@ -0,0 +1,120 @@
1
+ const MacRecorder = require('./index');
2
+ const WindowSelector = MacRecorder.WindowSelector;
3
+
4
+ console.log('🧪 Testing multi-display fixes...');
5
+
6
+ async function testDisplaySelection() {
7
+ console.log('\nšŸ–„ļø Testing display selection:');
8
+
9
+ const recorder = new MacRecorder();
10
+ const displays = await recorder.getDisplays();
11
+
12
+ console.log(`Found ${displays.length} displays:`);
13
+ displays.forEach((display, index) => {
14
+ console.log(` ${index + 1}. ${display.name} (ID: ${display.id}) - ${display.resolution} at (${display.x}, ${display.y}) ${display.isPrimary ? '[PRIMARY]' : ''}`);
15
+ });
16
+
17
+ // Test recording on each display
18
+ for (const display of displays) {
19
+ console.log(`\nšŸ“¹ Testing recording on ${display.name}:`);
20
+ try {
21
+ const outputPath = `./test-output/display-${display.id}-test.mov`;
22
+
23
+ await recorder.startRecording(outputPath, {
24
+ displayId: display.id,
25
+ captureCursor: false,
26
+ includeMicrophone: false,
27
+ includeSystemAudio: false
28
+ });
29
+
30
+ console.log(`āœ… Recording started on ${display.name}`);
31
+
32
+ // Record for 2 seconds
33
+ await new Promise(resolve => setTimeout(resolve, 2000));
34
+
35
+ await recorder.stopRecording();
36
+ console.log(`āœ… Recording stopped on ${display.name}`);
37
+
38
+ } catch (error) {
39
+ console.log(`āŒ Recording failed on ${display.name}: ${error.message}`);
40
+ }
41
+
42
+ // Small delay between tests
43
+ await new Promise(resolve => setTimeout(resolve, 1000));
44
+ }
45
+ }
46
+
47
+ async function testScreenSelector() {
48
+ console.log('\nšŸŽÆ Testing screen selector multi-display...');
49
+
50
+ const selector = new WindowSelector();
51
+
52
+ try {
53
+ console.log('šŸ” Starting screen selection... (This should show overlays on ALL displays)');
54
+ await selector.startScreenSelection();
55
+
56
+ console.log('ā±ļø Screen selection UI is running...');
57
+ console.log(' - Check if overlays appear on ALL displays');
58
+ console.log(' - Check if buttons and text are positioned correctly');
59
+ console.log(' - Check if app icons are visible and animated');
60
+
61
+ // Let it run for 10 seconds for manual inspection
62
+ await new Promise(resolve => setTimeout(resolve, 10000));
63
+
64
+ console.log('šŸ›‘ Stopping screen selection...');
65
+ await selector.stopScreenSelection();
66
+
67
+ } catch (error) {
68
+ console.log(`āŒ Screen selector test failed: ${error.message}`);
69
+ }
70
+ }
71
+
72
+ async function testWindowSelector() {
73
+ console.log('\n🪟 Testing window selector multi-display...');
74
+
75
+ const selector = new WindowSelector();
76
+
77
+ try {
78
+ console.log('šŸ” Starting window selection... (This should work across ALL displays)');
79
+ await selector.startSelection();
80
+
81
+ console.log('ā±ļø Window selection UI is running...');
82
+ console.log(' - Move cursor to windows on different displays');
83
+ console.log(' - Check if highlighting works properly');
84
+ console.log(' - Check if buttons appear correctly over highlighted windows');
85
+
86
+ // Let it run for 15 seconds for manual inspection
87
+ await new Promise(resolve => setTimeout(resolve, 15000));
88
+
89
+ console.log('šŸ›‘ Stopping window selection...');
90
+ await selector.stopSelection();
91
+
92
+ } catch (error) {
93
+ console.log(`āŒ Window selector test failed: ${error.message}`);
94
+ }
95
+ }
96
+
97
+ async function runTests() {
98
+ try {
99
+ // Check permissions first
100
+ const recorder = new MacRecorder();
101
+ const permissions = await recorder.checkPermissions();
102
+
103
+ if (!permissions.screenRecording) {
104
+ console.log('āŒ Screen recording permission required. Enable in System Preferences > Security & Privacy > Privacy > Screen Recording');
105
+ return;
106
+ }
107
+
108
+ await testDisplaySelection();
109
+ await testScreenSelector();
110
+ await testWindowSelector();
111
+
112
+ console.log('\nšŸŽ‰ Multi-display tests completed!');
113
+
114
+ } catch (error) {
115
+ console.log(`āŒ Test suite failed: ${error.message}`);
116
+ console.log(error.stack);
117
+ }
118
+ }
119
+
120
+ runTests();
@@ -0,0 +1,88 @@
1
+ const MacRecorder = require('./index');
2
+ const WindowSelector = MacRecorder.WindowSelector;
3
+
4
+ async function testWindowSelector() {
5
+ console.log('🪟 Testing FIXED window selector multi-display...');
6
+
7
+ const recorder = new MacRecorder();
8
+ const displays = await recorder.getDisplays();
9
+
10
+ console.log('šŸ“Š Display setup:');
11
+ displays.forEach((display, index) => {
12
+ console.log(` ${display.name}: ${display.resolution} at (${display.x}, ${display.y}) ${display.isPrimary ? '[PRIMARY]' : ''}`);
13
+ });
14
+
15
+ const selector = new WindowSelector();
16
+
17
+ try {
18
+ console.log('\nšŸ” Starting window selection...');
19
+ console.log(' - Test windows on BOTH displays');
20
+ console.log(' - Check if buttons appear correctly positioned');
21
+ console.log(' - Primary display should now work correctly');
22
+
23
+ await selector.startSelection();
24
+
25
+ // Let it run for 20 seconds for thorough testing
26
+ await new Promise(resolve => setTimeout(resolve, 20000));
27
+
28
+ console.log('šŸ›‘ Stopping window selection...');
29
+ await selector.stopSelection();
30
+
31
+ console.log('āœ… Window selector test completed!');
32
+
33
+ } catch (error) {
34
+ console.log(`āŒ Window selector test failed: ${error.message}`);
35
+ console.log(error.stack);
36
+ }
37
+ }
38
+
39
+ async function testSecondaryDisplayRecording() {
40
+ console.log('\nšŸŽ„ Testing secondary display recording...');
41
+
42
+ const recorder = new MacRecorder();
43
+ const displays = await recorder.getDisplays();
44
+
45
+ const secondaryDisplay = displays.find(d => !d.isPrimary);
46
+ if (!secondaryDisplay) {
47
+ console.log('āš ļø No secondary display found');
48
+ return;
49
+ }
50
+
51
+ console.log(`šŸ–„ļø Testing recording on ${secondaryDisplay.name} (ID: ${secondaryDisplay.id})`);
52
+
53
+ try {
54
+ const outputPath = `./test-output/secondary-display-fix-test.mov`;
55
+
56
+ await recorder.startRecording(outputPath, {
57
+ displayId: secondaryDisplay.id,
58
+ captureCursor: true,
59
+ includeMicrophone: false,
60
+ includeSystemAudio: false
61
+ });
62
+
63
+ console.log('āœ… Recording started on secondary display');
64
+
65
+ // Record for 3 seconds
66
+ await new Promise(resolve => setTimeout(resolve, 3000));
67
+
68
+ await recorder.stopRecording();
69
+ console.log('āœ… Recording completed on secondary display');
70
+
71
+ } catch (error) {
72
+ console.log(`āŒ Secondary display recording failed: ${error.message}`);
73
+ }
74
+ }
75
+
76
+ async function runTests() {
77
+ try {
78
+ await testWindowSelector();
79
+ await testSecondaryDisplayRecording();
80
+
81
+ console.log('\nšŸŽ‰ All tests completed!');
82
+
83
+ } catch (error) {
84
+ console.log(`āŒ Test suite failed: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ runTests();