Bump version to 1.27.0 (#35)
Fix macOS 14 (Sonoma) compatibility issues: - Muted traffic lights and toolbar items caused by clipsToBounds default change - Toolbar buttons disappearing after sidebar toggle (Cmd+B) - Update check pill not appearing in titlebar
This commit is contained in:
parent
0dd3252980
commit
eb19e8fa25
5 changed files with 127 additions and 8 deletions
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
All notable changes to cmux are documented here.
|
||||
|
||||
## [1.27.0] - 2026-02-11
|
||||
|
||||
### Fixed
|
||||
- Muted traffic lights and toolbar items on macOS 14 (Sonoma) caused by `clipsToBounds` default change
|
||||
- Toolbar buttons (sidebar, notifications, new tab) disappearing after toggling sidebar with Cmd+B
|
||||
- Update check pill not appearing in titlebar on macOS 14 (Sonoma)
|
||||
|
||||
## [1.26.0] - 2026-02-11
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -538,7 +538,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 38;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
|
@ -554,7 +554,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.26.0;
|
||||
MARKETING_VERSION = 1.27.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-lc++",
|
||||
"-framework",
|
||||
|
|
@ -583,7 +583,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 38;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
|
@ -599,7 +599,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.26.0;
|
||||
MARKETING_VERSION = 1.27.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-lc++",
|
||||
"-framework",
|
||||
|
|
@ -652,10 +652,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 38;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.26.0;
|
||||
MARKETING_VERSION = 1.27.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -669,10 +669,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 38;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.26.0;
|
||||
MARKETING_VERSION = 1.27.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@ enum WindowGlassEffect {
|
|||
blurView.autoresizingMask = [.width, .height]
|
||||
blurView.layer?.zPosition = -1000
|
||||
|
||||
// macOS 14 (Sonoma) changed clipsToBounds default from YES to NO.
|
||||
// Explicitly restore clipping to prevent blur/tint from rendering
|
||||
// beyond contentView bounds and interfering with titlebar compositing.
|
||||
if #available(macOS 14, *) {
|
||||
blurView.clipsToBounds = true
|
||||
contentView.clipsToBounds = true
|
||||
}
|
||||
|
||||
contentView.addSubview(blurView, positioned: .below, relativeTo: contentView.subviews.first)
|
||||
|
||||
// Tint overlay on top of blur, still behind content
|
||||
|
|
@ -77,6 +85,9 @@ enum WindowGlassEffect {
|
|||
tintOverlay.wantsLayer = true
|
||||
tintOverlay.layer?.backgroundColor = color.cgColor
|
||||
tintOverlay.layer?.zPosition = -999
|
||||
if #available(macOS 14, *) {
|
||||
tintOverlay.clipsToBounds = true
|
||||
}
|
||||
contentView.addSubview(tintOverlay, positioned: .above, relativeTo: blurView)
|
||||
objc_setAssociatedObject(window, &tintOverlayKey, tintOverlay, .OBJC_ASSOCIATION_RETAIN)
|
||||
}
|
||||
|
|
@ -114,10 +125,12 @@ enum WindowGlassEffect {
|
|||
}
|
||||
|
||||
final class SidebarState: ObservableObject {
|
||||
static let didToggleNotification = Notification.Name("SidebarStateDidToggle")
|
||||
@Published var isVisible: Bool = true
|
||||
|
||||
func toggle() {
|
||||
isVisible.toggle()
|
||||
NotificationCenter.default.post(name: Self.didToggleNotification, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ final class DevBuildAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
hostingView.autoresizingMask = [.width, .height]
|
||||
containerView.addSubview(hostingView)
|
||||
|
||||
if #available(macOS 14, *) {
|
||||
containerView.clipsToBounds = true
|
||||
hostingView.clipsToBounds = true
|
||||
}
|
||||
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
|
|
@ -42,11 +47,17 @@ final class DevBuildAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
view.isHidden = false
|
||||
containerView.isHidden = false
|
||||
hostingView.isHidden = false
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
override func viewDidLayout() {
|
||||
super.viewDidLayout()
|
||||
view.isHidden = false
|
||||
containerView.isHidden = false
|
||||
hostingView.isHidden = false
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +74,7 @@ final class DevBuildAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
hostingView.invalidateIntrinsicContentSize()
|
||||
hostingView.layoutSubtreeIfNeeded()
|
||||
let labelSize = hostingView.fittingSize
|
||||
guard labelSize.width > 1 && labelSize.height > 1 else { return }
|
||||
let titlebarHeight = view.window.map { window in
|
||||
window.frame.height - window.contentLayoutRect.height
|
||||
} ?? labelSize.height
|
||||
|
|
@ -368,6 +380,13 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
|
|||
hostingView.autoresizingMask = [.width, .height]
|
||||
containerView.addSubview(hostingView)
|
||||
|
||||
// macOS 14 (Sonoma) changed clipsToBounds default to NO, which can cause
|
||||
// titlebar accessory views to render incorrectly or disappear during layout.
|
||||
if #available(macOS 14, *) {
|
||||
containerView.clipsToBounds = true
|
||||
hostingView.clipsToBounds = true
|
||||
}
|
||||
|
||||
userDefaultsObserver = NotificationCenter.default.addObserver(
|
||||
forName: UserDefaults.didChangeNotification,
|
||||
object: nil,
|
||||
|
|
@ -391,14 +410,24 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
|
|||
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
ensureVisible()
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
override func viewDidLayout() {
|
||||
super.viewDidLayout()
|
||||
ensureVisible()
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
/// Sonoma can hide titlebar accessory views during layout transitions.
|
||||
/// Force visibility on every layout pass.
|
||||
private func ensureVisible() {
|
||||
view.isHidden = false
|
||||
containerView.isHidden = false
|
||||
hostingView.isHidden = false
|
||||
}
|
||||
|
||||
private func scheduleSizeUpdate() {
|
||||
guard !pendingSizeUpdate else { return }
|
||||
pendingSizeUpdate = true
|
||||
|
|
@ -412,6 +441,8 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
|
|||
hostingView.invalidateIntrinsicContentSize()
|
||||
hostingView.layoutSubtreeIfNeeded()
|
||||
let contentSize = hostingView.fittingSize
|
||||
// Guard against zero-size frames during layout transitions (Sonoma)
|
||||
guard contentSize.width > 1 && contentSize.height > 1 else { return }
|
||||
let titlebarHeight = view.window.map { window in
|
||||
window.frame.height - window.contentLayoutRect.height
|
||||
} ?? contentSize.height
|
||||
|
|
@ -673,6 +704,11 @@ final class UpdateAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
hostingView.autoresizingMask = [.width, .height]
|
||||
containerView.addSubview(hostingView)
|
||||
|
||||
if #available(macOS 14, *) {
|
||||
containerView.clipsToBounds = true
|
||||
hostingView.clipsToBounds = true
|
||||
}
|
||||
|
||||
stateCancellable = model.$state
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
|
|
@ -688,14 +724,22 @@ final class UpdateAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
ensureVisible()
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
override func viewDidLayout() {
|
||||
super.viewDidLayout()
|
||||
ensureVisible()
|
||||
scheduleSizeUpdate()
|
||||
}
|
||||
|
||||
private func ensureVisible() {
|
||||
view.isHidden = false
|
||||
containerView.isHidden = false
|
||||
hostingView.isHidden = false
|
||||
}
|
||||
|
||||
private func scheduleSizeUpdate() {
|
||||
guard !pendingSizeUpdate else { return }
|
||||
pendingSizeUpdate = true
|
||||
|
|
@ -709,6 +753,7 @@ final class UpdateAccessoryViewController: NSTitlebarAccessoryViewController {
|
|||
hostingView.invalidateIntrinsicContentSize()
|
||||
hostingView.layoutSubtreeIfNeeded()
|
||||
let pillSize = hostingView.fittingSize
|
||||
guard pillSize.width > 1 && pillSize.height > 1 else { return }
|
||||
let titlebarHeight = view.window.map { window in
|
||||
window.frame.height - window.contentLayoutRect.height
|
||||
} ?? pillSize.height
|
||||
|
|
@ -750,6 +795,7 @@ final class UpdateTitlebarAccessoryController {
|
|||
attachToExistingWindows()
|
||||
installObservers()
|
||||
installStateObserver()
|
||||
installSidebarToggleObserver()
|
||||
}
|
||||
|
||||
func attach(to window: NSWindow) {
|
||||
|
|
@ -830,6 +876,52 @@ final class UpdateTitlebarAccessoryController {
|
|||
window.identifier?.rawValue == "cmux.main"
|
||||
}
|
||||
|
||||
/// After sidebar toggle on Sonoma, titlebar accessories can disappear. Re-add if needed.
|
||||
private func installSidebarToggleObserver() {
|
||||
let center = NotificationCenter.default
|
||||
observers.append(center.addObserver(
|
||||
forName: SidebarState.didToggleNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
// Delay slightly to let SwiftUI layout settle before revalidating
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self?.revalidateAllAccessories()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func revalidateAllAccessories() {
|
||||
guard let updateViewModel else { return }
|
||||
for window in attachedWindows.allObjects {
|
||||
// Re-add controls if they were removed during layout
|
||||
if !window.titlebarAccessoryViewControllers.contains(where: { $0.view.identifier == controlsIdentifier }) {
|
||||
let controls = TitlebarControlsAccessoryViewController(
|
||||
notificationStore: TerminalNotificationStore.shared
|
||||
)
|
||||
controls.layoutAttribute = .left
|
||||
controls.view.identifier = controlsIdentifier
|
||||
window.addTitlebarAccessoryViewController(controls)
|
||||
controlsControllers.add(controls)
|
||||
}
|
||||
|
||||
// Re-add update accessory if it was removed and state is not idle
|
||||
let isIdle = (updateViewModel.overrideState ?? updateViewModel.state).isIdle
|
||||
if !isIdle && !window.titlebarAccessoryViewControllers.contains(where: { $0.view.identifier == updateIdentifier }) {
|
||||
let accessory = UpdateAccessoryViewController(model: updateViewModel)
|
||||
accessory.layoutAttribute = .right
|
||||
accessory.view.identifier = updateIdentifier
|
||||
window.addTitlebarAccessoryViewController(accessory)
|
||||
}
|
||||
|
||||
// Ensure all accessories are visible and properly sized
|
||||
for controller in window.titlebarAccessoryViewControllers {
|
||||
controller.view.isHidden = false
|
||||
controller.view.needsLayout = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func installStateObserver() {
|
||||
guard let updateViewModel else { return }
|
||||
stateCancellable = Publishers.CombineLatest(updateViewModel.$state, updateViewModel.$overrideState)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ description: Release notes and version history for cmux
|
|||
|
||||
All notable changes to cmux are documented here.
|
||||
|
||||
## [1.27.0] - 2026-02-11
|
||||
|
||||
### Fixed
|
||||
- Muted traffic lights and toolbar items on macOS 14 (Sonoma) caused by `clipsToBounds` default change
|
||||
- Toolbar buttons (sidebar, notifications, new tab) disappearing after toggling sidebar with Cmd+B
|
||||
- Update check pill not appearing in titlebar on macOS 14 (Sonoma)
|
||||
|
||||
## [1.26.0] - 2026-02-11
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue