Skip to content

fix(tui): apply textStrong color to markdown bold spans#1879

Open
C0d3N1nja97342 wants to merge 2 commits into
MoonshotAI:mainfrom
C0d3N1nja97342:fix/tui-markdown-bold-uses-textstrong
Open

fix(tui): apply textStrong color to markdown bold spans#1879
C0d3N1nja97342 wants to merge 2 commits into
MoonshotAI:mainfrom
C0d3N1nja97342:fix/tui-markdown-bold-uses-textstrong

Conversation

@C0d3N1nja97342

Copy link
Copy Markdown

Related Issue

Resolves #1872

Problem

See linked issue. Summary: TUI markdown bold in assistant messages renders as dark gray on dark terminal backgrounds — nearly unreadable — even though the active theme's textStrong token (documented as "emphasised / bold text") is near-white. The reporter verified this against both the built-in dark palette (textStrong #F5F5F5) and a custom nord theme, ruled out iTerm2 profile settings (bold color already white, "use bright bold" on, minimum contrast off), and confirmed it reproduces on 0.27.0.

Root cause is in the pi-tui MarkdownTheme adapter at apps/kimi-code/src/tui/theme/pi-tui-theme.ts:43:

bold: (text) => chalk.bold(text),

That emits only the SGR bold code (ESC[1m) with no foreground colour. Chalk then inherits whatever fg the surrounding style left set, which for a fresh markdown paragraph is nothing — and most terminals render pure "bold, no colour" as a dim/faint gray on a dark background. Because no theme token is ever queried on this path, neither the built-in dark palette nor any custom theme can influence bold rendering, matching the reporter's ruled-out list.

What changed

Route bold through textStrong, mirroring how heading is composed at line 30:

bold: (text) => chalk.bold.hex(currentTheme.color('textStrong'))(text),

The lookup happens per call, so /reload-tui and palette swaps take effect immediately — same pattern as every other adapter in this file.

Scope kept tight:

  • Only bold. italic, strikethrough, and underline on lines 44–46 have the same "SGR-only, no colour" shape but the issue doesn't cover them, and their inherited-fg behaviour is intentional in some flows (e.g. italic inside a coloured codespan, where forcing textStrong would override the codespan's primary fg). A separate follow-up can revisit those once someone actually observes the regression.
  • No new theme token, no palette changes. textStrong is already defined in ColorPalette (line 33 of colors.ts) and set on both darkColors and lightColors — the fix just wires the markdown adapter to it.
  • No callers change. Every consumer of createMarkdownTheme — pi-tui's markdown renderer — keeps the same MarkdownTheme interface.

Reviewer Test Plan

How to verify

Unit coverage in apps/kimi-code/test/tui/theme/pi-tui-theme.test.ts (new file):

  • emits the textStrong hex for bold spans so themes actually govern bold (#1872) — sets the dark palette, calls theme.bold('hello'), asserts the output contains both the truecolor foreground SGR for #F5F5F5 and the SGR bold code. Without the fix, the first assertion fails.
  • follows the active palette when the theme changes (#1872) — creates the theme once, flips palettes between dark and light, asserts the two bold outputs differ. Guards against a future refactor that would read textStrong once at factory time instead of per call.

Both tests set chalk.level = 3 for their duration to keep vitest's non-TTY environment from stripping the SGRs we're asserting on, matching the pattern in banner.test.ts / footer.test.ts / server.test.ts.

Ran locally:

  • apps/kimi-code/test/tui/theme/ — 7/7 pass (2 new + 5 pre-existing).
  • oxlint on the two touched files — 0 warnings, 0 errors.

For a manual smoke: enable a dark palette, send any prompt whose response contains **bold** markdown, and observe the bold spans now render at textStrong brightness (near-white on the built-in dark) instead of dark-gray-on-black.

Evidence (Before & After)

The reporter offered screenshots on request; I don't have a live model to reproduce the dialog myself, but the deterministic mechanism above (SGR without fg → terminal default dim rendering) is what the two unit tests pin.

Tested on

OS Status
🍏 macOS ⚠️
🪟 Windows
🐧 Linux ⚠️

Environment

vitest run test/tui/theme/pi-tui-theme.test.ts and oxlint locally.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. — added .changeset/fix-tui-markdown-bold-uses-textstrong.md (patch on @moonshot-ai/kimi-code, since this changes user-visible CLI rendering).
  • Ran gen-docs skill, or this PR needs no doc update. — the touched behaviour is described exactly as the docs already promise (textStrong = "emphasised / bold text"). No docs page mentions the old "no-colour" behaviour, so no doc changes needed.

The pi-tui MarkdownTheme adapter's `bold` handler was `chalk.bold(text)` —
it emitted only the SGR bold code and no foreground color. Chalk then
inherited whatever fg the surrounding style left set (often none), and
most terminals rendered bold as a dim/faint gray on dark backgrounds,
nearly unreadable and unresponsive to theme changes. The palette already
defines a `textStrong` token documented as "emphasised / bold text", so
route bold through it: `chalk.bold.hex(currentTheme.color('textStrong'))`,
mirroring how `heading` is composed. Bold now tracks `textStrong` on both
the built-in dark theme and any custom theme, and `/reload-tui` picks up
palette swaps because the lookup happens per call.

Only touch the `bold` handler — italic/strikethrough/underline are not
covered by the issue and their inherited-fg behavior is intentional in
some flows (e.g. italic inside a colored codespan).

Fixes MoonshotAI#1872
@changeset-bot

changeset-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 65958d4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49910738da

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// — nearly unreadable, and unresponsive to theme changes. `textStrong`
// is defined as "emphasised / bold text" in the palette, so this makes
// the token actually govern markdown bold spans. See #1872.
bold: (text) => chalk.bold.hex(currentTheme.color('textStrong'))(text),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep structural bold from overriding heading colors

When rendering any Markdown heading, packages/pi-tui/src/components/markdown.ts:345-347 calls theme.heading(theme.bold(...)); this callback now embeds textStrong inside the heading's text wrapper, so the inner foreground code wins and every heading is rendered with textStrong rather than the documented text token. Custom themes that intentionally give headings a distinct color therefore stop working. Preserve contextual foreground colors for structural bold (or adjust the heading composition), while applying textStrong only to actual strong spans.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this is a real regression and I've addressed it in 65958d4.

You're right: packages/pi-tui/src/components/markdown.ts composes headings as theme.heading(theme.bold(text)) (lines 356–358), and pinning textStrong on theme.bold let the inner SGR override the outer heading wrapper's fg. Same pattern applies to table headers (line 833) and inline style contexts (lines 275/307) — theme.bold is expected to be structural (SGR bold code only, no fg) so an outer wrapper's colour can win.

The fix splits the two roles:

  • Added an optional strong handler to the MarkdownTheme interface, falling back to bold for themes that don't define it.
  • Updated pi-tui's case "strong" renderer to prefer strong.
  • Reverted bold in createMarkdownTheme to chalk.bold(text) (no fg), matching its structural role.
  • Wired the new strong handler to chalk.bold.hex(currentTheme.color('textStrong')) — the actual fix reporter TUI: Markdown bold in assistant messages renders dark-on-dark, ignoring theme textStrong #1872 asked for, now scoped to real **strong** spans.

Added a third regression test asserting theme.bold('hello') contains no truecolor fg SGR (\[38;2;), so a future refactor that pins fg on bold can no longer land silently. The existing 67 pi-tui markdown tests still pass under node:test.

… colours

Address codex review on MoonshotAI#1879: pinning textStrong on `theme.bold` broke
heading composition. pi-tui composes headings as
`theme.heading(theme.bold(text))` (markdown.ts:356-358), so any fg
color set inside `bold` bleeds through and overrides the outer
`heading` wrapper's fg — a custom theme that sets a distinct heading
colour via `text` no longer works. Table headers (markdown.ts:833) and
inline style contexts (markdown.ts:275, 307) show the same pattern:
`theme.bold` is expected to be *structural* (SGR bold code only, no fg),
so an outer wrapper's colour can win.

Split them:

- Add an optional `strong` handler to `MarkdownTheme`, falling back to
  `bold` for themes that don't define it.
- Update the `case "strong"` renderer in pi-tui to prefer `strong`.
- Revert `bold` in `createMarkdownTheme` to `chalk.bold(text)` — no fg,
  as its role demands.
- Wire the new `strong` handler to
  `chalk.bold.hex(currentTheme.color('textStrong'))`, which is the actual
  fix reporter MoonshotAI#1872 asked for.

Regression tests: `strong` emits the textStrong hex, `bold` emits no
truecolor fg SGR, both track `currentTheme.setPalette()` at call time.

Fixes MoonshotAI#1872 (still). Refs codex review.
@C0d3N1nja97342

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 65958d4158

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI: Markdown bold in assistant messages renders dark-on-dark, ignoring theme textStrong

1 participant