Description
Implement the generic ToolCallCardView — the shell that renders any tool call (Read / Edit / Bash / MCP / etc.) with consistent lifecycle animations (pending → in_progress → completed / failed). Specialized content per tool kind is injected via a slot (see D-16).
Spec: Epic #250 §6 (UI concept — tool-call level live feel).
Scope
File
MacApp/Packages/AgentChatUI/Sources/AgentChatUI/ToolCall/ToolCallCardView.swift
Structure
```swift
public struct ToolCallCardView<Content: View>: View {
public init(
toolCall: ToolCall,
@ViewBuilder content: @escaping (ToolCall) -> Content
)
}
```
- Uses
Card(style: .emphasized) from DS.
- Header row: icon (per
ToolKind) + tool name + StatusIndicator(kind:) (per status) + optional live duration ("2s").
- Body: injected via
content builder (specialized widget for this tool).
- Collapsible — default state: pending/in_progress expanded, completed/failed collapsed (click to expand).
- Animations (all
accessibilityReduceMotion-guarded):
- pending: shimmer skeleton (placeholder content);
- in_progress: subtle pill opacity pulse (opacity 0.7 ↔ 1.0, 0.8s cycle);
- completed: checkmark spring (scale 0.8 → 1.0, DS.Motion.fast);
- failed: shake (horizontal offset ±4pt, DS.Motion.fast, 2 cycles), red border.
Live duration
While status == .inProgress(startedAt:):
TimelineView(.periodic(from: .now, by: 1)) updates a "2s / 10s / 1m 3s" label in the header every second.
- Stops when status transitions.
Error presentation
.failed(at:reason:) — red DS.Color.destructive border, warning SF Symbol in status indicator, reason string in body.
Accessibility
.accessibilityLabel: "Tool call: {tool_name}, status {status.description}".
.accessibilityValue: status + duration.
- Live region announce on status transitions (pending → in_progress → completed / failed).
- Shake animation for
.failed — guarded by reduceMotion (skip animation, only red border remains).
Acceptance Criteria
Relationships
Description
Implement the generic
ToolCallCardView— the shell that renders any tool call (Read / Edit / Bash / MCP / etc.) with consistent lifecycle animations (pending → in_progress → completed / failed). Specialized content per tool kind is injected via a slot (see D-16).Spec: Epic #250 §6 (UI concept — tool-call level live feel).
Scope
File
MacApp/Packages/AgentChatUI/Sources/AgentChatUI/ToolCall/ToolCallCardView.swiftStructure
```swift
public struct ToolCallCardView<Content: View>: View {
public init(
toolCall: ToolCall,
@ViewBuilder content: @escaping (ToolCall) -> Content
)
}
```
Card(style: .emphasized)from DS.ToolKind) + tool name +StatusIndicator(kind:)(per status) + optional live duration ("2s").contentbuilder (specialized widget for this tool).accessibilityReduceMotion-guarded):Live duration
While
status == .inProgress(startedAt:):TimelineView(.periodic(from: .now, by: 1))updates a "2s / 10s / 1m 3s" label in the header every second.Error presentation
.failed(at:reason:)— redDS.Color.destructiveborder, warning SF Symbol in status indicator, reason string in body.Accessibility
.accessibilityLabel: "Tool call: {tool_name}, status {status.description}"..accessibilityValue: status + duration..failed— guarded by reduceMotion (skip animation, only red border remains).Acceptance Criteria
ToolCallCardViewimplemented with generic content slot.TimelineView).accessibilityReduceMotion— under reduceMotion: no pulse, no shake, no spring (fade only)..textSelection(.enabled)on content (user can copy tool output).Relationships