diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 51c4f6ddd..54cf9a02a 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -131,8 +131,8 @@ fileprivate struct UpdateOverlay: View { HStack { Spacer() UpdatePill(model: appDelegate.updateViewModel) - .padding(.bottom, 12) - .padding(.trailing, 12) + .padding(.bottom, 9) + .padding(.trailing, 9) } } } diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift index 8ffdc3e35..661c89121 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift @@ -547,9 +547,10 @@ extension TerminalWindow { @ObservedObject var model: UpdateViewModel var body: some View { + // We use the same top/trailing padding so that it hugs the same. UpdatePill(model: model) .padding(.top, viewModel.accessoryTopPadding) - .padding(.trailing, 10) + .padding(.trailing, viewModel.accessoryTopPadding) } } diff --git a/macos/Sources/Features/Update/UpdatePill.swift b/macos/Sources/Features/Update/UpdatePill.swift index 1dc29e250..b975e81c9 100644 --- a/macos/Sources/Features/Update/UpdatePill.swift +++ b/macos/Sources/Features/Update/UpdatePill.swift @@ -8,6 +8,9 @@ struct UpdatePill: View { /// Whether the update popover is currently visible @State private var showPopover = false + /// The font used for the pill text + private let textFont = NSFont.systemFont(ofSize: 11, weight: .medium) + var body: some View { if !model.state.isIdle { pillButton @@ -43,8 +46,9 @@ struct UpdatePill: View { .frame(width: 14, height: 14) Text(model.text) - .font(.system(size: 11, weight: .medium)) + .font(Font(textFont)) .lineLimit(1) + .frame(width: textWidth) } .padding(.horizontal, 8) .padding(.vertical, 4) @@ -58,4 +62,11 @@ struct UpdatePill: View { .buttonStyle(.plain) .help(model.text) } + + /// Calculated width for the text to prevent resizing during progress updates + private var textWidth: CGFloat? { + let attributes: [NSAttributedString.Key: Any] = [.font: textFont] + let size = (model.maxWidthText as NSString).size(withAttributes: attributes) + return size.width + } } diff --git a/macos/Sources/Features/Update/UpdateViewModel.swift b/macos/Sources/Features/Update/UpdateViewModel.swift index 0678997d7..6341b3b42 100644 --- a/macos/Sources/Features/Update/UpdateViewModel.swift +++ b/macos/Sources/Features/Update/UpdateViewModel.swift @@ -37,6 +37,19 @@ class UpdateViewModel: ObservableObject { } } + /// The maximum width text for states that show progress. + /// Used to prevent the pill from resizing as percentages change. + var maxWidthText: String { + switch state { + case .downloading: + return "Downloading: 100%" + case .extracting: + return "Preparing: 100%" + default: + return text + } + } + /// The SF Symbol icon name for the current update state. /// Returns nil for idle, downloading, and extracting states. var iconName: String? {