node-mac-recorder 2.20.0 → 2.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +2 -9
- package/index.js +22 -158
- package/package.json +1 -1
- package/src/cursor_tracker.mm +495 -297
- package/src/mac_recorder.mm +7 -58
- package/src/screen_capture_kit.mm +14 -23
- package/temp_cursor_1758662416766.json +0 -1
- package/temp_cursor_1758662420113.json +0 -1
- package/temp_cursor_1758662423166.json +0 -1
- package/temp_cursor_1758662426222.json +0 -1
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
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
|
-
|
|
779
|
-
const cursorData = this.getCursorPosition();
|
|
774
|
+
const position = nativeBinding.getCursorPosition();
|
|
780
775
|
const timestamp = Date.now() - this.cursorCaptureStartTime;
|
|
781
776
|
|
|
782
|
-
//
|
|
783
|
-
let x =
|
|
784
|
-
let y =
|
|
785
|
-
let coordinateSystem = "
|
|
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
|
|
782
|
+
// Apply video-relative transformation for all recording types
|
|
788
783
|
if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
|
804
|
+
const cursorData = {
|
|
816
805
|
x: x,
|
|
817
806
|
y: y,
|
|
818
807
|
timestamp: timestamp,
|
|
819
808
|
unixTimeMs: Date.now(),
|
|
820
|
-
cursorType:
|
|
821
|
-
type:
|
|
809
|
+
cursorType: position.cursorType,
|
|
810
|
+
type: position.eventType || "move",
|
|
822
811
|
coordinateSystem: coordinateSystem,
|
|
823
|
-
//
|
|
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(
|
|
828
|
+
if (this.shouldCaptureEvent(cursorData)) {
|
|
854
829
|
// Dosyaya ekle
|
|
855
|
-
const jsonString = JSON.stringify(
|
|
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 = { ...
|
|
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
|
-
|
|
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
|
*/
|