Files
wehub-resource-sync a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

75 lines
2.4 KiB
Swift

import JCodeKit
import SwiftUI
/// Collapsible tool call card with live status.
struct ToolCallCard: View {
let call: TranscriptEntry.ToolCall
@State private var expanded = false
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Button {
withAnimation(.easeInOut(duration: 0.15)) {
expanded.toggle()
}
} label: {
HStack(spacing: 8) {
statusIcon
Text(call.name)
.font(Theme.mono(13, weight: .medium))
.foregroundStyle(Theme.textPrimary)
Spacer()
Image(systemName: expanded ? "chevron.up" : "chevron.down")
.font(.caption2)
.foregroundStyle(Theme.textTertiary)
}
}
if expanded {
if !call.input.isEmpty {
codeBlock(call.input)
}
if !call.output.isEmpty {
codeBlock(String(call.output.prefix(2000)))
}
if case let .failed(message) = call.status {
Text(message)
.font(Theme.mono(12))
.foregroundStyle(Theme.error)
}
}
}
.padding(8)
.background(Theme.surfaceElevated)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
@ViewBuilder
private var statusIcon: some View {
switch call.status {
case .streamingInput, .running:
ProgressView()
.controlSize(.mini)
.tint(Theme.mint)
case .succeeded:
Image(systemName: "checkmark.circle.fill")
.font(.caption)
.foregroundStyle(Theme.mint)
case .failed:
Image(systemName: "xmark.circle.fill")
.font(.caption)
.foregroundStyle(Theme.error)
}
}
private func codeBlock(_ text: String) -> some View {
ScrollView(.horizontal, showsIndicators: false) {
Text(text)
.font(Theme.mono(11))
.foregroundStyle(Theme.textSecondary)
.padding(8)
}
.background(Theme.background)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}