From 3b83c9bd34fbe4b2051df728b0af88482919f2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Mon, 6 Apr 2026 21:55:00 +0800 Subject: [PATCH] fix: disable hljs.highlightAuto for unknown languages, hide line numbers for plain text - highlightAuto was misdetecting plain text (file trees, command output) as code and applying wrong syntax colors (red keywords, green tags, etc.) - Now only highlights when language is explicitly specified and known to hljs - Code blocks without a specified language render as clean plain text - Line numbers hidden for plain text blocks (only shown for actual code) Co-Authored-By: Claude Opus 4.6 --- desktop/src/components/chat/CodeViewer.tsx | 47 ++++++++++++------- desktop/src/components/chat/highlightCode.ts | 12 ++++- .../components/markdown/MarkdownRenderer.tsx | 29 ++++++++---- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/desktop/src/components/chat/CodeViewer.tsx b/desktop/src/components/chat/CodeViewer.tsx index 4098915e..4af18121 100644 --- a/desktop/src/components/chat/CodeViewer.tsx +++ b/desktop/src/components/chat/CodeViewer.tsx @@ -16,18 +16,23 @@ export function CodeViewer({ code, language, maxLines = 20, showLineNumbers = tr const isTruncated = !expanded && lines.length > maxLines const visibleCode = isTruncated ? lines.slice(0, maxLines).join('\n') : code + // Only highlight when language is explicitly known. + // Do NOT use highlightAuto — it misdetects plain text as code. + const hasLanguage = !!language && !!hljs.getLanguage(language) + const effectiveShowLineNumbers = showLineNumbers && hasLanguage + const highlightedLines = useMemo(() => { return visibleCode.split('\n').map((line) => { try { - if (language && hljs.getLanguage(language)) { - return hljs.highlight(line || ' ', { language, ignoreIllegals: true }).value + if (hasLanguage) { + return hljs.highlight(line || ' ', { language: language!, ignoreIllegals: true }).value } - return hljs.highlightAuto(line || ' ').value + return escapeHtml(line || ' ') } catch { return escapeHtml(line) } }) - }, [language, visibleCode]) + }, [hasLanguage, language, visibleCode]) const startLine = 1 @@ -55,22 +60,30 @@ export function CodeViewer({ code, language, maxLines = 20, showLineNumbers = tr
{visibleLines.map((line, index) => ( -
- {showLineNumbers ? ( + effectiveShowLineNumbers ? ( +
{startLine + index} - ) : ( - - )} - -
+ +
+ ) : ( +
+ +
+ ) ))}
diff --git a/desktop/src/components/chat/highlightCode.ts b/desktop/src/components/chat/highlightCode.ts index ce8c29b9..de21612b 100644 --- a/desktop/src/components/chat/highlightCode.ts +++ b/desktop/src/components/chat/highlightCode.ts @@ -12,14 +12,22 @@ export function highlightCodeLines(code: string, language?: string): string[] { return code.split('\n').map((line) => highlightCodeLine(line, language)) } +/** Returns true if the language is known to hljs and should be highlighted */ +export function isHighlightable(language?: string): boolean { + return !!language && !!hljs.getLanguage(language) +} + function highlightCodeLine(line: string, language?: string): string { const safeLine = line.length > 0 ? line : ' ' try { + // Only highlight when language is explicitly specified and known. + // Do NOT use highlightAuto — it misdetects plain text (file trees, + // command output, etc.) as code and applies wrong colors. if (language && hljs.getLanguage(language)) { - return hljs.highlight(safeLine, { language }).value + return hljs.highlight(safeLine, { language, ignoreIllegals: true }).value } - return hljs.highlightAuto(safeLine).value + return escapeHtml(safeLine) } catch { return escapeHtml(safeLine) } diff --git a/desktop/src/components/markdown/MarkdownRenderer.tsx b/desktop/src/components/markdown/MarkdownRenderer.tsx index 7874356c..bd10e9f7 100644 --- a/desktop/src/components/markdown/MarkdownRenderer.tsx +++ b/desktop/src/components/markdown/MarkdownRenderer.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' import { marked, type Tokens } from 'marked' -import { escapeHtml, highlightCodeLines } from '../chat/highlightCode' +import { escapeHtml, highlightCodeLines, isHighlightable } from '../chat/highlightCode' type Props = { content: string @@ -11,16 +11,27 @@ const renderer = new marked.Renderer() renderer.code = function ({ text, lang }: Tokens.Code) { const languageLabel = escapeHtml(lang || 'code') const lines = text.split('\n') + const hasLanguage = isHighlightable(lang) const highlightedLines = highlightCodeLines(text, lang) - const body = highlightedLines - .map((line, index) => ` -
- ${index + 1} - ${line || ' '} -
- `) - .join('') + // Show line numbers only when language is known (actual code). + // Plain text, file trees, command output etc. look better without them. + const body = hasLanguage + ? highlightedLines + .map((line, index) => ` +
+ ${index + 1} + ${line || ' '} +
+ `) + .join('') + : highlightedLines + .map((line) => ` +
+ ${line || ' '} +
+ `) + .join('') return `