node-mac-recorder 2.4.0 → 2.4.2
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/index.js +17 -8
- package/install.js +19 -2
- package/package.json +6 -2
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/src/mac_recorder.mm +70 -27
- package/window-selector.js +50 -34
package/index.js
CHANGED
|
@@ -5,16 +5,25 @@ const fs = require("fs");
|
|
|
5
5
|
// Native modülü yükle
|
|
6
6
|
let nativeBinding;
|
|
7
7
|
try {
|
|
8
|
-
|
|
8
|
+
// Prefer prebuild on arm64
|
|
9
|
+
if (process.platform === "darwin" && process.arch === "arm64") {
|
|
10
|
+
nativeBinding = require("./prebuilds/darwin-arm64/node.napi.node");
|
|
11
|
+
} else {
|
|
12
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
13
|
+
}
|
|
9
14
|
} catch (error) {
|
|
10
15
|
try {
|
|
11
|
-
nativeBinding = require("./build/
|
|
12
|
-
} catch (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
17
|
+
} catch (_) {
|
|
18
|
+
try {
|
|
19
|
+
nativeBinding = require("./build/Debug/mac_recorder.node");
|
|
20
|
+
} catch (debugError) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'Native module not found. Please run "npm run build" to compile the native module.\n' +
|
|
23
|
+
"Original error: " +
|
|
24
|
+
error.message
|
|
25
|
+
);
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
}
|
|
20
29
|
|
package/install.js
CHANGED
|
@@ -2,7 +2,7 @@ const { spawn } = require("child_process");
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
|
-
console.log("🔨
|
|
5
|
+
console.log("🔨 Installing node-mac-recorder...\n");
|
|
6
6
|
|
|
7
7
|
// Check if we're on macOS
|
|
8
8
|
if (process.platform !== "darwin") {
|
|
@@ -10,7 +10,24 @@ if (process.platform !== "darwin") {
|
|
|
10
10
|
process.exit(1);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// Prefer prebuilds on supported platforms
|
|
14
|
+
const prebuildPath = path.join(
|
|
15
|
+
__dirname,
|
|
16
|
+
"prebuilds",
|
|
17
|
+
`darwin-${process.arch}`,
|
|
18
|
+
"node.napi.node"
|
|
19
|
+
);
|
|
20
|
+
if (
|
|
21
|
+
process.platform === "darwin" &&
|
|
22
|
+
process.arch === "arm64" &&
|
|
23
|
+
fs.existsSync(prebuildPath)
|
|
24
|
+
) {
|
|
25
|
+
console.log("✅ Using prebuilt binary:", prebuildPath);
|
|
26
|
+
console.log("🎉 node-mac-recorder is ready to use (no compilation needed)");
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback to building from source
|
|
14
31
|
console.log("🔍 Checking Xcode Command Line Tools...");
|
|
15
32
|
const xcodebuild = spawn("xcode-select", ["--print-path"], { stdio: "pipe" });
|
|
16
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-mac-recorder",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "Native macOS screen recording package for Node.js applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
"install": "node install.js",
|
|
38
38
|
"build": "node-gyp build",
|
|
39
39
|
"rebuild": "node-gyp rebuild",
|
|
40
|
+
"prebuild:node-arm64": "prebuildify --platform darwin --arch arm64 --napi --strip",
|
|
41
|
+
"prebuild:electron-arm64": "prebuildify --platform darwin --arch arm64 --napi --strip --targets electron@27.0.0",
|
|
42
|
+
"prebuild:all-arm64": "npm run prebuild:node-arm64 && npm run prebuild:electron-arm64",
|
|
40
43
|
"clean": "node-gyp clean",
|
|
41
44
|
"test:window-selector": "node window-selector-test.js",
|
|
42
45
|
"example:window-selector": "node examples/window-selector-example.js"
|
|
@@ -45,7 +48,8 @@
|
|
|
45
48
|
"node-addon-api": "^7.0.0"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
|
-
"node-gyp": "^10.0.0"
|
|
51
|
+
"node-gyp": "^10.0.0",
|
|
52
|
+
"prebuildify": "^6.0.1"
|
|
49
53
|
},
|
|
50
54
|
"gypfile": true
|
|
51
55
|
}
|
|
Binary file
|
package/src/mac_recorder.mm
CHANGED
|
@@ -124,29 +124,71 @@ API_AVAILABLE(macos(12.3))
|
|
|
124
124
|
static SCStream *g_scStream = nil;
|
|
125
125
|
static SCKRecorderDelegate *g_scDelegate = nil;
|
|
126
126
|
static bool g_isRecording = false;
|
|
127
|
+
static BOOL g_screenOutputAttached = NO;
|
|
128
|
+
static BOOL g_audioOutputAttached = NO;
|
|
129
|
+
static dispatch_queue_t g_outputQueue = NULL; // use a dedicated serial queue for sample handling
|
|
127
130
|
|
|
128
131
|
// Helper function to cleanup ScreenCaptureKit recording resources
|
|
129
132
|
void cleanupSCKRecording() {
|
|
130
133
|
NSLog(@"🛑 Cleaning up ScreenCaptureKit recording");
|
|
131
|
-
|
|
134
|
+
|
|
135
|
+
// Detach outputs first to prevent further callbacks into the delegate
|
|
136
|
+
if (g_scStream && g_scDelegate) {
|
|
137
|
+
NSError *rmError = nil;
|
|
138
|
+
if (g_screenOutputAttached) {
|
|
139
|
+
[g_scStream removeStreamOutput:g_scDelegate type:SCStreamOutputTypeScreen error:&rmError];
|
|
140
|
+
g_screenOutputAttached = NO;
|
|
141
|
+
}
|
|
142
|
+
if (g_audioOutputAttached) {
|
|
143
|
+
rmError = nil;
|
|
144
|
+
[g_scStream removeStreamOutput:g_scDelegate type:SCStreamOutputTypeAudio error:&rmError];
|
|
145
|
+
g_audioOutputAttached = NO;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
132
149
|
if (g_scStream) {
|
|
133
150
|
NSLog(@"🛑 Stopping SCStream");
|
|
134
|
-
|
|
151
|
+
SCStream *streamToStop = g_scStream; // keep local until stop completes
|
|
152
|
+
[streamToStop stopCaptureWithCompletionHandler:^(NSError * _Nullable error) {
|
|
135
153
|
if (error) {
|
|
136
154
|
NSLog(@"❌ Error stopping SCStream: %@", error.localizedDescription);
|
|
137
155
|
} else {
|
|
138
156
|
NSLog(@"✅ SCStream stopped successfully");
|
|
139
157
|
}
|
|
158
|
+
|
|
159
|
+
// Finish writer after stream has stopped to ensure no further buffers arrive
|
|
160
|
+
if (g_scDelegate && g_scDelegate.assetWriter && g_scDelegate.isWriting) {
|
|
161
|
+
NSLog(@"🛑 Finishing asset writer (status: %ld)", (long)g_scDelegate.assetWriter.status);
|
|
162
|
+
g_scDelegate.isWriting = NO;
|
|
163
|
+
|
|
164
|
+
if (g_scDelegate.assetWriter.status == AVAssetWriterStatusWriting) {
|
|
165
|
+
if (g_scDelegate.videoInput) {
|
|
166
|
+
[g_scDelegate.videoInput markAsFinished];
|
|
167
|
+
}
|
|
168
|
+
if (g_scDelegate.audioInput) {
|
|
169
|
+
[g_scDelegate.audioInput markAsFinished];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
[g_scDelegate.assetWriter finishWritingWithCompletionHandler:^{
|
|
173
|
+
NSLog(@"✅ Asset writer finished. Status: %ld", (long)g_scDelegate.assetWriter.status);
|
|
174
|
+
if (g_scDelegate.assetWriter.error) {
|
|
175
|
+
NSLog(@"❌ Asset writer error: %@", g_scDelegate.assetWriter.error.localizedDescription);
|
|
176
|
+
}
|
|
177
|
+
}];
|
|
178
|
+
} else if (g_scDelegate.assetWriter.status == AVAssetWriterStatusFailed) {
|
|
179
|
+
NSLog(@"❌ Asset writer failed: %@", g_scDelegate.assetWriter.error.localizedDescription);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
g_isRecording = false;
|
|
184
|
+
g_scStream = nil;
|
|
185
|
+
g_scDelegate = nil;
|
|
140
186
|
}];
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (g_scDelegate) {
|
|
145
|
-
if (g_scDelegate.assetWriter && g_scDelegate.isWriting) {
|
|
187
|
+
} else {
|
|
188
|
+
// No stream, just finalize writer if needed
|
|
189
|
+
if (g_scDelegate && g_scDelegate.assetWriter && g_scDelegate.isWriting) {
|
|
146
190
|
NSLog(@"🛑 Finishing asset writer (status: %ld)", (long)g_scDelegate.assetWriter.status);
|
|
147
191
|
g_scDelegate.isWriting = NO;
|
|
148
|
-
|
|
149
|
-
// Only mark inputs as finished if asset writer is actually writing
|
|
150
192
|
if (g_scDelegate.assetWriter.status == AVAssetWriterStatusWriting) {
|
|
151
193
|
if (g_scDelegate.videoInput) {
|
|
152
194
|
[g_scDelegate.videoInput markAsFinished];
|
|
@@ -154,23 +196,12 @@ void cleanupSCKRecording() {
|
|
|
154
196
|
if (g_scDelegate.audioInput) {
|
|
155
197
|
[g_scDelegate.audioInput markAsFinished];
|
|
156
198
|
}
|
|
157
|
-
|
|
158
|
-
[g_scDelegate.assetWriter finishWritingWithCompletionHandler:^{
|
|
159
|
-
NSLog(@"✅ Asset writer finished. Status: %ld", (long)g_scDelegate.assetWriter.status);
|
|
160
|
-
if (g_scDelegate.assetWriter.error) {
|
|
161
|
-
NSLog(@"❌ Asset writer error: %@", g_scDelegate.assetWriter.error.localizedDescription);
|
|
162
|
-
}
|
|
163
|
-
}];
|
|
164
|
-
} else {
|
|
165
|
-
NSLog(@"⚠️ Asset writer not in writing status, cannot finish normally");
|
|
166
|
-
if (g_scDelegate.assetWriter.status == AVAssetWriterStatusFailed) {
|
|
167
|
-
NSLog(@"❌ Asset writer failed: %@", g_scDelegate.assetWriter.error.localizedDescription);
|
|
168
|
-
}
|
|
199
|
+
[g_scDelegate.assetWriter finishWritingWithCompletionHandler:^{}];
|
|
169
200
|
}
|
|
170
201
|
}
|
|
202
|
+
g_isRecording = false;
|
|
171
203
|
g_scDelegate = nil;
|
|
172
204
|
}
|
|
173
|
-
g_isRecording = false;
|
|
174
205
|
}
|
|
175
206
|
|
|
176
207
|
// Check if ScreenCaptureKit is available
|
|
@@ -184,7 +215,7 @@ bool isScreenCaptureKitAvailable() {
|
|
|
184
215
|
// NAPI Function: Start Recording with ScreenCaptureKit
|
|
185
216
|
Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
186
217
|
Napi::Env env = info.Env();
|
|
187
|
-
|
|
218
|
+
@autoreleasepool {
|
|
188
219
|
if (!isScreenCaptureKitAvailable()) {
|
|
189
220
|
NSLog(@"ScreenCaptureKit requires macOS 12.3 or later");
|
|
190
221
|
return Napi::Boolean::New(env, false);
|
|
@@ -472,19 +503,22 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
472
503
|
}
|
|
473
504
|
}
|
|
474
505
|
|
|
475
|
-
// Create
|
|
476
|
-
|
|
477
|
-
|
|
506
|
+
// Create a dedicated serial queue for output callbacks
|
|
507
|
+
if (g_outputQueue == NULL) {
|
|
508
|
+
g_outputQueue = dispatch_queue_create("com.node-mac-recorder.stream-output", DISPATCH_QUEUE_SERIAL);
|
|
509
|
+
}
|
|
510
|
+
|
|
478
511
|
// Create and start stream first
|
|
479
512
|
g_scStream = [[SCStream alloc] initWithFilter:contentFilter configuration:config delegate:g_scDelegate];
|
|
480
513
|
|
|
481
514
|
// Attach outputs to actually receive sample buffers
|
|
482
515
|
NSLog(@"✅ Setting up stream output callback for sample buffers");
|
|
483
|
-
dispatch_queue_t outputQueue =
|
|
516
|
+
dispatch_queue_t outputQueue = g_outputQueue;
|
|
484
517
|
NSError *outputError = nil;
|
|
485
518
|
BOOL addedScreenOutput = [g_scStream addStreamOutput:g_scDelegate type:SCStreamOutputTypeScreen sampleHandlerQueue:outputQueue error:&outputError];
|
|
486
519
|
if (addedScreenOutput) {
|
|
487
520
|
NSLog(@"✅ Screen output attached to SCStream");
|
|
521
|
+
g_screenOutputAttached = YES;
|
|
488
522
|
} else {
|
|
489
523
|
NSLog(@"❌ Failed to attach screen output to SCStream: %@", outputError.localizedDescription);
|
|
490
524
|
}
|
|
@@ -493,6 +527,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
493
527
|
BOOL addedAudioOutput = [g_scStream addStreamOutput:g_scDelegate type:SCStreamOutputTypeAudio sampleHandlerQueue:outputQueue error:&outputError];
|
|
494
528
|
if (addedAudioOutput) {
|
|
495
529
|
NSLog(@"✅ Audio output attached to SCStream");
|
|
530
|
+
g_audioOutputAttached = YES;
|
|
496
531
|
} else {
|
|
497
532
|
NSLog(@"⚠️ Failed to attach audio output to SCStream (audio may be disabled): %@", outputError.localizedDescription);
|
|
498
533
|
}
|
|
@@ -546,6 +581,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
546
581
|
|
|
547
582
|
NSLog(@"🎬 Recording initialized successfully");
|
|
548
583
|
return Napi::Boolean::New(env, true);
|
|
584
|
+
}
|
|
549
585
|
}
|
|
550
586
|
|
|
551
587
|
// NAPI Function: Stop Recording
|
|
@@ -560,6 +596,12 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
|
|
|
560
596
|
return Napi::Boolean::New(env, true);
|
|
561
597
|
}
|
|
562
598
|
|
|
599
|
+
// NAPI Function: Get Recording Status (for JS compatibility)
|
|
600
|
+
Napi::Value GetRecordingStatus(const Napi::CallbackInfo& info) {
|
|
601
|
+
Napi::Env env = info.Env();
|
|
602
|
+
return Napi::Boolean::New(env, g_isRecording);
|
|
603
|
+
}
|
|
604
|
+
|
|
563
605
|
// NAPI Function: Get Recording Status
|
|
564
606
|
Napi::Value IsRecording(const Napi::CallbackInfo& info) {
|
|
565
607
|
Napi::Env env = info.Env();
|
|
@@ -741,6 +783,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
741
783
|
exports.Set("startRecording", Napi::Function::New(env, StartRecording));
|
|
742
784
|
exports.Set("stopRecording", Napi::Function::New(env, StopRecording));
|
|
743
785
|
exports.Set("isRecording", Napi::Function::New(env, IsRecording));
|
|
786
|
+
exports.Set("getRecordingStatus", Napi::Function::New(env, GetRecordingStatus));
|
|
744
787
|
exports.Set("getDisplays", Napi::Function::New(env, GetDisplays));
|
|
745
788
|
exports.Set("getWindows", Napi::Function::New(env, GetWindows));
|
|
746
789
|
exports.Set("checkPermissions", Napi::Function::New(env, CheckPermissions));
|
package/window-selector.js
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
const { EventEmitter } = require("events");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
|
|
4
|
-
// Native modülü yükle
|
|
4
|
+
// Native modülü yükle (arm64 prebuild öncelikli)
|
|
5
5
|
let nativeBinding;
|
|
6
6
|
try {
|
|
7
|
-
|
|
7
|
+
if (process.platform === "darwin" && process.arch === "arm64") {
|
|
8
|
+
nativeBinding = require("./prebuilds/darwin-arm64/node.napi.node");
|
|
9
|
+
} else {
|
|
10
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
11
|
+
}
|
|
8
12
|
} catch (error) {
|
|
9
13
|
try {
|
|
10
|
-
nativeBinding = require("./build/
|
|
11
|
-
} catch (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
15
|
+
} catch (_) {
|
|
16
|
+
try {
|
|
17
|
+
nativeBinding = require("./build/Debug/mac_recorder.node");
|
|
18
|
+
} catch (debugError) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Native module not found. Please run "npm run build" to compile the native module.\n' +
|
|
21
|
+
"Original error: " +
|
|
22
|
+
error.message
|
|
23
|
+
);
|
|
24
|
+
}
|
|
17
25
|
}
|
|
18
26
|
}
|
|
19
27
|
|
|
@@ -40,11 +48,11 @@ class WindowSelector extends EventEmitter {
|
|
|
40
48
|
try {
|
|
41
49
|
// Native window selection başlat
|
|
42
50
|
const success = nativeBinding.startWindowSelection();
|
|
43
|
-
|
|
51
|
+
|
|
44
52
|
if (success) {
|
|
45
53
|
this.isSelecting = true;
|
|
46
54
|
this.selectedWindow = null;
|
|
47
|
-
|
|
55
|
+
|
|
48
56
|
// Status polling timer başlat (higher frequency for overlay updates)
|
|
49
57
|
this.selectionTimer = setInterval(() => {
|
|
50
58
|
this.checkSelectionStatus();
|
|
@@ -72,7 +80,7 @@ class WindowSelector extends EventEmitter {
|
|
|
72
80
|
return new Promise((resolve, reject) => {
|
|
73
81
|
try {
|
|
74
82
|
const success = nativeBinding.stopWindowSelection();
|
|
75
|
-
|
|
83
|
+
|
|
76
84
|
// Timer'ı durdur
|
|
77
85
|
if (this.selectionTimer) {
|
|
78
86
|
clearInterval(this.selectionTimer);
|
|
@@ -98,14 +106,14 @@ class WindowSelector extends EventEmitter {
|
|
|
98
106
|
|
|
99
107
|
try {
|
|
100
108
|
const status = nativeBinding.getWindowSelectionStatus();
|
|
101
|
-
|
|
109
|
+
|
|
102
110
|
// Seçim tamamlandı mı kontrol et
|
|
103
111
|
if (status.hasSelectedWindow && !this.selectedWindow) {
|
|
104
112
|
const windowInfo = nativeBinding.getSelectedWindowInfo();
|
|
105
113
|
if (windowInfo) {
|
|
106
114
|
this.selectedWindow = windowInfo;
|
|
107
115
|
this.isSelecting = false;
|
|
108
|
-
|
|
116
|
+
|
|
109
117
|
// Timer'ı durdur
|
|
110
118
|
if (this.selectionTimer) {
|
|
111
119
|
clearInterval(this.selectionTimer);
|
|
@@ -121,17 +129,20 @@ class WindowSelector extends EventEmitter {
|
|
|
121
129
|
if (this.lastStatus) {
|
|
122
130
|
const lastWindow = this.lastStatus.currentWindow;
|
|
123
131
|
const currentWindow = status.currentWindow;
|
|
124
|
-
|
|
132
|
+
|
|
125
133
|
if (!lastWindow && currentWindow) {
|
|
126
134
|
// Yeni pencere üstüne gelindi
|
|
127
135
|
this.emit("windowEntered", currentWindow);
|
|
128
136
|
} else if (lastWindow && !currentWindow) {
|
|
129
137
|
// Pencere üstünden ayrıldı
|
|
130
138
|
this.emit("windowLeft", lastWindow);
|
|
131
|
-
} else if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
} else if (
|
|
140
|
+
lastWindow &&
|
|
141
|
+
currentWindow &&
|
|
142
|
+
(lastWindow.id !== currentWindow.id ||
|
|
143
|
+
lastWindow.title !== currentWindow.title ||
|
|
144
|
+
lastWindow.appName !== currentWindow.appName)
|
|
145
|
+
) {
|
|
135
146
|
// Farklı bir pencereye geçildi
|
|
136
147
|
this.emit("windowLeft", lastWindow);
|
|
137
148
|
this.emit("windowEntered", currentWindow);
|
|
@@ -164,14 +175,14 @@ class WindowSelector extends EventEmitter {
|
|
|
164
175
|
isSelecting: this.isSelecting && nativeStatus.isSelecting,
|
|
165
176
|
hasSelectedWindow: !!this.selectedWindow,
|
|
166
177
|
selectedWindow: this.selectedWindow,
|
|
167
|
-
nativeStatus: nativeStatus
|
|
178
|
+
nativeStatus: nativeStatus,
|
|
168
179
|
};
|
|
169
180
|
} catch (error) {
|
|
170
181
|
return {
|
|
171
182
|
isSelecting: this.isSelecting,
|
|
172
183
|
hasSelectedWindow: !!this.selectedWindow,
|
|
173
184
|
selectedWindow: this.selectedWindow,
|
|
174
|
-
error: error.message
|
|
185
|
+
error: error.message,
|
|
175
186
|
};
|
|
176
187
|
}
|
|
177
188
|
}
|
|
@@ -205,7 +216,6 @@ class WindowSelector extends EventEmitter {
|
|
|
205
216
|
|
|
206
217
|
// Seçimi başlat
|
|
207
218
|
await this.startSelection();
|
|
208
|
-
|
|
209
219
|
} catch (error) {
|
|
210
220
|
this.removeAllListeners("windowSelected");
|
|
211
221
|
this.removeAllListeners("error");
|
|
@@ -242,7 +252,9 @@ class WindowSelector extends EventEmitter {
|
|
|
242
252
|
nativeBinding.setBringToFrontEnabled(enabled);
|
|
243
253
|
// Only log if explicitly setting, not on startup
|
|
244
254
|
if (arguments.length > 0) {
|
|
245
|
-
console.log(
|
|
255
|
+
console.log(
|
|
256
|
+
`🔄 Auto bring-to-front: ${enabled ? "ENABLED" : "DISABLED"}`
|
|
257
|
+
);
|
|
246
258
|
}
|
|
247
259
|
} catch (error) {
|
|
248
260
|
throw new Error(`Failed to set bring to front: ${error.message}`);
|
|
@@ -376,14 +388,14 @@ class WindowSelector extends EventEmitter {
|
|
|
376
388
|
try {
|
|
377
389
|
// Start screen selection
|
|
378
390
|
await this.startScreenSelection();
|
|
379
|
-
|
|
391
|
+
|
|
380
392
|
// Poll for selection completion
|
|
381
393
|
return new Promise((resolve, reject) => {
|
|
382
394
|
let isResolved = false;
|
|
383
|
-
|
|
395
|
+
|
|
384
396
|
const checkSelection = () => {
|
|
385
397
|
if (isResolved) return; // Prevent multiple resolutions
|
|
386
|
-
|
|
398
|
+
|
|
387
399
|
const selectedScreen = this.getSelectedScreen();
|
|
388
400
|
if (selectedScreen) {
|
|
389
401
|
isResolved = true;
|
|
@@ -394,19 +406,19 @@ class WindowSelector extends EventEmitter {
|
|
|
394
406
|
} else {
|
|
395
407
|
// Selection was cancelled (probably ESC key)
|
|
396
408
|
isResolved = true;
|
|
397
|
-
reject(new Error(
|
|
409
|
+
reject(new Error("Screen selection was cancelled"));
|
|
398
410
|
}
|
|
399
411
|
};
|
|
400
|
-
|
|
412
|
+
|
|
401
413
|
// Start polling
|
|
402
414
|
checkSelection();
|
|
403
|
-
|
|
415
|
+
|
|
404
416
|
// Timeout after 60 seconds
|
|
405
417
|
setTimeout(() => {
|
|
406
418
|
if (!isResolved) {
|
|
407
419
|
isResolved = true;
|
|
408
420
|
this.stopScreenSelection();
|
|
409
|
-
reject(new Error(
|
|
421
|
+
reject(new Error("Screen selection timed out"));
|
|
410
422
|
}
|
|
411
423
|
}, 60000);
|
|
412
424
|
});
|
|
@@ -430,7 +442,9 @@ class WindowSelector extends EventEmitter {
|
|
|
430
442
|
const success = nativeBinding.showScreenRecordingPreview(screenInfo);
|
|
431
443
|
return success;
|
|
432
444
|
} catch (error) {
|
|
433
|
-
throw new Error(
|
|
445
|
+
throw new Error(
|
|
446
|
+
`Failed to show screen recording preview: ${error.message}`
|
|
447
|
+
);
|
|
434
448
|
}
|
|
435
449
|
}
|
|
436
450
|
|
|
@@ -443,7 +457,9 @@ class WindowSelector extends EventEmitter {
|
|
|
443
457
|
const success = nativeBinding.hideScreenRecordingPreview();
|
|
444
458
|
return success;
|
|
445
459
|
} catch (error) {
|
|
446
|
-
throw new Error(
|
|
460
|
+
throw new Error(
|
|
461
|
+
`Failed to hide screen recording preview: ${error.message}`
|
|
462
|
+
);
|
|
447
463
|
}
|
|
448
464
|
}
|
|
449
465
|
|
|
@@ -460,10 +476,10 @@ class WindowSelector extends EventEmitter {
|
|
|
460
476
|
return {
|
|
461
477
|
screenRecording: false,
|
|
462
478
|
accessibility: false,
|
|
463
|
-
error: error.message
|
|
479
|
+
error: error.message,
|
|
464
480
|
};
|
|
465
481
|
}
|
|
466
482
|
}
|
|
467
483
|
}
|
|
468
484
|
|
|
469
|
-
module.exports = WindowSelector;
|
|
485
|
+
module.exports = WindowSelector;
|