diff --git a/apps/ios/Sources/Design/AgentProNodesDestination.swift b/apps/ios/Sources/Design/AgentProNodesDestination.swift index a9af709476fb..7fb76625c32f 100644 --- a/apps/ios/Sources/Design/AgentProNodesDestination.swift +++ b/apps/ios/Sources/Design/AgentProNodesDestination.swift @@ -193,19 +193,19 @@ struct AgentProNodesDestination: View { ProCard { VStack(spacing: 0) { - self.nodeDetailRow("Instance", value: entry.instanceid) + self.nodeDetailRow("Instance", copyLabel: "Copy instance", value: entry.instanceid) Divider() - self.nodeDetailRow("Device", value: entry.deviceid) + self.nodeDetailRow("Device", copyLabel: "Copy device", value: entry.deviceid) Divider() - self.nodeDetailRow("Host", value: entry.host) + self.nodeDetailRow("Host", copyLabel: "Copy host", value: entry.host) Divider() - self.nodeDetailRow("IP", value: entry.ip) + self.nodeDetailRow("IP", copyLabel: "Copy IP", value: entry.ip) Divider() - self.nodeDetailRow("Platform", value: entry.platform) + self.nodeDetailRow("Platform", copyLabel: "Copy platform", value: entry.platform) Divider() - self.nodeDetailRow("Version", value: entry.version) + self.nodeDetailRow("Version", copyLabel: "Copy version", value: entry.version) Divider() - self.nodeDetailRow("Mode", value: entry.mode) + self.nodeDetailRow("Mode", copyLabel: "Copy mode", value: entry.mode) } } .padding(.horizontal, OpenClawProMetric.pagePadding) @@ -223,7 +223,11 @@ struct AgentProNodesDestination: View { .navigationBarTitleDisplayMode(.inline) } - private func nodeDetailRow(_ title: OpenClawTextValue, value: String?) -> some View { + private func nodeDetailRow( + _ title: OpenClawTextValue, + copyLabel: LocalizedStringKey, + value: String?) -> some View + { let normalized = Self.normalized(value) ?? "n/a" return HStack(spacing: 10) { title.text @@ -241,7 +245,7 @@ struct AgentProNodesDestination: View { } .buttonStyle(.plain) .disabled(normalized == "n/a") - .accessibilityLabel("Copy value") + .accessibilityLabel(copyLabel) } .font(OpenClawType.subhead) .padding(.vertical, 10) diff --git a/apps/ios/Sources/Design/SettingsProTabActions.swift b/apps/ios/Sources/Design/SettingsProTabActions.swift index 721d924479b7..b05ade3bb8e0 100644 --- a/apps/ios/Sources/Design/SettingsProTabActions.swift +++ b/apps/ios/Sources/Design/SettingsProTabActions.swift @@ -1066,12 +1066,32 @@ extension SettingsProTab { let setupApplied = String(localized: "Setup code applied. Connecting...").lowercased() let checkingReachability = String(localized: "Checking gateway reachability...").lowercased() let qrFormat = String(localized: "QR loaded. Connecting to %@:%@...").lowercased() - let qrPrefix = qrFormat.components(separatedBy: "%@").first ?? qrFormat return lower == setupApplied - || (!qrPrefix.isEmpty && lower.hasPrefix(qrPrefix)) + || Self.localizedFormat(qrFormat, matches: lower) || lower == checkingReachability } + private static func localizedFormat(_ format: String, matches value: String) -> Bool { + guard let placeholder = try? NSRegularExpression(pattern: #"%(\d+\$)?@"#) else { + return format == value + } + let formatRange = NSRange(format.startIndex..., in: format) + let matches = placeholder.matches(in: format, range: formatRange) + guard !matches.isEmpty else { return format == value } + + var pattern = "^" + var cursor = format.startIndex + for match in matches { + guard let range = Range(match.range, in: format) else { return false } + pattern += NSRegularExpression.escapedPattern(for: String(format[cursor.. = { "func metricTile(\n icon: String,\n title: OpenClawTextValue,\n value: String,\n detail: OpenClawTextValue", ], "apps/ios/Sources/Design/AgentProNodesDestination.swift": [ - "private func nodeDetailRow(_ title: OpenClawTextValue, value: String?)", + "private func nodeDetailRow(\n _ title: OpenClawTextValue,\n copyLabel: LocalizedStringKey", "private func nodeListCard(title: OpenClawTextValue, values: [String])", "private func detailMetric(label: OpenClawTextValue, value: String)", "title: OpenClawTextValue,\n detail: OpenClawTextValue",