node-mac-recorder 2.4.8 → 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/.claude/settings.local.json +2 -1
- package/ELECTRON-INTEGRATION.md +342 -0
- package/electron-window-selector.js +698 -0
- package/index.js +57 -0
- package/package.json +1 -1
- package/src/mac_recorder.mm +27 -2
- package/src/window_selector.mm +85 -8
- package/test-electron-window-selector.js +119 -0
- package/test-sck-availability.js +26 -0
- package/test-screencapture-overlay.js +54 -0
- package/test-simple-windows.js +29 -0
- package/test-window-details.js +34 -0
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"Bash(git checkout:*)",
|
|
26
26
|
"Bash(ELECTRON_VERSION=25.0.0 node -e \"\nconsole.log(''ELECTRON_VERSION env:'', process.env.ELECTRON_VERSION);\nconsole.log(''getenv result would be:'', process.env.ELECTRON_VERSION || ''null'');\n\")",
|
|
27
27
|
"Bash(ELECTRON_VERSION=25.0.0 node test-env-detection.js)",
|
|
28
|
-
"Bash(ELECTRON_VERSION=25.0.0 node test-native-call.js)"
|
|
28
|
+
"Bash(ELECTRON_VERSION=25.0.0 node test-native-call.js)",
|
|
29
|
+
"Bash(chmod:*)"
|
|
29
30
|
],
|
|
30
31
|
"deny": []
|
|
31
32
|
}
|
|
@@ -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.
|