node-mac-recorder 2.17.19 → 2.17.21

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.
Files changed (41) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/index.js +21 -200
  3. package/package.json +1 -1
  4. package/publish.sh +18 -0
  5. package/src/cursor_tracker.mm +37 -20
  6. package/cursor-data-1751364226346.json +0 -1
  7. package/cursor-data-1751364314136.json +0 -1
  8. package/cursor-data.json +0 -1
  9. package/cursor-debug-test.js +0 -60
  10. package/cursor-dpr-test.js +0 -73
  11. package/cursor-dpr-test.json +0 -1
  12. package/cursor-macbook-test.js +0 -63
  13. package/cursor-permission-test.js +0 -46
  14. package/cursor-scaling-debug.js +0 -53
  15. package/cursor-simple-test.js +0 -26
  16. package/debug-audio.js +0 -79
  17. package/debug-coordinates.js +0 -69
  18. package/debug-cursor-output.json +0 -1
  19. package/debug-macos14.js +0 -110
  20. package/debug-primary-window.js +0 -84
  21. package/debug-screen-selection.js +0 -81
  22. package/debug-window-selector.js +0 -178
  23. package/examples/electron-integration-example.js +0 -230
  24. package/examples/electron-preload.js +0 -46
  25. package/examples/electron-renderer.html +0 -634
  26. package/examples/integration-example.js +0 -228
  27. package/examples/window-selector-example.js +0 -254
  28. package/quick-cursor-test.json +0 -1
  29. package/test-both-cursor.json +0 -1
  30. package/test-cursor-fix.js +0 -105
  31. package/test-cursor-types.js +0 -117
  32. package/test-display-coordinates.js +0 -72
  33. package/test-integrated-recording.js +0 -120
  34. package/test-menubar-offset.js +0 -110
  35. package/test-output/primary-fix-test-1758266910543.json +0 -1
  36. package/test-output/unified-cursor-1758313640878.json +0 -1
  37. package/test-output/unified-cursor-1758313689471.json +0 -1
  38. package/test-primary-cursor.js +0 -154
  39. package/test-primary-fix.js +0 -120
  40. package/test-unified-cursor.js +0 -75
  41. package/test-window-recording.js +0 -149
@@ -1,178 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const WindowSelector = require('./window-selector');
4
-
5
- async function debugWindowSelector() {
6
- console.log('🔍 Window Selector Debug Mode');
7
- console.log('=============================\n');
8
-
9
- const selector = new WindowSelector();
10
-
11
- try {
12
- // 1. İzin kontrolü
13
- console.log('1️⃣ 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.warn('⚠️ Missing permissions detected!');
19
- console.warn('Go to: System Preferences > Security & Privacy > Privacy');
20
- console.warn('Enable for Terminal/Node in both:');
21
- console.warn('- Screen Recording');
22
- console.warn('- Accessibility');
23
- console.log();
24
- }
25
-
26
- // 2. Native status before start
27
- console.log('\n2️⃣ Native status before start:');
28
- const initialStatus = selector.getStatus();
29
- console.log(JSON.stringify(initialStatus, null, 2));
30
-
31
- // 3. Start selection with detailed logging
32
- console.log('\n3️⃣ Starting window selection...');
33
-
34
- selector.on('selectionStarted', () => {
35
- console.log('✅ Selection started event received');
36
- });
37
-
38
- selector.on('windowEntered', (window) => {
39
- console.log(`🏠 Window entered: "${window.title}" (${window.appName})`);
40
- });
41
-
42
- selector.on('windowLeft', (window) => {
43
- console.log(`🚪 Window left: "${window.title}" (${window.appName})`);
44
- });
45
-
46
- selector.on('windowSelected', (window) => {
47
- console.log('🎯 Window selected:', window.title);
48
- });
49
-
50
- selector.on('error', (error) => {
51
- console.error('❌ Error event:', error);
52
- });
53
-
54
- await selector.startSelection();
55
-
56
- // 4. Status after start
57
- console.log('\n4️⃣ Status after start:');
58
- const runningStatus = selector.getStatus();
59
- console.log(JSON.stringify(runningStatus, null, 2));
60
-
61
- // 5. Detailed native status monitoring
62
- console.log('\n5️⃣ Monitoring native status (10 seconds)...');
63
- console.log('Move your cursor over different windows');
64
- console.log('The overlay should appear over windows\n');
65
-
66
- for (let i = 0; i < 20; i++) {
67
- await new Promise(resolve => setTimeout(resolve, 500));
68
- const status = selector.getStatus();
69
-
70
- process.stdout.write(`\r[${i+1}/20] `);
71
- process.stdout.write(`Selecting: ${status.nativeStatus?.isSelecting || false}, `);
72
- process.stdout.write(`Overlay: ${status.nativeStatus?.hasOverlay || false}, `);
73
- process.stdout.write(`Windows: ${status.nativeStatus?.windowCount || 0}`);
74
-
75
- if (status.nativeStatus?.currentWindow) {
76
- process.stdout.write(`, Current: ${status.nativeStatus.currentWindow.appName}`);
77
- }
78
- }
79
-
80
- console.log('\n\n6️⃣ Final status:');
81
- const finalStatus = selector.getStatus();
82
- console.log(JSON.stringify(finalStatus, null, 2));
83
-
84
- } catch (error) {
85
- console.error('\n❌ Debug failed:', error.message);
86
- console.error('Stack:', error.stack);
87
- } finally {
88
- console.log('\n🛑 Stopping selection...');
89
- await selector.cleanup();
90
- }
91
- }
92
-
93
- // Alternative: Test native functions directly
94
- async function testNativeFunctions() {
95
- console.log('🧪 Testing Native Functions Directly');
96
- console.log('====================================\n');
97
-
98
- try {
99
- // Try to load native binding directly
100
- let nativeBinding;
101
- try {
102
- nativeBinding = require("./build/Release/mac_recorder.node");
103
- } catch (error) {
104
- try {
105
- nativeBinding = require("./build/Debug/mac_recorder.node");
106
- } catch (debugError) {
107
- console.error('❌ Cannot load native module');
108
- console.error('Release error:', error.message);
109
- console.error('Debug error:', debugError.message);
110
- return;
111
- }
112
- }
113
-
114
- console.log('✅ Native module loaded successfully');
115
-
116
- // Test if window selector functions exist
117
- console.log('\n🔍 Available native functions:');
118
- const functions = [
119
- 'startWindowSelection',
120
- 'stopWindowSelection',
121
- 'getSelectedWindowInfo',
122
- 'getWindowSelectionStatus'
123
- ];
124
-
125
- for (const func of functions) {
126
- if (typeof nativeBinding[func] === 'function') {
127
- console.log(`✅ ${func} - available`);
128
- } else {
129
- console.log(`❌ ${func} - missing`);
130
- }
131
- }
132
-
133
- // Test direct native call
134
- console.log('\n🚀 Testing direct native startWindowSelection...');
135
- try {
136
- const result = nativeBinding.startWindowSelection();
137
- console.log('Native start result:', result);
138
-
139
- if (result) {
140
- console.log('✅ Native selection started');
141
-
142
- // Check status
143
- const status = nativeBinding.getWindowSelectionStatus();
144
- console.log('Native status:', JSON.stringify(status, null, 2));
145
-
146
- // Wait a bit
147
- console.log('\nWaiting 3 seconds for overlay to appear...');
148
- await new Promise(resolve => setTimeout(resolve, 3000));
149
-
150
- // Stop
151
- const stopResult = nativeBinding.stopWindowSelection();
152
- console.log('Native stop result:', stopResult);
153
- } else {
154
- console.log('❌ Native selection failed to start');
155
- }
156
- } catch (nativeError) {
157
- console.error('❌ Native function error:', nativeError.message);
158
- }
159
-
160
- } catch (error) {
161
- console.error('❌ Native test failed:', error.message);
162
- }
163
- }
164
-
165
- // Main function
166
- async function main() {
167
- const args = process.argv.slice(2);
168
-
169
- if (args.includes('--native')) {
170
- await testNativeFunctions();
171
- } else {
172
- await debugWindowSelector();
173
- }
174
- }
175
-
176
- if (require.main === module) {
177
- main().catch(console.error);
178
- }
@@ -1,230 +0,0 @@
1
- // Electron Integration Example for node-mac-recorder
2
- // This example shows how to use the Electron-safe version in an Electron app
3
-
4
- const { app, BrowserWindow, ipcMain, dialog } = require("electron");
5
- const path = require("path");
6
-
7
- // Import the Electron-safe version
8
- const ElectronSafeMacRecorder = require("../electron-safe-index");
9
-
10
- let mainWindow;
11
- let recorder;
12
-
13
- function createWindow() {
14
- // Create the browser window
15
- mainWindow = new BrowserWindow({
16
- width: 1200,
17
- height: 800,
18
- webPreferences: {
19
- nodeIntegration: false,
20
- contextIsolation: true,
21
- preload: path.join(__dirname, "electron-preload.js"),
22
- },
23
- });
24
-
25
- // Load the app
26
- mainWindow.loadFile("electron-renderer.html");
27
-
28
- // Initialize the Electron-safe recorder
29
- try {
30
- recorder = new ElectronSafeMacRecorder();
31
- console.log("✅ ElectronSafeMacRecorder initialized");
32
-
33
- // Setup event listeners
34
- recorder.on("recordingStarted", (data) => {
35
- console.log("🎬 Recording started:", data);
36
- mainWindow.webContents.send("recording-started", data);
37
- });
38
-
39
- recorder.on("stopped", (result) => {
40
- console.log("🛑 Recording stopped:", result);
41
- mainWindow.webContents.send("recording-stopped", result);
42
- });
43
-
44
- recorder.on("completed", (outputPath) => {
45
- console.log("✅ Recording completed:", outputPath);
46
- mainWindow.webContents.send("recording-completed", outputPath);
47
- });
48
-
49
- recorder.on("timeUpdate", (elapsed) => {
50
- mainWindow.webContents.send("recording-time-update", elapsed);
51
- });
52
- } catch (error) {
53
- console.error("❌ Failed to initialize recorder:", error);
54
- dialog.showErrorBox(
55
- "Recorder Error",
56
- "Failed to initialize screen recorder. Please ensure the Electron-safe module is built."
57
- );
58
- }
59
- }
60
-
61
- // IPC handlers for safe communication with renderer
62
- ipcMain.handle("recorder:getModuleInfo", async () => {
63
- try {
64
- return recorder ? recorder.getModuleInfo() : null;
65
- } catch (error) {
66
- console.error("Error getting module info:", error);
67
- return { error: error.message };
68
- }
69
- });
70
-
71
- ipcMain.handle("recorder:checkPermissions", async () => {
72
- try {
73
- return await recorder.checkPermissions();
74
- } catch (error) {
75
- console.error("Error checking permissions:", error);
76
- return { error: error.message };
77
- }
78
- });
79
-
80
- ipcMain.handle("recorder:getDisplays", async () => {
81
- try {
82
- return await recorder.getDisplays();
83
- } catch (error) {
84
- console.error("Error getting displays:", error);
85
- return [];
86
- }
87
- });
88
-
89
- ipcMain.handle("recorder:getWindows", async () => {
90
- try {
91
- return await recorder.getWindows();
92
- } catch (error) {
93
- console.error("Error getting windows:", error);
94
- return [];
95
- }
96
- });
97
-
98
- ipcMain.handle(
99
- "recorder:startRecording",
100
- async (event, outputPath, options) => {
101
- try {
102
- if (!recorder) {
103
- throw new Error("Recorder not initialized");
104
- }
105
-
106
- console.log("🎬 Starting recording with options:", options);
107
- const result = await recorder.startRecording(outputPath, options);
108
- return { success: true, result };
109
- } catch (error) {
110
- console.error("Error starting recording:", error);
111
- return { success: false, error: error.message };
112
- }
113
- }
114
- );
115
-
116
- ipcMain.handle("recorder:stopRecording", async () => {
117
- try {
118
- if (!recorder) {
119
- throw new Error("Recorder not initialized");
120
- }
121
-
122
- console.log("🛑 Stopping recording");
123
- const result = await recorder.stopRecording();
124
- return { success: true, result };
125
- } catch (error) {
126
- console.error("Error stopping recording:", error);
127
- return { success: false, error: error.message };
128
- }
129
- });
130
-
131
- ipcMain.handle("recorder:getStatus", async () => {
132
- try {
133
- return recorder ? recorder.getStatus() : null;
134
- } catch (error) {
135
- console.error("Error getting status:", error);
136
- return { error: error.message };
137
- }
138
- });
139
-
140
- ipcMain.handle("recorder:getCursorPosition", async () => {
141
- try {
142
- return recorder ? recorder.getCursorPosition() : null;
143
- } catch (error) {
144
- console.error("Error getting cursor position:", error);
145
- return { error: error.message };
146
- }
147
- });
148
-
149
- ipcMain.handle(
150
- "recorder:getDisplayThumbnail",
151
- async (event, displayId, options) => {
152
- try {
153
- return await recorder.getDisplayThumbnail(displayId, options);
154
- } catch (error) {
155
- console.error("Error getting display thumbnail:", error);
156
- return null;
157
- }
158
- }
159
- );
160
-
161
- ipcMain.handle(
162
- "recorder:getWindowThumbnail",
163
- async (event, windowId, options) => {
164
- try {
165
- return await recorder.getWindowThumbnail(windowId, options);
166
- } catch (error) {
167
- console.error("Error getting window thumbnail:", error);
168
- return null;
169
- }
170
- }
171
- );
172
-
173
- ipcMain.handle("dialog:showSaveDialog", async () => {
174
- const result = await dialog.showSaveDialog(mainWindow, {
175
- title: "Save Recording",
176
- defaultPath: "recording.mov",
177
- filters: [{ name: "Movies", extensions: ["mov", "mp4"] }],
178
- });
179
- return result;
180
- });
181
-
182
- // App event listeners
183
- app.whenReady().then(createWindow);
184
-
185
- app.on("window-all-closed", () => {
186
- // Stop any ongoing recording before quitting
187
- if (recorder && recorder.getStatus().isRecording) {
188
- console.log("🛑 Stopping recording before quit");
189
- recorder.stopRecording().finally(() => {
190
- if (process.platform !== "darwin") {
191
- app.quit();
192
- }
193
- });
194
- } else {
195
- if (process.platform !== "darwin") {
196
- app.quit();
197
- }
198
- }
199
- });
200
-
201
- app.on("activate", () => {
202
- if (BrowserWindow.getAllWindows().length === 0) {
203
- createWindow();
204
- }
205
- });
206
-
207
- // Handle app termination gracefully
208
- process.on("SIGINT", async () => {
209
- console.log("🛑 SIGINT received, stopping recording...");
210
- if (recorder && recorder.getStatus().isRecording) {
211
- try {
212
- await recorder.stopRecording();
213
- } catch (error) {
214
- console.error("Error stopping recording on exit:", error);
215
- }
216
- }
217
- app.quit();
218
- });
219
-
220
- process.on("SIGTERM", async () => {
221
- console.log("🛑 SIGTERM received, stopping recording...");
222
- if (recorder && recorder.getStatus().isRecording) {
223
- try {
224
- await recorder.stopRecording();
225
- } catch (error) {
226
- console.error("Error stopping recording on exit:", error);
227
- }
228
- }
229
- app.quit();
230
- });
@@ -1,46 +0,0 @@
1
- // Preload script for secure IPC communication
2
- const { contextBridge, ipcRenderer } = require("electron");
3
-
4
- // Expose protected methods that allow the renderer process to use
5
- // the ipcRenderer without exposing the entire object
6
- contextBridge.exposeInMainWorld("electronAPI", {
7
- // Recorder API
8
- recorder: {
9
- getModuleInfo: () => ipcRenderer.invoke("recorder:getModuleInfo"),
10
- checkPermissions: () => ipcRenderer.invoke("recorder:checkPermissions"),
11
- getDisplays: () => ipcRenderer.invoke("recorder:getDisplays"),
12
- getWindows: () => ipcRenderer.invoke("recorder:getWindows"),
13
- startRecording: (outputPath, options) =>
14
- ipcRenderer.invoke("recorder:startRecording", outputPath, options),
15
- stopRecording: () => ipcRenderer.invoke("recorder:stopRecording"),
16
- getStatus: () => ipcRenderer.invoke("recorder:getStatus"),
17
- getCursorPosition: () => ipcRenderer.invoke("recorder:getCursorPosition"),
18
- getDisplayThumbnail: (displayId, options) =>
19
- ipcRenderer.invoke("recorder:getDisplayThumbnail", displayId, options),
20
- getWindowThumbnail: (windowId, options) =>
21
- ipcRenderer.invoke("recorder:getWindowThumbnail", windowId, options),
22
-
23
- // Event listeners
24
- onRecordingStarted: (callback) =>
25
- ipcRenderer.on("recording-started", callback),
26
- onRecordingStopped: (callback) =>
27
- ipcRenderer.on("recording-stopped", callback),
28
- onRecordingCompleted: (callback) =>
29
- ipcRenderer.on("recording-completed", callback),
30
- onTimeUpdate: (callback) =>
31
- ipcRenderer.on("recording-time-update", callback),
32
-
33
- // Remove listeners
34
- removeAllListeners: () => {
35
- ipcRenderer.removeAllListeners("recording-started");
36
- ipcRenderer.removeAllListeners("recording-stopped");
37
- ipcRenderer.removeAllListeners("recording-completed");
38
- ipcRenderer.removeAllListeners("recording-time-update");
39
- },
40
- },
41
-
42
- // Dialog API
43
- dialog: {
44
- showSaveDialog: () => ipcRenderer.invoke("dialog:showSaveDialog"),
45
- },
46
- });