fix(assistant): unify tool rendering and stack detail fields

Use generic MCP result rendering and place labels above values on macOS and iOS. Update existing presentation expectations.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Kaede AkinoandClaude Code committed 2026-10-19 16:39:10 +08:00
1 parent 3b104e3046
commit f117777ef8
5 files changed
+8 -14

No files matched your search

@@ -38,7 +38,7 @@ public struct AssistantToolDetails: View {
@ViewBuilder private func item(_ value: AgentToolDetail.Item) -> some View {
switch value {
case .field(let field):
HStack(alignment: .firstTextBaseline, spacing: 8) {
VStack(alignment: .leading, spacing: 4) {
Text(field.label).font(.caption.weight(.medium)).foregroundStyle(.secondary)
if !field.value.isEmpty {
if let navigation = field.navigation, let navigate {
@@ -54,6 +54,8 @@ public struct AssistantToolDetails: View {
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
case .message(let text, let tint): Text(text).foregroundStyle(color(tint))
case .text(let block):
if let title = block.title { Text(title).font(.caption.weight(.medium)).foregroundStyle(.secondary) }
@@ -24,9 +24,7 @@ nonisolated public enum TermindToolPresentationProfiles {
public static func profile(for descriptor: AgentToolDescriptor) -> AgentToolPresentationProfile {
if descriptor.target == .mcp {
var profile = AgentToolPresentationProfile()
profile.preservesUnprojectedJSON = true
return profile
return .init()
}
// Privacy is independent of presenter availability; it is not semantic recovery.
if descriptor.target == .local,
+1 -2
View File
@@ -1545,14 +1545,13 @@ private enum NativeTranscriptFactory {
) -> NSView {
switch item {
case .field(let field):
let row = NativeStack.horizontal(spacing: Metrics.Space.s)
let row = NativeStack.vertical(spacing: Metrics.Space.xs)
let key = NativeLabel(
field.label,
font: .systemFont(ofSize: NSFont.smallSystemFontSize, weight: .medium),
color: .secondaryLabelColor
)
if field.value.isEmpty { return key }
key.widthAnchor.constraint(greaterThanOrEqualToConstant: 76).isActive = true
row.addArrangedSubview(key)
let font: NSFont =
field.isMonospaced
@@ -82,7 +82,7 @@ struct AgentToolPresentationProfileTests {
#expect(detail.layout.singleLineHeader)
let fallback = try #require(TermindToolPresentationProfiles.renderer.detail(for: card))
#expect(fallback.items != detail.items)
#expect(AgentToolDetailPreview(fallback).fullText.contains("\"value\""))
#expect(fallback.items == [.field(.init(label: "Value", value: "42"))])
}
@Test
+2 -7
View File
@@ -280,7 +280,7 @@ struct AgentToolPresentationTests {
}
@Test
func builtInFallbackUsesFieldsWhileMCPKeepsJSON() throws {
func builtInAndMCPFallbackUseTheSameFields() throws {
let payload = AgentJSONValue.object([
"internal_flag": .bool(true), "count": .number(2),
]).encodedString
@@ -306,12 +306,7 @@ struct AgentToolPresentationTests {
let mcpDetail = try #require(
TermindToolPresentationProfiles.renderer.detail(
for: mcp, presenter: AppDependencies.toolPresenter(for: mcp.descriptor)))
guard case .text(let block) = try #require(mcpDetail.items.first) else {
Issue.record("Expected MCP JSON text")
return
}
#expect(block.text.contains("{"))
#expect(block.text.contains("\"internal_flag\""))
#expect(mcpDetail.items == builtInDetail.items)
}
@Test