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
This commit is contained in:
Flinn-X 2026-05-31 13:25:00 +08:00 committed by Xubin Ren
parent a3241c33ba
commit bdb3a2ded7
4 changed files with 29 additions and 2 deletions

View File

@ -33,7 +33,7 @@ const LazyHighlightedCode = lazy(async () => {
default({ language, code, isDark }: HighlightedCodeProps) {
return (
<SyntaxHighlighter
language={language}
language={language || "text"}
style={isDark ? oneDark : oneLight}
customStyle={{
margin: 0,

View File

@ -264,7 +264,7 @@ export default function MarkdownTextRenderer({
if (fence) {
return (
<CodeBlock
language={fence.language}
language={fence.language || "text"}
code={fence.code}
className="my-3"
highlight={highlightCode}

View File

@ -48,6 +48,22 @@ describe("CodeBlock", () => {
expect(screen.getByTestId("plain-code-fallback")).toHaveClass("text-foreground/90");
});
it("falls back to 'text' language when language is undefined", async () => {
render(
<ThemeProvider theme="dark">
<CodeBlock language={undefined} code="const value = 1;" />
</ThemeProvider>,
);
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();

View File

@ -16,6 +16,17 @@ describe("MarkdownTextRenderer", () => {
expect(container.querySelector("pre div")).toBeNull();
});
it("renders bare fenced code blocks without crashing", () => {
const { container } = render(
<MarkdownTextRenderer highlightCode={false}>
{"Some text\n\n```\ncode without language\n```"}
</MarkdownTextRenderer>,
);
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(
<MarkdownTextRenderer highlightCode={false}>