tweak(tui): remove italics from thinking labels (#28737)

This commit is contained in:
Aiden Cline
2026-05-21 22:22:51 -05:00
committed by GitHub
parent ad1d14775d
commit 3e931152d1
2 changed files with 12 additions and 5 deletions

View File

@@ -49,6 +49,7 @@ type Theme = TuiThemeCurrent & {
_hasSelectedListItemText: boolean _hasSelectedListItemText: boolean
} }
type ThemeColor = Exclude<keyof TuiThemeCurrent, "thinkingOpacity"> type ThemeColor = Exclude<keyof TuiThemeCurrent, "thinkingOpacity">
type SyntaxStyleOverrides = Record<string, { italic?: boolean }>
export function selectedForeground(theme: Theme, bg?: RGBA): RGBA { export function selectedForeground(theme: Theme, bg?: RGBA): RGBA {
// If theme explicitly defines selectedListItemText, use it // If theme explicitly defines selectedListItemText, use it
@@ -718,16 +719,18 @@ export function generateSyntax(theme: Theme) {
return SyntaxStyle.fromTheme(getSyntaxRules(theme)) return SyntaxStyle.fromTheme(getSyntaxRules(theme))
} }
export function generateSubtleSyntax(theme: Theme) { export function generateSubtleSyntax(theme: Theme, overrides?: SyntaxStyleOverrides) {
const rules = getSyntaxRules(theme) const rules = getSyntaxRules(theme)
return SyntaxStyle.fromTheme( return SyntaxStyle.fromTheme(
rules.map((rule) => { rules.map((rule) => {
const override = rule.scope.reduce((acc, scope) => ({ ...acc, ...overrides?.[scope] }), {})
if (rule.style.foreground) { if (rule.style.foreground) {
const fg = rule.style.foreground const fg = rule.style.foreground
return { return {
...rule, ...rule,
style: { style: {
...rule.style, ...rule.style,
...override,
foreground: RGBA.fromInts( foreground: RGBA.fromInts(
Math.round(fg.r * 255), Math.round(fg.r * 255),
Math.round(fg.g * 255), Math.round(fg.g * 255),

View File

@@ -21,7 +21,7 @@ import { useSync } from "@tui/context/sync"
import { useEvent } from "@tui/context/event" import { useEvent } from "@tui/context/event"
import { SplitBorder } from "@tui/component/border" import { SplitBorder } from "@tui/component/border"
import { Spinner } from "@tui/component/spinner" import { Spinner } from "@tui/component/spinner"
import { selectedForeground, useTheme } from "@tui/context/theme" import { generateSubtleSyntax, selectedForeground, useTheme } from "@tui/context/theme"
import { BoxRenderable, ScrollBoxRenderable, addDefaultParsers, TextAttributes, RGBA } from "@opentui/core" import { BoxRenderable, ScrollBoxRenderable, addDefaultParsers, TextAttributes, RGBA } from "@opentui/core"
import { Prompt, type PromptRef } from "@tui/component/prompt" import { Prompt, type PromptRef } from "@tui/component/prompt"
import type { import type {
@@ -1497,7 +1497,7 @@ const PART_MAPPING = {
} }
function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: AssistantMessage }) { function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: AssistantMessage }) {
const { theme, subtleSyntax } = useTheme() const { theme } = useTheme()
const ctx = use() const ctx = use()
// Collapsed by default in hide mode: a single line throughout, so the // Collapsed by default in hide mode: a single line throughout, so the
// layout never shifts. Click to open the full markdown block, click to close. // layout never shifts. Click to open the full markdown block, click to close.
@@ -1519,6 +1519,8 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
// blocks. Surface the title both while streaming and after settling so the // blocks. Surface the title both while streaming and after settling so the
// collapsed line carries real signal, not just a duration. // collapsed line carries real signal, not just a duration.
const title = createMemo(() => reasoningTitle(content())) const title = createMemo(() => reasoningTitle(content()))
// Keep markdown emphasis for the existing thinking color/concealment, but render it without italics.
const syntax = createMemo(() => generateSubtleSyntax(theme, { "markup.italic": { italic: false } }))
const toggle = () => { const toggle = () => {
if (!inMinimal()) return if (!inMinimal()) return
@@ -1535,7 +1537,9 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
filetype="markdown" filetype="markdown"
drawUnstyledText={false} drawUnstyledText={false}
streaming={true} streaming={true}
syntaxStyle={subtleSyntax()} syntaxStyle={syntax()}
// `_Thinking:_`/`_Thought:_` still drives markdown emphasis color and conceals the underscores;
// the syntax override above removes only the italic attribute from that emphasis token.
content={(inMinimal() ? "- " : "") + (isDone() ? "_Thought:_ " : "_Thinking:_ ") + content()} content={(inMinimal() ? "- " : "") + (isDone() ? "_Thought:_ " : "_Thinking:_ ") + content()}
conceal={ctx.conceal()} conceal={ctx.conceal()}
fg={theme.textMuted} fg={theme.textMuted}
@@ -1563,7 +1567,7 @@ function CollapsedReasoningText(props: { title: string | null; duration: number
return ( return (
<text fg={theme.warning} wrapMode="none"> <text fg={theme.warning} wrapMode="none">
<span style={{ fg: theme.warning, italic: true }}> <span style={{ fg: theme.warning }}>
{props.title ? "+ Thought: " + props.title + " · " + duration() : "+ Thought: " + duration()} {props.title ? "+ Thought: " + props.title + " · " + duration() : "+ Thought: " + duration()}
</span> </span>
</text> </text>