diff --git a/CHANGELOG.md b/CHANGELOG.md index 809ad75..ef810ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ All notable changes to Pullo are listed here, newest first. The format follows [ ### Changed - Pullo no longer wakes up on every mouse move: it only follows the pointer while it is over the notch, which cuts its CPU use while you move the mouse elsewhere by about 95%. +### Fixed +- Opening the panel with the glass style no longer briefly takes about 300 MB of memory. The glass background stays the same size while the panel animates, so it peaks under 30 MB. + ## [0.11.3] - 2026-10-25 ### Fixed diff --git a/Sources/Pullo/Features/Notch/NotchBackground.swift b/Sources/Pullo/Features/Notch/NotchBackground.swift index 2bbc583..cc730f7 100644 --- a/Sources/Pullo/Features/Notch/NotchBackground.swift +++ b/Sources/Pullo/Features/Notch/NotchBackground.swift @@ -10,13 +10,14 @@ struct NotchBackground: View { let height: CGFloat var style: PanelStyle? var scale: CGFloat = 1 + var glassSize: CGSize? private var currentStyle: PanelStyle { style ?? settings.panelStyle } var body: some View { if usesGlass, #available(macOS 26, *) { ZStack { - Color.clear.glassEffect(.regular, in: shape) + glass if currentStyle == .glassDarkTop { darkTop } @@ -27,6 +28,20 @@ struct NotchBackground: View { } } + @available(macOS 26, *) + @ViewBuilder + private var glass: some View { + if let glassSize { + Color.clear + .glassEffect(.regular, in: Rectangle()) + .frame(width: glassSize.width, height: glassSize.height) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .transaction { $0.animation = nil } + } else { + Color.clear.glassEffect(.regular, in: shape) + } + } + private var darkTop: some View { RadialGradient( stops: [ diff --git a/Sources/Pullo/Features/Notch/NotchRootView.swift b/Sources/Pullo/Features/Notch/NotchRootView.swift index 12d6f36..c2f00d5 100644 --- a/Sources/Pullo/Features/Notch/NotchRootView.swift +++ b/Sources/Pullo/Features/Notch/NotchRootView.swift @@ -5,6 +5,7 @@ struct NotchRootView: View { @Environment(AccountStore.self) private var accounts @Environment(AppSettings.self) private var settings @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @State private var lastShown: NotchState = .open var body: some View { let state = notch.state @@ -23,6 +24,22 @@ struct NotchRootView: View { .hintLayer(within: CGSize(width: size.width + radii.top * 2, height: size.height)) .animation(Motion.peek, value: notch.liveCI) .environment(\.panelGlass, settings.panelStyle.usesGlass(reduceTransparency: reduceTransparency)) + .onChange(of: state) { _, new in + if new != .closed { lastShown = new } + } + } + + private func background(state: NotchState, shape: NotchShape, height: CGFloat) -> some View { + let glassState = state == .closed ? lastShown : state + let glassSize = notch.layout.size(for: glassState) + let glassRadii = notch.layout.radii(for: glassState) + return NotchBackground( + state: state, + shape: shape, + notchWidth: notch.geometry.notchSize.width, + height: height, + glassSize: CGSize(width: glassSize.width + glassRadii.top * 2, height: glassSize.height) + ) } private func panel(state: NotchState, size: CGSize, radii: (top: CGFloat, bottom: CGFloat), shape: NotchShape) -> some View { @@ -40,7 +57,7 @@ struct NotchRootView: View { } .frame(width: size.width, height: size.height, alignment: .top) .padding(.horizontal, radii.top) - .background { NotchBackground(state: state, shape: shape, notchWidth: notch.geometry.notchSize.width, height: size.height) } + .background { background(state: state, shape: shape, height: size.height) } .clipShape(shape) .compositingGroup() .shadow(color: .black.opacity(state == .closed ? 0 : 0.55), radius: state == .open ? 26 : 14, y: 8)