Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/AgentWorkbench/TaskContextPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct TaskContextButton: View {
Button { model.taskContextWindow.togglePage(.task, model: model) } label: {
WorkbenchToolbarSymbol(name: "checklist")
.foregroundStyle(open ? WorkbenchTheme.accent : .secondary)
}.buttonStyle(.plain).help("任务面板").accessibilityLabel("任务面板")
}.buttonStyle(WorkbenchToolbarButtonStyle(selected: open)).help("任务面板").accessibilityLabel("任务面板")
.accessibilityValue(open ? "已展开" : "已收起")
}
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/AgentWorkbench/WorkbenchHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ struct WorkbenchHeaderActions: View {
Button { model.toggleFileViewer() } label: {
WorkbenchToolbarSymbol(name: "folder")
.foregroundStyle(model.taskContextWindow.isShowing(.files) ? WorkbenchTheme.accent : .secondary)
}.buttonStyle(.plain).help("查看文件(只读)").accessibilityLabel("查看文件")
}.buttonStyle(WorkbenchToolbarButtonStyle(selected: model.taskContextWindow.isShowing(.files)))
.help("查看文件(只读)").accessibilityLabel("查看文件")
.accessibilityValue(model.taskContextWindow.isShowing(.files) ? "已展开" : "已收起")
TaskContextButton(model: model)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/AgentWorkbench/WorkspaceInspector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct ChangesButton: View {
Button { model.taskContextWindow.togglePage(.changes, model: model) } label: {
WorkbenchToolbarSymbol(name: "plus.forwardslash.minus")
.foregroundStyle(open ? WorkbenchTheme.accent : .secondary)
}.buttonStyle(.plain).help("查看改动").accessibilityLabel("改动")
}.buttonStyle(WorkbenchToolbarButtonStyle(selected: open)).help("查看改动").accessibilityLabel("改动")
.accessibilityValue(open ? "已展开" : "已收起")
}
}
Expand Down
32 changes: 28 additions & 4 deletions Sources/AgentWorkbench/WorkspaceSplitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import SwiftUI
import WorkbenchCore

enum WorkbenchChrome {
/// Measured height of the hidden-title-bar window with the unified toolbar.
static let headerHeight: CGFloat = 52
static let symbolSize: CGFloat = 15
/// Compact native toolbar, with a small gutter above the reading sheet.
static let headerHeight: CGFloat = 40
static let symbolSize: CGFloat = 14
static let controlSize: CGFloat = 32
static let controlSpacing: CGFloat = 16
static let labelSpacing: CGFloat = 8
Expand Down Expand Up @@ -40,6 +40,30 @@ struct WorkbenchToolbarSymbol: View {
}
}

/// One quiet state treatment inside the shared toolbar glass, with no extra elevation.
struct WorkbenchToolbarButtonStyle: ButtonStyle {
var selected = false

func makeBody(configuration: Configuration) -> some View {
ToolbarButton(configuration: configuration, selected: selected)
}

private struct ToolbarButton: View {
let configuration: ButtonStyleConfiguration
let selected: Bool
@State private var hovered = false

var body: some View {
configuration.label.background {
RoundedRectangle(cornerRadius: WorkbenchTheme.Radius.row, style: .continuous)
.fill(Color.primary.opacity(configuration.isPressed ? WorkbenchTheme.Fill.pressed
: selected ? WorkbenchTheme.Fill.selected : hovered ? WorkbenchTheme.Fill.hover : 0))
.padding(2)
}.onHover { hovered = $0 }
}
}
}

/// One translucent window backdrop contains navigation and the toolbar. The
/// workspace sheet floats above it and contains the reader and inspector; their hosts survive resizing.
struct WorkspaceSplitView<Sidebar: View, Header: View, Actions: View, Content: View>: NSViewControllerRepresentable {
Expand Down Expand Up @@ -840,7 +864,7 @@ final class WorkbenchSplitController: NSSplitViewController {
window.titlebarAppearsTransparent = true
window.titlebarSeparatorStyle = .none
window.styleMask.insert(.fullSizeContentView)
window.toolbarStyle = .unified
window.toolbarStyle = .unifiedCompact
if appAppearance != .system { applyAppearance() }
// The toolbar style sets the window's corner; the glass follows it.
windowBackdropView?.updateCornerRadius()
Expand Down
13 changes: 13 additions & 0 deletions Tests/HostLifecycleChecks/NativeDesignChecks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,19 @@ import WorkbenchCore
"title": fromTop(title), "actions": fromTop(toolbarActions),
"sidebar": fromTop(split.floatingSidebarView), "conversation": fromTop(card),
]
let actualTitlebarHeight = window.frame.height - window.contentLayoutRect.height
precondition(window.toolbarStyle == .unifiedCompact && actualTitlebarHeight <= 44,
"Use the native compact toolbar rather than sliding content underneath a tall toolbar: \(actualTitlebarHeight)")
let liveCardFrame = card.convert(card.bounds, to: nil)
let liveCardTop = window.frame.height - liveCardFrame.maxY
precondition(abs(liveCardTop - 44) < 1 && liveCardTop >= actualTitlebarHeight &&
abs(liveCardFrame.minY - WorkspaceLayoutMetrics.frameInset) < 1,
"The reading sheet must start at 44 pt with a clear toolbar and a balanced bottom gutter")
let closeFrame = fromTop(window.standardWindowButton(.closeButton))
for control in [title, toolbarActions, toggle].compactMap({ $0 }) {
precondition(abs((fromTop(control)["midY"] ?? 0) - (closeFrame["midY"] ?? 0)) < 2,
"Title, sidebar toggle and actions must share the window controls' vertical center")
}
print("Native window chrome: \(chrome)")
if let folder = ProcessInfo.processInfo.environment["PERCH_DESIGN_CAPTURES"] {
func rectangle(_ rect: NSRect) -> [String: Double] {
Expand Down
4 changes: 3 additions & 1 deletion docs/design/ui-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,11 @@
`isSidebarYieldingToInspector`);此时点侧栏按钮会让侧栏浮回正文上方。更窄时右栏让出至少 320pt 阅读区,不覆盖回复或输入框。
- **标题锚定窗格,阅读栏居中**:工具栏标题只显示文字,14pt medium,距卡片左侧 24pt;最大宽度 360pt,长标题单行尾部截断,悬停显示完整标题、Agent、机器和目录。标题恢复或更换后仍保持此对齐;正文、活动条和输入区在卡片当前可用宽度内居中。
右栏展开或关闭、左右侧栏缩放后必须重新居中,不能把正文固定在左边。对话和草稿宿主保持挂载。
工具栏放在统一底板上,正文卡片从窗口顶部 56pt 开始;hosting root 不重复应用标题栏安全区。
工具栏放在统一底板上,使用系统紧凑样式,正文卡片从窗口顶部 44pt 开始;hosting root 不重复应用标题栏安全区。
- **工具栏按钮一组胶囊**:改动、文件、任务面板和会话菜单共用一个玻璃胶囊,单个按钮不再各带底。前三项仅显示图标:改动采用加减号、文件采用文件夹、任务采用清单;保留悬停提示与辅助功能名称,不显示常驻文字。会话菜单保留省略号。整组固定在窗口右缘,不随检查器开合向中间漂移。
机器和 Agent 只在输入框底部显示,标题旁不再重复。
图标统一为 14pt 常规字重,保留 32pt 点击区域;改动、文件、任务哪个已打开,哪个显示内缩的轻量选中底色。
选中底色与悬停、按压分级,不为每个按钮增加独立玻璃或阴影。
- **模型必须可见**:输入框底部明确显示实际模型名及独立的思考强度选择器,不能用 Agent 名替换模型名。
运行环境作为次级信息;控制行不再给每组文字各套一层胶囊。窄窗口先将辅助控件移到第二行,保留模型与发送按钮。
- **输入文字与正文对齐**:正文最大 680pt,输入框外壳最大 704pt,内部 12pt 留白;
Expand Down
Loading