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.
- package/ELECTRON-INTEGRATION.md +342 -0
- package/electron-window-selector.js +27 -0
- package/package.json +1 -1
- package/test-electron-window-selector.js +119 -0
|
@@ -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
|
@@ -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
|
+
});
|