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 `