From 23201526d7fc5f99222a0a790cc14895cda01a81 Mon Sep 17 00:00:00 2001 From: liruifengv Date: Tue, 21 Jul 2026 12:47:56 +0800 Subject: [PATCH] fix(tui): remove red coloring from code syntax highlighting --- .changeset/tui-code-highlight-no-red.md | 5 +++ .../tui/components/media/code-highlight.ts | 4 ++- .../src/tui/theme/highlight-theme.ts | 17 ++++++++++ apps/kimi-code/src/tui/theme/pi-tui-theme.ts | 3 +- .../components/media/code-highlight.test.ts | 34 +++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 .changeset/tui-code-highlight-no-red.md create mode 100644 apps/kimi-code/src/tui/theme/highlight-theme.ts diff --git a/.changeset/tui-code-highlight-no-red.md b/.changeset/tui-code-highlight-no-red.md new file mode 100644 index 0000000000..35c4242c8e --- /dev/null +++ b/.changeset/tui-code-highlight-no-red.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Remove red coloring from syntax highlighting in code previews and markdown code blocks. diff --git a/apps/kimi-code/src/tui/components/media/code-highlight.ts b/apps/kimi-code/src/tui/components/media/code-highlight.ts index bfef3d91b5..deec7a2591 100644 --- a/apps/kimi-code/src/tui/components/media/code-highlight.ts +++ b/apps/kimi-code/src/tui/components/media/code-highlight.ts @@ -7,6 +7,8 @@ import { extname } from 'node:path'; import { highlight, supportsLanguage } from 'cli-highlight'; +import { codeHighlightTheme } from '#/tui/theme/highlight-theme'; + const EXT_LANG_MAP: Record = { ts: 'typescript', tsx: 'typescript', @@ -45,7 +47,7 @@ export function highlightLines(code: string, lang: string | undefined): string[] const normalizedLang = lang?.trim().toLowerCase(); if (!normalizedLang || !supportsLanguage(normalizedLang)) return code.split('\n'); try { - return highlight(code, { language: normalizedLang, ignoreIllegals: true }).split('\n'); + return highlight(code, { language: normalizedLang, ignoreIllegals: true, theme: codeHighlightTheme }).split('\n'); } catch { return code.split('\n'); } diff --git a/apps/kimi-code/src/tui/theme/highlight-theme.ts b/apps/kimi-code/src/tui/theme/highlight-theme.ts new file mode 100644 index 0000000000..e16f4b3108 --- /dev/null +++ b/apps/kimi-code/src/tui/theme/highlight-theme.ts @@ -0,0 +1,17 @@ +/** + * Shared cli-highlight theme for code previews (Write/Edit tool calls, + * approval panels) and markdown code blocks. + * + * cli-highlight's DEFAULT_THEME paints `string`, `regexp` and `deletion` + * tokens red; reset exactly those tokens to `plain` so highlighted code + * contains no red at all. Tokens not listed here fall back to DEFAULT_THEME. + */ + +import { plain } from 'cli-highlight'; +import type { Theme } from 'cli-highlight'; + +export const codeHighlightTheme: Theme = { + string: plain, + regexp: plain, + deletion: plain, +}; diff --git a/apps/kimi-code/src/tui/theme/pi-tui-theme.ts b/apps/kimi-code/src/tui/theme/pi-tui-theme.ts index 1b53bce0c0..91161a5b4f 100644 --- a/apps/kimi-code/src/tui/theme/pi-tui-theme.ts +++ b/apps/kimi-code/src/tui/theme/pi-tui-theme.ts @@ -13,6 +13,7 @@ import chalk from 'chalk'; import { highlight, supportsLanguage } from 'cli-highlight'; import { currentTheme } from './theme'; +import { codeHighlightTheme } from './highlight-theme'; // pi-tui's renderer emits literal "### " / "#### " / ... markers for h3-h6 // headings (h1/h2 are rendered without the `#` prefix). The prefix arrives @@ -51,7 +52,7 @@ export function createMarkdownTheme(options?: { transient?: boolean }): Markdown const language = normalizedLang !== undefined && supportsLanguage(normalizedLang) ? normalizedLang : 'text'; try { - const highlighted = highlight(code, { language, ignoreIllegals: true }); + const highlighted = highlight(code, { language, ignoreIllegals: true, theme: codeHighlightTheme }); return highlighted.split('\n'); } catch { return code.split('\n'); diff --git a/apps/kimi-code/test/tui/components/media/code-highlight.test.ts b/apps/kimi-code/test/tui/components/media/code-highlight.test.ts index b6df1cf22d..b8b1b50a5c 100644 --- a/apps/kimi-code/test/tui/components/media/code-highlight.test.ts +++ b/apps/kimi-code/test/tui/components/media/code-highlight.test.ts @@ -1,9 +1,15 @@ +import { createRequire } from 'node:module'; +import { dirname } from 'node:path'; + import { describe, expect, it } from 'vitest'; import { highlightLines, langFromPath } from '#/tui/components/media/code-highlight'; +import { codeHighlightTheme } from '#/tui/theme/highlight-theme'; import { captureProcessWrite } from '../../../helpers/process'; +const ESC = String.fromCodePoint(27); + describe('code-highlight', () => { it('maps known file extensions to supported highlight languages', () => { expect(langFromPath('src/foo.ts')).toBe('typescript'); @@ -23,4 +29,32 @@ describe('code-highlight', () => { stderr.restore(); } }); + + it('resets red tokens to plain styling', () => { + for (const token of ['string', 'regexp', 'deletion'] as const) { + expect(codeHighlightTheme[token]?.('code')).toBe('code'); + } + }); + + it('emits no red SGR for strings, regexps and diff deletions', () => { + // cli-highlight styles through its own chalk v4 instance; force colors on + // so the assertions below observe real SGR sequences. + const req = createRequire(import.meta.url); + const chalkV4 = req( + req.resolve('chalk', { paths: [dirname(req.resolve('cli-highlight'))] }), + ) as { level: number }; + const prevLevel = chalkV4.level; + chalkV4.level = 1; + try { + const js = highlightLines("const s = 'str';\nconst r = /re+/g;", 'javascript').join('\n'); + expect(js).not.toContain(`${ESC}[31m`); + expect(js).toContain(`${ESC}[34m`); // keywords stay highlighted + + const diff = highlightLines('+ added\n- removed', 'diff').join('\n'); + expect(diff).not.toContain(`${ESC}[31m`); + expect(diff).toContain(`${ESC}[32m`); // additions stay green + } finally { + chalkV4.level = prevLevel; + } + }); });