From 91ea6854160a6918793462f16d488adfad4e6a32 Mon Sep 17 00:00:00 2001 From: taekchef Date: Fri, 30 Oct 2026 00:51:01 +0800 Subject: [PATCH 1/2] Unify toolbar icons with reading controls --- Sources/WeiBei/Views/ContentView.swift | 10 +++-- .../WeiBei/Views/WeiBeiSegmentedControl.swift | 41 +++++++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/Sources/WeiBei/Views/ContentView.swift b/Sources/WeiBei/Views/ContentView.swift index 7d56526d..a0031d7f 100644 --- a/Sources/WeiBei/Views/ContentView.swift +++ b/Sources/WeiBei/Views/ContentView.swift @@ -1662,7 +1662,7 @@ private struct UnifiedTopBarView: View { // Light/dark quick toggle — keeps the active style pair, flips the // preference. Command palette stays reachable on ⌘K. topIconButton( - store.appearanceMode.isDark ? "sun.max" : "moon.stars", + store.appearanceMode.isDark ? "sun.max" : "moon", help: store.ui("切换深浅外观", "Toggle Light / Dark"), actionID: "appearance" ) { @@ -1678,6 +1678,7 @@ private struct UnifiedTopBarView: View { } } + .background { WeiBeiToolbarGroupBackground() } } private var customTopBar: some View { @@ -1818,12 +1819,13 @@ private struct UnifiedTopBarView: View { .transition(.opacity.combined(with: .scale(scale: 0.94))) } } + .background { WeiBeiToolbarGroupBackground() } } @ViewBuilder private var navigationButtons: some View { HStack(spacing: 0) { - topIconButton("arrow.left", help: store.ui("后退", "Back"), actionID: "back") { + topIconButton("chevron.left", help: store.ui("后退", "Back"), actionID: "back") { withAnimation(WeiBeiMotion.layout) { store.navigateBackInWorkspace() } @@ -1831,7 +1833,7 @@ private struct UnifiedTopBarView: View { .weiBeiKeyboardShortcut(store.executableChord(for: .navigateBack)) .disabled(!store.canNavigateBack) - topIconButton("arrow.right", help: store.ui("前进", "Forward"), actionID: "forward") { + topIconButton("chevron.right", help: store.ui("前进", "Forward"), actionID: "forward") { withAnimation(WeiBeiMotion.layout) { store.navigateForwardInWorkspace() } @@ -1852,7 +1854,7 @@ private struct UnifiedTopBarView: View { ), WeiBeiSegmentedControl.Segment( id: "agent", - systemImage: "bubble.left.and.text.bubble.right", + systemImage: "text.bubble", help: agentPaneToggleHelp, isSelected: store.isPaneToggleActive(.agent), action: store.toggleAgent diff --git a/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift b/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift index 2bae81a3..517b6e53 100644 --- a/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift +++ b/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift @@ -1,8 +1,8 @@ import SwiftUI import WeiBeiCore -/// One toolbar control for pane segments and the surrounding icon actions. -/// Segments share their moving hover pill; standalone actions draw the same pill. +/// Reading toolbar controls share an outline symbol, a fixed hit region, and +/// a neutral selection fill. Segments also share their moving hover pill. struct WeiBeiTopBarButton: View { static let width: CGFloat = 34 static let height: CGFloat = 28 @@ -31,13 +31,23 @@ struct WeiBeiTopBarButton: View { action() } label: { Image(systemName: systemImage) - .weiBeiText(13, weight: .semibold) + .symbolRenderingMode(.monochrome) + .symbolVariant(.none) + .weiBeiText(14, weight: .medium) .foregroundStyle(iconColor) .frame(width: Self.width * textScale, height: Self.height * textScale) .contentShape(Rectangle()) } .buttonStyle(WeiBeiTopBarPressStyle(reduceMotion: reduceMotion)) .background { + Capsule() + .fill(WeiBeiTheme.ink.opacity(colorScheme == .dark ? 0.16 : 0.09)) + .frame(width: (Self.width - Self.hoverInset * 2) * textScale, + height: (Self.height - Self.hoverInset * 2) * textScale) + .opacity(isSelected && isEnabled ? 1 : 0) + .animation(reduceMotion ? nil : WeiBeiMotion.hover, value: isSelected) + .allowsHitTesting(false) + .accessibilityHidden(true) if !usesGroupHover { RoundedRectangle(cornerRadius: (Self.height - Self.hoverInset * 2) * textScale / 2, style: .continuous) @@ -68,13 +78,29 @@ struct WeiBeiTopBarButton: View { private var iconColor: Color { if !isEnabled { return WeiBeiTheme.secondaryInk.opacity(0.4) } - if isSelected { return WeiBeiTheme.cinnabar } - return isHovered ? WeiBeiTheme.ink : WeiBeiTheme.secondaryInk + return isSelected || isHovered ? WeiBeiTheme.ink : WeiBeiTheme.ink.opacity(0.85) } } -/// Pane switches share the toolbar's transparent surface. Selection stays on -/// the cinnabar icon; only pointer hover adds a transient, translucent highlight. +/// A quiet group outline, sized by the existing controls so grouping does not +/// change their hit regions or the native toolbar's overflow measurements. +struct WeiBeiToolbarGroupBackground: View { + @Environment(\.colorScheme) private var colorScheme + + var body: some View { + Capsule() + .fill(WeiBeiTheme.paperRaised.opacity(colorScheme == .dark ? 0.65 : 0.55)) + .overlay { + Capsule() + .strokeBorder(WeiBeiTheme.ink.opacity(colorScheme == .dark ? 0.18 : 0.10), lineWidth: 0.5) + } + .allowsHitTesting(false) + .accessibilityHidden(true) + } +} + +/// Independent pane switches share a reading-toolbar capsule. Each open pane +/// keeps its own selected fill; the moving hover highlight is only transient. struct WeiBeiSegmentedControl: View { struct Segment: Identifiable { let id: String @@ -107,6 +133,7 @@ struct WeiBeiSegmentedControl: View { } .frame(height: height) .background(alignment: .leading) { hoverPill } + .background { WeiBeiToolbarGroupBackground() } .accessibilityElement(children: .contain) } From 2f797f86c6211ca9dc29abb9abb547d1e5a98c7d Mon Sep 17 00:00:00 2001 From: taekchef Date: Fri, 30 Oct 2026 01:14:09 +0800 Subject: [PATCH 2/2] Restore cinnabar icons for selected toolbar controls --- Sources/WeiBei/Views/WeiBeiSegmentedControl.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift b/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift index 517b6e53..b812e597 100644 --- a/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift +++ b/Sources/WeiBei/Views/WeiBeiSegmentedControl.swift @@ -2,7 +2,7 @@ import SwiftUI import WeiBeiCore /// Reading toolbar controls share an outline symbol, a fixed hit region, and -/// a neutral selection fill. Segments also share their moving hover pill. +/// a cinnabar selected symbol with a neutral fill. Segments also share their moving hover pill. struct WeiBeiTopBarButton: View { static let width: CGFloat = 34 static let height: CGFloat = 28 @@ -78,7 +78,8 @@ struct WeiBeiTopBarButton: View { private var iconColor: Color { if !isEnabled { return WeiBeiTheme.secondaryInk.opacity(0.4) } - return isSelected || isHovered ? WeiBeiTheme.ink : WeiBeiTheme.ink.opacity(0.85) + if isSelected { return WeiBeiTheme.cinnabar } + return isHovered ? WeiBeiTheme.ink : WeiBeiTheme.ink.opacity(0.85) } }