node-mac-recorder 1.2.11 → 1.3.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.
@@ -10,7 +10,10 @@
10
10
  "Bash(brew install:*)",
11
11
  "Bash(open:*)",
12
12
  "Bash(curl:*)",
13
- "Bash(system_profiler:*)"
13
+ "Bash(system_profiler:*)",
14
+ "Bash(mkdir:*)",
15
+ "Bash(/dev/null)",
16
+ "Bash(ls:*)"
14
17
  ],
15
18
  "deny": []
16
19
  }
@@ -0,0 +1,396 @@
1
+ # Window Selector
2
+
3
+ **macOS Window Selection Tool with Real-time Visual Overlay**
4
+
5
+ Bu modül, macOS'ta sistem imleci ile pencere seçimi yapabilmenizi sağlayan güçlü bir araçtır. İmleç hangi pencerenin üstüne gelirse, o pencereyi mavi kapsayıcı ile highlight eder ve merkeze yerleştirilen "Select Window" butonu ile seçim yapabilirsiniz.
6
+
7
+ ## ✨ Özellikler
8
+
9
+ - **Real-time Window Detection**: İmleç hangi pencereye gelirse otomatik olarak tespit eder
10
+ - **Visual Overlay**: Seçilebilir pencereleri mavi transparant kapsayıcı ile highlight eder
11
+ - **Interactive Selection**: Merkeze yerleştirilen "Select Window" butonu ile kolay seçim
12
+ - **Multi-display Support**: Çoklu ekran kurulumlarında çalışır
13
+ - **Detailed Window Info**: Pencere pozisyonu, boyutu ve hangi ekranda olduğunu döndürür
14
+ - **Event-driven API**: Pencere hover, seçim ve hata durumları için event'ler
15
+ - **Permission Management**: macOS izin kontrolü ve yönetimi
16
+
17
+ ## 🚀 Kurulum
18
+
19
+ ```bash
20
+ # Ana proje dizininde
21
+ npm install
22
+
23
+ # Native modülü build edin
24
+ npm run build
25
+ ```
26
+
27
+ ## 📋 Sistem Gereksinimleri
28
+
29
+ - **macOS 10.15+** (Catalina veya üzeri)
30
+ - **Node.js 14+**
31
+ - **Xcode Command Line Tools**
32
+ - **System Permissions**:
33
+ - Screen Recording permission
34
+ - Accessibility permission
35
+
36
+ ## 🔐 İzinler
37
+
38
+ İlk kullanımda macOS aşağıdaki izinleri isteyecektir:
39
+
40
+ 1. **System Preferences > Security & Privacy > Privacy > Screen Recording**
41
+ - Terminal veya kullandığınız IDE'yi (VSCode, WebStorm, vb.) etkinleştirin
42
+
43
+ 2. **System Preferences > Security & Privacy > Privacy > Accessibility**
44
+ - Terminal veya kullandığınız IDE'yi etkinleştirin
45
+
46
+ ## 🎯 Temel Kullanım
47
+
48
+ ### Basit Pencere Seçimi
49
+
50
+ ```javascript
51
+ const WindowSelector = require('./window-selector');
52
+
53
+ async function selectWindow() {
54
+ const selector = new WindowSelector();
55
+
56
+ try {
57
+ console.log('Bir pencere seçin...');
58
+ const selectedWindow = await selector.selectWindow();
59
+
60
+ console.log('Seçilen pencere:', {
61
+ title: selectedWindow.title,
62
+ app: selectedWindow.appName,
63
+ position: `(${selectedWindow.x}, ${selectedWindow.y})`,
64
+ size: `${selectedWindow.width}x${selectedWindow.height}`,
65
+ screen: selectedWindow.screenId
66
+ });
67
+
68
+ return selectedWindow;
69
+
70
+ } catch (error) {
71
+ console.error('Hata:', error.message);
72
+ } finally {
73
+ await selector.cleanup();
74
+ }
75
+ }
76
+
77
+ selectWindow();
78
+ ```
79
+
80
+ ### Manuel Kontrol
81
+
82
+ ```javascript
83
+ const WindowSelector = require('./window-selector');
84
+
85
+ async function manualSelection() {
86
+ const selector = new WindowSelector();
87
+
88
+ // Event listener'lar
89
+ selector.on('windowEntered', (window) => {
90
+ console.log(`Pencere üstünde: ${window.title} (${window.appName})`);
91
+ });
92
+
93
+ selector.on('windowSelected', (window) => {
94
+ console.log(`Seçildi: ${window.title}`);
95
+ });
96
+
97
+ // Seçimi başlat
98
+ await selector.startSelection();
99
+
100
+ // Kullanıcı seçim yapana kadar bekle
101
+ // Seçim tamamlandığında 'windowSelected' event'i tetiklenir
102
+
103
+ // Seçimi durdurmak için:
104
+ // await selector.stopSelection();
105
+ }
106
+ ```
107
+
108
+ ## 📚 API Reference
109
+
110
+ ### WindowSelector Class
111
+
112
+ #### Constructor
113
+ ```javascript
114
+ const selector = new WindowSelector();
115
+ ```
116
+
117
+ #### Methods
118
+
119
+ ##### `async selectWindow()`
120
+ Promise tabanlı pencere seçimi. Kullanıcı bir pencere seçene kadar bekler.
121
+
122
+ **Returns:** `Promise<WindowInfo>`
123
+
124
+ ```javascript
125
+ const window = await selector.selectWindow();
126
+ ```
127
+
128
+ ##### `async startSelection()`
129
+ Pencere seçim modunu başlatır.
130
+
131
+ **Returns:** `Promise<boolean>`
132
+
133
+ ##### `async stopSelection()`
134
+ Pencere seçim modunu durdurur.
135
+
136
+ **Returns:** `Promise<boolean>`
137
+
138
+ ##### `getSelectedWindow()`
139
+ Son seçilen pencere bilgisini döndürür.
140
+
141
+ **Returns:** `WindowInfo | null`
142
+
143
+ ##### `getStatus()`
144
+ Seçici durumunu döndürür.
145
+
146
+ **Returns:** `SelectionStatus`
147
+
148
+ ##### `async checkPermissions()`
149
+ macOS izinlerini kontrol eder.
150
+
151
+ **Returns:** `Promise<PermissionStatus>`
152
+
153
+ ##### `async cleanup()`
154
+ Tüm kaynakları temizler ve seçimi durdurur.
155
+
156
+ #### Events
157
+
158
+ ##### `selectionStarted`
159
+ Seçim modu başladığında tetiklenir.
160
+
161
+ ```javascript
162
+ selector.on('selectionStarted', () => {
163
+ console.log('Seçim başladı');
164
+ });
165
+ ```
166
+
167
+ ##### `windowEntered`
168
+ İmleç bir pencereye geldiğinde tetiklenir.
169
+
170
+ ```javascript
171
+ selector.on('windowEntered', (windowInfo) => {
172
+ console.log(`Pencere: ${windowInfo.title}`);
173
+ });
174
+ ```
175
+
176
+ ##### `windowLeft`
177
+ İmleç bir pencereden ayrıldığında tetiklenir.
178
+
179
+ ```javascript
180
+ selector.on('windowLeft', (windowInfo) => {
181
+ console.log(`Ayrıldı: ${windowInfo.title}`);
182
+ });
183
+ ```
184
+
185
+ ##### `windowSelected`
186
+ Bir pencere seçildiğinde tetiklenir.
187
+
188
+ ```javascript
189
+ selector.on('windowSelected', (windowInfo) => {
190
+ console.log('Seçilen pencere:', windowInfo);
191
+ });
192
+ ```
193
+
194
+ ##### `selectionStopped`
195
+ Seçim modu durduğunda tetiklenir.
196
+
197
+ ##### `error`
198
+ Bir hata oluştuğunda tetiklenir.
199
+
200
+ ```javascript
201
+ selector.on('error', (error) => {
202
+ console.error('Hata:', error.message);
203
+ });
204
+ ```
205
+
206
+ ## 📊 Data Types
207
+
208
+ ### WindowInfo
209
+ ```javascript
210
+ {
211
+ id: number, // Pencere ID'si
212
+ title: string, // Pencere başlığı
213
+ appName: string, // Uygulama adı
214
+ x: number, // Global X pozisyonu
215
+ y: number, // Global Y pozisyonu
216
+ width: number, // Pencere genişliği
217
+ height: number, // Pencere yüksekliği
218
+ screenId: number, // Hangi ekranda olduğu
219
+ screenX: number, // Ekranın X pozisyonu
220
+ screenY: number, // Ekranın Y pozisyonu
221
+ screenWidth: number, // Ekran genişliği
222
+ screenHeight: number // Ekran yüksekliği
223
+ }
224
+ ```
225
+
226
+ ### SelectionStatus
227
+ ```javascript
228
+ {
229
+ isSelecting: boolean, // Seçim modunda mı?
230
+ hasSelectedWindow: boolean, // Seçilmiş pencere var mı?
231
+ selectedWindow: WindowInfo | null,
232
+ nativeStatus: object // Native durum bilgisi
233
+ }
234
+ ```
235
+
236
+ ### PermissionStatus
237
+ ```javascript
238
+ {
239
+ screenRecording: boolean, // Ekran kaydı izni
240
+ accessibility: boolean, // Erişilebilirlik izni
241
+ microphone: boolean // Mikrofon izni
242
+ }
243
+ ```
244
+
245
+ ## 🎮 Test Etme
246
+
247
+ ### Test Dosyasını Çalıştır
248
+ ```bash
249
+ # Interaktif test
250
+ node window-selector-test.js
251
+
252
+ # API test modu
253
+ node window-selector-test.js --api-test
254
+ ```
255
+
256
+ ### Örnekleri Çalıştır
257
+ ```bash
258
+ # Basit örnek
259
+ node examples/window-selector-example.js
260
+
261
+ # Gelişmiş örnek (event'lerle)
262
+ node examples/window-selector-example.js --advanced
263
+
264
+ # Çoklu seçim
265
+ node examples/window-selector-example.js --multiple
266
+
267
+ # Detaylı analiz
268
+ node examples/window-selector-example.js --analysis
269
+
270
+ # Yardım
271
+ node examples/window-selector-example.js --help
272
+ ```
273
+
274
+ ## ⚡ Nasıl Çalışır?
275
+
276
+ 1. **Window Detection**: macOS `CGWindowListCopyWindowInfo` API'si ile açık pencereleri tespit eder
277
+ 2. **Cursor Tracking**: Real-time olarak imleç pozisyonunu takip eder
278
+ 3. **Overlay Rendering**: NSWindow ile transparant overlay penceresi oluşturur
279
+ 4. **Hit Testing**: İmlecin hangi pencere üstünde olduğunu hesaplar
280
+ 5. **Visual Feedback**: Pencereyi highlight eden mavi kapsayıcı çizer
281
+ 6. **User Interaction**: Merkeze yerleştirilen button ile seçim yapar
282
+ 7. **Data Collection**: Seçilen pencerenin tüm bilgilerini toplar
283
+
284
+ ## 🔧 Troubleshooting
285
+
286
+ ### Build Hataları
287
+ ```bash
288
+ # Xcode Command Line Tools'u yükle
289
+ xcode-select --install
290
+
291
+ # Node-gyp'i yeniden build et
292
+ npm run clean
293
+ npm run build
294
+ ```
295
+
296
+ ### İzin Hataları
297
+ 1. **System Preferences > Security & Privacy > Privacy** bölümüne git
298
+ 2. **Screen Recording** ve **Accessibility** sekmelerinde Terminal'i etkinleştir
299
+ 3. Uygulamayı yeniden başlat
300
+
301
+ ### Runtime Hataları
302
+ ```javascript
303
+ // İzinleri kontrol et
304
+ const permissions = await selector.checkPermissions();
305
+ if (!permissions.screenRecording) {
306
+ console.log('Screen recording permission required');
307
+ }
308
+ ```
309
+
310
+ ## 🌟 Gelişmiş Örnekler
311
+
312
+ ### Otomatik Pencere Kaydı
313
+ ```javascript
314
+ const WindowSelector = require('./window-selector');
315
+ const MacRecorder = require('./index');
316
+
317
+ async function recordSelectedWindow() {
318
+ const selector = new WindowSelector();
319
+ const recorder = new MacRecorder();
320
+
321
+ try {
322
+ // Pencere seç
323
+ const window = await selector.selectWindow();
324
+ console.log(`Recording: ${window.title}`);
325
+
326
+ // Seçilen pencereyi kaydet
327
+ const outputPath = `./recordings/${window.appName}-${Date.now()}.mov`;
328
+ await recorder.startRecording(outputPath, {
329
+ windowId: window.id,
330
+ captureCursor: true,
331
+ includeMicrophone: true
332
+ });
333
+
334
+ // 10 saniye kaydet
335
+ setTimeout(async () => {
336
+ await recorder.stopRecording();
337
+ console.log(`Recording saved: ${outputPath}`);
338
+ }, 10000);
339
+
340
+ } finally {
341
+ await selector.cleanup();
342
+ }
343
+ }
344
+ ```
345
+
346
+ ### Pencere Monitoring
347
+ ```javascript
348
+ const WindowSelector = require('./window-selector');
349
+
350
+ async function monitorWindowChanges() {
351
+ const selector = new WindowSelector();
352
+ const visitedWindows = new Set();
353
+
354
+ selector.on('windowEntered', (window) => {
355
+ const key = `${window.appName}-${window.title}`;
356
+ if (!visitedWindows.has(key)) {
357
+ visitedWindows.add(key);
358
+ console.log(`Yeni pencere keşfedildi: ${window.title} (${window.appName})`);
359
+ }
360
+ });
361
+
362
+ await selector.startSelection();
363
+
364
+ // İptal etmek için Ctrl+C
365
+ process.on('SIGINT', async () => {
366
+ console.log(`\nToplam keşfedilen pencere: ${visitedWindows.size}`);
367
+ await selector.cleanup();
368
+ process.exit(0);
369
+ });
370
+ }
371
+ ```
372
+
373
+ ## 📄 Lisans
374
+
375
+ Bu modül ana projenin lisansı altındadır.
376
+
377
+ ## 🤝 Katkıda Bulunma
378
+
379
+ 1. Fork edin
380
+ 2. Feature branch oluşturun (`git checkout -b feature/amazing-feature`)
381
+ 3. Commit edin (`git commit -m 'Add amazing feature'`)
382
+ 4. Push edin (`git push origin feature/amazing-feature`)
383
+ 5. Pull Request açın
384
+
385
+ ## ⭐ Özellik İstekleri
386
+
387
+ - [ ] Pencere gruplandırma
388
+ - [ ] Hotkey desteği
389
+ - [ ] Pencere filtreleme
390
+ - [ ] Çoklu seçim modu
391
+ - [ ] Screenshot alma
392
+ - [ ] Window history
393
+
394
+ ---
395
+
396
+ **Not**: Bu modül sadece macOS'ta çalışır ve sistem izinleri gerektirir.
package/binding.gyp CHANGED
@@ -6,7 +6,8 @@
6
6
  "src/mac_recorder.mm",
7
7
  "src/screen_capture.mm",
8
8
  "src/audio_capture.mm",
9
- "src/cursor_tracker.mm"
9
+ "src/cursor_tracker.mm",
10
+ "src/window_selector.mm"
10
11
  ],
11
12
  "include_dirs": [
12
13
  "<!@(node -p \"require('node-addon-api').include\")"
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function debugWindowSelector() {
6
+ console.log('🔍 Window Selector Debug Mode');
7
+ console.log('=============================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ // 1. İzin kontrolü
13
+ console.log('1️⃣ Checking permissions...');
14
+ const permissions = await selector.checkPermissions();
15
+ console.log('Permissions:', JSON.stringify(permissions, null, 2));
16
+
17
+ if (!permissions.screenRecording || !permissions.accessibility) {
18
+ console.warn('⚠️ Missing permissions detected!');
19
+ console.warn('Go to: System Preferences > Security & Privacy > Privacy');
20
+ console.warn('Enable for Terminal/Node in both:');
21
+ console.warn('- Screen Recording');
22
+ console.warn('- Accessibility');
23
+ console.log();
24
+ }
25
+
26
+ // 2. Native status before start
27
+ console.log('\n2️⃣ Native status before start:');
28
+ const initialStatus = selector.getStatus();
29
+ console.log(JSON.stringify(initialStatus, null, 2));
30
+
31
+ // 3. Start selection with detailed logging
32
+ console.log('\n3️⃣ Starting window selection...');
33
+
34
+ selector.on('selectionStarted', () => {
35
+ console.log('✅ Selection started event received');
36
+ });
37
+
38
+ selector.on('windowEntered', (window) => {
39
+ console.log(`🏠 Window entered: "${window.title}" (${window.appName})`);
40
+ });
41
+
42
+ selector.on('windowLeft', (window) => {
43
+ console.log(`🚪 Window left: "${window.title}" (${window.appName})`);
44
+ });
45
+
46
+ selector.on('windowSelected', (window) => {
47
+ console.log('🎯 Window selected:', window.title);
48
+ });
49
+
50
+ selector.on('error', (error) => {
51
+ console.error('❌ Error event:', error);
52
+ });
53
+
54
+ await selector.startSelection();
55
+
56
+ // 4. Status after start
57
+ console.log('\n4️⃣ Status after start:');
58
+ const runningStatus = selector.getStatus();
59
+ console.log(JSON.stringify(runningStatus, null, 2));
60
+
61
+ // 5. Detailed native status monitoring
62
+ console.log('\n5️⃣ Monitoring native status (10 seconds)...');
63
+ console.log('Move your cursor over different windows');
64
+ console.log('The overlay should appear over windows\n');
65
+
66
+ for (let i = 0; i < 20; i++) {
67
+ await new Promise(resolve => setTimeout(resolve, 500));
68
+ const status = selector.getStatus();
69
+
70
+ process.stdout.write(`\r[${i+1}/20] `);
71
+ process.stdout.write(`Selecting: ${status.nativeStatus?.isSelecting || false}, `);
72
+ process.stdout.write(`Overlay: ${status.nativeStatus?.hasOverlay || false}, `);
73
+ process.stdout.write(`Windows: ${status.nativeStatus?.windowCount || 0}`);
74
+
75
+ if (status.nativeStatus?.currentWindow) {
76
+ process.stdout.write(`, Current: ${status.nativeStatus.currentWindow.appName}`);
77
+ }
78
+ }
79
+
80
+ console.log('\n\n6️⃣ Final status:');
81
+ const finalStatus = selector.getStatus();
82
+ console.log(JSON.stringify(finalStatus, null, 2));
83
+
84
+ } catch (error) {
85
+ console.error('\n❌ Debug failed:', error.message);
86
+ console.error('Stack:', error.stack);
87
+ } finally {
88
+ console.log('\n🛑 Stopping selection...');
89
+ await selector.cleanup();
90
+ }
91
+ }
92
+
93
+ // Alternative: Test native functions directly
94
+ async function testNativeFunctions() {
95
+ console.log('🧪 Testing Native Functions Directly');
96
+ console.log('====================================\n');
97
+
98
+ try {
99
+ // Try to load native binding directly
100
+ let nativeBinding;
101
+ try {
102
+ nativeBinding = require("./build/Release/mac_recorder.node");
103
+ } catch (error) {
104
+ try {
105
+ nativeBinding = require("./build/Debug/mac_recorder.node");
106
+ } catch (debugError) {
107
+ console.error('❌ Cannot load native module');
108
+ console.error('Release error:', error.message);
109
+ console.error('Debug error:', debugError.message);
110
+ return;
111
+ }
112
+ }
113
+
114
+ console.log('✅ Native module loaded successfully');
115
+
116
+ // Test if window selector functions exist
117
+ console.log('\n🔍 Available native functions:');
118
+ const functions = [
119
+ 'startWindowSelection',
120
+ 'stopWindowSelection',
121
+ 'getSelectedWindowInfo',
122
+ 'getWindowSelectionStatus'
123
+ ];
124
+
125
+ for (const func of functions) {
126
+ if (typeof nativeBinding[func] === 'function') {
127
+ console.log(`✅ ${func} - available`);
128
+ } else {
129
+ console.log(`❌ ${func} - missing`);
130
+ }
131
+ }
132
+
133
+ // Test direct native call
134
+ console.log('\n🚀 Testing direct native startWindowSelection...');
135
+ try {
136
+ const result = nativeBinding.startWindowSelection();
137
+ console.log('Native start result:', result);
138
+
139
+ if (result) {
140
+ console.log('✅ Native selection started');
141
+
142
+ // Check status
143
+ const status = nativeBinding.getWindowSelectionStatus();
144
+ console.log('Native status:', JSON.stringify(status, null, 2));
145
+
146
+ // Wait a bit
147
+ console.log('\nWaiting 3 seconds for overlay to appear...');
148
+ await new Promise(resolve => setTimeout(resolve, 3000));
149
+
150
+ // Stop
151
+ const stopResult = nativeBinding.stopWindowSelection();
152
+ console.log('Native stop result:', stopResult);
153
+ } else {
154
+ console.log('❌ Native selection failed to start');
155
+ }
156
+ } catch (nativeError) {
157
+ console.error('❌ Native function error:', nativeError.message);
158
+ }
159
+
160
+ } catch (error) {
161
+ console.error('❌ Native test failed:', error.message);
162
+ }
163
+ }
164
+
165
+ // Main function
166
+ async function main() {
167
+ const args = process.argv.slice(2);
168
+
169
+ if (args.includes('--native')) {
170
+ await testNativeFunctions();
171
+ } else {
172
+ await debugWindowSelector();
173
+ }
174
+ }
175
+
176
+ if (require.main === module) {
177
+ main().catch(console.error);
178
+ }