node-mac-recorder 2.6.0 → 2.6.2

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/index.js CHANGED
@@ -37,7 +37,7 @@ class MacRecorder extends EventEmitter {
37
37
 
38
38
  this.options = {
39
39
  includeMicrophone: false, // Default olarak mikrofon kapalı
40
- includeSystemAudio: true, // Default olarak sistem sesi açık
40
+ includeSystemAudio: false, // Default olarak sistem sesi kapalı - kullanıcı explicit olarak açmalı
41
41
  quality: "medium",
42
42
  frameRate: 30,
43
43
  captureArea: null, // { x, y, width, height }
@@ -110,8 +110,8 @@ class MacRecorder extends EventEmitter {
110
110
  */
111
111
  setOptions(options = {}) {
112
112
  this.options = {
113
- includeMicrophone: options.includeMicrophone || false,
114
- includeSystemAudio: options.includeSystemAudio !== false, // Default true unless explicitly disabled
113
+ includeMicrophone: options.includeMicrophone === true, // Explicit true required, default false
114
+ includeSystemAudio: options.includeSystemAudio === true, // Explicit true required, default false
115
115
  captureCursor: options.captureCursor || false,
116
116
  displayId: options.displayId || null, // null = ana ekran
117
117
  windowId: options.windowId || null, // null = tam ekran
@@ -121,6 +121,53 @@ class MacRecorder extends EventEmitter {
121
121
  };
122
122
  }
123
123
 
124
+ /**
125
+ * Mikrofon kaydını açar/kapatır
126
+ */
127
+ setMicrophoneEnabled(enabled) {
128
+ this.options.includeMicrophone = enabled === true;
129
+ return this.options.includeMicrophone;
130
+ }
131
+
132
+ /**
133
+ * Sistem sesi kaydını açar/kapatır
134
+ */
135
+ setSystemAudioEnabled(enabled) {
136
+ this.options.includeSystemAudio = enabled === true;
137
+ return this.options.includeSystemAudio;
138
+ }
139
+
140
+ /**
141
+ * Mikrofon durumunu döndürür
142
+ */
143
+ isMicrophoneEnabled() {
144
+ return this.options.includeMicrophone === true;
145
+ }
146
+
147
+ /**
148
+ * Sistem sesi durumunu döndürür
149
+ */
150
+ isSystemAudioEnabled() {
151
+ return this.options.includeSystemAudio === true;
152
+ }
153
+
154
+ /**
155
+ * Audio ayarlarını toplu olarak değiştirir
156
+ */
157
+ setAudioSettings(settings = {}) {
158
+ if (typeof settings.microphone === 'boolean') {
159
+ this.setMicrophoneEnabled(settings.microphone);
160
+ }
161
+ if (typeof settings.systemAudio === 'boolean') {
162
+ this.setSystemAudioEnabled(settings.systemAudio);
163
+ }
164
+
165
+ return {
166
+ microphone: this.isMicrophoneEnabled(),
167
+ systemAudio: this.isSystemAudioEnabled()
168
+ };
169
+ }
170
+
124
171
  /**
125
172
  * Ekran kaydını başlatır (macOS native AVFoundation kullanarak)
126
173
  */
@@ -266,8 +313,8 @@ class MacRecorder extends EventEmitter {
266
313
  try {
267
314
  // Native kayıt başlat
268
315
  const recordingOptions = {
269
- includeMicrophone: this.options.includeMicrophone || false,
270
- includeSystemAudio: this.options.includeSystemAudio !== false, // Default true unless explicitly disabled
316
+ includeMicrophone: this.options.includeMicrophone === true, // Only if explicitly enabled
317
+ includeSystemAudio: this.options.includeSystemAudio === true, // Only if explicitly enabled
271
318
  captureCursor: this.options.captureCursor || false,
272
319
  displayId: this.options.displayId || null, // null = ana ekran
273
320
  windowId: this.options.windowId || null, // null = tam ekran
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
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,106 @@ 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
+ if (isActiveScreen) {
866
+ NSLog(@"🖥️ ACTIVE SCREEN: Display %ld", (long)(i + 1));
867
+ }
868
+
869
+ // Bring active screen overlay to front
870
+ if (isActiveScreen) {
871
+ [overlayWindow orderFront:nil];
872
+ [overlayWindow makeKeyAndOrderFront:nil];
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+
769
879
  // Screen selection functions
770
880
  void cleanupScreenSelector() {
771
881
  g_isScreenSelecting = false;
772
882
 
883
+ // Stop screen tracking timer
884
+ if (g_screenTrackingTimer) {
885
+ [g_screenTrackingTimer invalidate];
886
+ g_screenTrackingTimer = nil;
887
+ }
888
+
773
889
  // Remove key event monitor
774
890
  if (g_screenKeyEventMonitor) {
775
891
  [NSEvent removeMonitor:g_screenKeyEventMonitor];
@@ -790,6 +906,9 @@ void cleanupScreenSelector() {
790
906
  [g_allScreens release];
791
907
  g_allScreens = nil;
792
908
  }
909
+
910
+ // Reset active screen tracking
911
+ g_currentActiveScreenIndex = -1;
793
912
  }
794
913
 
795
914
  bool startScreenSelection() {
@@ -1003,6 +1122,7 @@ bool startScreenSelection() {
1003
1122
  g_allScreens = [screenInfoArray retain];
1004
1123
  [screenInfoArray release];
1005
1124
  g_isScreenSelecting = true;
1125
+ g_currentActiveScreenIndex = -1;
1006
1126
 
1007
1127
  // Add ESC key event monitor to cancel selection
1008
1128
  g_screenKeyEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown
@@ -1013,7 +1133,18 @@ bool startScreenSelection() {
1013
1133
  }
1014
1134
  }];
1015
1135
 
1136
+ // Start screen tracking timer to update overlays based on mouse position
1137
+ if (!g_delegate) {
1138
+ g_delegate = [[WindowSelectorDelegate alloc] init];
1139
+ }
1140
+ g_screenTrackingTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 // 20 FPS
1141
+ target:g_delegate
1142
+ selector:@selector(screenTimerUpdate:)
1143
+ userInfo:nil
1144
+ repeats:YES];
1145
+
1016
1146
  NSLog(@"🖥️ SCREEN SELECTION: Started with %lu screens (ESC to cancel)", (unsigned long)[screens count]);
1147
+ NSLog(@"🖥️ SCREEN TRACKING: Timer started for overlay updates");
1017
1148
 
1018
1149
  return true;
1019
1150
 
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ const MacRecorder = require('./index');
4
+
5
+ async function testAudioControls() {
6
+ console.log('🎵 Audio Control Test');
7
+ console.log('====================\n');
8
+
9
+ const recorder = new MacRecorder();
10
+
11
+ try {
12
+ // Test initial state
13
+ console.log('📋 Initial Audio State:');
14
+ console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
15
+ console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
16
+
17
+ // Test individual controls
18
+ console.log('🔧 Testing Individual Controls:\n');
19
+
20
+ console.log(' Enabling microphone...');
21
+ recorder.setMicrophoneEnabled(true);
22
+ console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
23
+
24
+ console.log(' Enabling system audio...');
25
+ recorder.setSystemAudioEnabled(true);
26
+ console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
27
+
28
+ console.log(' Disabling microphone...');
29
+ recorder.setMicrophoneEnabled(false);
30
+ console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
31
+
32
+ console.log(' Disabling system audio...');
33
+ recorder.setSystemAudioEnabled(false);
34
+ console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
35
+
36
+ // Test bulk settings
37
+ console.log('🔧 Testing Bulk Audio Settings:\n');
38
+
39
+ console.log(' Setting both to ON...');
40
+ let settings = recorder.setAudioSettings({
41
+ microphone: true,
42
+ systemAudio: true
43
+ });
44
+ console.log(` Result: Microphone=${settings.microphone ? 'ON' : 'OFF'}, System Audio=${settings.systemAudio ? 'ON' : 'OFF'}\n`);
45
+
46
+ console.log(' Setting microphone OFF, system audio ON...');
47
+ settings = recorder.setAudioSettings({
48
+ microphone: false,
49
+ systemAudio: true
50
+ });
51
+ console.log(` Result: Microphone=${settings.microphone ? 'ON' : 'OFF'}, System Audio=${settings.systemAudio ? 'ON' : 'OFF'}\n`);
52
+
53
+ // Test recording with different audio settings
54
+ const testOutput = `./test-output/audio-test-${Date.now()}.mov`;
55
+
56
+ console.log('🎬 Testing Recording with Audio Settings:\n');
57
+
58
+ // Test 1: No audio
59
+ console.log(' Test 1: No Audio Recording');
60
+ recorder.setAudioSettings({ microphone: false, systemAudio: false });
61
+ console.log(` Starting 3-second recording: ${testOutput}`);
62
+ console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
63
+
64
+ await recorder.startRecording(testOutput);
65
+ await new Promise(resolve => setTimeout(resolve, 3000));
66
+ await recorder.stopRecording();
67
+ console.log(' ✅ Recording completed (no audio)\n');
68
+
69
+ // Test 2: Microphone only
70
+ console.log(' Test 2: Microphone Only Recording');
71
+ recorder.setAudioSettings({ microphone: true, systemAudio: false });
72
+ const testOutput2 = `./test-output/audio-test-mic-${Date.now()}.mov`;
73
+ console.log(` Starting 3-second recording: ${testOutput2}`);
74
+ console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
75
+
76
+ await recorder.startRecording(testOutput2);
77
+ await new Promise(resolve => setTimeout(resolve, 3000));
78
+ await recorder.stopRecording();
79
+ console.log(' ✅ Recording completed (microphone only)\n');
80
+
81
+ // Test 3: System audio only
82
+ console.log(' Test 3: System Audio Only Recording');
83
+ recorder.setAudioSettings({ microphone: false, systemAudio: true });
84
+ const testOutput3 = `./test-output/audio-test-system-${Date.now()}.mov`;
85
+ console.log(` Starting 3-second recording: ${testOutput3}`);
86
+ console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
87
+
88
+ await recorder.startRecording(testOutput3);
89
+ await new Promise(resolve => setTimeout(resolve, 3000));
90
+ await recorder.stopRecording();
91
+ console.log(' ✅ Recording completed (system audio only)\n');
92
+
93
+ console.log('✅ All audio control tests completed successfully!');
94
+ console.log('\n📁 Check test-output/ directory for video files.');
95
+
96
+ } catch (error) {
97
+ console.error('\n❌ Test failed:', error.message);
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ if (require.main === module) {
103
+ testAudioControls();
104
+ }
@@ -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
+ }