node-mac-recorder 2.6.1 → 2.6.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.6.1",
3
+ "version": "2.6.3",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -30,6 +30,8 @@ static NSMutableArray *g_screenOverlayWindows = nil;
30
30
  static NSDictionary *g_selectedScreenInfo = nil;
31
31
  static NSArray *g_allScreens = nil;
32
32
  static id g_screenKeyEventMonitor = nil;
33
+ static NSTimer *g_screenTrackingTimer = nil;
34
+ static NSInteger g_currentActiveScreenIndex = -1;
33
35
 
34
36
  // Forward declarations
35
37
  void cleanupWindowSelector();
@@ -46,6 +48,7 @@ bool stopScreenSelection();
46
48
  NSDictionary* getSelectedScreenInfo();
47
49
  bool showScreenRecordingPreview(NSDictionary *screenInfo);
48
50
  bool hideScreenRecordingPreview();
51
+ void updateScreenOverlays();
49
52
 
50
53
  // Custom overlay view class
51
54
  @interface WindowSelectorOverlayView : NSView
@@ -139,6 +142,7 @@ bool hideScreenRecordingPreview();
139
142
  // Screen selection overlay view
140
143
  @interface ScreenSelectorOverlayView : NSView
141
144
  @property (nonatomic, strong) NSDictionary *screenInfo;
145
+ @property (nonatomic) BOOL isActiveScreen;
142
146
  @end
143
147
 
144
148
  @implementation ScreenSelectorOverlayView
@@ -151,6 +155,7 @@ bool hideScreenRecordingPreview();
151
155
  // Ensure no borders or decorations
152
156
  self.layer.borderWidth = 0.0;
153
157
  self.layer.cornerRadius = 0.0;
158
+ self.isActiveScreen = NO;
154
159
  }
155
160
  return self;
156
161
  }
@@ -160,12 +165,23 @@ bool hideScreenRecordingPreview();
160
165
 
161
166
  if (!self.screenInfo) return;
162
167
 
163
- // Background with transparency - purple tone (no border)
164
- // Ensure no borders or frames are drawn
165
- [[NSColor colorWithRed:0.5 green:0.3 blue:0.8 alpha:0.3] setFill];
168
+ // Background with transparency - purple tone varies by active state
169
+ if (self.isActiveScreen) {
170
+ // Active screen: brighter, more opaque
171
+ [[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] setFill];
172
+ } else {
173
+ // Inactive screen: dimmer, less opaque
174
+ [[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25] setFill];
175
+ }
166
176
  NSRectFill(self.bounds);
167
177
 
168
- // Text will be handled by separate label above button
178
+ // Add subtle border for active screen
179
+ if (self.isActiveScreen) {
180
+ [[NSColor colorWithRed:0.7 green:0.5 blue:1.0 alpha:0.6] setStroke];
181
+ NSBezierPath *borderPath = [NSBezierPath bezierPathWithRect:NSInsetRect(self.bounds, 2, 2)];
182
+ [borderPath setLineWidth:4.0];
183
+ [borderPath stroke];
184
+ }
169
185
  }
170
186
 
171
187
  @end
@@ -216,6 +232,10 @@ bool hideScreenRecordingPreview();
216
232
  - (void)timerUpdate:(NSTimer *)timer {
217
233
  updateOverlay();
218
234
  }
235
+
236
+ - (void)screenTimerUpdate:(NSTimer *)timer {
237
+ updateScreenOverlays();
238
+ }
219
239
  @end
220
240
 
221
241
  static WindowSelectorDelegate *g_delegate = nil;
@@ -766,10 +786,109 @@ bool hideRecordingPreview() {
766
786
  }
767
787
  }
768
788
 
789
+ // Update screen overlays based on mouse position
790
+ void updateScreenOverlays() {
791
+ @autoreleasepool {
792
+ if (!g_isScreenSelecting || !g_screenOverlayWindows || !g_allScreens) return;
793
+
794
+ // Get current mouse position
795
+ NSPoint mouseLocation = [NSEvent mouseLocation];
796
+ // Convert from NSEvent coordinates (bottom-left) to screen coordinates
797
+ NSArray *screens = [NSScreen screens];
798
+ NSScreen *mouseScreen = nil;
799
+ NSInteger mouseScreenIndex = -1;
800
+
801
+ // Find which screen contains the mouse
802
+ for (NSInteger i = 0; i < [screens count]; i++) {
803
+ NSScreen *screen = [screens objectAtIndex:i];
804
+ NSRect screenFrame = [screen frame];
805
+
806
+ if (NSPointInRect(mouseLocation, screenFrame)) {
807
+ mouseScreen = screen;
808
+ mouseScreenIndex = i;
809
+ break;
810
+ }
811
+ }
812
+
813
+ // If mouse screen changed, update overlays
814
+ if (mouseScreenIndex != g_currentActiveScreenIndex) {
815
+ g_currentActiveScreenIndex = mouseScreenIndex;
816
+
817
+ // Update all screen overlays
818
+ for (NSInteger i = 0; i < [g_screenOverlayWindows count] && i < [screens count]; i++) {
819
+ NSWindow *overlayWindow = [g_screenOverlayWindows objectAtIndex:i];
820
+ ScreenSelectorOverlayView *overlayView = (ScreenSelectorOverlayView *)[overlayWindow contentView];
821
+
822
+ // Update overlay appearance based on whether it's the active screen
823
+ bool isActiveScreen = (i == mouseScreenIndex);
824
+
825
+ // Update overlay state and appearance
826
+ [overlayView setIsActiveScreen:isActiveScreen];
827
+ [overlayView setNeedsDisplay:YES];
828
+
829
+ // Update UI elements based on active state
830
+ for (NSView *subview in [overlayView subviews]) {
831
+ if ([subview isKindOfClass:[NSButton class]]) {
832
+ NSButton *button = (NSButton *)subview;
833
+ if ([button.title isEqualToString:@"Start Record"]) {
834
+ if (isActiveScreen) {
835
+ // Active screen: bright, prominent button
836
+ [button.layer setBackgroundColor:[[NSColor colorWithRed:0.7 green:0.45 blue:0.9 alpha:1.0] CGColor]];
837
+ [button setAlphaValue:1.0];
838
+ } else {
839
+ // Inactive screen: dimmer button
840
+ [button.layer setBackgroundColor:[[NSColor colorWithRed:0.4 green:0.25 blue:0.55 alpha:0.8] CGColor]];
841
+ [button setAlphaValue:0.7];
842
+ }
843
+ }
844
+ }
845
+ if ([subview isKindOfClass:[NSTextField class]]) {
846
+ NSTextField *label = (NSTextField *)subview;
847
+ if (isActiveScreen) {
848
+ [label setTextColor:[NSColor whiteColor]];
849
+ [label setAlphaValue:1.0];
850
+ } else {
851
+ [label setTextColor:[NSColor colorWithWhite:0.8 alpha:0.8]];
852
+ [label setAlphaValue:0.7];
853
+ }
854
+ }
855
+ if ([subview isKindOfClass:[NSImageView class]]) {
856
+ NSImageView *imageView = (NSImageView *)subview;
857
+ if (isActiveScreen) {
858
+ [imageView setAlphaValue:1.0];
859
+ } else {
860
+ [imageView setAlphaValue:0.6];
861
+ }
862
+ }
863
+ }
864
+
865
+ // Log active screen changes for debugging (optional)
866
+ if (isActiveScreen) {
867
+ NSLog(@"šŸ–„ļø Active screen: Display %ld", (long)(i + 1));
868
+ }
869
+
870
+ // Ensure ALL overlays are visible, but active one is on top
871
+ [overlayWindow orderFront:nil];
872
+ if (isActiveScreen) {
873
+ [overlayWindow makeKeyAndOrderFront:nil];
874
+ } else {
875
+ [overlayWindow orderFront:nil]; // Keep inactive screens visible too
876
+ }
877
+ }
878
+ }
879
+ }
880
+ }
881
+
769
882
  // Screen selection functions
770
883
  void cleanupScreenSelector() {
771
884
  g_isScreenSelecting = false;
772
885
 
886
+ // Stop screen tracking timer
887
+ if (g_screenTrackingTimer) {
888
+ [g_screenTrackingTimer invalidate];
889
+ g_screenTrackingTimer = nil;
890
+ }
891
+
773
892
  // Remove key event monitor
774
893
  if (g_screenKeyEventMonitor) {
775
894
  [NSEvent removeMonitor:g_screenKeyEventMonitor];
@@ -790,6 +909,9 @@ void cleanupScreenSelector() {
790
909
  [g_allScreens release];
791
910
  g_allScreens = nil;
792
911
  }
912
+
913
+ // Reset active screen tracking
914
+ g_currentActiveScreenIndex = -1;
793
915
  }
794
916
 
795
917
  bool startScreenSelection() {
@@ -993,9 +1115,14 @@ bool startScreenSelection() {
993
1115
  [overlayView addSubview:screenInfoLabel];
994
1116
  [overlayView addSubview:selectButton];
995
1117
  [overlayView addSubview:screenCancelButton];
1118
+
996
1119
  [overlayWindow orderFront:nil];
997
1120
  [overlayWindow makeKeyAndOrderFront:nil];
998
1121
 
1122
+ // Additional visibility settings
1123
+ [overlayWindow setAlphaValue:1.0];
1124
+ [overlayWindow setIsVisible:YES];
1125
+
999
1126
  [g_screenOverlayWindows addObject:overlayWindow];
1000
1127
  [screenInfo release];
1001
1128
  }
@@ -1003,6 +1130,7 @@ bool startScreenSelection() {
1003
1130
  g_allScreens = [screenInfoArray retain];
1004
1131
  [screenInfoArray release];
1005
1132
  g_isScreenSelecting = true;
1133
+ g_currentActiveScreenIndex = -1;
1006
1134
 
1007
1135
  // Add ESC key event monitor to cancel selection
1008
1136
  g_screenKeyEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown
@@ -1013,7 +1141,21 @@ bool startScreenSelection() {
1013
1141
  }
1014
1142
  }];
1015
1143
 
1144
+ // Start screen tracking timer to update overlays based on mouse position
1145
+ if (!g_delegate) {
1146
+ g_delegate = [[WindowSelectorDelegate alloc] init];
1147
+ }
1148
+ g_screenTrackingTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 // 20 FPS
1149
+ target:g_delegate
1150
+ selector:@selector(screenTimerUpdate:)
1151
+ userInfo:nil
1152
+ repeats:YES];
1153
+
1154
+ // Initial update to set correct highlighting
1155
+ updateScreenOverlays();
1156
+
1016
1157
  NSLog(@"šŸ–„ļø SCREEN SELECTION: Started with %lu screens (ESC to cancel)", (unsigned long)[screens count]);
1158
+ NSLog(@"šŸ–„ļø SCREEN TRACKING: Timer started for overlay updates");
1017
1159
 
1018
1160
  return true;
1019
1161
 
File without changes
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function testMultiDisplayOverlay() {
6
+ console.log('šŸ–„ļø Multi-Display Overlay Test');
7
+ console.log('=============================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ // Check permissions first
13
+ const permissions = await selector.checkPermissions();
14
+ console.log('šŸ” Permissions:');
15
+ console.log(` Screen Recording: ${permissions.screenRecording ? 'āœ…' : 'āŒ'}`);
16
+ console.log(` Accessibility: ${permissions.accessibility ? 'āœ…' : 'āŒ'}\n`);
17
+
18
+ // Get display information
19
+ const MacRecorder = require('./index');
20
+ const recorder = new MacRecorder();
21
+ const displays = await recorder.getDisplays();
22
+
23
+ console.log(`šŸ–„ļø Found ${displays.length} display(s):`);
24
+ displays.forEach((display, index) => {
25
+ console.log(` Display ${index + 1}: ${display.name} (${display.resolution}) at (${display.x}, ${display.y}) ${display.isPrimary ? '🌟 PRIMARY' : ''}`);
26
+ });
27
+ console.log('');
28
+
29
+ if (displays.length < 2) {
30
+ console.log('āš ļø Only one display detected. Connect a second display to fully test multi-display overlay functionality.');
31
+ console.log('Continuing with single display test...\n');
32
+ }
33
+
34
+ let windowDetectionCount = 0;
35
+ let displaySwitchCount = 0;
36
+ let lastDisplayId = null;
37
+
38
+ // Event listeners for detailed tracking
39
+ selector.on('windowEntered', (window) => {
40
+ windowDetectionCount++;
41
+
42
+ // Try to determine which display this window is on
43
+ let windowDisplayId = null;
44
+ const windowCenterX = window.x + window.width / 2;
45
+ const windowCenterY = window.y + window.height / 2;
46
+
47
+ for (const display of displays) {
48
+ if (windowCenterX >= display.x &&
49
+ windowCenterX < display.x + display.width &&
50
+ windowCenterY >= display.y &&
51
+ windowCenterY < display.y + display.height) {
52
+ windowDisplayId = display.id;
53
+ break;
54
+ }
55
+ }
56
+
57
+ if (windowDisplayId !== lastDisplayId) {
58
+ displaySwitchCount++;
59
+ lastDisplayId = windowDisplayId;
60
+ }
61
+
62
+ const displayName = displays.find(d => d.id === windowDisplayId)?.name || 'Unknown';
63
+
64
+ console.log(`\nšŸŽÆ WINDOW DETECTED #${windowDetectionCount}:`);
65
+ console.log(` App: ${window.appName}`);
66
+ console.log(` Title: "${window.title}"`);
67
+ console.log(` Position: (${window.x}, ${window.y})`);
68
+ console.log(` Size: ${window.width} Ɨ ${window.height}`);
69
+ console.log(` šŸ–„ļø Estimated Display: ${displayName} (ID: ${windowDisplayId})`);
70
+ console.log(` Display switches so far: ${displaySwitchCount}`);
71
+
72
+ if (displays.length > 1) {
73
+ console.log(`\nšŸ’” Multi-Display Test Instructions:`);
74
+ console.log(` • Move cursor to windows on different displays`);
75
+ console.log(` • Overlay should follow cursor across displays`);
76
+ console.log(` • Window detection should work on all displays`);
77
+ }
78
+ });
79
+
80
+ selector.on('windowLeft', (window) => {
81
+ console.log(`\n🚪 LEFT WINDOW: ${window.appName} - "${window.title}"`);
82
+ });
83
+
84
+ selector.on('windowSelected', (selectedWindow) => {
85
+ console.log('\n' + 'šŸŽ‰'.repeat(20));
86
+ console.log('šŸŽÆ WINDOW SELECTED!');
87
+ console.log('šŸŽ‰'.repeat(20));
88
+
89
+ const selectedDisplayName = displays.find(d =>
90
+ selectedWindow.x >= d.x &&
91
+ selectedWindow.x < d.x + d.width &&
92
+ selectedWindow.y >= d.y &&
93
+ selectedWindow.y < d.y + d.height
94
+ )?.name || 'Unknown';
95
+
96
+ console.log(`\nšŸ“Š Selection Results:`);
97
+ console.log(` Selected: ${selectedWindow.appName} - "${selectedWindow.title}"`);
98
+ console.log(` Position: (${selectedWindow.x}, ${selectedWindow.y})`);
99
+ console.log(` Size: ${selectedWindow.width} Ɨ ${selectedWindow.height}`);
100
+ console.log(` Display: ${selectedDisplayName}`);
101
+ console.log(`\nšŸ“ˆ Test Statistics:`);
102
+ console.log(` Total window detections: ${windowDetectionCount}`);
103
+ console.log(` Display switches: ${displaySwitchCount}`);
104
+ console.log(` Multi-display support: ${displaySwitchCount > 0 || displays.length === 1 ? 'āœ…' : 'āŒ'}`);
105
+
106
+ process.exit(0);
107
+ });
108
+
109
+ // Interactive selection with instructions
110
+ console.log('šŸš€ Starting multi-display overlay test...\n');
111
+ console.log('šŸ“‹ Test Instructions:');
112
+ console.log(' 1. Move your cursor to windows on different displays');
113
+ console.log(' 2. Watch for overlay following cursor across displays');
114
+ console.log(' 3. Verify window detection works on all displays');
115
+ console.log(' 4. Press ENTER when you want to select a window');
116
+ console.log(' 5. Press ESC or Ctrl+C to cancel\n');
117
+
118
+ if (displays.length > 1) {
119
+ console.log('šŸ–„ļø Multi-Display Tips:');
120
+ console.log(' • Try windows on both primary and secondary displays');
121
+ console.log(' • Move cursor quickly between displays to test overlay tracking');
122
+ console.log(' • Overlay should appear on the display where the cursor is\n');
123
+ }
124
+
125
+ // Setup manual selection with ENTER key
126
+ const readline = require('readline');
127
+ const rl = readline.createInterface({
128
+ input: process.stdin,
129
+ output: process.stdout
130
+ });
131
+
132
+ rl.on('line', () => {
133
+ // Get current status to see if there's a window under cursor
134
+ const status = selector.getStatus();
135
+ if (status.nativeStatus && status.nativeStatus.currentWindow) {
136
+ selector.emit('windowSelected', status.nativeStatus.currentWindow);
137
+ } else {
138
+ console.log('\nāš ļø No window under cursor. Move cursor over a window first.');
139
+ }
140
+ });
141
+
142
+ await selector.startSelection();
143
+
144
+ // Status monitoring
145
+ let statusCount = 0;
146
+ const statusInterval = setInterval(() => {
147
+ statusCount++;
148
+
149
+ if (statusCount % 40 === 0) { // Every 20 seconds
150
+ console.log(`\nā±ļø Status Update (${statusCount/2}s):`);
151
+ console.log(` Window detections: ${windowDetectionCount}`);
152
+ console.log(` Display switches: ${displaySwitchCount}`);
153
+ console.log(` Current displays: ${displays.length}`);
154
+ if (displays.length > 1) {
155
+ console.log(` Multi-display overlay: ${displaySwitchCount > 0 ? 'āœ… Working' : 'ā³ Waiting for cross-display movement'}`);
156
+ }
157
+ }
158
+ }, 500);
159
+
160
+ // Graceful shutdown
161
+ process.on('SIGINT', async () => {
162
+ clearInterval(statusInterval);
163
+ rl.close();
164
+
165
+ console.log('\n\nšŸ›‘ Test stopped by user');
166
+ console.log('\nšŸ“Š Final Test Results:');
167
+ console.log(` Total window detections: ${windowDetectionCount}`);
168
+ console.log(` Display switches observed: ${displaySwitchCount}`);
169
+ console.log(` Displays available: ${displays.length}`);
170
+ console.log(` Multi-display support: ${displaySwitchCount > 0 || displays.length === 1 ? 'āœ… WORKING' : 'āŒ NEEDS INVESTIGATION'}`);
171
+
172
+ await selector.cleanup();
173
+ console.log('āœ… Cleanup completed');
174
+ process.exit(0);
175
+ });
176
+
177
+ } catch (error) {
178
+ console.error('\nāŒ Test failed:', error.message);
179
+ process.exit(1);
180
+ }
181
+ }
182
+
183
+ if (require.main === module) {
184
+ testMultiDisplayOverlay();
185
+ }
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function testMultiScreenSelection() {
6
+ console.log('šŸ–„ļø Multi-Screen Selection Test');
7
+ console.log('==============================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ // Check permissions first
13
+ const permissions = await selector.checkPermissions();
14
+ console.log('šŸ” Permissions:');
15
+ console.log(` Screen Recording: ${permissions.screenRecording ? 'āœ…' : 'āŒ'}`);
16
+ console.log(` Accessibility: ${permissions.accessibility ? 'āœ…' : 'āŒ'}\n`);
17
+
18
+ if (!permissions.screenRecording || !permissions.accessibility) {
19
+ console.log('āš ļø Missing required permissions. Please grant permissions in System Preferences.');
20
+ process.exit(1);
21
+ }
22
+
23
+ // Get display information
24
+ const MacRecorder = require('./index');
25
+ const recorder = new MacRecorder();
26
+ const displays = await recorder.getDisplays();
27
+
28
+ console.log(`šŸ–„ļø Found ${displays.length} display(s):`);
29
+ displays.forEach((display, index) => {
30
+ console.log(` Display ${index + 1}: ${display.name} (${display.resolution}) at (${display.x}, ${display.y}) ${display.isPrimary ? '🌟 PRIMARY' : ''}`);
31
+ });
32
+ console.log('');
33
+
34
+ if (displays.length < 2) {
35
+ console.log('āš ļø Only one display detected. Connect a second display to fully test multi-screen selection functionality.');
36
+ console.log('Continuing with single display test...\n');
37
+ } else {
38
+ console.log('āœ… Multiple displays detected. Perfect for multi-screen testing!\n');
39
+ }
40
+
41
+ console.log('šŸš€ Starting multi-screen selection test...\n');
42
+ console.log('šŸ“‹ What to expect:');
43
+ console.log(' 1. Overlay will appear on ALL screens simultaneously');
44
+ console.log(' 2. The screen where your mouse is located will be HIGHLIGHTED (brighter)');
45
+ console.log(' 3. Other screens will be dimmer');
46
+ console.log(' 4. Move mouse between screens to see the highlighting change');
47
+ console.log(' 5. Click "Start Record" on the screen you want to record');
48
+ console.log(' 6. Press ESC to cancel\n');
49
+
50
+ if (displays.length > 1) {
51
+ console.log('šŸ–±ļø Multi-Screen Instructions:');
52
+ console.log(' • Move your mouse from one screen to another');
53
+ console.log(' • Watch the overlay highlighting follow your mouse');
54
+ console.log(' • Notice how the active screen becomes brighter');
55
+ console.log(' • All screens should show the overlay simultaneously\n');
56
+ }
57
+
58
+ console.log('ā±ļø Starting screen selection in 3 seconds...');
59
+ await new Promise(resolve => setTimeout(resolve, 3000));
60
+
61
+ // Start screen selection
62
+ const success = await selector.startScreenSelection();
63
+
64
+ if (!success) {
65
+ console.error('āŒ Failed to start screen selection');
66
+ process.exit(1);
67
+ }
68
+
69
+ console.log('āœ… Screen selection started successfully!');
70
+ console.log('šŸ–±ļø Move your mouse between screens to test highlighting...\n');
71
+
72
+ // Monitor for selection completion
73
+ let checkCount = 0;
74
+ const selectionChecker = setInterval(() => {
75
+ checkCount++;
76
+ const selectedScreen = selector.getSelectedScreen();
77
+
78
+ if (selectedScreen) {
79
+ clearInterval(selectionChecker);
80
+
81
+ console.log('\n' + 'šŸŽ‰'.repeat(25));
82
+ console.log('šŸŽÆ SCREEN SELECTED!');
83
+ console.log('šŸŽ‰'.repeat(25));
84
+
85
+ console.log(`\nšŸ“Š Selected Screen Details:`);
86
+ console.log(` Name: ${selectedScreen.name}`);
87
+ console.log(` Resolution: ${selectedScreen.resolution}`);
88
+ console.log(` Position: (${selectedScreen.x}, ${selectedScreen.y})`);
89
+ console.log(` Size: ${selectedScreen.width} Ɨ ${selectedScreen.height}`);
90
+ console.log(` Primary: ${selectedScreen.isPrimary ? 'Yes' : 'No'}`);
91
+
92
+ console.log(`\nāœ… Multi-screen selection test completed successfully!`);
93
+ console.log(`šŸ“ˆ Test ran for ${(checkCount * 100) / 1000} seconds`);
94
+
95
+ process.exit(0);
96
+ }
97
+
98
+ // Show progress every 5 seconds
99
+ if (checkCount % 50 === 0) {
100
+ const elapsed = (checkCount * 100) / 1000;
101
+ console.log(`ā±ļø Test running for ${elapsed}s - Move mouse between screens to test highlighting`);
102
+
103
+ if (displays.length > 1) {
104
+ console.log('šŸ’” Remember: The overlay should appear on ALL screens, but only the one with your mouse should be bright');
105
+ }
106
+ }
107
+ }, 100); // Check every 100ms
108
+
109
+ // Timeout after 60 seconds
110
+ setTimeout(() => {
111
+ clearInterval(selectionChecker);
112
+ console.log('\nā° Test timeout reached (60 seconds)');
113
+ console.log('šŸ›‘ Stopping screen selection...');
114
+
115
+ selector.stopScreenSelection().then(() => {
116
+ console.log('āœ… Screen selection stopped');
117
+
118
+ if (displays.length > 1) {
119
+ console.log('\nšŸ“Š Multi-Screen Test Summary:');
120
+ console.log(' Expected behavior:');
121
+ console.log(' āœ“ Overlays should have appeared on all screens');
122
+ console.log(' āœ“ Mouse screen should have been highlighted brighter');
123
+ console.log(' āœ“ Non-mouse screens should have been dimmer');
124
+ console.log(' āœ“ Highlighting should have changed as you moved mouse');
125
+ } else {
126
+ console.log('\nšŸ“Š Single-Screen Test Summary:');
127
+ console.log(' āœ“ Overlay should have appeared on your screen');
128
+ console.log(' āœ“ Screen should have been highlighted');
129
+ }
130
+
131
+ process.exit(0);
132
+ }).catch((error) => {
133
+ console.error('āŒ Error stopping screen selection:', error.message);
134
+ process.exit(1);
135
+ });
136
+ }, 60000);
137
+
138
+ // Handle Ctrl+C gracefully
139
+ process.on('SIGINT', async () => {
140
+ clearInterval(selectionChecker);
141
+ console.log('\n\nšŸ›‘ Test interrupted by user');
142
+
143
+ try {
144
+ await selector.stopScreenSelection();
145
+ console.log('āœ… Screen selection stopped');
146
+ } catch (error) {
147
+ console.log('āš ļø Error during cleanup:', error.message);
148
+ }
149
+
150
+ console.log('\nšŸ“Š Test Results Summary:');
151
+ console.log(` Displays available: ${displays.length}`);
152
+ console.log(` Test duration: ${(checkCount * 100) / 1000} seconds`);
153
+ if (displays.length > 1) {
154
+ console.log(' Multi-screen support: Test interrupted before completion');
155
+ }
156
+
157
+ process.exit(0);
158
+ });
159
+
160
+ } catch (error) {
161
+ console.error('\nāŒ Test failed:', error.message);
162
+ if (error.stack) {
163
+ console.error('Stack trace:', error.stack);
164
+ }
165
+ process.exit(1);
166
+ }
167
+ }
168
+
169
+ if (require.main === module) {
170
+ testMultiScreenSelection();
171
+ }