node-mac-recorder 2.10.18 โ 2.10.20
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 +9 -12
- package/src/window_selector.mm +101 -14
package/package.json
CHANGED
package/quick-test.js
CHANGED
|
@@ -1,33 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const WindowSelector = require(
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
4
|
|
|
5
5
|
async function quickTest() {
|
|
6
|
-
console.log(
|
|
7
|
-
console.log(
|
|
8
|
-
");
|
|
6
|
+
console.log('๐งช Quick Window Selector Test');
|
|
7
|
+
console.log('============================\n');
|
|
9
8
|
|
|
10
9
|
const selector = new WindowSelector();
|
|
11
10
|
|
|
12
11
|
try {
|
|
13
|
-
console.log(
|
|
14
|
-
console.log(
|
|
15
|
-
console.log(
|
|
16
|
-
console.log(
|
|
17
|
-
");
|
|
12
|
+
console.log('โ
Starting window selection...');
|
|
13
|
+
console.log('๐ฏ Hover over windows to see highlighting (no border)');
|
|
14
|
+
console.log('๐ Window dragging should be blocked');
|
|
15
|
+
console.log('โ Test will auto-stop in 15 seconds\n');
|
|
18
16
|
|
|
19
17
|
await selector.startSelection();
|
|
20
18
|
|
|
21
19
|
// Auto stop after 15 seconds
|
|
22
20
|
setTimeout(async () => {
|
|
23
|
-
console.log(
|
|
24
|
-
โน๏ธ Auto-stopping test...");
|
|
21
|
+
console.log('\nโน๏ธ Auto-stopping test...');
|
|
25
22
|
await selector.cleanup();
|
|
26
23
|
process.exit(0);
|
|
27
24
|
}, 15000);
|
|
28
25
|
|
|
29
26
|
} catch (error) {
|
|
30
|
-
console.error(
|
|
27
|
+
console.error('โ Test failed:', error.message);
|
|
31
28
|
await selector.cleanup();
|
|
32
29
|
process.exit(1);
|
|
33
30
|
}
|
package/src/window_selector.mm
CHANGED
|
@@ -60,6 +60,12 @@ void updateScreenOverlays();
|
|
|
60
60
|
- (void)setHighlightFrame:(NSRect)frame;
|
|
61
61
|
@end
|
|
62
62
|
|
|
63
|
+
// Custom button with hover effects
|
|
64
|
+
@interface HoverButton : NSButton
|
|
65
|
+
@property (nonatomic) BOOL isHovered;
|
|
66
|
+
- (void)setupHoverTracking;
|
|
67
|
+
@end
|
|
68
|
+
|
|
63
69
|
@implementation WindowSelectorOverlayView
|
|
64
70
|
|
|
65
71
|
- (instancetype)initWithFrame:(NSRect)frameRect {
|
|
@@ -95,17 +101,29 @@ void updateScreenOverlays();
|
|
|
95
101
|
xRadius:8.0
|
|
96
102
|
yRadius:8.0];
|
|
97
103
|
|
|
98
|
-
// Fill color based on toggle state
|
|
104
|
+
// Fill color and border based on toggle state
|
|
99
105
|
NSColor *fillColor;
|
|
106
|
+
NSColor *strokeColor;
|
|
107
|
+
CGFloat lineWidth;
|
|
100
108
|
|
|
101
109
|
if (self.isToggled) {
|
|
110
|
+
// Locked state: thicker border
|
|
102
111
|
fillColor = [NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4];
|
|
112
|
+
strokeColor = [NSColor colorWithRed:0.45 green:0.25 blue:0.75 alpha:0.9];
|
|
113
|
+
lineWidth = 3.0;
|
|
103
114
|
} else {
|
|
115
|
+
// Normal state: thin border
|
|
104
116
|
fillColor = [NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4];
|
|
117
|
+
strokeColor = [NSColor whiteColor];
|
|
118
|
+
lineWidth = 1.0;
|
|
105
119
|
}
|
|
106
120
|
|
|
107
121
|
[fillColor setFill];
|
|
108
122
|
[highlightPath fill];
|
|
123
|
+
|
|
124
|
+
[strokeColor setStroke];
|
|
125
|
+
[highlightPath setLineWidth:lineWidth];
|
|
126
|
+
[highlightPath stroke];
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
- (void)updateAppearance {
|
|
@@ -162,6 +180,65 @@ void updateScreenOverlays();
|
|
|
162
180
|
|
|
163
181
|
@end
|
|
164
182
|
|
|
183
|
+
@implementation HoverButton
|
|
184
|
+
|
|
185
|
+
- (instancetype)initWithFrame:(NSRect)frameRect {
|
|
186
|
+
self = [super initWithFrame:frameRect];
|
|
187
|
+
if (self) {
|
|
188
|
+
self.isHovered = NO;
|
|
189
|
+
[self setupHoverTracking];
|
|
190
|
+
}
|
|
191
|
+
return self;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
- (void)setupHoverTracking {
|
|
195
|
+
NSTrackingArea *trackingArea = [[NSTrackingArea alloc]
|
|
196
|
+
initWithRect:self.bounds
|
|
197
|
+
options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways)
|
|
198
|
+
owner:self
|
|
199
|
+
userInfo:nil];
|
|
200
|
+
[self addTrackingArea:trackingArea];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
- (void)mouseEntered:(NSEvent *)event {
|
|
204
|
+
self.isHovered = YES;
|
|
205
|
+
[self animateScale:1.2 duration:0.15];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
- (void)mouseExited:(NSEvent *)event {
|
|
209
|
+
self.isHovered = NO;
|
|
210
|
+
[self animateScale:1.0 duration:0.15];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
- (void)animateScale:(CGFloat)scale duration:(NSTimeInterval)duration {
|
|
214
|
+
// Set anchor point to center for center-based scaling
|
|
215
|
+
self.layer.anchorPoint = CGPointMake(0.5, 0.5);
|
|
216
|
+
|
|
217
|
+
[NSAnimationContext beginGrouping];
|
|
218
|
+
[NSAnimationContext currentContext].duration = duration;
|
|
219
|
+
[NSAnimationContext currentContext].timingFunction =
|
|
220
|
+
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
|
221
|
+
|
|
222
|
+
CATransform3D transform = CATransform3DMakeScale(scale, scale, 1.0);
|
|
223
|
+
self.layer.transform = transform;
|
|
224
|
+
|
|
225
|
+
[NSAnimationContext endGrouping];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
- (void)updateTrackingAreas {
|
|
229
|
+
[super updateTrackingAreas];
|
|
230
|
+
|
|
231
|
+
// Remove old tracking areas
|
|
232
|
+
for (NSTrackingArea *area in self.trackingAreas) {
|
|
233
|
+
[self removeTrackingArea:area];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add new tracking area
|
|
237
|
+
[self setupHoverTracking];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@end
|
|
241
|
+
|
|
165
242
|
// Recording preview overlay view - full screen with cutout
|
|
166
243
|
@interface RecordingPreviewView : NSView
|
|
167
244
|
@property (nonatomic, strong) NSDictionary *recordingWindowInfo;
|
|
@@ -950,7 +1027,7 @@ bool showRecordingPreview(NSDictionary *windowInfo) {
|
|
|
950
1027
|
[g_recordingPreviewWindow setAcceptsMouseMovedEvents:NO];
|
|
951
1028
|
[g_recordingPreviewWindow setHasShadow:NO];
|
|
952
1029
|
[g_recordingPreviewWindow setAlphaValue:1.0];
|
|
953
|
-
[g_recordingPreviewWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces];
|
|
1030
|
+
[g_recordingPreviewWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle];
|
|
954
1031
|
|
|
955
1032
|
// Remove any default window decorations and borders
|
|
956
1033
|
[g_recordingPreviewWindow setTitlebarAppearsTransparent:YES];
|
|
@@ -1127,6 +1204,11 @@ bool startScreenSelection() {
|
|
|
1127
1204
|
@try {
|
|
1128
1205
|
if (g_isScreenSelecting) return false;
|
|
1129
1206
|
|
|
1207
|
+
// Clean up any existing window selector first
|
|
1208
|
+
if (g_isWindowSelecting) {
|
|
1209
|
+
cleanupWindowSelector();
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1130
1212
|
// Get all available screens
|
|
1131
1213
|
NSArray *screens = [NSScreen screens];
|
|
1132
1214
|
if (!screens || [screens count] == 0) return false;
|
|
@@ -1219,7 +1301,7 @@ bool startScreenSelection() {
|
|
|
1219
1301
|
[overlayWindow setHasShadow:NO];
|
|
1220
1302
|
[overlayWindow setAlphaValue:1.0];
|
|
1221
1303
|
// Ensure window appears on all spaces and stays put - match g_overlayWindow
|
|
1222
|
-
[overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces];
|
|
1304
|
+
[overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle];
|
|
1223
1305
|
|
|
1224
1306
|
// Remove any default window decorations and borders
|
|
1225
1307
|
[overlayWindow setTitlebarAppearsTransparent:YES];
|
|
@@ -1239,8 +1321,8 @@ bool startScreenSelection() {
|
|
|
1239
1321
|
|
|
1240
1322
|
// Note: NSWindow doesn't have setWantsLayer method, only NSView does
|
|
1241
1323
|
|
|
1242
|
-
// Create select button with more padding
|
|
1243
|
-
NSButton *selectButton = [[
|
|
1324
|
+
// Create select button with more padding and hover effects
|
|
1325
|
+
NSButton *selectButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 200, 60)];
|
|
1244
1326
|
[selectButton setTitle:@"Start Record"];
|
|
1245
1327
|
[selectButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1246
1328
|
[selectButton setBordered:NO];
|
|
@@ -1282,8 +1364,8 @@ bool startScreenSelection() {
|
|
|
1282
1364
|
[selectButton setFocusRingType:NSFocusRingTypeNone];
|
|
1283
1365
|
[selectButton setShowsBorderOnlyWhileMouseInside:NO];
|
|
1284
1366
|
|
|
1285
|
-
// Create cancel button for screen selection
|
|
1286
|
-
NSButton *screenCancelButton = [[
|
|
1367
|
+
// Create cancel button for screen selection with hover effects
|
|
1368
|
+
NSButton *screenCancelButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
1287
1369
|
[screenCancelButton setTitle:@"Cancel"];
|
|
1288
1370
|
[screenCancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1289
1371
|
[screenCancelButton setBordered:NO];
|
|
@@ -1519,7 +1601,7 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
1519
1601
|
// no border
|
|
1520
1602
|
[overlayWindow setStyleMask:NSWindowStyleMaskBorderless];
|
|
1521
1603
|
[overlayWindow setAlphaValue:1.0];
|
|
1522
|
-
[overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces];
|
|
1604
|
+
[overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle];
|
|
1523
1605
|
|
|
1524
1606
|
|
|
1525
1607
|
// Force content view to have no borders
|
|
@@ -1576,7 +1658,7 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1576
1658
|
}
|
|
1577
1659
|
|
|
1578
1660
|
@try {
|
|
1579
|
-
// Clean up any existing
|
|
1661
|
+
// Clean up any existing overlays first
|
|
1580
1662
|
if (g_overlayWindow) {
|
|
1581
1663
|
[g_overlayWindow close];
|
|
1582
1664
|
g_overlayWindow = nil;
|
|
@@ -1584,6 +1666,11 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1584
1666
|
g_selectButton = nil;
|
|
1585
1667
|
}
|
|
1586
1668
|
|
|
1669
|
+
// Also cleanup screen selector if running
|
|
1670
|
+
if (g_isScreenSelecting) {
|
|
1671
|
+
cleanupScreenSelector();
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1587
1674
|
// Get all windows
|
|
1588
1675
|
g_allWindows = [getAllSelectableWindows() retain];
|
|
1589
1676
|
|
|
@@ -1610,7 +1697,7 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1610
1697
|
[g_overlayWindow setAcceptsMouseMovedEvents:YES];
|
|
1611
1698
|
[g_overlayWindow setHasShadow:NO];
|
|
1612
1699
|
[g_overlayWindow setAlphaValue:1.0];
|
|
1613
|
-
[g_overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorFullScreenAuxiliary];
|
|
1700
|
+
[g_overlayWindow setCollectionBehavior:NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorFullScreenAuxiliary | NSWindowCollectionBehaviorIgnoresCycle];
|
|
1614
1701
|
|
|
1615
1702
|
// Remove any default window decorations and borders
|
|
1616
1703
|
[g_overlayWindow setTitlebarAppearsTransparent:YES];
|
|
@@ -1640,8 +1727,8 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1640
1727
|
[g_overlayWindow setMovable:NO];
|
|
1641
1728
|
[g_overlayWindow setMovableByWindowBackground:NO];
|
|
1642
1729
|
|
|
1643
|
-
// Create select button with purple theme
|
|
1644
|
-
g_selectButton = [[
|
|
1730
|
+
// Create select button with purple theme and hover effects
|
|
1731
|
+
g_selectButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 200, 60)];
|
|
1645
1732
|
[g_selectButton setTitle:@"Start Record"];
|
|
1646
1733
|
[g_selectButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1647
1734
|
[g_selectButton setBordered:NO];
|
|
@@ -1681,8 +1768,8 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1681
1768
|
// Add select button directly to window (not view) for proper layering
|
|
1682
1769
|
[g_overlayWindow.contentView addSubview:g_selectButton];
|
|
1683
1770
|
|
|
1684
|
-
// Create cancel button
|
|
1685
|
-
NSButton *cancelButton = [[
|
|
1771
|
+
// Create cancel button with hover effects
|
|
1772
|
+
NSButton *cancelButton = [[HoverButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
1686
1773
|
[cancelButton setTitle:@"Cancel"];
|
|
1687
1774
|
[cancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1688
1775
|
[cancelButton setBordered:NO];
|