Fix long Assistant approval titles

This commit is contained in:
Kaede Akino committed 2026-09-14 11:06:38 +08:00
1 parent 639912deb6
commit 84f3565b37
5 files changed
+141 -8

No files matched your search

+58 -4
View File
@@ -2935,6 +2935,23 @@
}
}
},
"Approval action target: %@ %@" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在%2$@上%1$@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "在%2$@上%1$@"
}
}
}
},
"Approval required" : {
"localizations" : {
"zh-Hans" : {
@@ -2951,18 +2968,36 @@
}
}
},
"Arguments" : {
"Approval tool action target: %@ %@ %@" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "参数"
"value" : "在%3$@上%1$@%2$@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "參數"
"value" : "在%3$@上%1$@%2$@"
}
}
}
},
"Approval tool action: %@ %@" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$@%2$@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$@%2$@"
}
}
}
@@ -3015,6 +3050,23 @@
}
}
},
"Arguments" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "参数"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "參數"
}
}
}
},
"Armed" : {
"extractionState" : "manual",
"localizations" : {
@@ -10793,6 +10845,7 @@
}
},
"Host: %@" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
@@ -19038,6 +19091,7 @@
}
},
"Safety details" : {
"extractionState" : "stale",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
@@ -26868,4 +26922,4 @@
}
},
"version" : "1.2"
}
}
@@ -42,7 +42,7 @@ nonisolated public enum AgentToolPresentation {
)
return AgentApprovalPresentation(
symbol: symbol(for: card),
title: summary(for: card, hostName: hostName, locale: locale),
title: approvalSummary(for: card, hostName: hostName, locale: locale),
detail: AgentToolDetail(
items: approvalArgumentItems(
request.invocation.call.arguments, presenter: presenter,
@@ -51,6 +51,65 @@ nonisolated public enum AgentToolPresentation {
)
}
/// Approval chrome already communicates that the call is waiting. Keep its
/// action title short and move argument values into the reviewable detail
/// below, where long commands and paths can scroll without hiding actions.
private static func approvalSummary(
for card: AgentToolCard,
hostName: String?,
locale: Locale
) -> String {
let activity = approvalActivity(for: card, locale: locale)
guard let kind = actionKind(for: card) else {
let action = localized("Run \(activity)", locale: locale)
guard let hostName, !hostName.isEmpty else { return action }
if isChinese(locale) {
return localized("Approval action target: \(action) \(hostName)", locale: locale)
}
return "\(action) on \(hostName)"
}
let action = localizedActionVerb(kind, locale: locale)
if isChinese(locale) {
guard let hostName, !hostName.isEmpty else {
return localized("Approval tool action: \(action) \(activity)", locale: locale)
}
return localized(
"Approval tool action target: \(action) \(activity) \(hostName)",
locale: locale
)
}
let rendered =
uppercaseFirst(actionName(kind, form: .infinitive, locale: locale))
+ (activity.isEmpty ? "" : " \(activity)")
guard let hostName, !hostName.isEmpty else { return rendered }
return "\(rendered) on \(hostName)"
}
private static func approvalActivity(
for card: AgentToolCard,
locale: Locale
) -> String {
guard let presentation = card.descriptor.presentation else {
return humanized(card.descriptor.qualifiedName)
}
switch presentation.activity {
case .label(let label):
return legacyActivity(
label, descriptorName: card.descriptor.name, locale: locale
)
case .argument(_, let fallback):
return legacyActivity(
fallback, descriptorName: card.descriptor.name, locale: locale
)
case .arrayCount, .localizedArrayCount, .semanticArrayCount:
return operation(for: card, locale: locale).text
case .semanticLabel(let kind), .semanticArgument(_, let kind):
return activityName(kind, locale: locale)
}
}
/// While work is live, use the icon of the tool(s) still in flight. After
/// completion, a homogeneous batch keeps its tool icon; mixed batches use
/// the generic tools symbol.
@@ -1104,6 +1163,11 @@ nonisolated public enum AgentToolPresentation {
return first.lowercased() + String(text.dropFirst())
}
private static func uppercaseFirst(_ text: String) -> String {
guard let first = text.first else { return text }
return first.uppercased() + String(text.dropFirst())
}
public static func tint(for state: AgentToolCard.State) -> BatchTint {
switch state {
case .denied, .failed: .failure
@@ -72,7 +72,7 @@ struct MobileAssistantInteraction: View {
.accessibilityHidden(true)
Text(presentation.title)
.font(.callout.weight(.medium))
.fixedSize(horizontal: false, vertical: true)
.lineLimit(3)
}
ViewThatFits(in: .vertical) {
@@ -99,6 +99,8 @@ struct MobileAssistantInteraction: View {
.frame(minHeight: 44)
.accessibilityIdentifier("AssistantApproval.AllowOnce")
}
.fixedSize(horizontal: false, vertical: true)
.layoutPriority(2)
}
.frame(maxWidth: .infinity, alignment: .topLeading)
.accessibilityElement(children: .contain)
@@ -550,7 +550,7 @@ private struct AgentApprovalCard: View {
.accessibilityHidden(true)
Text(presentation.title)
.font(.callout.weight(.medium))
.fixedSize(horizontal: false, vertical: true)
.lineLimit(3)
}
ViewThatFits(in: .vertical) {
@@ -575,6 +575,8 @@ private struct AgentApprovalCard: View {
.buttonStyle(.borderedProminent)
.accessibilityIdentifier("AssistantApproval.AllowOnce")
}
.fixedSize(horizontal: false, vertical: true)
.layoutPriority(2)
}
.accessibilityElement(children: .contain)
.accessibilityIdentifier("AssistantApproval.Card")
+12 -1
View File
@@ -55,8 +55,17 @@ struct AgentToolPresentationTests {
)
#expect(presentation.symbol == "terminal")
#expect(presentation.title.contains("Production"))
#expect(presentation.title == "Run the SSH command on Production")
#expect(!presentation.title.contains(command))
#expect(!presentation.title.contains(hostID.uuidString))
#expect(
AgentToolPresentation.approval(
for: request,
presenter: SSHExecTool.presenter,
hostName: "Production",
locale: Locale(identifier: "zh-Hans")
).title == "在Production上运行SSH命令"
)
#expect(
presentation.detail.items == [
.text(.init(title: "Command", text: command, style: .monospaced)),
@@ -88,6 +97,8 @@ struct AgentToolPresentationTests {
let presentation = AgentToolPresentation.approval(
for: request, locale: Locale(identifier: "en")
)
#expect(presentation.title == "Run remote tool")
#expect(!presentation.title.contains("hunter2"))
#expect(
presentation.detail.items == [
.field(.init(label: "Password", value: "hunter2"))