mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
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 <noreply@anthropic.com>
This commit is contained in:
parent
be9ae99793
commit
3b83c9bd34
@ -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
|
||||
<div className="max-h-[420px] overflow-auto">
|
||||
<div className="min-w-full font-[var(--font-mono)] text-[12px] leading-[1.3]">
|
||||
{visibleLines.map((line, index) => (
|
||||
<div
|
||||
key={`${startLine + index}-${line}`}
|
||||
className="grid grid-cols-[3rem,minmax(0,1fr)] gap-0 hover:bg-[#f6f8fa]/50"
|
||||
>
|
||||
{showLineNumbers ? (
|
||||
effectiveShowLineNumbers ? (
|
||||
<div
|
||||
key={`${startLine + index}-${line}`}
|
||||
className="grid grid-cols-[3rem,minmax(0,1fr)] gap-0 hover:bg-[#f6f8fa]/50"
|
||||
>
|
||||
<span className="select-none border-r border-[#eaeef2] bg-[#fafbfc] px-2 py-px text-right text-[11px] text-[#8b949e]">
|
||||
{startLine + index}
|
||||
</span>
|
||||
) : (
|
||||
<span className="w-0" />
|
||||
)}
|
||||
<span
|
||||
className="overflow-hidden bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedLines[index] ?? escapeHtml(line) }}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="overflow-hidden bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedLines[index] ?? escapeHtml(line) }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
key={`${startLine + index}-${line}`}
|
||||
className="hover:bg-[#f6f8fa]/50"
|
||||
>
|
||||
<span
|
||||
className="block bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedLines[index] ?? escapeHtml(line) }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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) => `
|
||||
<div class="grid grid-cols-[3rem,minmax(0,1fr)] gap-0 hover:bg-[#f6f8fa]/50">
|
||||
<span class="select-none border-r border-[#eaeef2] bg-[#fafbfc] px-2 py-px text-right text-[11px] text-[#8b949e]">${index + 1}</span>
|
||||
<span class="overflow-hidden bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]">${line || ' '}</span>
|
||||
</div>
|
||||
`)
|
||||
.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) => `
|
||||
<div class="grid grid-cols-[3rem,minmax(0,1fr)] gap-0 hover:bg-[#f6f8fa]/50">
|
||||
<span class="select-none border-r border-[#eaeef2] bg-[#fafbfc] px-2 py-px text-right text-[11px] text-[#8b949e]">${index + 1}</span>
|
||||
<span class="overflow-hidden bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]">${line || ' '}</span>
|
||||
</div>
|
||||
`)
|
||||
.join('')
|
||||
: highlightedLines
|
||||
.map((line) => `
|
||||
<div class="hover:bg-[#f6f8fa]/50">
|
||||
<span class="block bg-white px-3 py-px whitespace-pre-wrap break-words text-[#24292f]">${line || ' '}</span>
|
||||
</div>
|
||||
`)
|
||||
.join('')
|
||||
|
||||
return `
|
||||
<div class="my-4 overflow-hidden rounded-lg border border-[#d0d7de] bg-[#f6f8fa] text-[#24292f]">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user