node-mac-recorder 2.10.16 → 2.10.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.10.16",
3
+ "version": "2.10.18",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
package/quick-test.js CHANGED
@@ -1,50 +1,38 @@
1
- const MacRecorder = require('./index');
1
+ #!/usr/bin/env node
2
+
3
+ const WindowSelector = require("./window-selector");
2
4
 
3
5
  async function quickTest() {
4
- const recorder = new MacRecorder();
5
-
6
- console.log('🚀 Hızlı Sistem Sesi Testi\n');
7
-
6
+ console.log("🧪 Quick Window Selector Test");
7
+ console.log("============================
8
+ ");
9
+
10
+ const selector = new WindowSelector();
11
+
8
12
  try {
9
- // Ses cihazlarını listele
10
- const devices = await recorder.getAudioDevices();
11
- console.log('🎤 Mevcut ses cihazları:');
12
- devices.forEach((d, i) => console.log(`${i+1}. ${d.name}`));
13
-
14
- // Sistem sesi cihazı var mı?
15
- const sysDevice = devices.find(d =>
16
- d.name.toLowerCase().includes('aggregate') ||
17
- d.name.toLowerCase().includes('blackhole') ||
18
- d.name.toLowerCase().includes('soundflower')
19
- );
20
-
21
- if (sysDevice) {
22
- console.log(`\n✅ Sistem sesi cihazı bulundu: ${sysDevice.name}`);
23
- console.log('🎵 Sistem sesi yakalama çalışmalı');
24
- } else {
25
- console.log('\n⚠️ Sistem sesi cihazı yok');
26
- console.log('💡 BlackHole veya Soundflower yüklemen gerekiyor');
27
- }
28
-
29
- // Kısa test kayıt
30
- console.log('\n🔴 2 saniyelik test kayıt başlıyor...');
31
- console.log('🎵 Şimdi müzik çal!');
32
-
33
- await recorder.startRecording('./test-output/quick-test.mov', {
34
- includeSystemAudio: true,
35
- includeMicrophone: false,
36
- systemAudioDeviceId: sysDevice?.id,
37
- captureArea: { x: 0, y: 0, width: 200, height: 150 }
38
- });
39
-
40
- await new Promise(resolve => setTimeout(resolve, 2000));
41
- await recorder.stopRecording();
42
-
43
- console.log('✅ Test tamamlandı! ./test-output/quick-test.mov dosyasını kontrol et');
44
-
13
+ console.log("✅ Starting window selection...");
14
+ console.log("🎯 Hover over windows to see highlighting (no border)");
15
+ console.log("🔒 Window dragging should be blocked");
16
+ console.log("⌛ Test will auto-stop in 15 seconds
17
+ ");
18
+
19
+ await selector.startSelection();
20
+
21
+ // Auto stop after 15 seconds
22
+ setTimeout(async () => {
23
+ console.log("
24
+ ⏹️ Auto-stopping test...");
25
+ await selector.cleanup();
26
+ process.exit(0);
27
+ }, 15000);
28
+
45
29
  } catch (error) {
46
- console.error('Hata:', error.message);
30
+ console.error("Test failed:", error.message);
31
+ await selector.cleanup();
32
+ process.exit(1);
47
33
  }
48
34
  }
49
35
 
50
- quickTest();
36
+ if (require.main === module) {
37
+ quickTest().catch(console.error);
38
+ }
@@ -56,6 +56,8 @@ void updateScreenOverlays();
56
56
  @property (nonatomic, strong) NSDictionary *windowInfo;
57
57
  @property (nonatomic) BOOL isActiveWindow;
58
58
  @property (nonatomic) BOOL isToggled;
59
+ @property (nonatomic) NSRect highlightFrame;
60
+ - (void)setHighlightFrame:(NSRect)frame;
59
61
  @end
60
62
 
61
63
  @implementation WindowSelectorOverlayView
@@ -63,41 +65,52 @@ void updateScreenOverlays();
63
65
  - (instancetype)initWithFrame:(NSRect)frameRect {
64
66
  self = [super initWithFrame:frameRect];
65
67
  if (self) {
66
- // Use layer for background instead of custom drawing
68
+ // Full-screen transparent overlay for window highlighting
67
69
  self.wantsLayer = YES;
68
- self.isActiveWindow = YES; // Default to active for current window under mouse
70
+ self.isActiveWindow = YES;
71
+ self.highlightFrame = NSZeroRect;
69
72
 
70
- // Set purple background with border using layer
71
- [self updateAppearance];
73
+ // Transparent background for full-screen overlay
74
+ self.layer.backgroundColor = [[NSColor clearColor] CGColor];
72
75
 
73
76
  // Window selector overlay view created
74
77
  }
75
78
  return self;
76
79
  }
77
80
 
78
- - (void)updateAppearance {
81
+ - (void)setHighlightFrame:(NSRect)frame {
82
+ _highlightFrame = frame;
83
+ [self setNeedsDisplay:YES]; // Trigger redraw
84
+ }
85
+
86
+ - (void)drawRect:(NSRect)dirtyRect {
87
+ [super drawRect:dirtyRect];
88
+
89
+ if (NSEqualRects(self.highlightFrame, NSZeroRect)) {
90
+ return; // No window to highlight
91
+ }
92
+
93
+ // Draw highlight rectangle for the selected window
94
+ NSBezierPath *highlightPath = [NSBezierPath bezierPathWithRoundedRect:self.highlightFrame
95
+ xRadius:8.0
96
+ yRadius:8.0];
97
+
98
+ // Fill color based on toggle state - no border
99
+ NSColor *fillColor;
100
+
79
101
  if (self.isToggled) {
80
- // Toggled window: same background, thick border with darker version of fill color
81
- self.layer.backgroundColor = [[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] CGColor];
82
- self.layer.borderColor = [[NSColor colorWithRed:0.45 green:0.25 blue:0.75 alpha:0.9] CGColor]; // Darker purple
83
- self.layer.borderWidth = 3.0;
84
- } else if (self.isActiveWindow) {
85
- // Active window: brighter background, thin white border
86
- self.layer.backgroundColor = [[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] CGColor];
87
- self.layer.borderColor = [[NSColor whiteColor] CGColor];
88
- self.layer.borderWidth = 1.0;
102
+ fillColor = [NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4];
89
103
  } else {
90
- // Inactive window: dimmer background, thin white border
91
- self.layer.backgroundColor = [[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25] CGColor];
92
- self.layer.borderColor = [[NSColor whiteColor] CGColor];
93
- self.layer.borderWidth = 1.0;
104
+ fillColor = [NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4];
94
105
  }
95
106
 
96
- self.layer.cornerRadius = 8.0;
97
- self.layer.masksToBounds = YES;
98
- self.layer.shadowOpacity = 0.0;
99
- self.layer.shadowRadius = 0.0;
100
- self.layer.shadowOffset = NSMakeSize(0, 0);
107
+ [fillColor setFill];
108
+ [highlightPath fill];
109
+ }
110
+
111
+ - (void)updateAppearance {
112
+ // Appearance is now handled in drawRect
113
+ [self setNeedsDisplay:YES];
101
114
  }
102
115
 
103
116
  - (void)setIsActiveWindow:(BOOL)isActiveWindow {
@@ -500,7 +513,7 @@ void updateOverlay() {
500
513
  CGFloat screenHeight = [mainScreen frame].size.height;
501
514
  CGPoint globalPoint = CGPointMake(mouseLocation.x, screenHeight - mouseLocation.y);
502
515
 
503
- // Find window under cursor
516
+ // Find window under cursor (no need to refresh g_allWindows frequently since windows can't move)
504
517
  NSDictionary *windowUnderCursor = getWindowUnderCursor(globalPoint);
505
518
 
506
519
  // Check if we need to update overlay (new window or position change of current window)
@@ -653,11 +666,10 @@ void updateOverlay() {
653
666
  }
654
667
  }
655
668
 
656
- // Ensure overlay is on the correct screen
657
- [g_overlayWindow setFrame:overlayFrame display:YES];
658
-
659
- // Update overlay view window info
669
+ // No need to resize window since it's full-screen, just update the highlight area
670
+ // Update overlay view window info for highlighting
660
671
  [(WindowSelectorOverlayView *)g_overlayView setWindowInfo:targetWindow];
672
+ [(WindowSelectorOverlayView *)g_overlayView setHighlightFrame:NSMakeRect(x, [g_overlayView frame].size.height - y - height, width, height)];
661
673
 
662
674
  // Only reset toggle state when switching to different window (not for position updates)
663
675
  if (!g_hasToggledWindow) {
@@ -755,18 +767,20 @@ void updateOverlay() {
755
767
  NSString *labelAppName = [windowUnderCursor objectForKey:@"appName"] ?: @"Unknown App";
756
768
  [infoLabel setStringValue:[NSString stringWithFormat:@"%@\n%@", labelAppName, labelWindowTitle]];
757
769
 
758
- // Position buttons - Start Record in center, Cancel below it
770
+ // Position buttons - Start Record in center of highlighted window
759
771
  if (g_selectButton) {
760
772
  NSSize buttonSize = [g_selectButton frame].size;
773
+ // Convert window coordinates to overlay view coordinates
774
+ NSRect highlightFrame = NSMakeRect(x, [g_overlayView frame].size.height - y - height, width, height);
761
775
  NSPoint buttonCenter = NSMakePoint(
762
- (width - buttonSize.width) / 2,
763
- (height - buttonSize.height) / 2 + 15 // Slightly above center
776
+ highlightFrame.origin.x + (highlightFrame.size.width - buttonSize.width) / 2,
777
+ highlightFrame.origin.y + (highlightFrame.size.height - buttonSize.height) / 2 + 15
764
778
  );
765
779
  [g_selectButton setFrameOrigin:buttonCenter];
766
780
 
767
- // Position app icon above text label
781
+ // Position app icon above text label within highlighted area
768
782
  NSPoint iconCenter = NSMakePoint(
769
- (width - 96) / 2, // Center horizontally (icon is 96px wide)
783
+ highlightFrame.origin.x + (highlightFrame.size.width - 96) / 2, // Center horizontally within highlight
770
784
  buttonCenter.y + buttonSize.height + 60 + 10 // Above label + text height + margin
771
785
  );
772
786
  [appIconView setFrameOrigin:iconCenter];
@@ -787,9 +801,9 @@ void updateOverlay() {
787
801
  floatAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
788
802
  [appIconView.layer addAnimation:floatAnimationX forKey:@"floatAnimationX"];
789
803
 
790
- // Position info label at overlay center, above button
804
+ // Position info label within highlighted area, above button
791
805
  NSPoint labelCenter = NSMakePoint(
792
- (width - [infoLabel frame].size.width) / 2, // Center horizontally
806
+ highlightFrame.origin.x + (highlightFrame.size.width - [infoLabel frame].size.width) / 2, // Center horizontally within highlight
793
807
  buttonCenter.y + buttonSize.height + 10 // 10px above button, below icon
794
808
  );
795
809
  [infoLabel setFrameOrigin:labelCenter];
@@ -807,7 +821,7 @@ void updateOverlay() {
807
821
  if (cancelButton) {
808
822
  NSSize cancelButtonSize = [cancelButton frame].size;
809
823
  NSPoint cancelButtonCenter = NSMakePoint(
810
- (width - cancelButtonSize.width) / 2,
824
+ highlightFrame.origin.x + (highlightFrame.size.width - cancelButtonSize.width) / 2,
811
825
  buttonCenter.y - buttonSize.height - 20 // 20px below main button
812
826
  );
813
827
  [cancelButton setFrameOrigin:cancelButtonCenter];
@@ -1562,6 +1576,14 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1562
1576
  }
1563
1577
 
1564
1578
  @try {
1579
+ // Clean up any existing overlay first
1580
+ if (g_overlayWindow) {
1581
+ [g_overlayWindow close];
1582
+ g_overlayWindow = nil;
1583
+ g_overlayView = nil;
1584
+ g_selectButton = nil;
1585
+ }
1586
+
1565
1587
  // Get all windows
1566
1588
  g_allWindows = [getAllSelectableWindows() retain];
1567
1589
 
@@ -1570,9 +1592,10 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1570
1592
  return env.Null();
1571
1593
  }
1572
1594
 
1573
- // Create overlay window (initially hidden)
1574
- NSRect initialFrame = NSMakeRect(0, 0, 100, 100);
1575
- g_overlayWindow = [[NSWindow alloc] initWithContentRect:initialFrame
1595
+ // Create full-screen overlay window to prevent window dragging
1596
+ NSScreen *mainScreen = [NSScreen mainScreen];
1597
+ NSRect fullScreenFrame = [mainScreen frame];
1598
+ g_overlayWindow = [[NSWindow alloc] initWithContentRect:fullScreenFrame
1576
1599
  styleMask:NSWindowStyleMaskBorderless
1577
1600
  backing:NSBackingStoreBuffered
1578
1601
  defer:NO];
@@ -1583,11 +1606,11 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1583
1606
  [g_overlayWindow setLevel:CGWindowLevelForKey(kCGMaximumWindowLevelKey)]; // Absolute highest level
1584
1607
  [g_overlayWindow setOpaque:NO];
1585
1608
  [g_overlayWindow setBackgroundColor:[NSColor clearColor]];
1586
- [g_overlayWindow setIgnoresMouseEvents:NO];
1609
+ [g_overlayWindow setIgnoresMouseEvents:NO]; // Capture mouse events to prevent window dragging
1587
1610
  [g_overlayWindow setAcceptsMouseMovedEvents:YES];
1588
1611
  [g_overlayWindow setHasShadow:NO];
1589
1612
  [g_overlayWindow setAlphaValue:1.0];
1590
- [g_overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces];
1613
+ [g_overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorFullScreenAuxiliary];
1591
1614
 
1592
1615
  // Remove any default window decorations and borders
1593
1616
  [g_overlayWindow setTitlebarAppearsTransparent:YES];
@@ -1600,8 +1623,8 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
1600
1623
  [g_overlayWindow setOpaque:NO];
1601
1624
  [g_overlayWindow setBackgroundColor:[NSColor clearColor]];
1602
1625
 
1603
- // Create overlay view
1604
- g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:initialFrame];
1626
+ // Create overlay view covering full screen
1627
+ g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:fullScreenFrame];
1605
1628
  [g_overlayWindow setContentView:g_overlayView];
1606
1629
 
1607
1630
  // Note: NSWindow doesn't have setWantsLayer method, only NSView does