node-mac-recorder 1.2.8 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +13 -0
- package/CLAUDE.md +120 -0
- package/cursor-data.json +1 -0
- package/debug-cursor-output.json +1 -0
- package/debug-cursor.js +50 -0
- package/index.js +6 -5
- package/package.json +1 -1
- package/quick-cursor-test.js +20 -0
- package/quick-cursor-test.json +1 -0
- package/src/cursor_tracker.mm +10 -4
- package/src/mac_recorder.mm +17 -3
- package/test-both-cursor.json +1 -0
- package/test-both.js +46 -0
- package/test-cursor-visible.js +41 -0
- package/test-native-cursor.js +31 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
node-mac-recorder is a Node.js native addon that provides macOS screen recording capabilities using AVFoundation. The package allows recording of full screens, specific windows, or custom areas, with support for multi-display setups, audio capture, and cursor tracking.
|
|
8
|
+
|
|
9
|
+
## Build System & Commands
|
|
10
|
+
|
|
11
|
+
### Building the Native Module
|
|
12
|
+
```bash
|
|
13
|
+
npm run build # Build the native module using node-gyp
|
|
14
|
+
npm run rebuild # Clean rebuild of the native module
|
|
15
|
+
npm run clean # Clean build artifacts
|
|
16
|
+
npm install # Runs install.js which builds the module automatically
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Testing
|
|
20
|
+
```bash
|
|
21
|
+
npm test # Run the main test suite (test.js)
|
|
22
|
+
node cursor-test.js # Test cursor tracking functionality only
|
|
23
|
+
node test.js # Run comprehensive API tests
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Development
|
|
27
|
+
The package uses node-gyp for building the native C++/Objective-C module. Requires:
|
|
28
|
+
- macOS 10.15+ (Catalina or later)
|
|
29
|
+
- Xcode Command Line Tools
|
|
30
|
+
- Node.js 14+
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
### Core Components
|
|
35
|
+
|
|
36
|
+
**Main Entry Point**
|
|
37
|
+
- `index.js` - Main MacRecorder class (EventEmitter-based)
|
|
38
|
+
- Handles all high-level recording operations and coordinate transformations
|
|
39
|
+
|
|
40
|
+
**Native Module** (`src/`)
|
|
41
|
+
- `mac_recorder.mm` - Main native module entry point and N-API bindings
|
|
42
|
+
- `screen_capture.mm` - AVFoundation-based screen/window recording
|
|
43
|
+
- `audio_capture.mm` - Audio device enumeration and capture
|
|
44
|
+
- `cursor_tracker.mm` - Real-time cursor position and event tracking
|
|
45
|
+
|
|
46
|
+
**Build Configuration**
|
|
47
|
+
- `binding.gyp` - Native module build configuration
|
|
48
|
+
- Links against AVFoundation, ScreenCaptureKit, AppKit, and other macOS frameworks
|
|
49
|
+
|
|
50
|
+
### Key Features
|
|
51
|
+
|
|
52
|
+
1. **Multi-Display Support**: Automatic display detection and coordinate conversion
|
|
53
|
+
2. **Window Recording**: Smart window detection with thumbnail generation
|
|
54
|
+
3. **Audio Control**: Separate microphone and system audio controls with device selection
|
|
55
|
+
4. **Cursor Tracking**: Real-time cursor position, type, and click event capture
|
|
56
|
+
5. **Permission Management**: Built-in macOS permission checking and requesting
|
|
57
|
+
|
|
58
|
+
### Coordinate System Handling
|
|
59
|
+
|
|
60
|
+
The package handles complex multi-display coordinate transformations:
|
|
61
|
+
- Global macOS coordinates (can be negative for secondary displays)
|
|
62
|
+
- Display-relative coordinates (always positive, 0-based)
|
|
63
|
+
- Automatic window-to-display mapping for recording
|
|
64
|
+
|
|
65
|
+
## API Structure
|
|
66
|
+
|
|
67
|
+
### Main Class Methods
|
|
68
|
+
- `startRecording(outputPath, options)` - Begin screen/window recording
|
|
69
|
+
- `stopRecording()` - Stop recording and finalize video file
|
|
70
|
+
- `getWindows()` - List all recordable application windows
|
|
71
|
+
- `getDisplays()` - Get all available displays with metadata
|
|
72
|
+
- `getAudioDevices()` - Enumerate available audio input devices
|
|
73
|
+
- `checkPermissions()` - Verify macOS recording permissions
|
|
74
|
+
|
|
75
|
+
### Cursor Tracking
|
|
76
|
+
- `startCursorCapture(filepath)` - Begin real-time cursor tracking to JSON
|
|
77
|
+
- `stopCursorCapture()` - Stop tracking and close output file
|
|
78
|
+
- `getCursorPosition()` - Get current cursor position and state
|
|
79
|
+
|
|
80
|
+
### Thumbnails
|
|
81
|
+
- `getWindowThumbnail(windowId, options)` - Capture window preview image
|
|
82
|
+
- `getDisplayThumbnail(displayId, options)` - Capture display preview image
|
|
83
|
+
|
|
84
|
+
## Development Notes
|
|
85
|
+
|
|
86
|
+
### Testing Strategy
|
|
87
|
+
- Use `npm test` for full API validation
|
|
88
|
+
- `cursor-test.js` for testing cursor tracking specifically
|
|
89
|
+
- Test files create output in `test-output/` directory
|
|
90
|
+
|
|
91
|
+
### Common Development Patterns
|
|
92
|
+
- All recording operations are Promise-based
|
|
93
|
+
- Event emission for recording state changes (`started`, `stopped`, `completed`)
|
|
94
|
+
- Automatic permission checking before operations
|
|
95
|
+
- Error handling with descriptive messages for permission issues
|
|
96
|
+
|
|
97
|
+
### Platform Requirements
|
|
98
|
+
- macOS only (enforced in install.js)
|
|
99
|
+
- Native module compilation required on install
|
|
100
|
+
- Requires screen recording and accessibility permissions
|
|
101
|
+
|
|
102
|
+
### File Outputs
|
|
103
|
+
- Video recordings: `.mov` format (H.264/AAC)
|
|
104
|
+
- Cursor data: JSON format with timestamped events
|
|
105
|
+
- Thumbnails: Base64-encoded PNG data URIs
|
|
106
|
+
|
|
107
|
+
## Troubleshooting
|
|
108
|
+
|
|
109
|
+
### Build Issues
|
|
110
|
+
1. Ensure Xcode Command Line Tools: `xcode-select --install`
|
|
111
|
+
2. Clean rebuild: `npm run clean && npm run build`
|
|
112
|
+
3. Check Node.js version compatibility (14+)
|
|
113
|
+
|
|
114
|
+
### Runtime Issues
|
|
115
|
+
1. Permission failures: Check System Preferences > Security & Privacy
|
|
116
|
+
2. Recording failures: Verify target windows/displays are accessible
|
|
117
|
+
3. Audio issues: Check audio device availability and permissions
|
|
118
|
+
|
|
119
|
+
### Native Module Loading
|
|
120
|
+
The module tries loading from `build/Release/` first, then falls back to `build/Debug/` with helpful error messages if neither exists.
|
package/cursor-data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"x":1151,"y":726,"timestamp":20,"cursorType":"text","type":"move"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"x":48,"y":72,"timestamp":22,"unixTimeMs":1752410259890,"cursorType":"pointer","type":"move"},{"x":47,"y":71,"timestamp":87,"unixTimeMs":1752410259955,"cursorType":"pointer","type":"mousedown"},{"x":47,"y":71,"timestamp":107,"unixTimeMs":1752410259975,"cursorType":"pointer","type":"move"},{"x":47,"y":71,"timestamp":169,"unixTimeMs":1752410260037,"cursorType":"pointer","type":"mouseup"},{"x":47,"y":71,"timestamp":781,"unixTimeMs":1752410260649,"cursorType":"default","type":"move"}]
|
package/debug-cursor.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
// Create a minimal direct test
|
|
5
|
+
const recorder = new MacRecorder();
|
|
6
|
+
|
|
7
|
+
// Get current cursor position to test the new field
|
|
8
|
+
console.log('Testing cursor position API...');
|
|
9
|
+
const pos = recorder.getCursorPosition();
|
|
10
|
+
console.log('Current cursor position:', JSON.stringify(pos, null, 2));
|
|
11
|
+
|
|
12
|
+
// Test if we can start/stop cursor tracking
|
|
13
|
+
console.log('\nTesting cursor tracking start/stop...');
|
|
14
|
+
const testFile = 'debug-cursor-output.json';
|
|
15
|
+
|
|
16
|
+
// Remove existing file
|
|
17
|
+
if (fs.existsSync(testFile)) {
|
|
18
|
+
fs.unlinkSync(testFile);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const started = recorder.startCursorCapture(testFile);
|
|
22
|
+
console.log('Start result:', started);
|
|
23
|
+
|
|
24
|
+
if (started) {
|
|
25
|
+
// Wait 1 second and check what gets written
|
|
26
|
+
setTimeout(() => {
|
|
27
|
+
recorder.stopCursorCapture();
|
|
28
|
+
console.log('Stopped tracking');
|
|
29
|
+
|
|
30
|
+
// Check file content
|
|
31
|
+
if (fs.existsSync(testFile)) {
|
|
32
|
+
const content = fs.readFileSync(testFile, 'utf8');
|
|
33
|
+
console.log('\nFile content:');
|
|
34
|
+
console.log(content);
|
|
35
|
+
|
|
36
|
+
// Parse and pretty print
|
|
37
|
+
try {
|
|
38
|
+
const data = JSON.parse(content);
|
|
39
|
+
console.log('\nParsed data:');
|
|
40
|
+
console.log(JSON.stringify(data, null, 2));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.log('Error parsing JSON:', e.message);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
console.log('No output file created');
|
|
46
|
+
}
|
|
47
|
+
}, 1000);
|
|
48
|
+
} else {
|
|
49
|
+
console.log('Failed to start cursor tracking');
|
|
50
|
+
}
|
package/index.js
CHANGED
|
@@ -163,7 +163,7 @@ class MacRecorder extends EventEmitter {
|
|
|
163
163
|
targetWindow.y >= display.y &&
|
|
164
164
|
targetWindow.y < display.y + displayHeight
|
|
165
165
|
) {
|
|
166
|
-
targetDisplayId =
|
|
166
|
+
targetDisplayId = display.id; // Use actual display ID, not array index
|
|
167
167
|
// Koordinatları display'e göre normalize et
|
|
168
168
|
adjustedX = targetWindow.x - display.x;
|
|
169
169
|
adjustedY = targetWindow.y - display.y;
|
|
@@ -175,7 +175,7 @@ class MacRecorder extends EventEmitter {
|
|
|
175
175
|
if (targetDisplayId === null) {
|
|
176
176
|
const mainDisplay = displays.find((d) => d.x === 0 && d.y === 0);
|
|
177
177
|
if (mainDisplay) {
|
|
178
|
-
targetDisplayId =
|
|
178
|
+
targetDisplayId = mainDisplay.id; // Use actual display ID, not array index
|
|
179
179
|
adjustedX = Math.max(
|
|
180
180
|
0,
|
|
181
181
|
Math.min(
|
|
@@ -200,7 +200,7 @@ class MacRecorder extends EventEmitter {
|
|
|
200
200
|
this.options.displayId = targetDisplayId;
|
|
201
201
|
|
|
202
202
|
// Recording için display bilgisini sakla (cursor capture için)
|
|
203
|
-
const targetDisplay = displays
|
|
203
|
+
const targetDisplay = displays.find(d => d.id === targetDisplayId);
|
|
204
204
|
this.recordingDisplayInfo = {
|
|
205
205
|
displayId: targetDisplayId,
|
|
206
206
|
x: targetDisplay.x,
|
|
@@ -233,8 +233,8 @@ class MacRecorder extends EventEmitter {
|
|
|
233
233
|
if (this.options.displayId !== null && !this.recordingDisplayInfo) {
|
|
234
234
|
try {
|
|
235
235
|
const displays = await this.getDisplays();
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
const targetDisplay = displays.find(d => d.id === this.options.displayId);
|
|
237
|
+
if (targetDisplay) {
|
|
238
238
|
this.recordingDisplayInfo = {
|
|
239
239
|
displayId: this.options.displayId,
|
|
240
240
|
x: targetDisplay.x,
|
|
@@ -585,6 +585,7 @@ class MacRecorder extends EventEmitter {
|
|
|
585
585
|
x: x,
|
|
586
586
|
y: y,
|
|
587
587
|
timestamp: timestamp,
|
|
588
|
+
unixTimeMs: Date.now(),
|
|
588
589
|
cursorType: position.cursorType,
|
|
589
590
|
type: position.eventType || "move",
|
|
590
591
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
const recorder = new MacRecorder();
|
|
3
|
+
|
|
4
|
+
console.log('Starting 3-second cursor test...');
|
|
5
|
+
const testPath = 'quick-cursor-test.json';
|
|
6
|
+
|
|
7
|
+
// Start cursor tracking
|
|
8
|
+
const started = recorder.startCursorCapture(testPath);
|
|
9
|
+
if (started) {
|
|
10
|
+
console.log('Cursor tracking started, collecting data for 3 seconds...');
|
|
11
|
+
|
|
12
|
+
setTimeout(() => {
|
|
13
|
+
console.log('Stopping cursor tracking...');
|
|
14
|
+
recorder.stopCursorCapture();
|
|
15
|
+
console.log('Done! Check quick-cursor-test.json');
|
|
16
|
+
}, 3000);
|
|
17
|
+
} else {
|
|
18
|
+
console.log('Failed to start cursor tracking');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"x":798,"y":526,"timestamp":20,"unixTimeMs":1752410795804,"cursorType":"text","type":"move"}]
|
package/src/cursor_tracker.mm
CHANGED
|
@@ -199,7 +199,9 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
CGPoint location = CGEventGetLocation(event);
|
|
202
|
-
|
|
202
|
+
NSDate *currentDate = [NSDate date];
|
|
203
|
+
NSTimeInterval timestamp = [currentDate timeIntervalSinceDate:g_trackingStartTime] * 1000; // milliseconds
|
|
204
|
+
NSTimeInterval unixTimeMs = [currentDate timeIntervalSince1970] * 1000; // unix timestamp in milliseconds
|
|
203
205
|
NSString *cursorType = getCursorType();
|
|
204
206
|
NSString *eventType = @"move";
|
|
205
207
|
|
|
@@ -230,7 +232,8 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
230
232
|
NSDictionary *cursorInfo = @{
|
|
231
233
|
@"x": @((int)location.x),
|
|
232
234
|
@"y": @((int)location.y),
|
|
233
|
-
@"timestamp": @(
|
|
235
|
+
@"timestamp": @(timestamp),
|
|
236
|
+
@"unixTimeMs": @(unixTimeMs),
|
|
234
237
|
@"cursorType": cursorType,
|
|
235
238
|
@"type": eventType
|
|
236
239
|
};
|
|
@@ -258,14 +261,17 @@ void cursorTimerCallback() {
|
|
|
258
261
|
CFRelease(event);
|
|
259
262
|
}
|
|
260
263
|
|
|
261
|
-
|
|
264
|
+
NSDate *currentDate = [NSDate date];
|
|
265
|
+
NSTimeInterval timestamp = [currentDate timeIntervalSinceDate:g_trackingStartTime] * 1000; // milliseconds
|
|
266
|
+
NSTimeInterval unixTimeMs = [currentDate timeIntervalSince1970] * 1000; // unix timestamp in milliseconds
|
|
262
267
|
NSString *cursorType = getCursorType();
|
|
263
268
|
|
|
264
269
|
// Cursor data oluştur
|
|
265
270
|
NSDictionary *cursorInfo = @{
|
|
266
271
|
@"x": @((int)location.x),
|
|
267
272
|
@"y": @((int)location.y),
|
|
268
|
-
@"timestamp": @(
|
|
273
|
+
@"timestamp": @(timestamp),
|
|
274
|
+
@"unixTimeMs": @(unixTimeMs),
|
|
269
275
|
@"cursorType": cursorType,
|
|
270
276
|
@"type": @"move"
|
|
271
277
|
};
|
package/src/mac_recorder.mm
CHANGED
|
@@ -113,16 +113,30 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
113
113
|
if (options.Has("displayId") && !options.Get("displayId").IsNull()) {
|
|
114
114
|
double displayIdNum = options.Get("displayId").As<Napi::Number>().DoubleValue();
|
|
115
115
|
|
|
116
|
-
//
|
|
116
|
+
// Use the display ID directly (not as an index)
|
|
117
|
+
// The JavaScript layer passes the actual CGDirectDisplayID
|
|
118
|
+
displayID = (CGDirectDisplayID)displayIdNum;
|
|
119
|
+
|
|
120
|
+
// Verify that this display ID is valid
|
|
117
121
|
uint32_t displayCount;
|
|
118
122
|
CGGetActiveDisplayList(0, NULL, &displayCount);
|
|
119
123
|
if (displayCount > 0) {
|
|
120
124
|
CGDirectDisplayID *displays = (CGDirectDisplayID*)malloc(displayCount * sizeof(CGDirectDisplayID));
|
|
121
125
|
CGGetActiveDisplayList(displayCount, displays, &displayCount);
|
|
122
126
|
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
bool validDisplay = false;
|
|
128
|
+
for (uint32_t i = 0; i < displayCount; i++) {
|
|
129
|
+
if (displays[i] == displayID) {
|
|
130
|
+
validDisplay = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!validDisplay) {
|
|
136
|
+
// Fallback to main display if invalid ID provided
|
|
137
|
+
displayID = CGMainDisplayID();
|
|
125
138
|
}
|
|
139
|
+
|
|
126
140
|
free(displays);
|
|
127
141
|
}
|
|
128
142
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"x":798,"y":526,"timestamp":21,"unixTimeMs":1752410827986,"cursorType":"text","type":"move"}]
|
package/test-both.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
const recorder = new MacRecorder();
|
|
3
|
+
|
|
4
|
+
console.log('Testing screen recording + cursor tracking together...');
|
|
5
|
+
|
|
6
|
+
async function testBoth() {
|
|
7
|
+
try {
|
|
8
|
+
// Start both simultaneously
|
|
9
|
+
console.log('Starting screen recording...');
|
|
10
|
+
await recorder.startRecording('./test-recordings/test-both.mov');
|
|
11
|
+
|
|
12
|
+
console.log('Starting cursor tracking...');
|
|
13
|
+
await recorder.startCursorCapture('./test-both-cursor.json');
|
|
14
|
+
|
|
15
|
+
console.log('Both running for 3 seconds...');
|
|
16
|
+
|
|
17
|
+
// Let them run together for 3 seconds
|
|
18
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
19
|
+
|
|
20
|
+
// Stop both
|
|
21
|
+
console.log('Stopping both...');
|
|
22
|
+
await recorder.stopRecording();
|
|
23
|
+
recorder.stopCursorCapture();
|
|
24
|
+
|
|
25
|
+
console.log('✅ Both completed successfully!');
|
|
26
|
+
|
|
27
|
+
// Check results
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
if (fs.existsSync('./test-recordings/test-both.mov')) {
|
|
30
|
+
const stats = fs.statSync('./test-recordings/test-both.mov');
|
|
31
|
+
console.log(`Video file: ${stats.size} bytes`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (fs.existsSync('./test-both-cursor.json')) {
|
|
35
|
+
const content = fs.readFileSync('./test-both-cursor.json', 'utf8');
|
|
36
|
+
const data = JSON.parse(content);
|
|
37
|
+
console.log(`Cursor data: ${data.length} entries`);
|
|
38
|
+
console.log('Sample entry:', JSON.stringify(data[0], null, 2));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Error:', error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
testBoth();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
const recorder = new MacRecorder();
|
|
3
|
+
|
|
4
|
+
async function testCursorVisibility() {
|
|
5
|
+
console.log('Testing cursor visibility in screen recording...');
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
// Test 1: Cursor hidden (default)
|
|
9
|
+
console.log('🎬 Recording with cursor HIDDEN...');
|
|
10
|
+
await recorder.startRecording('./test-recordings/cursor-hidden.mov', {
|
|
11
|
+
captureCursor: false
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
15
|
+
await recorder.stopRecording();
|
|
16
|
+
console.log('✅ Hidden cursor recording done');
|
|
17
|
+
|
|
18
|
+
// Test 2: Cursor visible
|
|
19
|
+
console.log('🎬 Recording with cursor VISIBLE...');
|
|
20
|
+
await recorder.startRecording('./test-recordings/cursor-visible.mov', {
|
|
21
|
+
captureCursor: true
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
25
|
+
await recorder.stopRecording();
|
|
26
|
+
console.log('✅ Visible cursor recording done');
|
|
27
|
+
|
|
28
|
+
// Check file sizes
|
|
29
|
+
const fs = require('fs');
|
|
30
|
+
const hiddenStats = fs.statSync('./test-recordings/cursor-hidden.mov');
|
|
31
|
+
const visibleStats = fs.statSync('./test-recordings/cursor-visible.mov');
|
|
32
|
+
|
|
33
|
+
console.log(`Hidden cursor video: ${hiddenStats.size} bytes`);
|
|
34
|
+
console.log(`Visible cursor video: ${visibleStats.size} bytes`);
|
|
35
|
+
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Error:', error.message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
testCursorVisibility();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const nativeBinding = require('./build/Release/mac_recorder.node');
|
|
2
|
+
|
|
3
|
+
console.log('Testing native cursor tracking...');
|
|
4
|
+
|
|
5
|
+
// Test native startCursorTracking function directly
|
|
6
|
+
const testFile = 'native-cursor-test.json';
|
|
7
|
+
const started = nativeBinding.startCursorTracking(testFile);
|
|
8
|
+
|
|
9
|
+
console.log('Native tracking started:', started);
|
|
10
|
+
|
|
11
|
+
if (started) {
|
|
12
|
+
setTimeout(() => {
|
|
13
|
+
const stopped = nativeBinding.stopCursorTracking();
|
|
14
|
+
console.log('Native tracking stopped:', stopped);
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
if (fs.existsSync(testFile)) {
|
|
18
|
+
const content = fs.readFileSync(testFile, 'utf8');
|
|
19
|
+
console.log('\nNative output:');
|
|
20
|
+
try {
|
|
21
|
+
const data = JSON.parse(content);
|
|
22
|
+
console.log(JSON.stringify(data.slice(0, 3), null, 2)); // Show first 3 entries
|
|
23
|
+
console.log('Total entries:', data.length);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.log('Raw content:', content.substring(0, 500));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, 2000);
|
|
29
|
+
} else {
|
|
30
|
+
console.log('Failed to start native tracking');
|
|
31
|
+
}
|