node-mac-recorder 1.3.0 → 1.5.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.
@@ -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,47 @@ if (!permissions.screenRecording) {
309
334
 
310
335
  ## 🌟 Gelişmiş Örnekler
311
336
 
337
+ ### Auto Bring-to-Front (DEFAULT - Otomatik Focus)
338
+ ```javascript
339
+ const WindowSelector = require('./window-selector');
340
+
341
+ async function autoBringToFront() {
342
+ const selector = new WindowSelector();
343
+
344
+ // Auto bring-to-front varsayılan olarak AÇIK
345
+ // (Kapatmak için: selector.setBringToFrontEnabled(false))
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
+ console.log('💡 Only the specific window focuses, not all windows of the app');
354
+ }
355
+ ```
356
+
357
+ ### Manuel Window Focus
358
+ ```javascript
359
+ const WindowSelector = require('./window-selector');
360
+
361
+ async function manualFocus() {
362
+ const selector = new WindowSelector();
363
+
364
+ selector.on('windowEntered', async (window) => {
365
+ console.log(`Found: ${window.appName} - "${window.title}"`);
366
+
367
+ // Manuel olarak pencereyi en öne getir
368
+ const success = await selector.bringWindowToFront(window.id);
369
+ if (success) {
370
+ console.log('✅ Window brought to front!');
371
+ }
372
+ });
373
+
374
+ await selector.startSelection();
375
+ }
376
+ ```
377
+
312
378
  ### Otomatik Pencere Kaydı
313
379
  ```javascript
314
380
  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/debug-test.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function debugTest() {
6
+ console.log('🔍 Debug Window Selector Test');
7
+ console.log('==============================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ // İzinleri kontrol et
13
+ console.log('📋 Checking permissions...');
14
+ const permissions = await selector.checkPermissions();
15
+ console.log('Permissions:', JSON.stringify(permissions, null, 2));
16
+
17
+ if (!permissions.screenRecording || !permissions.accessibility) {
18
+ console.log('\n❌ MISSING PERMISSIONS!');
19
+ console.log('Please enable in System Preferences > Security & Privacy:');
20
+ console.log(' ✓ Screen Recording - Add Terminal/your IDE');
21
+ console.log(' ✓ Accessibility - Add Terminal/your IDE');
22
+ console.log('\nAfter enabling permissions, restart this test.');
23
+ return;
24
+ }
25
+
26
+ console.log('✅ Permissions OK\n');
27
+
28
+ // Debug mode ile başlat
29
+ console.log('🚀 Starting selection with debug info...');
30
+ await selector.startSelection();
31
+
32
+ let windowCount = 0;
33
+
34
+ selector.on('windowEntered', (window) => {
35
+ windowCount++;
36
+ console.log(`\n[${windowCount}] 🎯 WINDOW DETECTED:`);
37
+ console.log(` App: ${window.appName}`);
38
+ console.log(` Title: "${window.title}"`);
39
+ console.log(` ID: ${window.id}`);
40
+ console.log(` Position: (${window.x}, ${window.y})`);
41
+ console.log(` Size: ${window.width} × ${window.height}`);
42
+ console.log(` 🔝 Should auto-focus now...`);
43
+ });
44
+
45
+ selector.on('windowLeft', (window) => {
46
+ console.log(`\n🚪 LEFT WINDOW: ${window.appName} - "${window.title}"`);
47
+ });
48
+
49
+ selector.on('error', (error) => {
50
+ console.error('\n❌ ERROR:', error.message);
51
+ });
52
+
53
+ console.log('📋 Test Instructions:');
54
+ console.log(' 1. Move cursor over different application windows');
55
+ console.log(' 2. You should see:');
56
+ console.log(' - Blue overlay rectangle around windows');
57
+ console.log(' - "Select Window" button in center');
58
+ console.log(' - Windows automatically coming to front');
59
+ console.log(' 3. If overlay not visible, check permissions');
60
+ console.log(' 4. Press Ctrl+C to exit\n');
61
+ console.log('🖱️ START MOVING CURSOR NOW...\n');
62
+
63
+ // Status monitoring
64
+ let statusCount = 0;
65
+ setInterval(() => {
66
+ statusCount++;
67
+ const status = selector.getStatus();
68
+
69
+ if (statusCount % 50 === 0) { // Every 5 seconds
70
+ console.log(`⏱️ Status Check #${statusCount/50}:`);
71
+ console.log(` - Selecting: ${status.isSelecting}`);
72
+ console.log(` - Windows found: ${status.nativeStatus?.windowCount || 0}`);
73
+ console.log(` - Overlay active: ${status.nativeStatus?.hasOverlay || false}`);
74
+ if (status.nativeStatus?.currentWindow) {
75
+ console.log(` - Current: ${status.nativeStatus.currentWindow.appName}`);
76
+ }
77
+ console.log('');
78
+ }
79
+ }, 100);
80
+
81
+ } catch (error) {
82
+ console.error('❌ Fatal Error:', error.message);
83
+ console.error(error.stack);
84
+ }
85
+ }
86
+
87
+ // Handle Ctrl+C gracefully
88
+ process.on('SIGINT', async () => {
89
+ console.log('\n\n🛑 Stopping debug test...');
90
+ process.exit(0);
91
+ });
92
+
93
+ if (require.main === module) {
94
+ debugTest();
95
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require('./window-selector');
4
+
5
+ async function testDefaultAutoBringToFront() {
6
+ console.log('🔝 Default Auto Bring-To-Front Test');
7
+ console.log('====================================\n');
8
+
9
+ const selector = new WindowSelector();
10
+
11
+ try {
12
+ console.log('🚀 Starting window selector with DEFAULT auto bring-to-front...');
13
+ console.log('(Auto bring-to-front is now enabled by default)\n');
14
+
15
+ console.log('📋 Instructions:');
16
+ console.log(' • Move cursor over different windows');
17
+ console.log(' • Each window should automatically come to front');
18
+ console.log(' • Only the specific window should focus (not whole app)');
19
+ console.log(' • Press D to disable auto mode');
20
+ console.log(' • Press E to re-enable auto mode');
21
+ console.log(' • Press Ctrl+C to exit\n');
22
+
23
+ let windowCount = 0;
24
+ let lastWindowId = null;
25
+
26
+ selector.on('windowEntered', (window) => {
27
+ if (window.id !== lastWindowId) {
28
+ windowCount++;
29
+ console.log(`[${windowCount}] 🎯 WINDOW: ${window.appName} - "${window.title}"`);
30
+ console.log(` 📍 Position: (${window.x}, ${window.y})`);
31
+ console.log(` 📏 Size: ${window.width} × ${window.height}`);
32
+ console.log(` 🔝 Should auto-focus THIS specific window only!`);
33
+ lastWindowId = window.id;
34
+ }
35
+ });
36
+
37
+ selector.on('windowLeft', (window) => {
38
+ console.log(`🚪 Left: ${window.appName} - "${window.title}"\n`);
39
+ });
40
+
41
+ // Keyboard controls
42
+ const readline = require('readline');
43
+ readline.emitKeypressEvents(process.stdin);
44
+ if (process.stdin.isTTY) {
45
+ process.stdin.setRawMode(true);
46
+ }
47
+
48
+ process.stdin.on('keypress', async (str, key) => {
49
+ if (key.name === 'd') {
50
+ console.log('\n🔄 Disabling auto bring-to-front...');
51
+ selector.setBringToFrontEnabled(false);
52
+ console.log(' ✅ Auto mode OFF - Windows will not auto-focus');
53
+ } else if (key.name === 'e') {
54
+ console.log('\n🔄 Enabling auto bring-to-front...');
55
+ selector.setBringToFrontEnabled(true);
56
+ console.log(' ✅ Auto mode ON - Windows will auto-focus again');
57
+ } else if (key.ctrl && key.name === 'c') {
58
+ console.log('\n\n🛑 Stopping...');
59
+ console.log(`📊 Total windows encountered: ${windowCount}`);
60
+ await selector.cleanup();
61
+ process.exit(0);
62
+ }
63
+ });
64
+
65
+ await selector.startSelection();
66
+
67
+ // Status update every 10 seconds
68
+ setInterval(() => {
69
+ console.log(`\n⏱️ Status: ${windowCount} windows encountered so far`);
70
+ console.log(' (Continue moving cursor over windows to test auto-focus)');
71
+ }, 10000);
72
+
73
+ // Keep running
74
+ setInterval(() => {}, 1000);
75
+
76
+ } catch (error) {
77
+ console.error('❌ Error:', error.message);
78
+ console.error(error.stack);
79
+ } finally {
80
+ await selector.cleanup();
81
+ }
82
+ }
83
+
84
+ if (require.main === module) {
85
+ testDefaultAutoBringToFront();
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -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
@@ -33,8 +35,8 @@ NSArray* getAllSelectableWindows();
33
35
  self = [super initWithFrame:frameRect];
34
36
  if (self) {
35
37
  self.wantsLayer = YES;
36
- self.layer.backgroundColor = [[NSColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.5] CGColor];
37
- self.layer.borderColor = [[NSColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.9] CGColor];
38
+ self.layer.backgroundColor = [[NSColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.6] CGColor];
39
+ self.layer.borderColor = [[NSColor colorWithRed:0.0 green:0.4 blue:0.8 alpha:0.9] CGColor];
38
40
  self.layer.borderWidth = 5.0;
39
41
  self.layer.cornerRadius = 8.0;
40
42
  }
@@ -47,11 +49,11 @@ NSArray* getAllSelectableWindows();
47
49
  if (!self.windowInfo) return;
48
50
 
49
51
  // Background with transparency
50
- [[NSColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.5] setFill];
52
+ [[NSColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.6] setFill];
51
53
  NSRectFill(dirtyRect);
52
54
 
53
55
  // Border
54
- [[NSColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.9] setStroke];
56
+ [[NSColor colorWithRed:0.0 green:0.4 blue:0.8 alpha:0.9] setStroke];
55
57
  NSBezierPath *border = [NSBezierPath bezierPathWithRoundedRect:self.bounds xRadius:8 yRadius:8];
56
58
  [border setLineWidth:3.0];
57
59
  [border stroke];
@@ -65,14 +67,14 @@ NSArray* getAllSelectableWindows();
65
67
  [style setAlignment:NSTextAlignmentCenter];
66
68
 
67
69
  NSDictionary *attributes = @{
68
- NSFontAttributeName: [NSFont systemFontOfSize:14 weight:NSFontWeightMedium],
70
+ NSFontAttributeName: [NSFont systemFontOfSize:21 weight:NSFontWeightMedium],
69
71
  NSForegroundColorAttributeName: [NSColor whiteColor],
70
72
  NSParagraphStyleAttributeName: style,
71
73
  NSStrokeColorAttributeName: [NSColor blackColor],
72
74
  NSStrokeWidthAttributeName: @(-2.0)
73
75
  };
74
76
 
75
- NSRect textRect = NSMakeRect(10, self.bounds.size.height - 60, self.bounds.size.width - 20, 50);
77
+ NSRect textRect = NSMakeRect(10, self.bounds.size.height - 90, self.bounds.size.width - 20, 80);
76
78
  [infoText drawInRect:textRect withAttributes:attributes];
77
79
  }
78
80
 
@@ -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
@@ -326,12 +445,26 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
326
445
  g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:initialFrame];
327
446
  [g_overlayWindow setContentView:g_overlayView];
328
447
 
329
- // Create select button
330
- g_selectButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
448
+ // Create select button with blue theme
449
+ g_selectButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 140, 50)];
331
450
  [g_selectButton setTitle:@"Select Window"];
332
451
  [g_selectButton setButtonType:NSButtonTypeMomentaryPushIn];
333
452
  [g_selectButton setBezelStyle:NSBezelStyleRounded];
334
- [g_selectButton setFont:[NSFont systemFontOfSize:14 weight:NSFontWeightMedium]];
453
+ [g_selectButton setFont:[NSFont systemFontOfSize:16 weight:NSFontWeightSemibold]];
454
+
455
+ // Blue themed button styling
456
+ [g_selectButton setWantsLayer:YES];
457
+ [g_selectButton.layer setBackgroundColor:[[NSColor colorWithRed:0.0 green:0.4 blue:0.8 alpha:0.9] CGColor]];
458
+ [g_selectButton.layer setCornerRadius:8.0];
459
+ [g_selectButton.layer setBorderColor:[[NSColor colorWithRed:0.0 green:0.3 blue:0.7 alpha:1.0] CGColor]];
460
+ [g_selectButton.layer setBorderWidth:2.0];
461
+ [g_selectButton setContentTintColor:[NSColor whiteColor]];
462
+
463
+ // Add shadow for better visibility
464
+ [g_selectButton.layer setShadowColor:[[NSColor blackColor] CGColor]];
465
+ [g_selectButton.layer setShadowOffset:NSMakeSize(0, -2)];
466
+ [g_selectButton.layer setShadowRadius:4.0];
467
+ [g_selectButton.layer setShadowOpacity:0.3];
335
468
 
336
469
  // Create delegate for button action and timer
337
470
  g_delegate = [[WindowSelectorDelegate alloc] init];
@@ -441,6 +574,43 @@ Napi::Value GetSelectedWindowInfo(const Napi::CallbackInfo& info) {
441
574
  }
442
575
  }
443
576
 
577
+ // NAPI Function: Bring Window To Front
578
+ Napi::Value BringWindowToFront(const Napi::CallbackInfo& info) {
579
+ Napi::Env env = info.Env();
580
+
581
+ if (info.Length() < 1) {
582
+ Napi::TypeError::New(env, "Window ID required").ThrowAsJavaScriptException();
583
+ return env.Null();
584
+ }
585
+
586
+ int windowId = info[0].As<Napi::Number>().Int32Value();
587
+
588
+ @try {
589
+ bool success = bringWindowToFront(windowId);
590
+ return Napi::Boolean::New(env, success);
591
+
592
+ } @catch (NSException *exception) {
593
+ return Napi::Boolean::New(env, false);
594
+ }
595
+ }
596
+
597
+ // NAPI Function: Enable/Disable Auto Bring To Front
598
+ Napi::Value SetBringToFrontEnabled(const Napi::CallbackInfo& info) {
599
+ Napi::Env env = info.Env();
600
+
601
+ if (info.Length() < 1) {
602
+ Napi::TypeError::New(env, "Boolean value required").ThrowAsJavaScriptException();
603
+ return env.Null();
604
+ }
605
+
606
+ bool enabled = info[0].As<Napi::Boolean>();
607
+ g_bringToFrontEnabled = enabled;
608
+
609
+ NSLog(@"🔄 Auto bring-to-front: %s", enabled ? "ENABLED" : "DISABLED");
610
+
611
+ return Napi::Boolean::New(env, true);
612
+ }
613
+
444
614
  // NAPI Function: Get Window Selection Status
445
615
  Napi::Value GetWindowSelectionStatus(const Napi::CallbackInfo& info) {
446
616
  Napi::Env env = info.Env();
@@ -477,6 +647,8 @@ Napi::Object InitWindowSelector(Napi::Env env, Napi::Object exports) {
477
647
  exports.Set("stopWindowSelection", Napi::Function::New(env, StopWindowSelection));
478
648
  exports.Set("getSelectedWindowInfo", Napi::Function::New(env, GetSelectedWindowInfo));
479
649
  exports.Set("getWindowSelectionStatus", Napi::Function::New(env, GetWindowSelectionStatus));
650
+ exports.Set("bringWindowToFront", Napi::Function::New(env, BringWindowToFront));
651
+ exports.Set("setBringToFrontEnabled", Napi::Function::New(env, SetBringToFrontEnabled));
480
652
 
481
653
  return exports;
482
654
  }
@@ -214,6 +214,41 @@ 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
+ // Only log if explicitly setting, not on startup
244
+ if (arguments.length > 0) {
245
+ console.log(`🔄 Auto bring-to-front: ${enabled ? 'ENABLED' : 'DISABLED'}`);
246
+ }
247
+ } catch (error) {
248
+ throw new Error(`Failed to set bring to front: ${error.message}`);
249
+ }
250
+ }
251
+
217
252
  /**
218
253
  * Cleanup - tüm kaynakları temizle
219
254
  */