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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.10.18",
3
+ "version": "2.10.20",
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,33 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const WindowSelector = require("./window-selector");
3
+ const WindowSelector = require('./window-selector');
4
4
 
5
5
  async function quickTest() {
6
- console.log("๐Ÿงช Quick Window Selector Test");
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("โœ… 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
- ");
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("โŒ Test failed:", error.message);
27
+ console.error('โŒ Test failed:', error.message);
31
28
  await selector.cleanup();
32
29
  process.exit(1);
33
30
  }
@@ -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 - no border
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 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 200, 60)];
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 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
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 overlay first
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 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 200, 60)];
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 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
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];