node-mac-recorder 1.2.11 → 1.4.0

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,160 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+ const readline = require('readline');
5
+
6
+ async function main() {
7
+ console.log('🔍 Window Selector Test');
8
+ console.log('=======================\n');
9
+
10
+ const selector = new WindowSelector();
11
+
12
+ try {
13
+ // Permissions kontrolü
14
+ console.log('1️⃣ Checking permissions...');
15
+ const permissions = await selector.checkPermissions();
16
+ console.log('Permissions:', permissions);
17
+
18
+ if (!permissions.screenRecording || !permissions.accessibility) {
19
+ console.warn('⚠️ Warning: Some permissions missing. Go to System Preferences > Security & Privacy');
20
+ console.warn(' - Privacy > Screen Recording: Enable for Terminal/Node');
21
+ console.warn(' - Privacy > Accessibility: Enable for Terminal/Node');
22
+ console.log();
23
+ }
24
+
25
+ // Event listener'ları ayarla
26
+ selector.on('selectionStarted', () => {
27
+ console.log('✅ Window selection started');
28
+ console.log('🖱️ Move your cursor over windows to highlight them');
29
+ console.log('🎯 Click "Select Window" button to choose a window');
30
+ console.log('❌ Press Ctrl+C to cancel\n');
31
+ });
32
+
33
+ selector.on('windowEntered', (window) => {
34
+ console.log(`🏠 Entered window: "${window.title}" - ${window.appName}`);
35
+ });
36
+
37
+ selector.on('windowLeft', (window) => {
38
+ console.log(`🚪 Left window: "${window.title}" - ${window.appName}`);
39
+ });
40
+
41
+ selector.on('windowSelected', (windowInfo) => {
42
+ console.log('\n🎉 Window Selected!');
43
+ console.log('==================');
44
+ console.log(`Title: "${windowInfo.title}"`);
45
+ console.log(`Application: ${windowInfo.appName}`);
46
+ console.log(`Position: (${windowInfo.x}, ${windowInfo.y})`);
47
+ console.log(`Size: ${windowInfo.width} x ${windowInfo.height}`);
48
+ console.log(`Screen ID: ${windowInfo.screenId}`);
49
+ console.log(`Screen Position: (${windowInfo.screenX}, ${windowInfo.screenY})`);
50
+ console.log(`Screen Size: ${windowInfo.screenWidth} x ${windowInfo.screenHeight}`);
51
+ });
52
+
53
+ selector.on('selectionStopped', () => {
54
+ console.log('🛑 Window selection stopped');
55
+ });
56
+
57
+ selector.on('error', (error) => {
58
+ console.error('❌ Error:', error.message);
59
+ });
60
+
61
+ // Ctrl+C handler
62
+ process.on('SIGINT', async () => {
63
+ console.log('\n\n🛑 Stopping window selection...');
64
+ await selector.cleanup();
65
+ process.exit(0);
66
+ });
67
+
68
+ console.log('2️⃣ Testing window selection...\n');
69
+
70
+ // Seçim başlat
71
+ const selectedWindow = await selector.selectWindow();
72
+
73
+ if (selectedWindow) {
74
+ console.log('\n✨ Final result:');
75
+ console.log(JSON.stringify(selectedWindow, null, 2));
76
+
77
+ // Ek bilgiler
78
+ console.log('\n📊 Additional Analysis:');
79
+ console.log(`Window area: ${selectedWindow.width * selectedWindow.height} pixels`);
80
+ console.log(`Aspect ratio: ${(selectedWindow.width / selectedWindow.height).toFixed(2)}`);
81
+
82
+ // Window bounds relative to screen
83
+ const relativeX = selectedWindow.x - selectedWindow.screenX;
84
+ const relativeY = selectedWindow.y - selectedWindow.screenY;
85
+ console.log(`Relative position on screen: (${relativeX}, ${relativeY})`);
86
+
87
+ if (relativeX < 0 || relativeY < 0 ||
88
+ relativeX + selectedWindow.width > selectedWindow.screenWidth ||
89
+ relativeY + selectedWindow.height > selectedWindow.screenHeight) {
90
+ console.log('⚠️ Window extends beyond screen boundaries (multi-monitor setup)');
91
+ } else {
92
+ console.log('✅ Window is fully contained within its screen');
93
+ }
94
+ }
95
+
96
+ } catch (error) {
97
+ console.error('❌ Test failed:', error.message);
98
+ console.error(error.stack);
99
+ } finally {
100
+ await selector.cleanup();
101
+ console.log('\n🧹 Cleanup completed');
102
+ process.exit(0);
103
+ }
104
+ }
105
+
106
+ // Alternative test function for programmatic testing
107
+ async function testWindowSelectorAPI() {
108
+ console.log('🧪 API Test Mode');
109
+ console.log('===============\n');
110
+
111
+ const selector = new WindowSelector();
112
+
113
+ try {
114
+ // Test 1: Status before selection
115
+ console.log('Test 1: Initial status');
116
+ const initialStatus = selector.getStatus();
117
+ console.log('Initial status:', initialStatus);
118
+ console.assert(!initialStatus.isSelecting, 'Should not be selecting initially');
119
+ console.assert(!initialStatus.hasSelectedWindow, 'Should not have selected window initially');
120
+ console.log('✅ Initial status test passed\n');
121
+
122
+ // Test 2: Start selection
123
+ console.log('Test 2: Start selection');
124
+ await selector.startSelection();
125
+ const selectingStatus = selector.getStatus();
126
+ console.log('Selecting status:', selectingStatus);
127
+ console.assert(selectingStatus.isSelecting, 'Should be selecting after start');
128
+ console.log('✅ Start selection test passed\n');
129
+
130
+ // Wait a bit to let user move cursor
131
+ console.log('Move your cursor over different windows for 3 seconds...');
132
+ await new Promise(resolve => setTimeout(resolve, 3000));
133
+
134
+ // Test 3: Stop selection
135
+ console.log('Test 3: Stop selection');
136
+ await selector.stopSelection();
137
+ const stoppedStatus = selector.getStatus();
138
+ console.log('Stopped status:', stoppedStatus);
139
+ console.assert(!stoppedStatus.isSelecting, 'Should not be selecting after stop');
140
+ console.log('✅ Stop selection test passed\n');
141
+
142
+ console.log('🎉 All API tests passed!');
143
+
144
+ } catch (error) {
145
+ console.error('❌ API test failed:', error.message);
146
+ throw error;
147
+ } finally {
148
+ await selector.cleanup();
149
+ }
150
+ }
151
+
152
+ // Parse command line arguments
153
+ const args = process.argv.slice(2);
154
+ const testMode = args.includes('--api-test') ? 'api' : 'interactive';
155
+
156
+ if (testMode === 'api') {
157
+ testWindowSelectorAPI().catch(console.error);
158
+ } else {
159
+ main().catch(console.error);
160
+ }
@@ -0,0 +1,291 @@
1
+ const { EventEmitter } = require("events");
2
+ const path = require("path");
3
+
4
+ // Native modülü yükle
5
+ let nativeBinding;
6
+ try {
7
+ nativeBinding = require("./build/Release/mac_recorder.node");
8
+ } catch (error) {
9
+ try {
10
+ nativeBinding = require("./build/Debug/mac_recorder.node");
11
+ } catch (debugError) {
12
+ throw new Error(
13
+ 'Native module not found. Please run "npm run build" to compile the native module.\n' +
14
+ "Original error: " +
15
+ error.message
16
+ );
17
+ }
18
+ }
19
+
20
+ class WindowSelector extends EventEmitter {
21
+ constructor() {
22
+ super();
23
+ this.isSelecting = false;
24
+ this.selectionTimer = null;
25
+ this.selectedWindow = null;
26
+ this.lastStatus = null;
27
+ }
28
+
29
+ /**
30
+ * Pencere seçim modunu başlatır
31
+ * İmleç hangi pencerenin üstüne gelirse o pencereyi highlight eder
32
+ * Select butonuna basılınca seçim tamamlanır
33
+ */
34
+ async startSelection() {
35
+ if (this.isSelecting) {
36
+ throw new Error("Window selection is already in progress");
37
+ }
38
+
39
+ return new Promise((resolve, reject) => {
40
+ try {
41
+ // Native window selection başlat
42
+ const success = nativeBinding.startWindowSelection();
43
+
44
+ if (success) {
45
+ this.isSelecting = true;
46
+ this.selectedWindow = null;
47
+
48
+ // Status polling timer başlat (higher frequency for overlay updates)
49
+ this.selectionTimer = setInterval(() => {
50
+ this.checkSelectionStatus();
51
+ }, 50); // 20 FPS status check for smooth overlay
52
+
53
+ this.emit("selectionStarted");
54
+ resolve(true);
55
+ } else {
56
+ reject(new Error("Failed to start window selection"));
57
+ }
58
+ } catch (error) {
59
+ reject(error);
60
+ }
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Pencere seçim modunu durdurur
66
+ */
67
+ async stopSelection() {
68
+ if (!this.isSelecting) {
69
+ return false;
70
+ }
71
+
72
+ return new Promise((resolve, reject) => {
73
+ try {
74
+ const success = nativeBinding.stopWindowSelection();
75
+
76
+ // Timer'ı durdur
77
+ if (this.selectionTimer) {
78
+ clearInterval(this.selectionTimer);
79
+ this.selectionTimer = null;
80
+ }
81
+
82
+ this.isSelecting = false;
83
+ this.lastStatus = null;
84
+
85
+ this.emit("selectionStopped");
86
+ resolve(success);
87
+ } catch (error) {
88
+ reject(error);
89
+ }
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Selection durumunu kontrol eder ve event yayar
95
+ */
96
+ checkSelectionStatus() {
97
+ if (!this.isSelecting) return;
98
+
99
+ try {
100
+ const status = nativeBinding.getWindowSelectionStatus();
101
+
102
+ // Seçim tamamlandı mı kontrol et
103
+ if (status.hasSelectedWindow && !this.selectedWindow) {
104
+ const windowInfo = nativeBinding.getSelectedWindowInfo();
105
+ if (windowInfo) {
106
+ this.selectedWindow = windowInfo;
107
+ this.isSelecting = false;
108
+
109
+ // Timer'ı durdur
110
+ if (this.selectionTimer) {
111
+ clearInterval(this.selectionTimer);
112
+ this.selectionTimer = null;
113
+ }
114
+
115
+ this.emit("windowSelected", windowInfo);
116
+ return;
117
+ }
118
+ }
119
+
120
+ // Mevcut pencere değişti mi kontrol et
121
+ if (this.lastStatus) {
122
+ const lastWindow = this.lastStatus.currentWindow;
123
+ const currentWindow = status.currentWindow;
124
+
125
+ if (!lastWindow && currentWindow) {
126
+ // Yeni pencere üstüne gelindi
127
+ this.emit("windowEntered", currentWindow);
128
+ } else if (lastWindow && !currentWindow) {
129
+ // Pencere üstünden ayrıldı
130
+ this.emit("windowLeft", lastWindow);
131
+ } else if (lastWindow && currentWindow &&
132
+ (lastWindow.id !== currentWindow.id ||
133
+ lastWindow.title !== currentWindow.title ||
134
+ lastWindow.appName !== currentWindow.appName)) {
135
+ // Farklı bir pencereye geçildi
136
+ this.emit("windowLeft", lastWindow);
137
+ this.emit("windowEntered", currentWindow);
138
+ }
139
+ } else if (!this.lastStatus && status.currentWindow) {
140
+ // İlk pencere detection
141
+ this.emit("windowEntered", status.currentWindow);
142
+ }
143
+
144
+ this.lastStatus = status;
145
+ } catch (error) {
146
+ this.emit("error", error);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Seçilen pencere bilgisini döndürür
152
+ */
153
+ getSelectedWindow() {
154
+ return this.selectedWindow;
155
+ }
156
+
157
+ /**
158
+ * Seçim durumunu döndürür
159
+ */
160
+ getStatus() {
161
+ try {
162
+ const nativeStatus = nativeBinding.getWindowSelectionStatus();
163
+ return {
164
+ isSelecting: this.isSelecting && nativeStatus.isSelecting,
165
+ hasSelectedWindow: !!this.selectedWindow,
166
+ selectedWindow: this.selectedWindow,
167
+ nativeStatus: nativeStatus
168
+ };
169
+ } catch (error) {
170
+ return {
171
+ isSelecting: this.isSelecting,
172
+ hasSelectedWindow: !!this.selectedWindow,
173
+ selectedWindow: this.selectedWindow,
174
+ error: error.message
175
+ };
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Promise tabanlı pencere seçimi
181
+ * Kullanıcı bir pencere seçene kadar bekler
182
+ */
183
+ async selectWindow() {
184
+ if (this.isSelecting) {
185
+ throw new Error("Selection already in progress");
186
+ }
187
+
188
+ return new Promise(async (resolve, reject) => {
189
+ try {
190
+ // Event listener'ları ayarla
191
+ const onWindowSelected = (windowInfo) => {
192
+ this.removeAllListeners("windowSelected");
193
+ this.removeAllListeners("error");
194
+ resolve(windowInfo);
195
+ };
196
+
197
+ const onError = (error) => {
198
+ this.removeAllListeners("windowSelected");
199
+ this.removeAllListeners("error");
200
+ reject(error);
201
+ };
202
+
203
+ this.once("windowSelected", onWindowSelected);
204
+ this.once("error", onError);
205
+
206
+ // Seçimi başlat
207
+ await this.startSelection();
208
+
209
+ } catch (error) {
210
+ this.removeAllListeners("windowSelected");
211
+ this.removeAllListeners("error");
212
+ reject(error);
213
+ }
214
+ });
215
+ }
216
+
217
+ /**
218
+ * Pencereyi en öne getirir (focus yapar)
219
+ * @param {number} windowId - Window ID
220
+ * @returns {Promise<boolean>} Success/failure
221
+ */
222
+ async bringWindowToFront(windowId) {
223
+ if (!windowId) {
224
+ throw new Error("Window ID is required");
225
+ }
226
+
227
+ try {
228
+ const success = nativeBinding.bringWindowToFront(windowId);
229
+ return success;
230
+ } catch (error) {
231
+ throw new Error(`Failed to bring window to front: ${error.message}`);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Otomatik pencere en öne getirme özelliğini aktif/pasif yapar
237
+ * Cursor hangi pencereye gelirse otomatik olarak en öne getirir
238
+ * @param {boolean} enabled - Enable/disable auto bring to front
239
+ */
240
+ setBringToFrontEnabled(enabled) {
241
+ try {
242
+ nativeBinding.setBringToFrontEnabled(enabled);
243
+ console.log(`🔄 Auto bring-to-front: ${enabled ? 'ENABLED' : 'DISABLED'}`);
244
+ } catch (error) {
245
+ throw new Error(`Failed to set bring to front: ${error.message}`);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Cleanup - tüm kaynakları temizle
251
+ */
252
+ async cleanup() {
253
+ if (this.isSelecting) {
254
+ await this.stopSelection();
255
+ }
256
+
257
+ // Timer'ı temizle
258
+ if (this.selectionTimer) {
259
+ clearInterval(this.selectionTimer);
260
+ this.selectionTimer = null;
261
+ }
262
+
263
+ // Event listener'ları temizle
264
+ this.removeAllListeners();
265
+
266
+ // State'i sıfırla
267
+ this.selectedWindow = null;
268
+ this.lastStatus = null;
269
+ this.isSelecting = false;
270
+ }
271
+
272
+ /**
273
+ * macOS'ta pencere seçim izinlerini kontrol eder
274
+ */
275
+ async checkPermissions() {
276
+ try {
277
+ // Mevcut MacRecorder'dan permission check'i kullan
278
+ const MacRecorder = require("./index.js");
279
+ const recorder = new MacRecorder();
280
+ return await recorder.checkPermissions();
281
+ } catch (error) {
282
+ return {
283
+ screenRecording: false,
284
+ accessibility: false,
285
+ error: error.message
286
+ };
287
+ }
288
+ }
289
+ }
290
+
291
+ module.exports = WindowSelector;
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function workingExample() {
6
+ console.log('🎯 Window Selector - Working Example');
7
+ console.log('====================================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ console.log('Starting window selection...');
13
+ console.log('Move cursor over different windows to see detection');
14
+ console.log('The system will detect which window is under cursor');
15
+ console.log('Press Ctrl+C to stop\n');
16
+
17
+ let currentWindow = null;
18
+
19
+ selector.on('windowEntered', (window) => {
20
+ currentWindow = window;
21
+ console.log(`\n🏠 ENTERED WINDOW:`);
22
+ console.log(` App: ${window.appName}`);
23
+ console.log(` Title: "${window.title}"`);
24
+ console.log(` Position: (${window.x}, ${window.y})`);
25
+ console.log(` Size: ${window.width} x ${window.height}`);
26
+ console.log(` 💡 This window is now highlighted (overlay may not be visible due to macOS security)`);
27
+ });
28
+
29
+ selector.on('windowLeft', (window) => {
30
+ console.log(`\n🚪 LEFT WINDOW: ${window.appName} - "${window.title}"`);
31
+ currentWindow = null;
32
+ });
33
+
34
+ selector.on('windowSelected', (selectedWindow) => {
35
+ console.log('\n🎉 WINDOW SELECTED!');
36
+ console.log('==================');
37
+ console.log(`App: ${selectedWindow.appName}`);
38
+ console.log(`Title: "${selectedWindow.title}"`);
39
+ console.log(`Position: (${selectedWindow.x}, ${selectedWindow.y})`);
40
+ console.log(`Size: ${selectedWindow.width} x ${selectedWindow.height}`);
41
+ console.log(`Screen: ${selectedWindow.screenId}`);
42
+ process.exit(0);
43
+ });
44
+
45
+ // Manual selection trigger
46
+ let readline = require('readline');
47
+ const rl = readline.createInterface({
48
+ input: process.stdin,
49
+ output: process.stdout
50
+ });
51
+
52
+ console.log('💡 Pro tip: Press ENTER to select the current window under cursor');
53
+ rl.on('line', () => {
54
+ if (currentWindow) {
55
+ console.log('\n✅ Manually selecting current window...');
56
+ selector.emit('windowSelected', currentWindow);
57
+ } else {
58
+ console.log('\n⚠️ No window under cursor. Move cursor over a window first.');
59
+ }
60
+ });
61
+
62
+ await selector.startSelection();
63
+
64
+ // Status monitoring
65
+ let statusCount = 0;
66
+ const statusInterval = setInterval(() => {
67
+ const status = selector.getStatus();
68
+ statusCount++;
69
+
70
+ if (statusCount % 20 === 0) { // Every 10 seconds
71
+ console.log(`\n📊 Status (${statusCount/2}s): Windows detected: ${status.nativeStatus?.windowCount || 0}`);
72
+ if (status.nativeStatus?.currentWindow) {
73
+ console.log(` Current: ${status.nativeStatus.currentWindow.appName}`);
74
+ }
75
+ }
76
+ }, 500);
77
+
78
+ process.on('SIGINT', async () => {
79
+ clearInterval(statusInterval);
80
+ rl.close();
81
+ console.log('\n🛑 Stopping window selector...');
82
+ await selector.cleanup();
83
+ console.log('✅ Cleanup completed');
84
+ process.exit(0);
85
+ });
86
+
87
+ } catch (error) {
88
+ console.error('\n❌ Error:', error.message);
89
+ console.error(error.stack);
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ workingExample();