ios: support safe assistant inline images

This commit is contained in:
2026-10-20 14:31:15 -07:00
parent 9098fee32a
commit ee845c9fa3
13 changed files with 369 additions and 15 deletions
@@ -6,6 +6,57 @@ import UIKit
@Suite @MainActor
struct MessagePresentationTests {
@Test func agentMarkdownKeepsAltAndFormattingWithoutImageOrHTMLNodes() {
let source = """
**Caption** ![Inline *landscape*](https://images.invalid/inline.png)
![Reference landscape][photo]
[![Linked landscape](//images.invalid/linked.png)](https://example.invalid)
> ![Nested ![inner](file:///private/image.png)](data:image/png;base64,AAAA)
<picture><source srcset="https://images.invalid/source.png"><img src="https://images.invalid/html.png" alt="HTML landscape" title="a > b"></picture>
[Useful link](https://example.invalid)
[photo]: https://images.invalid/reference.png
"""
let content = AgentMessageMarkdown.content(source)
let html = content.renderHTML(), plain = content.renderPlainText()
for label in ["Inline landscape", "Reference landscape", "Linked landscape", "Nested inner", "HTML landscape"] {
#expect(plain.contains(label))
}
#expect(!html.contains("<img")); #expect(!html.contains("<picture")); #expect(!html.contains("<source"))
#expect(!html.contains("images.invalid")); #expect(!html.contains("/private/"))
#expect(html.contains("<strong>Caption</strong>"))
#expect(html.contains(#"href="https://example.invalid""#))
// Normal chat still uses unmodified MarkdownUI content.
#expect(MarkdownContent(source).renderHTML().contains("<img"))
#expect(AgentMessageMarkdown.content("<img src='https://images.invalid/a.png' alt='HTML landscape'>\n").renderHTML() == "<p>HTML landscape</p>\n")
}
@Test func agentMarkdownRetainsCodeTablesAndEscapesImageLikeAltText() {
let source = """
`![literal](https://images.invalid/code.png)`
```html
<img src="literal-code">
```
| Image | Value |
| --- | --- |
| ![Table alt](https://images.invalid/table.png) | **Kept** |
<IMG ALT='![literal alt](https://images.invalid/alt.png)' SRC='ignored'>
"""
let html = AgentMessageMarkdown.content(source).renderHTML()
#expect(html.contains("<table>")); #expect(html.contains("Table alt"))
#expect(html.contains("<strong>Kept</strong>"))
#expect(html.contains("&lt;img src=")); #expect(html.contains("![literal]"))
#expect(html.contains("![literal alt]")); #expect(!html.contains("<img"))
}
@Test func userMarkupRemainsLiteral() {
let source = "**bold** [link](https://example.invalid) `code`\n- item\n<script>literal</script>"
let content = SybilMessageText.literalContent(source)