mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 23:23:54 +00:00
* test(gateway): shift advertised-method window for tts.speak * feat(ios): add Listen action speaking assistant messages via gateway TTS * docs(ios): document the chat Listen action * style(ios): keep outbox label doc comment attached and give speech chip its own * fix(ios): harden chat Listen playback * docs(changelog): note iOS chat Listen * fix(ios): unwrap speech MIME before classification * docs(changelog): keep Listen note app-scoped
36 lines
1.4 KiB
Swift
36 lines
1.4 KiB
Swift
import Foundation
|
|
|
|
/// Plain-text projection of a transcript message: exactly what the reader sees
|
|
/// in the bubble, with tool traces and non-text blocks removed. Shared by the
|
|
/// transcript exporter and the Listen action so exported and spoken text
|
|
/// always match the visible transcript.
|
|
enum ChatMessageVisibleText {
|
|
static func visibleText(in message: OpenClawChatMessage) -> String {
|
|
let text = self.primaryText(in: message)
|
|
let role = message.role.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
guard role != "user" else { return text }
|
|
return AssistantTextParser.visibleSegments(from: text)
|
|
.map(\.text)
|
|
.joined(separator: "\n\n")
|
|
}
|
|
|
|
static func hasVisibleText(in message: OpenClawChatMessage) -> Bool {
|
|
!self.visibleText(in: message)
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
.isEmpty
|
|
}
|
|
|
|
private static func primaryText(in message: OpenClawChatMessage) -> String {
|
|
let parts = message.content.compactMap { content -> String? in
|
|
let kind = (content.type ?? "text").lowercased()
|
|
guard kind == "text" || kind.isEmpty else { return nil }
|
|
return content.text
|
|
}
|
|
return OpenClawChatMessage.displayText(
|
|
contentText: parts.joined(separator: "\n"),
|
|
role: message.role,
|
|
stopReason: message.stopReason,
|
|
errorMessage: message.errorMessage)
|
|
}
|
|
}
|