Port the remaining tabs to the macOS client

This commit is contained in:
Ian Gulliver
2026-10-17 07:26:18 -07:00
parent 9b3f775dac
commit e28c313854
15 changed files with 3648 additions and 50 deletions
+139 -2
View File
@@ -227,13 +227,14 @@ private struct WebButton: View {
@ViewState private var hover = false
var body: some View {
let fill = danger ? Palette.danger : Palette.accent
configuration.label
.font(Fonts.font(13, weight: primary ? 700 : 400))
.foregroundStyle(primary ? Palette.bg : danger ? Palette.danger : Palette.text)
.padding(.horizontal, 12)
.padding(.vertical, 4)
.background(primary ? Palette.accent : Palette.raised, in: RoundedRectangle(cornerRadius: 6))
.overlay(RoundedRectangle(cornerRadius: 6).stroke(primary ? Palette.accent : hover && enabled ? Palette.muted : Palette.border))
.background(primary ? fill : Palette.raised, in: RoundedRectangle(cornerRadius: 6))
.overlay(RoundedRectangle(cornerRadius: 6).stroke(primary ? fill : hover && enabled ? Palette.muted : Palette.border))
.opacity(enabled ? (configuration.isPressed ? 0.8 : 1) : 0.45)
.contentShape(Rectangle())
.onHover { hover = $0 }
@@ -269,6 +270,142 @@ struct FieldStyle: ViewModifier {
}
}
struct HeaderBar: ViewModifier {
func body(content: Content) -> some View {
content
.padding(.horizontal, 16)
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.overlay(alignment: .bottom) {
Rectangle().fill(Palette.border).frame(height: 1)
}
}
}
struct RangeTabButton: View {
let label: String
let active: Bool
var help = ""
let action: () -> Void
@ViewState private var hover = false
var body: some View {
Button(action: action) {
Text(label)
.font(Fonts.font(12))
.foregroundStyle(active || hover ? Palette.text : Palette.muted)
.padding(.horizontal, 6)
.padding(.top, 2)
.padding(.bottom, 3)
.overlay(alignment: .bottom) {
Rectangle().fill(active ? Palette.accent : .clear).frame(height: 2)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.help(help)
.onHover { hover = $0 }
}
}
struct EmptyText: View {
let text: String
var error = false
var body: some View {
VStack {
Text(text)
.foregroundStyle(error ? Palette.danger : Palette.muted)
.multilineTextAlignment(.center)
.textSelection(.enabled)
.padding(32)
.frame(maxWidth: .infinity)
Spacer(minLength: 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct StateDot: View {
let filled: Color?
var body: some View {
Group {
if let filled {
Circle().fill(filled)
} else {
Circle().strokeBorder(Palette.muted, lineWidth: 1.5)
}
}
.frame(width: 8, height: 8)
}
}
struct StateMark: View {
let state: TaskState
let color: Color
var body: some View {
Group {
switch state.kind {
case .working, .merging:
Spinner(color: color)
case .answer:
StateDot(filled: Palette.warn)
case .error:
StateDot(filled: Palette.danger)
default:
StateDot(filled: nil)
}
}
.help(state.title)
}
}
enum PatchLine {
case hunk, add, del, meta, ctx
init(_ l: String) {
self = l.hasPrefix("@@") ? .hunk : l.hasPrefix("+") ? .add : l.hasPrefix("-") ? .del : l.hasPrefix("\\") ? .meta : .ctx
}
var background: Color {
switch self {
case .hunk: Palette.surface
case .add: Palette.ok.opacity(0.14)
case .del: Palette.danger.opacity(0.14)
default: .clear
}
}
var color: Color {
self == .hunk || self == .meta ? Palette.muted : Palette.text
}
}
struct StretchColumn: Layout {
var minWidth: CGFloat = 0
private func width(_ proposal: ProposedViewSize, _ subviews: Subviews) -> CGFloat {
max(minWidth, proposal.width ?? 0, subviews.map { $0.sizeThatFits(.unspecified).width }.max() ?? 0)
}
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
let w = width(ProposedViewSize(width: nil, height: nil), subviews)
let h = subviews.reduce(0) { $0 + $1.sizeThatFits(ProposedViewSize(width: w, height: nil)).height }
return CGSize(width: w, height: h)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
var y = bounds.minY
for sub in subviews {
let h = sub.sizeThatFits(ProposedViewSize(width: bounds.width, height: nil)).height
sub.place(at: CGPoint(x: bounds.minX, y: y), proposal: ProposedViewSize(width: bounds.width, height: h))
y += h
}
}
}
struct TrailingBubble: Layout {
var fraction: CGFloat = 0.85