node-mac-recorder 2.22.8 → 2.22.10

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
@@ -1604,13 +1604,15 @@ class MacRecorder extends EventEmitter {
1604
1604
  windowHeight: wh
1605
1605
  };
1606
1606
 
1607
- // If this is a click event, mark click location
1608
- // Native eventType values: 'mousedown', 'mouseup', 'rightmousedown', 'rightmouseup'
1607
+ // If this is a click/drag event, mark click location
1608
+ // Native eventType values: 'mousedown', 'mouseup', 'drag', 'rightmousedown', 'rightmouseup', 'rightdrag'
1609
1609
  const eventType = position.eventType || '';
1610
1610
  if (eventType === 'mousedown' ||
1611
1611
  eventType === 'mouseup' ||
1612
+ eventType === 'drag' ||
1612
1613
  eventType === 'rightmousedown' ||
1613
- eventType === 'rightmouseup') {
1614
+ eventType === 'rightmouseup' ||
1615
+ eventType === 'rightdrag') {
1614
1616
  location.click = windowInfo.windowId;
1615
1617
  }
1616
1618
  break; // Found the window, stop searching
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.22.8",
3
+ "version": "2.22.10",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -132,11 +132,9 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
132
132
  codecKey = AVVideoCodecH264;
133
133
  }
134
134
 
135
- // QUALITY FIX: ULTRA HIGH quality screen recording
136
- // ProMotion displays may capture at 10 FPS - use very high bitrate for perfect quality
137
- NSInteger bitrate = (NSInteger)(recordingSize.width * recordingSize.height * 60);
138
- bitrate = MAX(bitrate, 70 * 1000 * 1000); // Minimum 70 Mbps
139
- bitrate = MIN(bitrate, 250 * 1000 * 1000); // Maximum 250 Mbps
135
+ NSInteger bitrate = (NSInteger)(recordingSize.width * recordingSize.height * 100);
136
+ bitrate = MAX(bitrate, 120 * 1000 * 1000);
137
+ bitrate = MIN(bitrate, 500 * 1000 * 1000);
140
138
 
141
139
  NSLog(@"🎬 ULTRA QUALITY AVFoundation: %dx%d, bitrate=%.2fMbps",
142
140
  (int)recordingSize.width, (int)recordingSize.height, bitrate / (1000.0 * 1000.0));
@@ -155,7 +153,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
155
153
  AVVideoMaxKeyFrameIntervalKey: @((int)fps),
156
154
  AVVideoAllowFrameReorderingKey: @YES,
157
155
  AVVideoExpectedSourceFrameRateKey: @((int)fps),
158
- AVVideoQualityKey: @(0.95), // 0.0-1.0, higher is better
156
+ AVVideoQualityKey: @(1.0),
159
157
  AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
160
158
  AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
161
159
  }
@@ -360,10 +360,10 @@ static void InitializeCursorFingerprintMap(void) {
360
360
  AddCursorIfAvailable(@selector(dragLinkCursor), @"alias");
361
361
  AddCursorIfAvailable(@selector(resizeLeftRightCursor), @"col-resize");
362
362
  AddCursorIfAvailable(@selector(resizeUpDownCursor), @"row-resize");
363
- AddCursorIfAvailableByName(@"resizeLeftCursor", @"w-resize");
364
- AddCursorIfAvailableByName(@"resizeRightCursor", @"e-resize");
365
- AddCursorIfAvailableByName(@"resizeUpCursor", @"n-resize");
366
- AddCursorIfAvailableByName(@"resizeDownCursor", @"s-resize");
363
+ AddCursorIfAvailableByName(@"resizeLeftCursor", @"col-resize");
364
+ AddCursorIfAvailableByName(@"resizeRightCursor", @"col-resize");
365
+ AddCursorIfAvailableByName(@"resizeUpCursor", @"ns-resize");
366
+ AddCursorIfAvailableByName(@"resizeDownCursor", @"ns-resize");
367
367
  AddCursorIfAvailableByName(@"resizeNorthWestSouthEastCursor", @"nwse-resize");
368
368
  AddCursorIfAvailableByName(@"resizeNorthEastSouthWestCursor", @"nesw-resize");
369
369
  AddCursorIfAvailable(@selector(zoomInCursor), @"zoom-in");
@@ -994,10 +994,10 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
994
994
  return @"nwse-resize";
995
995
  }
996
996
  if (horizontal) {
997
- return @"ew-resize"; // Use ew-resize as primary horizontal
997
+ return @"col-resize"; // Desktop SVG var: col-resize
998
998
  }
999
999
  if (vertical) {
1000
- return @"ns-resize"; // Use ns-resize as primary vertical
1000
+ return @"ns-resize"; // Desktop SVG var: ns-resize
1001
1001
  }
1002
1002
 
1003
1003
  // If contains "resize" but no specific direction, return generic resize
@@ -1332,34 +1332,35 @@ static NSString* cursorTypeFromSeed(int seed) {
1332
1332
  }
1333
1333
  }
1334
1334
  switch(seed) {
1335
- case 741324: return @"auto";
1336
- case 741336: return @"none";
1337
- case 741338: return @"context-menu";
1335
+ // Desktop'ta SVG karşılığı olan tiplere normalize edilmiş seed map
1336
+ case 741324: return @"default"; // auto → default
1337
+ case 741336: return @"default"; // none → default (gizli cursor kayıtta default gösterilir)
1338
+ case 741338: return @"default"; // context-menu → default (SVG yok)
1338
1339
  case 741339: return @"pointer";
1339
1340
  case 741341: return @"progress";
1340
- case 741343: return @"wait";
1341
- case 741345: return @"cell";
1341
+ case 741343: return @"progress"; // wait → progress
1342
+ case 741345: return @"crosshair"; // cell → crosshair (en yakın SVG)
1342
1343
  case 741347: return @"crosshair";
1343
1344
  case 741357: return @"text";
1344
- case 741359: return @"vertical-text";
1345
+ case 741359: return @"text"; // vertical-text → text
1345
1346
  case 741361: return @"alias";
1346
1347
  case 741362: return @"copy";
1347
- case 741364: return @"move";
1348
- case 741368: return @"no-drop";
1348
+ case 741364: return @"all-scroll"; // move → all-scroll
1349
+ case 741368: return @"not-allowed"; // no-drop → not-allowed
1349
1350
  case 741370: return @"not-allowed";
1350
1351
  case 741381: return @"grab";
1351
1352
  case 741385: return @"grabbing";
1352
1353
  case 741389: return @"col-resize";
1353
1354
  case 741393: return @"row-resize";
1354
- case 741397: return @"n-resize";
1355
- case 741398: return @"e-resize";
1356
- case 741409: return @"s-resize";
1357
- case 741413: return @"w-resize";
1358
- case 741417: return @"ne-resize";
1359
- case 741418: return @"nw-resize";
1360
- case 741420: return @"se-resize";
1361
- case 741424: return @"sw-resize";
1362
- case 741426: return @"ew-resize";
1355
+ case 741397: return @"ns-resize"; // n-resize → ns-resize
1356
+ case 741398: return @"col-resize"; // e-resize → col-resize
1357
+ case 741409: return @"ns-resize"; // s-resize → ns-resize
1358
+ case 741413: return @"col-resize"; // w-resize → col-resize
1359
+ case 741417: return @"nesw-resize"; // ne-resize → nesw-resize
1360
+ case 741418: return @"nwse-resize"; // nw-resize → nwse-resize
1361
+ case 741420: return @"nwse-resize"; // se-resize → nwse-resize
1362
+ case 741424: return @"nesw-resize"; // sw-resize → nesw-resize
1363
+ case 741426: return @"col-resize"; // ew-resize → col-resize
1363
1364
  case 741436: return @"ns-resize";
1364
1365
  case 741438: return @"nesw-resize";
1365
1366
  case 741442: return @"nwse-resize";
@@ -1787,6 +1788,71 @@ static NSString* detectSystemCursorType(void) {
1787
1788
  return cursorType;
1788
1789
  }
1789
1790
 
1791
+ // Desktop'ta SVG karşılığı olmayan cursor tiplerini desteklenen tiplere normalize et
1792
+ static NSString* normalizeCursorTypeForDesktop(NSString *cursorType) {
1793
+ if (!cursorType || [cursorType length] == 0) {
1794
+ return @"default";
1795
+ }
1796
+
1797
+ // Desteklenen tipler — desktop/public/cursor/default/ dizinindeki SVG'lere karşılık gelir
1798
+ static NSSet *supportedTypes = nil;
1799
+ static dispatch_once_t onceToken;
1800
+ dispatch_once(&onceToken, ^{
1801
+ supportedTypes = [[NSSet alloc] initWithArray:@[
1802
+ @"default", @"pointer", @"grabbing", @"text", @"grab",
1803
+ @"alias", @"copy", @"not-allowed", @"help", @"progress",
1804
+ @"crosshair", @"all-scroll", @"zoom-in", @"zoom-out",
1805
+ @"row-resize", @"col-resize", @"ns-resize",
1806
+ @"nwse-resize", @"nesw-resize"
1807
+ ]];
1808
+ });
1809
+
1810
+ if ([supportedTypes containsObject:cursorType]) {
1811
+ return cursorType;
1812
+ }
1813
+
1814
+ // Normalize edilmemiş tipleri en yakın desteklenen tipe eşle
1815
+ if ([cursorType isEqualToString:@"auto"] || [cursorType isEqualToString:@"none"] ||
1816
+ [cursorType isEqualToString:@"context-menu"]) {
1817
+ return @"default";
1818
+ }
1819
+ if ([cursorType isEqualToString:@"wait"]) {
1820
+ return @"progress";
1821
+ }
1822
+ if ([cursorType isEqualToString:@"cell"]) {
1823
+ return @"crosshair";
1824
+ }
1825
+ if ([cursorType isEqualToString:@"vertical-text"]) {
1826
+ return @"text";
1827
+ }
1828
+ if ([cursorType isEqualToString:@"move"]) {
1829
+ return @"all-scroll";
1830
+ }
1831
+ if ([cursorType isEqualToString:@"no-drop"]) {
1832
+ return @"not-allowed";
1833
+ }
1834
+ // Yönlü resize → iki yönlü resize
1835
+ if ([cursorType isEqualToString:@"ew-resize"] ||
1836
+ [cursorType isEqualToString:@"e-resize"] ||
1837
+ [cursorType isEqualToString:@"w-resize"]) {
1838
+ return @"col-resize";
1839
+ }
1840
+ if ([cursorType isEqualToString:@"n-resize"] ||
1841
+ [cursorType isEqualToString:@"s-resize"]) {
1842
+ return @"ns-resize";
1843
+ }
1844
+ if ([cursorType isEqualToString:@"ne-resize"] ||
1845
+ [cursorType isEqualToString:@"sw-resize"]) {
1846
+ return @"nesw-resize";
1847
+ }
1848
+ if ([cursorType isEqualToString:@"nw-resize"] ||
1849
+ [cursorType isEqualToString:@"se-resize"]) {
1850
+ return @"nwse-resize";
1851
+ }
1852
+
1853
+ return @"default";
1854
+ }
1855
+
1790
1856
  NSString* getCursorType() {
1791
1857
  @autoreleasepool {
1792
1858
  g_cursorTypeCounter++;
@@ -1823,7 +1889,10 @@ NSString* getCursorType() {
1823
1889
  // Use cursorTypeFromNSCursor for detection (pointer equality + image-based)
1824
1890
  // DO NOT use accessibility detection as it's unreliable and causes false positives
1825
1891
  NSString *systemCursorType = detectSystemCursorType();
1826
- NSString *finalType = systemCursorType && [systemCursorType length] > 0 ? systemCursorType : @"default";
1892
+ NSString *rawType = systemCursorType && [systemCursorType length] > 0 ? systemCursorType : @"default";
1893
+
1894
+ // Desktop SVG'lerine uyumlu tipe normalize et
1895
+ NSString *finalType = normalizeCursorTypeForDesktop(rawType);
1827
1896
 
1828
1897
  // Only log when cursor type changes
1829
1898
  static NSString *lastLoggedType = nil;
@@ -1967,12 +2036,43 @@ void cursorTimerCallback() {
1967
2036
  if (!cursorType) {
1968
2037
  cursorType = @"default";
1969
2038
  }
2039
+
2040
+ // Mouse button state polling — event tap olmadığında click/drag tespiti
2041
+ bool currentLeftMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft);
2042
+ bool currentRightMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
2043
+
1970
2044
  NSString *eventType = @"move";
1971
2045
 
2046
+ if (currentLeftMouseDown && !g_leftMouseDown) {
2047
+ eventType = @"mousedown";
2048
+ g_lastEventType = @"mousedown";
2049
+ } else if (!currentLeftMouseDown && g_leftMouseDown) {
2050
+ eventType = @"mouseup";
2051
+ g_lastEventType = @"mouseup";
2052
+ } else if (currentRightMouseDown && !g_rightMouseDown) {
2053
+ eventType = @"rightmousedown";
2054
+ g_lastEventType = @"rightmousedown";
2055
+ } else if (!currentRightMouseDown && g_rightMouseDown) {
2056
+ eventType = @"rightmouseup";
2057
+ g_lastEventType = @"rightmouseup";
2058
+ } else if (currentLeftMouseDown) {
2059
+ eventType = @"drag";
2060
+ g_lastEventType = @"drag";
2061
+ } else if (currentRightMouseDown) {
2062
+ eventType = @"rightdrag";
2063
+ g_lastEventType = @"rightdrag";
2064
+ } else {
2065
+ eventType = @"move";
2066
+ g_lastEventType = @"move";
2067
+ }
2068
+
2069
+ g_leftMouseDown = currentLeftMouseDown;
2070
+ g_rightMouseDown = currentRightMouseDown;
2071
+
1972
2072
  if (!ShouldEmitCursorEvent(location, cursorType, eventType)) {
1973
2073
  return;
1974
2074
  }
1975
-
2075
+
1976
2076
  // Cursor data oluştur
1977
2077
  NSDictionary *cursorInfo = @{
1978
2078
  @"x": @((int)location.x),
@@ -1982,7 +2082,7 @@ void cursorTimerCallback() {
1982
2082
  @"cursorType": cursorType,
1983
2083
  @"type": eventType
1984
2084
  };
1985
-
2085
+
1986
2086
  // Direkt dosyaya yaz
1987
2087
  writeToFile(cursorInfo);
1988
2088
  RememberCursorEvent(location, cursorType, eventType);
@@ -2237,20 +2337,32 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
2237
2337
  bool currentRightMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
2238
2338
 
2239
2339
  NSString *eventType = @"move";
2240
-
2340
+
2241
2341
  // Mouse button state değişikliklerini tespit et
2242
2342
  if (currentLeftMouseDown && !g_leftMouseDown) {
2343
+ // Sol tuş basıldı (geçiş: up → down)
2243
2344
  eventType = @"mousedown";
2244
2345
  g_lastEventType = @"mousedown";
2245
2346
  } else if (!currentLeftMouseDown && g_leftMouseDown) {
2347
+ // Sol tuş bırakıldı (geçiş: down → up)
2246
2348
  eventType = @"mouseup";
2247
2349
  g_lastEventType = @"mouseup";
2248
2350
  } else if (currentRightMouseDown && !g_rightMouseDown) {
2351
+ // Sağ tuş basıldı
2249
2352
  eventType = @"rightmousedown";
2250
2353
  g_lastEventType = @"rightmousedown";
2251
2354
  } else if (!currentRightMouseDown && g_rightMouseDown) {
2355
+ // Sağ tuş bırakıldı
2252
2356
  eventType = @"rightmouseup";
2253
2357
  g_lastEventType = @"rightmouseup";
2358
+ } else if (currentLeftMouseDown) {
2359
+ // Sol tuş basılı tutuluyor — sürükleme
2360
+ eventType = @"drag";
2361
+ g_lastEventType = @"drag";
2362
+ } else if (currentRightMouseDown) {
2363
+ // Sağ tuş basılı tutuluyor
2364
+ eventType = @"rightdrag";
2365
+ g_lastEventType = @"rightdrag";
2254
2366
  } else {
2255
2367
  eventType = @"move";
2256
2368
  g_lastEventType = @"move";
@@ -214,9 +214,9 @@ static void SCKQualityBitrateForDimensions(NSString *preset,
214
214
  minBitrate = 18 * 1000 * 1000;
215
215
  maxBitrate = 80 * 1000 * 1000;
216
216
  } else { // high/default - ULTRA quality
217
- multiplier = 80;
218
- minBitrate = 100 * 1000 * 1000;
219
- maxBitrate = 400 * 1000 * 1000;
217
+ multiplier = 120;
218
+ minBitrate = 120 * 1000 * 1000;
219
+ maxBitrate = 600 * 1000 * 1000;
220
220
  }
221
221
 
222
222
  double base = ((double)MAX(1, width)) * ((double)MAX(1, height)) * (double)multiplier;
@@ -783,7 +783,7 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
783
783
  NSString *normalizedQuality = SCKNormalizeQualityPreset(g_qualityPreset);
784
784
  SCKQualityBitrateForDimensions(normalizedQuality, width, height, &bitrate, &bitrateMultiplier, &minBitrate, &maxBitrate);
785
785
 
786
- NSNumber *qualityHint = @0.95;
786
+ NSNumber *qualityHint = @1.0;
787
787
  if ([normalizedQuality isEqualToString:@"medium"]) {
788
788
  qualityHint = @0.9;
789
789
  } else if ([normalizedQuality isEqualToString:@"low"]) {
@@ -1349,14 +1349,6 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
1349
1349
  g_targetFPS = 60;
1350
1350
  }
1351
1351
 
1352
- // CRITICAL ELECTRON FIX: Lower FPS to 30 when recording with camera
1353
- // This prevents resource conflicts and crashes when running both simultaneously
1354
- BOOL isCameraEnabled = captureCamera && [captureCamera boolValue];
1355
- if (isCameraEnabled && g_targetFPS > 30) {
1356
- MRLog(@"📹 Camera recording detected - lowering ScreenCaptureKit FPS from %ld to 30 for stability", (long)g_targetFPS);
1357
- g_targetFPS = 30;
1358
- }
1359
-
1360
1352
  MRLog(@"🎬 Starting PURE ScreenCaptureKit recording (NO AVFoundation)");
1361
1353
  MRLog(@"🔧 Config: cursor=%@ mic=%@ system=%@ display=%@ window=%@ crop=%@",
1362
1354
  captureCursor, includeMicrophone, includeSystemAudio, displayId, windowId, captureRect);