node-mac-recorder 2.4.11 → 2.4.13

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.
@@ -1,710 +0,0 @@
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
- ### Real-Time Window Detection (Electron 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. Real-time mouse tracking başlat
44
- const nativeBinding = require('./build/Release/mac_recorder.node');
45
- const trackingInterval = setInterval(() => {
46
- const status = nativeBinding.getWindowSelectionStatus();
47
-
48
- if (status && status.currentWindow) {
49
- const window = status.currentWindow;
50
- console.log(`Window under cursor: ${window.appName} - "${window.title}"`);
51
- console.log(`Position: (${window.x}, ${window.y}) Size: ${window.width}x${window.height}`);
52
-
53
- if (window.screenId !== undefined) {
54
- console.log(`Screen: ${window.screenId} (${window.screenWidth}x${window.screenHeight})`);
55
- }
56
-
57
- // Electron UI'da bu window'u real-time highlight et
58
- highlightWindowInElectronUI(window);
59
- }
60
- }, 100); // 100ms polling for smooth tracking
61
-
62
- // 3. Pencere seçimi tamamlandığında
63
- function selectWindow(windowInfo) {
64
- clearInterval(trackingInterval);
65
- console.log('Window selected:', windowInfo);
66
-
67
- // Recording başlat
68
- startRecordingWithWindow(windowInfo);
69
- }
70
- ```
71
-
72
- ### Screen/Display Seçimi (Electron Mode)
73
- ```javascript
74
- // 1. Mevcut display listesini al
75
- const displays = await selector.getAvailableDisplays();
76
- console.log(`Found ${displays.length} displays`);
77
-
78
- // 2. Screen seçim başlat (Electron mode'da otomatik main screen)
79
- const nativeBinding = require('./build/Release/mac_recorder.node');
80
- const screenResult = nativeBinding.startScreenSelection();
81
-
82
- if (screenResult) {
83
- // Screen seçim bilgisini al
84
- const selectedScreen = nativeBinding.getSelectedScreenInfo();
85
-
86
- if (selectedScreen) {
87
- console.log('Screen selected:', selectedScreen);
88
- console.log(`Resolution: ${selectedScreen.width}x${selectedScreen.height}`);
89
- console.log(`Position: (${selectedScreen.x}, ${selectedScreen.y})`);
90
-
91
- // Screen recording başlat
92
- startScreenRecording(selectedScreen);
93
- }
94
- }
95
-
96
- // 3. Manuel screen seçimi (UI ile)
97
- function selectScreen(screenInfo) {
98
- console.log('Screen manually selected:', screenInfo);
99
-
100
- // Recording başlat
101
- startScreenRecordingWithScreen(screenInfo);
102
- }
103
- ```
104
-
105
- ## 🎨 Electron UI Implementation Önerisi
106
-
107
- ### 1. Window Picker UI Component
108
-
109
- ```html
110
- <!-- Electron Renderer Process -->
111
- <div class="window-picker">
112
- <h3>Select Window to Record</h3>
113
- <div class="window-grid">
114
- <!-- Her window için thumbnail ve bilgi -->
115
- <div v-for="window in availableWindows"
116
- :key="window.id"
117
- class="window-card"
118
- :class="{ selected: selectedWindow?.id === window.id }"
119
- @click="selectWindow(window)">
120
-
121
- <div class="window-thumbnail">
122
- <!-- Thumbnail görseli buraya -->
123
- <img :src="window.thumbnail" v-if="window.thumbnail" />
124
- <div class="window-placeholder" v-else>
125
- {{ window.appName?.charAt(0) || '?' }}
126
- </div>
127
- </div>
128
-
129
- <div class="window-info">
130
- <div class="app-name">{{ window.appName || 'Unknown App' }}</div>
131
- <div class="window-title">{{ window.title || 'Untitled' }}</div>
132
- <div class="window-size">{{ window.width }}×{{ window.height }}</div>
133
- </div>
134
- </div>
135
- </div>
136
- </div>
137
- ```
138
-
139
- ### 2. Real-Time Window Tracking (Renderer Process)
140
- ```javascript
141
- // renderer.js
142
- const { ipcRenderer } = require('electron');
143
-
144
- let windowSelector = null;
145
- let trackingInterval = null;
146
- let currentHighlightedWindow = null;
147
-
148
- async function initializeWindowPicker() {
149
- // Main process'ten window selector'ı başlat
150
- const result = await ipcRenderer.invoke('init-window-selector');
151
-
152
- if (result.success) {
153
- // Mevcut windows'ları al
154
- const windows = await ipcRenderer.invoke('get-available-windows');
155
- displayWindowsInUI(windows);
156
-
157
- // Real-time tracking başlat
158
- startRealTimeTracking();
159
- }
160
- }
161
-
162
- function startRealTimeTracking() {
163
- trackingInterval = setInterval(async () => {
164
- try {
165
- // Main process'ten mouse altındaki pencere bilgisini al
166
- const currentWindow = await ipcRenderer.invoke('get-current-window-under-cursor');
167
-
168
- if (currentWindow && currentWindow.id !== currentHighlightedWindow?.id) {
169
- // Yeni pencere tespit edildi
170
- highlightWindowInUI(currentWindow);
171
- currentHighlightedWindow = currentWindow;
172
-
173
- // UI'da bilgileri güncelle
174
- updateCurrentWindowInfo(currentWindow);
175
- } else if (!currentWindow && currentHighlightedWindow) {
176
- // Mouse hiçbir pencere üstünde değil
177
- clearWindowHighlight();
178
- currentHighlightedWindow = null;
179
- clearCurrentWindowInfo();
180
- }
181
- } catch (error) {
182
- console.warn('Window tracking error:', error.message);
183
- }
184
- }, 100); // 100ms smooth tracking
185
- }
186
-
187
- function highlightWindowInUI(window) {
188
- // Tüm window card'ları normal hale getir
189
- document.querySelectorAll('.window-card').forEach(card => {
190
- card.classList.remove('hover-detected');
191
- });
192
-
193
- // Eşleşen window card'ı highlight et
194
- const matchingCard = document.querySelector(`[data-window-id="${window.id}"]`);
195
- if (matchingCard) {
196
- matchingCard.classList.add('hover-detected');
197
- }
198
- }
199
-
200
- function clearWindowHighlight() {
201
- document.querySelectorAll('.window-card').forEach(card => {
202
- card.classList.remove('hover-detected');
203
- });
204
- }
205
-
206
- function updateCurrentWindowInfo(window) {
207
- const infoDiv = document.getElementById('current-window-info');
208
- if (infoDiv) {
209
- infoDiv.innerHTML = `
210
- <h4>Window Under Cursor</h4>
211
- <p><strong>App:</strong> ${window.appName}</p>
212
- <p><strong>Title:</strong> ${window.title}</p>
213
- <p><strong>Size:</strong> ${window.width}×${window.height}</p>
214
- <p><strong>Position:</strong> (${window.x}, ${window.y})</p>
215
- ${window.screenId !== undefined ?
216
- `<p><strong>Screen:</strong> ${window.screenId} (${window.screenWidth}×${window.screenHeight})</p>` :
217
- ''}
218
- `;
219
- infoDiv.classList.add('visible');
220
- }
221
- }
222
-
223
- function clearCurrentWindowInfo() {
224
- const infoDiv = document.getElementById('current-window-info');
225
- if (infoDiv) {
226
- infoDiv.classList.remove('visible');
227
- }
228
- }
229
-
230
- function displayWindowsInUI(windows) {
231
- const windowGrid = document.querySelector('.window-grid');
232
- windowGrid.innerHTML = '';
233
-
234
- windows.forEach(window => {
235
- const windowCard = createWindowCard(window);
236
- windowGrid.appendChild(windowCard);
237
- });
238
- }
239
-
240
- function createWindowCard(window) {
241
- const card = document.createElement('div');
242
- card.className = 'window-card';
243
- card.setAttribute('data-window-id', window.id);
244
- card.innerHTML = `
245
- <div class="window-thumbnail">
246
- <div class="window-placeholder">${window.appName?.charAt(0) || '?'}</div>
247
- </div>
248
- <div class="window-info">
249
- <div class="app-name">${window.appName || 'Unknown App'}</div>
250
- <div class="window-title">${window.title || 'Untitled'}</div>
251
- <div class="window-size">${window.width}×${window.height}</div>
252
- </div>
253
- `;
254
-
255
- card.addEventListener('click', () => selectWindow(window));
256
- return card;
257
- }
258
-
259
- function selectWindow(window) {
260
- // Tracking'i durdur
261
- if (trackingInterval) {
262
- clearInterval(trackingInterval);
263
- trackingInterval = null;
264
- }
265
-
266
- // UI'da seçimi görsel olarak göster
267
- document.querySelectorAll('.window-card').forEach(card => {
268
- card.classList.remove('selected', 'hover-detected');
269
- });
270
- event.target.closest('.window-card').classList.add('selected');
271
-
272
- // Main process'e seçimi bildir
273
- ipcRenderer.invoke('window-selected', window);
274
- }
275
-
276
- // Cleanup function
277
- window.addEventListener('beforeunload', () => {
278
- if (trackingInterval) {
279
- clearInterval(trackingInterval);
280
- }
281
- });
282
- ```
283
-
284
- ### 3. Enhanced Main Process Handler
285
- ```javascript
286
- // main.js
287
- const { ipcMain } = require('electron');
288
- const ElectronWindowSelector = require('node-mac-recorder/electron-window-selector');
289
-
290
- let windowSelector = null;
291
- let nativeBinding = null;
292
-
293
- ipcMain.handle('init-window-selector', async () => {
294
- try {
295
- windowSelector = new ElectronWindowSelector();
296
-
297
- // Native binding'i yükle (real-time tracking için)
298
- try {
299
- nativeBinding = require('./build/Release/mac_recorder.node');
300
- } catch (error) {
301
- console.warn('Native binding yüklenemedi:', error.message);
302
- }
303
-
304
- return { success: true };
305
- } catch (error) {
306
- return { success: false, error: error.message };
307
- }
308
- });
309
-
310
- ipcMain.handle('get-available-windows', async () => {
311
- if (!windowSelector) return [];
312
- return await windowSelector.getAvailableWindows();
313
- });
314
-
315
- ipcMain.handle('get-available-displays', async () => {
316
- if (!windowSelector) return [];
317
- return await windowSelector.getAvailableDisplays();
318
- });
319
-
320
- // Real-time window tracking için yeni handler
321
- ipcMain.handle('get-current-window-under-cursor', async () => {
322
- if (!nativeBinding) return null;
323
-
324
- try {
325
- const status = nativeBinding.getWindowSelectionStatus();
326
- return status?.currentWindow || null;
327
- } catch (error) {
328
- console.warn('Window status alınamadı:', error.message);
329
- return null;
330
- }
331
- });
332
-
333
- ipcMain.handle('window-selected', async (event, windowInfo) => {
334
- console.log('Window selected in Electron UI:', windowInfo);
335
- console.log(` - App: ${windowInfo.appName}`);
336
- console.log(` - Title: ${windowInfo.title}`);
337
- console.log(` - Size: ${windowInfo.width}×${windowInfo.height}`);
338
- console.log(` - Position: (${windowInfo.x}, ${windowInfo.y})`);
339
-
340
- if (windowInfo.screenId !== undefined) {
341
- console.log(` - Screen: ${windowInfo.screenId} (${windowInfo.screenWidth}×${windowInfo.screenHeight})`);
342
- }
343
-
344
- // Recording başlatılabilir
345
- const MacRecorder = require('node-mac-recorder');
346
- const recorder = new MacRecorder();
347
-
348
- try {
349
- // Window recording başlat
350
- await recorder.startRecording('./output.mov', {
351
- windowId: windowInfo.id,
352
- // Screen coordination
353
- x: windowInfo.x,
354
- y: windowInfo.y,
355
- width: windowInfo.width,
356
- height: windowInfo.height,
357
- // Diğer options
358
- fps: 30,
359
- audioEnabled: true
360
- });
361
-
362
- return {
363
- success: true,
364
- message: 'Recording started successfully',
365
- windowInfo: windowInfo
366
- };
367
- } catch (error) {
368
- return {
369
- success: false,
370
- error: error.message
371
- };
372
- }
373
- });
374
-
375
- // Screen recording handler
376
- ipcMain.handle('screen-selected', async (event, screenInfo) => {
377
- console.log('Screen selected in Electron UI:', screenInfo);
378
-
379
- const MacRecorder = require('node-mac-recorder');
380
- const recorder = new MacRecorder();
381
-
382
- try {
383
- // Screen recording başlat
384
- await recorder.startRecording('./screen-output.mov', {
385
- // Screen mode
386
- screenId: screenInfo.id,
387
- x: screenInfo.x,
388
- y: screenInfo.y,
389
- width: screenInfo.width,
390
- height: screenInfo.height,
391
- fps: 30,
392
- audioEnabled: true
393
- });
394
-
395
- return {
396
- success: true,
397
- message: 'Screen recording started',
398
- screenInfo: screenInfo
399
- };
400
- } catch (error) {
401
- return {
402
- success: false,
403
- error: error.message
404
- };
405
- }
406
- });
407
-
408
- // Recording control handlers
409
- ipcMain.handle('stop-recording', async () => {
410
- // Aktif recorder instance'ı durdur
411
- // Bu implementation'a recorder management eklenmeli
412
- return { success: true, message: 'Recording stopped' };
413
- });
414
- ```
415
-
416
- ## 🎬 Recording Preview (Electron Mode)
417
-
418
- Electron modunda native preview'lar çalışmaz. Bunun yerine Electron UI'da preview gösterin:
419
-
420
- ```javascript
421
- // Recording preview'ı Electron UI'da göster
422
- function showRecordingPreview(windowInfo) {
423
- // Electron window'da overlay div oluştur
424
- const overlay = document.createElement('div');
425
- overlay.className = 'recording-preview-overlay';
426
- overlay.style.cssText = `
427
- position: fixed;
428
- top: 0;
429
- left: 0;
430
- right: 0;
431
- bottom: 0;
432
- background: rgba(0, 0, 0, 0.5);
433
- z-index: 9999;
434
- display: flex;
435
- align-items: center;
436
- justify-content: center;
437
- `;
438
-
439
- overlay.innerHTML = `
440
- <div class="preview-info">
441
- <h3>Recording Preview</h3>
442
- <p>Recording: ${windowInfo.appName} - ${windowInfo.title}</p>
443
- <p>Area: ${windowInfo.width}×${windowInfo.height}</p>
444
- <button id="start-recording">Start Recording</button>
445
- <button id="cancel-preview">Cancel</button>
446
- </div>
447
- `;
448
-
449
- document.body.appendChild(overlay);
450
- }
451
- ```
452
-
453
- ## 🎨 Enhanced CSS Styling (Real-Time Tracking)
454
- ```css
455
- .window-picker {
456
- padding: 20px;
457
- max-height: 500px;
458
- overflow-y: auto;
459
- position: relative;
460
- }
461
-
462
- .window-grid {
463
- display: grid;
464
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
465
- gap: 15px;
466
- margin-top: 15px;
467
- }
468
-
469
- .window-card {
470
- border: 2px solid #e1e1e1;
471
- border-radius: 8px;
472
- padding: 10px;
473
- cursor: pointer;
474
- transition: all 0.2s;
475
- position: relative;
476
- }
477
-
478
- .window-card:hover {
479
- border-color: #007acc;
480
- background-color: #f0f8ff;
481
- }
482
-
483
- .window-card.selected {
484
- border-color: #007acc;
485
- background-color: #e6f3ff;
486
- }
487
-
488
- /* Real-time hover detection styling */
489
- .window-card.hover-detected {
490
- border-color: #ff6b35 !important;
491
- background-color: #fff3f0 !important;
492
- box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
493
- transform: translateY(-2px);
494
- }
495
-
496
- .window-card.hover-detected::before {
497
- content: "🎯 CURSOR HERE";
498
- position: absolute;
499
- top: -10px;
500
- right: 5px;
501
- background: #ff6b35;
502
- color: white;
503
- padding: 2px 6px;
504
- border-radius: 4px;
505
- font-size: 10px;
506
- font-weight: bold;
507
- }
508
-
509
- .window-thumbnail {
510
- height: 80px;
511
- background: #f5f5f5;
512
- border-radius: 4px;
513
- display: flex;
514
- align-items: center;
515
- justify-content: center;
516
- margin-bottom: 8px;
517
- }
518
-
519
- .window-placeholder {
520
- font-size: 24px;
521
- font-weight: bold;
522
- color: #666;
523
- }
524
-
525
- .window-info {
526
- text-align: center;
527
- }
528
-
529
- .app-name {
530
- font-weight: bold;
531
- color: #333;
532
- margin-bottom: 2px;
533
- }
534
-
535
- .window-title {
536
- color: #666;
537
- font-size: 12px;
538
- margin-bottom: 2px;
539
- }
540
-
541
- .window-size {
542
- color: #999;
543
- font-size: 11px;
544
- }
545
-
546
- /* Real-time info panel */
547
- #current-window-info {
548
- position: fixed;
549
- top: 20px;
550
- right: 20px;
551
- background: rgba(0, 0, 0, 0.9);
552
- color: white;
553
- padding: 15px;
554
- border-radius: 8px;
555
- min-width: 250px;
556
- opacity: 0;
557
- transform: translateX(300px);
558
- transition: all 0.3s ease;
559
- z-index: 1000;
560
- font-family: 'Monaco', monospace;
561
- font-size: 12px;
562
- }
563
-
564
- #current-window-info.visible {
565
- opacity: 1;
566
- transform: translateX(0);
567
- }
568
-
569
- #current-window-info h4 {
570
- margin: 0 0 10px 0;
571
- color: #ff6b35;
572
- font-size: 14px;
573
- }
574
-
575
- #current-window-info p {
576
- margin: 4px 0;
577
- line-height: 1.4;
578
- }
579
-
580
- #current-window-info strong {
581
- color: #fff;
582
- }
583
-
584
- /* Animation for smooth transitions */
585
- .window-card {
586
- will-change: transform, box-shadow, border-color;
587
- }
588
-
589
- @keyframes pulseHover {
590
- 0% { transform: scale(1) translateY(-2px); }
591
- 50% { transform: scale(1.02) translateY(-3px); }
592
- 100% { transform: scale(1) translateY(-2px); }
593
- }
594
-
595
- .window-card.hover-detected {
596
- animation: pulseHover 2s infinite;
597
- }
598
-
599
- /* Status indicator */
600
- .tracking-status {
601
- position: absolute;
602
- top: 10px;
603
- left: 20px;
604
- background: #4CAF50;
605
- color: white;
606
- padding: 5px 10px;
607
- border-radius: 15px;
608
- font-size: 12px;
609
- font-weight: bold;
610
- }
611
-
612
- .tracking-status::before {
613
- content: "🔴 ";
614
- animation: blink 1s infinite;
615
- }
616
-
617
- @keyframes blink {
618
- 0%, 50% { opacity: 1; }
619
- 51%, 100% { opacity: 0; }
620
- }
621
- ```
622
-
623
- ## ✨ Yeni Real-Time Tracking Özellikleri
624
-
625
- ### 🎯 Anlık Window Tespit
626
- - **100ms polling** ile smooth mouse tracking
627
- - Mouse hangi pencere üstüne giderse **otomatik highlight**
628
- - **Screen detection** - pencere hangi ekranda, otomatik tespit
629
- - **Koordinat bilgisi** - x, y, width, height gerçek zamanlı
630
-
631
- ### 🔥 UI Features
632
- - `hover-detected` class ile anlık görsel feedback
633
- - Real-time info panel (sağ üst köşe)
634
- - Pulse animation effect
635
- - "🎯 CURSOR HERE" indicator
636
-
637
- ### 🖥️ Multi-Screen Support
638
- - Pencere hangi screen'de otomatik tespit
639
- - Screen koordinatları ve boyutları dahil
640
- - Cross-screen window tracking
641
-
642
- ## ⚠️ Önemli Notlar
643
-
644
- ### 🔧 Teknik Gereksinimler
645
- 1. **Native Module**: Real-time tracking için native binding gerekli
646
- 2. **macOS Permissions**: Screen Recording ve Accessibility izinleri
647
- 3. **Electron Environment**: `ELECTRON_VERSION` env variable otomatik tespit
648
- 4. **Performance**: 100ms polling interval (ayarlanabilir)
649
-
650
- ### 🛠️ Implementation Notes
651
- 1. **IPC Communication**: Main ↔ Renderer process real-time data exchange
652
- 2. **Memory Management**: Interval cleanup önemli
653
- 3. **Error Handling**: Native binding yoksa graceful fallback
654
- 4. **UI Responsiveness**: CSS transitions ile smooth UX
655
-
656
- ### 🚨 Troubleshooting
657
- - **Native binding yüklenemezse**: `npm run build` ile tekrar derle
658
- - **Permission hatası**: System Preferences → Security & Privacy
659
- - **Tracking çalışmıyorsa**: `ELECTRON_VERSION` environment variable kontrol et
660
- - **UI update yavaşsa**: Polling interval'ı artır (100ms → 200ms)
661
-
662
- ## 🚀 Sonraki Adımlar
663
-
664
- ### Phase 1: Core Enhancement
665
- 1. ✅ **Real-time window tracking** - TAMAMLANDI
666
- 2. ✅ **Screen detection accuracy** - TAMAMLANDI
667
- 3. ✅ **Electron compatibility** - TAMAMLANDI
668
- 4. 🔄 Thumbnail generation implementasyonu
669
-
670
- ### Phase 2: Advanced Features
671
- 5. 📋 Window list real-time updates
672
- 6. 🖥️ Multiple display UI enhancement
673
- 7. 📹 Recording progress indicator
674
- 8. ✂️ Custom recording area selection
675
- 9. 🎨 Window preview thumbnails
676
-
677
- ### Phase 3: Performance & UX
678
- 10. ⚡ Performance optimization
679
- 11. 🎭 Advanced animations
680
- 12. 📱 Responsive design improvements
681
- 13. 🔧 Settings panel
682
-
683
- ## 📞 Test Komutları
684
-
685
- ```bash
686
- # Fixed overlay functionality test
687
- node test-overlay-fix.js
688
-
689
- # Electron mode test
690
- ELECTRON_VERSION=25.0.0 node test-overlay-fix.js
691
-
692
- # Build native module
693
- npm run build
694
-
695
- # Full integration test
696
- node test-electron-window-selector.js
697
- ```
698
-
699
- ## 🎉 Sonuç
700
-
701
- Bu güncellenmiş implementasyon ile:
702
-
703
- ✅ **Mouse tracking** gerçek zamanlı çalışıyor
704
- ✅ **Window detection** hassas ve hızlı
705
- ✅ **Screen coordination** doğru hesaplanıyor
706
- ✅ **Electron integration** sorunsuz çalışıyor
707
- ✅ **Multi-display support** tam uyumlu
708
- ✅ **Real-time UI feedback** kullanıcı dostu
709
-
710
- Electron uygulamanızda artık native overlay benzeri deneyim sunabilir, kullanıcı mouse'u hareket ettirdikçe hangi pencere üstünde olduğunu görebilir ve tek tıkla recording başlatabilirsiniz!