mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix: preserve markdown formatting around media attachments (#116786)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
This commit is contained in:
committed by
GitHub
parent
82231b425b
commit
b4e10e8a90
@@ -432,6 +432,86 @@ describe("message-normalizer", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves paragraph breaks and code indentation before an assistant attachment", () => {
|
||||
const text = [
|
||||
"Here is the code.",
|
||||
"",
|
||||
"```python",
|
||||
"def run():",
|
||||
" if ready:",
|
||||
" return True",
|
||||
"```",
|
||||
"",
|
||||
"The attachment is ready.",
|
||||
].join("\n");
|
||||
|
||||
expect(
|
||||
normalizeMessage({
|
||||
role: "assistant",
|
||||
content: `${text}\nMEDIA:https://example.com/image.png`,
|
||||
}).content,
|
||||
).toEqual([
|
||||
{ type: "text", text },
|
||||
{
|
||||
type: "attachment",
|
||||
attachment: {
|
||||
url: "https://example.com/image.png",
|
||||
kind: "image",
|
||||
label: "image.png",
|
||||
mimeType: "image/png",
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(["", " ", "\t"])(
|
||||
"preserves a %j paragraph separator around an assistant attachment",
|
||||
(whitespace) => {
|
||||
expect(
|
||||
normalizeMessage({
|
||||
role: "assistant",
|
||||
content: `First paragraph\n${whitespace}\nMEDIA:https://example.com/image.png\n${whitespace}\nSecond paragraph`,
|
||||
}).content,
|
||||
).toEqual([
|
||||
{ type: "text", text: "First paragraph\n" },
|
||||
{
|
||||
type: "attachment",
|
||||
attachment: {
|
||||
url: "https://example.com/image.png",
|
||||
kind: "image",
|
||||
label: "image.png",
|
||||
mimeType: "image/png",
|
||||
},
|
||||
},
|
||||
{ type: "text", text: "Second paragraph" },
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves canonical code fences after removing reply and audio directives", () => {
|
||||
const code = ["```python", "value = 'a b'", "``` not a close", "other = 'c d'", "```"].join(
|
||||
"\n",
|
||||
);
|
||||
|
||||
expect(
|
||||
normalizeMessage({
|
||||
role: "assistant",
|
||||
content: `[[reply_to_current]]\n[[audio_as_voice]]\n${code}\nMEDIA:https://example.com/image.png`,
|
||||
}).content,
|
||||
).toEqual([
|
||||
{ type: "text", text: code },
|
||||
{
|
||||
type: "attachment",
|
||||
attachment: {
|
||||
url: "https://example.com/image.png",
|
||||
kind: "image",
|
||||
label: "image.png",
|
||||
mimeType: "image/png",
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("marks media-only audio attachments as voice notes when audio_as_voice is present", () => {
|
||||
const result = normalizeMessage({
|
||||
role: "assistant",
|
||||
|
||||
@@ -412,7 +412,8 @@ function expandTextContent(text: string): {
|
||||
replyTarget = { kind: "current" };
|
||||
}
|
||||
if (directives.text) {
|
||||
parts.push({ type: "text", text: directives.text });
|
||||
const normalizedText = directives.text + (segment.text.endsWith("\n") ? "\n" : "");
|
||||
parts.push({ type: "text", text: normalizedText });
|
||||
}
|
||||
}
|
||||
for (const preview of extracted.previews) {
|
||||
|
||||
@@ -622,6 +622,21 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe("grouped chat rendering", () => {
|
||||
it("preserves paragraph breaks around assistant attachments in rendered markdown", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderAssistantMessage(container, {
|
||||
role: "assistant",
|
||||
content: "First paragraph\n \nMEDIA:https://example.com/image.png\n\t\nSecond paragraph",
|
||||
timestamp: 1000,
|
||||
});
|
||||
|
||||
expect(markdownRenderMock).toHaveBeenCalledWith(
|
||||
"First paragraph\n\nSecond paragraph",
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("renders a compact count for collapsed duplicate messages", () => {
|
||||
const container = document.createElement("div");
|
||||
renderAssistantMessageEntries(container, [
|
||||
|
||||
Reference in New Issue
Block a user