node-mac-recorder 2.17.21 → 2.18.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.
@@ -0,0 +1 @@
1
+ []
@@ -0,0 +1 @@
1
+ []
@@ -0,0 +1 @@
1
+ [{"x":1151,"y":726,"timestamp":20,"cursorType":"text","type":"move"}
package/index.js CHANGED
@@ -283,16 +283,25 @@ class MacRecorder extends EventEmitter {
283
283
  }
284
284
  }
285
285
 
286
- // DisplayId manuel ayarlanmışsa display bilgisini sakla
287
- if (this.options.displayId !== null && !this.recordingDisplayInfo) {
286
+ // Ensure recordingDisplayInfo is always set for cursor tracking
287
+ if (!this.recordingDisplayInfo) {
288
288
  try {
289
289
  const displays = await this.getDisplays();
290
- const targetDisplay = displays.find(d => d.id === this.options.displayId);
290
+ let targetDisplay;
291
+
292
+ if (this.options.displayId !== null) {
293
+ // Manual displayId specified
294
+ targetDisplay = displays.find(d => d.id === this.options.displayId);
295
+ } else {
296
+ // Default to main display
297
+ targetDisplay = displays.find(d => d.isPrimary) || displays[0];
298
+ }
299
+
291
300
  if (targetDisplay) {
292
301
  this.recordingDisplayInfo = {
293
- displayId: this.options.displayId,
294
- x: targetDisplay.x,
295
- y: targetDisplay.y,
302
+ displayId: targetDisplay.id,
303
+ x: targetDisplay.x || 0,
304
+ y: targetDisplay.y || 0,
296
305
  width: parseInt(targetDisplay.resolution.split("x")[0]),
297
306
  height: parseInt(targetDisplay.resolution.split("x")[1]),
298
307
  // Add scaling information for cursor coordinate transformation
@@ -356,12 +365,19 @@ class MacRecorder extends EventEmitter {
356
365
  this.isRecording = true;
357
366
  this.recordingStartTime = Date.now();
358
367
 
359
- // Start cursor tracking automatically with recording
360
- let cursorOptions = {};
368
+ // Start unified cursor tracking for all recording types
369
+ // Use the same standard cursor tracking logic that works best (display-relative)
370
+ const standardCursorOptions = {
371
+ displayRelative: true,
372
+ displayInfo: this.recordingDisplayInfo,
373
+ recordingType: this.options.windowId ? 'window' :
374
+ this.options.captureArea ? 'area' : 'display',
375
+ captureArea: this.options.captureArea,
376
+ windowId: this.options.windowId
377
+ };
361
378
 
362
- // Hem window hem display recording için aynı native cursor tracking kullan
363
- this.startCursorCapture(cursorFilePath).catch(cursorError => {
364
- console.warn('Cursor tracking failed to start:', cursorError.message);
379
+ this.startCursorCapture(cursorFilePath, standardCursorOptions).catch(cursorError => {
380
+ console.warn('Unified cursor tracking failed:', cursorError.message);
365
381
  });
366
382
 
367
383
  // Timer başlat (progress tracking için)
@@ -456,11 +472,9 @@ class MacRecorder extends EventEmitter {
456
472
 
457
473
  // Stop cursor tracking automatically
458
474
  if (this.cursorCaptureInterval) {
459
- try {
460
- this.stopCursorCapture();
461
- } catch (cursorError) {
475
+ this.stopCursorCapture().catch(cursorError => {
462
476
  console.warn('Cursor tracking failed to stop:', cursorError.message);
463
- }
477
+ });
464
478
  }
465
479
 
466
480
  // Timer durdur
@@ -638,18 +652,20 @@ class MacRecorder extends EventEmitter {
638
652
  }
639
653
 
640
654
  /**
641
- * Cursor capture başlatır - otomatik olarak dosyaya yazmaya başlar
642
- * Recording başlatılmışsa otomatik olarak display-relative koordinatlar kullanır
655
+ * Unified cursor capture for all recording types - uses standardized display-relative coordinates
643
656
  * @param {string|number} intervalOrFilepath - Cursor data JSON dosya yolu veya interval
644
657
  * @param {Object} options - Cursor capture seçenekleri
645
- * @param {Object} options.windowInfo - Pencere bilgileri (window-relative koordinatlar için)
646
- * @param {boolean} options.windowRelative - Koordinatları pencereye göre relative yap
658
+ * @param {boolean} options.displayRelative - Use display-relative coordinates (recommended)
659
+ * @param {Object} options.displayInfo - Display information for coordinate transformation
660
+ * @param {string} options.recordingType - Type of recording: 'display', 'window', 'area'
647
661
  */
648
662
  async startCursorCapture(intervalOrFilepath = 100, options = {}) {
649
663
  let filepath;
664
+ let interval = 20; // Default 50 FPS
650
665
 
651
666
  // Parameter parsing: number = interval, string = filepath
652
667
  if (typeof intervalOrFilepath === "number") {
668
+ interval = Math.max(10, intervalOrFilepath); // Min 10ms
653
669
  filepath = `cursor-data-${Date.now()}.json`;
654
670
  } else if (typeof intervalOrFilepath === "string") {
655
671
  filepath = intervalOrFilepath;
@@ -663,28 +679,127 @@ class MacRecorder extends EventEmitter {
663
679
  throw new Error("Cursor capture is already running");
664
680
  }
665
681
 
682
+ // Use standardized display-relative coordinate system for all recording types
683
+ if (options.displayRelative && options.displayInfo) {
684
+ // Standardized display-relative coordinates for all recording types
685
+ this.cursorDisplayInfo = {
686
+ displayId: options.displayInfo.displayId || options.displayInfo.id,
687
+ x: options.displayInfo.x || 0,
688
+ y: options.displayInfo.y || 0,
689
+ width: options.displayInfo.width || options.displayInfo.logicalWidth,
690
+ height: options.displayInfo.height || options.displayInfo.logicalHeight,
691
+ displayRelative: true,
692
+ recordingType: options.recordingType || 'display',
693
+ // Store additional context for debugging
694
+ captureArea: options.captureArea,
695
+ windowId: options.windowId
696
+ };
697
+ } else if (this.recordingDisplayInfo) {
698
+ // Fallback: Use recording display info if available
699
+ this.cursorDisplayInfo = {
700
+ ...this.recordingDisplayInfo,
701
+ displayRelative: true,
702
+ recordingType: options.recordingType || 'display'
703
+ };
704
+ } else {
705
+ // Final fallback: Main display global coordinates
706
+ try {
707
+ const displays = await this.getDisplays();
708
+ const mainDisplay = displays.find((d) => d.isPrimary) || displays[0];
709
+ if (mainDisplay) {
710
+ this.cursorDisplayInfo = {
711
+ displayId: mainDisplay.id,
712
+ x: mainDisplay.x,
713
+ y: mainDisplay.y,
714
+ width: parseInt(mainDisplay.resolution.split("x")[0]),
715
+ height: parseInt(mainDisplay.resolution.split("x")[1]),
716
+ };
717
+ }
718
+ } catch (error) {
719
+ console.warn("Main display bilgisi alınamadı:", error.message);
720
+ this.cursorDisplayInfo = null; // Fallback: global koordinatlar
721
+ }
722
+ }
723
+
666
724
  return new Promise((resolve, reject) => {
667
725
  try {
668
- // Native cursor tracking kullan - hem screen hem window için aynı yöntem
669
- const success = nativeBinding.startCursorTracking(filepath);
726
+ // Dosyayı oluştur ve temizle
727
+ const fs = require("fs");
728
+ fs.writeFileSync(filepath, "[");
670
729
 
671
- if (success) {
672
- this.cursorCaptureFile = filepath;
673
- this.cursorCaptureStartTime = Date.now();
674
- this.cursorCaptureInterval = true; // Mark as active
730
+ this.cursorCaptureFile = filepath;
731
+ this.cursorCaptureStartTime = Date.now();
732
+ this.cursorCaptureFirstWrite = true;
733
+ this.lastCapturedData = null;
675
734
 
676
- this.emit("cursorCaptureStarted", {
677
- filepath: filepath,
678
- timestamp: Date.now()
679
- });
735
+ // JavaScript interval ile polling yap (daha sık - mouse event'leri yakalamak için)
736
+ this.cursorCaptureInterval = setInterval(() => {
737
+ try {
738
+ const position = nativeBinding.getCursorPosition();
739
+ const timestamp = Date.now() - this.cursorCaptureStartTime;
740
+
741
+ // Standardized coordinate transformation for all recording types
742
+ let x = position.x;
743
+ let y = position.y;
744
+ let coordinateSystem = "global";
745
+
746
+ // Apply display-relative transformation for all recording types
747
+ if (this.cursorDisplayInfo && this.cursorDisplayInfo.displayRelative) {
748
+ // Transform global → display-relative coordinates
749
+ x = position.x - this.cursorDisplayInfo.x;
750
+ y = position.y - this.cursorDisplayInfo.y;
751
+ coordinateSystem = "display-relative";
752
+
753
+ // Optional bounds check for display (don't skip, just note if outside)
754
+ const outsideDisplay = x < 0 || y < 0 ||
755
+ x >= this.cursorDisplayInfo.width ||
756
+ y >= this.cursorDisplayInfo.height;
757
+
758
+ // For debugging - add metadata if cursor is outside recording area
759
+ if (outsideDisplay) {
760
+ coordinateSystem = "display-relative-outside";
761
+ }
762
+ }
680
763
 
681
- resolve({
682
- filepath: filepath,
683
- started: true
684
- });
685
- } else {
686
- reject(new Error("Failed to start native cursor tracking"));
687
- }
764
+ const cursorData = {
765
+ x: x,
766
+ y: y,
767
+ timestamp: timestamp,
768
+ unixTimeMs: Date.now(),
769
+ cursorType: position.cursorType,
770
+ type: position.eventType || "move",
771
+ coordinateSystem: coordinateSystem,
772
+ // Standardized metadata for all recording types
773
+ recordingType: this.cursorDisplayInfo?.recordingType || "display",
774
+ displayInfo: this.cursorDisplayInfo ? {
775
+ displayId: this.cursorDisplayInfo.displayId,
776
+ width: this.cursorDisplayInfo.width,
777
+ height: this.cursorDisplayInfo.height
778
+ } : null
779
+ };
780
+
781
+ // Sadece eventType değiştiğinde veya pozisyon değiştiğinde kaydet
782
+ if (this.shouldCaptureEvent(cursorData)) {
783
+ // Dosyaya ekle
784
+ const jsonString = JSON.stringify(cursorData);
785
+
786
+ if (this.cursorCaptureFirstWrite) {
787
+ fs.appendFileSync(filepath, jsonString);
788
+ this.cursorCaptureFirstWrite = false;
789
+ } else {
790
+ fs.appendFileSync(filepath, "," + jsonString);
791
+ }
792
+
793
+ // Son pozisyonu sakla
794
+ this.lastCapturedData = { ...cursorData };
795
+ }
796
+ } catch (error) {
797
+ console.error("Cursor capture error:", error);
798
+ }
799
+ }, interval); // Configurable FPS
800
+
801
+ this.emit("cursorCaptureStarted", filepath);
802
+ resolve(true);
688
803
  } catch (error) {
689
804
  reject(error);
690
805
  }
@@ -694,34 +809,7 @@ class MacRecorder extends EventEmitter {
694
809
  /**
695
810
  * Cursor capture durdurur - dosya yazma işlemini sonlandırır
696
811
  */
697
- stopCursorCapture() {
698
- if (!this.cursorCaptureInterval) {
699
- return false;
700
- }
701
-
702
- try {
703
- // Native cursor tracking'i durdur
704
- const success = nativeBinding.stopCursorTracking();
705
-
706
- this.cursorCaptureInterval = null;
707
- this.cursorCaptureFile = null;
708
- this.cursorCaptureStartTime = null;
709
-
710
- this.emit("cursorCaptureStopped", {
711
- timestamp: Date.now()
712
- });
713
-
714
- return success;
715
- } catch (error) {
716
- console.warn("Error stopping cursor tracking:", error.message);
717
- return false;
718
- }
719
- }
720
-
721
- /**
722
- * LEGACY: Eski JS-based cursor capture durdurmak için geriye uyumluluk
723
- */
724
- async _legacyStopCursorCapture() {
812
+ async stopCursorCapture() {
725
813
  return new Promise((resolve, reject) => {
726
814
  try {
727
815
  if (!this.cursorCaptureInterval) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.17.21",
3
+ "version": "2.18.1",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
package/publish.sh CHANGED
@@ -3,6 +3,37 @@
3
3
  # Publish script for node-mac-recorder
4
4
  # Usage: ./publish.sh <patch|minor|major> "commit message"
5
5
 
6
+ # Clean up development files before publishing
7
+ echo "🧹 Cleaning up development files..."
8
+
9
+ # Remove video files
10
+ echo " • Removing .mov and .mp4 files..."
11
+ find . -name "*.mov" -type f -delete 2>/dev/null
12
+ find . -name "*.mp4" -type f -delete 2>/dev/null
13
+
14
+ # Remove files containing specific keywords
15
+ echo " • Removing files containing test, debug, example, demo, sample..."
16
+ find . -type f -not -path "./node_modules/*" \( \
17
+ -name "*test*" -o \
18
+ -name "*debug*" -o \
19
+ -name "*example*" -o \
20
+ -name "*demo*" -o \
21
+ -name "*sample*" \
22
+ \) -delete 2>/dev/null
23
+
24
+ # Remove folders containing specific keywords
25
+ echo " • Removing folders containing test, debug, example, demo, sample..."
26
+ find . -type d -not -path "./node_modules/*" \( \
27
+ -name "*test*" -o \
28
+ -name "*debug*" -o \
29
+ -name "*example*" -o \
30
+ -name "*demo*" -o \
31
+ -name "*sample*" \
32
+ \) -exec rm -rf {} + 2>/dev/null
33
+
34
+ echo "✅ Cleanup completed"
35
+ echo ""
36
+
6
37
  # Check if correct number of arguments provided
7
38
  if [ $# -ne 2 ]; then
8
39
  echo "❌ Usage: $0 <patch|minor|major> \"commit message\""
@@ -26,24 +57,6 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
26
57
  exit 1
27
58
  fi
28
59
 
29
- # Clean up test files and video files before publishing
30
- echo "🧹 Cleaning up test files and video files..."
31
-
32
- # Remove video files (MP4, MOV, AVI, etc.)
33
- find . -maxdepth 1 -type f \( -name "*.mp4" -o -name "*.mov" -o -name "*.avi" -o -name "*.mkv" -o -name "*.webm" \) -delete
34
-
35
- # Remove test output directories
36
- rm -rf test-output debug-output
37
-
38
- # Remove test files and temporary files
39
- find . -maxdepth 1 -type f \( -name "*test*.json" -o -name "*debug*.json" -o -name "*cursor*.json" -o -name "temp-*" -o -name "*-temp.*" -o -name "*.tmp" \) -delete
40
-
41
- # Remove test JavaScript files that shouldn't be published
42
- find . -maxdepth 1 -type f -name "*test*.js" ! -name "test.js" -delete
43
- find . -maxdepth 1 -type f -name "*debug*.js" -delete
44
-
45
- echo "✅ Cleanup completed"
46
-
47
60
  # Check if there are uncommitted changes
48
61
  if ! git diff --quiet || ! git diff --cached --quiet; then
49
62
  echo "📁 Adding all changes to git..."