node-mac-recorder 2.20.0 → 2.20.1

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.
@@ -1,15 +1,8 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(npm test)",
5
- "Bash(node:*)",
6
- "Bash(npm install)",
7
- "Bash(npm run clean:*)",
8
- "Bash(npm run build:*)",
9
- "Bash(npm run rebuild:*)",
10
- "Bash(system_profiler:*)",
11
- "Bash(sw_vers)",
12
- "Bash(find:*)"
4
+ "Bash(node-gyp:*)",
5
+ "Bash(node:*)"
13
6
  ],
14
7
  "deny": [],
15
8
  "ask": []
package/index.js CHANGED
@@ -87,11 +87,7 @@ class MacRecorder extends EventEmitter {
87
87
  x: display.x,
88
88
  y: display.y,
89
89
  isPrimary: display.isPrimary,
90
- scaleFactor: display.scaleFactor || 1.0,
91
- physicalWidth: display.physicalWidth || display.width,
92
- physicalHeight: display.physicalHeight || display.height,
93
90
  resolution: `${display.width}x${display.height}`,
94
- physicalResolution: `${display.physicalWidth || display.width}x${display.physicalHeight || display.height}`,
95
91
  }));
96
92
  }
97
93
 
@@ -775,31 +771,24 @@ class MacRecorder extends EventEmitter {
775
771
  // JavaScript interval ile polling yap (daha sık - mouse event'leri yakalamak için)
776
772
  this.cursorCaptureInterval = setInterval(() => {
777
773
  try {
778
- // Get cursor position with recording coordinates
779
- const cursorData = this.getCursorPosition();
774
+ const position = nativeBinding.getCursorPosition();
780
775
  const timestamp = Date.now() - this.cursorCaptureStartTime;
781
776
 
782
- // Use recording coordinates for video overlay alignment
783
- let x = cursorData.recordingX || cursorData.x;
784
- let y = cursorData.recordingY || cursorData.y;
785
- let coordinateSystem = "recording-coordinates";
777
+ // Video-relative coordinate transformation for all recording types
778
+ let x = position.x;
779
+ let y = position.y;
780
+ let coordinateSystem = "global";
786
781
 
787
- // Apply video-relative transformation for window/area recording
782
+ // Apply video-relative transformation for all recording types
788
783
  if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
789
- // For window/area recording: adjust coordinates relative to capture area
790
- if (this.cursorDisplayInfo.videoOffsetX || this.cursorDisplayInfo.videoOffsetY) {
791
- // Convert from full recording resolution to capture area coordinates
792
- const recordingScaleX = cursorData.scaleX || 1;
793
- const recordingScaleY = cursorData.scaleY || 1;
794
-
795
- // Scale offsets to recording resolution
796
- const scaledOffsetX = this.cursorDisplayInfo.videoOffsetX * recordingScaleX;
797
- const scaledOffsetY = this.cursorDisplayInfo.videoOffsetY * recordingScaleY;
798
-
799
- x = x - scaledOffsetX;
800
- y = y - scaledOffsetY;
801
- coordinateSystem = "video-relative";
802
- }
784
+ // Step 1: Transform global → display-relative coordinates
785
+ const displayRelativeX = position.x - this.cursorDisplayInfo.displayX;
786
+ const displayRelativeY = position.y - this.cursorDisplayInfo.displayY;
787
+
788
+ // Step 2: Transform display-relative → video-relative coordinates
789
+ x = displayRelativeX - this.cursorDisplayInfo.videoOffsetX;
790
+ y = displayRelativeY - this.cursorDisplayInfo.videoOffsetY;
791
+ coordinateSystem = "video-relative";
803
792
 
804
793
  // Bounds check for video area (don't skip, just note if outside)
805
794
  const outsideVideo = x < 0 || y < 0 ||
@@ -812,30 +801,16 @@ class MacRecorder extends EventEmitter {
812
801
  }
813
802
  }
814
803
 
815
- const captureData = {
804
+ const cursorData = {
816
805
  x: x,
817
806
  y: y,
818
807
  timestamp: timestamp,
819
808
  unixTimeMs: Date.now(),
820
- cursorType: cursorData.cursorType,
821
- type: cursorData.eventType || "move",
809
+ cursorType: position.cursorType,
810
+ type: position.eventType || "move",
822
811
  coordinateSystem: coordinateSystem,
823
- // Recording metadata for video overlay alignment
812
+ // Video-relative metadata for all recording types
824
813
  recordingType: this.cursorDisplayInfo?.recordingType || "display",
825
- recordingInfo: {
826
- // Logical coordinates for backward compatibility
827
- logicalX: cursorData.x,
828
- logicalY: cursorData.y,
829
- // Recording coordinates for video overlay
830
- recordingX: x,
831
- recordingY: y,
832
- // Recording resolution
833
- recordingWidth: cursorData.recordingWidth,
834
- recordingHeight: cursorData.recordingHeight,
835
- // Scale factors
836
- scaleX: cursorData.scaleX || 1,
837
- scaleY: cursorData.scaleY || 1
838
- },
839
814
  videoInfo: this.cursorDisplayInfo ? {
840
815
  width: this.cursorDisplayInfo.videoWidth,
841
816
  height: this.cursorDisplayInfo.videoHeight,
@@ -850,9 +825,9 @@ class MacRecorder extends EventEmitter {
850
825
  };
851
826
 
852
827
  // Sadece eventType değiştiğinde veya pozisyon değiştiğinde kaydet
853
- if (this.shouldCaptureEvent(captureData)) {
828
+ if (this.shouldCaptureEvent(cursorData)) {
854
829
  // Dosyaya ekle
855
- const jsonString = JSON.stringify(captureData);
830
+ const jsonString = JSON.stringify(cursorData);
856
831
 
857
832
  if (this.cursorCaptureFirstWrite) {
858
833
  fs.appendFileSync(filepath, jsonString);
@@ -862,7 +837,7 @@ class MacRecorder extends EventEmitter {
862
837
  }
863
838
 
864
839
  // Son pozisyonu sakla
865
- this.lastCapturedData = { ...captureData };
840
+ this.lastCapturedData = { ...cursorData };
866
841
  }
867
842
  } catch (error) {
868
843
  console.error("Cursor capture error:", error);
@@ -921,123 +896,12 @@ class MacRecorder extends EventEmitter {
921
896
  const position = nativeBinding.getCursorPosition();
922
897
 
923
898
  // Cursor hangi display'de ise o display'e relative döndür
924
- const displayRelativePosition = this.getDisplayRelativePositionSync(position);
925
-
926
- // Add recording coordinates for video overlay alignment
927
- const recordingCoordinates = this.calculateRecordingCoordinates(displayRelativePosition);
928
-
929
- return {
930
- ...displayRelativePosition,
931
- // Recording coordinates for video overlay (matches actual video resolution)
932
- recordingX: recordingCoordinates.x,
933
- recordingY: recordingCoordinates.y,
934
- recordingWidth: recordingCoordinates.width,
935
- recordingHeight: recordingCoordinates.height,
936
- // Scale factors for reference
937
- scaleX: recordingCoordinates.scaleX,
938
- scaleY: recordingCoordinates.scaleY,
939
- // Original coordinates for backward compatibility
940
- x: displayRelativePosition.x,
941
- y: displayRelativePosition.y
942
- };
899
+ return this.getDisplayRelativePositionSync(position);
943
900
  } catch (error) {
944
901
  throw new Error("Failed to get cursor position: " + error.message);
945
902
  }
946
903
  }
947
904
 
948
- /**
949
- * Calculate cursor coordinates for recording video overlay alignment
950
- * Converts logical display coordinates to actual video recording coordinates
951
- */
952
- calculateRecordingCoordinates(displayPosition) {
953
- try {
954
- // Get current display information
955
- if (!this.cachedDisplays) {
956
- this.refreshDisplayCache();
957
- }
958
-
959
- // If still null, use native binding directly
960
- if (!this.cachedDisplays) {
961
- try {
962
- const nativeDisplays = nativeBinding.getDisplays();
963
- this.cachedDisplays = nativeDisplays.map((display, index) => ({
964
- id: display.id,
965
- name: display.name,
966
- width: display.width,
967
- height: display.height,
968
- x: display.x,
969
- y: display.y,
970
- isPrimary: display.isPrimary,
971
- scaleFactor: display.scaleFactor || 1.0,
972
- physicalWidth: display.physicalWidth || display.width,
973
- physicalHeight: display.physicalHeight || display.height,
974
- resolution: `${display.width}x${display.height}`,
975
- }));
976
- } catch (e) {
977
- console.warn('Failed to get displays from native binding:', e.message);
978
- }
979
- }
980
-
981
- // Find the target display
982
- let targetDisplay = null;
983
- if (displayPosition.displayId) {
984
- targetDisplay = this.cachedDisplays.find(d => d.id === displayPosition.displayId);
985
- }
986
- if (!targetDisplay) {
987
- targetDisplay = this.cachedDisplays.find(d => d.isPrimary) || this.cachedDisplays[0];
988
- }
989
-
990
- if (!targetDisplay) {
991
- // Fallback: assume 1:1 mapping
992
- return {
993
- x: displayPosition.x,
994
- y: displayPosition.y,
995
- width: displayPosition.x,
996
- height: displayPosition.y
997
- };
998
- }
999
-
1000
- // Parse display dimensions
1001
- const logicalWidth = parseInt(targetDisplay.resolution.split("x")[0]);
1002
- const logicalHeight = parseInt(targetDisplay.resolution.split("x")[1]);
1003
-
1004
- // Get physical recording dimensions
1005
- // On Retina displays, recording is done at physical resolution (2x logical)
1006
- const physicalWidth = targetDisplay.physicalWidth || (logicalWidth * (targetDisplay.scaleFactor || 1));
1007
- const physicalHeight = targetDisplay.physicalHeight || (logicalHeight * (targetDisplay.scaleFactor || 1));
1008
-
1009
- // Calculate scale factors for coordinate conversion
1010
- const scaleX = physicalWidth / logicalWidth;
1011
- const scaleY = physicalHeight / logicalHeight;
1012
-
1013
- // Convert logical cursor coordinates to recording coordinates
1014
- const recordingX = Math.round(displayPosition.x * scaleX);
1015
- const recordingY = Math.round(displayPosition.y * scaleY);
1016
-
1017
- return {
1018
- x: recordingX,
1019
- y: recordingY,
1020
- width: physicalWidth,
1021
- height: physicalHeight,
1022
- scaleX: scaleX,
1023
- scaleY: scaleY,
1024
- logicalWidth: logicalWidth,
1025
- logicalHeight: logicalHeight
1026
- };
1027
- } catch (error) {
1028
- console.warn('Failed to calculate recording coordinates:', error.message);
1029
- // Fallback: 1:1 mapping
1030
- return {
1031
- x: displayPosition.x,
1032
- y: displayPosition.y,
1033
- width: displayPosition.x,
1034
- height: displayPosition.y,
1035
- scaleX: 1,
1036
- scaleY: 1
1037
- };
1038
- }
1039
- }
1040
-
1041
905
  /**
1042
906
  * Global koordinatları en uygun display'e relative çevirir (sync version)
1043
907
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.20.0",
3
+ "version": "2.20.1",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -5,6 +5,7 @@
5
5
  #import <ApplicationServices/ApplicationServices.h>
6
6
  #import <Carbon/Carbon.h>
7
7
  #import <Accessibility/Accessibility.h>
8
+ #import <dispatch/dispatch.h>
8
9
 
9
10
  // Global state for cursor tracking
10
11
  static bool g_isCursorTracking = false;
@@ -49,34 +50,13 @@ static CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEv
49
50
  return event;
50
51
  }
51
52
 
52
- // Cursor type detection helper - sistem genelindeki cursor type'ı al
53
- NSString* getCursorType() {
53
+ // Accessibility tabanlı cursor tip tespiti
54
+ static NSString* detectCursorTypeUsingAccessibility(CGPoint cursorPos) {
54
55
  @autoreleasepool {
55
- g_cursorTypeCounter++;
56
-
57
56
  @try {
58
57
  // ACCESSIBILITY API BASED CURSOR DETECTION
59
58
  // Determine cursor type based on the UI element under the cursor
60
59
 
61
- // MACOS 14 FIX: Get cursor position for accessibility API
62
- CGPoint cursorPos;
63
-
64
- if (@available(macOS 10.15, *)) {
65
- // Use NSEvent method for macOS 14 compatibility
66
- NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
67
-
68
- // Convert NSEvent bottom-left coordinates to top-left
69
- CGDirectDisplayID primaryDisplay = CGMainDisplayID();
70
- CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
71
-
72
- cursorPos = CGPointMake(
73
- mouseLocationInWindow.x,
74
- primaryBounds.size.height - mouseLocationInWindow.y
75
- );
76
- } else {
77
- // Fallback for older macOS versions
78
- cursorPos = CGEventGetLocation(CGEventCreate(NULL));
79
- }
80
60
  AXUIElementRef systemWide = AXUIElementCreateSystemWide();
81
61
  AXUIElementRef elementAtPosition = NULL;
82
62
  AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
@@ -298,9 +278,11 @@ NSString* getCursorType() {
298
278
  }
299
279
  }
300
280
 
301
- CFRelease(elementAtPosition);
302
281
  }
303
282
 
283
+ if (elementAtPosition) {
284
+ CFRelease(elementAtPosition);
285
+ }
304
286
  if (systemWide) {
305
287
  CFRelease(systemWide);
306
288
  }
@@ -310,16 +292,244 @@ NSString* getCursorType() {
310
292
  cursorType = @"default";
311
293
  }
312
294
 
313
- NSLog(@"🎯 FINAL CURSOR TYPE: %@", cursorType);
295
+ NSLog(@"🎯 AX CURSOR TYPE: %@", cursorType);
314
296
  return cursorType;
315
297
 
316
298
  } @catch (NSException *exception) {
317
- NSLog(@"Error in getCursorType: %@", exception);
299
+ NSLog(@"Error in detectCursorTypeUsingAccessibility: %@", exception);
318
300
  return @"default";
319
301
  }
320
302
  }
321
303
  }
322
304
 
305
+ static NSString* cursorTypeFromCursorName(NSString *value) {
306
+ if (!value || [value length] == 0) {
307
+ return nil;
308
+ }
309
+
310
+ NSString *normalized = [[value stringByReplacingOccurrencesOfString:@"_" withString:@"-"] lowercaseString];
311
+
312
+ if ([normalized containsString:@"arrow"]) {
313
+ return @"default";
314
+ }
315
+ if ([normalized containsString:@"ibeam"] ||
316
+ [normalized containsString:@"insertion"] ||
317
+ [normalized containsString:@"text"]) {
318
+ return @"text";
319
+ }
320
+ if ([normalized containsString:@"openhand"]) {
321
+ return @"grab";
322
+ }
323
+ if ([normalized containsString:@"closedhand"]) {
324
+ return @"grabbing";
325
+ }
326
+ if ([normalized containsString:@"pointing"] ||
327
+ ([normalized containsString:@"hand"] && ![normalized containsString:@"closed"])) {
328
+ return @"pointer";
329
+ }
330
+ if ([normalized containsString:@"crosshair"]) {
331
+ return @"crosshair";
332
+ }
333
+ if ([normalized containsString:@"not-allowed"] ||
334
+ [normalized containsString:@"notallowed"] ||
335
+ [normalized containsString:@"forbidden"]) {
336
+ return @"not-allowed";
337
+ }
338
+ if ([normalized containsString:@"dragcopy"] || [normalized containsString:@"copy"]) {
339
+ return @"copy";
340
+ }
341
+ if ([normalized containsString:@"draglink"] || [normalized containsString:@"alias"]) {
342
+ return @"alias";
343
+ }
344
+ if ([normalized containsString:@"context"] && [normalized containsString:@"menu"]) {
345
+ return @"context-menu";
346
+ }
347
+ if ([normalized containsString:@"zoom"]) {
348
+ if ([normalized containsString:@"out"]) {
349
+ return @"zoom-out";
350
+ }
351
+ return @"zoom-in";
352
+ }
353
+ if ([normalized containsString:@"resize"] || [normalized containsString:@"size"]) {
354
+ BOOL diagonalUp = [normalized containsString:@"diagonalup"] || [normalized containsString:@"nesw"];
355
+ BOOL diagonalDown = [normalized containsString:@"diagonaldown"] || [normalized containsString:@"nwse"];
356
+ BOOL horizontal = [normalized containsString:@"leftright"] ||
357
+ [normalized containsString:@"horizontal"] ||
358
+ ([normalized containsString:@"left"] && [normalized containsString:@"right"]);
359
+ BOOL vertical = [normalized containsString:@"updown"] ||
360
+ [normalized containsString:@"vertical"] ||
361
+ ([normalized containsString:@"up"] && [normalized containsString:@"down"]);
362
+
363
+ if (diagonalUp) {
364
+ return @"nesw-resize";
365
+ }
366
+ if (diagonalDown) {
367
+ return @"nwse-resize";
368
+ }
369
+ if (vertical) {
370
+ return @"ns-resize";
371
+ }
372
+ if (horizontal) {
373
+ return @"col-resize";
374
+ }
375
+ }
376
+
377
+ return nil;
378
+ }
379
+
380
+ static NSString* cursorTypeFromNSCursor(NSCursor *cursor) {
381
+ if (!cursor) {
382
+ return nil;
383
+ }
384
+
385
+ if (cursor == [NSCursor arrowCursor]) {
386
+ return @"default";
387
+ }
388
+ if (cursor == [NSCursor IBeamCursor]) {
389
+ return @"text";
390
+ }
391
+ if ([NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] &&
392
+ cursor == [NSCursor IBeamCursorForVerticalLayout]) {
393
+ return @"text";
394
+ }
395
+ if (cursor == [NSCursor pointingHandCursor]) {
396
+ return @"pointer";
397
+ }
398
+ if (cursor == [NSCursor crosshairCursor]) {
399
+ return @"crosshair";
400
+ }
401
+ if (cursor == [NSCursor openHandCursor]) {
402
+ return @"grab";
403
+ }
404
+ if (cursor == [NSCursor closedHandCursor]) {
405
+ return @"grabbing";
406
+ }
407
+ if (cursor == [NSCursor operationNotAllowedCursor]) {
408
+ return @"not-allowed";
409
+ }
410
+ if (cursor == [NSCursor dragCopyCursor]) {
411
+ return @"copy";
412
+ }
413
+ if (cursor == [NSCursor dragLinkCursor]) {
414
+ return @"alias";
415
+ }
416
+ if (cursor == [NSCursor contextualMenuCursor]) {
417
+ return @"context-menu";
418
+ }
419
+ if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)] &&
420
+ (cursor == [NSCursor resizeLeftRightCursor] ||
421
+ cursor == [NSCursor resizeLeftCursor] ||
422
+ cursor == [NSCursor resizeRightCursor])) {
423
+ return @"col-resize";
424
+ }
425
+ if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)] &&
426
+ (cursor == [NSCursor resizeUpDownCursor] ||
427
+ cursor == [NSCursor resizeUpCursor] ||
428
+ cursor == [NSCursor resizeDownCursor])) {
429
+ return @"ns-resize";
430
+ }
431
+ if ([NSCursor respondsToSelector:@selector(disappearingItemCursor)] &&
432
+ cursor == [NSCursor disappearingItemCursor]) {
433
+ return @"default";
434
+ }
435
+
436
+ NSString *derived = cursorTypeFromCursorName(NSStringFromClass([cursor class]));
437
+ if (derived) {
438
+ return derived;
439
+ }
440
+
441
+ derived = cursorTypeFromCursorName([cursor description]);
442
+ if (derived) {
443
+ return derived;
444
+ }
445
+
446
+ return nil;
447
+ }
448
+
449
+ static NSString* detectSystemCursorType(void) {
450
+ __block NSString *cursorType = nil;
451
+
452
+ void (^fetchCursorBlock)(void) = ^{
453
+ NSCursor *currentCursor = nil;
454
+ if ([NSCursor respondsToSelector:@selector(currentSystemCursor)]) {
455
+ currentCursor = [NSCursor currentSystemCursor];
456
+ }
457
+ if (!currentCursor) {
458
+ currentCursor = [NSCursor currentCursor];
459
+ }
460
+
461
+ NSString *derivedType = cursorTypeFromNSCursor(currentCursor);
462
+ if (derivedType) {
463
+ cursorType = derivedType;
464
+ } else if (currentCursor) {
465
+ cursorType = @"default";
466
+ }
467
+
468
+ NSLog(@"🎯 SYSTEM CURSOR TYPE: %@", cursorType ? cursorType : @"(nil)");
469
+ };
470
+
471
+ if ([NSThread isMainThread]) {
472
+ fetchCursorBlock();
473
+ } else {
474
+ dispatch_sync(dispatch_get_main_queue(), fetchCursorBlock);
475
+ }
476
+
477
+ return cursorType;
478
+ }
479
+
480
+ NSString* getCursorType() {
481
+ @autoreleasepool {
482
+ g_cursorTypeCounter++;
483
+
484
+ NSString *systemCursorType = detectSystemCursorType();
485
+ if (systemCursorType && ![systemCursorType isEqualToString:@"default"]) {
486
+ NSLog(@"🎯 FINAL CURSOR TYPE: %@", systemCursorType);
487
+ return systemCursorType;
488
+ }
489
+
490
+ NSString *axCursorType = nil;
491
+ BOOL hasCursorPosition = NO;
492
+ CGPoint cursorPos = CGPointZero;
493
+
494
+ CGEventRef event = CGEventCreate(NULL);
495
+ if (event) {
496
+ cursorPos = CGEventGetLocation(event);
497
+ hasCursorPosition = YES;
498
+ CFRelease(event);
499
+ }
500
+
501
+ if (!hasCursorPosition) {
502
+ if ([NSThread isMainThread]) {
503
+ cursorPos = [NSEvent mouseLocation];
504
+ hasCursorPosition = YES;
505
+ } else {
506
+ __block CGPoint fallbackPos = CGPointZero;
507
+ dispatch_sync(dispatch_get_main_queue(), ^{
508
+ fallbackPos = [NSEvent mouseLocation];
509
+ });
510
+ cursorPos = fallbackPos;
511
+ hasCursorPosition = YES;
512
+ }
513
+ }
514
+
515
+ if (hasCursorPosition) {
516
+ axCursorType = detectCursorTypeUsingAccessibility(cursorPos);
517
+ }
518
+
519
+ NSString *finalType = nil;
520
+ if (axCursorType && ![axCursorType isEqualToString:@"default"]) {
521
+ finalType = axCursorType;
522
+ } else if (systemCursorType && [systemCursorType length] > 0) {
523
+ finalType = systemCursorType;
524
+ } else {
525
+ finalType = @"default";
526
+ }
527
+
528
+ NSLog(@"🎯 FINAL CURSOR TYPE: %@", finalType);
529
+ return finalType;
530
+ }
531
+ }
532
+
323
533
  // Dosyaya yazma helper fonksiyonu
324
534
  void writeToFile(NSDictionary *cursorData) {
325
535
  @autoreleasepool {
@@ -363,31 +573,7 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
363
573
  return event;
364
574
  }
365
575
 
366
- // MACOS 14 FIX: Use alternative coordinate method if available
367
- CGPoint rawLocation;
368
-
369
- if (@available(macOS 10.15, *)) {
370
- // For event callbacks, we can still use CGEventGetLocation
371
- // but we'll apply coordinate correction if needed
372
- rawLocation = CGEventGetLocation(event);
373
-
374
- // Additional validation for macOS 14 scaled displays
375
- if (rawLocation.x > 5000 || rawLocation.y > 5000) {
376
- // Coordinates seem unreasonable, try NSEvent method as backup
377
- NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
378
-
379
- // Convert NSEvent bottom-left coordinates to top-left
380
- CGDirectDisplayID primaryDisplay = CGMainDisplayID();
381
- CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
382
-
383
- rawLocation = CGPointMake(
384
- mouseLocationInWindow.x,
385
- primaryBounds.size.height - mouseLocationInWindow.y
386
- );
387
- }
388
- } else {
389
- rawLocation = CGEventGetLocation(event);
390
- }
576
+ CGPoint rawLocation = CGEventGetLocation(event);
391
577
 
392
578
  // Apply DPR scaling correction for Retina displays
393
579
  NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
@@ -455,28 +641,11 @@ void cursorTimerCallback() {
455
641
  return;
456
642
  }
457
643
 
458
- // MACOS 14 FIX: Get cursor position with DPR scaling correction
459
- CGPoint rawLocation;
460
-
461
- if (@available(macOS 10.15, *)) {
462
- // Use NSEvent method for better macOS 14 compatibility
463
- NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
464
-
465
- // Convert NSEvent bottom-left coordinates to top-left
466
- CGDirectDisplayID primaryDisplay = CGMainDisplayID();
467
- CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
468
-
469
- rawLocation = CGPointMake(
470
- mouseLocationInWindow.x,
471
- primaryBounds.size.height - mouseLocationInWindow.y
472
- );
473
- } else {
474
- // Fallback for older macOS versions
475
- CGEventRef event = CGEventCreate(NULL);
476
- rawLocation = CGEventGetLocation(event);
477
- if (event) {
478
- CFRelease(event);
479
- }
644
+ // Get cursor position with DPR scaling correction
645
+ CGEventRef event = CGEventCreate(NULL);
646
+ CGPoint rawLocation = CGEventGetLocation(event);
647
+ if (event) {
648
+ CFRelease(event);
480
649
  }
481
650
 
482
651
  // Apply DPR scaling correction for Retina displays
@@ -809,48 +978,23 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
809
978
  Napi::Env env = info.Env();
810
979
 
811
980
  @try {
812
- // MACOS 14 FIX: Use NSEvent mouseLocation instead of CGEventGetLocation
813
- // CGEventGetLocation has coordinate system issues on macOS 14 with scaled displays
814
- CGPoint rawLocation;
815
-
816
- if (@available(macOS 10.15, *)) {
817
- // Try NSEvent method first (more reliable on macOS 14)
818
- NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
819
-
820
- // NSEvent.mouseLocation returns coordinates in the global coordinate space
821
- // where (0,0) is at bottom-left of PRIMARY SCREEN
822
- // CGDisplayBounds uses (0,0) at top-left
823
- // We need to convert Y coordinate from bottom-left to top-left
824
-
825
- CGDirectDisplayID primaryDisplay = CGMainDisplayID();
826
- CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
827
-
828
- rawLocation = CGPointMake(
829
- mouseLocationInWindow.x,
830
- primaryBounds.size.height - mouseLocationInWindow.y
831
- );
832
-
833
- NSLog(@"🔧 macOS 14 cursor fix: NSEvent(%.0f,%.0f) -> CGPoint(%.0f,%.0f)",
834
- mouseLocationInWindow.x, mouseLocationInWindow.y,
835
- rawLocation.x, rawLocation.y);
836
- } else {
837
- // Fallback for older macOS versions
838
- CGEventRef event = CGEventCreate(NULL);
839
- rawLocation = CGEventGetLocation(event);
840
- if (event) {
841
- CFRelease(event);
842
- }
981
+ // Get raw cursor position (may be scaled on Retina displays)
982
+ CGEventRef event = CGEventCreate(NULL);
983
+ CGPoint rawLocation = CGEventGetLocation(event);
984
+ if (event) {
985
+ CFRelease(event);
843
986
  }
844
-
987
+
845
988
  // Get display scaling information
846
989
  NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
847
990
  CGPoint logicalLocation = rawLocation;
848
-
991
+
849
992
  if (scalingInfo) {
850
993
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
851
994
  NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
852
-
853
- // Use the corrected coordinates
995
+
996
+ // CGEventGetLocation returns LOGICAL coordinates (correct for JS layer)
997
+ // Keep logical coordinates - transformation happens in JS layer
854
998
  logicalLocation = rawLocation;
855
999
  }
856
1000
 
@@ -944,4 +1088,4 @@ Napi::Object InitCursorTracker(Napi::Env env, Napi::Object exports) {
944
1088
  exports.Set("getCursorTrackingStatus", Napi::Function::New(env, GetCursorTrackingStatus));
945
1089
 
946
1090
  return exports;
947
- }
1091
+ }
@@ -577,60 +577,15 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
577
577
  CGDirectDisplayID displayID = activeDisplays[i];
578
578
  CGRect displayBounds = CGDisplayBounds(displayID);
579
579
  bool isPrimary = (displayID == CGMainDisplayID());
580
-
581
- // Calculate scale factor and physical dimensions
582
- CGSize logicalSize = displayBounds.size;
583
- CGFloat scaleFactor = 1.0;
584
- CGSize physicalSize = logicalSize;
585
- CGSize actualPhysicalSize = logicalSize;
586
-
587
- // Get actual display mode info first (most accurate)
588
- CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID);
589
- if (mode) {
590
- size_t modePixelWidth = CGDisplayModeGetPixelWidth(mode);
591
- size_t modePixelHeight = CGDisplayModeGetPixelHeight(mode);
592
- actualPhysicalSize = CGSizeMake(modePixelWidth, modePixelHeight);
593
- CGDisplayModeRelease(mode);
594
- }
595
-
596
- // Use NSScreen for backing scale factor (this is the UI scaling)
597
- NSArray *screens = [NSScreen screens];
598
- for (NSScreen *screen in screens) {
599
- NSDictionary *screenDescription = [screen deviceDescription];
600
- NSNumber *screenNumber = [screenDescription objectForKey:@"NSScreenNumber"];
601
- if (screenNumber && [screenNumber unsignedIntValue] == displayID) {
602
- scaleFactor = [screen backingScaleFactor];
603
-
604
- // For cursor tracking, we want to use the logical coordinates that macOS reports
605
- // Physical size should be what the system actually uses for display output
606
- physicalSize = actualPhysicalSize;
607
-
608
- NSLog(@"🔍 Display %u scale analysis:", displayID);
609
- NSLog(@" Logical bounds: %.0fx%.0f", logicalSize.width, logicalSize.height);
610
- NSLog(@" NSScreen backing scale: %.1fx", scaleFactor);
611
- NSLog(@" CGDisplayMode pixels: %.0fx%.0f", actualPhysicalSize.width, actualPhysicalSize.height);
612
- NSLog(@" Calculated scale: %.2fx", actualPhysicalSize.width / logicalSize.width);
613
- break;
614
- }
615
- }
616
-
617
- // Fallback if NSScreen method didn't work
618
- if (scaleFactor == 1.0 && actualPhysicalSize.width > logicalSize.width) {
619
- scaleFactor = actualPhysicalSize.width / logicalSize.width;
620
- physicalSize = actualPhysicalSize;
621
- }
622
-
580
+
623
581
  NSDictionary *displayInfo = @{
624
582
  @"id": @(displayID), // Direct CGDirectDisplayID
625
583
  @"name": [NSString stringWithFormat:@"Display %u", (unsigned int)(i + 1)],
626
- @"width": @((int)logicalSize.width),
627
- @"height": @((int)logicalSize.height),
584
+ @"width": @((int)displayBounds.size.width),
585
+ @"height": @((int)displayBounds.size.height),
628
586
  @"x": @((int)displayBounds.origin.x),
629
587
  @"y": @((int)displayBounds.origin.y),
630
- @"isPrimary": @(isPrimary),
631
- @"scaleFactor": @(scaleFactor),
632
- @"physicalWidth": @((int)physicalSize.width),
633
- @"physicalHeight": @((int)physicalSize.height)
588
+ @"isPrimary": @(isPrimary)
634
589
  };
635
590
  [displays addObject:displayInfo];
636
591
  }
@@ -640,16 +595,13 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
640
595
 
641
596
  for (NSUInteger i = 0; i < displays.count; i++) {
642
597
  NSDictionary *display = displays[i];
643
- NSLog(@"Display %lu: ID=%u, Name=%@, Logical=%@x%@, Physical=%@x%@, Scale=%.1fx",
598
+ NSLog(@"Display %lu: ID=%u, Name=%@, Size=%@x%@",
644
599
  (unsigned long)i,
645
600
  [display[@"id"] unsignedIntValue],
646
601
  display[@"name"],
647
602
  display[@"width"],
648
- display[@"height"],
649
- display[@"physicalWidth"],
650
- display[@"physicalHeight"],
651
- [display[@"scaleFactor"] doubleValue]);
652
-
603
+ display[@"height"]);
604
+
653
605
  Napi::Object displayObj = Napi::Object::New(env);
654
606
  displayObj.Set("id", Napi::Number::New(env, [display[@"id"] unsignedIntValue]));
655
607
  displayObj.Set("name", Napi::String::New(env, [display[@"name"] UTF8String]));
@@ -658,9 +610,6 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
658
610
  displayObj.Set("x", Napi::Number::New(env, [display[@"x"] doubleValue]));
659
611
  displayObj.Set("y", Napi::Number::New(env, [display[@"y"] doubleValue]));
660
612
  displayObj.Set("isPrimary", Napi::Boolean::New(env, [display[@"isPrimary"] boolValue]));
661
- displayObj.Set("scaleFactor", Napi::Number::New(env, [display[@"scaleFactor"] doubleValue]));
662
- displayObj.Set("physicalWidth", Napi::Number::New(env, [display[@"physicalWidth"] doubleValue]));
663
- displayObj.Set("physicalHeight", Napi::Number::New(env, [display[@"physicalHeight"] doubleValue]));
664
613
  result[i] = displayObj;
665
614
  }
666
615
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  // Pure ScreenCaptureKit implementation - NO AVFoundation
4
4
  static SCStream * API_AVAILABLE(macos(12.3)) g_stream = nil;
5
- static id g_recordingOutput API_AVAILABLE(macos(15.0)) = nil;
5
+ static SCRecordingOutput * API_AVAILABLE(macos(15.0)) g_recordingOutput = nil;
6
6
  static id<SCStreamDelegate> API_AVAILABLE(macos(12.3)) g_streamDelegate = nil;
7
7
  static BOOL g_isRecording = NO;
8
8
  static BOOL g_isCleaningUp = NO; // Prevent recursive cleanup
@@ -42,8 +42,7 @@ static NSString *g_outputPath = nil;
42
42
 
43
43
  + (BOOL)isScreenCaptureKitAvailable {
44
44
  if (@available(macOS 15.0, *)) {
45
- Class recordingOutputClass = NSClassFromString(@"SCRecordingOutput");
46
- return [SCShareableContent class] != nil && [SCStream class] != nil && recordingOutputClass != nil;
45
+ return [SCShareableContent class] != nil && [SCStream class] != nil && [SCRecordingOutput class] != nil;
47
46
  }
48
47
  return NO;
49
48
  }
@@ -276,23 +275,17 @@ static NSString *g_outputPath = nil;
276
275
  }
277
276
 
278
277
  if (@available(macOS 15.0, *)) {
279
- // Create recording output configuration using runtime class lookup
280
- Class recordingConfigClass = NSClassFromString(@"SCRecordingOutputConfiguration");
281
- Class recordingOutputClass = NSClassFromString(@"SCRecordingOutput");
282
-
283
- if (recordingConfigClass && recordingOutputClass) {
284
- id recordingConfig = [[recordingConfigClass alloc] init];
285
- [recordingConfig setValue:outputURL forKey:@"outputURL"];
286
- // Use string constant for video codec
287
- [recordingConfig setValue:@"avc1" forKey:@"videoCodecType"];
288
-
289
- // Audio configuration - using available properties
290
- // Note: Specific audio routing handled by ScreenCaptureKit automatically
291
-
292
- // Create recording output with correct initializer
293
- g_recordingOutput = [[recordingOutputClass alloc] initWithConfiguration:recordingConfig
294
- delegate:nil];
295
- }
278
+ // Create recording output configuration
279
+ SCRecordingOutputConfiguration *recordingConfig = [[SCRecordingOutputConfiguration alloc] init];
280
+ recordingConfig.outputURL = outputURL;
281
+ recordingConfig.videoCodecType = AVVideoCodecTypeH264;
282
+
283
+ // Audio configuration - using available properties
284
+ // Note: Specific audio routing handled by ScreenCaptureKit automatically
285
+
286
+ // Create recording output with correct initializer
287
+ g_recordingOutput = [[SCRecordingOutput alloc] initWithConfiguration:recordingConfig
288
+ delegate:nil];
296
289
  if (shouldCaptureMic && shouldCaptureSystemAudio) {
297
290
  NSLog(@"🔧 Created SCRecordingOutput with microphone and system audio");
298
291
  } else if (shouldCaptureMic) {
@@ -327,9 +320,7 @@ static NSString *g_outputPath = nil;
327
320
  BOOL outputAdded = NO;
328
321
 
329
322
  if (@available(macOS 15.0, *)) {
330
- if (g_recordingOutput && [g_stream respondsToSelector:@selector(addRecordingOutput:error:)]) {
331
- outputAdded = [g_stream addRecordingOutput:g_recordingOutput error:&outputError];
332
- }
323
+ outputAdded = [g_stream addRecordingOutput:g_recordingOutput error:&outputError];
333
324
  }
334
325
 
335
326
  if (!outputAdded || outputError) {
@@ -1 +0,0 @@
1
- [{"x":661,"y":888,"timestamp":75,"unixTimeMs":1758662417142,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -1 +0,0 @@
1
- [{"x":661,"y":888,"timestamp":28,"unixTimeMs":1758662420161,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":691,"y":857,"timestamp":468,"unixTimeMs":1758662420601,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":796,"y":749,"timestamp":490,"unixTimeMs":1758662420623,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":971,"y":567,"timestamp":515,"unixTimeMs":1758662420648,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1262,"y":304,"timestamp":525,"unixTimeMs":1758662420658,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1519,"y":95,"timestamp":583,"unixTimeMs":1758662420716,"cursorType":"pointer","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":621,"unixTimeMs":1758662420754,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":0,"timestamp":1510,"unixTimeMs":1758662421643,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":0,"timestamp":1526,"unixTimeMs":1758662421659,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":0,"timestamp":1549,"unixTimeMs":1758662421682,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":0,"timestamp":1633,"unixTimeMs":1758662421766,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1653,"unixTimeMs":1758662421786,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":0,"timestamp":1989,"unixTimeMs":1758662422122,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -1 +0,0 @@
1
- [{"x":1709,"y":0,"timestamp":33,"unixTimeMs":1758662423222,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":3,"timestamp":654,"unixTimeMs":1758662423843,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":8,"timestamp":677,"unixTimeMs":1758662423866,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":10,"timestamp":697,"unixTimeMs":1758662423886,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":13,"timestamp":718,"unixTimeMs":1758662423907,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":2,"timestamp":780,"unixTimeMs":1758662423969,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":802,"unixTimeMs":1758662423991,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":3,"timestamp":968,"unixTimeMs":1758662424157,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1698,"y":15,"timestamp":989,"unixTimeMs":1758662424178,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1691,"y":29,"timestamp":1016,"unixTimeMs":1758662424205,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1688,"y":36,"timestamp":1029,"unixTimeMs":1758662424218,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1687,"y":41,"timestamp":1056,"unixTimeMs":1758662424245,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1687,"y":42,"timestamp":1072,"unixTimeMs":1758662424261,"cursorType":"ns-resize","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1689,"y":38,"timestamp":1094,"unixTimeMs":1758662424283,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1700,"y":23,"timestamp":1116,"unixTimeMs":1758662424305,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":9,"timestamp":1134,"unixTimeMs":1758662424323,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":1,"timestamp":1157,"unixTimeMs":1758662424346,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":1,"timestamp":1554,"unixTimeMs":1758662424743,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":5,"timestamp":1578,"unixTimeMs":1758662424767,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1694,"y":13,"timestamp":1594,"unixTimeMs":1758662424783,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1690,"y":19,"timestamp":1618,"unixTimeMs":1758662424807,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1685,"y":25,"timestamp":1634,"unixTimeMs":1758662424823,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1684,"y":28,"timestamp":1658,"unixTimeMs":1758662424847,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1686,"y":30,"timestamp":1700,"unixTimeMs":1758662424889,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1691,"y":30,"timestamp":1720,"unixTimeMs":1758662424909,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1699,"y":30,"timestamp":1746,"unixTimeMs":1758662424935,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":25,"timestamp":1764,"unixTimeMs":1758662424953,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":16,"timestamp":1783,"unixTimeMs":1758662424972,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":10,"timestamp":1808,"unixTimeMs":1758662424997,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1825,"unixTimeMs":1758662425014,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":0,"timestamp":1890,"unixTimeMs":1758662425079,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1690,"y":1,"timestamp":1919,"unixTimeMs":1758662425108,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1682,"y":4,"timestamp":1930,"unixTimeMs":1758662425119,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1672,"y":12,"timestamp":1949,"unixTimeMs":1758662425138,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1669,"y":16,"timestamp":1976,"unixTimeMs":1758662425165,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1665,"y":22,"timestamp":1991,"unixTimeMs":1758662425180,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -1 +0,0 @@
1
- [{"x":1698,"y":14,"timestamp":31,"unixTimeMs":1758662426275,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":11,"timestamp":51,"unixTimeMs":1758662426295,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":6,"timestamp":76,"unixTimeMs":1758662426320,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":1,"timestamp":95,"unixTimeMs":1758662426339,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":8,"timestamp":426,"unixTimeMs":1758662426670,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":13,"timestamp":449,"unixTimeMs":1758662426693,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":16,"timestamp":467,"unixTimeMs":1758662426711,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1699,"y":19,"timestamp":489,"unixTimeMs":1758662426733,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":17,"timestamp":573,"unixTimeMs":1758662426817,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":15,"timestamp":595,"unixTimeMs":1758662426839,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":12,"timestamp":623,"unixTimeMs":1758662426867,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":9,"timestamp":635,"unixTimeMs":1758662426879,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":6,"timestamp":660,"unixTimeMs":1758662426904,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":4,"timestamp":678,"unixTimeMs":1758662426922,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":2,"timestamp":702,"unixTimeMs":1758662426946,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":744,"unixTimeMs":1758662426988,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":3,"timestamp":890,"unixTimeMs":1758662427134,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":8,"timestamp":917,"unixTimeMs":1758662427161,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":12,"timestamp":928,"unixTimeMs":1758662427172,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":14,"timestamp":951,"unixTimeMs":1758662427195,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":10,"timestamp":1036,"unixTimeMs":1758662427280,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":5,"timestamp":1058,"unixTimeMs":1758662427302,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":2,"timestamp":1079,"unixTimeMs":1758662427323,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1100,"unixTimeMs":1758662427344,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":5,"timestamp":1222,"unixTimeMs":1758662427466,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":8,"timestamp":1244,"unixTimeMs":1758662427488,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":13,"timestamp":1266,"unixTimeMs":1758662427510,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":16,"timestamp":1288,"unixTimeMs":1758662427532,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":14,"timestamp":1371,"unixTimeMs":1758662427615,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":12,"timestamp":1395,"unixTimeMs":1758662427639,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":9,"timestamp":1432,"unixTimeMs":1758662427676,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":6,"timestamp":1454,"unixTimeMs":1758662427698,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":4,"timestamp":1494,"unixTimeMs":1758662427738,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":7,"timestamp":1561,"unixTimeMs":1758662427805,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":10,"timestamp":1578,"unixTimeMs":1758662427822,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":12,"timestamp":1617,"unixTimeMs":1758662427861,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":12,"timestamp":1661,"unixTimeMs":1758662427905,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":12,"timestamp":1685,"unixTimeMs":1758662427929,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":11,"timestamp":1726,"unixTimeMs":1758662427970,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":9,"timestamp":1748,"unixTimeMs":1758662427992,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":7,"timestamp":1769,"unixTimeMs":1758662428013,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":5,"timestamp":1791,"unixTimeMs":1758662428035,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":5,"timestamp":1850,"unixTimeMs":1758662428094,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":3,"timestamp":1978,"unixTimeMs":1758662428222,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]