node-mac-recorder 2.4.9 → 2.4.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,342 @@
1
+ # Electron Integration Guide
2
+
3
+ Bu döküman `node-mac-recorder` paketinin Electron uygulamaları ile güvenli entegrasyonunu açıklar.
4
+
5
+ ## 🔧 Sorun Çözümü
6
+
7
+ **Problem**: ScreenCaptureKit'e geçtikten sonra Electron uygulamalarında native NSWindow overlay'lar crash'e sebep oluyordu.
8
+
9
+ **Çözüm**: `ElectronWindowSelector` sınıfı otomatik olarak Electron ortamını tespit eder ve güvenli mod kullanır.
10
+
11
+ ## 📋 Electron Güvenli Mod Özellikleri
12
+
13
+ ### 🔍 Otomatik Tespit
14
+ ```javascript
15
+ // Otomatik olarak tespit edilen environment variable'lar:
16
+ - process.versions.electron
17
+ - process.env.ELECTRON_VERSION
18
+ - process.env.ELECTRON_RUN_AS_NODE
19
+ ```
20
+
21
+ ### 🚫 Native Overlay'lar Devre Dışı
22
+ Electron modunda aşağıdaki native işlevler güvenli modda çalışır:
23
+ - ✅ Window listing (güvenli)
24
+ - ❌ Native NSWindow overlays (devre dışı)
25
+ - ❌ Native recording preview overlays (devre dışı)
26
+
27
+ ## 🎯 API Kullanımı
28
+
29
+ ### Temel Kurulum
30
+ ```javascript
31
+ const ElectronWindowSelector = require('node-mac-recorder/electron-window-selector');
32
+
33
+ const selector = new ElectronWindowSelector();
34
+ console.log(`Environment: ${selector.isElectron ? 'Electron' : 'Node.js'}`);
35
+ ```
36
+
37
+ ### Pencere Seçimi (Electron Safe Mode)
38
+ ```javascript
39
+ // 1. Mevcut pencere listesini al
40
+ const windows = await selector.getAvailableWindows();
41
+ console.log(`Found ${windows.length} windows`);
42
+
43
+ // 2. Pencere seçim başlat (otomatik mode - demo amaçlı)
44
+ const selectedWindow = await selector.selectWindow();
45
+
46
+ // 3. Event listener ile dinle
47
+ selector.on('windowSelected', (windowInfo) => {
48
+ console.log('Selected:', windowInfo.title, windowInfo.appName);
49
+
50
+ // Electron UI'da bu window'u highlight et
51
+ showWindowInElectronUI(windowInfo);
52
+ });
53
+ ```
54
+
55
+ ### Display Seçimi (Electron Safe Mode)
56
+ ```javascript
57
+ // 1. Mevcut display listesini al
58
+ const displays = await selector.getAvailableDisplays();
59
+ console.log(`Found ${displays.length} displays`);
60
+
61
+ // 2. Display seçim başlat (otomatik mode - demo amaçlı)
62
+ const selectedDisplay = await selector.selectScreen();
63
+
64
+ // 3. Event listener ile dinle
65
+ selector.on('screenSelected', (screenInfo) => {
66
+ console.log('Selected Display:', screenInfo.name, screenInfo.resolution);
67
+
68
+ // Electron UI'da bu display'i highlight et
69
+ showDisplayInElectronUI(screenInfo);
70
+ });
71
+ ```
72
+
73
+ ## 🎨 Electron UI Implementation Önerisi
74
+
75
+ ### 1. Window Picker UI Component
76
+
77
+ ```html
78
+ <!-- Electron Renderer Process -->
79
+ <div class="window-picker">
80
+ <h3>Select Window to Record</h3>
81
+ <div class="window-grid">
82
+ <!-- Her window için thumbnail ve bilgi -->
83
+ <div v-for="window in availableWindows"
84
+ :key="window.id"
85
+ class="window-card"
86
+ :class="{ selected: selectedWindow?.id === window.id }"
87
+ @click="selectWindow(window)">
88
+
89
+ <div class="window-thumbnail">
90
+ <!-- Thumbnail görseli buraya -->
91
+ <img :src="window.thumbnail" v-if="window.thumbnail" />
92
+ <div class="window-placeholder" v-else>
93
+ {{ window.appName?.charAt(0) || '?' }}
94
+ </div>
95
+ </div>
96
+
97
+ <div class="window-info">
98
+ <div class="app-name">{{ window.appName || 'Unknown App' }}</div>
99
+ <div class="window-title">{{ window.title || 'Untitled' }}</div>
100
+ <div class="window-size">{{ window.width }}×{{ window.height }}</div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ ```
106
+
107
+ ### 2. Renderer Process Logic
108
+ ```javascript
109
+ // renderer.js
110
+ const { ipcRenderer } = require('electron');
111
+
112
+ let windowSelector = null;
113
+
114
+ async function initializeWindowPicker() {
115
+ // Main process'ten window selector'ı başlat
116
+ const result = await ipcRenderer.invoke('init-window-selector');
117
+
118
+ if (result.success) {
119
+ // Mevcut windows'ları al
120
+ const windows = await ipcRenderer.invoke('get-available-windows');
121
+ displayWindowsInUI(windows);
122
+ }
123
+ }
124
+
125
+ function displayWindowsInUI(windows) {
126
+ const windowGrid = document.querySelector('.window-grid');
127
+ windowGrid.innerHTML = '';
128
+
129
+ windows.forEach(window => {
130
+ const windowCard = createWindowCard(window);
131
+ windowGrid.appendChild(windowCard);
132
+ });
133
+ }
134
+
135
+ function createWindowCard(window) {
136
+ const card = document.createElement('div');
137
+ card.className = 'window-card';
138
+ card.innerHTML = `
139
+ <div class="window-thumbnail">
140
+ <div class="window-placeholder">${window.appName?.charAt(0) || '?'}</div>
141
+ </div>
142
+ <div class="window-info">
143
+ <div class="app-name">${window.appName || 'Unknown App'}</div>
144
+ <div class="window-title">${window.title || 'Untitled'}</div>
145
+ <div class="window-size">${window.width}×${window.height}</div>
146
+ </div>
147
+ `;
148
+
149
+ card.addEventListener('click', () => selectWindow(window));
150
+ return card;
151
+ }
152
+
153
+ function selectWindow(window) {
154
+ // UI'da seçimi görsel olarak göster
155
+ document.querySelectorAll('.window-card').forEach(card =>
156
+ card.classList.remove('selected')
157
+ );
158
+ event.target.closest('.window-card').classList.add('selected');
159
+
160
+ // Main process'e seçimi bildir
161
+ ipcRenderer.invoke('window-selected', window);
162
+ }
163
+ ```
164
+
165
+ ### 3. Main Process Handler
166
+ ```javascript
167
+ // main.js
168
+ const { ipcMain } = require('electron');
169
+ const ElectronWindowSelector = require('node-mac-recorder/electron-window-selector');
170
+
171
+ let windowSelector = null;
172
+
173
+ ipcMain.handle('init-window-selector', async () => {
174
+ try {
175
+ windowSelector = new ElectronWindowSelector();
176
+ return { success: true };
177
+ } catch (error) {
178
+ return { success: false, error: error.message };
179
+ }
180
+ });
181
+
182
+ ipcMain.handle('get-available-windows', async () => {
183
+ if (!windowSelector) return [];
184
+ return await windowSelector.getAvailableWindows();
185
+ });
186
+
187
+ ipcMain.handle('get-available-displays', async () => {
188
+ if (!windowSelector) return [];
189
+ return await windowSelector.getAvailableDisplays();
190
+ });
191
+
192
+ ipcMain.handle('window-selected', async (event, windowInfo) => {
193
+ console.log('Window selected in Electron UI:', windowInfo);
194
+
195
+ // Recording başlatılabilir
196
+ const MacRecorder = require('node-mac-recorder');
197
+ const recorder = new MacRecorder();
198
+
199
+ // Window recording başlat
200
+ await recorder.startRecording('./output.mov', {
201
+ windowId: windowInfo.id,
202
+ // ... diğer options
203
+ });
204
+
205
+ return { success: true };
206
+ });
207
+ ```
208
+
209
+ ## 🎬 Recording Preview (Electron Mode)
210
+
211
+ Electron modunda native preview'lar çalışmaz. Bunun yerine Electron UI'da preview gösterin:
212
+
213
+ ```javascript
214
+ // Recording preview'ı Electron UI'da göster
215
+ function showRecordingPreview(windowInfo) {
216
+ // Electron window'da overlay div oluştur
217
+ const overlay = document.createElement('div');
218
+ overlay.className = 'recording-preview-overlay';
219
+ overlay.style.cssText = `
220
+ position: fixed;
221
+ top: 0;
222
+ left: 0;
223
+ right: 0;
224
+ bottom: 0;
225
+ background: rgba(0, 0, 0, 0.5);
226
+ z-index: 9999;
227
+ display: flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ `;
231
+
232
+ overlay.innerHTML = `
233
+ <div class="preview-info">
234
+ <h3>Recording Preview</h3>
235
+ <p>Recording: ${windowInfo.appName} - ${windowInfo.title}</p>
236
+ <p>Area: ${windowInfo.width}×${windowInfo.height}</p>
237
+ <button id="start-recording">Start Recording</button>
238
+ <button id="cancel-preview">Cancel</button>
239
+ </div>
240
+ `;
241
+
242
+ document.body.appendChild(overlay);
243
+ }
244
+ ```
245
+
246
+ ## 🔧 CSS Styling
247
+ ```css
248
+ .window-picker {
249
+ padding: 20px;
250
+ max-height: 500px;
251
+ overflow-y: auto;
252
+ }
253
+
254
+ .window-grid {
255
+ display: grid;
256
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
257
+ gap: 15px;
258
+ margin-top: 15px;
259
+ }
260
+
261
+ .window-card {
262
+ border: 2px solid #e1e1e1;
263
+ border-radius: 8px;
264
+ padding: 10px;
265
+ cursor: pointer;
266
+ transition: all 0.2s;
267
+ }
268
+
269
+ .window-card:hover {
270
+ border-color: #007acc;
271
+ background-color: #f0f8ff;
272
+ }
273
+
274
+ .window-card.selected {
275
+ border-color: #007acc;
276
+ background-color: #e6f3ff;
277
+ }
278
+
279
+ .window-thumbnail {
280
+ height: 80px;
281
+ background: #f5f5f5;
282
+ border-radius: 4px;
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ margin-bottom: 8px;
287
+ }
288
+
289
+ .window-placeholder {
290
+ font-size: 24px;
291
+ font-weight: bold;
292
+ color: #666;
293
+ }
294
+
295
+ .window-info {
296
+ text-align: center;
297
+ }
298
+
299
+ .app-name {
300
+ font-weight: bold;
301
+ color: #333;
302
+ margin-bottom: 2px;
303
+ }
304
+
305
+ .window-title {
306
+ color: #666;
307
+ font-size: 12px;
308
+ margin-bottom: 2px;
309
+ }
310
+
311
+ .window-size {
312
+ color: #999;
313
+ font-size: 11px;
314
+ }
315
+ ```
316
+
317
+ ## ⚠️ Önemli Notlar
318
+
319
+ 1. **Native Overlays**: Electron modunda native NSWindow overlays devre dışıdır
320
+ 2. **Auto Selection**: Şu an demo amaçlı otomatik seçim yapıyor, gerçek uygulamada UI ile seçim yapılmalı
321
+ 3. **Permission Check**: `checkPermissions()` tüm modlarda çalışır
322
+ 4. **Event Handling**: Electron'da event'ler IPC ile main ve renderer process arasında taşınmalı
323
+
324
+ ## 🚀 Sonraki Adımlar
325
+
326
+ 1. Thumbnail generation implementasyonu
327
+ 2. Real-time window list updates
328
+ 3. Multiple display support UI
329
+ 4. Recording progress indicator
330
+ 5. Custom recording area selection
331
+
332
+ ## 📞 Test Komutları
333
+
334
+ ```bash
335
+ # Electron mode test
336
+ ELECTRON_VERSION=25.0.0 node test-electron-window-selector.js
337
+
338
+ # Node.js mode test
339
+ node test-electron-window-selector.js
340
+ ```
341
+
342
+ Bu implementasyon sayesinde Electron uygulamaları crash olmadan pencere seçimi yapabilir ve recording işlevlerini güvenli şekilde kullanabilir.
@@ -666,6 +666,33 @@ class ElectronWindowSelector extends EventEmitter {
666
666
  };
667
667
  }
668
668
  }
669
+
670
+ /**
671
+ * Electron tarafından kullanılacak window listesi döndürür
672
+ */
673
+ async getAvailableWindows() {
674
+ try {
675
+ const windows = nativeBinding.getWindows();
676
+ return windows || [];
677
+ } catch (error) {
678
+ console.error('Get available windows failed:', error.message);
679
+ return [];
680
+ }
681
+ }
682
+
683
+ /**
684
+ * Electron tarafından kullanılacak display listesi döndürür
685
+ */
686
+ async getAvailableDisplays() {
687
+ try {
688
+ const MacRecorder = require('./index.js');
689
+ const recorder = new MacRecorder();
690
+ return await recorder.getDisplays();
691
+ } catch (error) {
692
+ console.error('Get available displays failed:', error.message);
693
+ return [];
694
+ }
695
+ }
669
696
  }
670
697
 
671
698
  module.exports = ElectronWindowSelector;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.4.9",
3
+ "version": "2.4.10",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -0,0 +1,119 @@
1
+ const ElectronWindowSelector = require('./electron-window-selector');
2
+
3
+ // Electron environment simülasyonu
4
+ console.log('🧪 Testing Electron Window Selector...\n');
5
+
6
+ // Electron environment variable'ları set et
7
+ process.env.ELECTRON_VERSION = '25.0.0';
8
+
9
+ async function testElectronWindowSelector() {
10
+ const selector = new ElectronWindowSelector();
11
+
12
+ console.log(`🔍 Environment: ${selector.isElectron ? 'Electron' : 'Node.js'}`);
13
+
14
+ try {
15
+ console.log('\n1️⃣ Testing Permission Check...');
16
+ const permissions = await selector.checkPermissions();
17
+ console.log('✅ Permissions:', permissions);
18
+
19
+ console.log('\n2️⃣ Testing Available Windows...');
20
+ const windows = await selector.getAvailableWindows();
21
+ console.log(`✅ Found ${windows.length} windows`);
22
+ if (windows.length > 0) {
23
+ console.log(' 📱 Sample window:', {
24
+ title: windows[0].title,
25
+ appName: windows[0].appName,
26
+ size: `${windows[0].width}x${windows[0].height}`
27
+ });
28
+ }
29
+
30
+ console.log('\n3️⃣ Testing Available Displays...');
31
+ const displays = await selector.getAvailableDisplays();
32
+ console.log(`✅ Found ${displays.length} displays`);
33
+ if (displays.length > 0) {
34
+ console.log(' 🖥️ Primary display:', {
35
+ name: displays[0].name,
36
+ resolution: `${displays[0].width}x${displays[0].height}`,
37
+ isPrimary: displays[0].isPrimary
38
+ });
39
+ }
40
+
41
+ console.log('\n4️⃣ Testing Window Selection (Electron Safe Mode)...');
42
+ const windowSelectionPromise = selector.selectWindow();
43
+
44
+ // Event listeners
45
+ selector.on('windowSelected', (windowInfo) => {
46
+ console.log('🎯 Window selected event:', {
47
+ title: windowInfo.title,
48
+ appName: windowInfo.appName,
49
+ position: `${windowInfo.x},${windowInfo.y}`,
50
+ size: `${windowInfo.width}x${windowInfo.height}`
51
+ });
52
+ });
53
+
54
+ selector.on('selectionStarted', () => {
55
+ console.log('🟢 Window selection started');
56
+ });
57
+
58
+ const selectedWindow = await windowSelectionPromise;
59
+ console.log('✅ Window selection completed');
60
+
61
+ console.log('\n5️⃣ Testing Screen Selection (Electron Safe Mode)...');
62
+ const screenSelectionPromise = selector.selectScreen();
63
+
64
+ selector.on('screenSelected', (screenInfo) => {
65
+ console.log('🖥️ Screen selected event:', {
66
+ name: screenInfo.name || 'Display ' + screenInfo.id,
67
+ resolution: `${screenInfo.width}x${screenInfo.height}`,
68
+ isPrimary: screenInfo.isPrimary
69
+ });
70
+ });
71
+
72
+ const selectedScreen = await screenSelectionPromise;
73
+ console.log('✅ Screen selection completed');
74
+
75
+ console.log('\n6️⃣ Testing Recording Preview (Electron Safe Mode)...');
76
+ if (selectedWindow) {
77
+ await selector.showRecordingPreview(selectedWindow);
78
+ console.log('✅ Recording preview shown (Electron mode - no native overlay)');
79
+
80
+ await selector.hideRecordingPreview();
81
+ console.log('✅ Recording preview hidden');
82
+ }
83
+
84
+ console.log('\n7️⃣ Testing Screen Recording Preview (Electron Safe Mode)...');
85
+ if (selectedScreen) {
86
+ await selector.showScreenRecordingPreview(selectedScreen);
87
+ console.log('✅ Screen recording preview shown (Electron mode - no native overlay)');
88
+
89
+ await selector.hideScreenRecordingPreview();
90
+ console.log('✅ Screen recording preview hidden');
91
+ }
92
+
93
+ console.log('\n8️⃣ Cleanup...');
94
+ await selector.cleanup();
95
+ console.log('✅ Cleanup completed');
96
+
97
+ console.log('\n🎉 All Electron Window Selector tests PASSED!');
98
+
99
+ } catch (error) {
100
+ console.error('\n❌ Test failed:', error.message);
101
+ console.error(' Stack:', error.stack);
102
+
103
+ // Cleanup on error
104
+ try {
105
+ await selector.cleanup();
106
+ } catch (cleanupError) {
107
+ console.error('❌ Cleanup failed:', cleanupError.message);
108
+ }
109
+ }
110
+ }
111
+
112
+ // Run test
113
+ testElectronWindowSelector().then(() => {
114
+ console.log('\n✅ Test completed');
115
+ process.exit(0);
116
+ }).catch((error) => {
117
+ console.error('\n❌ Test suite failed:', error);
118
+ process.exit(1);
119
+ });