From 7f5cadc02f318e52737949ddfdd3c4d6801de2f1 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 27 Sep 2026 07:46:54 -0400 Subject: [PATCH 1/4] Make pane indicator strokes and the file path header match shared chrome metrics The active pane border, notification ring, focus flash and Canvas focus border now share one 2pt stroke (PaneIndicatorMetrics.strokeWidth in CmuxFoundation). The attention ring was 2.5pt and sat a quarter point outside the 2pt active border it overlays. The file and Markdown preview path header now uses the right sidebar path header metrics: 28pt bar (grows with font magnification), 14pt file icon, and 10pt glyphs in 20pt header buttons, instead of 30pt, 16pt and 13pt. Co-Authored-By: Claude Opus 5.5 --- .../Sources/CmuxCanvasUI/CanvasPaneView.swift | 3 ++- .../CmuxFoundation/PaneIndicatorMetrics.swift | 8 +++++++ Sources/PaneChromeSettings.swift | 1 - Sources/Panels/Panel.swift | 3 ++- Sources/Panels/PanelContentView.swift | 24 ++++++++++++++----- Sources/TmuxWorkspacePaneOverlayView.swift | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift diff --git a/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift b/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift index 24291da3907b..8c876d901407 100644 --- a/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift +++ b/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift @@ -1,6 +1,7 @@ import AppKit import SwiftUI import CmuxCanvas +import CmuxFoundation /// Delegate through which a pane view reports gestures to the canvas root. @MainActor @@ -185,7 +186,7 @@ final class CanvasPaneView: NSView { layer?.borderColor = chrome.isFocused ? NSColor.controlAccentColor.cgColor : NSColor.separatorColor.cgColor - layer?.borderWidth = chrome.isFocused ? 2 : 1 + layer?.borderWidth = chrome.isFocused ? PaneIndicatorMetrics.strokeWidth : 1 layer?.backgroundColor = paneBackground.cgColor } diff --git a/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift b/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift new file mode 100644 index 000000000000..49b803c38fdd --- /dev/null +++ b/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift @@ -0,0 +1,8 @@ +public import CoreGraphics + +/// Stroke width shared by the pane focus and attention indicators: the active +/// pane border, the notification ring, the focus flash and the Canvas focus +/// border. They draw along the same edge, so one width keeps them aligned. +public enum PaneIndicatorMetrics { + public static let strokeWidth: CGFloat = 2 +} diff --git a/Sources/PaneChromeSettings.swift b/Sources/PaneChromeSettings.swift index 1ce1c3aed3a8..fd15c05fe0c7 100644 --- a/Sources/PaneChromeSettings.swift +++ b/Sources/PaneChromeSettings.swift @@ -4,7 +4,6 @@ enum PaneChromeSettings { static let paneBorderColorKey = "paneBorderColor" static let activePaneBorderColorKey = "activePaneBorderColor" static let defaultColorHex = "" - static let activeBorderLineWidth = 2.0 static let didChangeNotification = Notification.Name("cmux.paneChromeSettingsDidChange") static func paneBorderColorHex(defaults: UserDefaults = .standard) -> String? { diff --git a/Sources/Panels/Panel.swift b/Sources/Panels/Panel.swift index c6c036f3ac4c..95dc4438f519 100644 --- a/Sources/Panels/Panel.swift +++ b/Sources/Panels/Panel.swift @@ -1,6 +1,7 @@ import Foundation import Combine import AppKit +import CmuxFoundation /// Type of panel content public enum PanelType: String, Codable, CaseIterable, Sendable { @@ -210,7 +211,7 @@ enum FocusFlashCurve: Equatable { enum PanelOverlayRingMetrics { static let inset: CGFloat = 2 static let cornerRadius: CGFloat = 6 - static let lineWidth: CGFloat = 2.5 + static let lineWidth: CGFloat = PaneIndicatorMetrics.strokeWidth static func pathRect(in bounds: CGRect) -> CGRect { bounds.insetBy(dx: inset, dy: inset) diff --git a/Sources/Panels/PanelContentView.swift b/Sources/Panels/PanelContentView.swift index b5fa4ce6f2d7..09924a45c550 100644 --- a/Sources/Panels/PanelContentView.swift +++ b/Sources/Panels/PanelContentView.swift @@ -267,8 +267,12 @@ struct PanelFilePathHeader: View { var body: some View { HStack(spacing: 8) { - CmuxSystemSymbolImage(systemName: iconSystemName, pointSize: 16, tint: .secondary) - .frame(width: 16) + CmuxSystemSymbolImage( + systemName: iconSystemName, + pointSize: RightSidebarChromeMetrics.contentIconFrameSize, + tint: .secondary + ) + .frame(width: RightSidebarChromeMetrics.contentIconFrameSize) Text(filePath) .cmuxFont(size: 11, design: .monospaced) .foregroundStyle(Color(nsColor: foregroundColor).opacity(0.68)) @@ -278,8 +282,8 @@ struct PanelFilePathHeader: View { Spacer(minLength: 8) trailingContent() } - .padding(.horizontal, 12) - .frame(height: 30) + .padding(.horizontal, RightSidebarChromeMetrics.contentIconLeadingPadding) + .frame(height: RightSidebarChromeMetrics.secondaryBarHeight) .background(Color.clear) } } @@ -306,8 +310,16 @@ struct PanelHeaderIconGlyph: View { let systemName: String var body: some View { - CmuxSystemSymbolImage(systemName: systemName, pointSize: 13, tint: .secondary) - .frame(width: 20, height: 20, alignment: .center) + CmuxSystemSymbolImage( + systemName: systemName, + pointSize: RightSidebarChromeMetrics.headerIconSize, + tint: .secondary + ) + .frame( + width: RightSidebarChromeMetrics.headerControlSize, + height: RightSidebarChromeMetrics.headerControlSize, + alignment: .center + ) .contentShape(Rectangle()) } } diff --git a/Sources/TmuxWorkspacePaneOverlayView.swift b/Sources/TmuxWorkspacePaneOverlayView.swift index a04d87654110..e97d3d56eb2c 100644 --- a/Sources/TmuxWorkspacePaneOverlayView.swift +++ b/Sources/TmuxWorkspacePaneOverlayView.swift @@ -89,7 +89,7 @@ struct TmuxWorkspacePaneOverlayView: View { path, with: .color(Color(nsColor: color)), style: StrokeStyle( - lineWidth: CGFloat(PaneChromeSettings.activeBorderLineWidth), + lineWidth: PanelOverlayRingMetrics.lineWidth, lineJoin: .round ) ) From 84a0568d9c9cef63349b2475cc86b732995176c6 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 27 Sep 2026 07:47:20 -0400 Subject: [PATCH 2/4] Add changelog entry for pane chrome metrics Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 450ebb29eb0c..e7f02e46d25c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to cmux are documented here. - Global search finds text in the scrollback of open terminals, and Return on a terminal hit opens that pane's find bar on the match ([#11665](https://github.com/manaflow-ai/cmux/pull/11665)) -- thanks @smoreg! ### Changed +- The pane notification ring and focus flash draw at the same 2pt width as the active pane border, and the file and Markdown preview path header matches the 28pt bars around it with smaller icons ([#14982](https://github.com/manaflow-ai/cmux/pull/14982)) - The Settings > Mobile pairing row says Open Pairing and no longer promises a Tailscale QR code, which pairing does not use ([#14817](https://github.com/manaflow-ai/cmux/pull/14817)) -- thanks @aliyansajid! ### Fixed From f36fa67dd00ef2db6c6c1d8548621a4d2579e5f1 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 27 Sep 2026 08:06:30 -0400 Subject: [PATCH 3/4] Use a CGFloat extension for the pane indicator stroke width The package conventions lint rejects all-static namespace enums. Co-Authored-By: Claude Opus 5.5 --- .../Sources/CmuxCanvasUI/CanvasPaneView.swift | 2 +- .../Sources/CmuxFoundation/CGFloat+PaneIndicator.swift | 8 ++++++++ .../Sources/CmuxFoundation/PaneIndicatorMetrics.swift | 8 -------- Sources/Panels/Panel.swift | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/CGFloat+PaneIndicator.swift delete mode 100644 Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift diff --git a/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift b/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift index 8c876d901407..f91229c7a2b7 100644 --- a/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift +++ b/Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasPaneView.swift @@ -186,7 +186,7 @@ final class CanvasPaneView: NSView { layer?.borderColor = chrome.isFocused ? NSColor.controlAccentColor.cgColor : NSColor.separatorColor.cgColor - layer?.borderWidth = chrome.isFocused ? PaneIndicatorMetrics.strokeWidth : 1 + layer?.borderWidth = chrome.isFocused ? CGFloat.paneIndicatorStrokeWidth : 1 layer?.backgroundColor = paneBackground.cgColor } diff --git a/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/CGFloat+PaneIndicator.swift b/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/CGFloat+PaneIndicator.swift new file mode 100644 index 000000000000..4b52fc27fe00 --- /dev/null +++ b/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/CGFloat+PaneIndicator.swift @@ -0,0 +1,8 @@ +public import CoreGraphics + +extension CGFloat { + /// Stroke width shared by the pane focus and attention indicators: the active + /// pane border, the notification ring, the focus flash and the Canvas focus + /// border. They draw along the same edge, so one width keeps them aligned. + public static let paneIndicatorStrokeWidth: CGFloat = 2 +} diff --git a/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift b/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift deleted file mode 100644 index 49b803c38fdd..000000000000 --- a/Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/PaneIndicatorMetrics.swift +++ /dev/null @@ -1,8 +0,0 @@ -public import CoreGraphics - -/// Stroke width shared by the pane focus and attention indicators: the active -/// pane border, the notification ring, the focus flash and the Canvas focus -/// border. They draw along the same edge, so one width keeps them aligned. -public enum PaneIndicatorMetrics { - public static let strokeWidth: CGFloat = 2 -} diff --git a/Sources/Panels/Panel.swift b/Sources/Panels/Panel.swift index 95dc4438f519..b1e1db76e2c4 100644 --- a/Sources/Panels/Panel.swift +++ b/Sources/Panels/Panel.swift @@ -211,7 +211,7 @@ enum FocusFlashCurve: Equatable { enum PanelOverlayRingMetrics { static let inset: CGFloat = 2 static let cornerRadius: CGFloat = 6 - static let lineWidth: CGFloat = PaneIndicatorMetrics.strokeWidth + static let lineWidth: CGFloat = .paneIndicatorStrokeWidth static func pathRect(in bounds: CGRect) -> CGRect { bounds.insetBy(dx: inset, dy: inset) From 2d6fefbaf04be6bb78e7f245d664929f75acf5ee Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 27 Sep 2026 08:09:37 -0400 Subject: [PATCH 4/4] Move the release note into the PR description Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7f02e46d25c..450ebb29eb0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ All notable changes to cmux are documented here. - Global search finds text in the scrollback of open terminals, and Return on a terminal hit opens that pane's find bar on the match ([#11665](https://github.com/manaflow-ai/cmux/pull/11665)) -- thanks @smoreg! ### Changed -- The pane notification ring and focus flash draw at the same 2pt width as the active pane border, and the file and Markdown preview path header matches the 28pt bars around it with smaller icons ([#14982](https://github.com/manaflow-ai/cmux/pull/14982)) - The Settings > Mobile pairing row says Open Pairing and no longer promises a Tailscale QR code, which pairing does not use ([#14817](https://github.com/manaflow-ai/cmux/pull/14817)) -- thanks @aliyansajid! ### Fixed