diff --git a/apps/.i18n/native-source.json b/apps/.i18n/native-source.json index 00718765f515..48c4a1a9ab10 100644 --- a/apps/.i18n/native-source.json +++ b/apps/.i18n/native-source.json @@ -40227,7 +40227,7 @@ }, { "kind": "conditional-branch", - "line": 735, + "line": 737, "path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift", "source": "Image", "surface": "apple", diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift index 1a631758a0d8..e239bc1a7c55 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift @@ -287,10 +287,12 @@ struct ChatMarkdownRenderSnapshot { preparesReveal: false), isExpanded: disclosure.isExpanded, blocks: disclosure.blocks.map { + // Reveal locations address only top-level blocks. Disclosure content + // uses a nested renderer without reveal state, so render it directly. Self.renderedBlock( $0, isComplete: isComplete, - preparesReveal: preparesReveal) + preparesReveal: false) })) case .thematicBreak: .thematicBreak diff --git a/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatMarkdownRendererTests.swift b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatMarkdownRendererTests.swift new file mode 100644 index 000000000000..06bc28467905 --- /dev/null +++ b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatMarkdownRendererTests.swift @@ -0,0 +1,51 @@ +import Testing +@testable import OpenClawChatUI + +struct ChatMarkdownRendererTests { + @Test @MainActor func `streaming disclosure body stays directly renderable`() throws { + let snapshot = ChatMarkdownRenderSnapshot( + text: """ +
+ More detail + + I'm here to help with questions, projects, and practical tasks. + + - Clear answers + - Thoughtful assistance + + ```bash + echo "Hello" + ``` + +
+ """, + isComplete: false, + preparesReveal: true) + guard case let .disclosure(disclosure) = try #require(snapshot.blocks.first) else { + Issue.record("expected rendered disclosure") + return + } + + #expect(String(disclosure.summary.attributed.characters) == "More detail") + #expect(disclosure.blocks.count == 2) + + guard case let .prose(prose) = disclosure.blocks[0] else { + Issue.record("expected renderable disclosure prose") + return + } + let proseText = String(prose.attributed.characters) + #expect(proseText.contains("I'm here to help with questions, projects, and practical tasks.")) + #expect(proseText.contains("Clear answers")) + #expect(proseText.contains("Thoughtful assistance")) + #expect(prose.plainText.isEmpty) + #expect(prose.prefix.characters.isEmpty) + #expect(prose.tail.isEmpty) + + guard case let .code(code) = disclosure.blocks[1] else { + Issue.record("expected rendered disclosure code block") + return + } + #expect(code.language == "bash") + #expect(code.code == "echo \"Hello\"") + } +}