diff --git a/packages/app/src/components/markdown/renderer.tsx b/packages/app/src/components/markdown/renderer.tsx
index 2eb619b9a..6c1eb4e01 100644
--- a/packages/app/src/components/markdown/renderer.tsx
+++ b/packages/app/src/components/markdown/renderer.tsx
@@ -569,8 +569,26 @@ export function createSharedMarkdownRules(): RenderRules {
{children}
),
- hardbreak: (node: ASTNode) => {"\n"},
- softbreak: (node: ASTNode) => {"\n"},
+ hardbreak: (
+ node: ASTNode,
+ _children: ReactNode[],
+ _parent: ASTNode[],
+ styles: MarkdownStyles,
+ ) => (
+
+ {"\n"}
+
+ ),
+ softbreak: (
+ node: ASTNode,
+ _children: ReactNode[],
+ _parent: ASTNode[],
+ styles: MarkdownStyles,
+ ) => (
+
+ {"\n"}
+
+ ),
code_block: (
node: ASTNode,
_children: ReactNode[],
diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx
index 08792bfd5..b17982a6e 100644
--- a/packages/app/src/components/message.tsx
+++ b/packages/app/src/components/message.tsx
@@ -1689,10 +1689,29 @@ export const AssistantMessage = memo(function AssistantMessage({
// plain is not hoisted into a UITextViewChild and is dropped (same
// root cause as strong/em/s) — so on iOS a hard line break vanished, and
// a softbreak between words jammed them together ("one\ntwo" -> "onetwo").
- // Emit the break through MarkdownTextSpan so it composes on iOS; web and
- // Android keep the same "\n" they rendered before.
- hardbreak: (node: ASTNode) => {"\n"},
- softbreak: (node: ASTNode) => {"\n"},
+ // Emit the break through MarkdownTextSpan so it composes on iOS. Keep
+ // the resolved break styles: hardbreak is a full-width flex-row child on
+ // Android, and dropping that width joins the surrounding text spans.
+ hardbreak: (
+ node: ASTNode,
+ _children: ReactNode[],
+ _parent: ASTNode[],
+ styles: MarkdownStyles,
+ ) => (
+
+ {"\n"}
+
+ ),
+ softbreak: (
+ node: ASTNode,
+ _children: ReactNode[],
+ _parent: ASTNode[],
+ styles: MarkdownStyles,
+ ) => (
+
+ {"\n"}
+
+ ),
code_block: (
node: ASTNode,
_children: ReactNode[],