node-mac-recorder 2.17.14 โ†’ 2.17.16

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 CHANGED
@@ -830,9 +830,26 @@ class MacRecorder extends EventEmitter {
830
830
 
831
831
  if (this.cursorDisplayInfo) {
832
832
  if (this.cursorDisplayInfo.windowRelative) {
833
- // Window recording: Transform global โ†’ window-relative coordinates
834
- x = position.x - this.cursorDisplayInfo.x;
835
- y = position.y - this.cursorDisplayInfo.y;
833
+ // Window recording: Transform cursor coordinates relative to window's display first
834
+ let adjustedX = position.x;
835
+ let adjustedY = position.y;
836
+
837
+ // If we have target display info, first convert global coords to display-relative
838
+ if (this.cursorDisplayInfo.targetDisplay) {
839
+ adjustedX = position.x - this.cursorDisplayInfo.targetDisplay.x;
840
+ adjustedY = position.y - this.cursorDisplayInfo.targetDisplay.y;
841
+ }
842
+
843
+ // Then convert display-relative coords to window-relative
844
+ if (this.cursorDisplayInfo.windowInfo && this.cursorDisplayInfo.windowInfo.displayOffsetX !== undefined) {
845
+ x = adjustedX - this.cursorDisplayInfo.windowInfo.displayOffsetX;
846
+ y = adjustedY - this.cursorDisplayInfo.windowInfo.displayOffsetY;
847
+ } else {
848
+ // Fallback: direct window offset from global coordinates
849
+ x = position.x - this.cursorDisplayInfo.x;
850
+ y = position.y - this.cursorDisplayInfo.y;
851
+ }
852
+
836
853
  coordinateSystem = "window-relative";
837
854
 
838
855
  // Window bounds check - skip if cursor is outside window
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.17.14",
3
+ "version": "2.17.16",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -43,9 +43,9 @@ static NSTimeInterval g_lastCursorCheckTime = 0;
43
43
  static int g_sameCursorDetectionCount = 0;
44
44
  static NSString *g_pendingCursorType = nil;
45
45
 
46
- // Cursor stability constants - more responsive
47
- static const NSTimeInterval CURSOR_STABILITY_THRESHOLD = 0.05; // 50ms (faster response)
48
- static const int CURSOR_CONFIRMATION_COUNT = 1; // Need 1 detection (more responsive)
46
+ // Cursor stability constants - minimal stability for real-time response
47
+ static const NSTimeInterval CURSOR_STABILITY_THRESHOLD = 0.02; // 20ms (very fast)
48
+ static const int CURSOR_CONFIRMATION_COUNT = 1; // Immediate response
49
49
 
50
50
  // Mouse button state tracking
51
51
  static bool g_leftMouseDown = false;
@@ -261,9 +261,18 @@ NSString* getCursorType() {
261
261
  else if ([elementRole isEqualToString:@"AXProgressIndicator"]) {
262
262
  contextualCursorType = @"progress";
263
263
  }
264
- // OTHER ELEMENTS - be conservative, don't assume pointer
264
+ // DEFAULT AREAS - explicitly set default for known non-interactive elements
265
+ else if ([elementRole isEqualToString:@"AXApplication"] ||
266
+ [elementRole isEqualToString:@"AXWindow"] ||
267
+ [elementRole isEqualToString:@"AXScrollArea"] ||
268
+ [elementRole isEqualToString:@"AXScrollBar"] ||
269
+ [elementRole isEqualToString:@"AXStaticText"] ||
270
+ [elementRole isEqualToString:@"AXGroup"]) {
271
+ // These are typically non-interactive - should be default cursor
272
+ contextualCursorType = @"default";
273
+ }
274
+ // OTHER ELEMENTS - let NSCursor handle or default
265
275
  else {
266
- // Don't override NSCursor for unknown elements
267
276
  contextualCursorType = nil;
268
277
  }
269
278
  }
@@ -271,59 +280,68 @@ NSString* getCursorType() {
271
280
  }
272
281
  if (systemWide) CFRelease(systemWide);
273
282
 
274
- // Layer 3: Intelligent fusion of NSCursor and contextual results
283
+ // Layer 3: Contextual-first approach (NSCursor is unreliable)
275
284
  NSString *detectedCursorType = @"default";
276
285
 
277
- // Priority logic with better fallback:
278
- // 1. If contextual gives resize cursor, always use it (resize has highest priority)
279
- if (contextualCursorType != nil &&
280
- ([contextualCursorType hasSuffix:@"resize"] ||
281
- [contextualCursorType isEqualToString:@"col-resize"] ||
282
- [contextualCursorType isEqualToString:@"ns-resize"])) {
286
+ // Priority logic - contextual detection first:
287
+ // 1. If contextual gives ANY specific cursor, use it
288
+ if (contextualCursorType != nil) {
283
289
  detectedCursorType = contextualCursorType;
284
290
  }
285
- // 2. If NSCursor gives a definitive answer, use it
286
- else if (nsCursorType != nil) {
291
+ // 2. Fallback to NSCursor only for very specific cases
292
+ else if (nsCursorType != nil &&
293
+ ([nsCursorType isEqualToString:@"text"] ||
294
+ [nsCursorType isEqualToString:@"pointer"])) {
295
+ // Only trust NSCursor for text and pointer (most reliable)
287
296
  detectedCursorType = nsCursorType;
288
297
  }
289
- // 3. If contextual gives specific non-resize cursor, use it
290
- else if (contextualCursorType != nil &&
291
- ![contextualCursorType isEqualToString:@"default"]) {
292
- detectedCursorType = contextualCursorType;
293
- }
294
- // 4. If both are nil or default, use true default
298
+ // 3. Everything else defaults to default
295
299
  else {
296
300
  detectedCursorType = @"default";
297
301
  }
298
302
 
299
- // Layer 4: Stability filtering to prevent oscillation
303
+ // Layer 4: Minimal stability - mostly real-time with quick default fallback
304
+ NSString *finalCursorType = detectedCursorType;
300
305
 
301
- // Time-based stability check
306
+ // Quick validation and immediate response for most cases
302
307
  if (currentTime - g_lastCursorCheckTime > CURSOR_STABILITY_THRESHOLD) {
303
- // Enough time has passed, reset counters
304
- g_sameCursorDetectionCount = 0;
305
- g_pendingCursorType = detectedCursorType;
306
- }
307
-
308
- // Check if detected cursor matches pending cursor
309
- if ([detectedCursorType isEqualToString:g_pendingCursorType]) {
310
- g_sameCursorDetectionCount++;
311
-
312
- // If we have enough confirmations, update stable cursor
313
- if (g_sameCursorDetectionCount >= CURSOR_CONFIRMATION_COUNT) {
308
+ // Update immediately for most cursor types
309
+ if ([detectedCursorType isEqualToString:@"default"] ||
310
+ [detectedCursorType isEqualToString:@"text"] ||
311
+ [detectedCursorType isEqualToString:@"pointer"]) {
312
+ // High-confidence cursor types - update immediately
313
+ g_stableCursorType = detectedCursorType;
314
+ finalCursorType = detectedCursorType;
315
+ }
316
+ // Resize cursors need tiny bit of stability to avoid jitter
317
+ else if ([detectedCursorType hasSuffix:@"resize"]) {
318
+ if ([detectedCursorType isEqualToString:g_pendingCursorType]) {
319
+ g_sameCursorDetectionCount++;
320
+ if (g_sameCursorDetectionCount >= CURSOR_CONFIRMATION_COUNT) {
321
+ g_stableCursorType = detectedCursorType;
322
+ finalCursorType = detectedCursorType;
323
+ } else {
324
+ finalCursorType = g_stableCursorType; // Keep previous
325
+ }
326
+ } else {
327
+ g_pendingCursorType = detectedCursorType;
328
+ g_sameCursorDetectionCount = 1;
329
+ finalCursorType = g_stableCursorType; // Keep previous
330
+ }
331
+ }
332
+ // Everything else - immediate update
333
+ else {
314
334
  g_stableCursorType = detectedCursorType;
315
- g_lastDetectedCursorType = detectedCursorType;
335
+ finalCursorType = detectedCursorType;
316
336
  }
337
+
338
+ g_lastCursorCheckTime = currentTime;
317
339
  } else {
318
- // Different cursor detected, start new pending
319
- g_pendingCursorType = detectedCursorType;
320
- g_sameCursorDetectionCount = 1;
340
+ // Too soon - use stable cursor
341
+ finalCursorType = g_stableCursorType;
321
342
  }
322
343
 
323
- g_lastCursorCheckTime = currentTime;
324
-
325
- // Final validation
326
- NSString *finalCursorType = g_stableCursorType;
344
+ // Final fallback validation
327
345
  if (!finalCursorType || [finalCursorType length] == 0) {
328
346
  finalCursorType = @"default";
329
347
  }
@@ -119,8 +119,41 @@ static NSString *g_outputPath = nil;
119
119
  }
120
120
 
121
121
  if (targetWindow && targetApp) {
122
- NSLog(@"๐ŸชŸ Recording window: %@ (%ux%u)",
122
+ NSLog(@"๐ŸชŸ Recording window: %@ (%ux%u)",
123
123
  targetWindow.title, (unsigned)targetWindow.frame.size.width, (unsigned)targetWindow.frame.size.height);
124
+
125
+ // Find which display contains this window for proper cursor coordinate transformation
126
+ CGRect windowFrame = targetWindow.frame;
127
+ NSLog(@"๐Ÿ” Window frame: (%.0f,%.0f,%.0fx%.0f)",
128
+ windowFrame.origin.x, windowFrame.origin.y,
129
+ windowFrame.size.width, windowFrame.size.height);
130
+
131
+ // Find the display that contains the window's center point
132
+ CGPoint windowCenter = CGPointMake(
133
+ windowFrame.origin.x + windowFrame.size.width / 2,
134
+ windowFrame.origin.y + windowFrame.size.height / 2
135
+ );
136
+
137
+ for (SCDisplay *display in content.displays) {
138
+ CGRect displayFrame = display.frame;
139
+ NSLog(@"๐Ÿ” Checking if window center (%.0f,%.0f) is in display ID=%u frame (%.0f,%.0f,%.0fx%.0f)",
140
+ windowCenter.x, windowCenter.y, display.displayID,
141
+ displayFrame.origin.x, displayFrame.origin.y,
142
+ displayFrame.size.width, displayFrame.size.height);
143
+
144
+ if (CGRectContainsPoint(displayFrame, windowCenter)) {
145
+ targetDisplay = display;
146
+ NSLog(@"โœ… Window is on display ID=%u", display.displayID);
147
+ break;
148
+ }
149
+ }
150
+
151
+ if (!targetDisplay) {
152
+ // Fallback: use main display if no display contains the window center
153
+ targetDisplay = content.displays.firstObject;
154
+ NSLog(@"โš ๏ธ Window not contained in any display, using main display ID=%u as fallback", targetDisplay.displayID);
155
+ }
156
+
124
157
  filter = [[SCContentFilter alloc] initWithDesktopIndependentWindow:targetWindow];
125
158
  recordingWidth = (NSInteger)targetWindow.frame.size.width;
126
159
  recordingHeight = (NSInteger)targetWindow.frame.size.height;
@@ -0,0 +1,105 @@
1
+ const MacRecorder = require('./index.js');
2
+
3
+ async function testCursorCoordinates() {
4
+ console.log('๐Ÿงช Testing cursor coordinate fixes for window recording...');
5
+
6
+ const recorder = new MacRecorder();
7
+
8
+ try {
9
+ // Get available windows and displays
10
+ const windows = await recorder.getWindows();
11
+ const displays = await recorder.getDisplays();
12
+
13
+ console.log('\n๐Ÿ“ฑ Available displays:');
14
+ displays.forEach((display, i) => {
15
+ console.log(` ${i + 1}. Display ${display.id}: ${display.resolution} at (${display.x}, ${display.y}) ${display.isPrimary ? '(Primary)' : ''}`);
16
+ });
17
+
18
+ console.log('\n๐ŸชŸ Available windows:');
19
+ windows.slice(0, 5).forEach((window, i) => {
20
+ console.log(` ${i + 1}. ${window.title} (${window.app}) - ${window.width}x${window.height} at (${window.x}, ${window.y})`);
21
+ });
22
+
23
+ if (windows.length === 0) {
24
+ console.log('โŒ No windows available for testing');
25
+ return;
26
+ }
27
+
28
+ // Pick the first available window for testing
29
+ const testWindow = windows[0];
30
+ console.log(`\n๐ŸŽฏ Testing with window: ${testWindow.title}`);
31
+ console.log(` Window position: (${testWindow.x}, ${testWindow.y})`);
32
+ console.log(` Window size: ${testWindow.width}x${testWindow.height}`);
33
+
34
+ // Check current cursor position before recording
35
+ const cursorPos = recorder.getCursorPosition();
36
+ console.log(`\n๐Ÿ–ฑ๏ธ Current cursor position (global): (${cursorPos.x}, ${cursorPos.y})`);
37
+
38
+ // Setup recording options for window recording
39
+ recorder.options.windowId = testWindow.id;
40
+ recorder.options.captureCursor = true;
41
+
42
+ console.log('\n๐Ÿ”ง Starting cursor capture test for window recording...');
43
+
44
+ // Start cursor capture with window-relative coordinates
45
+ const outputFile = `./test-output/cursor-test-window-${Date.now()}.json`;
46
+
47
+ // Start cursor tracking first to see if coordinate transformation works
48
+ await recorder.startCursorCapture(outputFile, {
49
+ windowRelative: true,
50
+ windowInfo: {
51
+ x: testWindow.x,
52
+ y: testWindow.y,
53
+ width: testWindow.width,
54
+ height: testWindow.height,
55
+ displayId: null // Let it auto-detect
56
+ }
57
+ });
58
+
59
+ console.log('โœ… Cursor capture started successfully!');
60
+ console.log('๐Ÿ–ฑ๏ธ Move your mouse around the window for 5 seconds...');
61
+ console.log(` Window bounds: (0, 0) to (${testWindow.width}, ${testWindow.height})`);
62
+
63
+ // Wait for 5 seconds to capture cursor movements
64
+ await new Promise(resolve => setTimeout(resolve, 5000));
65
+
66
+ console.log('\n๐Ÿ›‘ Stopping cursor capture...');
67
+ await recorder.stopCursorCapture();
68
+
69
+ console.log(`โœ… Test completed! Check cursor data in: ${outputFile}`);
70
+
71
+ // Read and analyze some cursor data
72
+ const fs = require('fs');
73
+ if (fs.existsSync(outputFile)) {
74
+ const cursorData = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
75
+ console.log(`\n๐Ÿ“Š Captured ${cursorData.length} cursor events`);
76
+
77
+ if (cursorData.length > 0) {
78
+ const first = cursorData[0];
79
+ const last = cursorData[cursorData.length - 1];
80
+
81
+ console.log(` First event: (${first.x}, ${first.y}) - ${first.coordinateSystem}`);
82
+ console.log(` Last event: (${last.x}, ${last.y}) - ${last.coordinateSystem}`);
83
+
84
+ // Check if coordinates are within window bounds
85
+ const inBounds = cursorData.filter(event =>
86
+ event.x >= 0 && event.x < testWindow.width &&
87
+ event.y >= 0 && event.y < testWindow.height
88
+ );
89
+
90
+ console.log(` Events within window bounds: ${inBounds.length}/${cursorData.length} (${Math.round(inBounds.length/cursorData.length*100)}%)`);
91
+
92
+ if (inBounds.length > 0) {
93
+ console.log('โœ… Cursor coordinates appear to be correctly transformed to window-relative!');
94
+ } else {
95
+ console.log('โŒ No cursor coordinates are within window bounds - transformation may be incorrect');
96
+ }
97
+ }
98
+ }
99
+
100
+ } catch (error) {
101
+ console.error('โŒ Test failed:', error.message);
102
+ }
103
+ }
104
+
105
+ testCursorCoordinates().catch(console.error);
@@ -0,0 +1 @@
1
+ [{"x":758,"y":1089,"timestamp":37,"unixTimeMs":1758264693047,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":758,"y":1089,"timestamp":80,"unixTimeMs":1758264693090,"cursorType":"default","type":"mousedown","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":758,"y":1089,"timestamp":99,"unixTimeMs":1758264693109,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":758,"y":1089,"timestamp":180,"unixTimeMs":1758264693190,"cursorType":"default","type":"mouseup","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":758,"y":1089,"timestamp":201,"unixTimeMs":1758264693211,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":774,"y":1071,"timestamp":1610,"unixTimeMs":1758264694620,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":818,"y":1030,"timestamp":1631,"unixTimeMs":1758264694641,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":893,"y":972,"timestamp":1654,"unixTimeMs":1758264694664,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1015,"y":886,"timestamp":1677,"unixTimeMs":1758264694687,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1087,"y":843,"timestamp":1696,"unixTimeMs":1758264694706,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1233,"y":793,"timestamp":1714,"unixTimeMs":1758264694724,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1301,"y":776,"timestamp":1733,"unixTimeMs":1758264694743,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1360,"y":765,"timestamp":1755,"unixTimeMs":1758264694765,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1366,"y":764,"timestamp":1774,"unixTimeMs":1758264694784,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1363,"y":751,"timestamp":1814,"unixTimeMs":1758264694824,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1343,"y":709,"timestamp":1838,"unixTimeMs":1758264694848,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1335,"y":691,"timestamp":1855,"unixTimeMs":1758264694865,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1271,"y":583,"timestamp":1878,"unixTimeMs":1758264694888,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1250,"y":558,"timestamp":1898,"unixTimeMs":1758264694908,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1242,"y":552,"timestamp":1917,"unixTimeMs":1758264694927,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1238,"y":552,"timestamp":1937,"unixTimeMs":1758264694947,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1234,"y":554,"timestamp":1959,"unixTimeMs":1758264694969,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1233,"y":556,"timestamp":2207,"unixTimeMs":1758264695217,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1232,"y":559,"timestamp":2231,"unixTimeMs":1758264695241,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1232,"y":561,"timestamp":2252,"unixTimeMs":1758264695262,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1231,"y":565,"timestamp":2272,"unixTimeMs":1758264695282,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1231,"y":567,"timestamp":2290,"unixTimeMs":1758264695300,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1231,"y":595,"timestamp":2311,"unixTimeMs":1758264695321,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1236,"y":629,"timestamp":2330,"unixTimeMs":1758264695341,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1242,"y":647,"timestamp":2352,"unixTimeMs":1758264695362,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1251,"y":670,"timestamp":2369,"unixTimeMs":1758264695379,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1259,"y":702,"timestamp":2391,"unixTimeMs":1758264695401,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1265,"y":746,"timestamp":2409,"unixTimeMs":1758264695419,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1276,"y":763,"timestamp":2430,"unixTimeMs":1758264695440,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1272,"y":760,"timestamp":2985,"unixTimeMs":1758264695995,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1270,"y":758,"timestamp":3005,"unixTimeMs":1758264696015,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1269,"y":756,"timestamp":3047,"unixTimeMs":1758264696057,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1268,"y":754,"timestamp":3089,"unixTimeMs":1758264696099,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1268,"y":752,"timestamp":3132,"unixTimeMs":1758264696142,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1267,"y":750,"timestamp":3173,"unixTimeMs":1758264696183,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1266,"y":747,"timestamp":3215,"unixTimeMs":1758264696225,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1265,"y":745,"timestamp":3238,"unixTimeMs":1758264696248,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1263,"y":742,"timestamp":3278,"unixTimeMs":1758264696288,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1264,"y":740,"timestamp":3482,"unixTimeMs":1758264696492,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1266,"y":739,"timestamp":3522,"unixTimeMs":1758264696532,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1268,"y":738,"timestamp":3541,"unixTimeMs":1758264696551,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1277,"y":731,"timestamp":3563,"unixTimeMs":1758264696573,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1279,"y":730,"timestamp":3583,"unixTimeMs":1758264696593,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1281,"y":729,"timestamp":3688,"unixTimeMs":1758264696698,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1283,"y":727,"timestamp":3708,"unixTimeMs":1758264696718,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1286,"y":718,"timestamp":3873,"unixTimeMs":1758264696883,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1287,"y":713,"timestamp":3893,"unixTimeMs":1758264696903,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1289,"y":713,"timestamp":4204,"unixTimeMs":1758264697214,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}},{"x":1291,"y":713,"timestamp":4330,"unixTimeMs":1758264697340,"cursorType":"default","type":"move","coordinateSystem":"window-relative","windowInfo":{"width":3440,"height":1440,"displayId":null}}]