From 97e56d86aba833ce53e76beccb74bb9862185829 Mon Sep 17 00:00:00 2001 From: Chema Date: Thu, 8 Oct 2026 22:26:52 +0200 Subject: [PATCH] Move Resources/Browser toggles to the session header as icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bottom bar's RESOURCES/BROWSER text toggles competed for width with the quick keys and the hint text, and ViewThatFits fell back to bare glyphs as soon as the window narrowed. Move both to small icon buttons next to Reconnect in the header, where there's always room, and drop the now-empty bottom-bar slot. The Resources panel gets its own close button (✕) since the toolbar toggle is no longer right next to it. --- App/ResourcesPanel.swift | 13 ++++++++-- App/SessionView.swift | 51 +++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/App/ResourcesPanel.swift b/App/ResourcesPanel.swift index 48d0af8..7172b7c 100644 --- a/App/ResourcesPanel.swift +++ b/App/ResourcesPanel.swift @@ -8,13 +8,22 @@ import ShepherdrCore struct ResourcesPanel: View { let workspace: SessionWorkspace let checks: ChecksMonitor + /// Hides the panel; the toolbar toggle is still there to bring it back. + let close: () -> Void private static let shown = 10 @ViewState private var expanded = Set() var body: some View { VStack(alignment: .leading, spacing: 0) { - ConsoleHeader(title: "Resources", trailing: "\(workspace.visibleResources.count)") - .padding(.horizontal, 12).padding(.top, 10).padding(.bottom, 6) + HStack(spacing: 8) { + ConsoleHeader(title: "Resources", trailing: "\(workspace.visibleResources.count)") + Button(action: close) { + Text("✕").font(Theme.mono(10.5, .semibold)).foregroundStyle(Theme.dim) + } + .buttonStyle(.plain) + .help("Close Resources panel — the ≡ button in the header brings it back") + } + .padding(.horizontal, 12).padding(.top, 10).padding(.bottom, 6) ScrollView { VStack(alignment: .leading, spacing: 0) { ForEach(SessionResource.Kind.allCases, id: \.self) { kind in diff --git a/App/SessionView.swift b/App/SessionView.swift index e57e8f9..7838fd8 100644 --- a/App/SessionView.swift +++ b/App/SessionView.swift @@ -37,7 +37,8 @@ struct SessionView: View { workArea if showsResources && !workspace.visibleResources.isEmpty { Rectangle().fill(Theme.line).frame(width: 1) - ResourcesPanel(workspace: workspace, checks: model.checksMonitor).frame(width: 230) + ResourcesPanel(workspace: workspace, checks: model.checksMonitor) { showsResources = false } + .frame(width: 230) } } } @@ -125,10 +126,14 @@ struct SessionView: View { Spacer(minLength: 8) if let agent = context.agent { StateTag(state: agent.agent.state, stale: agent.isStale) } lockButton - Button { terminal.open() } label: { Text("↻") } - .buttonStyle(ConsoleButtonStyle(tint: Theme.dim)) - .disabled(terminal.status == .connecting || !context.canConnect) - .help("Reconnect to this terminal") + HStack(spacing: 4) { + Button { terminal.open() } label: { Text("↻").frame(width: 16, height: 14) } + .buttonStyle(ConsoleButtonStyle(tint: Theme.dim)) + .disabled(terminal.status == .connecting || !context.canConnect) + .help("Reconnect to this terminal") + resourcesButton + browserButton + } Button { showDetails.toggle() } label: { Text("i") } .buttonStyle(ConsoleButtonStyle(tint: Theme.dim)) .help("Session details") @@ -163,6 +168,23 @@ struct SessionView: View { .accessibilityLabel(locked ? "Unlock session" : "Lock session") } + private var resourcesButton: some View { + let links = workspace.visibleResources.count + return Button { showsResources.toggle() } label: { Text("≡").frame(width: 16, height: 14) } + .buttonStyle(ConsoleButtonStyle(tint: showsResources && links > 0 ? Theme.phosphor : Theme.dim)) + .disabled(links == 0) + .help(links > 0 ? "Pull requests, issues and Claude artifacts this session linked to" + : "Pull requests, issues and Claude artifacts this session links to gather here") + } + + private var browserButton: some View { + Button { model.toggleBrowser() } label: { + Image(systemName: "globe").font(.system(size: 11, weight: .semibold)).frame(width: 16, height: 14) + } + .buttonStyle(ConsoleButtonStyle(tint: workspace.browser.isVisible ? Theme.phosphor : Theme.dim)) + .help("This session's browser (⌘B). Its tabs stay open while you work elsewhere.") + } + // MARK: Banners and states private func banner(_ message: String, dismiss: (() -> Void)? = nil, action: (String, () -> Void)? = nil) -> some View { @@ -278,25 +300,6 @@ struct SessionView: View { key("↓", .down, help: "Down arrow") key("⏎", .enter, help: "Return") Spacer(minLength: 8) - if !compact { - Text(isLive ? "⇧⏎ new line · click a link to browse · ⌘-click opens your browser" : "locked · read-only · ⌘E to unlock") - .font(Theme.mono(9.5)).foregroundStyle(Theme.faint).lineLimit(1).truncationMode(.head) - .layoutPriority(-1) - } - let links = workspace.visibleResources.count - barToggle(compact ? (links > 0 ? "≡ \(links)" : "≡") : (links > 0 ? "≡ RESOURCES \(links)" : "≡ RESOURCES"), - active: showsResources && links > 0, - help: links > 0 ? "Pull requests, issues and Claude artifacts this session linked to" - : "Pull requests, issues and Claude artifacts this session links to gather here") { - showsResources.toggle() - } - .disabled(links == 0) - let tabs = workspace.browser.tabs.count - barToggle(compact ? (tabs > 0 ? "◫ \(tabs)" : "◫") : (tabs > 0 ? "◫ BROWSER \(tabs)" : "◫ BROWSER"), - active: workspace.browser.isVisible, - help: "This session's browser (⌘B). Its tabs stay open while you work elsewhere.") { - model.toggleBrowser() - } } }