From bdb3a2ded79f858fbc48ab4e6c6b6d84b233fb74 Mon Sep 17 00:00:00 2001 From: Flinn-X Date: Sun, 31 May 2026 13:25:00 +0800 Subject: [PATCH] fix(webui): handle undefined language in code blocks When fenced code blocks have no language specifier, react-syntax-highlighter receives undefined for the language prop, causing a white screen crash. - CodeBlock.tsx: fallback to 'text' when language is undefined - MarkdownTextRenderer.tsx: defensive fallback at fence rendering site - Added test cases for both components Fixes #4116 --- webui/src/components/CodeBlock.tsx | 2 +- webui/src/components/MarkdownTextRenderer.tsx | 2 +- webui/src/tests/code-block.test.tsx | 16 ++++++++++++++++ webui/src/tests/markdown-text-renderer.test.tsx | 11 +++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/webui/src/components/CodeBlock.tsx b/webui/src/components/CodeBlock.tsx index fc2e97bc6..52d274c00 100644 --- a/webui/src/components/CodeBlock.tsx +++ b/webui/src/components/CodeBlock.tsx @@ -33,7 +33,7 @@ const LazyHighlightedCode = lazy(async () => { default({ language, code, isDark }: HighlightedCodeProps) { return ( { expect(screen.getByTestId("plain-code-fallback")).toHaveClass("text-foreground/90"); }); + it("falls back to 'text' language when language is undefined", async () => { + render( + + + , + ); + + await act(async () => { + await Promise.resolve(); + await Promise.resolve(); + }); + + expect(screen.getByTestId("highlighted-code")).toBeInTheDocument(); + expect(screen.getByText("const value = 1;")).toBeInTheDocument(); + }); + it("reads theme from context without creating per-block observers", async () => { const originalMutationObserver = globalThis.MutationObserver; const observer = vi.fn(); diff --git a/webui/src/tests/markdown-text-renderer.test.tsx b/webui/src/tests/markdown-text-renderer.test.tsx index e3ff59727..2eb9488f5 100644 --- a/webui/src/tests/markdown-text-renderer.test.tsx +++ b/webui/src/tests/markdown-text-renderer.test.tsx @@ -16,6 +16,17 @@ describe("MarkdownTextRenderer", () => { expect(container.querySelector("pre div")).toBeNull(); }); + it("renders bare fenced code blocks without crashing", () => { + const { container } = render( + + {"Some text\n\n```\ncode without language\n```"} + , + ); + + expect(screen.getByText("code without language")).toBeInTheDocument(); + expect(container.querySelectorAll("pre")).toHaveLength(1); + }); + it("keeps streaming unfinished fenced code blocks to a single shell", () => { const { container } = render(