diff --git a/packages/color-diff-napi/src/index.ts b/packages/color-diff-napi/src/index.ts index afaf924ea..65a93001c 100644 --- a/packages/color-diff-napi/src/index.ts +++ b/packages/color-diff-napi/src/index.ts @@ -188,7 +188,7 @@ type Theme = { function defaultSyntaxThemeName(themeName: string): string { if (themeName.includes('ansi')) return 'ansi' - if (themeName.includes('dark')) return 'Monokai Extended' + if (themeName.includes('dark')) return 'Royal Gold Dark' return 'GitHub' } @@ -221,6 +221,35 @@ const MONOKAI_SCOPES: Record = { subst: rgb(248, 248, 242), } +// Custom dark theme for the TUI: lower saturation, richer gold accents, and +// cooler blue-green contrast so code feels more refined on black backgrounds. +const ROYAL_GOLD_DARK_SCOPES: Record = { + keyword: rgb(254, 200, 74), + _storage: rgb(135, 195, 255), + built_in: rgb(135, 195, 255), + type: rgb(135, 195, 255), + literal: rgb(224, 164, 88), + number: rgb(224, 164, 88), + string: rgb(246, 224, 176), + title: rgb(235, 200, 141), + 'title.function': rgb(235, 200, 141), + 'title.class': rgb(235, 200, 141), + 'title.class.inherited': rgb(235, 200, 141), + params: rgb(243, 240, 232), + comment: rgb(139, 125, 107), + meta: rgb(139, 125, 107), + attr: rgb(135, 195, 255), + attribute: rgb(135, 195, 255), + variable: rgb(243, 240, 232), + 'variable.language': rgb(243, 240, 232), + property: rgb(243, 240, 232), + operator: rgb(231, 185, 76), + punctuation: rgb(229, 223, 211), + symbol: rgb(224, 164, 88), + regexp: rgb(246, 224, 176), + subst: rgb(229, 223, 211), +} + // highlight.js scope → syntect GitHub-light foreground (measured from Rust) const GITHUB_SCOPES: Record = { keyword: rgb(167, 29, 93), @@ -286,6 +315,18 @@ const ANSI_SCOPES: Record = { meta: ansiIdx(8), } +// Brand colors for diff highlighting +const BRAND_DIFF_RED = rgb(162, 0, 67) +const BRAND_DIFF_GREEN = rgb(34, 139, 34) +const BRAND_DIFF_RED_DARK_LINE = rgb(92, 0, 38) +const BRAND_DIFF_RED_DARK_WORD = rgb(132, 0, 54) +const BRAND_DIFF_GREEN_DARK_LINE = rgb(10, 74, 41) +const BRAND_DIFF_GREEN_DARK_WORD = rgb(16, 110, 60) +const BRAND_DIFF_RED_LIGHT_LINE = rgb(242, 220, 230) +const BRAND_DIFF_RED_LIGHT_WORD = rgb(228, 170, 196) +const BRAND_DIFF_GREEN_LIGHT_LINE = rgb(220, 238, 220) +const BRAND_DIFF_GREEN_LIGHT_WORD = rgb(170, 214, 170) + function buildTheme(themeName: string, mode: ColorMode): Theme { const isDark = themeName.includes('dark') const isAnsi = themeName.includes('ansi') @@ -308,57 +349,57 @@ function buildTheme(themeName: string, mode: ColorMode): Theme { if (isDark) { const fg = rgb(248, 248, 242) - const deleteLine = rgb(61, 1, 0) - const deleteWord = rgb(92, 2, 0) - const deleteDecoration = rgb(220, 90, 90) + const deleteLine = BRAND_DIFF_RED_DARK_LINE + const deleteWord = BRAND_DIFF_RED_DARK_WORD + const deleteDecoration = BRAND_DIFF_RED if (isDaltonized) { return { addLine: tc ? rgb(0, 27, 41) : ansiIdx(17), addWord: tc ? rgb(0, 48, 71) : ansiIdx(24), addDecoration: rgb(81, 160, 200), - deleteLine, - deleteWord, - deleteDecoration, + deleteLine: rgb(61, 1, 0), + deleteWord: rgb(92, 2, 0), + deleteDecoration: rgb(220, 90, 90), foreground: fg, background: DEFAULT_BG, - scopes: MONOKAI_SCOPES, + scopes: ROYAL_GOLD_DARK_SCOPES, } } return { - addLine: tc ? rgb(2, 40, 0) : ansiIdx(22), - addWord: tc ? rgb(4, 71, 0) : ansiIdx(28), - addDecoration: rgb(80, 200, 80), + addLine: tc ? BRAND_DIFF_GREEN_DARK_LINE : BRAND_DIFF_GREEN_DARK_LINE, + addWord: tc ? BRAND_DIFF_GREEN_DARK_WORD : BRAND_DIFF_GREEN_DARK_WORD, + addDecoration: BRAND_DIFF_GREEN, deleteLine, deleteWord, deleteDecoration, foreground: fg, background: DEFAULT_BG, - scopes: MONOKAI_SCOPES, + scopes: ROYAL_GOLD_DARK_SCOPES, } } // light const fg = rgb(51, 51, 51) - const deleteLine = rgb(255, 220, 220) - const deleteWord = rgb(255, 199, 199) - const deleteDecoration = rgb(207, 34, 46) + const deleteLine = BRAND_DIFF_RED_LIGHT_LINE + const deleteWord = BRAND_DIFF_RED_LIGHT_WORD + const deleteDecoration = BRAND_DIFF_RED if (isDaltonized) { return { - addLine: rgb(219, 237, 255), - addWord: rgb(179, 217, 255), - addDecoration: rgb(36, 87, 138), - deleteLine, - deleteWord, - deleteDecoration, + addLine: BRAND_DIFF_GREEN_LIGHT_LINE, + addWord: BRAND_DIFF_GREEN_LIGHT_WORD, + addDecoration: BRAND_DIFF_GREEN, + deleteLine: rgb(255, 220, 220), + deleteWord: rgb(255, 199, 199), + deleteDecoration: rgb(207, 34, 46), foreground: fg, background: DEFAULT_BG, scopes: GITHUB_SCOPES, } } return { - addLine: rgb(220, 255, 220), - addWord: rgb(178, 255, 178), - addDecoration: rgb(36, 138, 61), + addLine: BRAND_DIFF_GREEN_LIGHT_LINE, + addWord: BRAND_DIFF_GREEN_LIGHT_WORD, + addDecoration: BRAND_DIFF_GREEN, deleteLine, deleteWord, deleteDecoration, diff --git a/src/components/FullscreenLayout.tsx b/src/components/FullscreenLayout.tsx index 8502e46de..2c8a277a9 100644 --- a/src/components/FullscreenLayout.tsx +++ b/src/components/FullscreenLayout.tsx @@ -398,7 +398,7 @@ export function FullscreenLayout({ ref={scrollRef} flexGrow={1} flexDirection="column" - paddingTop={padCollapsed ? 0 : 1} + paddingTop={0} stickyScroll > diff --git a/src/components/HighlightedCode.tsx b/src/components/HighlightedCode.tsx index 47f7271bc..6d1f02de8 100644 --- a/src/components/HighlightedCode.tsx +++ b/src/components/HighlightedCode.tsx @@ -1,6 +1,5 @@ import * as React from 'react' import { memo, useEffect, useMemo, useRef, useState } from 'react' -import { useSettings } from '../hooks/useSettings.js' import { Ansi, Box, @@ -34,20 +33,14 @@ export const HighlightedCode = memo(function HighlightedCode({ const ref = useRef(null) const [measuredWidth, setMeasuredWidth] = useState(width || DEFAULT_WIDTH) const [theme] = useTheme() - const settings = useSettings() - const syntaxHighlightingDisabled = - settings.syntaxHighlightingDisabled ?? false const colorFile = useMemo(() => { - if (syntaxHighlightingDisabled) { - return null - } const ColorFile = expectColorFile() if (!ColorFile) { return null } return new ColorFile(code, filePath) - }, [code, filePath, syntaxHighlightingDisabled]) + }, [code, filePath]) useEffect(() => { if (!width && ref.current) { @@ -69,7 +62,7 @@ export const HighlightedCode = memo(function HighlightedCode({ // line number (max_digits = lineCount.toString().length) + space. No marker // column like the diff path. Wrap in so fullscreen selection // yields clean code without line numbers. Only split in fullscreen mode - // (~4× DOM nodes + sliceAnsi cost); non-fullscreen uses terminal-native + // (~4x DOM nodes + sliceAnsi cost); non-fullscreen uses terminal-native // selection where noSelect is meaningless. const gutterWidth = useMemo(() => { if (!isFullscreenEnvEnabled()) return 0 @@ -96,7 +89,7 @@ export const HighlightedCode = memo(function HighlightedCode({ code={code} filePath={filePath} dim={dim} - skipColoring={syntaxHighlightingDisabled} + skipColoring={false} /> )} diff --git a/src/components/Spinner/GlimmerMessage.tsx b/src/components/Spinner/GlimmerMessage.tsx index 3e488f9a1..2b8cc3c9b 100644 --- a/src/components/Spinner/GlimmerMessage.tsx +++ b/src/components/Spinner/GlimmerMessage.tsx @@ -16,8 +16,6 @@ type Props = { stalledIntensity?: number } -const ERROR_RED = { r: 171, g: 43, b: 63 } - export function GlimmerMessage({ message, mode, @@ -25,7 +23,6 @@ export function GlimmerMessage({ glimmerIndex, flashOpacity, shimmerColor, - stalledIntensity = 0, }: Props): React.ReactNode { const [themeName] = useTheme() const theme = getTheme(themeName) @@ -43,36 +40,6 @@ export function GlimmerMessage({ if (!message) return null - // When stalled, show text that smoothly transitions to red - if (stalledIntensity > 0) { - const baseColorStr = theme[messageColor] - const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null - - if (baseRGB) { - const interpolated = interpolateColor( - baseRGB, - ERROR_RED, - stalledIntensity, - ) - const color = toRGBColor(interpolated) - return ( - <> - {message} - - - ) - } - - // Fallback for ANSI themes: use messageColor until fully stalled, then error - const color = stalledIntensity > 0.5 ? 'error' : messageColor - return ( - <> - {message} - - - ) - } - // tool-use mode: all chars flash with the same opacity, so render as a // single instead of N individual FlashingChar components. if (mode === 'tool-use') { diff --git a/src/components/Spinner/SpinnerAnimationRow.tsx b/src/components/Spinner/SpinnerAnimationRow.tsx index 93b2fc64a..da7f98d81 100644 --- a/src/components/Spinner/SpinnerAnimationRow.tsx +++ b/src/components/Spinner/SpinnerAnimationRow.tsx @@ -334,7 +334,6 @@ export function SpinnerAnimationRow({ @@ -345,7 +344,6 @@ export function SpinnerAnimationRow({ glimmerIndex={glimmerIndex} flashOpacity={flashOpacity} shimmerColor={shimmerColor} - stalledIntensity={overrideColor ? 0 : stalledIntensity} /> {status} diff --git a/src/components/Spinner/SpinnerGlyph.tsx b/src/components/Spinner/SpinnerGlyph.tsx index 242d05971..d389cf75c 100644 --- a/src/components/Spinner/SpinnerGlyph.tsx +++ b/src/components/Spinner/SpinnerGlyph.tsx @@ -1,12 +1,7 @@ import * as React from 'react' -import { Box, Text, useTheme } from '../../ink.js' -import { getTheme, type Theme } from '../../utils/theme.js' -import { - getDefaultCharacters, - interpolateColor, - parseRGB, - toRGBColor, -} from './utils.js' +import { Box, Text } from '../../ink.js' +import type { Theme } from '../../utils/theme.js' +import { getDefaultCharacters } from './utils.js' const DEFAULT_CHARACTERS = getDefaultCharacters() @@ -17,7 +12,6 @@ const SPINNER_FRAMES = [ const REDUCED_MOTION_DOT = '●' const REDUCED_MOTION_CYCLE_MS = 2000 // 2-second cycle: 1s visible, 1s dim -const ERROR_RED = { r: 171, g: 43, b: 63 } type Props = { frame: number @@ -30,13 +24,9 @@ type Props = { export function SpinnerGlyph({ frame, messageColor, - stalledIntensity = 0, reducedMotion = false, time = 0, }: Props): React.ReactNode { - const [themeName] = useTheme() - const theme = getTheme(themeName) - // Reduced motion: slowly flashing orange dot if (reducedMotion) { const isDim = Math.floor(time / (REDUCED_MOTION_CYCLE_MS / 2)) % 2 === 1 @@ -51,33 +41,6 @@ export function SpinnerGlyph({ const spinnerChar = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] - // Smoothly interpolate from current color to red when stalled - if (stalledIntensity > 0) { - const baseColorStr = theme[messageColor] - const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null - - if (baseRGB) { - const interpolated = interpolateColor( - baseRGB, - ERROR_RED, - stalledIntensity, - ) - return ( - - {spinnerChar} - - ) - } - - // Fallback for ANSI themes - const color = stalledIntensity > 0.5 ? 'error' : messageColor - return ( - - {spinnerChar} - - ) - } - return ( {spinnerChar} diff --git a/src/components/ThemePicker.tsx b/src/components/ThemePicker.tsx index b14bcfd2c..1cc0bb85f 100644 --- a/src/components/ThemePicker.tsx +++ b/src/components/ThemePicker.tsx @@ -10,11 +10,7 @@ import { useThemeSetting, } from '../ink.js' import { useRegisterKeybindingContext } from '../keybindings/KeybindingContext.js' -import { useKeybinding } from '../keybindings/useKeybinding.js' -import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' -import { useAppState, useSetAppState } from '../state/AppState.js' import { gracefulShutdown } from '../utils/gracefulShutdown.js' -import { updateSettingsForSource } from '../utils/settings/settings.js' import type { ThemeSetting } from '../utils/theme.js' import { Select } from './CustomSelect/index.js' import { Byline } from './design-system/Byline.js' @@ -53,35 +49,10 @@ export function ThemePicker({ const syntaxTheme = colorModuleUnavailableReason === null ? getSyntaxTheme(theme) : null const { setPreviewTheme, savePreview, cancelPreview } = usePreviewTheme() - const syntaxHighlightingDisabled = - useAppState(s => s.settings.syntaxHighlightingDisabled) ?? false - const setAppState = useSetAppState() // Register ThemePicker context so its keybindings take precedence over Global useRegisterKeybindingContext('ThemePicker') - const syntaxToggleShortcut = useShortcutDisplay( - 'theme:toggleSyntaxHighlighting', - 'ThemePicker', - 'ctrl+t', - ) - - useKeybinding( - 'theme:toggleSyntaxHighlighting', - () => { - if (colorModuleUnavailableReason === null) { - const newValue = !syntaxHighlightingDisabled - updateSettingsForSource('userSettings', { - syntaxHighlightingDisabled: newValue, - }) - setAppState(prev => ({ - ...prev, - settings: { ...prev.settings, syntaxHighlightingDisabled: newValue }, - })) - } - }, - { context: 'ThemePicker' }, - ) // Always call the hook to follow React rules, but conditionally assign the exit handler const exitState = useExitOnCtrlCDWithKeybindings( skipExitHandling ? () => {} : undefined, @@ -115,7 +86,7 @@ export function ThemePicker({ {showIntroText ? ( - Let's get started. + Let's get started. ) : ( Theme @@ -184,12 +155,10 @@ export function ThemePicker({ {' '} {colorModuleUnavailableReason === 'env' - ? `Syntax highlighting disabled (via CLAUDE_CODE_SYNTAX_HIGHLIGHT=${process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT})` - : syntaxHighlightingDisabled - ? `Syntax highlighting disabled (${syntaxToggleShortcut} to enable)` - : syntaxTheme - ? `Syntax theme: ${syntaxTheme.theme}${syntaxTheme.source ? ` (from ${syntaxTheme.source})` : ''} (${syntaxToggleShortcut} to disable)` - : `Syntax highlighting enabled (${syntaxToggleShortcut} to disable)`} + ? `Syntax highlighting unavailable (via CLAUDE_CODE_SYNTAX_HIGHLIGHT=${process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT})` + : syntaxTheme + ? `Syntax theme: ${syntaxTheme.theme}${syntaxTheme.source ? ` (from ${syntaxTheme.source})` : ''}` + : 'Syntax highlighting enabled'} diff --git a/src/keybindings/defaultBindings.ts b/src/keybindings/defaultBindings.ts index 8629809d9..22ff9c363 100644 --- a/src/keybindings/defaultBindings.ts +++ b/src/keybindings/defaultBindings.ts @@ -189,7 +189,6 @@ export const DEFAULT_BINDINGS: KeybindingBlock[] = [ { context: 'ThemePicker', bindings: { - 'ctrl+t': 'theme:toggleSyntaxHighlighting', }, }, { diff --git a/src/keybindings/schema.ts b/src/keybindings/schema.ts index 3e61d63a5..66fad71b6 100644 --- a/src/keybindings/schema.ts +++ b/src/keybindings/schema.ts @@ -120,7 +120,6 @@ export const KEYBINDING_ACTIONS = [ // Task/agent actions 'task:background', // Theme picker actions - 'theme:toggleSyntaxHighlighting', // Help menu actions 'help:dismiss', // Attachment navigation (select dialog image attachments) diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts index b2c673e6d..f13681e9c 100644 --- a/src/utils/markdown.ts +++ b/src/utils/markdown.ts @@ -87,7 +87,9 @@ export function formatToken( } case 'codespan': { // inline code - return color('permission', theme)(token.text) + return color(theme?.startsWith('dark') ? '#FEC84A' : 'permission', theme)( + token.text, + ) } case 'em': return chalk.italic( diff --git a/src/utils/theme.ts b/src/utils/theme.ts index 9491dca65..af18c4814 100644 --- a/src/utils/theme.ts +++ b/src/utils/theme.ts @@ -1,6 +1,11 @@ import chalk, { Chalk } from 'chalk' import { env } from './env.js' +const BRAND_COLOR = 'rgb(88,190,255)' +const BRAND_COLOR_LIGHT = 'rgb(135,210,255)' // Lighter for shimmer +const BRAND_RED = 'rgb(162,0,67)' +const BRAND_GREEN = 'rgb(34,139,34)' + export type Theme = { autoAccept: string bashBorder: string @@ -115,8 +120,8 @@ export type ThemeSetting = (typeof THEME_SETTINGS)[number] const lightTheme: Theme = { autoAccept: 'rgb(135,0,255)', // Electric violet bashBorder: 'rgb(255,0,135)', // Vibrant pink - claude: 'rgb(215,119,87)', // Claude orange - claudeShimmer: 'rgb(245,149,117)', // Lighter claude orange for shimmer effect + claude: BRAND_COLOR, + claudeShimmer: BRAND_COLOR_LIGHT, claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(87,105,247)', // Medium blue for system spinner claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(117,135,255)', // Lighter blue for system spinner shimmer permission: 'rgb(87,105,247)', // Medium blue @@ -133,17 +138,17 @@ const lightTheme: Theme = { suggestion: 'rgb(87,105,247)', // Medium blue remember: 'rgb(0,0,255)', // Blue background: 'rgb(0,153,153)', // Cyan - success: 'rgb(44,122,57)', // Green - error: 'rgb(171,43,63)', // Red + success: BRAND_GREEN, + error: BRAND_RED, warning: 'rgb(150,108,30)', // Amber merged: 'rgb(135,0,255)', // Electric violet (matches autoAccept) warningShimmer: 'rgb(200,158,80)', // Lighter amber for shimmer effect - diffAdded: 'rgb(105,219,124)', // Light green - diffRemoved: 'rgb(255,168,180)', // Light red - diffAddedDimmed: 'rgb(199,225,203)', // Very light green - diffRemovedDimmed: 'rgb(253,210,216)', // Very light red - diffAddedWord: 'rgb(47,157,68)', // Medium green - diffRemovedWord: 'rgb(209,69,75)', // Medium red + diffAdded: 'rgb(153,204,255)', // Light blue instead of green + diffRemoved: 'rgb(255,204,204)', // Light red + diffAddedDimmed: 'rgb(209,231,253)', // Very light blue + diffRemovedDimmed: 'rgb(255,233,233)', // Very light red + diffAddedWord: 'rgb(51,102,204)', // Medium blue (less intense than deep blue) + diffRemovedWord: 'rgb(153,51,51)', // Softer red (less intense than deep red) // Agent colors red_FOR_SUBAGENTS_ONLY: 'rgb(220,38,38)', // Red 600 blue_FOR_SUBAGENTS_ONLY: 'rgb(37,99,235)', // Blue 600 @@ -158,7 +163,7 @@ const lightTheme: Theme = { // Chrome colors chromeYellow: 'rgb(251,188,4)', // Chrome yellow // TUI V2 colors - clawd_body: 'rgb(215,119,87)', + clawd_body: BRAND_COLOR, clawd_background: 'rgb(0,0,0)', userMessageBackground: 'rgb(240, 240, 240)', // Slightly darker grey for optimal contrast userMessageBackgroundHover: 'rgb(252, 252, 252)', // ≥250 to quantize distinct from base at 256-color level @@ -173,7 +178,7 @@ const lightTheme: Theme = { fastModeShimmer: 'rgb(255,150,50)', // Lighter orange for shimmer // Brief/assistant mode briefLabelYou: 'rgb(37,99,235)', // Blue - briefLabelClaude: 'rgb(215,119,87)', // Brand orange + briefLabelClaude: BRAND_COLOR, rainbow_red: 'rgb(235,95,87)', rainbow_orange: 'rgb(245,139,87)', rainbow_yellow: 'rgb(250,195,95)', @@ -254,7 +259,7 @@ const lightAnsiTheme: Theme = { fastMode: 'ansi:red', fastModeShimmer: 'ansi:redBright', briefLabelYou: 'ansi:blue', - briefLabelClaude: 'ansi:redBright', + briefLabelClaude: BRAND_COLOR, rainbow_red: 'ansi:red', rainbow_orange: 'ansi:redBright', rainbow_yellow: 'ansi:yellow', @@ -335,7 +340,7 @@ const darkAnsiTheme: Theme = { fastMode: 'ansi:redBright', fastModeShimmer: 'ansi:redBright', briefLabelYou: 'ansi:blueBright', - briefLabelClaude: 'ansi:redBright', + briefLabelClaude: BRAND_COLOR, rainbow_red: 'ansi:red', rainbow_orange: 'ansi:redBright', rainbow_yellow: 'ansi:yellow', @@ -359,8 +364,8 @@ const darkAnsiTheme: Theme = { const lightDaltonizedTheme: Theme = { autoAccept: 'rgb(135,0,255)', // Electric violet bashBorder: 'rgb(0,102,204)', // Blue instead of pink - claude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia - claudeShimmer: 'rgb(255,183,101)', // Lighter orange for shimmer effect + claude: BRAND_COLOR, + claudeShimmer: BRAND_COLOR_LIGHT, claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(51,102,255)', // Bright blue for system spinner claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(101,152,255)', // Lighter bright blue for system spinner shimmer permission: 'rgb(51,102,255)', // Bright blue @@ -382,12 +387,12 @@ const lightDaltonizedTheme: Theme = { warning: 'rgb(255,153,0)', // Orange adjusted for deuteranopia merged: 'rgb(135,0,255)', // Electric violet (matches autoAccept) warningShimmer: 'rgb(255,183,50)', // Lighter orange for shimmer - diffAdded: 'rgb(153,204,255)', // Light blue instead of green - diffRemoved: 'rgb(255,204,204)', // Light red - diffAddedDimmed: 'rgb(209,231,253)', // Very light blue - diffRemovedDimmed: 'rgb(255,233,233)', // Very light red - diffAddedWord: 'rgb(51,102,204)', // Medium blue (less intense than deep blue) - diffRemovedWord: 'rgb(153,51,51)', // Softer red (less intense than deep red) + diffAdded: 'rgb(170,214,170)', + diffRemoved: 'rgb(228,170,196)', + diffAddedDimmed: 'rgb(220,238,220)', + diffRemovedDimmed: 'rgb(242,220,230)', + diffAddedWord: BRAND_GREEN, + diffRemovedWord: BRAND_RED, // Agent colors (daltonism-friendly) red_FOR_SUBAGENTS_ONLY: 'rgb(204,0,0)', // Pure red blue_FOR_SUBAGENTS_ONLY: 'rgb(0,102,204)', // Pure blue @@ -402,7 +407,7 @@ const lightDaltonizedTheme: Theme = { // Chrome colors chromeYellow: 'rgb(251,188,4)', // Chrome yellow // TUI V2 colors - clawd_body: 'rgb(215,119,87)', + clawd_body: BRAND_COLOR, clawd_background: 'rgb(0,0,0)', userMessageBackground: 'rgb(220, 220, 220)', // Slightly darker grey for optimal contrast userMessageBackgroundHover: 'rgb(232, 232, 232)', // ≥230 to quantize distinct from base at 256-color level @@ -416,7 +421,7 @@ const lightDaltonizedTheme: Theme = { fastMode: 'rgb(255,106,0)', // Electric orange (color-blind safe) fastModeShimmer: 'rgb(255,150,50)', // Lighter orange for shimmer briefLabelYou: 'rgb(37,99,235)', // Blue - briefLabelClaude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia (matches claude) + briefLabelClaude: BRAND_COLOR, rainbow_red: 'rgb(235,95,87)', rainbow_orange: 'rgb(245,139,87)', rainbow_yellow: 'rgb(250,195,95)', @@ -440,12 +445,12 @@ const lightDaltonizedTheme: Theme = { const darkTheme: Theme = { autoAccept: 'rgb(175,135,255)', // Electric violet bashBorder: 'rgb(253,93,177)', // Bright pink - claude: 'rgb(215,119,87)', // Claude orange - claudeShimmer: 'rgb(235,159,127)', // Lighter claude orange for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(147,165,255)', // Blue for system spinner - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(177,195,255)', // Lighter blue for system spinner shimmer - permission: 'rgb(177,185,249)', // Light blue-purple - permissionShimmer: 'rgb(207,215,255)', // Lighter blue-purple for shimmer + claude: BRAND_COLOR, + claudeShimmer: BRAND_COLOR_LIGHT, + claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(131,210,238)', // Light cyan-blue + claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(177,231,245)', // Lighter cyan-blue for shimmer + permission: 'rgb(131,210,238)', // Light cyan-blue + permissionShimmer: 'rgb(177,231,245)', // Lighter cyan-blue for shimmer planMode: 'rgb(72,150,140)', // Muted sage green ide: 'rgb(71,130,200)', // Muted blue promptBorder: 'rgb(136,136,136)', // Medium gray @@ -455,20 +460,20 @@ const darkTheme: Theme = { inactive: 'rgb(153,153,153)', // Light gray inactiveShimmer: 'rgb(193,193,193)', // Lighter gray for shimmer effect subtle: 'rgb(80,80,80)', // Dark gray - suggestion: 'rgb(177,185,249)', // Light blue-purple - remember: 'rgb(177,185,249)', // Light blue-purple + suggestion: 'rgb(131,210,238)', // Light cyan-blue + remember: 'rgb(131,210,238)', // Light cyan-blue background: 'rgb(0,204,204)', // Bright cyan - success: 'rgb(78,186,101)', // Bright green - error: 'rgb(255,107,128)', // Bright red + success: BRAND_GREEN, + error: BRAND_RED, warning: 'rgb(255,193,7)', // Bright amber merged: 'rgb(175,135,255)', // Electric violet (matches autoAccept) warningShimmer: 'rgb(255,223,57)', // Lighter amber for shimmer - diffAdded: 'rgb(34,92,43)', // Dark green - diffRemoved: 'rgb(122,41,54)', // Dark red - diffAddedDimmed: 'rgb(71,88,74)', // Very dark green - diffRemovedDimmed: 'rgb(105,72,77)', // Very dark red - diffAddedWord: 'rgb(56,166,96)', // Medium green - diffRemovedWord: 'rgb(179,89,107)', // Softer red (less intense than bright red) + diffAdded: 'rgb(20,54,20)', + diffRemoved: 'rgb(74,0,31)', + diffAddedDimmed: 'rgb(38,48,38)', + diffRemovedDimmed: 'rgb(57,38,46)', + diffAddedWord: BRAND_GREEN, + diffRemovedWord: BRAND_RED, // Agent colors red_FOR_SUBAGENTS_ONLY: 'rgb(220,38,38)', // Red 600 blue_FOR_SUBAGENTS_ONLY: 'rgb(37,99,235)', // Blue 600 @@ -483,21 +488,21 @@ const darkTheme: Theme = { // Chrome colors chromeYellow: 'rgb(251,188,4)', // Chrome yellow // TUI V2 colors - clawd_body: 'rgb(215,119,87)', + clawd_body: BRAND_COLOR, clawd_background: 'rgb(0,0,0)', - userMessageBackground: 'rgb(55, 55, 55)', // Lighter grey for better visual contrast - userMessageBackgroundHover: 'rgb(70, 70, 70)', + userMessageBackground: '#0f0f0f', + userMessageBackgroundHover: '#191919', messageActionsBackground: 'rgb(44, 50, 62)', // cool gray, slight blue selectionBg: 'rgb(38, 79, 120)', // classic dark-mode selection blue (VS Code dark default); light fgs stay readable bashMessageBackgroundColor: 'rgb(65, 60, 65)', memoryBackgroundColor: 'rgb(55, 65, 70)', - rate_limit_fill: 'rgb(177,185,249)', // Light blue-purple + rate_limit_fill: 'rgb(131,210,238)', // Light cyan-blue rate_limit_empty: 'rgb(80,83,112)', // Medium blue-purple fastMode: 'rgb(255,120,20)', // Electric orange for dark bg fastModeShimmer: 'rgb(255,165,70)', // Lighter orange for shimmer briefLabelYou: 'rgb(122,180,232)', // Light blue - briefLabelClaude: 'rgb(215,119,87)', // Brand orange + briefLabelClaude: BRAND_COLOR, rainbow_red: 'rgb(235,95,87)', rainbow_orange: 'rgb(245,139,87)', rainbow_yellow: 'rgb(250,195,95)', @@ -521,8 +526,8 @@ const darkTheme: Theme = { const darkDaltonizedTheme: Theme = { autoAccept: 'rgb(175,135,255)', // Electric violet bashBorder: 'rgb(51,153,255)', // Bright blue - claude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia - claudeShimmer: 'rgb(255,183,101)', // Lighter orange for shimmer effect + claude: BRAND_COLOR, + claudeShimmer: BRAND_COLOR_LIGHT, claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(153,204,255)', // Light blue for system spinner claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(183,224,255)', // Lighter blue for system spinner shimmer permission: 'rgb(153,204,255)', // Light blue @@ -539,17 +544,17 @@ const darkDaltonizedTheme: Theme = { suggestion: 'rgb(153,204,255)', // Light blue remember: 'rgb(153,204,255)', // Light blue background: 'rgb(0,204,204)', // Bright cyan (color-blind friendly) - success: 'rgb(51,153,255)', // Blue instead of green - error: 'rgb(255,102,102)', // Bright red + success: 'rgb(0,153,204)', // Cyan-blue instead of green for deuteranopia + error: 'rgb(255,102,102)', // Bright red for better distinction warning: 'rgb(255,204,0)', // Yellow-orange for deuteranopia merged: 'rgb(175,135,255)', // Electric violet (matches autoAccept) warningShimmer: 'rgb(255,234,50)', // Lighter yellow-orange for shimmer - diffAdded: 'rgb(0,68,102)', // Dark blue - diffRemoved: 'rgb(102,0,0)', // Dark red - diffAddedDimmed: 'rgb(62,81,91)', // Dimmed blue - diffRemovedDimmed: 'rgb(62,44,44)', // Dimmed red - diffAddedWord: 'rgb(0,119,179)', // Medium blue - diffRemovedWord: 'rgb(179,0,0)', // Medium red + diffAdded: 'rgb(0,27,41)', // Dark blue instead of green + diffRemoved: 'rgb(122,41,54)', // Dark red + diffAddedDimmed: 'rgb(51,68,71)', // Very dark blue + diffRemovedDimmed: 'rgb(105,72,77)', // Very dark red + diffAddedWord: 'rgb(81,160,200)', // Medium blue + diffRemovedWord: 'rgb(179,89,107)', // Softer red // Agent colors (daltonism-friendly, dark mode) red_FOR_SUBAGENTS_ONLY: 'rgb(255,102,102)', // Bright red blue_FOR_SUBAGENTS_ONLY: 'rgb(102,178,255)', // Bright blue @@ -564,7 +569,7 @@ const darkDaltonizedTheme: Theme = { // Chrome colors chromeYellow: 'rgb(251,188,4)', // Chrome yellow // TUI V2 colors - clawd_body: 'rgb(215,119,87)', + clawd_body: BRAND_COLOR, clawd_background: 'rgb(0,0,0)', userMessageBackground: 'rgb(55, 55, 55)', // Lighter grey for better visual contrast userMessageBackgroundHover: 'rgb(70, 70, 70)', @@ -578,7 +583,7 @@ const darkDaltonizedTheme: Theme = { fastMode: 'rgb(255,120,20)', // Electric orange for dark bg (color-blind safe) fastModeShimmer: 'rgb(255,165,70)', // Lighter orange for shimmer briefLabelYou: 'rgb(122,180,232)', // Light blue - briefLabelClaude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia (matches claude) + briefLabelClaude: BRAND_COLOR, rainbow_red: 'rgb(235,95,87)', rainbow_orange: 'rgb(245,139,87)', rainbow_yellow: 'rgb(250,195,95)',