diff --git a/DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index fed7c00..d6b56aa 100644 --- a/DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615", - "version" : "1.7.0" + "revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b", + "version" : "1.7.1" } }, { @@ -132,8 +132,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/migueldeicaza/SwiftTerm", "state" : { - "revision" : "b1262db5b6bea699a8260a8c66999436c508ca56", - "version" : "1.11.2" + "revision" : "8e7a1e154f470e19c709a00a8768df348ba5fc43", + "version" : "1.13.0" } }, { diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index 8e88c9a..1a40e0d 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -549,7 +549,7 @@ struct ContentView: View { // When sneak peek finishes, check if user is still hovering and open notch if needed if !sneakPeekShowing { runAfter(0.2) { - if isHovering && vm.notchState == .closed { + if isHovering && vm.notchState == .closed && !coordinator.isHoverOpenSuppressed { openNotch() } } @@ -1799,6 +1799,7 @@ struct ContentView: View { guard let vm, let lockScreenManager else { return } guard !lockScreenManager.isLocked else { return } guard vm.notchState == .closed else { return } + guard !self.coordinator.isHoverOpenSuppressed else { return } guard self.isHovering else { return } guard !self.handleClosedMusicWaveformTapIfNeeded() else { return } if Defaults[.enableHaptics] { @@ -1850,6 +1851,10 @@ struct ContentView: View { stickyTerminalClickMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDown]) { [weak vm] _ in Task { @MainActor in guard let vm, vm.notchState == .open else { return } + let clickLocation = NSEvent.mouseLocation + if self.isPointInsideNotchWindow(clickLocation) { + return + } vm.close() } } @@ -1902,7 +1907,8 @@ struct ContentView: View { await MainActor.run { guard self.vm.notchState == .closed, self.isHovering, - !self.isSneakPeekVisibleOnCurrentScreen else { return } + !self.isSneakPeekVisibleOnCurrentScreen, + !self.coordinator.isHoverOpenSuppressed else { return } if shouldFocusTimerTab { withAnimation(.smooth) { @@ -1927,14 +1933,27 @@ struct ContentView: View { } else if self.vm.notchState == .open && Defaults[.terminalStickyMode] && self.coordinator.currentView == .terminal { - // Terminal sticky mode kept the notch open — install a - // global click monitor so a click outside closes it. - self.installStickyTerminalClickMonitor() + // Re-sync monitor state through one code path to avoid + // monitor lifecycle races between hover and state updates. + self.syncStickyTerminalOutsideClickMonitor() } } } } } + + private func isPointInsideNotchWindow(_ point: CGPoint) -> Bool { + if let appDelegate = AppDelegate.shared { + if Defaults[.showOnAllDisplays] { + return appDelegate.windows.values.contains(where: { $0.frame.contains(point) }) + } + if let window = appDelegate.window { + return window.frame.contains(point) + } + } + + return NSApp.windows.contains(where: { $0.frame.contains(point) }) + } // Helper function to check if any popovers are active private func hasAnyActivePopovers() -> Bool { diff --git a/DynamicIsland/DynamicIslandApp.swift b/DynamicIsland/DynamicIslandApp.swift index 902d5e6..8650f79 100644 --- a/DynamicIsland/DynamicIslandApp.swift +++ b/DynamicIsland/DynamicIslandApp.swift @@ -1046,13 +1046,21 @@ class AppDelegate: NSObject, NSApplicationDelegate { closeNotchWorkItem = nil vm.open() coordinator.currentView = .terminal + TerminalManager.shared.refreshTerminalAppearanceIfNeeded() + TerminalManager.shared.focusTerminalIfPossible() + TerminalManager.shared.refreshTerminalAppearanceIfNeeded() } else { if coordinator.currentView == .terminal { + coordinator.suppressHoverOpen() + TerminalManager.shared.resignTerminalFirstResponderIfNeeded() vm.close() } else { closeNotchWorkItem?.cancel() closeNotchWorkItem = nil coordinator.currentView = .terminal + TerminalManager.shared.refreshTerminalAppearanceIfNeeded() + TerminalManager.shared.focusTerminalIfPossible() + TerminalManager.shared.refreshTerminalAppearanceIfNeeded() } } } diff --git a/DynamicIsland/DynamicIslandViewCoordinator.swift b/DynamicIsland/DynamicIslandViewCoordinator.swift index c6ed432..be70f58 100644 --- a/DynamicIsland/DynamicIslandViewCoordinator.swift +++ b/DynamicIsland/DynamicIslandViewCoordinator.swift @@ -102,6 +102,7 @@ struct ExpandedItem { class DynamicIslandViewCoordinator: ObservableObject { static let shared = DynamicIslandViewCoordinator() private var cancellables = Set() + private var hoverOpenSuppressedUntil: Date = .distantPast private static let tabOrder: [NotchViews] = [.home, .shelf, .timer, .stats, .colorPicker, .notes, .clipboard, .terminal, .extensionExperience] @@ -246,6 +247,14 @@ class DynamicIslandViewCoordinator: ObservableObject { enforceMinimumNotchWidth() } + var isHoverOpenSuppressed: Bool { + Date() < hoverOpenSuppressedUntil + } + + func suppressHoverOpen(for duration: TimeInterval = 0.35) { + hoverOpenSuppressedUntil = Date().addingTimeInterval(max(0, duration)) + } + private func handleStatsTabTransition(from oldValue: NotchViews, to newValue: NotchViews) { guard oldValue != newValue else { return } if newValue == .stats && Defaults[.enableStatsFeature] { diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index 03c4a70..7d726a5 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -5622,6 +5622,9 @@ } } } + }, + "Amazon Music" : { + }, "Animation" : { "extractionState" : "stale", @@ -41026,6 +41029,9 @@ } } } + }, + "Terminal opacity only affects the terminal backdrop; text stays fully opaque. Blur uses the system material behind the window." : { + }, "Text" : { "comment" : "Name of a clipboard item type that contains plain text.", diff --git a/DynamicIsland/components/Notch/NotchTerminalView.swift b/DynamicIsland/components/Notch/NotchTerminalView.swift index a3f4336..2db4e1f 100644 --- a/DynamicIsland/components/Notch/NotchTerminalView.swift +++ b/DynamicIsland/components/Notch/NotchTerminalView.swift @@ -86,9 +86,39 @@ struct NotchTerminalView: View { @ObservedObject var terminalManager = TerminalManager.shared @EnvironmentObject var vm: DynamicIslandViewModel @Default(.enableTerminalFeature) var enableTerminalFeature + @Default(.cornerRadiusScaling) var cornerRadiusScaling + @Default(.enableMinimalisticUI) var enableMinimalisticUI @State private var suppressionToken = UUID() @State private var isSuppressing = false + /// Top corners stay mild; bottom follows shell − padding (see `notchTerminalBottomCornerRadii`). + private static let terminalClipTopCornerRadius: CGFloat = 6 + + private var currentScreenName: String { + vm.screen ?? DynamicIslandViewCoordinator.shared.selectedScreen + } + + private var isDynamicIslandMode: Bool { + shouldUseDynamicIslandMode(for: currentScreenName) + } + + private var terminalClipShape: UnevenRoundedRectangle { + let innerBottom = notchTerminalBottomCornerRadii( + isDynamicIslandMode: isDynamicIslandMode, + notchState: vm.notchState, + cornerRadiusScaling: cornerRadiusScaling, + enableMinimalisticUI: enableMinimalisticUI, + closedNotchHeight: vm.closedNotchSize.height + ).innerBottom + return UnevenRoundedRectangle( + topLeadingRadius: Self.terminalClipTopCornerRadius, + bottomLeadingRadius: innerBottom, + bottomTrailingRadius: innerBottom, + topTrailingRadius: Self.terminalClipTopCornerRadius, + style: .continuous + ) + } + var body: some View { VStack(spacing: 0) { if enableTerminalFeature { @@ -121,15 +151,20 @@ struct NotchTerminalView: View { .padding(.bottom, 4) Divider() - .padding(.horizontal, 8) + .padding(.horizontal, notchTerminalContentEdgePadding.horizontal) // Terminal content — the containerView is stable across // notch close/open cycles; updateNSView handles restart. TerminalNSViewRepresentable(terminalManager: terminalManager) - .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) - .padding(.horizontal, 8) - .padding(.bottom, 8) - .padding(.top, 4) + .clipShape(terminalClipShape) + .padding( + EdgeInsets( + top: notchTerminalContentEdgePadding.top, + leading: notchTerminalContentEdgePadding.horizontal, + bottom: notchTerminalContentEdgePadding.bottom, + trailing: notchTerminalContentEdgePadding.horizontal + ) + ) .onHover { hovering in updateSuppression(for: hovering) } @@ -154,6 +189,10 @@ struct NotchTerminalView: View { .onDisappear { updateSuppression(for: false) } + .onAppear { + terminalManager.refreshTerminalAppearanceIfNeeded() + terminalManager.focusTerminalIfPossible() + } } private func updateSuppression(for hovering: Bool) { diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index 38302ff..2a4464b 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -897,7 +897,7 @@ struct SettingsView: View { SettingsSearchEntry(tab: .terminal, title: "Enable terminal", keywords: ["terminal", "guake", "shell"], highlightID: SettingsTab.terminal.highlightID(for: "Enable terminal")), SettingsSearchEntry(tab: .terminal, title: "Shell path", keywords: ["shell", "zsh", "bash", "terminal"], highlightID: SettingsTab.terminal.highlightID(for: "Shell path")), SettingsSearchEntry(tab: .terminal, title: "Font size", keywords: ["terminal", "font", "text size"], highlightID: SettingsTab.terminal.highlightID(for: "Font size")), - SettingsSearchEntry(tab: .terminal, title: "Terminal opacity", keywords: ["terminal", "opacity", "transparency"], highlightID: SettingsTab.terminal.highlightID(for: "Terminal opacity")), + SettingsSearchEntry(tab: .terminal, title: "Terminal opacity", keywords: ["terminal", "opacity", "transparency", "blur", "background"], highlightID: SettingsTab.terminal.highlightID(for: "Terminal opacity")), SettingsSearchEntry(tab: .terminal, title: "Maximum height", keywords: ["terminal", "height", "size"], highlightID: SettingsTab.terminal.highlightID(for: "Maximum height")), SettingsSearchEntry(tab: .terminal, title: "Background color", keywords: ["terminal", "background", "color", "theme"], highlightID: SettingsTab.terminal.highlightID(for: "Background color")), SettingsSearchEntry(tab: .terminal, title: "Foreground color", keywords: ["terminal", "foreground", "text color", "theme"], highlightID: SettingsTab.terminal.highlightID(for: "Foreground color")), @@ -7875,6 +7875,10 @@ struct TerminalSettings: View { .settingsHighlight(id: highlightID("Maximum height")) } header: { Text("Appearance") + } footer: { + Text("Terminal opacity only affects the terminal backdrop; text stays fully opaque. Blur uses the system material behind the window.") + .font(.caption) + .foregroundStyle(.secondary) } // MARK: Colors diff --git a/DynamicIsland/managers/TerminalManager.swift b/DynamicIsland/managers/TerminalManager.swift index 4ec7224..3275f0e 100644 --- a/DynamicIsland/managers/TerminalManager.swift +++ b/DynamicIsland/managers/TerminalManager.swift @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +import AppKit import Foundation import SwiftUI import SwiftTerm @@ -59,6 +60,9 @@ final class StableTerminalContainerView: NSView { for child in subviews { child.needsDisplay = true } + Task { @MainActor in + TerminalManager.shared.refreshTerminalAppearanceIfNeeded() + } } } @@ -89,12 +93,24 @@ class TerminalManager: ObservableObject { let v = StableTerminalContainerView(frame: .zero) v.autoresizingMask = [.width, .height] v.wantsLayer = true + v.layer?.backgroundColor = NSColor.clear.cgColor return v }() /// The actual terminal view (child of `containerView`). private(set) var terminalView: LocalProcessTerminalView? + /// Frosted blur of desktop/content behind the terminal; always below `terminalView`. + private lazy var terminalBackgroundEffectView: NSVisualEffectView = { + let v = NSVisualEffectView() + // Minimal frosted look with very light blur/vibrancy. + v.material = .underWindowBackground + v.blendingMode = .behindWindow + v.state = .active + v.autoresizingMask = [.width, .height] + return v + }() + private init() {} // MARK: - Lifecycle @@ -105,9 +121,8 @@ class TerminalManager: ObservableObject { if let existing = terminalView, existing.superview === containerView { // Already mounted — just re-wire the delegate in case the coordinator changed. existing.processDelegate = delegate - // Force a redraw — the view may have been off-screen (notch closed) - // and needs to re-render the terminal buffer. - existing.needsDisplay = true + // Re-apply opacity/translucency after hide/show cycles (e.g. shortcut toggle). + refreshTerminalOpacityAndTranslucency(for: existing) return } @@ -119,6 +134,9 @@ class TerminalManager: ObservableObject { ? containerView.bounds : CGRect(x: 0, y: 0, width: 400, height: 300) + // Replace only the terminal view - keep the blur underlay between restarts. + terminalView?.removeFromSuperview() + let view = LocalProcessTerminalView(frame: initialFrame) view.autoresizingMask = [.width, .height] @@ -127,16 +145,27 @@ class TerminalManager: ObservableObject { view.processDelegate = delegate - // Mount inside the stable container - containerView.subviews.forEach { $0.removeFromSuperview() } + if terminalBackgroundEffectView.superview == nil { + containerView.addSubview(terminalBackgroundEffectView) + } containerView.addSubview(view) terminalView = view // If the container already has a valid size, snap the child to it. let containerSize = containerView.bounds.size if containerSize.width >= 10, containerSize.height >= 10 { + terminalBackgroundEffectView.frame = containerView.bounds view.frame = containerView.bounds } + + // Apply translucency immediately and again on the next run loop tick. + // The deferred pass handles AppKit/SwiftUI mount timing where layer + // changes can otherwise be lost on first open. + refreshTerminalOpacityAndTranslucency(for: view) + DispatchQueue.main.async { [weak self, weak view] in + guard let self, let view, self.terminalView === view else { return } + self.refreshTerminalOpacityAndTranslucency(for: view) + } } /// Starts the shell process if not already running. @@ -196,6 +225,46 @@ class TerminalManager: ObservableObject { return NSFont.monospacedSystemFont(ofSize: size, weight: .regular) } + /// Background color with opacity slider applied to alpha only (glyphs stay fully opaque). + private func resolvedTerminalBackgroundNSColor( + baseColor: NSColor? = nil, + opacitySlider: CGFloat? = nil + ) -> NSColor { + let raw = baseColor ?? NSColor(Defaults[.terminalBackgroundColor]) + let rgb = raw.usingColorSpace(.deviceRGB) ?? raw.usingColorSpace(.sRGB) ?? raw + let opacity = opacitySlider ?? CGFloat(Defaults[.terminalOpacity]) + let alpha = CGFloat(rgb.cgColor.alpha) * opacity + return rgb.withAlphaComponent(alpha) + } + + /// Composites the terminal buffer over `terminalBackgroundEffectView` without dimming glyphs. + /// SwiftTerm’s `TerminalView.isOpaque` is read-only; translucency relies on alpha in `nativeBackgroundColor`. + private func applyTerminalBackgroundAppearance(to view: LocalProcessTerminalView) { + view.layer?.opacity = 1 + view.nativeBackgroundColor = resolvedTerminalBackgroundNSColor() + } + + /// Upstream `TerminalView.setupOptions()` assigns `layer.backgroundColor` from `nativeBackgroundColor`, which + /// prevents the `NSVisualEffectView` under the terminal from showing through. Re-clear after our color updates. + /// Default-cell alpha and attribute-cache invalidation on native color changes may still need a SwiftTerm PR + private func applyTerminalLayerTranslucencyHacks(to view: LocalProcessTerminalView) { + view.layer?.backgroundColor = NSColor.clear.cgColor + view.layer?.isOpaque = false + } + + /// Best-effort redraw. `TerminalView.installColors(_:)` could clear SwiftTerm color caches but needs 16 + /// `SwiftTerm.Color` values; `Terminal.ansiColors` is not public, so we do not duplicate a palette here. + private func synchronizeTerminalTranslucencyPresentation(to view: LocalProcessTerminalView) { + applyTerminalLayerTranslucencyHacks(to: view) + view.setNeedsDisplay(view.bounds) + } + + /// Re-applies backdrop opacity + translucency hacks after remount/reopen paths. + private func refreshTerminalOpacityAndTranslucency(for view: LocalProcessTerminalView) { + applyTerminalBackgroundAppearance(to: view) + synchronizeTerminalTranslucencyPresentation(to: view) + } + // MARK: - Settings Application /// Applies all persisted settings to a terminal view at creation time. @@ -205,11 +274,8 @@ class TerminalManager: ObservableObject { let fontFamily = Defaults[.terminalFontFamily] view.font = resolveFont(family: fontFamily, size: fontSize) - // Opacity - view.layer?.opacity = Float(Defaults[.terminalOpacity]) - - // Colors - view.nativeBackgroundColor = NSColor(Defaults[.terminalBackgroundColor]) + // Colors (background opacity is separate from terminal opacity slider — see `applyTerminalBackgroundAppearance`) + applyTerminalBackgroundAppearance(to: view) view.nativeForegroundColor = NSColor(Defaults[.terminalForegroundColor]) view.caretColor = NSColor(Defaults[.terminalCursorColor]) @@ -219,6 +285,7 @@ class TerminalManager: ObservableObject { view.getTerminal().setCursorStyle(cursorStyle.swiftTermStyle) // Keep drawing the selected cursor style when TerminalView.hasFocus is false; SwiftTerm + // otherwise uses an unfocused/outline caret while tracksFocus is true. view.caretViewTracksFocus = false // Scrollback @@ -232,6 +299,8 @@ class TerminalManager: ObservableObject { // Rendering view.useBrightColors = Defaults[.terminalBoldAsBright] + + refreshTerminalOpacityAndTranslucency(for: view) } /// Updates font size on the live terminal view. @@ -248,10 +317,12 @@ class TerminalManager: ObservableObject { view.font = resolveFont(family: family, size: fontSize) } - /// Updates opacity on the live terminal view. + /// Updates background opacity on the live terminal view (glyphs stay fully opaque). func applyOpacity(_ opacity: Double) { guard let view = terminalView else { return } - view.layer?.opacity = Float(opacity) + view.layer?.opacity = 1 + view.nativeBackgroundColor = resolvedTerminalBackgroundNSColor(opacitySlider: CGFloat(opacity)) + refreshTerminalOpacityAndTranslucency(for: view) } /// Updates cursor style on the live terminal view. @@ -289,13 +360,68 @@ class TerminalManager: ObservableObject { /// Updates background color on the live terminal view. func applyBackgroundColor(_ color: SwiftUI.Color) { guard let view = terminalView else { return } - view.nativeBackgroundColor = NSColor(color) + view.layer?.opacity = 1 + view.nativeBackgroundColor = resolvedTerminalBackgroundNSColor(baseColor: NSColor(color)) + refreshTerminalOpacityAndTranslucency(for: view) } /// Updates foreground color on the live terminal view. func applyForegroundColor(_ color: SwiftUI.Color) { guard let view = terminalView else { return } view.nativeForegroundColor = NSColor(color) + synchronizeTerminalTranslucencyPresentation(to: view) + } + + /// Called by UI lifecycle hooks after tab re-appearance to keep opacity stable. + func refreshTerminalAppearanceIfNeeded() { + guard let view = terminalView else { return } + refreshTerminalOpacityAndTranslucency(for: view) + } + + /// Makes terminal view first responder when terminal tab opens. + /// Retries a few times to cover mount/animation timing so typing works immediately. + func focusTerminalIfPossible() { + focusTerminalIfPossible(attemptsRemaining: 3) + } + + private func focusTerminalIfPossible(attemptsRemaining: Int) { + guard let view = terminalView else { return } + guard let window = containerView.window ?? view.window else { + guard attemptsRemaining > 0 else { return } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in + self?.focusTerminalIfPossible(attemptsRemaining: attemptsRemaining - 1) + } + return + } + + if !NSApp.isActive { + NSApp.activate(ignoringOtherApps: true) + } + if !window.isKeyWindow { + window.makeKeyAndOrderFront(nil) + } + + guard window.firstResponder !== view else { return } + if !window.makeFirstResponder(view), attemptsRemaining > 0 { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in + self?.focusTerminalIfPossible(attemptsRemaining: attemptsRemaining - 1) + } + } + } + + /// Resigns terminal focus before closing notch to avoid stale responder state. + func resignTerminalFirstResponderIfNeeded() { + guard let view = terminalView else { return } + guard let window = containerView.window ?? view.window else { return } + guard window.firstResponder === view else { return } + _ = window.makeFirstResponder(nil) + } + + /// True when the terminal view is currently first responder. + func isTerminalFirstResponder() -> Bool { + guard let view = terminalView else { return false } + guard let window = containerView.window ?? view.window else { return false } + return window.firstResponder === view } /// Updates cursor color on the live terminal view. diff --git a/DynamicIsland/sizing/matters.swift b/DynamicIsland/sizing/matters.swift index b9418b2..5017138 100644 --- a/DynamicIsland/sizing/matters.swift +++ b/DynamicIsland/sizing/matters.swift @@ -154,6 +154,46 @@ var minimalisticOpenNotchSize: CGSize { let cornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 19, bottom: 24), closed: (top: 6, bottom: 14)) let minimalisticCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 35, bottom: 35), closed: cornerRadiusInsets.closed) +// MARK: - Terminal tab clip (notch surface) + +/// Padding on the terminal block inside the notch. Inner corner radius = outer shell radius on that edge, minus the matching edge padding. +let notchTerminalContentEdgePadding: (top: CGFloat, horizontal: CGFloat, bottom: CGFloat) = (4, 8, 8) + +/// Bottom radii for the shell (outer) and the terminal ``clipShape`` (inner), per design: inner = outer shell bottom radius − `notchTerminalContentEdgePadding.bottom`. +func notchTerminalBottomCornerRadii( + isDynamicIslandMode: Bool, + notchState: NotchState, + cornerRadiusScaling: Bool, + enableMinimalisticUI: Bool, + closedNotchHeight: CGFloat +) -> (outerBottom: CGFloat, innerBottom: CGFloat) { + let p = notchTerminalContentEdgePadding.bottom + if isDynamicIslandMode { + let outer: CGFloat + if notchState == .open { + outer = enableMinimalisticUI + ? minimalisticCornerRadiusInsets.opened.top + : dynamicIslandPillCornerRadiusInsets.opened + } else { + outer = max(closedNotchHeight / 2, dynamicIslandPillCornerRadiusInsets.closed.standard) + } + return (outer, max(0, outer - p)) + } + let active: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = { + if enableMinimalisticUI { + return (opened: minimalisticCornerRadiusInsets.opened, closed: cornerRadiusInsets.closed) + } + return cornerRadiusInsets + }() + let outerBottom: CGFloat + if notchState == .open && cornerRadiusScaling { + outerBottom = active.opened.bottom + } else { + outerBottom = active.closed.bottom + } + return (outerBottom, max(0, outerBottom - p)) +} + func statsAdjustedNotchSize( from baseSize: CGSize, isStatsTabActive: Bool,