From d52700a3d40a7f3f81249aad7c6e264753f1a831 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Mon, 28 Jul 2025 19:24:11 -0700 Subject: [PATCH 1/3] feat: Implement RCTPausedInDebuggerOverlayController --- .../React/CoreModules/RCTDevLoadingView.mm | 2 +- .../React/CoreModules/RCTLogBoxView.mm | 6 +- .../RCTPausedInDebuggerOverlayController.mm | 112 ++++++++++++++---- packages/rn-tester/Podfile | 3 +- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTDevLoadingView.mm b/packages/react-native/React/CoreModules/RCTDevLoadingView.mm index 7f48819eab3987..3298232b47e981 100644 --- a/packages/react-native/React/CoreModules/RCTDevLoadingView.mm +++ b/packages/react-native/React/CoreModules/RCTDevLoadingView.mm @@ -130,7 +130,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo self->_window.rootViewController = [UIViewController new]; #else // [macOS self->_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 375, 20) - styleMask:NSWindowStyleMaskBorderless + styleMask:NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView backing:NSBackingStoreBuffered defer:YES]; [self->_window setIdentifier:sRCTDevLoadingViewWindowIdentifier]; diff --git a/packages/react-native/React/CoreModules/RCTLogBoxView.mm b/packages/react-native/React/CoreModules/RCTLogBoxView.mm index eb63d842fc834b..0f7a31a78430fa 100644 --- a/packages/react-native/React/CoreModules/RCTLogBoxView.mm +++ b/packages/react-native/React/CoreModules/RCTLogBoxView.mm @@ -79,7 +79,11 @@ - (instancetype)initWithWindow:(RCTPlatformWindow *)window surfacePresenter:(id< #if !TARGET_OS_OSX // [macOS] self = [super initWithWindowScene:window.windowScene]; #else // [macOS - self = [super initWithContentRect:NSMakeRect(0, 0, 600, 800) styleMask:NSWindowStyleMaskTitled backing:NSBackingStoreBuffered defer:YES]; + self = [super + initWithContentRect:NSMakeRect(0, 0, 600, 800) + styleMask:NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView + backing:NSBackingStoreBuffered + defer:YES]; _window = window; #endif // macOS] diff --git a/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm b/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm index 62b7697b6a114e..a1605f094536e9 100644 --- a/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm +++ b/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm @@ -16,19 +16,17 @@ @interface RCTPausedInDebuggerViewController : UIViewController @end @interface RCTPausedInDebuggerOverlayController () -#if !TARGET_OS_OSX // [macOS] -@property (nonatomic, strong) UIWindow *alertWindow; +@property (nonatomic, strong) RCTPlatformWindow *alertWindow; // [macOS] -#endif // [macOS]; @end @implementation RCTPausedInDebuggerViewController -#if !TARGET_OS_OSX // [macOS] - (void)viewDidLoad { [super viewDidLoad]; +#if !TARGET_OS_OSX // [macOS] UIView *dimmingView = [[UIView alloc] init]; dimmingView.translatesAutoresizingMaskIntoConstraints = NO; dimmingView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2]; @@ -39,15 +37,19 @@ - (void)viewDidLoad [dimmingView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], [dimmingView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor] ]]; - - UILabel *messageLabel = [[UILabel alloc] init]; +#endif // [macOS] + + RCTUILabel *messageLabel = [[RCTUILabel alloc] init]; // [macOS] messageLabel.text = self.message; messageLabel.textAlignment = NSTextAlignmentCenter; +#if !TARGET_OS_OSX // [macOS] messageLabel.numberOfLines = 0; +#endif // [macOS] messageLabel.font = [UIFont boldSystemFontOfSize:16]; - messageLabel.textColor = [UIColor blackColor]; + messageLabel.textColor = [RCTUIColor blackColor]; // [macOS] messageLabel.translatesAutoresizingMaskIntoConstraints = NO; - UIView *messageContainer = [[UIView alloc] init]; + + RCTUIView *messageContainer = [[RCTUIView alloc] init]; // [macOS] [messageContainer addSubview:messageLabel]; [NSLayoutConstraint activateConstraints:@[ [messageLabel.topAnchor constraintEqualToAnchor:messageContainer.topAnchor constant:-1], @@ -56,6 +58,7 @@ - (void)viewDidLoad [messageLabel.trailingAnchor constraintEqualToAnchor:messageContainer.trailingAnchor], ]]; +#if !TARGET_OS_OSX // [macOS] UIButton *resumeButton = [UIButton buttonWithType:UIButtonTypeCustom]; [resumeButton setImage:[UIImage systemImageNamed:@"forward.frame.fill"] forState:UIControlStateNormal]; resumeButton.tintColor = [UIColor colorWithRed:0.37 green:0.37 blue:0.37 alpha:1]; @@ -65,82 +68,149 @@ - (void)viewDidLoad }; resumeButton.enabled = NO; +#else // [macOS + NSButton *resumeButton = [[NSButton alloc] init]; + [resumeButton setImage:[NSImage imageWithSystemSymbolName:@"forward.frame.fill" accessibilityDescription:@"Resume"]]; + resumeButton.bordered = NO; + resumeButton.target = self; + resumeButton.action = @selector(handleResume:); + resumeButton.contentTintColor = [NSColor colorWithRed:0.37 green:0.37 blue:0.37 alpha:1]; +#endif // macOS] + [NSLayoutConstraint activateConstraints:@[ [resumeButton.widthAnchor constraintEqualToConstant:48], [resumeButton.heightAnchor constraintEqualToConstant:46], ]]; +#if !TARGET_OS_OSX // [macOS] UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ messageContainer, resumeButton ]]; stackView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:0.757 alpha:1]; + stackView.axis = UILayoutConstraintAxisHorizontal; + stackView.distribution = UIStackViewDistributionFill; + stackView.alignment = UIStackViewAlignmentCenter; stackView.layer.cornerRadius = 12; stackView.layer.borderWidth = 2; stackView.layer.borderColor = [UIColor colorWithRed:0.71 green:0.71 blue:0.62 alpha:1].CGColor; stackView.translatesAutoresizingMaskIntoConstraints = NO; - stackView.axis = UILayoutConstraintAxisHorizontal; - stackView.distribution = UIStackViewDistributionFill; - stackView.alignment = UIStackViewAlignmentCenter; UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleResume:)]; [stackView addGestureRecognizer:gestureRecognizer]; stackView.userInteractionEnabled = YES; +#else // [macOS + NSStackView *stackView = [NSStackView stackViewWithViews:@[ messageContainer, resumeButton ]]; + stackView.wantsLayer = YES; + stackView.layer.backgroundColor = [NSColor colorWithRed:1 green:1 blue:0.757 alpha:1].CGColor; + stackView.orientation = NSUserInterfaceLayoutOrientationHorizontal; + stackView.distribution = NSStackViewDistributionFill; + stackView.alignment = NSLayoutAttributeCenterY; + stackView.translatesAutoresizingMaskIntoConstraints = NO; + NSClickGestureRecognizer *gestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self + action:@selector(handleResume:)]; + [stackView addGestureRecognizer:gestureRecognizer]; +#endif // macOS] + +#if !TARGET_OS_OSX [self.view addSubview:stackView]; [NSLayoutConstraint activateConstraints:@[ [stackView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12], - [stackView.centerXAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor], + [stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20], + [stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20], + [stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20], + [stackView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20], + [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], ]]; +#else + [self setView:stackView]; +#endif +#if !TARGET_OS_OSX // [macOS] stackView.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight; +#else // [macOS + stackView.userInterfaceLayoutDirection = NSUserInterfaceLayoutDirectionLeftToRight; +#endif // macOS] } +#if !TARGET_OS_OSX // [macOS] - (void)handleResume:(UITapGestureRecognizer *)recognizer { self.onResume(); } -#endif // [macOS] +#else // [macOS +- (void)handleResume:(id)sender +{ + self.onResume(); +} +#endif // macOS] @end @implementation RCTPausedInDebuggerOverlayController -#if !TARGET_OS_OSX // [macOS] -- (UIWindow *)alertWindow +- (RCTPlatformWindow *)alertWindow // [macOS] { if (_alertWindow == nil) { +#if !TARGET_OS_OSX // [macOS] _alertWindow = [[UIWindow alloc] initWithWindowScene:RCTKeyWindow().windowScene]; if (_alertWindow) { _alertWindow.rootViewController = [UIViewController new]; _alertWindow.windowLevel = UIWindowLevelAlert + 1; } +#else // [macOS] + _alertWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) + styleMask:NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView + backing:NSBackingStoreBuffered + defer:YES]; + _alertWindow.backgroundColor = [NSColor clearColor]; + _alertWindow.opaque = NO; +#endif // macOS] } return _alertWindow; } -#endif // [macOS] - (void)showWithMessage:(NSString *)message onResume:(void (^)(void))onResume { [self hide]; RCTPausedInDebuggerViewController *view = [[RCTPausedInDebuggerViewController alloc] init]; -#if !TARGET_OS_OSX // [macOS] - view.modalPresentationStyle = UIModalPresentationOverFullScreen; view.message = message; view.onResume = onResume; + +#if !TARGET_OS_OSX // [macOS] + view.modalPresentationStyle = UIModalPresentationOverFullScreen; [self.alertWindow makeKeyAndVisible]; [self.alertWindow.rootViewController presentViewController:view animated:NO completion:nil]; -#endif // [macOS] +#else // [macOS] + self.alertWindow.contentViewController = view; + + NSWindow *parentWindow = RCTKeyWindow(); + if (![[parentWindow sheets] doesContain:self->_alertWindow]) { + [parentWindow beginSheet:self.alertWindow completionHandler:^(NSModalResponse returnCode) { + [self->_alertWindow orderOut:self]; + }]; + } +#endif // macOS] } - (void)hide { #if !TARGET_OS_OSX // [macOS] [_alertWindow setHidden:YES]; - _alertWindow.windowScene = nil; - + _alertWindow = nil; +#else // [macOS] + NSWindow *parentWindow = RCTKeyWindow(); + if (parentWindow) { + for (NSWindow *sheet in [parentWindow sheets]) { + if (sheet == _alertWindow) { + [parentWindow endSheet:sheet]; + break; + } + } + } _alertWindow = nil; #endif // macOS] } diff --git a/packages/rn-tester/Podfile b/packages/rn-tester/Podfile index 36de2659c76c89..ddc6190c0f09ea 100644 --- a/packages/rn-tester/Podfile +++ b/packages/rn-tester/Podfile @@ -39,8 +39,7 @@ def pods(target_name, options = {}) # Hermes is now enabled by default. # The following line will only disable Hermes if the USE_HERMES envvar is SET to a value other than 1 (e.g. USE_HERMES=0). - # [macOS] Make hermes disabled by default for our fork - hermes_enabled = ENV['USE_HERMES'] == '1' + hermes_enabled = !ENV.has_key?('USE_HERMES') || ENV['USE_HERMES'] == '1' puts "Configuring #{target_name} with Fabric #{fabric_enabled ? "enabled" : "disabled"}.#{hermes_enabled ? " Using Hermes engine." : ""}" use_react_native!( From 26bfb6f6b4fcfceae652ac4a9b78f0b37cae2eaa Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Tue, 29 Jul 2025 17:20:05 -0700 Subject: [PATCH 2/3] version plan --- .nx/version-plans/version-plan-1753834798331.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .nx/version-plans/version-plan-1753834798331.md diff --git a/.nx/version-plans/version-plan-1753834798331.md b/.nx/version-plans/version-plan-1753834798331.md new file mode 100644 index 00000000000000..ab4a6a5b73c71b --- /dev/null +++ b/.nx/version-plans/version-plan-1753834798331.md @@ -0,0 +1,5 @@ +--- +__default__: patch +--- + +Implement paused in Debugger overlay From eae40dcf36c4d77217c745d83bae449fea43fa19 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Wed, 30 Jul 2025 07:12:51 -0700 Subject: [PATCH 3/3] PR feedback --- .../React/CoreModules/RCTLogBoxView.mm | 9 +++---- .../RCTPausedInDebuggerOverlayController.mm | 26 +++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTLogBoxView.mm b/packages/react-native/React/CoreModules/RCTLogBoxView.mm index 0f7a31a78430fa..1cea7f966300df 100644 --- a/packages/react-native/React/CoreModules/RCTLogBoxView.mm +++ b/packages/react-native/React/CoreModules/RCTLogBoxView.mm @@ -79,11 +79,10 @@ - (instancetype)initWithWindow:(RCTPlatformWindow *)window surfacePresenter:(id< #if !TARGET_OS_OSX // [macOS] self = [super initWithWindowScene:window.windowScene]; #else // [macOS - self = [super - initWithContentRect:NSMakeRect(0, 0, 600, 800) - styleMask:NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView - backing:NSBackingStoreBuffered - defer:YES]; + self = [super initWithContentRect:NSMakeRect(0, 0, 600, 800) + styleMask:NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView + backing:NSBackingStoreBuffered + defer:YES]; _window = window; #endif // macOS] diff --git a/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm b/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm index a1605f094536e9..984ba47376f0aa 100644 --- a/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm +++ b/packages/react-native/React/DevSupport/RCTPausedInDebuggerOverlayController.mm @@ -48,7 +48,6 @@ - (void)viewDidLoad messageLabel.font = [UIFont boldSystemFontOfSize:16]; messageLabel.textColor = [RCTUIColor blackColor]; // [macOS] messageLabel.translatesAutoresizingMaskIntoConstraints = NO; - RCTUIView *messageContainer = [[RCTUIView alloc] init]; // [macOS] [messageContainer addSubview:messageLabel]; [NSLayoutConstraint activateConstraints:@[ @@ -85,13 +84,13 @@ - (void)viewDidLoad #if !TARGET_OS_OSX // [macOS] UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ messageContainer, resumeButton ]]; stackView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:0.757 alpha:1]; - stackView.axis = UILayoutConstraintAxisHorizontal; - stackView.distribution = UIStackViewDistributionFill; - stackView.alignment = UIStackViewAlignmentCenter; stackView.layer.cornerRadius = 12; stackView.layer.borderWidth = 2; stackView.layer.borderColor = [UIColor colorWithRed:0.71 green:0.71 blue:0.62 alpha:1].CGColor; stackView.translatesAutoresizingMaskIntoConstraints = NO; + stackView.axis = UILayoutConstraintAxisHorizontal; + stackView.distribution = UIStackViewDistributionFill; + stackView.alignment = UIStackViewAlignmentCenter; UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleResume:)]; @@ -101,10 +100,10 @@ - (void)viewDidLoad NSStackView *stackView = [NSStackView stackViewWithViews:@[ messageContainer, resumeButton ]]; stackView.wantsLayer = YES; stackView.layer.backgroundColor = [NSColor colorWithRed:1 green:1 blue:0.757 alpha:1].CGColor; + stackView.translatesAutoresizingMaskIntoConstraints = NO; stackView.orientation = NSUserInterfaceLayoutOrientationHorizontal; stackView.distribution = NSStackViewDistributionFill; stackView.alignment = NSLayoutAttributeCenterY; - stackView.translatesAutoresizingMaskIntoConstraints = NO; NSClickGestureRecognizer *gestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleResume:)]; @@ -116,11 +115,7 @@ - (void)viewDidLoad [NSLayoutConstraint activateConstraints:@[ [stackView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12], - [stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20], - [stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20], - [stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20], - [stackView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20], - [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], + [stackView.centerXAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor], ]]; #else [self setView:stackView]; @@ -176,15 +171,17 @@ - (void)showWithMessage:(NSString *)message onResume:(void (^)(void))onResume [self hide]; RCTPausedInDebuggerViewController *view = [[RCTPausedInDebuggerViewController alloc] init]; +#if !TARGET_OS_OSX // [macOS] + view.modalPresentationStyle = UIModalPresentationOverFullScreen; view.message = message; view.onResume = onResume; -#if !TARGET_OS_OSX // [macOS] - view.modalPresentationStyle = UIModalPresentationOverFullScreen; [self.alertWindow makeKeyAndVisible]; [self.alertWindow.rootViewController presentViewController:view animated:NO completion:nil]; #else // [macOS] self.alertWindow.contentViewController = view; + view.message = message; + view.onResume = onResume; NSWindow *parentWindow = RCTKeyWindow(); if (![[parentWindow sheets] doesContain:self->_alertWindow]) { @@ -199,8 +196,8 @@ - (void)hide { #if !TARGET_OS_OSX // [macOS] [_alertWindow setHidden:YES]; + _alertWindow.windowScene = nil; - _alertWindow = nil; #else // [macOS] NSWindow *parentWindow = RCTKeyWindow(); if (parentWindow) { @@ -211,8 +208,9 @@ - (void)hide } } } - _alertWindow = nil; #endif // macOS] + + _alertWindow = nil; } @end