From d3055702ef1b5e238ffa84a58833ec6f8d85f2e0 Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Mon, 20 Apr 2026 14:22:35 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20connection=20pill=20=E2=80=94=20revert?= =?UTF-8?q?=20to=20.principal,=20swap=20dot=20for=20state=20SF=20Symbol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rolling back the .primaryAction placement (the pill shifted right and lost its centered position in the toolbar). The "funny background with shadow" visible in the toolbar is macOS's own .principal emphasis bezel — not something Scarf draws, and not something we can cleanly hide without disabling the toolbar surface itself. The native bezel is the pill's frame; we just have to make the pill's interior read well inside it. Two changes to make the pill itself look like a toolbar tool inside that bezel: - Drop the colored dot, replace with a state-specific SF Symbol. The icon's shape signals clickability (looks like a tool button), and its color signals state (green/orange/yellow/red hierarchical). Less "status chip", more "toolbar button with status". - Icons per state: - connected → checkmark.circle.fill (click to re-probe) - degraded → stethoscope (click to run diagnostics, matches the stethoscope on the Manage Servers row) - idle → arrow.triangle.2.circlepath (checking/retry) - error → exclamationmark.triangle.fill (click for stderr) Horizontal padding = 4 so the icon-and-label sit balanced inside the bezel rather than pushed up against its edges. Co-Authored-By: Claude Opus 4.7 (1M context) --- scarf/scarf/ContentView.swift | 11 ++++--- .../Servers/Views/ConnectionStatusPill.swift | 29 ++++++++++++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/scarf/scarf/ContentView.swift b/scarf/scarf/ContentView.swift index 7571002..9e60787 100644 --- a/scarf/scarf/ContentView.swift +++ b/scarf/scarf/ContentView.swift @@ -21,12 +21,11 @@ struct ContentView: View { ServerSwitcherToolbar() } if serverContext.isRemote { - // `.principal` placement renders the item inside a - // centered emphasis bezel on macOS, which reads as - // an unwanted capsule-with-shadow around the pill. - // `.primaryAction` (right side of the toolbar) has - // no decorative background — what we want. - ToolbarItem(placement: .primaryAction) { + // `.principal` centers the pill in the toolbar — + // the native emphasis bezel is the intended frame; + // the pill's own visual content (icon + label, no + // background) sits inside it in balance. + ToolbarItem(placement: .principal) { ConnectionStatusPill(status: connectionStatus) } } diff --git a/scarf/scarf/Features/Servers/Views/ConnectionStatusPill.swift b/scarf/scarf/Features/Servers/Views/ConnectionStatusPill.swift index a7ede8d..6961de3 100644 --- a/scarf/scarf/Features/Servers/Views/ConnectionStatusPill.swift +++ b/scarf/scarf/Features/Servers/Views/ConnectionStatusPill.swift @@ -23,18 +23,20 @@ struct ConnectionStatusPill: View { status.retry() } } label: { - // No explicit background — the SwiftUI toolbar gives the item - // its own bezel, and painting a second capsule on top looked - // doubly-framed. Just the colored dot + label reads cleanly. - HStack(spacing: 4) { - Circle() - .fill(color) - .frame(width: 8, height: 8) + // Leading SF Symbol does double duty: its color is the status + // signal (green/orange/yellow/red), and its shape reads as a + // clickable toolbar tool. No custom background — the toolbar's + // `.principal` emphasis bezel is the frame. + HStack(spacing: 5) { + Image(systemName: iconName) + .foregroundStyle(color) + .symbolRenderingMode(.hierarchical) Text(label) .font(.caption) .foregroundStyle(.secondary) .lineLimit(1) } + .padding(.horizontal, 4) } .buttonStyle(.plain) .help(tooltip) @@ -55,6 +57,19 @@ struct ConnectionStatusPill: View { } } + /// State-specific SF Symbol. The icon shape itself signals what the + /// click will do: checkmark for connected (click to re-probe), + /// stethoscope for degraded (click to run diagnostics), spinning + /// arrows for probing, triangle for error. + private var iconName: String { + switch status.status { + case .connected: return "checkmark.circle.fill" + case .degraded: return "stethoscope" + case .idle: return "arrow.triangle.2.circlepath" + case .error: return "exclamationmark.triangle.fill" + } + } + private var label: String { switch status.status { case .connected: return "Connected"