From f117777ef85bf71c56ac8ed1fd20545d3cc2c683 Mon Sep 17 00:00:00 2001 From: Kaede Akino Date: Mon, 19 Oct 2026 16:39:10 +0800 Subject: [PATCH] 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 --- .../TermindPresentation/AssistantToolDetails.swift | 4 +++- .../TermindToolPresentationProfiles.swift | 4 +--- TFM/Features/AI/TranscriptNativeRows.swift | 3 +-- TFMTests/AgentToolPresentationProfileTests.swift | 2 +- TFMTests/AgentToolPresentationTests.swift | 9 ++------- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Frameworks/TermindPresentation/AssistantToolDetails.swift b/Frameworks/TermindPresentation/AssistantToolDetails.swift index 4c100dcf..16ff17c2 100644 --- a/Frameworks/TermindPresentation/AssistantToolDetails.swift +++ b/Frameworks/TermindPresentation/AssistantToolDetails.swift @@ -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) } diff --git a/Frameworks/TermindPresentation/TermindToolPresentationProfiles.swift b/Frameworks/TermindPresentation/TermindToolPresentationProfiles.swift index 29c14d61..5d8b1935 100644 --- a/Frameworks/TermindPresentation/TermindToolPresentationProfiles.swift +++ b/Frameworks/TermindPresentation/TermindToolPresentationProfiles.swift @@ -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, diff --git a/TFM/Features/AI/TranscriptNativeRows.swift b/TFM/Features/AI/TranscriptNativeRows.swift index 08b0b646..4ed0b5d8 100644 --- a/TFM/Features/AI/TranscriptNativeRows.swift +++ b/TFM/Features/AI/TranscriptNativeRows.swift @@ -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 diff --git a/TFMTests/AgentToolPresentationProfileTests.swift b/TFMTests/AgentToolPresentationProfileTests.swift index d494cf72..ecb576ea 100644 --- a/TFMTests/AgentToolPresentationProfileTests.swift +++ b/TFMTests/AgentToolPresentationProfileTests.swift @@ -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 diff --git a/TFMTests/AgentToolPresentationTests.swift b/TFMTests/AgentToolPresentationTests.swift index 062b83f2..ab75d34a 100644 --- a/TFMTests/AgentToolPresentationTests.swift +++ b/TFMTests/AgentToolPresentationTests.swift @@ -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