From eacb3e053daeac2f9de94fcd43694669542d25fe Mon Sep 17 00:00:00 2001
From: "(CJ) Chukwudi Nwobodo" <142016413+chuks-qua@users.noreply.github.com>
Date: Thu, 24 Sep 2026 11:21:54 +0100
Subject: [PATCH] fix(web): keep message text visible while markdown content
suspends
---
.../conversation/messages/MessageBubble.tsx | 4 +-
.../__tests__/MessageBubble.suspense.test.tsx | 57 +++++++++++++++++++
2 files changed, 59 insertions(+), 2 deletions(-)
create mode 100644 apps/web/src/features/conversation/messages/__tests__/MessageBubble.suspense.test.tsx
diff --git a/apps/web/src/features/conversation/messages/MessageBubble.tsx b/apps/web/src/features/conversation/messages/MessageBubble.tsx
index 41c946f5e..a350c1d2a 100644
--- a/apps/web/src/features/conversation/messages/MessageBubble.tsx
+++ b/apps/web/src/features/conversation/messages/MessageBubble.tsx
@@ -631,7 +631,7 @@ function UserMessageText({ message, displayText, userGoal }: { message: Message;
{hasMentions ? (
) : (
-
+ {displayText}
}>
)}
@@ -854,7 +854,7 @@ function AssistantResponseText({
{renderDelta ? (
) : (
-
+ {message.content}}>
)}
diff --git a/apps/web/src/features/conversation/messages/__tests__/MessageBubble.suspense.test.tsx b/apps/web/src/features/conversation/messages/__tests__/MessageBubble.suspense.test.tsx
new file mode 100644
index 000000000..6e9c78905
--- /dev/null
+++ b/apps/web/src/features/conversation/messages/__tests__/MessageBubble.suspense.test.tsx
@@ -0,0 +1,57 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { cleanup, render } from "@testing-library/react";
+import type { Message, StoredAttachment } from "@/transport";
+import { MessageBubble } from "../MessageBubble";
+import { createEmptyThreadRecord } from "@/stores/thread-record";
+import { resetThreadStoreForTests } from "@/stores/thread-store-test-utils";
+
+vi.mock("@/components/chat/MarkdownContent", () => {
+ const SuspendedMarkdownContent = () => {
+ throw new Promise(() => {});
+ };
+ return {
+ __esModule: true,
+ default: SuspendedMarkdownContent,
+ MarkdownContent: SuspendedMarkdownContent,
+ };
+});
+
+beforeEach(() => {
+ resetThreadStoreForTests({ records: new Map([["thread-1", createEmptyThreadRecord()]]) });
+});
+
+afterEach(() => {
+ cleanup();
+});
+
+function makeMessage(content: string, role: "user" | "assistant" = "user"): Message {
+ return {
+ id: "msg-1",
+ thread_id: "thread-1",
+ role,
+ content,
+ timestamp: new Date().toISOString(),
+ attachments: [] as StoredAttachment[],
+ cost_usd: null,
+ tokens_used: null,
+ sequence: 1,
+ tool_calls: null,
+ files_changed: null,
+ };
+}
+
+describe("MessageBubble while MarkdownContent is suspended", () => {
+ it("keeps the user text visible in the bubble instead of rendering it empty", () => {
+ const { container } = render();
+ const bubble = container.querySelector(".bg-accent");
+ expect(bubble).not.toBeNull();
+ expect(bubble?.textContent).toContain("hello there");
+ });
+
+ it("keeps the assistant text visible instead of rendering it empty", () => {
+ const { container } = render();
+ const body = container.querySelector("[data-testid='assistant-response-text']");
+ expect(body).not.toBeNull();
+ expect(body?.textContent).toContain("done.");
+ });
+});