Revert "Update semantic color tokens" (#6365)

This commit is contained in:
Jacob Richman 2025-08-15 20:18:31 -07:00 committed by GitHub
parent d57cc0b930
commit 6c1373c332
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 502 additions and 606 deletions

View File

@ -38,7 +38,7 @@ import { EditorSettingsDialog } from './components/EditorSettingsDialog.js';
import { FolderTrustDialog } from './components/FolderTrustDialog.js'; import { FolderTrustDialog } from './components/FolderTrustDialog.js';
import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js'; import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js';
import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js'; import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
import { theme } from './semantic-colors.js'; import { Colors } from './colors.js';
import { loadHierarchicalGeminiMemory } from '../config/config.js'; import { loadHierarchicalGeminiMemory } from '../config/config.js';
import { LoadedSettings, SettingScope } from '../config/settings.js'; import { LoadedSettings, SettingScope } from '../config/settings.js';
import { Tips } from './components/Tips.js'; import { Tips } from './components/Tips.js';
@ -949,13 +949,13 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
{startupWarnings.length > 0 && ( {startupWarnings.length > 0 && (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.status.warning} borderColor={Colors.AccentYellow}
paddingX={1} paddingX={1}
marginY={1} marginY={1}
flexDirection="column" flexDirection="column"
> >
{startupWarnings.map((warning, index) => ( {startupWarnings.map((warning, index) => (
<Text key={index} color={theme.status.warning}> <Text key={index} color={Colors.AccentYellow}>
{warning} {warning}
</Text> </Text>
))} ))}
@ -991,7 +991,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
<Box flexDirection="column"> <Box flexDirection="column">
{themeError && ( {themeError && (
<Box marginBottom={1}> <Box marginBottom={1}>
<Text color={theme.status.error}>{themeError}</Text> <Text color={Colors.AccentRed}>{themeError}</Text>
</Box> </Box>
)} )}
<ThemeDialog <ThemeDialog
@ -1050,7 +1050,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
<Box flexDirection="column"> <Box flexDirection="column">
{editorError && ( {editorError && (
<Box marginBottom={1}> <Box marginBottom={1}>
<Text color={theme.status.error}>{editorError}</Text> <Text color={Colors.AccentRed}>{editorError}</Text>
</Box> </Box>
)} )}
<EditorSettingsDialog <EditorSettingsDialog
@ -1090,20 +1090,18 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
> >
<Box> <Box>
{process.env.GEMINI_SYSTEM_MD && ( {process.env.GEMINI_SYSTEM_MD && (
<Text color={theme.status.error}>|_| </Text> <Text color={Colors.AccentRed}>|_| </Text>
)} )}
{ctrlCPressedOnce ? ( {ctrlCPressedOnce ? (
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
Press Ctrl+C again to exit. Press Ctrl+C again to exit.
</Text> </Text>
) : ctrlDPressedOnce ? ( ) : ctrlDPressedOnce ? (
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
Press Ctrl+D again to exit. Press Ctrl+D again to exit.
</Text> </Text>
) : showEscapePrompt ? ( ) : showEscapePrompt ? (
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>Press Esc again to clear.</Text>
Press Esc again to clear.
</Text>
) : ( ) : (
<ContextSummaryDisplay <ContextSummaryDisplay
ideContext={ideContextState} ideContext={ideContextState}
@ -1166,7 +1164,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
{initError && streamingState !== StreamingState.Responding && ( {initError && streamingState !== StreamingState.Responding && (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.status.error} borderColor={Colors.AccentRed}
paddingX={1} paddingX={1}
marginBottom={1} marginBottom={1}
> >
@ -1174,7 +1172,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
(item) => (item) =>
item.type === 'error' && item.text?.includes(initError), item.type === 'error' && item.text?.includes(initError),
)?.text ? ( )?.text ? (
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
{ {
history.find( history.find(
(item) => (item) =>
@ -1184,10 +1182,10 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
</Text> </Text>
) : ( ) : (
<> <>
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
Initialization Error: {initError} Initialization Error: {initError}
</Text> </Text>
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
{' '} {' '}
Please check API key and configuration. Please check API key and configuration.
</Text> </Text>

View File

@ -7,7 +7,7 @@
import * as fsPromises from 'fs/promises'; import * as fsPromises from 'fs/promises';
import React from 'react'; import React from 'react';
import { Text } from 'ink'; import { Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { import {
CommandContext, CommandContext,
SlashCommand, SlashCommand,
@ -124,7 +124,7 @@ const saveCommand: SlashCommand = {
Text, Text,
null, null,
'A checkpoint with the tag ', 'A checkpoint with the tag ',
React.createElement(Text, { color: theme.text.accent }, tag), React.createElement(Text, { color: Colors.AccentPurple }, tag),
' already exists. Do you want to overwrite it?', ' already exists. Do you want to overwrite it?',
), ),
originalInvocation: { originalInvocation: {

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js'; import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
interface AboutBoxProps { interface AboutBoxProps {
@ -30,77 +30,77 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
}) => ( }) => (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
padding={1} padding={1}
marginY={1} marginY={1}
width="100%" width="100%"
> >
<Box marginBottom={1}> <Box marginBottom={1}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
About Gemini CLI About Gemini CLI
</Text> </Text>
</Box> </Box>
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
CLI Version CLI Version
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{cliVersion}</Text> <Text>{cliVersion}</Text>
</Box> </Box>
</Box> </Box>
{GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && ( {GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && (
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
Git Commit Git Commit
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{GIT_COMMIT_INFO}</Text> <Text>{GIT_COMMIT_INFO}</Text>
</Box> </Box>
</Box> </Box>
)} )}
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
Model Model
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{modelVersion}</Text> <Text>{modelVersion}</Text>
</Box> </Box>
</Box> </Box>
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
Sandbox Sandbox
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{sandboxEnv}</Text> <Text>{sandboxEnv}</Text>
</Box> </Box>
</Box> </Box>
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
OS OS
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{osVersion}</Text> <Text>{osVersion}</Text>
</Box> </Box>
</Box> </Box>
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
Auth Method Auth Method
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}> <Text>
{selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType} {selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType}
</Text> </Text>
</Box> </Box>
@ -108,19 +108,19 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
{gcpProject && ( {gcpProject && (
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
GCP Project GCP Project
</Text> </Text>
</Box> </Box>
<Box> <Box>
<Text color={theme.text.primary}>{gcpProject}</Text> <Text>{gcpProject}</Text>
</Box> </Box>
</Box> </Box>
)} )}
{ideClient && ( {ideClient && (
<Box flexDirection="row"> <Box flexDirection="row">
<Box width="35%"> <Box width="35%">
<Text bold color={theme.text.link}> <Text bold color={Colors.LightBlue}>
IDE Client IDE Client
</Text> </Text>
</Box> </Box>

View File

@ -6,7 +6,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js'; import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { LoadedSettings, SettingScope } from '../../config/settings.js'; import { LoadedSettings, SettingScope } from '../../config/settings.js';
import { AuthType } from '@google/gemini-cli-core'; import { AuthType } from '@google/gemini-cli-core';
@ -133,18 +133,14 @@ export function AuthDialog({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
padding={1} padding={1}
width="100%" width="100%"
> >
<Text bold color={theme.text.primary}> <Text bold>Get started</Text>
Get started
</Text>
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.primary}> <Text>How would you like to authenticate for this project?</Text>
How would you like to authenticate for this project?
</Text>
</Box> </Box>
<Box marginTop={1}> <Box marginTop={1}>
<RadioButtonSelect <RadioButtonSelect
@ -155,19 +151,17 @@ export function AuthDialog({
</Box> </Box>
{errorMessage && ( {errorMessage && (
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.status.error}>{errorMessage}</Text> <Text color={Colors.AccentRed}>{errorMessage}</Text>
</Box> </Box>
)} )}
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.secondary}>(Use Enter to select)</Text> <Text color={Colors.Gray}>(Use Enter to select)</Text>
</Box> </Box>
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.primary}> <Text>Terms of Services and Privacy Notice for Gemini CLI</Text>
Terms of Services and Privacy Notice for Gemini CLI
</Text>
</Box> </Box>
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.link}> <Text color={Colors.AccentBlue}>
{ {
'https://github.com/google-gemini/gemini-cli/blob/main/docs/tos-privacy.md' 'https://github.com/google-gemini/gemini-cli/blob/main/docs/tos-privacy.md'
} }

View File

@ -7,7 +7,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import Spinner from 'ink-spinner'; import Spinner from 'ink-spinner';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useKeypress } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js';
interface AuthInProgressProps { interface AuthInProgressProps {
@ -40,18 +40,18 @@ export function AuthInProgress({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
padding={1} padding={1}
width="100%" width="100%"
> >
{timedOut ? ( {timedOut ? (
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
Authentication timed out. Please try again. Authentication timed out. Please try again.
</Text> </Text>
) : ( ) : (
<Box> <Box>
<Text color={theme.text.primary}> <Text>
<Spinner type="dots" /> Waiting for auth... (Press ESC or CTRL+C to <Spinner type="dots" /> Waiting for auth... (Press ESC or CTRL+C to
cancel) cancel)
</Text> </Text>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { ApprovalMode } from '@google/gemini-cli-core'; import { ApprovalMode } from '@google/gemini-cli-core';
interface AutoAcceptIndicatorProps { interface AutoAcceptIndicatorProps {
@ -22,12 +22,12 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
switch (approvalMode) { switch (approvalMode) {
case ApprovalMode.AUTO_EDIT: case ApprovalMode.AUTO_EDIT:
textColor = theme.status.success; textColor = Colors.AccentGreen;
textContent = 'accepting edits'; textContent = 'accepting edits';
subText = ' (shift + tab to toggle)'; subText = ' (shift + tab to toggle)';
break; break;
case ApprovalMode.YOLO: case ApprovalMode.YOLO:
textColor = theme.status.error; textColor = Colors.AccentRed;
textContent = 'YOLO mode'; textContent = 'YOLO mode';
subText = ' (ctrl + y to toggle)'; subText = ' (ctrl + y to toggle)';
break; break;
@ -40,7 +40,7 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
<Box> <Box>
<Text color={textColor}> <Text color={textColor}>
{textContent} {textContent}
{subText && <Text color={theme.text.secondary}>{subText}</Text>} {subText && <Text color={Colors.Gray}>{subText}</Text>}
</Text> </Text>
</Box> </Box>
); );

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
interface ConsoleSummaryDisplayProps { interface ConsoleSummaryDisplayProps {
errorCount: number; errorCount: number;
@ -25,9 +25,9 @@ export const ConsoleSummaryDisplay: React.FC<ConsoleSummaryDisplayProps> = ({
return ( return (
<Box> <Box>
{errorCount > 0 && ( {errorCount > 0 && (
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
{errorIcon} {errorCount} error{errorCount > 1 ? 's' : ''}{' '} {errorIcon} {errorCount} error{errorCount > 1 ? 's' : ''}{' '}
<Text color={theme.text.secondary}>(ctrl+o for details)</Text> <Text color={Colors.Gray}>(ctrl+o for details)</Text>
</Text> </Text>
)} )}
</Box> </Box>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core'; import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core';
import { useTerminalSize } from '../hooks/useTerminalSize.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js';
import { isNarrowWidth } from '../utils/isNarrowWidth.js'; import { isNarrowWidth } from '../utils/isNarrowWidth.js';
@ -99,9 +99,9 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
if (isNarrow) { if (isNarrow) {
return ( return (
<Box flexDirection="column"> <Box flexDirection="column">
<Text color={theme.text.secondary}>Using:</Text> <Text color={Colors.Gray}>Using:</Text>
{summaryParts.map((part, index) => ( {summaryParts.map((part, index) => (
<Text key={index} color={theme.text.secondary}> <Text key={index} color={Colors.Gray}>
{' '}- {part} {' '}- {part}
</Text> </Text>
))} ))}
@ -111,9 +111,7 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
return ( return (
<Box> <Box>
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>Using: {summaryParts.join(' | ')}</Text>
Using: {summaryParts.join(' | ')}
</Text>
</Box> </Box>
); );
}; };

View File

@ -5,7 +5,7 @@
*/ */
import { Text } from 'ink'; import { Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { tokenLimit } from '@google/gemini-cli-core'; import { tokenLimit } from '@google/gemini-cli-core';
export const ContextUsageDisplay = ({ export const ContextUsageDisplay = ({
@ -18,7 +18,7 @@ export const ContextUsageDisplay = ({
const percentage = promptTokenCount / tokenLimit(model); const percentage = promptTokenCount / tokenLimit(model);
return ( return (
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
({((1 - percentage) * 100).toFixed(0)}% context left) ({((1 - percentage) * 100).toFixed(0)}% context left)
</Text> </Text>
); );

View File

@ -6,7 +6,7 @@
import { Text } from 'ink'; import { Text } from 'ink';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useKeypress } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js';
export const DebugProfiler = () => { export const DebugProfiler = () => {
@ -31,6 +31,6 @@ export const DebugProfiler = () => {
} }
return ( return (
<Text color={theme.status.warning}>Renders: {numRenders.current} </Text> <Text color={Colors.AccentYellow}>Renders: {numRenders.current} </Text>
); );
}; };

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { ConsoleMessageItem } from '../types.js'; import { ConsoleMessageItem } from '../types.js';
import { MaxSizedBox } from './shared/MaxSizedBox.js'; import { MaxSizedBox } from './shared/MaxSizedBox.js';
@ -31,32 +31,31 @@ export const DetailedMessagesDisplay: React.FC<
flexDirection="column" flexDirection="column"
marginTop={1} marginTop={1}
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
paddingX={1} paddingX={1}
width={width} width={width}
> >
<Box marginBottom={1}> <Box marginBottom={1}>
<Text bold color={theme.text.primary}> <Text bold color={Colors.Foreground}>
Debug Console{' '} Debug Console <Text color={Colors.Gray}>(ctrl+o to close)</Text>
<Text color={theme.text.secondary}>(ctrl+o to close)</Text>
</Text> </Text>
</Box> </Box>
<MaxSizedBox maxHeight={maxHeight} maxWidth={width - borderAndPadding}> <MaxSizedBox maxHeight={maxHeight} maxWidth={width - borderAndPadding}>
{messages.map((msg, index) => { {messages.map((msg, index) => {
let textColor = theme.text.primary; let textColor = Colors.Foreground;
let icon = '\u2139'; // Information source () let icon = '\u2139'; // Information source ()
switch (msg.type) { switch (msg.type) {
case 'warn': case 'warn':
textColor = theme.status.warning; textColor = Colors.AccentYellow;
icon = '\u26A0'; // Warning sign (⚠) icon = '\u26A0'; // Warning sign (⚠)
break; break;
case 'error': case 'error':
textColor = theme.status.error; textColor = Colors.AccentRed;
icon = '\u2716'; // Heavy multiplication x (✖) icon = '\u2716'; // Heavy multiplication x (✖)
break; break;
case 'debug': case 'debug':
textColor = theme.text.secondary; textColor = Colors.Gray; // Or Colors.Gray
icon = '\u1F50D'; // Left-pointing magnifying glass (????) icon = '\u1F50D'; // Left-pointing magnifying glass (????)
break; break;
case 'log': case 'log':
@ -71,7 +70,7 @@ export const DetailedMessagesDisplay: React.FC<
<Text color={textColor} wrap="wrap"> <Text color={textColor} wrap="wrap">
{msg.content} {msg.content}
{msg.count && msg.count > 1 && ( {msg.count && msg.count > 1 && (
<Text color={theme.text.secondary}> (x{msg.count})</Text> <Text color={Colors.Gray}> (x{msg.count})</Text>
)} )}
</Text> </Text>
</Box> </Box>

View File

@ -6,7 +6,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { import {
EDITOR_DISPLAY_NAMES, EDITOR_DISPLAY_NAMES,
editorSettingsManager, editorSettingsManager,
@ -103,7 +103,7 @@ export function EditorSettingsDialog({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="row" flexDirection="row"
padding={1} padding={1}
width="100%" width="100%"
@ -111,7 +111,7 @@ export function EditorSettingsDialog({
<Box flexDirection="column" width="45%" paddingRight={2}> <Box flexDirection="column" width="45%" paddingRight={2}>
<Text bold={focusedSection === 'editor'}> <Text bold={focusedSection === 'editor'}>
{focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '} {focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '}
<Text color={theme.text.secondary}>{otherScopeModifiedMessage}</Text> <Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
items={editorItems.map((item) => ({ items={editorItems.map((item) => ({
@ -138,7 +138,7 @@ export function EditorSettingsDialog({
</Box> </Box>
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
(Use Enter to select, Tab to change focus) (Use Enter to select, Tab to change focus)
</Text> </Text>
</Box> </Box>
@ -147,17 +147,17 @@ export function EditorSettingsDialog({
<Box flexDirection="column" width="55%" paddingLeft={2}> <Box flexDirection="column" width="55%" paddingLeft={2}>
<Text bold>Editor Preference</Text> <Text bold>Editor Preference</Text>
<Box flexDirection="column" gap={1} marginTop={1}> <Box flexDirection="column" gap={1} marginTop={1}>
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
These editors are currently supported. Please note that some editors These editors are currently supported. Please note that some editors
cannot be used in sandbox mode. cannot be used in sandbox mode.
</Text> </Text>
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
Your preferred editor is:{' '} Your preferred editor is:{' '}
<Text <Text
color={ color={
mergedEditorName === 'None' mergedEditorName === 'None'
? theme.status.error ? Colors.AccentRed
: theme.text.accent : Colors.AccentCyan
} }
bold bold
> >

View File

@ -6,7 +6,7 @@
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import React from 'react'; import React from 'react';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { import {
RadioButtonSelect, RadioButtonSelect,
RadioSelectItem, RadioSelectItem,
@ -54,16 +54,14 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
<Box <Box
flexDirection="column" flexDirection="column"
borderStyle="round" borderStyle="round"
borderColor={theme.status.warning} borderColor={Colors.AccentYellow}
padding={1} padding={1}
width="100%" width="100%"
marginLeft={1} marginLeft={1}
> >
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.primary}> <Text bold>Do you trust this folder?</Text>
Do you trust this folder? <Text>
</Text>
<Text color={theme.text.primary}>
Trusting a folder allows Gemini to execute commands it suggests. This Trusting a folder allows Gemini to execute commands it suggests. This
is a security feature to prevent accidental execution in untrusted is a security feature to prevent accidental execution in untrusted
directories. directories.

View File

@ -72,7 +72,7 @@ export const Footer: React.FC<FooterProps> = ({
{vimMode && <Text color={theme.text.secondary}>[{vimMode}] </Text>} {vimMode && <Text color={theme.text.secondary}>[{vimMode}] </Text>}
{nightly ? ( {nightly ? (
<Gradient colors={theme.ui.gradient}> <Gradient colors={theme.ui.gradient}>
<Text color={theme.text.primary}> <Text>
{displayPath} {displayPath}
{branchName && <Text> ({branchName}*)</Text>} {branchName && <Text> ({branchName}*)</Text>}
</Text> </Text>
@ -132,8 +132,8 @@ export const Footer: React.FC<FooterProps> = ({
/> />
</Text> </Text>
{corgiMode && ( {corgiMode && (
<Text color={theme.text.primary}> <Text>
<Text color={theme.text.secondary}>| </Text> <Text color={theme.ui.symbol}>| </Text>
<Text color={theme.status.error}></Text> <Text color={theme.status.error}></Text>
<Text color={theme.text.primary}>(´</Text> <Text color={theme.text.primary}>(´</Text>
<Text color={theme.status.error}></Text> <Text color={theme.status.error}></Text>
@ -143,7 +143,7 @@ export const Footer: React.FC<FooterProps> = ({
)} )}
{!showErrorDetails && errorCount > 0 && ( {!showErrorDetails && errorCount > 0 && (
<Box> <Box>
<Text color={theme.text.secondary}>| </Text> <Text color={theme.ui.symbol}>| </Text>
<ConsoleSummaryDisplay errorCount={errorCount} /> <ConsoleSummaryDisplay errorCount={errorCount} />
</Box> </Box>
)} )}

View File

@ -10,7 +10,6 @@ import Spinner from 'ink-spinner';
import type { SpinnerName } from 'cli-spinners'; import type { SpinnerName } from 'cli-spinners';
import { useStreamingContext } from '../contexts/StreamingContext.js'; import { useStreamingContext } from '../contexts/StreamingContext.js';
import { StreamingState } from '../types.js'; import { StreamingState } from '../types.js';
import { theme } from '../semantic-colors.js';
interface GeminiRespondingSpinnerProps { interface GeminiRespondingSpinnerProps {
/** /**
@ -29,7 +28,7 @@ export const GeminiRespondingSpinner: React.FC<
if (streamingState === StreamingState.Responding) { if (streamingState === StreamingState.Responding) {
return <Spinner type={spinnerType} />; return <Spinner type={spinnerType} />;
} else if (nonRespondingDisplay) { } else if (nonRespondingDisplay) {
return <Text color={theme.text.primary}>{nonRespondingDisplay}</Text>; return <Text>{nonRespondingDisplay}</Text>;
} }
return null; return null;
}; };

View File

@ -5,7 +5,7 @@
*/ */
import { render } from 'ink-testing-library'; import { render } from 'ink-testing-library';
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { Header } from './Header.js'; import { Header } from './Header.js';
import * as useTerminalSize from '../hooks/useTerminalSize.js'; import * as useTerminalSize from '../hooks/useTerminalSize.js';
import { longAsciiLogo } from './AsciiArt.js'; import { longAsciiLogo } from './AsciiArt.js';
@ -13,13 +13,15 @@ import { longAsciiLogo } from './AsciiArt.js';
vi.mock('../hooks/useTerminalSize.js'); vi.mock('../hooks/useTerminalSize.js');
describe('<Header />', () => { describe('<Header />', () => {
beforeEach(() => {});
it('renders the long logo on a wide terminal', () => { it('renders the long logo on a wide terminal', () => {
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({ vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
columns: 120, columns: 120,
rows: 20, rows: 20,
}); });
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />); const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
expect(lastFrame()?.trim()).toContain(longAsciiLogo.trim()); expect(lastFrame()).toContain(longAsciiLogo);
}); });
it('renders custom ASCII art when provided', () => { it('renders custom ASCII art when provided', () => {
@ -27,20 +29,16 @@ describe('<Header />', () => {
const { lastFrame } = render( const { lastFrame } = render(
<Header version="1.0.0" nightly={false} customAsciiArt={customArt} />, <Header version="1.0.0" nightly={false} customAsciiArt={customArt} />,
); );
expect(lastFrame()?.trim()).toContain(customArt); expect(lastFrame()).toContain(customArt);
}); });
it('displays the version number when nightly is true', () => { it('displays the version number when nightly is true', () => {
const { lastFrame } = render(<Header version="1.0.0" nightly={true} />); const { lastFrame } = render(<Header version="1.0.0" nightly={true} />);
expect(lastFrame()?.trim()).toContain('v1.0.0'); expect(lastFrame()).toContain('v1.0.0');
}); });
it('does not display the version number when nightly is false', () => { it('does not display the version number when nightly is false', () => {
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
columns: 40,
rows: 20,
});
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />); const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
expect(lastFrame()?.trim()).not.toContain('v1.0.0'); expect(lastFrame()).not.toContain('v1.0.0');
}); });
}); });

View File

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import Gradient from 'ink-gradient'; import Gradient from 'ink-gradient';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js'; import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js';
import { getAsciiArtWidth } from '../utils/textUtils.js'; import { getAsciiArtWidth } from '../utils/textUtils.js';
import { useTerminalSize } from '../hooks/useTerminalSize.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js';
@ -18,16 +18,6 @@ interface HeaderProps {
nightly: boolean; nightly: boolean;
} }
const GradientText: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const textElement = <Text color={theme.text.primary}>{children}</Text>;
if (theme.ui.gradient && theme.ui.gradient.length > 0) {
return <Gradient colors={theme.ui.gradient}>{textElement}</Gradient>;
}
return textElement;
};
export const Header: React.FC<HeaderProps> = ({ export const Header: React.FC<HeaderProps> = ({
customAsciiArt, customAsciiArt,
version, version,
@ -57,12 +47,22 @@ export const Header: React.FC<HeaderProps> = ({
flexShrink={0} flexShrink={0}
flexDirection="column" flexDirection="column"
> >
<Box> {Colors.GradientColors ? (
<GradientText>{displayTitle}</GradientText> <Gradient colors={Colors.GradientColors}>
</Box> <Text>{displayTitle}</Text>
</Gradient>
) : (
<Text>{displayTitle}</Text>
)}
{nightly && ( {nightly && (
<Box width="100%" flexDirection="row" justifyContent="flex-end"> <Box width="100%" flexDirection="row" justifyContent="flex-end">
<Text color={theme.text.primary}>v{version}</Text> {Colors.GradientColors ? (
<Gradient colors={Colors.GradientColors}>
<Text>v{version}</Text>
</Gradient>
) : (
<Text>v{version}</Text>
)}
</Box> </Box>
)} )}
</Box> </Box>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { SlashCommand } from '../commands/types.js'; import { SlashCommand } from '../commands/types.js';
interface Help { interface Help {
@ -17,42 +17,42 @@ export const Help: React.FC<Help> = ({ commands }) => (
<Box <Box
flexDirection="column" flexDirection="column"
marginBottom={1} marginBottom={1}
borderColor={theme.border.default} borderColor={Colors.Gray}
borderStyle="round" borderStyle="round"
padding={1} padding={1}
> >
{/* Basics */} {/* Basics */}
<Text bold color={theme.text.primary}> <Text bold color={Colors.Foreground}>
Basics: Basics:
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Add context Add context
</Text> </Text>
: Use{' '} : Use{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
@ @
</Text>{' '} </Text>{' '}
to specify files for context (e.g.,{' '} to specify files for context (e.g.,{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
@src/myFile.ts @src/myFile.ts
</Text> </Text>
) to target specific files or folders. ) to target specific files or folders.
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Shell mode Shell mode
</Text> </Text>
: Execute shell commands via{' '} : Execute shell commands via{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
! !
</Text>{' '} </Text>{' '}
(e.g.,{' '} (e.g.,{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
!npm run start !npm run start
</Text> </Text>
) or use natural language (e.g.{' '} ) or use natural language (e.g.{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
start server start server
</Text> </Text>
). ).
@ -61,15 +61,15 @@ export const Help: React.FC<Help> = ({ commands }) => (
<Box height={1} /> <Box height={1} />
{/* Commands */} {/* Commands */}
<Text bold color={theme.text.primary}> <Text bold color={Colors.Foreground}>
Commands: Commands:
</Text> </Text>
{commands {commands
.filter((command) => command.description) .filter((command) => command.description)
.map((command: SlashCommand) => ( .map((command: SlashCommand) => (
<Box key={command.name} flexDirection="column"> <Box key={command.name} flexDirection="column">
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{' '} {' '}
/{command.name} /{command.name}
</Text> </Text>
@ -77,8 +77,8 @@ export const Help: React.FC<Help> = ({ commands }) => (
</Text> </Text>
{command.subCommands && {command.subCommands &&
command.subCommands.map((subCommand) => ( command.subCommands.map((subCommand) => (
<Text key={subCommand.name} color={theme.text.primary}> <Text key={subCommand.name} color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{' '} {' '}
{subCommand.name} {subCommand.name}
</Text> </Text>
@ -87,8 +87,8 @@ export const Help: React.FC<Help> = ({ commands }) => (
))} ))}
</Box> </Box>
))} ))}
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{' '} {' '}
!{' '} !{' '}
</Text> </Text>
@ -98,75 +98,75 @@ export const Help: React.FC<Help> = ({ commands }) => (
<Box height={1} /> <Box height={1} />
{/* Shortcuts */} {/* Shortcuts */}
<Text bold color={theme.text.primary}> <Text bold color={Colors.Foreground}>
Keyboard Shortcuts: Keyboard Shortcuts:
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Alt+Left/Right Alt+Left/Right
</Text>{' '} </Text>{' '}
- Jump through words in the input - Jump through words in the input
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Ctrl+C Ctrl+C
</Text>{' '} </Text>{' '}
- Quit application - Quit application
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{process.platform === 'win32' ? 'Ctrl+Enter' : 'Ctrl+J'} {process.platform === 'win32' ? 'Ctrl+Enter' : 'Ctrl+J'}
</Text>{' '} </Text>{' '}
{process.platform === 'linux' {process.platform === 'linux'
? '- New line (Alt+Enter works for certain linux distros)' ? '- New line (Alt+Enter works for certain linux distros)'
: '- New line'} : '- New line'}
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Ctrl+L Ctrl+L
</Text>{' '} </Text>{' '}
- Clear the screen - Clear the screen
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'} {process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'}
</Text>{' '} </Text>{' '}
- Open input in external editor - Open input in external editor
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Ctrl+Y Ctrl+Y
</Text>{' '} </Text>{' '}
- Toggle YOLO mode - Toggle YOLO mode
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Enter Enter
</Text>{' '} </Text>{' '}
- Send message - Send message
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Esc Esc
</Text>{' '} </Text>{' '}
- Cancel operation - Cancel operation
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Shift+Tab Shift+Tab
</Text>{' '} </Text>{' '}
- Toggle auto-accepting edits - Toggle auto-accepting edits
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Up/Down Up/Down
</Text>{' '} </Text>{' '}
- Cycle through your prompt history - Cycle through your prompt history
</Text> </Text>
<Box height={1} /> <Box height={1} />
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
For a full list of shortcuts, see{' '} For a full list of shortcuts, see{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
docs/keyboard-shortcuts.md docs/keyboard-shortcuts.md
</Text> </Text>
</Text> </Text>

View File

@ -559,7 +559,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
<Box flexGrow={1} flexDirection="column"> <Box flexGrow={1} flexDirection="column">
{buffer.text.length === 0 && placeholder ? ( {buffer.text.length === 0 && placeholder ? (
focus ? ( focus ? (
<Text color={theme.text.primary}> <Text>
{chalk.inverse(placeholder.slice(0, 1))} {chalk.inverse(placeholder.slice(0, 1))}
<Text color={theme.text.secondary}>{placeholder.slice(1)}</Text> <Text color={theme.text.secondary}>{placeholder.slice(1)}</Text>
</Text> </Text>
@ -600,12 +600,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
} }
} }
return ( return (
<Text <Text key={`line-${visualIdxInRenderedSet}`}>{display}</Text>
key={`line-${visualIdxInRenderedSet}`}
color={theme.text.primary}
>
{display}
</Text>
); );
}) })
)} )}

View File

@ -7,7 +7,7 @@
import { ThoughtSummary } from '@google/gemini-cli-core'; import { ThoughtSummary } from '@google/gemini-cli-core';
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useStreamingContext } from '../contexts/StreamingContext.js'; import { useStreamingContext } from '../contexts/StreamingContext.js';
import { StreamingState } from '../types.js'; import { StreamingState } from '../types.js';
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js'; import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
@ -61,9 +61,11 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
} }
/> />
</Box> </Box>
{primaryText && <Text color={theme.text.accent}>{primaryText}</Text>} {primaryText && (
<Text color={Colors.AccentPurple}>{primaryText}</Text>
)}
{!isNarrow && cancelAndTimerContent && ( {!isNarrow && cancelAndTimerContent && (
<Text color={theme.text.secondary}> {cancelAndTimerContent}</Text> <Text color={Colors.Gray}> {cancelAndTimerContent}</Text>
)} )}
</Box> </Box>
{!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>} {!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
@ -71,7 +73,7 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
</Box> </Box>
{isNarrow && cancelAndTimerContent && ( {isNarrow && cancelAndTimerContent && (
<Box> <Box>
<Text color={theme.text.secondary}>{cancelAndTimerContent}</Text> <Text color={Colors.Gray}>{cancelAndTimerContent}</Text>
</Box> </Box>
)} )}
{isNarrow && rightContent && <Box>{rightContent}</Box>} {isNarrow && rightContent && <Box>{rightContent}</Box>}

View File

@ -6,24 +6,20 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import process from 'node:process'; import process from 'node:process';
import { formatMemoryUsage } from '../utils/formatters.js'; import { formatMemoryUsage } from '../utils/formatters.js';
export const MemoryUsageDisplay: React.FC = () => { export const MemoryUsageDisplay: React.FC = () => {
const [memoryUsage, setMemoryUsage] = useState<string>(''); const [memoryUsage, setMemoryUsage] = useState<string>('');
const [memoryUsageColor, setMemoryUsageColor] = useState<string>( const [memoryUsageColor, setMemoryUsageColor] = useState<string>(Colors.Gray);
theme.text.secondary,
);
useEffect(() => { useEffect(() => {
const updateMemory = () => { const updateMemory = () => {
const usage = process.memoryUsage().rss; const usage = process.memoryUsage().rss;
setMemoryUsage(formatMemoryUsage(usage)); setMemoryUsage(formatMemoryUsage(usage));
setMemoryUsageColor( setMemoryUsageColor(
usage >= 2 * 1024 * 1024 * 1024 usage >= 2 * 1024 * 1024 * 1024 ? Colors.AccentRed : Colors.Gray,
? theme.status.error
: theme.text.secondary,
); );
}; };
const intervalId = setInterval(updateMemory, 2000); const intervalId = setInterval(updateMemory, 2000);
@ -33,7 +29,7 @@ export const MemoryUsageDisplay: React.FC = () => {
return ( return (
<Box> <Box>
<Text color={theme.text.secondary}>| </Text> <Text color={Colors.Gray}>| </Text>
<Text color={memoryUsageColor}>{memoryUsage}</Text> <Text color={memoryUsageColor}>{memoryUsage}</Text>
</Box> </Box>
); );

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { formatDuration } from '../utils/formatters.js'; import { formatDuration } from '../utils/formatters.js';
import { import {
calculateAverageLatency, calculateAverageLatency,
@ -33,13 +33,13 @@ const StatRow: React.FC<StatRowProps> = ({
}) => ( }) => (
<Box> <Box>
<Box width={METRIC_COL_WIDTH}> <Box width={METRIC_COL_WIDTH}>
<Text bold color={isSection ? theme.text.primary : theme.text.link}> <Text bold={isSection} color={isSection ? undefined : Colors.LightBlue}>
{isSubtle ? `${title}` : title} {isSubtle ? `${title}` : title}
</Text> </Text>
</Box> </Box>
{values.map((value, index) => ( {values.map((value, index) => (
<Box width={MODEL_COL_WIDTH} key={index}> <Box width={MODEL_COL_WIDTH} key={index}>
<Text color={theme.text.primary}>{value}</Text> <Text>{value}</Text>
</Box> </Box>
))} ))}
</Box> </Box>
@ -56,13 +56,11 @@ export const ModelStatsDisplay: React.FC = () => {
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
paddingY={1} paddingY={1}
paddingX={2} paddingX={2}
> >
<Text color={theme.text.primary}> <Text>No API calls have been made in this session.</Text>
No API calls have been made in this session.
</Text>
</Box> </Box>
); );
} }
@ -84,12 +82,12 @@ export const ModelStatsDisplay: React.FC = () => {
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
paddingY={1} paddingY={1}
paddingX={2} paddingX={2}
> >
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Model Stats For Nerds Model Stats For Nerds
</Text> </Text>
<Box height={1} /> <Box height={1} />
@ -97,15 +95,11 @@ export const ModelStatsDisplay: React.FC = () => {
{/* Header */} {/* Header */}
<Box> <Box>
<Box width={METRIC_COL_WIDTH}> <Box width={METRIC_COL_WIDTH}>
<Text bold color={theme.text.primary}> <Text bold>Metric</Text>
Metric
</Text>
</Box> </Box>
{modelNames.map((name) => ( {modelNames.map((name) => (
<Box width={MODEL_COL_WIDTH} key={name}> <Box width={MODEL_COL_WIDTH} key={name}>
<Text bold color={theme.text.primary}> <Text bold>{name}</Text>
{name}
</Text>
</Box> </Box>
))} ))}
</Box> </Box>
@ -132,7 +126,7 @@ export const ModelStatsDisplay: React.FC = () => {
return ( return (
<Text <Text
color={ color={
m.api.totalErrors > 0 ? theme.status.error : theme.text.primary m.api.totalErrors > 0 ? Colors.AccentRed : Colors.Foreground
} }
> >
{m.api.totalErrors.toLocaleString()} ({errorRate.toFixed(1)}%) {m.api.totalErrors.toLocaleString()} ({errorRate.toFixed(1)}%)
@ -155,7 +149,7 @@ export const ModelStatsDisplay: React.FC = () => {
<StatRow <StatRow
title="Total" title="Total"
values={getModelValues((m) => ( values={getModelValues((m) => (
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
{m.tokens.total.toLocaleString()} {m.tokens.total.toLocaleString()}
</Text> </Text>
))} ))}
@ -172,7 +166,7 @@ export const ModelStatsDisplay: React.FC = () => {
values={getModelValues((m) => { values={getModelValues((m) => {
const cacheHitRate = calculateCacheHitRate(m); const cacheHitRate = calculateCacheHitRate(m);
return ( return (
<Text color={theme.status.success}> <Text color={Colors.AccentGreen}>
{m.tokens.cached.toLocaleString()} ({cacheHitRate.toFixed(1)}%) {m.tokens.cached.toLocaleString()} ({cacheHitRate.toFixed(1)}%)
</Text> </Text>
); );

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text } from 'ink'; import { Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
interface PrepareLabelProps { interface PrepareLabelProps {
label: string; label: string;
@ -21,7 +21,7 @@ export const PrepareLabel: React.FC<PrepareLabelProps> = ({
matchedIndex, matchedIndex,
userInput, userInput,
textColor, textColor,
highlightColor = theme.status.warning, highlightColor = Colors.AccentYellow,
}) => { }) => {
if ( if (
matchedIndex === undefined || matchedIndex === undefined ||
@ -37,7 +37,7 @@ export const PrepareLabel: React.FC<PrepareLabelProps> = ({
const end = label.slice(matchedIndex + userInput.length); const end = label.slice(matchedIndex + userInput.length);
return ( return (
<Text color={theme.text.primary}> <Text>
<Text color={textColor}>{start}</Text> <Text color={textColor}>{start}</Text>
<Text color="black" bold backgroundColor={highlightColor}> <Text color="black" bold backgroundColor={highlightColor}>
{match} {match}

View File

@ -6,7 +6,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { import {
LoadedSettings, LoadedSettings,
SettingScope, SettingScope,
@ -366,18 +366,18 @@ export function SettingsDialog({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="row" flexDirection="row"
padding={1} padding={1}
width="100%" width="100%"
height="100%" height="100%"
> >
<Box flexDirection="column" flexGrow={1}> <Box flexDirection="column" flexGrow={1}>
<Text bold color={theme.text.link}> <Text bold color={Colors.AccentBlue}>
Settings Settings
</Text> </Text>
<Box height={1} /> <Box height={1} />
{showScrollUp && <Text color={theme.text.secondary}></Text>} {showScrollUp && <Text color={Colors.Gray}></Text>}
{visibleItems.map((item, idx) => { {visibleItems.map((item, idx) => {
const isActive = const isActive =
focusSection === 'settings' && focusSection === 'settings' &&
@ -405,21 +405,17 @@ export function SettingsDialog({
<React.Fragment key={item.value}> <React.Fragment key={item.value}>
<Box flexDirection="row" alignItems="center"> <Box flexDirection="row" alignItems="center">
<Box minWidth={2} flexShrink={0}> <Box minWidth={2} flexShrink={0}>
<Text <Text color={isActive ? Colors.AccentGreen : Colors.Gray}>
color={
isActive ? theme.status.success : theme.text.secondary
}
>
{isActive ? '●' : ''} {isActive ? '●' : ''}
</Text> </Text>
</Box> </Box>
<Box minWidth={50}> <Box minWidth={50}>
<Text <Text
color={isActive ? theme.status.success : theme.text.primary} color={isActive ? Colors.AccentGreen : Colors.Foreground}
> >
{item.label} {item.label}
{scopeMessage && ( {scopeMessage && (
<Text color={theme.text.secondary}> {scopeMessage}</Text> <Text color={Colors.Gray}> {scopeMessage}</Text>
)} )}
</Text> </Text>
</Box> </Box>
@ -427,10 +423,10 @@ export function SettingsDialog({
<Text <Text
color={ color={
isActive isActive
? theme.status.success ? Colors.AccentGreen
: shouldBeGreyedOut : shouldBeGreyedOut
? theme.text.secondary ? Colors.Gray
: theme.text.primary : Colors.Foreground
} }
> >
{displayValue} {displayValue}
@ -440,16 +436,12 @@ export function SettingsDialog({
</React.Fragment> </React.Fragment>
); );
})} })}
{showScrollDown && <Text color={theme.text.secondary}></Text>} {showScrollDown && <Text color={Colors.Gray}></Text>}
<Box height={1} /> <Box height={1} />
<Box marginTop={1} flexDirection="column"> <Box marginTop={1} flexDirection="column">
<Text <Text bold={focusSection === 'scope'} wrap="truncate">
bold={focusSection === 'scope'}
wrap="truncate"
color={theme.text.primary}
>
{focusSection === 'scope' ? '> ' : ' '}Apply To {focusSection === 'scope' ? '> ' : ' '}Apply To
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
@ -463,11 +455,11 @@ export function SettingsDialog({
</Box> </Box>
<Box height={1} /> <Box height={1} />
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
(Use Enter to select, Tab to change focus) (Use Enter to select, Tab to change focus)
</Text> </Text>
{showRestartPrompt && ( {showRestartPrompt && (
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
To see changes, Gemini CLI must be restarted. Press r to exit and To see changes, Gemini CLI must be restarted. Press r to exit and
apply changes now. apply changes now.
</Text> </Text>

View File

@ -7,7 +7,7 @@
import { ToolConfirmationOutcome } from '@google/gemini-cli-core'; import { ToolConfirmationOutcome } from '@google/gemini-cli-core';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import React from 'react'; import React from 'react';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { import {
RadioButtonSelect, RadioButtonSelect,
RadioSelectItem, RadioSelectItem,
@ -69,27 +69,23 @@ export const ShellConfirmationDialog: React.FC<
<Box <Box
flexDirection="column" flexDirection="column"
borderStyle="round" borderStyle="round"
borderColor={theme.status.warning} borderColor={Colors.AccentYellow}
padding={1} padding={1}
width="100%" width="100%"
marginLeft={1} marginLeft={1}
> >
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.primary}> <Text bold>Shell Command Execution</Text>
Shell Command Execution <Text>A custom command wants to run the following shell commands:</Text>
</Text>
<Text color={theme.text.primary}>
A custom command wants to run the following shell commands:
</Text>
<Box <Box
flexDirection="column" flexDirection="column"
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
paddingX={1} paddingX={1}
marginTop={1} marginTop={1}
> >
{commands.map((cmd) => ( {commands.map((cmd) => (
<Text key={cmd} color={theme.text.accent}> <Text key={cmd} color={Colors.AccentCyan}>
{cmd} {cmd}
</Text> </Text>
))} ))}
@ -97,7 +93,7 @@ export const ShellConfirmationDialog: React.FC<
</Box> </Box>
<Box marginBottom={1}> <Box marginBottom={1}>
<Text color={theme.text.primary}>Do you want to proceed?</Text> <Text>Do you want to proceed?</Text>
</Box> </Box>
<RadioButtonSelect items={options} onSelect={handleSelect} isFocused /> <RadioButtonSelect items={options} onSelect={handleSelect} isFocused />

View File

@ -6,13 +6,13 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
export const ShellModeIndicator: React.FC = () => ( export const ShellModeIndicator: React.FC = () => (
<Box> <Box>
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
shell mode enabled shell mode enabled
<Text color={theme.text.secondary}> (esc to disable)</Text> <Text color={Colors.Gray}> (esc to disable)</Text>
</Text> </Text>
</Box> </Box>
); );

View File

@ -8,7 +8,7 @@ import { Box, Text } from 'ink';
import { useOverflowState } from '../contexts/OverflowContext.js'; import { useOverflowState } from '../contexts/OverflowContext.js';
import { useStreamingContext } from '../contexts/StreamingContext.js'; import { useStreamingContext } from '../contexts/StreamingContext.js';
import { StreamingState } from '../types.js'; import { StreamingState } from '../types.js';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
interface ShowMoreLinesProps { interface ShowMoreLinesProps {
constrainHeight: boolean; constrainHeight: boolean;
@ -32,7 +32,7 @@ export const ShowMoreLines = ({ constrainHeight }: ShowMoreLinesProps) => {
return ( return (
<Box> <Box>
<Text color={theme.text.secondary} wrap="truncate"> <Text color={Colors.Gray} wrap="truncate">
Press ctrl-s to show more lines Press ctrl-s to show more lines
</Text> </Text>
</Box> </Box>

View File

@ -43,7 +43,7 @@ describe('<StatsDisplay />', () => {
const zeroMetrics: SessionMetrics = { const zeroMetrics: SessionMetrics = {
models: {}, models: {},
tools: { tools: {
totalCalls: 1, totalCalls: 0,
totalSuccess: 0, totalSuccess: 0,
totalFail: 0, totalFail: 0,
totalDurationMs: 0, totalDurationMs: 0,

View File

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import Gradient from 'ink-gradient'; import Gradient from 'ink-gradient';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { formatDuration } from '../utils/formatters.js'; import { formatDuration } from '../utils/formatters.js';
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js'; import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
import { import {
@ -29,7 +29,7 @@ const StatRow: React.FC<StatRowProps> = ({ title, children }) => (
<Box> <Box>
{/* Fixed width for the label creates a clean "gutter" for alignment */} {/* Fixed width for the label creates a clean "gutter" for alignment */}
<Box width={28}> <Box width={28}>
<Text color={theme.text.link}>{title}</Text> <Text color={Colors.LightBlue}>{title}</Text>
</Box> </Box>
{children} {children}
</Box> </Box>
@ -45,7 +45,7 @@ const SubStatRow: React.FC<SubStatRowProps> = ({ title, children }) => (
<Box paddingLeft={2}> <Box paddingLeft={2}>
{/* Adjust width for the "» " prefix */} {/* Adjust width for the "» " prefix */}
<Box width={26}> <Box width={26}>
<Text color={theme.text.secondary}>» {title}</Text> <Text>» {title}</Text>
</Box> </Box>
{children} {children}
</Box> </Box>
@ -59,9 +59,7 @@ interface SectionProps {
const Section: React.FC<SectionProps> = ({ title, children }) => ( const Section: React.FC<SectionProps> = ({ title, children }) => (
<Box flexDirection="column" width="100%" marginBottom={1}> <Box flexDirection="column" width="100%" marginBottom={1}>
<Text bold color={theme.text.primary}> <Text bold>{title}</Text>
{title}
</Text>
{children} {children}
</Box> </Box>
); );
@ -81,24 +79,16 @@ const ModelUsageTable: React.FC<{
{/* Header */} {/* Header */}
<Box> <Box>
<Box width={nameWidth}> <Box width={nameWidth}>
<Text bold color={theme.text.primary}> <Text bold>Model Usage</Text>
Model Usage
</Text>
</Box> </Box>
<Box width={requestsWidth} justifyContent="flex-end"> <Box width={requestsWidth} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Reqs</Text>
Reqs
</Text>
</Box> </Box>
<Box width={inputTokensWidth} justifyContent="flex-end"> <Box width={inputTokensWidth} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Input Tokens</Text>
Input Tokens
</Text>
</Box> </Box>
<Box width={outputTokensWidth} justifyContent="flex-end"> <Box width={outputTokensWidth} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Output Tokens</Text>
Output Tokens
</Text>
</Box> </Box>
</Box> </Box>
{/* Divider */} {/* Divider */}
@ -108,7 +98,6 @@ const ModelUsageTable: React.FC<{
borderTop={false} borderTop={false}
borderLeft={false} borderLeft={false}
borderRight={false} borderRight={false}
borderColor={theme.border.default}
width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth} width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth}
></Box> ></Box>
@ -116,20 +105,18 @@ const ModelUsageTable: React.FC<{
{Object.entries(models).map(([name, modelMetrics]) => ( {Object.entries(models).map(([name, modelMetrics]) => (
<Box key={name}> <Box key={name}>
<Box width={nameWidth}> <Box width={nameWidth}>
<Text color={theme.text.primary}>{name.replace('-001', '')}</Text> <Text>{name.replace('-001', '')}</Text>
</Box> </Box>
<Box width={requestsWidth} justifyContent="flex-end"> <Box width={requestsWidth} justifyContent="flex-end">
<Text color={theme.text.primary}> <Text>{modelMetrics.api.totalRequests}</Text>
{modelMetrics.api.totalRequests}
</Text>
</Box> </Box>
<Box width={inputTokensWidth} justifyContent="flex-end"> <Box width={inputTokensWidth} justifyContent="flex-end">
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
{modelMetrics.tokens.prompt.toLocaleString()} {modelMetrics.tokens.prompt.toLocaleString()}
</Text> </Text>
</Box> </Box>
<Box width={outputTokensWidth} justifyContent="flex-end"> <Box width={outputTokensWidth} justifyContent="flex-end">
<Text color={theme.status.warning}> <Text color={Colors.AccentYellow}>
{modelMetrics.tokens.candidates.toLocaleString()} {modelMetrics.tokens.candidates.toLocaleString()}
</Text> </Text>
</Box> </Box>
@ -137,13 +124,13 @@ const ModelUsageTable: React.FC<{
))} ))}
{cacheEfficiency > 0 && ( {cacheEfficiency > 0 && (
<Box flexDirection="column" marginTop={1}> <Box flexDirection="column" marginTop={1}>
<Text color={theme.text.primary}> <Text>
<Text color={theme.status.success}>Savings Highlight:</Text>{' '} <Text color={Colors.AccentGreen}>Savings Highlight:</Text>{' '}
{totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)} {totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)}
%) of input tokens were served from the cache, reducing costs. %) of input tokens were served from the cache, reducing costs.
</Text> </Text>
<Box height={1} /> <Box height={1} />
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
» Tip: For a full token breakdown, run `/stats model`. » Tip: For a full token breakdown, run `/stats model`.
</Text> </Text>
</Box> </Box>
@ -182,18 +169,18 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
const renderTitle = () => { const renderTitle = () => {
if (title) { if (title) {
return theme.ui.gradient && theme.ui.gradient.length > 0 ? ( return Colors.GradientColors && Colors.GradientColors.length > 0 ? (
<Gradient colors={theme.ui.gradient}> <Gradient colors={Colors.GradientColors}>
<Text bold>{title}</Text> <Text bold>{title}</Text>
</Gradient> </Gradient>
) : ( ) : (
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
{title} {title}
</Text> </Text>
); );
} }
return ( return (
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Session Stats Session Stats
</Text> </Text>
); );
@ -202,7 +189,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
paddingY={1} paddingY={1}
paddingX={2} paddingX={2}
@ -210,55 +197,51 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
{renderTitle()} {renderTitle()}
<Box height={1} /> <Box height={1} />
{tools.totalCalls > 0 && ( <Section title="Interaction Summary">
<Section title="Interaction Summary"> <StatRow title="Session ID:">
<StatRow title="Session ID:"> <Text>{stats.sessionId}</Text>
<Text>{stats.sessionId}</Text> </StatRow>
</StatRow> <StatRow title="Tool Calls:">
<StatRow title="Tool Calls:"> <Text>
<Text color={theme.text.primary}> {tools.totalCalls} ({' '}
{tools.totalCalls} ({' '} <Text color={Colors.AccentGreen}> {tools.totalSuccess}</Text>{' '}
<Text color={theme.status.success}> {tools.totalSuccess}</Text>{' '} <Text color={Colors.AccentRed}> {tools.totalFail}</Text> )
<Text color={theme.status.error}> {tools.totalFail}</Text> ) </Text>
</StatRow>
<StatRow title="Success Rate:">
<Text color={successColor}>{computed.successRate.toFixed(1)}%</Text>
</StatRow>
{computed.totalDecisions > 0 && (
<StatRow title="User Agreement:">
<Text color={agreementColor}>
{computed.agreementRate.toFixed(1)}%{' '}
<Text color={Colors.Gray}>
({computed.totalDecisions} reviewed)
</Text>
</Text> </Text>
</StatRow> </StatRow>
<StatRow title="Success Rate:"> )}
<Text color={successColor}>{computed.successRate.toFixed(1)}%</Text> </Section>
</StatRow>
{computed.totalDecisions > 0 && (
<StatRow title="User Agreement:">
<Text color={agreementColor}>
{computed.agreementRate.toFixed(1)}%{' '}
<Text color={theme.text.secondary}>
({computed.totalDecisions} reviewed)
</Text>
</Text>
</StatRow>
)}
</Section>
)}
<Section title="Performance"> <Section title="Performance">
<StatRow title="Wall Time:"> <StatRow title="Wall Time:">
<Text color={theme.text.primary}>{duration}</Text> <Text>{duration}</Text>
</StatRow> </StatRow>
<StatRow title="Agent Active:"> <StatRow title="Agent Active:">
<Text color={theme.text.primary}> <Text>{formatDuration(computed.agentActiveTime)}</Text>
{formatDuration(computed.agentActiveTime)}
</Text>
</StatRow> </StatRow>
<SubStatRow title="API Time:"> <SubStatRow title="API Time:">
<Text color={theme.text.primary}> <Text>
{formatDuration(computed.totalApiTime)}{' '} {formatDuration(computed.totalApiTime)}{' '}
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
({computed.apiTimePercent.toFixed(1)}%) ({computed.apiTimePercent.toFixed(1)}%)
</Text> </Text>
</Text> </Text>
</SubStatRow> </SubStatRow>
<SubStatRow title="Tool Time:"> <SubStatRow title="Tool Time:">
<Text color={theme.text.primary}> <Text>
{formatDuration(computed.totalToolTime)}{' '} {formatDuration(computed.totalToolTime)}{' '}
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>
({computed.toolTimePercent.toFixed(1)}%) ({computed.toolTimePercent.toFixed(1)}%)
</Text> </Text>
</Text> </Text>

View File

@ -5,7 +5,7 @@
*/ */
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { PrepareLabel } from './PrepareLabel.js'; import { PrepareLabel } from './PrepareLabel.js';
export interface Suggestion { export interface Suggestion {
label: string; label: string;
@ -35,7 +35,7 @@ export function SuggestionsDisplay({
if (isLoading) { if (isLoading) {
return ( return (
<Box paddingX={1} width={width}> <Box paddingX={1} width={width}>
<Text color={theme.text.secondary}>Loading suggestions...</Text> <Text color="gray">Loading suggestions...</Text>
</Box> </Box>
); );
} }
@ -54,12 +54,12 @@ export function SuggestionsDisplay({
return ( return (
<Box flexDirection="column" paddingX={1} width={width}> <Box flexDirection="column" paddingX={1} width={width}>
{scrollOffset > 0 && <Text color={theme.text.primary}></Text>} {scrollOffset > 0 && <Text color={Colors.Foreground}></Text>}
{visibleSuggestions.map((suggestion, index) => { {visibleSuggestions.map((suggestion, index) => {
const originalIndex = startIndex + index; const originalIndex = startIndex + index;
const isActive = originalIndex === activeIndex; const isActive = originalIndex === activeIndex;
const textColor = isActive ? theme.text.accent : theme.text.secondary; const textColor = isActive ? Colors.AccentPurple : Colors.Gray;
const labelElement = ( const labelElement = (
<PrepareLabel <PrepareLabel
label={suggestion.label} label={suggestion.label}
@ -91,11 +91,9 @@ export function SuggestionsDisplay({
</Box> </Box>
); );
})} })}
{endIndex < suggestions.length && ( {endIndex < suggestions.length && <Text color="gray"></Text>}
<Text color={theme.text.secondary}></Text>
)}
{suggestions.length > MAX_SUGGESTIONS_TO_SHOW && ( {suggestions.length > MAX_SUGGESTIONS_TO_SHOW && (
<Text color={theme.text.secondary}> <Text color="gray">
({activeIndex + 1}/{suggestions.length}) ({activeIndex + 1}/{suggestions.length})
</Text> </Text>
)} )}

View File

@ -6,7 +6,7 @@
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js'; import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js'; import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { DiffRenderer } from './messages/DiffRenderer.js'; import { DiffRenderer } from './messages/DiffRenderer.js';
@ -207,7 +207,7 @@ export function ThemeDialog({
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
paddingTop={includePadding ? 1 : 0} paddingTop={includePadding ? 1 : 0}
paddingBottom={includePadding ? 1 : 0} paddingBottom={includePadding ? 1 : 0}
@ -218,19 +218,9 @@ export function ThemeDialog({
<Box flexDirection="row"> <Box flexDirection="row">
{/* Left Column: Selection */} {/* Left Column: Selection */}
<Box flexDirection="column" width="45%" paddingRight={2}> <Box flexDirection="column" width="45%" paddingRight={2}>
<Text <Text bold={currentFocusedSection === 'theme'} wrap="truncate">
bold={currentFocusedSection === 'theme'}
wrap="truncate"
color={
currentFocusedSection === 'theme'
? theme.text.primary
: theme.text.secondary
}
>
{currentFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '} {currentFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
{otherScopeModifiedMessage}
</Text>
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
key={selectInputKey} key={selectInputKey}
@ -247,15 +237,7 @@ export function ThemeDialog({
{/* Scope Selection */} {/* Scope Selection */}
{showScopeSelection && ( {showScopeSelection && (
<Box marginTop={1} flexDirection="column"> <Box marginTop={1} flexDirection="column">
<Text <Text bold={currentFocusedSection === 'scope'} wrap="truncate">
bold={currentFocusedSection === 'scope'}
wrap="truncate"
color={
currentFocusedSection === 'scope'
? theme.text.primary
: theme.text.secondary
}
>
{currentFocusedSection === 'scope' ? '> ' : ' '}Apply To {currentFocusedSection === 'scope' ? '> ' : ' '}Apply To
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
@ -272,9 +254,7 @@ export function ThemeDialog({
{/* Right Column: Preview */} {/* Right Column: Preview */}
<Box flexDirection="column" width="55%" paddingLeft={2}> <Box flexDirection="column" width="55%" paddingLeft={2}>
<Text bold color={theme.text.primary}> <Text bold>Preview</Text>
Preview
</Text>
{/* Get the Theme object for the highlighted theme, fall back to default if not found */} {/* Get the Theme object for the highlighted theme, fall back to default if not found */}
{(() => { {(() => {
const previewTheme = const previewTheme =
@ -284,7 +264,7 @@ export function ThemeDialog({
return ( return (
<Box <Box
borderStyle="single" borderStyle="single"
borderColor={theme.border.default} borderColor={Colors.Gray}
paddingTop={includePadding ? 1 : 0} paddingTop={includePadding ? 1 : 0}
paddingBottom={includePadding ? 1 : 0} paddingBottom={includePadding ? 1 : 0}
paddingLeft={1} paddingLeft={1}
@ -301,7 +281,6 @@ def fibonacci(n):
'python', 'python',
codeBlockHeight, codeBlockHeight,
colorizeCodeWidth, colorizeCodeWidth,
previewTheme,
)} )}
<Box marginTop={1} /> <Box marginTop={1} />
<DiffRenderer <DiffRenderer
@ -321,7 +300,7 @@ def fibonacci(n):
</Box> </Box>
</Box> </Box>
<Box marginTop={1}> <Box marginTop={1}>
<Text color={theme.text.secondary} wrap="truncate"> <Text color={Colors.Gray} wrap="truncate">
(Use Enter to select (Use Enter to select
{showScopeSelection ? ', Tab to change focus' : ''}) {showScopeSelection ? ', Tab to change focus' : ''})
</Text> </Text>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { type Config } from '@google/gemini-cli-core'; import { type Config } from '@google/gemini-cli-core';
interface TipsProps { interface TipsProps {
@ -17,25 +17,25 @@ export const Tips: React.FC<TipsProps> = ({ config }) => {
const geminiMdFileCount = config.getGeminiMdFileCount(); const geminiMdFileCount = config.getGeminiMdFileCount();
return ( return (
<Box flexDirection="column"> <Box flexDirection="column">
<Text color={theme.text.primary}>Tips for getting started:</Text> <Text color={Colors.Foreground}>Tips for getting started:</Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
1. Ask questions, edit files, or run commands. 1. Ask questions, edit files, or run commands.
</Text> </Text>
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
2. Be specific for the best results. 2. Be specific for the best results.
</Text> </Text>
{geminiMdFileCount === 0 && ( {geminiMdFileCount === 0 && (
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
3. Create{' '} 3. Create{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
GEMINI.md GEMINI.md
</Text>{' '} </Text>{' '}
files to customize your interactions with Gemini. files to customize your interactions with Gemini.
</Text> </Text>
)} )}
<Text color={theme.text.primary}> <Text color={Colors.Foreground}>
{geminiMdFileCount === 0 ? '4.' : '3.'}{' '} {geminiMdFileCount === 0 ? '4.' : '3.'}{' '}
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
/help /help
</Text>{' '} </Text>{' '}
for more information. for more information.

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { formatDuration } from '../utils/formatters.js'; import { formatDuration } from '../utils/formatters.js';
import { import {
getStatusColor, getStatusColor,
@ -37,16 +37,16 @@ const StatRow: React.FC<{
return ( return (
<Box> <Box>
<Box width={TOOL_NAME_COL_WIDTH}> <Box width={TOOL_NAME_COL_WIDTH}>
<Text color={theme.text.link}>{name}</Text> <Text color={Colors.LightBlue}>{name}</Text>
</Box> </Box>
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end"> <Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.text.primary}>{stats.count}</Text> <Text>{stats.count}</Text>
</Box> </Box>
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end"> <Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
<Text color={successColor}>{successRate.toFixed(1)}%</Text> <Text color={successColor}>{successRate.toFixed(1)}%</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.text.primary}>{formatDuration(avgDuration)}</Text> <Text>{formatDuration(avgDuration)}</Text>
</Box> </Box>
</Box> </Box>
); );
@ -63,13 +63,11 @@ export const ToolStatsDisplay: React.FC = () => {
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
paddingY={1} paddingY={1}
paddingX={2} paddingX={2}
> >
<Text color={theme.text.primary}> <Text>No tool calls have been made in this session.</Text>
No tool calls have been made in this session.
</Text>
</Box> </Box>
); );
} }
@ -96,13 +94,13 @@ export const ToolStatsDisplay: React.FC = () => {
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
flexDirection="column" flexDirection="column"
paddingY={1} paddingY={1}
paddingX={2} paddingX={2}
width={70} width={70}
> >
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Tool Stats For Nerds Tool Stats For Nerds
</Text> </Text>
<Box height={1} /> <Box height={1} />
@ -110,24 +108,16 @@ export const ToolStatsDisplay: React.FC = () => {
{/* Header */} {/* Header */}
<Box> <Box>
<Box width={TOOL_NAME_COL_WIDTH}> <Box width={TOOL_NAME_COL_WIDTH}>
<Text bold color={theme.text.primary}> <Text bold>Tool Name</Text>
Tool Name
</Text>
</Box> </Box>
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end"> <Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Calls</Text>
Calls
</Text>
</Box> </Box>
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end"> <Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Success Rate</Text>
Success Rate
</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text bold color={theme.text.primary}> <Text bold>Avg Duration</Text>
Avg Duration
</Text>
</Box> </Box>
</Box> </Box>
@ -149,47 +139,45 @@ export const ToolStatsDisplay: React.FC = () => {
<Box height={1} /> <Box height={1} />
{/* User Decision Summary */} {/* User Decision Summary */}
<Text bold color={theme.text.primary}> <Text bold>User Decision Summary</Text>
User Decision Summary
</Text>
<Box> <Box>
<Box <Box
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH} width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
> >
<Text color={theme.text.link}>Total Reviewed Suggestions:</Text> <Text color={Colors.LightBlue}>Total Reviewed Suggestions:</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.text.primary}>{totalReviewed}</Text> <Text>{totalReviewed}</Text>
</Box> </Box>
</Box> </Box>
<Box> <Box>
<Box <Box
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH} width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
> >
<Text color={theme.text.primary}> » Accepted:</Text> <Text> » Accepted:</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.status.success}>{totalDecisions.accept}</Text> <Text color={Colors.AccentGreen}>{totalDecisions.accept}</Text>
</Box> </Box>
</Box> </Box>
<Box> <Box>
<Box <Box
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH} width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
> >
<Text color={theme.text.primary}> » Rejected:</Text> <Text> » Rejected:</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.status.error}>{totalDecisions.reject}</Text> <Text color={Colors.AccentRed}>{totalDecisions.reject}</Text>
</Box> </Box>
</Box> </Box>
<Box> <Box>
<Box <Box
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH} width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
> >
<Text color={theme.text.primary}> » Modified:</Text> <Text> » Modified:</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text color={theme.status.warning}>{totalDecisions.modify}</Text> <Text color={Colors.AccentYellow}>{totalDecisions.modify}</Text>
</Box> </Box>
</Box> </Box>
@ -207,13 +195,10 @@ export const ToolStatsDisplay: React.FC = () => {
<Box <Box
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH} width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
> >
<Text color={theme.text.primary}> Overall Agreement Rate:</Text> <Text> Overall Agreement Rate:</Text>
</Box> </Box>
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end"> <Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
<Text <Text bold color={totalReviewed > 0 ? agreementColor : undefined}>
bold
color={totalReviewed > 0 ? agreementColor : theme.text.primary}
>
{totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : '--'} {totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : '--'}
</Text> </Text>
</Box> </Box>

View File

@ -5,7 +5,7 @@
*/ */
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
interface UpdateNotificationProps { interface UpdateNotificationProps {
message: string; message: string;
@ -14,10 +14,10 @@ interface UpdateNotificationProps {
export const UpdateNotification = ({ message }: UpdateNotificationProps) => ( export const UpdateNotification = ({ message }: UpdateNotificationProps) => (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={theme.status.warning} borderColor={Colors.AccentYellow}
paddingX={1} paddingX={1}
marginY={1} marginY={1}
> >
<Text color={theme.status.warning}>{message}</Text> <Text color={Colors.AccentYellow}>{message}</Text>
</Box> </Box>
); );

View File

@ -5,6 +5,11 @@ exports[`<SessionSummaryDisplay /> > renders the summary display with a title 1`
│ │ │ │
│ Agent powering down. Goodbye! │ │ Agent powering down. Goodbye! │
│ │ │ │
│ Interaction Summary │
│ Session ID: │
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │
│ │
│ Performance │ │ Performance │
│ Wall Time: 1h 23m 45s │ │ Wall Time: 1h 23m 45s │
│ Agent Active: 50.2s │ │ Agent Active: 50.2s │

View File

@ -65,6 +65,11 @@ exports[`<StatsDisplay /> > Conditional Rendering Tests > hides Efficiency secti
│ │ │ │
│ Session Stats │ │ Session Stats │
│ │ │ │
│ Interaction Summary │
│ Session ID: test-session-id │
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │
│ │
│ Performance │ │ Performance │
│ Wall Time: 1s │ │ Wall Time: 1s │
│ Agent Active: 100ms │ │ Agent Active: 100ms │
@ -104,6 +109,11 @@ exports[`<StatsDisplay /> > Title Rendering > renders the custom title when a ti
│ │ │ │
│ Agent powering down. Goodbye! │ │ Agent powering down. Goodbye! │
│ │ │ │
│ Interaction Summary │
│ Session ID: test-session-id │
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │
│ │
│ Performance │ │ Performance │
│ Wall Time: 1s │ │ Wall Time: 1s │
│ Agent Active: 0s │ │ Agent Active: 0s │
@ -119,6 +129,11 @@ exports[`<StatsDisplay /> > Title Rendering > renders the default title when no
│ │ │ │
│ Session Stats │ │ Session Stats │
│ │ │ │
│ Interaction Summary │
│ Session ID: test-session-id │
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │
│ │
│ Performance │ │ Performance │
│ Wall Time: 1s │ │ Wall Time: 1s │
│ Agent Active: 0s │ │ Agent Active: 0s │
@ -134,6 +149,11 @@ exports[`<StatsDisplay /> > renders a table with two models correctly 1`] = `
│ │ │ │
│ Session Stats │ │ Session Stats │
│ │ │ │
│ Interaction Summary │
│ Session ID: test-session-id │
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │
│ │
│ Performance │ │ Performance │
│ Wall Time: 1s │ │ Wall Time: 1s │
│ Agent Active: 19.5s │ │ Agent Active: 19.5s │
@ -189,7 +209,7 @@ exports[`<StatsDisplay /> > renders only the Performance section in its zero sta
│ │ │ │
│ Interaction Summary │ │ Interaction Summary │
│ Session ID: test-session-id │ │ Session ID: test-session-id │
│ Tool Calls: 1 ( ✔ 0 ✖ 0 ) │ │ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
│ Success Rate: 0.0% │ │ Success Rate: 0.0% │
│ │ │ │
│ Performance │ │ Performance │

View File

@ -8,7 +8,7 @@ import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { CompressionProps } from '../../types.js'; import { CompressionProps } from '../../types.js';
import Spinner from 'ink-spinner'; import Spinner from 'ink-spinner';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
export interface CompressionDisplayProps { export interface CompressionDisplayProps {
compression: CompressionProps; compression: CompressionProps;
@ -32,13 +32,13 @@ export const CompressionMessage: React.FC<CompressionDisplayProps> = ({
{compression.isPending ? ( {compression.isPending ? (
<Spinner type="dots" /> <Spinner type="dots" />
) : ( ) : (
<Text color={theme.text.accent}></Text> <Text color={Colors.AccentPurple}></Text>
)} )}
</Box> </Box>
<Box> <Box>
<Text <Text
color={ color={
compression.isPending ? theme.text.accent : theme.status.success compression.isPending ? Colors.AccentPurple : Colors.AccentGreen
} }
> >
{text} {text}

View File

@ -6,9 +6,11 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { Colors } from '../../colors.js';
import crypto from 'crypto'; import crypto from 'crypto';
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js'; import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
import { MaxSizedBox } from '../shared/MaxSizedBox.js'; import { MaxSizedBox } from '../shared/MaxSizedBox.js';
import { theme } from '../../semantic-colors.js';
interface DiffLine { interface DiffLine {
type: 'add' | 'del' | 'context' | 'hunk' | 'other'; type: 'add' | 'del' | 'context' | 'hunk' | 'other';
@ -106,20 +108,14 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
theme, theme,
}) => { }) => {
if (!diffContent || typeof diffContent !== 'string') { if (!diffContent || typeof diffContent !== 'string') {
return ( return <Text color={Colors.AccentYellow}>No diff content.</Text>;
<Text color={theme?.semanticColors.status.warning}>No diff content.</Text>
);
} }
const parsedLines = parseDiffWithLineNumbers(diffContent); const parsedLines = parseDiffWithLineNumbers(diffContent);
if (parsedLines.length === 0) { if (parsedLines.length === 0) {
return ( return (
<Box <Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
borderStyle="round"
borderColor={theme?.semanticColors.border.default}
padding={1}
>
<Text dimColor>No changes detected.</Text> <Text dimColor>No changes detected.</Text>
</Box> </Box>
); );
@ -162,7 +158,6 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
tabWidth, tabWidth,
availableTerminalHeight, availableTerminalHeight,
terminalWidth, terminalWidth,
theme,
); );
} }
@ -175,7 +170,6 @@ const renderDiffContent = (
tabWidth = DEFAULT_TAB_WIDTH, tabWidth = DEFAULT_TAB_WIDTH,
availableTerminalHeight: number | undefined, availableTerminalHeight: number | undefined,
terminalWidth: number, terminalWidth: number,
theme: import('../../themes/theme.js').Theme | undefined,
) => { ) => {
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing // 1. Normalize whitespace (replace tabs with spaces) *before* further processing
const normalizedLines = parsedLines.map((line) => ({ const normalizedLines = parsedLines.map((line) => ({
@ -190,11 +184,7 @@ const renderDiffContent = (
if (displayableLines.length === 0) { if (displayableLines.length === 0) {
return ( return (
<Box <Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
borderStyle="round"
borderColor={theme?.semanticColors.border.default}
padding={1}
>
<Text dimColor>No changes detected.</Text> <Text dimColor>No changes detected.</Text>
</Box> </Box>
); );
@ -258,10 +248,7 @@ const renderDiffContent = (
) { ) {
acc.push( acc.push(
<Box key={`gap-${index}`}> <Box key={`gap-${index}`}>
<Text <Text wrap="truncate" color={Colors.Gray}>
wrap="truncate"
color={theme?.semanticColors.border.default}
>
{'═'.repeat(terminalWidth)} {'═'.repeat(terminalWidth)}
</Text> </Text>
</Box>, </Box>,
@ -302,12 +289,12 @@ const renderDiffContent = (
acc.push( acc.push(
<Box key={lineKey} flexDirection="row"> <Box key={lineKey} flexDirection="row">
<Text <Text
color={theme?.semanticColors.text.secondary} color={theme.text.secondary}
backgroundColor={ backgroundColor={
line.type === 'add' line.type === 'add'
? theme?.semanticColors.background.diff.added ? theme.background.diff.added
: line.type === 'del' : line.type === 'del'
? theme?.semanticColors.background.diff.removed ? theme.background.diff.removed
: undefined : undefined
} }
> >
@ -315,32 +302,30 @@ const renderDiffContent = (
</Text> </Text>
{line.type === 'context' ? ( {line.type === 'context' ? (
<> <>
<Text color={theme?.semanticColors.text.primary}> <Text>{prefixSymbol} </Text>
{prefixSymbol}{' '}
</Text>
<Text wrap="wrap"> <Text wrap="wrap">
{colorizeLine(displayContent, language, theme)} {colorizeLine(displayContent, language)}
</Text> </Text>
</> </>
) : ( ) : (
<Text <Text
backgroundColor={ backgroundColor={
line.type === 'add' line.type === 'add'
? theme?.semanticColors.background.diff.added ? theme.background.diff.added
: theme?.semanticColors.background.diff.removed : theme.background.diff.removed
} }
wrap="wrap" wrap="wrap"
> >
<Text <Text
color={ color={
line.type === 'add' line.type === 'add'
? theme?.semanticColors.status.success ? theme.status.success
: theme?.semanticColors.status.error : theme.status.error
} }
> >
{prefixSymbol} {prefixSymbol}
</Text>{' '} </Text>{' '}
{colorizeLine(displayContent, language, theme)} {colorizeLine(displayContent, language)}
</Text> </Text>
)} )}
</Box>, </Box>,

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
interface ErrorMessageProps { interface ErrorMessageProps {
text: string; text: string;
@ -19,10 +19,10 @@ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
return ( return (
<Box flexDirection="row" marginBottom={1}> <Box flexDirection="row" marginBottom={1}>
<Box width={prefixWidth}> <Box width={prefixWidth}>
<Text color={theme.status.error}>{prefix}</Text> <Text color={Colors.AccentRed}>{prefix}</Text>
</Box> </Box>
<Box flexGrow={1}> <Box flexGrow={1}>
<Text wrap="wrap" color={theme.status.error}> <Text wrap="wrap" color={Colors.AccentRed}>
{text} {text}
</Text> </Text>
</Box> </Box>

View File

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
interface GeminiMessageProps { interface GeminiMessageProps {
text: string; text: string;
@ -28,7 +28,7 @@ export const GeminiMessage: React.FC<GeminiMessageProps> = ({
return ( return (
<Box flexDirection="row"> <Box flexDirection="row">
<Box width={prefixWidth}> <Box width={prefixWidth}>
<Text color={theme.text.accent}>{prefix}</Text> <Text color={Colors.AccentPurple}>{prefix}</Text>
</Box> </Box>
<Box flexGrow={1} flexDirection="column"> <Box flexGrow={1} flexDirection="column">
<MarkdownDisplay <MarkdownDisplay

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
interface InfoMessageProps { interface InfoMessageProps {
text: string; text: string;
@ -19,10 +19,10 @@ export const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
return ( return (
<Box flexDirection="row" marginTop={1}> <Box flexDirection="row" marginTop={1}>
<Box width={prefixWidth}> <Box width={prefixWidth}>
<Text color={theme.status.warning}>{prefix}</Text> <Text color={Colors.AccentYellow}>{prefix}</Text>
</Box> </Box>
<Box flexGrow={1}> <Box flexGrow={1}>
<Text wrap="wrap" color={theme.status.warning}> <Text wrap="wrap" color={Colors.AccentYellow}>
{text} {text}
</Text> </Text>
</Box> </Box>

View File

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { DiffRenderer } from './DiffRenderer.js'; import { DiffRenderer } from './DiffRenderer.js';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
import { import {
ToolCallConfirmationDetails, ToolCallConfirmationDetails,
ToolConfirmationOutcome, ToolConfirmationOutcome,
@ -112,13 +112,13 @@ export const ToolConfirmationMessage: React.FC<
<Box <Box
minWidth="90%" minWidth="90%"
borderStyle="round" borderStyle="round"
borderColor={theme.border.default} borderColor={Colors.Gray}
justifyContent="space-around" justifyContent="space-around"
padding={1} padding={1}
overflow="hidden" overflow="hidden"
> >
<Text color={theme.text.primary}>Modify in progress: </Text> <Text>Modify in progress: </Text>
<Text color={theme.status.success}> <Text color={Colors.AccentGreen}>
Save and close external editor to continue Save and close external editor to continue
</Text> </Text>
</Box> </Box>
@ -192,7 +192,7 @@ export const ToolConfirmationMessage: React.FC<
maxWidth={Math.max(childWidth - 4, 1)} maxWidth={Math.max(childWidth - 4, 1)}
> >
<Box> <Box>
<Text color={theme.text.accent}>{executionProps.command}</Text> <Text color={Colors.AccentCyan}>{executionProps.command}</Text>
</Box> </Box>
</MaxSizedBox> </MaxSizedBox>
</Box> </Box>
@ -222,15 +222,12 @@ export const ToolConfirmationMessage: React.FC<
bodyContent = ( bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}> <Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={theme.text.accent}>{infoProps.prompt}</Text> <Text color={Colors.AccentCyan}>{infoProps.prompt}</Text>
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && ( {displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
<Box flexDirection="column" marginTop={1}> <Box flexDirection="column" marginTop={1}>
<Text color={theme.text.primary}>URLs to fetch:</Text> <Text>URLs to fetch:</Text>
{infoProps.urls.map((url) => ( {infoProps.urls.map((url) => (
<Text key={url} color={theme.text.primary}> <Text key={url}> - {url}</Text>
{' '}
- {url}
</Text>
))} ))}
</Box> </Box>
)} )}
@ -242,8 +239,8 @@ export const ToolConfirmationMessage: React.FC<
bodyContent = ( bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}> <Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={theme.text.accent}>MCP Server: {mcpProps.serverName}</Text> <Text color={Colors.AccentCyan}>MCP Server: {mcpProps.serverName}</Text>
<Text color={theme.text.accent}>Tool: {mcpProps.toolName}</Text> <Text color={Colors.AccentCyan}>Tool: {mcpProps.toolName}</Text>
</Box> </Box>
); );
@ -278,9 +275,7 @@ export const ToolConfirmationMessage: React.FC<
{/* Confirmation Question */} {/* Confirmation Question */}
<Box marginBottom={1} flexShrink={0}> <Box marginBottom={1} flexShrink={0}>
<Text wrap="truncate" color={theme.text.primary}> <Text wrap="truncate">{question}</Text>
{question}
</Text>
</Box> </Box>
{/* Select Input for Options */} {/* Select Input for Options */}

View File

@ -9,7 +9,7 @@ import { Box } from 'ink';
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js'; import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
import { ToolMessage } from './ToolMessage.js'; import { ToolMessage } from './ToolMessage.js';
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js'; import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
import { Config } from '@google/gemini-cli-core'; import { Config } from '@google/gemini-cli-core';
import { SHELL_COMMAND_NAME } from '../../constants.js'; import { SHELL_COMMAND_NAME } from '../../constants.js';
@ -35,7 +35,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
); );
const isShellCommand = toolCalls.some((t) => t.name === SHELL_COMMAND_NAME); const isShellCommand = toolCalls.some((t) => t.name === SHELL_COMMAND_NAME);
const borderColor = const borderColor =
hasPending || isShellCommand ? theme.status.warning : theme.border.default; hasPending || isShellCommand ? Colors.AccentYellow : Colors.Gray;
const staticHeight = /* border */ 2 + /* marginBottom */ 1; const staticHeight = /* border */ 2 + /* marginBottom */ 1;
// This is a bit of a magic number, but it accounts for the border and // This is a bit of a magic number, but it accounts for the border and

View File

@ -8,7 +8,7 @@ import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js'; import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
import { DiffRenderer } from './DiffRenderer.js'; import { DiffRenderer } from './DiffRenderer.js';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js'; import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
import { MaxSizedBox } from '../shared/MaxSizedBox.js'; import { MaxSizedBox } from '../shared/MaxSizedBox.js';
@ -90,9 +90,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
{typeof resultDisplay === 'string' && !renderOutputAsMarkdown && ( {typeof resultDisplay === 'string' && !renderOutputAsMarkdown && (
<MaxSizedBox maxHeight={availableHeight} maxWidth={childWidth}> <MaxSizedBox maxHeight={availableHeight} maxWidth={childWidth}>
<Box> <Box>
<Text wrap="wrap" color={theme.text.primary}> <Text wrap="wrap">{resultDisplay}</Text>
{resultDisplay}
</Text>
</Box> </Box>
</MaxSizedBox> </MaxSizedBox>
)} )}
@ -120,7 +118,7 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
}) => ( }) => (
<Box minWidth={STATUS_INDICATOR_WIDTH}> <Box minWidth={STATUS_INDICATOR_WIDTH}>
{status === ToolCallStatus.Pending && ( {status === ToolCallStatus.Pending && (
<Text color={theme.status.success}>o</Text> <Text color={Colors.AccentGreen}>o</Text>
)} )}
{status === ToolCallStatus.Executing && ( {status === ToolCallStatus.Executing && (
<GeminiRespondingSpinner <GeminiRespondingSpinner
@ -129,18 +127,18 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
/> />
)} )}
{status === ToolCallStatus.Success && ( {status === ToolCallStatus.Success && (
<Text color={theme.status.success}></Text> <Text color={Colors.AccentGreen}></Text>
)} )}
{status === ToolCallStatus.Confirming && ( {status === ToolCallStatus.Confirming && (
<Text color={theme.status.warning}>?</Text> <Text color={Colors.AccentYellow}>?</Text>
)} )}
{status === ToolCallStatus.Canceled && ( {status === ToolCallStatus.Canceled && (
<Text color={theme.status.warning} bold> <Text color={Colors.AccentYellow} bold>
- -
</Text> </Text>
)} )}
{status === ToolCallStatus.Error && ( {status === ToolCallStatus.Error && (
<Text color={theme.status.error} bold> <Text color={Colors.AccentRed} bold>
x x
</Text> </Text>
)} )}
@ -162,11 +160,11 @@ const ToolInfo: React.FC<ToolInfo> = ({
const nameColor = React.useMemo<string>(() => { const nameColor = React.useMemo<string>(() => {
switch (emphasis) { switch (emphasis) {
case 'high': case 'high':
return theme.text.primary; return Colors.Foreground;
case 'medium': case 'medium':
return theme.text.primary; return Colors.Foreground;
case 'low': case 'low':
return theme.text.secondary; return Colors.Gray;
default: { default: {
const exhaustiveCheck: never = emphasis; const exhaustiveCheck: never = emphasis;
return exhaustiveCheck; return exhaustiveCheck;
@ -178,19 +176,18 @@ const ToolInfo: React.FC<ToolInfo> = ({
<Text <Text
wrap="truncate-end" wrap="truncate-end"
strikethrough={status === ToolCallStatus.Canceled} strikethrough={status === ToolCallStatus.Canceled}
color={theme.text.primary}
> >
<Text color={nameColor} bold> <Text color={nameColor} bold>
{name} {name}
</Text>{' '} </Text>{' '}
<Text color={theme.text.secondary}>{description}</Text> <Text color={Colors.Gray}>{description}</Text>
</Text> </Text>
</Box> </Box>
); );
}; };
const TrailingIndicator: React.FC = () => ( const TrailingIndicator: React.FC = () => (
<Text color={theme.text.primary} wrap="truncate"> <Text color={Colors.Foreground} wrap="truncate">
{' '} {' '}
</Text> </Text>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
interface UserMessageProps { interface UserMessageProps {
text: string; text: string;
@ -17,8 +17,8 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
const prefixWidth = prefix.length; const prefixWidth = prefix.length;
const isSlashCommand = text.startsWith('/'); const isSlashCommand = text.startsWith('/');
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary; const textColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
const borderColor = isSlashCommand ? theme.text.accent : theme.border.default; const borderColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
return ( return (
<Box <Box

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
interface UserShellMessageProps { interface UserShellMessageProps {
text: string; text: string;
@ -18,8 +18,8 @@ export const UserShellMessage: React.FC<UserShellMessageProps> = ({ text }) => {
return ( return (
<Box> <Box>
<Text color={theme.text.accent}>$ </Text> <Text color={Colors.AccentCyan}>$ </Text>
<Text color={theme.text.primary}>{commandToDisplay}</Text> <Text>{commandToDisplay}</Text>
</Box> </Box>
); );
}; };

View File

@ -7,7 +7,7 @@
import React, { Fragment, useEffect, useId } from 'react'; import React, { Fragment, useEffect, useId } from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import stringWidth from 'string-width'; import stringWidth from 'string-width';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
import { toCodePoints } from '../../utils/textUtils.js'; import { toCodePoints } from '../../utils/textUtils.js';
import { useOverflowActions } from '../../contexts/OverflowContext.js'; import { useOverflowActions } from '../../contexts/OverflowContext.js';
@ -173,7 +173,6 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
<Box key={index}> <Box key={index}>
{line.length > 0 ? ( {line.length > 0 ? (
line.map((segment, segIndex) => ( line.map((segment, segIndex) => (
// Avoid adding color styles to this <Text> element, breaks code colorization
<Text key={segIndex} {...segment.props}> <Text key={segIndex} {...segment.props}>
{segment.text} {segment.text}
</Text> </Text>
@ -187,14 +186,14 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
return ( return (
<Box flexDirection="column" width={maxWidth} flexShrink={0}> <Box flexDirection="column" width={maxWidth} flexShrink={0}>
{totalHiddenLines > 0 && overflowDirection === 'top' && ( {totalHiddenLines > 0 && overflowDirection === 'top' && (
<Text color={theme.text.secondary} wrap="truncate"> <Text color={Colors.Gray} wrap="truncate">
... first {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '} ... first {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
hidden ... hidden ...
</Text> </Text>
)} )}
{visibleLines} {visibleLines}
{totalHiddenLines > 0 && overflowDirection === 'bottom' && ( {totalHiddenLines > 0 && overflowDirection === 'bottom' && (
<Text color={theme.text.secondary} wrap="truncate"> <Text color={Colors.Gray} wrap="truncate">
... last {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '} ... last {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
hidden ... hidden ...
</Text> </Text>

View File

@ -6,7 +6,7 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js'; import { Colors } from '../../colors.js';
import { useKeypress } from '../../hooks/useKeypress.js'; import { useKeypress } from '../../hooks/useKeypress.js';
/** /**
@ -164,9 +164,7 @@ export function RadioButtonSelect<T>({
return ( return (
<Box flexDirection="column"> <Box flexDirection="column">
{showScrollArrows && ( {showScrollArrows && (
<Text <Text color={scrollOffset > 0 ? Colors.Foreground : Colors.Gray}>
color={scrollOffset > 0 ? theme.text.primary : theme.text.secondary}
>
</Text> </Text>
)} )}
@ -174,18 +172,18 @@ export function RadioButtonSelect<T>({
const itemIndex = scrollOffset + index; const itemIndex = scrollOffset + index;
const isSelected = activeIndex === itemIndex; const isSelected = activeIndex === itemIndex;
let textColor = theme.text.primary; let textColor = Colors.Foreground;
let numberColor = theme.text.primary; let numberColor = Colors.Foreground;
if (isSelected) { if (isSelected) {
textColor = theme.status.success; textColor = Colors.AccentGreen;
numberColor = theme.status.success; numberColor = Colors.AccentGreen;
} else if (item.disabled) { } else if (item.disabled) {
textColor = theme.text.secondary; textColor = Colors.Gray;
numberColor = theme.text.secondary; numberColor = Colors.Gray;
} }
if (!showNumbers) { if (!showNumbers) {
numberColor = theme.text.secondary; numberColor = Colors.Gray;
} }
const numberColumnWidth = String(items.length).length; const numberColumnWidth = String(items.length).length;
@ -196,9 +194,7 @@ export function RadioButtonSelect<T>({
return ( return (
<Box key={item.label} alignItems="center"> <Box key={item.label} alignItems="center">
<Box minWidth={2} flexShrink={0}> <Box minWidth={2} flexShrink={0}>
<Text <Text color={isSelected ? Colors.AccentGreen : Colors.Foreground}>
color={isSelected ? theme.status.success : theme.text.primary}
>
{isSelected ? '●' : ' '} {isSelected ? '●' : ' '}
</Text> </Text>
</Box> </Box>
@ -212,9 +208,7 @@ export function RadioButtonSelect<T>({
{item.themeNameDisplay && item.themeTypeDisplay ? ( {item.themeNameDisplay && item.themeTypeDisplay ? (
<Text color={textColor} wrap="truncate"> <Text color={textColor} wrap="truncate">
{item.themeNameDisplay}{' '} {item.themeNameDisplay}{' '}
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>{item.themeTypeDisplay}</Text>
{item.themeTypeDisplay}
</Text>
</Text> </Text>
) : ( ) : (
<Text color={textColor} wrap="truncate"> <Text color={textColor} wrap="truncate">
@ -228,8 +222,8 @@ export function RadioButtonSelect<T>({
<Text <Text
color={ color={
scrollOffset + maxItemsToShow < items.length scrollOffset + maxItemsToShow < items.length
? theme.text.primary ? Colors.Foreground
: theme.text.secondary : Colors.Gray
} }
> >

View File

@ -9,7 +9,7 @@ import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
import { usePrivacySettings } from '../hooks/usePrivacySettings.js'; import { usePrivacySettings } from '../hooks/usePrivacySettings.js';
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js'; import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
import { Config } from '@google/gemini-cli-core'; import { Config } from '@google/gemini-cli-core';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useKeypress } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js';
interface CloudFreePrivacyNoticeProps { interface CloudFreePrivacyNoticeProps {
@ -34,16 +34,16 @@ export const CloudFreePrivacyNotice = ({
); );
if (privacyState.isLoading) { if (privacyState.isLoading) {
return <Text color={theme.text.secondary}>Loading...</Text>; return <Text color={Colors.Gray}>Loading...</Text>;
} }
if (privacyState.error) { if (privacyState.error) {
return ( return (
<Box flexDirection="column" marginY={1}> <Box flexDirection="column" marginY={1}>
<Text color={theme.status.error}> <Text color={Colors.AccentRed}>
Error loading Opt-in settings: {privacyState.error} Error loading Opt-in settings: {privacyState.error}
</Text> </Text>
<Text color={theme.text.secondary}>Press Esc to exit.</Text> <Text color={Colors.Gray}>Press Esc to exit.</Text>
</Box> </Box>
); );
} }
@ -59,17 +59,17 @@ export const CloudFreePrivacyNotice = ({
return ( return (
<Box flexDirection="column" marginY={1}> <Box flexDirection="column" marginY={1}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Gemini Code Assist for Individuals Privacy Notice Gemini Code Assist for Individuals Privacy Notice
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
This notice and our Privacy Policy This notice and our Privacy Policy
<Text color={theme.text.link}>[1]</Text> describe how Gemini Code Assist <Text color={Colors.AccentBlue}>[1]</Text> describe how Gemini Code
handles your data. Please read them carefully. Assist handles your data. Please read them carefully.
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
When you use Gemini Code Assist for individuals with Gemini CLI, Google When you use Gemini Code Assist for individuals with Gemini CLI, Google
collects your prompts, related code, generated output, code edits, collects your prompts, related code, generated output, code edits,
related feature usage information, and your feedback to provide, related feature usage information, and your feedback to provide,
@ -77,7 +77,7 @@ export const CloudFreePrivacyNotice = ({
technologies. technologies.
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
To help with quality and improve our products (such as generative To help with quality and improve our products (such as generative
machine-learning models), human reviewers may read, annotate, and machine-learning models), human reviewers may read, annotate, and
process the data collected above. We take steps to protect your privacy process the data collected above. We take steps to protect your privacy
@ -90,7 +90,7 @@ export const CloudFreePrivacyNotice = ({
</Text> </Text>
<Newline /> <Newline />
<Box flexDirection="column"> <Box flexDirection="column">
<Text color={theme.text.primary}> <Text>
Allow Google to use this data to develop and improve our products? Allow Google to use this data to develop and improve our products?
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
@ -106,14 +106,12 @@ export const CloudFreePrivacyNotice = ({
/> />
</Box> </Box>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
<Text color={theme.text.link}>[1]</Text>{' '} <Text color={Colors.AccentBlue}>[1]</Text>{' '}
https://policies.google.com/privacy https://policies.google.com/privacy
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>Press Enter to choose an option and exit.</Text>
Press Enter to choose an option and exit.
</Text>
</Box> </Box>
); );
}; };

View File

@ -5,7 +5,7 @@
*/ */
import { Box, Newline, Text } from 'ink'; import { Box, Newline, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useKeypress } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js';
interface CloudPaidPrivacyNoticeProps { interface CloudPaidPrivacyNoticeProps {
@ -26,14 +26,14 @@ export const CloudPaidPrivacyNotice = ({
return ( return (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Vertex AI Notice Vertex AI Notice
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
Service Specific Terms<Text color={theme.text.link}>[1]</Text> are Service Specific Terms<Text color={Colors.AccentBlue}>[1]</Text> are
incorporated into the agreement under which Google has agreed to provide incorporated into the agreement under which Google has agreed to provide
Google Cloud Platform<Text color={theme.status.success}>[2]</Text> to Google Cloud Platform<Text color={Colors.AccentGreen}>[2]</Text> to
Customer (the Agreement). If the Agreement authorizes the resale or Customer (the Agreement). If the Agreement authorizes the resale or
supply of Google Cloud Platform under a Google Cloud partner or reseller supply of Google Cloud Platform under a Google Cloud partner or reseller
program, then except for in the section entitled Partner-Specific program, then except for in the section entitled Partner-Specific
@ -44,16 +44,16 @@ export const CloudPaidPrivacyNotice = ({
them in the Agreement. them in the Agreement.
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
<Text color={theme.text.link}>[1]</Text>{' '} <Text color={Colors.AccentBlue}>[1]</Text>{' '}
https://cloud.google.com/terms/service-terms https://cloud.google.com/terms/service-terms
</Text> </Text>
<Text color={theme.text.primary}> <Text>
<Text color={theme.status.success}>[2]</Text>{' '} <Text color={Colors.AccentGreen}>[2]</Text>{' '}
https://cloud.google.com/terms/services https://cloud.google.com/terms/services
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.secondary}>Press Esc to exit.</Text> <Text color={Colors.Gray}>Press Esc to exit.</Text>
</Box> </Box>
); );
}; };

View File

@ -5,7 +5,7 @@
*/ */
import { Box, Newline, Text } from 'ink'; import { Box, Newline, Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { useKeypress } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js';
interface GeminiPrivacyNoticeProps { interface GeminiPrivacyNoticeProps {
@ -24,39 +24,39 @@ export const GeminiPrivacyNotice = ({ onExit }: GeminiPrivacyNoticeProps) => {
return ( return (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentPurple}>
Gemini API Key Notice Gemini API Key Notice
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
By using the Gemini API<Text color={theme.text.link}>[1]</Text>, Google By using the Gemini API<Text color={Colors.AccentBlue}>[1]</Text>,
AI Studio Google AI Studio
<Text color={theme.status.error}>[2]</Text>, and the other Google <Text color={Colors.AccentRed}>[2]</Text>, and the other Google
developer services that reference these terms (collectively, the developer services that reference these terms (collectively, the
&quot;APIs&quot; or &quot;Services&quot;), you are agreeing to Google &quot;APIs&quot; or &quot;Services&quot;), you are agreeing to Google
APIs Terms of Service (the &quot;API Terms&quot;) APIs Terms of Service (the &quot;API Terms&quot;)
<Text color={theme.status.success}>[3]</Text>, and the Gemini API <Text color={Colors.AccentGreen}>[3]</Text>, and the Gemini API
Additional Terms of Service (the &quot;Additional Terms&quot;) Additional Terms of Service (the &quot;Additional Terms&quot;)
<Text color={theme.text.accent}>[4]</Text>. <Text color={Colors.AccentPurple}>[4]</Text>.
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.primary}> <Text>
<Text color={theme.text.link}>[1]</Text>{' '} <Text color={Colors.AccentBlue}>[1]</Text>{' '}
https://ai.google.dev/docs/gemini_api_overview https://ai.google.dev/docs/gemini_api_overview
</Text> </Text>
<Text color={theme.text.primary}> <Text>
<Text color={theme.status.error}>[2]</Text> https://aistudio.google.com/ <Text color={Colors.AccentRed}>[2]</Text> https://aistudio.google.com/
</Text> </Text>
<Text color={theme.text.primary}> <Text>
<Text color={theme.status.success}>[3]</Text>{' '} <Text color={Colors.AccentGreen}>[3]</Text>{' '}
https://developers.google.com/terms https://developers.google.com/terms
</Text> </Text>
<Text color={theme.text.primary}> <Text>
<Text color={theme.text.accent}>[4]</Text>{' '} <Text color={Colors.AccentPurple}>[4]</Text>{' '}
https://ai.google.dev/gemini-api/terms https://ai.google.dev/gemini-api/terms
</Text> </Text>
<Newline /> <Newline />
<Text color={theme.text.secondary}>Press Esc to exit.</Text> <Text color={Colors.Gray}>Press Esc to exit.</Text>
</Box> </Box>
); );
}; };

View File

@ -43,6 +43,8 @@ const noColorSemanticColors: SemanticColors = {
focused: '', focused: '',
}, },
ui: { ui: {
comment: '',
symbol: '',
gradient: [], gradient: [],
}, },
status: { status: {

View File

@ -25,6 +25,8 @@ export interface SemanticColors {
focused: string; focused: string;
}; };
ui: { ui: {
comment: string;
symbol: string;
gradient: string[] | undefined; gradient: string[] | undefined;
}; };
status: { status: {
@ -53,6 +55,8 @@ export const lightSemanticColors: SemanticColors = {
focused: lightTheme.AccentBlue, focused: lightTheme.AccentBlue,
}, },
ui: { ui: {
comment: lightTheme.Comment,
symbol: lightTheme.Gray,
gradient: lightTheme.GradientColors, gradient: lightTheme.GradientColors,
}, },
status: { status: {
@ -81,6 +85,8 @@ export const darkSemanticColors: SemanticColors = {
focused: darkTheme.AccentBlue, focused: darkTheme.AccentBlue,
}, },
ui: { ui: {
comment: darkTheme.Comment,
symbol: darkTheme.Gray,
gradient: darkTheme.GradientColors, gradient: darkTheme.GradientColors,
}, },
status: { status: {
@ -109,6 +115,8 @@ export const ansiSemanticColors: SemanticColors = {
focused: ansiTheme.AccentBlue, focused: ansiTheme.AccentBlue,
}, },
ui: { ui: {
comment: ansiTheme.Comment,
symbol: ansiTheme.Gray,
gradient: ansiTheme.GradientColors, gradient: ansiTheme.GradientColors,
}, },
status: { status: {

View File

@ -235,7 +235,7 @@ export function createCustomTheme(customTheme: CustomTheme): Theme {
customTheme.background?.diff?.added ?? customTheme.DiffAdded ?? '', customTheme.background?.diff?.added ?? customTheme.DiffAdded ?? '',
DiffRemoved: DiffRemoved:
customTheme.background?.diff?.removed ?? customTheme.DiffRemoved ?? '', customTheme.background?.diff?.removed ?? customTheme.DiffRemoved ?? '',
Comment: customTheme.text?.secondary ?? customTheme.Comment ?? '', Comment: customTheme.ui?.comment ?? customTheme.Comment ?? '',
Gray: customTheme.text?.secondary ?? customTheme.Gray ?? '', Gray: customTheme.text?.secondary ?? customTheme.Gray ?? '',
GradientColors: customTheme.ui?.gradient ?? customTheme.GradientColors, GradientColors: customTheme.ui?.gradient ?? customTheme.GradientColors,
}; };
@ -397,6 +397,8 @@ export function createCustomTheme(customTheme: CustomTheme): Theme {
focused: colors.AccentBlue, focused: colors.AccentBlue,
}, },
ui: { ui: {
comment: colors.Comment,
symbol: colors.Gray,
gradient: colors.GradientColors, gradient: colors.GradientColors,
}, },
status: { status: {

View File

@ -21,7 +21,6 @@ import {
MINIMUM_MAX_HEIGHT, MINIMUM_MAX_HEIGHT,
} from '../components/shared/MaxSizedBox.js'; } from '../components/shared/MaxSizedBox.js';
import { LoadedSettings } from '../../config/settings.js'; import { LoadedSettings } from '../../config/settings.js';
import { theme as semanticTheme } from '../semantic-colors.js';
// Configure theming and parsing utilities. // Configure theming and parsing utilities.
const lowlight = createLowlight(common); const lowlight = createLowlight(common);
@ -172,7 +171,7 @@ export function colorizeCode(
return ( return (
<Box key={index}> <Box key={index}>
{showLineNumbers && ( {showLineNumbers && (
<Text color={semanticTheme.text.secondary}> <Text color={activeTheme.colors.Gray}>
{`${String(index + 1 + hiddenLinesCount).padStart( {`${String(index + 1 + hiddenLinesCount).padStart(
padWidth, padWidth,
' ', ' ',
@ -209,7 +208,7 @@ export function colorizeCode(
{`${String(index + 1).padStart(padWidth, ' ')} `} {`${String(index + 1).padStart(padWidth, ' ')} `}
</Text> </Text>
)} )}
<Text color={semanticTheme.text.secondary}>{line}</Text> <Text color={activeTheme.colors.Gray}>{line}</Text>
</Box> </Box>
))} ))}
</MaxSizedBox> </MaxSizedBox>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text } from 'ink'; import { Text } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import stringWidth from 'string-width'; import stringWidth from 'string-width';
// Constants for Markdown parsing // Constants for Markdown parsing
@ -31,7 +31,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
while ((match = inlineRegex.exec(text)) !== null) { while ((match = inlineRegex.exec(text)) !== null) {
if (match.index > lastIndex) { if (match.index > lastIndex) {
nodes.push( nodes.push(
<Text key={`t-${lastIndex}`} color={theme.text.primary}> <Text key={`t-${lastIndex}`}>
{text.slice(lastIndex, match.index)} {text.slice(lastIndex, match.index)}
</Text>, </Text>,
); );
@ -48,7 +48,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
fullMatch.length > BOLD_MARKER_LENGTH * 2 fullMatch.length > BOLD_MARKER_LENGTH * 2
) { ) {
renderedNode = ( renderedNode = (
<Text key={key} bold color={theme.text.primary}> <Text key={key} bold>
{fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH)} {fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH)}
</Text> </Text>
); );
@ -60,13 +60,13 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
!/\w/.test( !/\w/.test(
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1), text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1),
) && ) &&
!/\S[./]/.test(text.substring(match.index - 2, match.index)) && !/\S[./\\]/.test(text.substring(match.index - 2, match.index)) &&
!/[./]\S/.test( !/[./\\]\S/.test(
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2), text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2),
) )
) { ) {
renderedNode = ( renderedNode = (
<Text key={key} italic color={theme.text.primary}> <Text key={key} italic>
{fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH)} {fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH)}
</Text> </Text>
); );
@ -76,7 +76,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2 fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2
) { ) {
renderedNode = ( renderedNode = (
<Text key={key} strikethrough color={theme.text.primary}> <Text key={key} strikethrough>
{fullMatch.slice( {fullMatch.slice(
STRIKETHROUGH_MARKER_LENGTH, STRIKETHROUGH_MARKER_LENGTH,
-STRIKETHROUGH_MARKER_LENGTH, -STRIKETHROUGH_MARKER_LENGTH,
@ -91,7 +91,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s); const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
if (codeMatch && codeMatch[2]) { if (codeMatch && codeMatch[2]) {
renderedNode = ( renderedNode = (
<Text key={key} color={theme.text.accent}> <Text key={key} color={Colors.AccentPurple}>
{codeMatch[2]} {codeMatch[2]}
</Text> </Text>
); );
@ -106,9 +106,9 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
const linkText = linkMatch[1]; const linkText = linkMatch[1];
const url = linkMatch[2]; const url = linkMatch[2];
renderedNode = ( renderedNode = (
<Text key={key} color={theme.text.primary}> <Text key={key}>
{linkText} {linkText}
<Text color={theme.text.link}> ({url})</Text> <Text color={Colors.AccentBlue}> ({url})</Text>
</Text> </Text>
); );
} }
@ -116,10 +116,10 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
fullMatch.startsWith('<u>') && fullMatch.startsWith('<u>') &&
fullMatch.endsWith('</u>') && fullMatch.endsWith('</u>') &&
fullMatch.length > fullMatch.length >
UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1 UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1 // -1 because length is compared to combined length of start and end tags
) { ) {
renderedNode = ( renderedNode = (
<Text key={key} underline color={theme.text.primary}> <Text key={key} underline>
{fullMatch.slice( {fullMatch.slice(
UNDERLINE_TAG_START_LENGTH, UNDERLINE_TAG_START_LENGTH,
-UNDERLINE_TAG_END_LENGTH, -UNDERLINE_TAG_END_LENGTH,
@ -132,22 +132,12 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
renderedNode = null; renderedNode = null;
} }
nodes.push( nodes.push(renderedNode ?? <Text key={key}>{fullMatch}</Text>);
renderedNode ?? (
<Text key={key} color={theme.text.primary}>
{fullMatch}
</Text>
),
);
lastIndex = inlineRegex.lastIndex; lastIndex = inlineRegex.lastIndex;
} }
if (lastIndex < text.length) { if (lastIndex < text.length) {
nodes.push( nodes.push(<Text key={`t-${lastIndex}`}>{text.slice(lastIndex)}</Text>);
<Text key={`t-${lastIndex}`} color={theme.text.primary}>
{text.slice(lastIndex)}
</Text>,
);
} }
return <>{nodes.filter((node) => node !== null)}</>; return <>{nodes.filter((node) => node !== null)}</>;

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { colorizeCode } from './CodeColorizer.js'; import { colorizeCode } from './CodeColorizer.js';
import { TableRenderer } from './TableRenderer.js'; import { TableRenderer } from './TableRenderer.js';
import { RenderInline } from './InlineMarkdownRenderer.js'; import { RenderInline } from './InlineMarkdownRenderer.js';
@ -115,7 +115,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
// Not a table, treat as regular text // Not a table, treat as regular text
addContentBlock( addContentBlock(
<Box key={key}> <Box key={key}>
<Text wrap="wrap" color={theme.text.primary}> <Text wrap="wrap">
<RenderInline text={line} /> <RenderInline text={line} />
</Text> </Text>
</Box>, </Box>,
@ -154,7 +154,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
if (line.trim().length > 0) { if (line.trim().length > 0) {
addContentBlock( addContentBlock(
<Box key={key}> <Box key={key}>
<Text wrap="wrap" color={theme.text.primary}> <Text wrap="wrap">
<RenderInline text={line} /> <RenderInline text={line} />
</Text> </Text>
</Box>, </Box>,
@ -173,35 +173,35 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
switch (level) { switch (level) {
case 1: case 1:
headerNode = ( headerNode = (
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentCyan}>
<RenderInline text={headerText} /> <RenderInline text={headerText} />
</Text> </Text>
); );
break; break;
case 2: case 2:
headerNode = ( headerNode = (
<Text bold color={theme.text.link}> <Text bold color={Colors.AccentBlue}>
<RenderInline text={headerText} /> <RenderInline text={headerText} />
</Text> </Text>
); );
break; break;
case 3: case 3:
headerNode = ( headerNode = (
<Text bold color={theme.text.primary}> <Text bold>
<RenderInline text={headerText} /> <RenderInline text={headerText} />
</Text> </Text>
); );
break; break;
case 4: case 4:
headerNode = ( headerNode = (
<Text italic color={theme.text.secondary}> <Text italic color={Colors.Gray}>
<RenderInline text={headerText} /> <RenderInline text={headerText} />
</Text> </Text>
); );
break; break;
default: default:
headerNode = ( headerNode = (
<Text color={theme.text.primary}> <Text>
<RenderInline text={headerText} /> <RenderInline text={headerText} />
</Text> </Text>
); );
@ -245,7 +245,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
} else { } else {
addContentBlock( addContentBlock(
<Box key={key}> <Box key={key}>
<Text wrap="wrap" color={theme.text.primary}> <Text wrap="wrap">
<RenderInline text={line} /> <RenderInline text={line} />
</Text> </Text>
</Box>, </Box>,
@ -314,9 +314,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
// Not enough space to even show the message meaningfully // Not enough space to even show the message meaningfully
return ( return (
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING}> <Box paddingLeft={CODE_BLOCK_PREFIX_PADDING}>
<Text color={theme.text.secondary}> <Text color={Colors.Gray}>... code is being written ...</Text>
... code is being written ...
</Text>
</Box> </Box>
); );
} }
@ -332,7 +330,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
return ( return (
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column"> <Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column">
{colorizedTruncatedCode} {colorizedTruncatedCode}
<Text color={theme.text.secondary}>... generating more ...</Text> <Text color={Colors.Gray}>... generating more ...</Text>
</Box> </Box>
); );
} }
@ -385,10 +383,10 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
flexDirection="row" flexDirection="row"
> >
<Box width={prefixWidth}> <Box width={prefixWidth}>
<Text color={theme.text.accent}>{prefix}</Text> <Text>{prefix}</Text>
</Box> </Box>
<Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}> <Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}>
<Text wrap="wrap" color={theme.text.primary}> <Text wrap="wrap">
<RenderInline text={itemText} /> <RenderInline text={itemText} />
</Text> </Text>
</Box> </Box>

View File

@ -6,7 +6,7 @@
import React from 'react'; import React from 'react';
import { Text, Box } from 'ink'; import { Text, Box } from 'ink';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js'; import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
interface TableRendererProps { interface TableRendererProps {
@ -87,9 +87,9 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
const paddingNeeded = Math.max(0, contentWidth - actualDisplayWidth); const paddingNeeded = Math.max(0, contentWidth - actualDisplayWidth);
return ( return (
<Text color={theme.text.primary}> <Text>
{isHeader ? ( {isHeader ? (
<Text bold color={theme.text.accent}> <Text bold color={Colors.AccentCyan}>
<RenderInline text={cellContent} /> <RenderInline text={cellContent} />
</Text> </Text>
) : ( ) : (
@ -112,7 +112,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w)); const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w));
const border = char.left + borderParts.join(char.middle) + char.right; const border = char.left + borderParts.join(char.middle) + char.right;
return <Text color={theme.text.primary}>{border}</Text>; return <Text>{border}</Text>;
}; };
// Helper function to render a table row // Helper function to render a table row
@ -123,7 +123,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
}); });
return ( return (
<Text color={theme.text.primary}> <Text>
{' '} {' '}
{renderedCells.map((cell, index) => ( {renderedCells.map((cell, index) => (
<React.Fragment key={index}> <React.Fragment key={index}>

View File

@ -14,7 +14,7 @@ import {
CACHE_EFFICIENCY_HIGH, CACHE_EFFICIENCY_HIGH,
CACHE_EFFICIENCY_MEDIUM, CACHE_EFFICIENCY_MEDIUM,
} from './displayUtils.js'; } from './displayUtils.js';
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
describe('displayUtils', () => { describe('displayUtils', () => {
describe('getStatusColor', () => { describe('getStatusColor', () => {
@ -24,24 +24,24 @@ describe('displayUtils', () => {
}; };
it('should return green for values >= green threshold', () => { it('should return green for values >= green threshold', () => {
expect(getStatusColor(90, thresholds)).toBe(theme.status.success); expect(getStatusColor(90, thresholds)).toBe(Colors.AccentGreen);
expect(getStatusColor(80, thresholds)).toBe(theme.status.success); expect(getStatusColor(80, thresholds)).toBe(Colors.AccentGreen);
}); });
it('should return yellow for values < green and >= yellow threshold', () => { it('should return yellow for values < green and >= yellow threshold', () => {
expect(getStatusColor(79, thresholds)).toBe(theme.status.warning); expect(getStatusColor(79, thresholds)).toBe(Colors.AccentYellow);
expect(getStatusColor(50, thresholds)).toBe(theme.status.warning); expect(getStatusColor(50, thresholds)).toBe(Colors.AccentYellow);
}); });
it('should return red for values < yellow threshold', () => { it('should return red for values < yellow threshold', () => {
expect(getStatusColor(49, thresholds)).toBe(theme.status.error); expect(getStatusColor(49, thresholds)).toBe(Colors.AccentRed);
expect(getStatusColor(0, thresholds)).toBe(theme.status.error); expect(getStatusColor(0, thresholds)).toBe(Colors.AccentRed);
}); });
it('should return defaultColor for values < yellow threshold when provided', () => { it('should return defaultColor for values < yellow threshold when provided', () => {
expect( expect(
getStatusColor(49, thresholds, { defaultColor: theme.text.primary }), getStatusColor(49, thresholds, { defaultColor: Colors.Foreground }),
).toBe(theme.text.primary); ).toBe(Colors.Foreground);
}); });
}); });

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { theme } from '../semantic-colors.js'; import { Colors } from '../colors.js';
// --- Thresholds --- // --- Thresholds ---
export const TOOL_SUCCESS_RATE_HIGH = 95; export const TOOL_SUCCESS_RATE_HIGH = 95;
@ -23,10 +23,10 @@ export const getStatusColor = (
options: { defaultColor?: string } = {}, options: { defaultColor?: string } = {},
) => { ) => {
if (value >= thresholds.green) { if (value >= thresholds.green) {
return theme.status.success; return Colors.AccentGreen;
} }
if (value >= thresholds.yellow) { if (value >= thresholds.yellow) {
return theme.status.warning; return Colors.AccentYellow;
} }
return options.defaultColor || theme.status.error; return options.defaultColor || Colors.AccentRed;
}; };