node-mac-recorder 1.3.0 → 1.4.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.
- package/WINDOW_SELECTOR_README.md +65 -0
- package/auto-front-demo.js +71 -0
- package/bring-to-front-test.js +159 -0
- package/package.json +1 -1
- package/src/window_selector.mm +158 -0
- package/window-selector.js +32 -0
|
@@ -12,6 +12,8 @@ Bu modül, macOS'ta sistem imleci ile pencere seçimi yapabilmenizi sağlayan g
|
|
|
12
12
|
- **Multi-display Support**: Çoklu ekran kurulumlarında çalışır
|
|
13
13
|
- **Detailed Window Info**: Pencere pozisyonu, boyutu ve hangi ekranda olduğunu döndürür
|
|
14
14
|
- **Event-driven API**: Pencere hover, seçim ve hata durumları için event'ler
|
|
15
|
+
- **Window Focus Control**: Detect edilen pencereyi otomatik olarak en öne getirir
|
|
16
|
+
- **Auto Bring-to-Front**: Cursor hangi pencereye gelirse otomatik focus yapar
|
|
15
17
|
- **Permission Management**: macOS izin kontrolü ve yönetimi
|
|
16
18
|
|
|
17
19
|
## 🚀 Kurulum
|
|
@@ -150,6 +152,29 @@ macOS izinlerini kontrol eder.
|
|
|
150
152
|
|
|
151
153
|
**Returns:** `Promise<PermissionStatus>`
|
|
152
154
|
|
|
155
|
+
##### `async bringWindowToFront(windowId)`
|
|
156
|
+
Belirtilen pencereyi en öne getirir (focus yapar).
|
|
157
|
+
|
|
158
|
+
**Parameters:**
|
|
159
|
+
- `windowId` (number) - Window ID
|
|
160
|
+
|
|
161
|
+
**Returns:** `Promise<boolean>` - Başarı/başarısızlık
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const success = await selector.bringWindowToFront(windowInfo.id);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
##### `setBringToFrontEnabled(enabled)`
|
|
168
|
+
Otomatik pencere en öne getirme özelliğini aktif/pasif yapar.
|
|
169
|
+
|
|
170
|
+
**Parameters:**
|
|
171
|
+
- `enabled` (boolean) - Enable/disable
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
selector.setBringToFrontEnabled(true); // Auto mode ON
|
|
175
|
+
selector.setBringToFrontEnabled(false); // Auto mode OFF
|
|
176
|
+
```
|
|
177
|
+
|
|
153
178
|
##### `async cleanup()`
|
|
154
179
|
Tüm kaynakları temizler ve seçimi durdurur.
|
|
155
180
|
|
|
@@ -309,6 +334,46 @@ if (!permissions.screenRecording) {
|
|
|
309
334
|
|
|
310
335
|
## 🌟 Gelişmiş Örnekler
|
|
311
336
|
|
|
337
|
+
### Auto Bring-to-Front (Otomatik Focus)
|
|
338
|
+
```javascript
|
|
339
|
+
const WindowSelector = require('./window-selector');
|
|
340
|
+
|
|
341
|
+
async function autoBringToFront() {
|
|
342
|
+
const selector = new WindowSelector();
|
|
343
|
+
|
|
344
|
+
// Otomatik focus modunu aktif et
|
|
345
|
+
selector.setBringToFrontEnabled(true);
|
|
346
|
+
|
|
347
|
+
selector.on('windowEntered', (window) => {
|
|
348
|
+
console.log(`🔝 Auto-focused: ${window.appName} - "${window.title}"`);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
await selector.startSelection();
|
|
352
|
+
console.log('🖱️ Move cursor over windows - they will come to front automatically!');
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Manuel Window Focus
|
|
357
|
+
```javascript
|
|
358
|
+
const WindowSelector = require('./window-selector');
|
|
359
|
+
|
|
360
|
+
async function manualFocus() {
|
|
361
|
+
const selector = new WindowSelector();
|
|
362
|
+
|
|
363
|
+
selector.on('windowEntered', async (window) => {
|
|
364
|
+
console.log(`Found: ${window.appName} - "${window.title}"`);
|
|
365
|
+
|
|
366
|
+
// Manuel olarak pencereyi en öne getir
|
|
367
|
+
const success = await selector.bringWindowToFront(window.id);
|
|
368
|
+
if (success) {
|
|
369
|
+
console.log('✅ Window brought to front!');
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
await selector.startSelection();
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
312
377
|
### Otomatik Pencere Kaydı
|
|
313
378
|
```javascript
|
|
314
379
|
const WindowSelector = require('./window-selector');
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
async function autoBringToFrontDemo() {
|
|
6
|
+
console.log('🤖 Auto Bring-To-Front Demo');
|
|
7
|
+
console.log('============================\n');
|
|
8
|
+
|
|
9
|
+
const selector = new WindowSelector();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
console.log('🔄 Enabling auto bring-to-front feature...');
|
|
13
|
+
selector.setBringToFrontEnabled(true);
|
|
14
|
+
|
|
15
|
+
console.log('✅ Auto mode enabled!');
|
|
16
|
+
console.log('🖱️ Now move your cursor over different windows');
|
|
17
|
+
console.log('🔝 Each window should automatically come to front\n');
|
|
18
|
+
|
|
19
|
+
let windowCount = 0;
|
|
20
|
+
let lastWindowId = null;
|
|
21
|
+
|
|
22
|
+
selector.on('windowEntered', (window) => {
|
|
23
|
+
if (window.id !== lastWindowId) {
|
|
24
|
+
windowCount++;
|
|
25
|
+
console.log(`[${windowCount}] 🎯 AUTO-FRONT: ${window.appName} - "${window.title}"`);
|
|
26
|
+
console.log(` 📍 Position: (${window.x}, ${window.y})`);
|
|
27
|
+
console.log(` 📏 Size: ${window.width} × ${window.height}`);
|
|
28
|
+
console.log(` 🔝 Window should come to front automatically!\n`);
|
|
29
|
+
lastWindowId = window.id;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
selector.on('windowLeft', (window) => {
|
|
34
|
+
console.log(`🚪 Left: ${window.appName} - "${window.title}"\n`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await selector.startSelection();
|
|
38
|
+
|
|
39
|
+
console.log('Demo started! Move cursor over different app windows to see them come to front.');
|
|
40
|
+
console.log('Press Ctrl+C to stop\n');
|
|
41
|
+
|
|
42
|
+
// Auto-stop after 60 seconds
|
|
43
|
+
setTimeout(async () => {
|
|
44
|
+
console.log('\n⏰ Demo completed!');
|
|
45
|
+
console.log(`📊 Total windows auto-focused: ${windowCount}`);
|
|
46
|
+
selector.setBringToFrontEnabled(false);
|
|
47
|
+
await selector.cleanup();
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}, 60000);
|
|
50
|
+
|
|
51
|
+
// Manual stop
|
|
52
|
+
process.on('SIGINT', async () => {
|
|
53
|
+
console.log('\n\n🛑 Stopping demo...');
|
|
54
|
+
console.log(`📊 Total windows auto-focused: ${windowCount}`);
|
|
55
|
+
selector.setBringToFrontEnabled(false);
|
|
56
|
+
await selector.cleanup();
|
|
57
|
+
process.exit(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Prevent exit
|
|
61
|
+
setInterval(() => {}, 1000);
|
|
62
|
+
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('❌ Error:', error.message);
|
|
65
|
+
await selector.cleanup();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (require.main === module) {
|
|
70
|
+
autoBringToFrontDemo();
|
|
71
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
async function testBringToFront() {
|
|
6
|
+
console.log('🔝 Bring To Front Test');
|
|
7
|
+
console.log('======================\n');
|
|
8
|
+
|
|
9
|
+
const selector = new WindowSelector();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Test 1: Manual bring to front
|
|
13
|
+
console.log('📋 Test 1: Manual window bring-to-front');
|
|
14
|
+
console.log('Move cursor over a window and press SPACE to bring it to front\n');
|
|
15
|
+
|
|
16
|
+
let currentWindow = null;
|
|
17
|
+
|
|
18
|
+
selector.on('windowEntered', (window) => {
|
|
19
|
+
currentWindow = window;
|
|
20
|
+
console.log(`\n🏠 Window: ${window.appName} - "${window.title}" (ID: ${window.id})`);
|
|
21
|
+
console.log(' 📍 Position:', `(${window.x}, ${window.y})`);
|
|
22
|
+
console.log(' 📏 Size:', `${window.width} × ${window.height}`);
|
|
23
|
+
console.log(' 💡 Press SPACE to bring this window to front');
|
|
24
|
+
console.log(' 💡 Press A to enable AUTO bring-to-front');
|
|
25
|
+
console.log(' 💡 Press D to disable AUTO bring-to-front');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
selector.on('windowLeft', (window) => {
|
|
29
|
+
console.log(`🚪 Left: ${window.appName} - "${window.title}"`);
|
|
30
|
+
currentWindow = null;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Keyboard controls
|
|
34
|
+
const readline = require('readline');
|
|
35
|
+
readline.emitKeypressEvents(process.stdin);
|
|
36
|
+
if (process.stdin.isTTY) {
|
|
37
|
+
process.stdin.setRawMode(true);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
process.stdin.on('keypress', async (str, key) => {
|
|
41
|
+
if (key.name === 'space' && currentWindow) {
|
|
42
|
+
console.log(`\n🔝 Bringing window to front: ${currentWindow.appName} - "${currentWindow.title}"`);
|
|
43
|
+
try {
|
|
44
|
+
const success = await selector.bringWindowToFront(currentWindow.id);
|
|
45
|
+
if (success) {
|
|
46
|
+
console.log(' ✅ Window brought to front successfully!');
|
|
47
|
+
} else {
|
|
48
|
+
console.log(' ❌ Failed to bring window to front');
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.log(' ❌ Error:', error.message);
|
|
52
|
+
}
|
|
53
|
+
} else if (key.name === 'a') {
|
|
54
|
+
console.log('\n🔄 Enabling AUTO bring-to-front mode...');
|
|
55
|
+
selector.setBringToFrontEnabled(true);
|
|
56
|
+
console.log(' ✅ Auto mode ON - Windows will come to front automatically');
|
|
57
|
+
} else if (key.name === 'd') {
|
|
58
|
+
console.log('\n🔄 Disabling AUTO bring-to-front mode...');
|
|
59
|
+
selector.setBringToFrontEnabled(false);
|
|
60
|
+
console.log(' ✅ Auto mode OFF - Manual control only');
|
|
61
|
+
} else if (key.ctrl && key.name === 'c') {
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
console.log('🚀 Starting window selection...\n');
|
|
67
|
+
console.log('📋 Controls:');
|
|
68
|
+
console.log(' SPACE - Bring current window to front');
|
|
69
|
+
console.log(' A - Enable AUTO bring-to-front');
|
|
70
|
+
console.log(' D - Disable AUTO bring-to-front');
|
|
71
|
+
console.log(' Ctrl+C - Exit\n');
|
|
72
|
+
|
|
73
|
+
await selector.startSelection();
|
|
74
|
+
|
|
75
|
+
// Keep running
|
|
76
|
+
process.on('SIGINT', async () => {
|
|
77
|
+
console.log('\n🛑 Stopping...');
|
|
78
|
+
await selector.cleanup();
|
|
79
|
+
process.exit(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Prevent exit
|
|
83
|
+
setInterval(() => {}, 1000);
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('❌ Error:', error.message);
|
|
87
|
+
console.error(error.stack);
|
|
88
|
+
} finally {
|
|
89
|
+
// Cleanup
|
|
90
|
+
await selector.cleanup();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function testAutoBringToFront() {
|
|
95
|
+
console.log('🤖 Auto Bring To Front Test');
|
|
96
|
+
console.log('============================\n');
|
|
97
|
+
|
|
98
|
+
const selector = new WindowSelector();
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Enable auto bring-to-front
|
|
102
|
+
console.log('🔄 Enabling auto bring-to-front...');
|
|
103
|
+
selector.setBringToFrontEnabled(true);
|
|
104
|
+
|
|
105
|
+
let windowCount = 0;
|
|
106
|
+
|
|
107
|
+
selector.on('windowEntered', (window) => {
|
|
108
|
+
windowCount++;
|
|
109
|
+
console.log(`\n[${windowCount}] 🔝 AUTO FRONT: ${window.appName} - "${window.title}"`);
|
|
110
|
+
console.log(` 📍 Position: (${window.x}, ${window.y})`);
|
|
111
|
+
console.log(` 📏 Size: ${window.width} × ${window.height}`);
|
|
112
|
+
console.log(' 🚀 Window should automatically come to front!');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log('✅ Auto bring-to-front enabled');
|
|
116
|
+
console.log('🖱️ Move cursor over different windows');
|
|
117
|
+
console.log('🔝 Each window should automatically come to front');
|
|
118
|
+
console.log('⏱️ Test will run for 30 seconds\n');
|
|
119
|
+
|
|
120
|
+
await selector.startSelection();
|
|
121
|
+
|
|
122
|
+
// Auto-stop after 30 seconds
|
|
123
|
+
setTimeout(async () => {
|
|
124
|
+
console.log('\n⏰ Test completed!');
|
|
125
|
+
console.log(`📊 Total windows auto-focused: ${windowCount}`);
|
|
126
|
+
selector.setBringToFrontEnabled(false);
|
|
127
|
+
await selector.cleanup();
|
|
128
|
+
process.exit(0);
|
|
129
|
+
}, 30000);
|
|
130
|
+
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('❌ Error:', error.message);
|
|
133
|
+
await selector.cleanup();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Main function
|
|
138
|
+
async function main() {
|
|
139
|
+
const args = process.argv.slice(2);
|
|
140
|
+
|
|
141
|
+
if (args.includes('--auto')) {
|
|
142
|
+
await testAutoBringToFront();
|
|
143
|
+
} else if (args.includes('--help')) {
|
|
144
|
+
console.log('Bring To Front Tests:');
|
|
145
|
+
console.log('====================');
|
|
146
|
+
console.log('node bring-to-front-test.js [option]');
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log('Options:');
|
|
149
|
+
console.log(' --manual Manual bring-to-front test (default)');
|
|
150
|
+
console.log(' --auto Auto bring-to-front test');
|
|
151
|
+
console.log(' --help Show this help');
|
|
152
|
+
} else {
|
|
153
|
+
await testBringToFront();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
main().catch(console.error);
|
|
159
|
+
}
|
package/package.json
CHANGED
package/src/window_selector.mm
CHANGED
|
@@ -15,12 +15,14 @@ static NSTimer *g_trackingTimer = nil;
|
|
|
15
15
|
static NSDictionary *g_selectedWindowInfo = nil;
|
|
16
16
|
static NSMutableArray *g_allWindows = nil;
|
|
17
17
|
static NSDictionary *g_currentWindowUnderCursor = nil;
|
|
18
|
+
static bool g_bringToFrontEnabled = true; // Default enabled
|
|
18
19
|
|
|
19
20
|
// Forward declarations
|
|
20
21
|
void cleanupWindowSelector();
|
|
21
22
|
void updateOverlay();
|
|
22
23
|
NSDictionary* getWindowUnderCursor(CGPoint point);
|
|
23
24
|
NSArray* getAllSelectableWindows();
|
|
25
|
+
bool bringWindowToFront(int windowId);
|
|
24
26
|
|
|
25
27
|
// Custom overlay view class
|
|
26
28
|
@interface WindowSelectorOverlayView : NSView
|
|
@@ -99,6 +101,112 @@ NSArray* getAllSelectableWindows();
|
|
|
99
101
|
|
|
100
102
|
static WindowSelectorDelegate *g_delegate = nil;
|
|
101
103
|
|
|
104
|
+
// Bring window to front using Accessibility API
|
|
105
|
+
bool bringWindowToFront(int windowId) {
|
|
106
|
+
@autoreleasepool {
|
|
107
|
+
@try {
|
|
108
|
+
// Method 1: Using Accessibility API (most reliable)
|
|
109
|
+
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
110
|
+
if (!systemWide) return false;
|
|
111
|
+
|
|
112
|
+
CFArrayRef windowList = NULL;
|
|
113
|
+
AXError error = AXUIElementCopyAttributeValue(systemWide, kAXWindowsAttribute, (CFTypeRef*)&windowList);
|
|
114
|
+
|
|
115
|
+
if (error == kAXErrorSuccess && windowList) {
|
|
116
|
+
CFIndex windowCount = CFArrayGetCount(windowList);
|
|
117
|
+
|
|
118
|
+
for (CFIndex i = 0; i < windowCount; i++) {
|
|
119
|
+
AXUIElementRef windowElement = (AXUIElementRef)CFArrayGetValueAtIndex(windowList, i);
|
|
120
|
+
|
|
121
|
+
// Get window ID by comparing with CGWindowList
|
|
122
|
+
// Since _AXUIElementGetWindow is not available, we'll use app PID approach
|
|
123
|
+
pid_t windowPid;
|
|
124
|
+
error = AXUIElementGetPid(windowElement, &windowPid);
|
|
125
|
+
|
|
126
|
+
if (error == kAXErrorSuccess) {
|
|
127
|
+
// Get window info for this PID from CGWindowList
|
|
128
|
+
CFArrayRef cgWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
|
|
129
|
+
if (cgWindowList) {
|
|
130
|
+
NSArray *windowArray = (__bridge NSArray *)cgWindowList;
|
|
131
|
+
|
|
132
|
+
for (NSDictionary *windowInfo in windowArray) {
|
|
133
|
+
NSNumber *cgWindowId = [windowInfo objectForKey:(NSString *)kCGWindowNumber];
|
|
134
|
+
NSNumber *processId = [windowInfo objectForKey:(NSString *)kCGWindowOwnerPID];
|
|
135
|
+
|
|
136
|
+
if ([cgWindowId intValue] == windowId && [processId intValue] == windowPid) {
|
|
137
|
+
// Found the window, bring it to front
|
|
138
|
+
NSLog(@"🔝 BRINGING TO FRONT: Window ID %d (PID: %d)", windowId, windowPid);
|
|
139
|
+
|
|
140
|
+
// Method 1: Raise specific window (not the whole app)
|
|
141
|
+
error = AXUIElementPerformAction(windowElement, kAXRaiseAction);
|
|
142
|
+
if (error == kAXErrorSuccess) {
|
|
143
|
+
NSLog(@" ✅ Specific window raised successfully");
|
|
144
|
+
} else {
|
|
145
|
+
NSLog(@" ⚠️ Raise action failed: %d", error);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Method 2: Focus specific window (not main window)
|
|
149
|
+
error = AXUIElementSetAttributeValue(windowElement, kAXFocusedAttribute, kCFBooleanTrue);
|
|
150
|
+
if (error == kAXErrorSuccess) {
|
|
151
|
+
NSLog(@" ✅ Specific window focused");
|
|
152
|
+
} else {
|
|
153
|
+
NSLog(@" ⚠️ Focus failed: %d", error);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
CFRelease(cgWindowList);
|
|
157
|
+
CFRelease(windowList);
|
|
158
|
+
CFRelease(systemWide);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
CFRelease(cgWindowList);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
CFRelease(windowList);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
CFRelease(systemWide);
|
|
170
|
+
|
|
171
|
+
// Method 2: Light activation fallback (minimal app activation)
|
|
172
|
+
NSLog(@" 🔄 Trying minimal activation for window %d", windowId);
|
|
173
|
+
|
|
174
|
+
// Get window info to find the process
|
|
175
|
+
CFArrayRef cgWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
|
|
176
|
+
if (cgWindowList) {
|
|
177
|
+
NSArray *windowArray = (__bridge NSArray *)cgWindowList;
|
|
178
|
+
|
|
179
|
+
for (NSDictionary *windowInfo in windowArray) {
|
|
180
|
+
NSNumber *cgWindowId = [windowInfo objectForKey:(NSString *)kCGWindowNumber];
|
|
181
|
+
if ([cgWindowId intValue] == windowId) {
|
|
182
|
+
// Get process ID
|
|
183
|
+
NSNumber *processId = [windowInfo objectForKey:(NSString *)kCGWindowOwnerPID];
|
|
184
|
+
if (processId) {
|
|
185
|
+
// Light activation - only bring app to front, don't activate all windows
|
|
186
|
+
NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:[processId intValue]];
|
|
187
|
+
if (app) {
|
|
188
|
+
// Use NSApplicationActivateIgnoringOtherApps only (no NSApplicationActivateAllWindows)
|
|
189
|
+
[app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
|
190
|
+
NSLog(@" ✅ App minimally activated: PID %d (specific window should be frontmost)", [processId intValue]);
|
|
191
|
+
CFRelease(cgWindowList);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
CFRelease(cgWindowList);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return false;
|
|
202
|
+
|
|
203
|
+
} @catch (NSException *exception) {
|
|
204
|
+
NSLog(@"❌ Error bringing window to front: %@", exception);
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
102
210
|
// Get all selectable windows
|
|
103
211
|
NSArray* getAllSelectableWindows() {
|
|
104
212
|
@autoreleasepool {
|
|
@@ -216,6 +324,17 @@ void updateOverlay() {
|
|
|
216
324
|
overlayFrame.origin.x, overlayFrame.origin.y,
|
|
217
325
|
overlayFrame.size.width, overlayFrame.size.height,
|
|
218
326
|
[g_overlayWindow level]);
|
|
327
|
+
|
|
328
|
+
// Bring window to front if enabled
|
|
329
|
+
if (g_bringToFrontEnabled) {
|
|
330
|
+
int windowId = [[windowUnderCursor objectForKey:@"id"] intValue];
|
|
331
|
+
if (windowId > 0) {
|
|
332
|
+
bool success = bringWindowToFront(windowId);
|
|
333
|
+
if (!success) {
|
|
334
|
+
NSLog(@" ⚠️ Failed to bring window to front");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
219
338
|
[g_overlayWindow setFrame:overlayFrame display:YES];
|
|
220
339
|
|
|
221
340
|
// Update overlay view window info
|
|
@@ -441,6 +560,43 @@ Napi::Value GetSelectedWindowInfo(const Napi::CallbackInfo& info) {
|
|
|
441
560
|
}
|
|
442
561
|
}
|
|
443
562
|
|
|
563
|
+
// NAPI Function: Bring Window To Front
|
|
564
|
+
Napi::Value BringWindowToFront(const Napi::CallbackInfo& info) {
|
|
565
|
+
Napi::Env env = info.Env();
|
|
566
|
+
|
|
567
|
+
if (info.Length() < 1) {
|
|
568
|
+
Napi::TypeError::New(env, "Window ID required").ThrowAsJavaScriptException();
|
|
569
|
+
return env.Null();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
int windowId = info[0].As<Napi::Number>().Int32Value();
|
|
573
|
+
|
|
574
|
+
@try {
|
|
575
|
+
bool success = bringWindowToFront(windowId);
|
|
576
|
+
return Napi::Boolean::New(env, success);
|
|
577
|
+
|
|
578
|
+
} @catch (NSException *exception) {
|
|
579
|
+
return Napi::Boolean::New(env, false);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// NAPI Function: Enable/Disable Auto Bring To Front
|
|
584
|
+
Napi::Value SetBringToFrontEnabled(const Napi::CallbackInfo& info) {
|
|
585
|
+
Napi::Env env = info.Env();
|
|
586
|
+
|
|
587
|
+
if (info.Length() < 1) {
|
|
588
|
+
Napi::TypeError::New(env, "Boolean value required").ThrowAsJavaScriptException();
|
|
589
|
+
return env.Null();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
bool enabled = info[0].As<Napi::Boolean>();
|
|
593
|
+
g_bringToFrontEnabled = enabled;
|
|
594
|
+
|
|
595
|
+
NSLog(@"🔄 Auto bring-to-front: %s", enabled ? "ENABLED" : "DISABLED");
|
|
596
|
+
|
|
597
|
+
return Napi::Boolean::New(env, true);
|
|
598
|
+
}
|
|
599
|
+
|
|
444
600
|
// NAPI Function: Get Window Selection Status
|
|
445
601
|
Napi::Value GetWindowSelectionStatus(const Napi::CallbackInfo& info) {
|
|
446
602
|
Napi::Env env = info.Env();
|
|
@@ -477,6 +633,8 @@ Napi::Object InitWindowSelector(Napi::Env env, Napi::Object exports) {
|
|
|
477
633
|
exports.Set("stopWindowSelection", Napi::Function::New(env, StopWindowSelection));
|
|
478
634
|
exports.Set("getSelectedWindowInfo", Napi::Function::New(env, GetSelectedWindowInfo));
|
|
479
635
|
exports.Set("getWindowSelectionStatus", Napi::Function::New(env, GetWindowSelectionStatus));
|
|
636
|
+
exports.Set("bringWindowToFront", Napi::Function::New(env, BringWindowToFront));
|
|
637
|
+
exports.Set("setBringToFrontEnabled", Napi::Function::New(env, SetBringToFrontEnabled));
|
|
480
638
|
|
|
481
639
|
return exports;
|
|
482
640
|
}
|
package/window-selector.js
CHANGED
|
@@ -214,6 +214,38 @@ class WindowSelector extends EventEmitter {
|
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Pencereyi en öne getirir (focus yapar)
|
|
219
|
+
* @param {number} windowId - Window ID
|
|
220
|
+
* @returns {Promise<boolean>} Success/failure
|
|
221
|
+
*/
|
|
222
|
+
async bringWindowToFront(windowId) {
|
|
223
|
+
if (!windowId) {
|
|
224
|
+
throw new Error("Window ID is required");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const success = nativeBinding.bringWindowToFront(windowId);
|
|
229
|
+
return success;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
throw new Error(`Failed to bring window to front: ${error.message}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Otomatik pencere en öne getirme özelliğini aktif/pasif yapar
|
|
237
|
+
* Cursor hangi pencereye gelirse otomatik olarak en öne getirir
|
|
238
|
+
* @param {boolean} enabled - Enable/disable auto bring to front
|
|
239
|
+
*/
|
|
240
|
+
setBringToFrontEnabled(enabled) {
|
|
241
|
+
try {
|
|
242
|
+
nativeBinding.setBringToFrontEnabled(enabled);
|
|
243
|
+
console.log(`🔄 Auto bring-to-front: ${enabled ? 'ENABLED' : 'DISABLED'}`);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
throw new Error(`Failed to set bring to front: ${error.message}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
217
249
|
/**
|
|
218
250
|
* Cleanup - tüm kaynakları temizle
|
|
219
251
|
*/
|