diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ca8eca..1e7f40d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to cmuxterm are documented here. +## [1.19.4] - 2026-02-09 + +### Fixed +- Update pill never appearing due to constraint/sizing feedback loop + ## [1.19.3] - 2026-02-09 ### Fixed diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index e0116a27..af21702e 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -538,7 +538,7 @@ CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; GENERATE_INFOPLIST_FILE = YES; @@ -554,7 +554,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.19.3; + MARKETING_VERSION = 1.19.4; OTHER_LDFLAGS = ( "-lc++", "-framework", @@ -583,7 +583,7 @@ CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; GENERATE_INFOPLIST_FILE = YES; @@ -599,7 +599,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.19.3; + MARKETING_VERSION = 1.19.4; OTHER_LDFLAGS = ( "-lc++", "-framework", @@ -652,10 +652,10 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 29; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.19.3; + MARKETING_VERSION = 1.19.4; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.appuitests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -669,10 +669,10 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 29; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.19.3; + MARKETING_VERSION = 1.19.4; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.appuitests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Sources/Update/UpdatePill.swift b/Sources/Update/UpdatePill.swift index ace5eb9b..a3ef524a 100644 --- a/Sources/Update/UpdatePill.swift +++ b/Sources/Update/UpdatePill.swift @@ -21,8 +21,6 @@ struct UpdatePill: View { UpdatePopoverView(model: model) } .opacity(visible ? 1 : 0) - .frame(width: visible ? nil : 0, height: visible ? nil : 0) - .clipped() } @ViewBuilder diff --git a/Sources/WindowToolbarController.swift b/Sources/WindowToolbarController.swift index c340ce88..dd7e5cc3 100644 --- a/Sources/WindowToolbarController.swift +++ b/Sources/WindowToolbarController.swift @@ -128,16 +128,17 @@ final class WindowToolbarController: NSObject, NSToolbarDelegate { let view = NonDraggableHostingView(rootView: UpdatePill(model: updateViewModel)) let key = ObjectIdentifier(toolbar) item.view = view - sizeToolbarItem(for: key, hostingView: view) + let visible = !updateViewModel.effectiveState.isIdle + sizeToolbarItem(for: key, hostingView: view, visible: visible) updateSizeCancellables[key]?.cancel() updateSizeCancellables[key] = updateViewModel.$state .receive(on: DispatchQueue.main) - .sink { [weak self, weak view] _ in + .sink { [weak self, weak view] newState in // @Published fires on willSet, so SwiftUI hasn't processed the // new state yet. Defer measurement to the next run loop cycle. DispatchQueue.main.async { [weak self, weak view] in guard let self, let view else { return } - self.sizeToolbarItem(for: key, hostingView: view) + self.sizeToolbarItem(for: key, hostingView: view, visible: !newState.isIdle) } } return item @@ -146,10 +147,19 @@ final class WindowToolbarController: NSObject, NSToolbarDelegate { return nil } - private func sizeToolbarItem(for key: ObjectIdentifier, hostingView: NSView) { + private func sizeToolbarItem(for key: ObjectIdentifier, hostingView: NSView, visible: Bool) { + // Deactivate existing constraints before measuring so they don't + // clamp fittingSize to zero (chicken-and-egg sizing problem). + if let constraints = updateViewConstraints[key] { + constraints.width.isActive = false + constraints.height.isActive = false + } + hostingView.invalidateIntrinsicContentSize() hostingView.layoutSubtreeIfNeeded() - let size = hostingView.fittingSize + let naturalSize = hostingView.fittingSize + let size = visible ? naturalSize : .zero + hostingView.setFrameSize(size) hostingView.setContentHuggingPriority(.required, for: .horizontal) hostingView.setContentHuggingPriority(.required, for: .vertical) @@ -157,6 +167,8 @@ final class WindowToolbarController: NSObject, NSToolbarDelegate { if let constraints = updateViewConstraints[key] { constraints.width.constant = size.width constraints.height.constant = size.height + constraints.width.isActive = true + constraints.height.isActive = true } else { let width = hostingView.widthAnchor.constraint(equalToConstant: size.width) let height = hostingView.heightAnchor.constraint(equalToConstant: size.height) diff --git a/docs-site/content/docs/changelog.mdx b/docs-site/content/docs/changelog.mdx index 3e575faa..c74f5ade 100644 --- a/docs-site/content/docs/changelog.mdx +++ b/docs-site/content/docs/changelog.mdx @@ -5,6 +5,11 @@ description: Release notes and version history for cmuxterm All notable changes to cmuxterm are documented here. +## [1.19.4] - 2026-02-09 + +### Fixed +- Update pill never appearing due to constraint/sizing feedback loop + ## [1.19.3] - 2026-02-09 ### Fixed