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 +1 -1
- package/quick-test.js +31 -43
- package/src/window_selector.mm +66 -43
package/package.json
CHANGED
package/quick-test.js
CHANGED
|
@@ -1,50 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require("./window-selector");
|
|
2
4
|
|
|
3
5
|
async function quickTest() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
console.log("🧪 Quick Window Selector Test");
|
|
7
|
+
console.log("============================
|
|
8
|
+
");
|
|
9
|
+
|
|
10
|
+
const selector = new WindowSelector();
|
|
11
|
+
|
|
8
12
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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(
|
|
30
|
+
console.error("❌ Test failed:", error.message);
|
|
31
|
+
await selector.cleanup();
|
|
32
|
+
process.exit(1);
|
|
47
33
|
}
|
|
48
34
|
}
|
|
49
35
|
|
|
50
|
-
|
|
36
|
+
if (require.main === module) {
|
|
37
|
+
quickTest().catch(console.error);
|
|
38
|
+
}
|
package/src/window_selector.mm
CHANGED
|
@@ -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
|
-
//
|
|
68
|
+
// Full-screen transparent overlay for window highlighting
|
|
67
69
|
self.wantsLayer = YES;
|
|
68
|
-
self.isActiveWindow = YES;
|
|
70
|
+
self.isActiveWindow = YES;
|
|
71
|
+
self.highlightFrame = NSZeroRect;
|
|
69
72
|
|
|
70
|
-
//
|
|
71
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
//
|
|
657
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1574
|
-
|
|
1575
|
-
|
|
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:
|
|
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
|