node-mac-recorder 2.17.7 → 2.17.9

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
@@ -359,60 +359,51 @@ class MacRecorder extends EventEmitter {
359
359
  // Start cursor tracking automatically with recording
360
360
  let cursorOptions = {};
361
361
 
362
- // For window recording, use the original window coordinates (not display-relative captureArea)
362
+ // For window recording, use simplified window-relative coordinates
363
363
  if (this.options.windowId) {
364
364
  // Use cached window info from the earlier window detection
365
365
  this.getWindows().then(windows => {
366
366
  const targetWindow = windows.find(w => w.id === this.options.windowId);
367
367
  if (targetWindow) {
368
- // Restart cursor capture with correct window coordinates
369
- if (this.cursorCaptureInterval) {
370
- this.stopCursorCapture().then(() => {
371
- this.startCursorCapture(cursorFilePath, {
372
- windowRelative: true,
373
- windowInfo: {
374
- x: targetWindow.x, // Global window X coordinate
375
- y: targetWindow.y, // Global window Y coordinate
376
- width: targetWindow.width,
377
- height: targetWindow.height,
378
- displayId: this.options.displayId,
379
- originalWindow: targetWindow,
380
- captureArea: this.options.captureArea
381
- }
382
- }).catch(cursorError => {
383
- console.warn('Cursor tracking failed to restart:', cursorError.message);
384
- });
385
- }).catch(stopError => {
386
- console.warn('Failed to stop cursor capture:', stopError.message);
368
+ // Start cursor capture with simplified window-relative tracking
369
+ this.startCursorCapture(cursorFilePath, {
370
+ windowRelative: true,
371
+ windowInfo: {
372
+ // Use original global window coordinates for reference
373
+ x: targetWindow.x,
374
+ y: targetWindow.y,
375
+ width: targetWindow.width,
376
+ height: targetWindow.height,
377
+ displayId: this.options.displayId,
378
+ // Persist capture area so we can rebuild global offsets reliably
379
+ captureArea: this.options.captureArea,
380
+ // Keep a snapshot of the window details for debugging/analytics
381
+ originalWindow: targetWindow,
382
+ // Store display info for multi-display coordinate fixes
383
+ targetDisplay: this.recordingDisplayInfo
384
+ }
385
+ }).catch(cursorError => {
386
+ console.warn('Window cursor tracking failed:', cursorError.message);
387
+ // Fallback to display recording
388
+ this.startCursorCapture(cursorFilePath).catch(fallbackError => {
389
+ console.warn('Fallback cursor tracking failed:', fallbackError.message);
387
390
  });
388
- } else {
389
- this.startCursorCapture(cursorFilePath, {
390
- windowRelative: true,
391
- windowInfo: {
392
- x: targetWindow.x, // Global window X coordinate
393
- y: targetWindow.y, // Global window Y coordinate
394
- width: targetWindow.width,
395
- height: targetWindow.height,
396
- displayId: this.options.displayId,
397
- originalWindow: targetWindow,
398
- captureArea: this.options.captureArea
399
- }
400
- }).catch(cursorError => {
401
- console.warn('Cursor tracking failed to start:', cursorError.message);
402
- });
403
- }
391
+ });
404
392
  }
405
393
  }).catch(error => {
406
394
  console.warn('Could not get window info for cursor tracking:', error.message);
407
- // Fallback to basic cursor tracking
408
- this.startCursorCapture(cursorFilePath, cursorOptions).catch(cursorError => {
395
+ // Fallback to display cursor tracking
396
+ this.startCursorCapture(cursorFilePath).catch(cursorError => {
409
397
  console.warn('Cursor tracking failed to start:', cursorError.message);
410
398
  });
411
399
  });
412
400
  } else {
413
- // For display recording, use basic cursor tracking
414
- this.startCursorCapture(cursorFilePath, cursorOptions).catch(cursorError => {
415
- console.warn('Cursor tracking failed to start:', cursorError.message);
401
+ // For display recording, use display-relative cursor tracking
402
+ this.startCursorCapture(cursorFilePath, {
403
+ displayRelative: true,
404
+ displayInfo: this.recordingDisplayInfo
405
+ }).catch(cursorError => {
406
+ console.warn('Display cursor tracking failed:', cursorError.message);
416
407
  });
417
408
  }
418
409
 
@@ -717,42 +708,92 @@ class MacRecorder extends EventEmitter {
717
708
 
718
709
  // Koordinat sistemi belirle: window-relative, display-relative veya global
719
710
  if (options.windowRelative && options.windowInfo) {
720
- // Window recording için display'e relative captureArea koordinatlarını kullan
721
- const captureArea = options.windowInfo.captureArea;
722
- if (captureArea) {
723
- // captureArea zaten display-relative koordinatlar
724
- this.cursorDisplayInfo = {
725
- displayId: options.windowInfo.displayId || null,
726
- x: captureArea.x, // Display-relative X
727
- y: captureArea.y, // Display-relative Y
728
- width: captureArea.width,
729
- height: captureArea.height,
730
- windowRelative: true,
731
- windowInfo: options.windowInfo
732
- };
733
- } else {
734
- // Fallback: orijinal window koordinatları
735
- this.cursorDisplayInfo = {
736
- displayId: options.windowInfo.displayId || null,
737
- x: options.windowInfo.x || 0,
738
- y: options.windowInfo.y || 0,
739
- width: options.windowInfo.width,
740
- height: options.windowInfo.height,
741
- windowRelative: true,
742
- windowInfo: options.windowInfo
743
- };
711
+ const windowInfo = options.windowInfo;
712
+ const targetDisplay = windowInfo.targetDisplay || this.recordingDisplayInfo || null;
713
+ const captureArea = windowInfo.captureArea || null;
714
+ const hasNumber = (value) => typeof value === "number" && Number.isFinite(value);
715
+
716
+ let globalX = hasNumber(windowInfo.x) ? windowInfo.x : null;
717
+ let globalY = hasNumber(windowInfo.y) ? windowInfo.y : null;
718
+
719
+ if (captureArea && targetDisplay) {
720
+ if (!hasNumber(globalX) && hasNumber(captureArea.x) && hasNumber(targetDisplay.x)) {
721
+ globalX = targetDisplay.x + captureArea.x;
722
+ }
723
+ if (!hasNumber(globalY) && hasNumber(captureArea.y) && hasNumber(targetDisplay.y)) {
724
+ globalY = targetDisplay.y + captureArea.y;
725
+ }
726
+ }
727
+
728
+ if (!hasNumber(globalX)) {
729
+ if (captureArea && hasNumber(captureArea.x) && targetDisplay && hasNumber(targetDisplay.x)) {
730
+ globalX = targetDisplay.x + captureArea.x;
731
+ } else {
732
+ globalX = hasNumber(captureArea?.x) ? captureArea.x : 0;
733
+ }
734
+ }
735
+
736
+ if (!hasNumber(globalY)) {
737
+ if (captureArea && hasNumber(captureArea.y) && targetDisplay && hasNumber(targetDisplay.y)) {
738
+ globalY = targetDisplay.y + captureArea.y;
739
+ } else {
740
+ globalY = hasNumber(captureArea?.y) ? captureArea.y : 0;
741
+ }
744
742
  }
743
+
744
+ const displayOffsetX = captureArea && hasNumber(captureArea.x)
745
+ ? captureArea.x
746
+ : (targetDisplay && hasNumber(globalX) && hasNumber(targetDisplay.x)
747
+ ? globalX - targetDisplay.x
748
+ : globalX);
749
+ const displayOffsetY = captureArea && hasNumber(captureArea.y)
750
+ ? captureArea.y
751
+ : (targetDisplay && hasNumber(globalY) && hasNumber(targetDisplay.y)
752
+ ? globalY - targetDisplay.y
753
+ : globalY);
754
+
755
+ this.cursorDisplayInfo = {
756
+ displayId:
757
+ windowInfo.displayId ??
758
+ targetDisplay?.displayId ??
759
+ targetDisplay?.id ??
760
+ null,
761
+ x: globalX,
762
+ y: globalY,
763
+ width: windowInfo.width,
764
+ height: windowInfo.height,
765
+ windowRelative: true,
766
+ windowInfo: {
767
+ ...windowInfo,
768
+ globalX,
769
+ globalY,
770
+ displayOffsetX,
771
+ displayOffsetY
772
+ },
773
+ targetDisplay,
774
+ captureArea
775
+ };
776
+ } else if (options.displayRelative && options.displayInfo) {
777
+ // Display recording: Use display-relative coordinates
778
+ this.cursorDisplayInfo = {
779
+ displayId: options.displayInfo.displayId,
780
+ x: options.displayInfo.x,
781
+ y: options.displayInfo.y,
782
+ width: options.displayInfo.width,
783
+ height: options.displayInfo.height,
784
+ displayRelative: true
785
+ };
745
786
  } else if (this.recordingDisplayInfo) {
746
- // Recording başlatılmışsa o display'i kullan
787
+ // Fallback: Use recording display info if available
747
788
  this.cursorDisplayInfo = this.recordingDisplayInfo;
748
789
  } else {
749
- // Main display bilgisini al (her zaman relative koordinatlar için)
790
+ // Final fallback: Main display global coordinates
750
791
  try {
751
792
  const displays = await this.getDisplays();
752
793
  const mainDisplay = displays.find((d) => d.isPrimary) || displays[0];
753
794
  if (mainDisplay) {
754
795
  this.cursorDisplayInfo = {
755
- displayId: 0,
796
+ displayId: mainDisplay.id,
756
797
  x: mainDisplay.x,
757
798
  y: mainDisplay.y,
758
799
  width: parseInt(mainDisplay.resolution.split("x")[0]),
@@ -782,60 +823,37 @@ class MacRecorder extends EventEmitter {
782
823
  const position = nativeBinding.getCursorPosition();
783
824
  const timestamp = Date.now() - this.cursorCaptureStartTime;
784
825
 
785
- // Global koordinatları relative koordinatlara çevir
826
+ // Transform coordinates based on recording type
786
827
  let x = position.x;
787
828
  let y = position.y;
788
829
  let coordinateSystem = "global";
789
830
 
790
831
  if (this.cursorDisplayInfo) {
791
832
  if (this.cursorDisplayInfo.windowRelative) {
792
- // Window recording: cursor'ı önce target display'e relative yap, sonra captureArea'ya relative yap
793
- const targetDisplay = this.recordingDisplayInfo;
794
- if (targetDisplay) {
795
- // 1. Global -> Display-relative
796
- const displayRelativeX = position.x - targetDisplay.x;
797
- const displayRelativeY = position.y - targetDisplay.y;
798
-
799
- // 2. Display-relative -> CaptureArea-relative (window-relative)
800
- x = displayRelativeX - this.cursorDisplayInfo.x;
801
- y = displayRelativeY - this.cursorDisplayInfo.y;
802
- } else {
803
- // Fallback: doğrudan offset çıkar
804
- x = position.x - this.cursorDisplayInfo.x;
805
- y = position.y - this.cursorDisplayInfo.y;
833
+ // Window recording: Transform global window-relative coordinates
834
+ x = position.x - this.cursorDisplayInfo.x;
835
+ y = position.y - this.cursorDisplayInfo.y;
836
+ coordinateSystem = "window-relative";
837
+
838
+ // Window bounds check - skip if cursor is outside window
839
+ if (x < 0 || y < 0 || x >= this.cursorDisplayInfo.width || y >= this.cursorDisplayInfo.height) {
840
+ return; // Skip frame - cursor outside window
806
841
  }
807
- } else {
808
- // Display recording: doğrudan offset çıkar
842
+ } else if (this.cursorDisplayInfo.displayRelative) {
843
+ // Display recording: Transform global → display-relative coordinates
809
844
  x = position.x - this.cursorDisplayInfo.x;
810
845
  y = position.y - this.cursorDisplayInfo.y;
811
- }
846
+ coordinateSystem = "display-relative";
812
847
 
813
- if (this.cursorDisplayInfo.windowRelative) {
814
- // Window-relative koordinatlar
815
- coordinateSystem = "window-relative";
816
-
817
- // Window bounds kontrolü - cursor window dışındaysa kaydetme
818
- if (
819
- x < 0 ||
820
- y < 0 ||
821
- x >= this.cursorDisplayInfo.width ||
822
- y >= this.cursorDisplayInfo.height
823
- ) {
824
- return; // Bu frame'i skip et - cursor pencere dışında
848
+ // Display bounds check - skip if cursor is outside display
849
+ if (x < 0 || y < 0 || x >= this.cursorDisplayInfo.width || y >= this.cursorDisplayInfo.height) {
850
+ return; // Skip frame - cursor outside display
825
851
  }
826
852
  } else {
827
- // Display-relative koordinatlar
853
+ // Legacy fallback: Use global coordinates with basic offset
854
+ x = position.x - this.cursorDisplayInfo.x;
855
+ y = position.y - this.cursorDisplayInfo.y;
828
856
  coordinateSystem = "display-relative";
829
-
830
- // Display bounds kontrolü
831
- if (
832
- x < 0 ||
833
- y < 0 ||
834
- x >= this.cursorDisplayInfo.width ||
835
- y >= this.cursorDisplayInfo.height
836
- ) {
837
- return; // Bu frame'i skip et - cursor display dışında
838
- }
839
857
  }
840
858
  }
841
859
 
@@ -847,11 +865,20 @@ class MacRecorder extends EventEmitter {
847
865
  cursorType: position.cursorType,
848
866
  type: position.eventType || "move",
849
867
  coordinateSystem: coordinateSystem,
868
+ // Include recording context for window-relative coordinates
850
869
  ...(this.cursorDisplayInfo?.windowRelative && {
851
870
  windowInfo: {
852
871
  width: this.cursorDisplayInfo.width,
853
872
  height: this.cursorDisplayInfo.height,
854
- originalWindow: this.cursorDisplayInfo.windowInfo
873
+ displayId: this.cursorDisplayInfo.displayId
874
+ }
875
+ }),
876
+ // Include display context for display-relative coordinates
877
+ ...(this.cursorDisplayInfo?.displayRelative && {
878
+ displayInfo: {
879
+ displayId: this.cursorDisplayInfo.displayId,
880
+ width: this.cursorDisplayInfo.width,
881
+ height: this.cursorDisplayInfo.height
855
882
  }
856
883
  })
857
884
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.17.7",
3
+ "version": "2.17.9",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -112,7 +112,7 @@ NSString* getCursorType() {
112
112
  [elementRole isEqualToString:@"AXTooltip"]) {
113
113
  cursorType = @"help";
114
114
  }
115
- // RESIZE CURSORS
115
+ // RESIZE CURSORS - sadece AXSplitter için
116
116
  else if ([elementRole isEqualToString:@"AXSplitter"]) {
117
117
  // Get splitter orientation to determine resize direction
118
118
  CFStringRef orientation = NULL;
@@ -120,34 +120,23 @@ NSString* getCursorType() {
120
120
  if (error == kAXErrorSuccess && orientation) {
121
121
  NSString *orientationStr = (__bridge_transfer NSString*)orientation;
122
122
  if ([orientationStr isEqualToString:@"AXHorizontalOrientation"]) {
123
- cursorType = @"row-resize"; // vertical splitter -> row resize
123
+ cursorType = @"ns-resize"; // Yatay splitter -> dikey hareket (north-south)
124
+ } else if ([orientationStr isEqualToString:@"AXVerticalOrientation"]) {
125
+ cursorType = @"col-resize"; // Dikey splitter -> yatay hareket (east-west)
124
126
  } else {
125
- cursorType = @"col-resize"; // horizontal splitter -> col resize
127
+ cursorType = @"default"; // Bilinmeyen orientation
126
128
  }
127
129
  } else {
128
- cursorType = @"col-resize"; // default
130
+ cursorType = @"default"; // Orientation alınamazsa default
129
131
  }
130
132
  }
131
- // SCROLL CURSORS - sadece gerçek scroll kontrollerinde
133
+ // SCROLL CURSORS - hep default olsun, all-scroll görünmesin
132
134
  else if ([elementRole isEqualToString:@"AXScrollBar"]) {
133
- // Check orientation for scroll direction
134
- CFStringRef orientation = NULL;
135
- error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXOrientation"), (CFTypeRef*)&orientation);
136
- if (error == kAXErrorSuccess && orientation) {
137
- NSString *orientationStr = (__bridge_transfer NSString*)orientation;
138
- if ([orientationStr isEqualToString:@"AXVerticalOrientation"]) {
139
- cursorType = @"ns-resize"; // vertical scrollbar
140
- } else {
141
- cursorType = @"col-resize"; // horizontal scrollbar
142
- }
143
- } else {
144
- cursorType = @"default"; // fallback to default
145
- }
135
+ cursorType = @"default"; // ScrollBar'lar için de default
146
136
  }
147
- // AXScrollArea için sadece belirli durumlarda all-scroll, çoğunlukla default
137
+ // AXScrollArea - hep default
148
138
  else if ([elementRole isEqualToString:@"AXScrollArea"]) {
149
- // ScrollArea genellikle default olmalı, sadece özel durumlar için all-scroll
150
- cursorType = @"default";
139
+ cursorType = @"default"; // ScrollArea her zaman default
151
140
  }
152
141
  // CROSSHAIR CURSORS (drawing/selection tools)
153
142
  else if ([elementRole isEqualToString:@"AXCanvas"] ||
@@ -196,22 +185,38 @@ NSString* getCursorType() {
196
185
  // Sadece çok kenar köşelerde resize cursor'ı göster
197
186
  BOOL isOnBorder = NO;
198
187
 
199
- // Corner resize detection - çok dar alanda
200
- if ((x <= edge && y <= edge) || (x >= w-edge && y >= h-edge)) {
201
- cursorType = @"nwse-resize";
188
+ // Corner resize detection - çok dar alanda, doğru açılar
189
+ if (x <= edge && y <= edge) {
190
+ cursorType = @"nwse-resize"; // Sol üst köşe - northwest-southeast
202
191
  isOnBorder = YES;
203
192
  }
204
- else if ((x >= w-edge && y <= edge) || (x <= edge && y >= h-edge)) {
205
- cursorType = @"nesw-resize";
193
+ else if (x >= w-edge && y <= edge) {
194
+ cursorType = @"nesw-resize"; // Sağ üst köşe - northeast-southwest
195
+ isOnBorder = YES;
196
+ }
197
+ else if (x <= edge && y >= h-edge) {
198
+ cursorType = @"nesw-resize"; // Sol alt köşe - southwest-northeast
199
+ isOnBorder = YES;
200
+ }
201
+ else if (x >= w-edge && y >= h-edge) {
202
+ cursorType = @"nwse-resize"; // Sağ alt köşe - southeast-northwest
206
203
  isOnBorder = YES;
207
204
  }
208
205
  // Edge resize detection - sadece çok kenarlarda
209
- else if ((x <= edge || x >= w-edge) && y > edge && y < h-edge) {
210
- cursorType = @"col-resize";
206
+ else if (x <= edge && y > edge && y < h-edge) {
207
+ cursorType = @"col-resize"; // Sol kenar - column resize (yatay)
208
+ isOnBorder = YES;
209
+ }
210
+ else if (x >= w-edge && y > edge && y < h-edge) {
211
+ cursorType = @"col-resize"; // Sağ kenar - column resize (yatay)
212
+ isOnBorder = YES;
213
+ }
214
+ else if (y <= edge && x > edge && x < w-edge) {
215
+ cursorType = @"ns-resize"; // Üst kenar - north-south resize (dikey)
211
216
  isOnBorder = YES;
212
217
  }
213
- else if ((y <= edge || y >= h-edge) && x > edge && x < w-edge) {
214
- cursorType = @"row-resize";
218
+ else if (y >= h-edge && x > edge && x < w-edge) {
219
+ cursorType = @"ns-resize"; // Alt kenar - north-south resize (dikey)
215
220
  isOnBorder = YES;
216
221
  }
217
222