Fix long Assistant approval titles
This commit is contained in:
1 parent
639912deb6
commit
84f3565b37
5 files changed
+141
-8
No files matched your search
@@ -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")
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in new issue
Block a user