mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
feat(ui): Semantic tokens refactor (#8087)
This commit is contained in:
@@ -10,6 +10,7 @@ import { Box, Text } from 'ink';
|
||||
import type { RadioSelectItem } from './components/shared/RadioButtonSelect.js';
|
||||
import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
|
||||
import { useKeypress } from './hooks/useKeypress.js';
|
||||
import { theme } from './semantic-colors.js';
|
||||
|
||||
export type IdeIntegrationNudgeResult = {
|
||||
userSelection: 'yes' | 'no' | 'dismiss';
|
||||
@@ -79,17 +80,17 @@ export function IdeIntegrationNudge({
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor="yellow"
|
||||
borderColor={theme.status.warning}
|
||||
padding={1}
|
||||
width="100%"
|
||||
marginLeft={1}
|
||||
>
|
||||
<Box marginBottom={1} flexDirection="column">
|
||||
<Text>
|
||||
<Text color="yellow">{'> '}</Text>
|
||||
<Text color={theme.status.warning}>{'> '}</Text>
|
||||
{`Do you want to connect ${ideName ?? 'your editor'} to Gemini CLI?`}
|
||||
</Text>
|
||||
<Text dimColor>{installText}</Text>
|
||||
<Text color={theme.text.secondary}>{installText}</Text>
|
||||
</Box>
|
||||
<RadioButtonSelect items={OPTIONS} onSelect={onComplete} />
|
||||
</Box>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
import { SettingScope } from '../../config/settings.js';
|
||||
@@ -149,14 +149,16 @@ Logging in with Google... Please restart Gemini CLI to continue.
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold>Get started</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text>How would you like to authenticate for this project?</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
How would you like to authenticate for this project?
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<RadioButtonSelect
|
||||
@@ -167,17 +169,19 @@ Logging in with Google... Please restart Gemini CLI to continue.
|
||||
</Box>
|
||||
{authError && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.AccentRed}>{authError}</Text>
|
||||
<Text color={theme.status.error}>{authError}</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.Gray}>(Use Enter to select)</Text>
|
||||
<Text color={theme.text.secondary}>(Use Enter to select)</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text>Terms of Services and Privacy Notice for Gemini CLI</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Terms of Services and Privacy Notice for Gemini CLI
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.AccentBlue}>
|
||||
<Text color={theme.text.link}>
|
||||
{
|
||||
'https://github.com/google-gemini/gemini-cli/blob/main/docs/tos-privacy.md'
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import type React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import Spinner from 'ink-spinner';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
|
||||
interface AuthInProgressProps {
|
||||
@@ -41,13 +41,13 @@ export function AuthInProgress({
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
{timedOut ? (
|
||||
<Text color={Colors.AccentRed}>
|
||||
<Text color={theme.status.error}>
|
||||
Authentication timed out. Please try again.
|
||||
</Text>
|
||||
) : (
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import * as fsPromises from 'node:fs/promises';
|
||||
import React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type {
|
||||
CommandContext,
|
||||
SlashCommand,
|
||||
@@ -126,7 +126,7 @@ const saveCommand: SlashCommand = {
|
||||
Text,
|
||||
null,
|
||||
'A checkpoint with the tag ',
|
||||
React.createElement(Text, { color: Colors.AccentPurple }, tag),
|
||||
React.createElement(Text, { color: theme.text.accent }, tag),
|
||||
' already exists. Do you want to overwrite it?',
|
||||
),
|
||||
originalInvocation: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
|
||||
interface AboutBoxProps {
|
||||
@@ -30,77 +30,77 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
|
||||
}) => (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
padding={1}
|
||||
marginY={1}
|
||||
width="100%"
|
||||
>
|
||||
<Box marginBottom={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
About Gemini CLI
|
||||
</Text>
|
||||
</Box>
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
CLI Version
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{cliVersion}</Text>
|
||||
<Text color={theme.text.primary}>{cliVersion}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
{GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && (
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
Git Commit
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{GIT_COMMIT_INFO}</Text>
|
||||
<Text color={theme.text.primary}>{GIT_COMMIT_INFO}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
Model
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{modelVersion}</Text>
|
||||
<Text color={theme.text.primary}>{modelVersion}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
Sandbox
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{sandboxEnv}</Text>
|
||||
<Text color={theme.text.primary}>{sandboxEnv}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
OS
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{osVersion}</Text>
|
||||
<Text color={theme.text.primary}>{osVersion}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
Auth Method
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType}
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -108,24 +108,24 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
|
||||
{gcpProject && (
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
GCP Project
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{gcpProject}</Text>
|
||||
<Text color={theme.text.primary}>{gcpProject}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{ideClient && (
|
||||
<Box flexDirection="row">
|
||||
<Box width="35%">
|
||||
<Text bold color={Colors.LightBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
IDE Client
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>{ideClient}</Text>
|
||||
<Text color={theme.text.primary}>{ideClient}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { ApprovalMode } from '@google/gemini-cli-core';
|
||||
|
||||
interface AutoAcceptIndicatorProps {
|
||||
@@ -22,12 +22,12 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
|
||||
|
||||
switch (approvalMode) {
|
||||
case ApprovalMode.AUTO_EDIT:
|
||||
textColor = Colors.AccentGreen;
|
||||
textColor = theme.status.success;
|
||||
textContent = 'accepting edits';
|
||||
subText = ' (shift + tab to toggle)';
|
||||
break;
|
||||
case ApprovalMode.YOLO:
|
||||
textColor = Colors.AccentRed;
|
||||
textColor = theme.status.error;
|
||||
textContent = 'YOLO mode';
|
||||
subText = ' (ctrl + y to toggle)';
|
||||
break;
|
||||
@@ -40,7 +40,7 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
|
||||
<Box>
|
||||
<Text color={textColor}>
|
||||
{textContent}
|
||||
{subText && <Text color={Colors.Gray}>{subText}</Text>}
|
||||
{subText && <Text color={theme.text.secondary}>{subText}</Text>}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { InputPrompt } from './InputPrompt.js';
|
||||
import { Footer, type FooterProps } from './Footer.js';
|
||||
import { ShowMoreLines } from './ShowMoreLines.js';
|
||||
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||
@@ -112,14 +112,18 @@ export const Composer = () => {
|
||||
>
|
||||
<Box>
|
||||
{process.env['GEMINI_SYSTEM_MD'] && (
|
||||
<Text color={Colors.AccentRed}>|⌐■_■| </Text>
|
||||
<Text color={theme.status.error}>|⌐■_■| </Text>
|
||||
)}
|
||||
{uiState.ctrlCPressedOnce ? (
|
||||
<Text color={Colors.AccentYellow}>Press Ctrl+C again to exit.</Text>
|
||||
<Text color={theme.status.warning}>
|
||||
Press Ctrl+C again to exit.
|
||||
</Text>
|
||||
) : uiState.ctrlDPressedOnce ? (
|
||||
<Text color={Colors.AccentYellow}>Press Ctrl+D again to exit.</Text>
|
||||
<Text color={theme.status.warning}>
|
||||
Press Ctrl+D again to exit.
|
||||
</Text>
|
||||
) : uiState.showEscapePrompt ? (
|
||||
<Text color={Colors.Gray}>Press Esc again to clear.</Text>
|
||||
<Text color={theme.text.secondary}>Press Esc again to clear.</Text>
|
||||
) : (
|
||||
!settings.merged.ui?.hideContextSummary && (
|
||||
<ContextSummaryDisplay
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Box, Text } from 'ink';
|
||||
import { useConfig } from '../contexts/ConfigContext.js';
|
||||
import { type McpClient, MCPServerStatus } from '@google/gemini-cli-core';
|
||||
import { GeminiSpinner } from './GeminiRespondingSpinner.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
export const ConfigInitDisplay = () => {
|
||||
const config = useConfig();
|
||||
@@ -39,7 +40,7 @@ export const ConfigInitDisplay = () => {
|
||||
return (
|
||||
<Box marginTop={1}>
|
||||
<Text>
|
||||
<GeminiSpinner /> {message}
|
||||
<GeminiSpinner /> <Text color={theme.text.primary}>{message}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface ConsoleSummaryDisplayProps {
|
||||
errorCount: number;
|
||||
@@ -25,9 +25,9 @@ export const ConsoleSummaryDisplay: React.FC<ConsoleSummaryDisplayProps> = ({
|
||||
return (
|
||||
<Box>
|
||||
{errorCount > 0 && (
|
||||
<Text color={Colors.AccentRed}>
|
||||
<Text color={theme.status.error}>
|
||||
{errorIcon} {errorCount} error{errorCount > 1 ? 's' : ''}{' '}
|
||||
<Text color={Colors.Gray}>(ctrl+o for details)</Text>
|
||||
<Text color={theme.text.secondary}>(ctrl+o for details)</Text>
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core';
|
||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
||||
@@ -99,9 +99,9 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
|
||||
if (isNarrow) {
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text color={Colors.Gray}>Using:</Text>
|
||||
<Text color={theme.text.secondary}>Using:</Text>
|
||||
{summaryParts.map((part, index) => (
|
||||
<Text key={index} color={Colors.Gray}>
|
||||
<Text key={index} color={theme.text.secondary}>
|
||||
{' '}- {part}
|
||||
</Text>
|
||||
))}
|
||||
@@ -111,7 +111,9 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text color={Colors.Gray}>Using: {summaryParts.join(' | ')}</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
Using: {summaryParts.join(' | ')}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { tokenLimit } from '@google/gemini-cli-core';
|
||||
|
||||
export const ContextUsageDisplay = ({
|
||||
@@ -18,7 +18,7 @@ export const ContextUsageDisplay = ({
|
||||
const percentage = promptTokenCount / tokenLimit(model);
|
||||
|
||||
return (
|
||||
<Text color={Colors.Gray}>
|
||||
<Text color={theme.text.secondary}>
|
||||
({((1 - percentage) * 100).toFixed(0)}% context left)
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Text } from 'ink';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
|
||||
export const DebugProfiler = () => {
|
||||
@@ -31,6 +31,6 @@ export const DebugProfiler = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Text color={Colors.AccentYellow}>Renders: {numRenders.current} </Text>
|
||||
<Text color={theme.status.warning}>Renders: {numRenders.current} </Text>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type { ConsoleMessageItem } from '../types.js';
|
||||
import { MaxSizedBox } from './shared/MaxSizedBox.js';
|
||||
|
||||
@@ -31,31 +31,32 @@ export const DetailedMessagesDisplay: React.FC<
|
||||
flexDirection="column"
|
||||
marginTop={1}
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
paddingX={1}
|
||||
width={width}
|
||||
>
|
||||
<Box marginBottom={1}>
|
||||
<Text bold color={Colors.Foreground}>
|
||||
Debug Console <Text color={Colors.Gray}>(ctrl+o to close)</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Debug Console{' '}
|
||||
<Text color={theme.text.secondary}>(ctrl+o to close)</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
<MaxSizedBox maxHeight={maxHeight} maxWidth={width - borderAndPadding}>
|
||||
{messages.map((msg, index) => {
|
||||
let textColor = Colors.Foreground;
|
||||
let textColor = theme.text.primary;
|
||||
let icon = '\u2139'; // Information source (ℹ)
|
||||
|
||||
switch (msg.type) {
|
||||
case 'warn':
|
||||
textColor = Colors.AccentYellow;
|
||||
textColor = theme.status.warning;
|
||||
icon = '\u26A0'; // Warning sign (⚠)
|
||||
break;
|
||||
case 'error':
|
||||
textColor = Colors.AccentRed;
|
||||
textColor = theme.status.error;
|
||||
icon = '\u2716'; // Heavy multiplication x (✖)
|
||||
break;
|
||||
case 'debug':
|
||||
textColor = Colors.Gray; // Or Colors.Gray
|
||||
textColor = theme.text.secondary; // Or theme.text.secondary
|
||||
icon = '\u{1F50D}'; // Left-pointing magnifying glass (🔍)
|
||||
break;
|
||||
case 'log':
|
||||
@@ -70,7 +71,7 @@ export const DetailedMessagesDisplay: React.FC<
|
||||
<Text color={textColor} wrap="wrap">
|
||||
{msg.content}
|
||||
{msg.count && msg.count > 1 && (
|
||||
<Text color={Colors.Gray}> (x{msg.count})</Text>
|
||||
<Text color={theme.text.secondary}> (x{msg.count})</Text>
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { EditorSettingsDialog } from './EditorSettingsDialog.js';
|
||||
import { PrivacyNotice } from '../privacy/PrivacyNotice.js';
|
||||
import { WorkspaceMigrationDialog } from './WorkspaceMigrationDialog.js';
|
||||
import { ProQuotaDialog } from './ProQuotaDialog.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||
import { useConfig } from '../contexts/ConfigContext.js';
|
||||
@@ -36,8 +36,8 @@ export const DialogManager = () => {
|
||||
|
||||
if (uiState.showIdeRestartPrompt) {
|
||||
return (
|
||||
<Box borderStyle="round" borderColor={Colors.AccentYellow} paddingX={1}>
|
||||
<Text color={Colors.AccentYellow}>
|
||||
<Box borderStyle="round" borderColor={theme.status.warning} paddingX={1}>
|
||||
<Text color={theme.status.warning}>
|
||||
Workspace trust has changed. Press 'r' to restart Gemini to
|
||||
apply the changes.
|
||||
</Text>
|
||||
@@ -106,7 +106,7 @@ export const DialogManager = () => {
|
||||
<Box flexDirection="column">
|
||||
{uiState.themeError && (
|
||||
<Box marginBottom={1}>
|
||||
<Text color={Colors.AccentRed}>{uiState.themeError}</Text>
|
||||
<Text color={theme.status.error}>{uiState.themeError}</Text>
|
||||
</Box>
|
||||
)}
|
||||
<ThemeDialog
|
||||
@@ -159,7 +159,7 @@ export const DialogManager = () => {
|
||||
<Box flexDirection="column">
|
||||
{uiState.editorError && (
|
||||
<Box marginBottom={1}>
|
||||
<Text color={Colors.AccentRed}>{uiState.editorError}</Text>
|
||||
<Text color={theme.status.error}>{uiState.editorError}</Text>
|
||||
</Box>
|
||||
)}
|
||||
<EditorSettingsDialog
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import {
|
||||
EDITOR_DISPLAY_NAMES,
|
||||
editorSettingsManager,
|
||||
@@ -112,7 +112,7 @@ export function EditorSettingsDialog({
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="row"
|
||||
padding={1}
|
||||
width="100%"
|
||||
@@ -120,7 +120,7 @@ export function EditorSettingsDialog({
|
||||
<Box flexDirection="column" width="45%" paddingRight={2}>
|
||||
<Text bold={focusedSection === 'editor'}>
|
||||
{focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '}
|
||||
<Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
|
||||
<Text color={theme.text.secondary}>{otherScopeModifiedMessage}</Text>
|
||||
</Text>
|
||||
<RadioButtonSelect
|
||||
items={editorItems.map((item) => ({
|
||||
@@ -147,7 +147,7 @@ export function EditorSettingsDialog({
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.Gray}>
|
||||
<Text color={theme.text.secondary}>
|
||||
(Use Enter to select, Tab to change focus)
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -156,17 +156,17 @@ export function EditorSettingsDialog({
|
||||
<Box flexDirection="column" width="55%" paddingLeft={2}>
|
||||
<Text bold>Editor Preference</Text>
|
||||
<Box flexDirection="column" gap={1} marginTop={1}>
|
||||
<Text color={Colors.Gray}>
|
||||
<Text color={theme.text.secondary}>
|
||||
These editors are currently supported. Please note that some editors
|
||||
cannot be used in sandbox mode.
|
||||
</Text>
|
||||
<Text color={Colors.Gray}>
|
||||
<Text color={theme.text.secondary}>
|
||||
Your preferred editor is:{' '}
|
||||
<Text
|
||||
color={
|
||||
mergedEditorName === 'None'
|
||||
? Colors.AccentRed
|
||||
: Colors.AccentCyan
|
||||
? theme.status.error
|
||||
: theme.text.link
|
||||
}
|
||||
bold
|
||||
>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Box, Text } from 'ink';
|
||||
import type React from 'react';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type { RadioSelectItem } from './shared/RadioButtonSelect.js';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
@@ -69,14 +69,16 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
borderColor={theme.status.warning}
|
||||
padding={1}
|
||||
width="100%"
|
||||
marginLeft={1}
|
||||
>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Do you trust this folder?</Text>
|
||||
<Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Do you trust this folder?
|
||||
</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Trusting a folder allows Gemini to execute commands it suggests.
|
||||
This is a security feature to prevent accidental execution in
|
||||
untrusted directories.
|
||||
@@ -91,7 +93,7 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
|
||||
</Box>
|
||||
{isRestarting && (
|
||||
<Box marginLeft={1} marginTop={1}>
|
||||
<Text color={Colors.AccentYellow}>
|
||||
<Text color={theme.status.warning}>
|
||||
To see changes, Gemini CLI must be restarted. Press r to exit and
|
||||
apply changes now.
|
||||
</Text>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SCREEN_READER_LOADING,
|
||||
SCREEN_READER_RESPONDING,
|
||||
} from '../textConstants.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface GeminiRespondingSpinnerProps {
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ export const GeminiRespondingSpinner: React.FC<
|
||||
return isScreenReaderEnabled ? (
|
||||
<Text>{SCREEN_READER_LOADING}</Text>
|
||||
) : (
|
||||
<Text>{nonRespondingDisplay}</Text>
|
||||
<Text color={theme.text.primary}>{nonRespondingDisplay}</Text>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -59,6 +60,8 @@ export const GeminiSpinner: React.FC<GeminiSpinnerProps> = ({
|
||||
return isScreenReaderEnabled ? (
|
||||
<Text>{altText}</Text>
|
||||
) : (
|
||||
<Spinner type={spinnerType} />
|
||||
<Text color={theme.text.primary}>
|
||||
<Spinner type={spinnerType} />
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import Gradient from 'ink-gradient';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js';
|
||||
import { getAsciiArtWidth } from '../utils/textUtils.js';
|
||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||
@@ -47,8 +47,8 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
flexShrink={0}
|
||||
flexDirection="column"
|
||||
>
|
||||
{Colors.GradientColors ? (
|
||||
<Gradient colors={Colors.GradientColors}>
|
||||
{theme.ui.gradient ? (
|
||||
<Gradient colors={theme.ui.gradient}>
|
||||
<Text>{displayTitle}</Text>
|
||||
</Gradient>
|
||||
) : (
|
||||
@@ -56,8 +56,8 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
)}
|
||||
{nightly && (
|
||||
<Box width="100%" flexDirection="row" justifyContent="flex-end">
|
||||
{Colors.GradientColors ? (
|
||||
<Gradient colors={Colors.GradientColors}>
|
||||
{theme.ui.gradient ? (
|
||||
<Gradient colors={theme.ui.gradient}>
|
||||
<Text>v{version}</Text>
|
||||
</Gradient>
|
||||
) : (
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { type SlashCommand, CommandKind } from '../commands/types.js';
|
||||
|
||||
interface Help {
|
||||
@@ -17,42 +17,42 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
marginBottom={1}
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
borderStyle="round"
|
||||
padding={1}
|
||||
>
|
||||
{/* Basics */}
|
||||
<Text bold color={Colors.Foreground}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Basics:
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Add context
|
||||
</Text>
|
||||
: Use{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
@
|
||||
</Text>{' '}
|
||||
to specify files for context (e.g.,{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
@src/myFile.ts
|
||||
</Text>
|
||||
) to target specific files or folders.
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Shell mode
|
||||
</Text>
|
||||
: Execute shell commands via{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
!
|
||||
</Text>{' '}
|
||||
(e.g.,{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
!npm run start
|
||||
</Text>
|
||||
) or use natural language (e.g.{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
start server
|
||||
</Text>
|
||||
).
|
||||
@@ -61,20 +61,20 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||
<Box height={1} />
|
||||
|
||||
{/* Commands */}
|
||||
<Text bold color={Colors.Foreground}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Commands:
|
||||
</Text>
|
||||
{commands
|
||||
.filter((command) => command.description && !command.hidden)
|
||||
.map((command: SlashCommand) => (
|
||||
<Box key={command.name} flexDirection="column">
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
{' '}
|
||||
/{command.name}
|
||||
</Text>
|
||||
{command.kind === CommandKind.MCP_PROMPT && (
|
||||
<Text color={Colors.Gray}> [MCP]</Text>
|
||||
<Text color={theme.text.secondary}> [MCP]</Text>
|
||||
)}
|
||||
{command.description && ' - ' + command.description}
|
||||
</Text>
|
||||
@@ -82,8 +82,8 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||
command.subCommands
|
||||
.filter((subCommand) => !subCommand.hidden)
|
||||
.map((subCommand) => (
|
||||
<Text key={subCommand.name} color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text key={subCommand.name} color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
{' '}
|
||||
{subCommand.name}
|
||||
</Text>
|
||||
@@ -92,90 +92,90 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||
))}
|
||||
</Box>
|
||||
))}
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
{' '}
|
||||
!{' '}
|
||||
</Text>
|
||||
- shell command
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={Colors.Gray}>[MCP]</Text> - Model Context Protocol command
|
||||
(from external servers)
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.text.secondary}>[MCP]</Text> - Model Context Protocol
|
||||
command (from external servers)
|
||||
</Text>
|
||||
|
||||
<Box height={1} />
|
||||
|
||||
{/* Shortcuts */}
|
||||
<Text bold color={Colors.Foreground}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Keyboard Shortcuts:
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Alt+Left/Right
|
||||
</Text>{' '}
|
||||
- Jump through words in the input
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Ctrl+C
|
||||
</Text>{' '}
|
||||
- Quit application
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
{process.platform === 'win32' ? 'Ctrl+Enter' : 'Ctrl+J'}
|
||||
</Text>{' '}
|
||||
{process.platform === 'linux'
|
||||
? '- New line (Alt+Enter works for certain linux distros)'
|
||||
: '- New line'}
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Ctrl+L
|
||||
</Text>{' '}
|
||||
- Clear the screen
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
{process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'}
|
||||
</Text>{' '}
|
||||
- Open input in external editor
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Ctrl+Y
|
||||
</Text>{' '}
|
||||
- Toggle YOLO mode
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Enter
|
||||
</Text>{' '}
|
||||
- Send message
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Esc
|
||||
</Text>{' '}
|
||||
- Cancel operation / Clear input (double press)
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Shift+Tab
|
||||
</Text>{' '}
|
||||
- Toggle auto-accepting edits
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Up/Down
|
||||
</Text>{' '}
|
||||
- Cycle through your prompt history
|
||||
</Text>
|
||||
<Box height={1} />
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={theme.text.primary}>
|
||||
For a full list of shortcuts, see{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
docs/keyboard-shortcuts.md
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
@@ -714,7 +714,11 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={
|
||||
shellModeActive ? theme.status.warning : theme.border.focused
|
||||
shellModeActive
|
||||
? theme.status.warning
|
||||
: focus
|
||||
? theme.border.focused
|
||||
: theme.border.default
|
||||
}
|
||||
paddingX={1}
|
||||
>
|
||||
@@ -797,7 +801,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
const color =
|
||||
token.type === 'command' || token.type === 'file'
|
||||
? theme.text.accent
|
||||
: undefined;
|
||||
: theme.text.primary;
|
||||
|
||||
renderedLine.push(
|
||||
<Text key={`token-${tokenIdx}`} color={color}>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type { ThoughtSummary } from '@google/gemini-cli-core';
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
||||
@@ -61,11 +61,9 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{primaryText && (
|
||||
<Text color={Colors.AccentPurple}>{primaryText}</Text>
|
||||
)}
|
||||
{primaryText && <Text color={theme.text.accent}>{primaryText}</Text>}
|
||||
{!isNarrow && cancelAndTimerContent && (
|
||||
<Text color={Colors.Gray}> {cancelAndTimerContent}</Text>
|
||||
<Text color={theme.text.secondary}> {cancelAndTimerContent}</Text>
|
||||
)}
|
||||
</Box>
|
||||
{!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
|
||||
@@ -73,7 +71,7 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
||||
</Box>
|
||||
{isNarrow && cancelAndTimerContent && (
|
||||
<Box>
|
||||
<Text color={Colors.Gray}>{cancelAndTimerContent}</Text>
|
||||
<Text color={theme.text.secondary}>{cancelAndTimerContent}</Text>
|
||||
</Box>
|
||||
)}
|
||||
{isNarrow && rightContent && <Box>{rightContent}</Box>}
|
||||
|
||||
@@ -7,20 +7,24 @@
|
||||
import type React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import process from 'node:process';
|
||||
import { formatMemoryUsage } from '../utils/formatters.js';
|
||||
|
||||
export const MemoryUsageDisplay: React.FC = () => {
|
||||
const [memoryUsage, setMemoryUsage] = useState<string>('');
|
||||
const [memoryUsageColor, setMemoryUsageColor] = useState<string>(Colors.Gray);
|
||||
const [memoryUsageColor, setMemoryUsageColor] = useState<string>(
|
||||
theme.text.secondary,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const updateMemory = () => {
|
||||
const usage = process.memoryUsage().rss;
|
||||
setMemoryUsage(formatMemoryUsage(usage));
|
||||
setMemoryUsageColor(
|
||||
usage >= 2 * 1024 * 1024 * 1024 ? Colors.AccentRed : Colors.Gray,
|
||||
usage >= 2 * 1024 * 1024 * 1024
|
||||
? theme.status.error
|
||||
: theme.text.secondary,
|
||||
);
|
||||
};
|
||||
const intervalId = setInterval(updateMemory, 2000);
|
||||
@@ -30,7 +34,7 @@ export const MemoryUsageDisplay: React.FC = () => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text color={Colors.Gray}> | </Text>
|
||||
<Text color={theme.text.secondary}> | </Text>
|
||||
<Text color={memoryUsageColor}>{memoryUsage}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import {
|
||||
calculateAverageLatency,
|
||||
@@ -34,13 +34,16 @@ const StatRow: React.FC<StatRowProps> = ({
|
||||
}) => (
|
||||
<Box>
|
||||
<Box width={METRIC_COL_WIDTH}>
|
||||
<Text bold={isSection} color={isSection ? undefined : Colors.LightBlue}>
|
||||
<Text
|
||||
bold={isSection}
|
||||
color={isSection ? theme.text.primary : theme.text.link}
|
||||
>
|
||||
{isSubtle ? ` ↳ ${title}` : title}
|
||||
</Text>
|
||||
</Box>
|
||||
{values.map((value, index) => (
|
||||
<Box width={MODEL_COL_WIDTH} key={index}>
|
||||
<Text>{value}</Text>
|
||||
<Text color={theme.text.primary}>{value}</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
@@ -57,11 +60,13 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
paddingY={1}
|
||||
paddingX={2}
|
||||
>
|
||||
<Text>No API calls have been made in this session.</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
No API calls have been made in this session.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -83,12 +88,12 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
paddingY={1}
|
||||
paddingX={2}
|
||||
>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Model Stats For Nerds
|
||||
</Text>
|
||||
<Box height={1} />
|
||||
@@ -96,11 +101,15 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
{/* Header */}
|
||||
<Box>
|
||||
<Box width={METRIC_COL_WIDTH}>
|
||||
<Text bold>Metric</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Metric
|
||||
</Text>
|
||||
</Box>
|
||||
{modelNames.map((name) => (
|
||||
<Box width={MODEL_COL_WIDTH} key={name}>
|
||||
<Text bold>{name}</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
{name}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
@@ -112,6 +121,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
borderTop={false}
|
||||
borderLeft={false}
|
||||
borderRight={false}
|
||||
borderColor={theme.border.default}
|
||||
/>
|
||||
|
||||
{/* API Section */}
|
||||
@@ -127,7 +137,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
return (
|
||||
<Text
|
||||
color={
|
||||
m.api.totalErrors > 0 ? Colors.AccentRed : Colors.Foreground
|
||||
m.api.totalErrors > 0 ? theme.status.error : theme.text.primary
|
||||
}
|
||||
>
|
||||
{m.api.totalErrors.toLocaleString()} ({errorRate.toFixed(1)}%)
|
||||
@@ -150,7 +160,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
<StatRow
|
||||
title="Total"
|
||||
values={getModelValues((m) => (
|
||||
<Text color={Colors.AccentYellow}>
|
||||
<Text color={theme.status.warning}>
|
||||
{m.tokens.total.toLocaleString()}
|
||||
</Text>
|
||||
))}
|
||||
@@ -167,7 +177,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||
values={getModelValues((m) => {
|
||||
const cacheHitRate = calculateCacheHitRate(m);
|
||||
return (
|
||||
<Text color={Colors.AccentGreen}>
|
||||
<Text color={theme.status.success}>
|
||||
{m.tokens.cached.toLocaleString()} ({cacheHitRate.toFixed(1)}%)
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Box, Text } from 'ink';
|
||||
import { useAppContext } from '../contexts/AppContext.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import { UpdateNotification } from './UpdateNotification.js';
|
||||
|
||||
@@ -29,13 +29,13 @@ export const Notifications = () => {
|
||||
{showStartupWarnings && (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
borderColor={theme.status.warning}
|
||||
paddingX={1}
|
||||
marginY={1}
|
||||
flexDirection="column"
|
||||
>
|
||||
{startupWarnings.map((warning, index) => (
|
||||
<Text key={index} color={Colors.AccentYellow}>
|
||||
<Text key={index} color={theme.status.warning}>
|
||||
{warning}
|
||||
</Text>
|
||||
))}
|
||||
@@ -44,14 +44,14 @@ export const Notifications = () => {
|
||||
{showInitError && (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentRed}
|
||||
borderColor={theme.status.error}
|
||||
paddingX={1}
|
||||
marginBottom={1}
|
||||
>
|
||||
<Text color={Colors.AccentRed}>
|
||||
<Text color={theme.status.error}>
|
||||
Initialization Error: {initError}
|
||||
</Text>
|
||||
<Text color={Colors.AccentRed}>
|
||||
<Text color={theme.status.error}>
|
||||
{' '}
|
||||
Please check API key and configuration.
|
||||
</Text>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface PrepareLabelProps {
|
||||
label: string;
|
||||
@@ -21,7 +21,7 @@ export const PrepareLabel: React.FC<PrepareLabelProps> = ({
|
||||
matchedIndex,
|
||||
userInput,
|
||||
textColor,
|
||||
highlightColor = Colors.AccentYellow,
|
||||
highlightColor = theme.status.warning,
|
||||
}) => {
|
||||
if (
|
||||
matchedIndex === undefined ||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface ProQuotaDialogProps {
|
||||
failedModel: string;
|
||||
@@ -37,7 +37,7 @@ export function ProQuotaDialog({
|
||||
|
||||
return (
|
||||
<Box borderStyle="round" flexDirection="column" paddingX={1}>
|
||||
<Text bold color={Colors.AccentYellow}>
|
||||
<Text bold color={theme.status.warning}>
|
||||
Pro quota limit reached for {failedModel}.
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type { LoadedSettings, Settings } from '../../config/settings.js';
|
||||
import { SettingScope } from '../../config/settings.js';
|
||||
import {
|
||||
@@ -656,18 +656,18 @@ export function SettingsDialog({
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="row"
|
||||
padding={1}
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<Box flexDirection="column" flexGrow={1}>
|
||||
<Text bold color={Colors.AccentBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
Settings
|
||||
</Text>
|
||||
<Box height={1} />
|
||||
{showScrollUp && <Text color={Colors.Gray}>▲</Text>}
|
||||
{showScrollUp && <Text color={theme.text.secondary}>▲</Text>}
|
||||
{visibleItems.map((item, idx) => {
|
||||
const isActive =
|
||||
focusSection === 'settings' &&
|
||||
@@ -748,17 +748,21 @@ export function SettingsDialog({
|
||||
<React.Fragment key={item.value}>
|
||||
<Box flexDirection="row" alignItems="center">
|
||||
<Box minWidth={2} flexShrink={0}>
|
||||
<Text color={isActive ? Colors.AccentGreen : Colors.Gray}>
|
||||
<Text
|
||||
color={
|
||||
isActive ? theme.status.success : theme.text.secondary
|
||||
}
|
||||
>
|
||||
{isActive ? '●' : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box minWidth={50}>
|
||||
<Text
|
||||
color={isActive ? Colors.AccentGreen : Colors.Foreground}
|
||||
color={isActive ? theme.status.success : theme.text.primary}
|
||||
>
|
||||
{item.label}
|
||||
{scopeMessage && (
|
||||
<Text color={Colors.Gray}> {scopeMessage}</Text>
|
||||
<Text color={theme.text.secondary}> {scopeMessage}</Text>
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -766,10 +770,10 @@ export function SettingsDialog({
|
||||
<Text
|
||||
color={
|
||||
isActive
|
||||
? Colors.AccentGreen
|
||||
? theme.status.success
|
||||
: shouldBeGreyedOut
|
||||
? Colors.Gray
|
||||
: Colors.Foreground
|
||||
? theme.text.secondary
|
||||
: theme.text.primary
|
||||
}
|
||||
>
|
||||
{displayValue}
|
||||
@@ -779,7 +783,7 @@ export function SettingsDialog({
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{showScrollDown && <Text color={Colors.Gray}>▼</Text>}
|
||||
{showScrollDown && <Text color={theme.text.secondary}>▼</Text>}
|
||||
|
||||
<Box height={1} />
|
||||
|
||||
@@ -798,11 +802,11 @@ export function SettingsDialog({
|
||||
</Box>
|
||||
|
||||
<Box height={1} />
|
||||
<Text color={Colors.Gray}>
|
||||
<Text color={theme.text.secondary}>
|
||||
(Use Enter to select, Tab to change focus)
|
||||
</Text>
|
||||
{showRestartPrompt && (
|
||||
<Text color={Colors.AccentYellow}>
|
||||
<Text color={theme.status.warning}>
|
||||
To see changes, Gemini CLI must be restarted. Press r to exit and
|
||||
apply changes now.
|
||||
</Text>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { ToolConfirmationOutcome } from '@google/gemini-cli-core';
|
||||
import { Box, Text } from 'ink';
|
||||
import type React from 'react';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { RenderInline } from '../utils/InlineMarkdownRenderer.js';
|
||||
import type { RadioSelectItem } from './shared/RadioButtonSelect.js';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
@@ -68,23 +68,27 @@ export const ShellConfirmationDialog: React.FC<
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
borderColor={theme.status.warning}
|
||||
padding={1}
|
||||
width="100%"
|
||||
marginLeft={1}
|
||||
>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Shell Command Execution</Text>
|
||||
<Text>A custom command wants to run the following shell commands:</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Shell Command Execution
|
||||
</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
A custom command wants to run the following shell commands:
|
||||
</Text>
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
paddingX={1}
|
||||
marginTop={1}
|
||||
>
|
||||
{commands.map((cmd) => (
|
||||
<Text key={cmd} color={Colors.AccentCyan}>
|
||||
<Text key={cmd} color={theme.text.link}>
|
||||
<RenderInline text={cmd} />
|
||||
</Text>
|
||||
))}
|
||||
@@ -92,7 +96,7 @@ export const ShellConfirmationDialog: React.FC<
|
||||
</Box>
|
||||
|
||||
<Box marginBottom={1}>
|
||||
<Text>Do you want to proceed?</Text>
|
||||
<Text color={theme.text.primary}>Do you want to proceed?</Text>
|
||||
</Box>
|
||||
|
||||
<RadioButtonSelect items={options} onSelect={handleSelect} isFocused />
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
export const ShellModeIndicator: React.FC = () => (
|
||||
<Box>
|
||||
<Text color={Colors.AccentYellow}>
|
||||
<Text color={theme.status.warning}>
|
||||
shell mode enabled
|
||||
<Text color={Colors.Gray}> (esc to disable)</Text>
|
||||
<Text color={theme.text.secondary}> (esc to disable)</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Box, Text } from 'ink';
|
||||
import { useOverflowState } from '../contexts/OverflowContext.js';
|
||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface ShowMoreLinesProps {
|
||||
constrainHeight: boolean;
|
||||
@@ -32,7 +32,7 @@ export const ShowMoreLines = ({ constrainHeight }: ShowMoreLinesProps) => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text color={Colors.Gray} wrap="truncate">
|
||||
<Text color={theme.text.secondary} wrap="truncate">
|
||||
Press ctrl-s to show more lines
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -47,7 +47,7 @@ const SubStatRow: React.FC<SubStatRowProps> = ({ title, children }) => (
|
||||
<Box paddingLeft={2}>
|
||||
{/* Adjust width for the "» " prefix */}
|
||||
<Box width={26}>
|
||||
<Text>» {title}</Text>
|
||||
<Text color={theme.text.secondary}>» {title}</Text>
|
||||
</Box>
|
||||
{/* FIX: Apply the same flexGrow fix here */}
|
||||
<Box flexGrow={1}>{children}</Box>
|
||||
@@ -62,7 +62,9 @@ interface SectionProps {
|
||||
|
||||
const Section: React.FC<SectionProps> = ({ title, children }) => (
|
||||
<Box flexDirection="column" width="100%" marginBottom={1}>
|
||||
<Text bold>{title}</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
{title}
|
||||
</Text>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
@@ -82,16 +84,24 @@ const ModelUsageTable: React.FC<{
|
||||
{/* Header */}
|
||||
<Box>
|
||||
<Box width={nameWidth}>
|
||||
<Text bold>Model Usage</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Model Usage
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={requestsWidth} justifyContent="flex-end">
|
||||
<Text bold>Reqs</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Reqs
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={inputTokensWidth} justifyContent="flex-end">
|
||||
<Text bold>Input Tokens</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Input Tokens
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={outputTokensWidth} justifyContent="flex-end">
|
||||
<Text bold>Output Tokens</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Output Tokens
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* Divider */}
|
||||
@@ -101,6 +111,7 @@ const ModelUsageTable: React.FC<{
|
||||
borderTop={false}
|
||||
borderLeft={false}
|
||||
borderRight={false}
|
||||
borderColor={theme.border.default}
|
||||
width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth}
|
||||
></Box>
|
||||
|
||||
@@ -108,10 +119,12 @@ const ModelUsageTable: React.FC<{
|
||||
{Object.entries(models).map(([name, modelMetrics]) => (
|
||||
<Box key={name}>
|
||||
<Box width={nameWidth}>
|
||||
<Text>{name.replace('-001', '')}</Text>
|
||||
<Text color={theme.text.primary}>{name.replace('-001', '')}</Text>
|
||||
</Box>
|
||||
<Box width={requestsWidth} justifyContent="flex-end">
|
||||
<Text>{modelMetrics.api.totalRequests}</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{modelMetrics.api.totalRequests}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={inputTokensWidth} justifyContent="flex-end">
|
||||
<Text color={theme.status.warning}>
|
||||
@@ -127,7 +140,7 @@ const ModelUsageTable: React.FC<{
|
||||
))}
|
||||
{cacheEfficiency > 0 && (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.status.success}>Savings Highlight:</Text>{' '}
|
||||
{totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)}
|
||||
%) of input tokens were served from the cache, reducing costs.
|
||||
@@ -202,10 +215,10 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||
|
||||
<Section title="Interaction Summary">
|
||||
<StatRow title="Session ID:">
|
||||
<Text>{stats.sessionId}</Text>
|
||||
<Text color={theme.text.primary}>{stats.sessionId}</Text>
|
||||
</StatRow>
|
||||
<StatRow title="Tool Calls:">
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{tools.totalCalls} ({' '}
|
||||
<Text color={theme.status.success}>✓ {tools.totalSuccess}</Text>{' '}
|
||||
<Text color={theme.status.error}>x {tools.totalFail}</Text> )
|
||||
@@ -227,7 +240,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||
{files &&
|
||||
(files.totalLinesAdded > 0 || files.totalLinesRemoved > 0) && (
|
||||
<StatRow title="Code Changes:">
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.status.success}>
|
||||
+{files.totalLinesAdded}
|
||||
</Text>{' '}
|
||||
@@ -241,13 +254,15 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||
|
||||
<Section title="Performance">
|
||||
<StatRow title="Wall Time:">
|
||||
<Text>{duration}</Text>
|
||||
<Text color={theme.text.primary}>{duration}</Text>
|
||||
</StatRow>
|
||||
<StatRow title="Agent Active:">
|
||||
<Text>{formatDuration(computed.agentActiveTime)}</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{formatDuration(computed.agentActiveTime)}
|
||||
</Text>
|
||||
</StatRow>
|
||||
<SubStatRow title="API Time:">
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{formatDuration(computed.totalApiTime)}{' '}
|
||||
<Text color={theme.text.secondary}>
|
||||
({computed.apiTimePercent.toFixed(1)}%)
|
||||
@@ -255,7 +270,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||
</Text>
|
||||
</SubStatRow>
|
||||
<SubStatRow title="Tool Time:">
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
{formatDuration(computed.totalToolTime)}{' '}
|
||||
<Text color={theme.text.secondary}>
|
||||
({computed.toolTimePercent.toFixed(1)}%)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { PrepareLabel } from './PrepareLabel.js';
|
||||
import { CommandKind } from '../commands/types.js';
|
||||
export interface Suggestion {
|
||||
@@ -56,12 +56,12 @@ export function SuggestionsDisplay({
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" paddingX={1} width={width}>
|
||||
{scrollOffset > 0 && <Text color={Colors.Foreground}>▲</Text>}
|
||||
{scrollOffset > 0 && <Text color={theme.text.primary}>▲</Text>}
|
||||
|
||||
{visibleSuggestions.map((suggestion, index) => {
|
||||
const originalIndex = startIndex + index;
|
||||
const isActive = originalIndex === activeIndex;
|
||||
const textColor = isActive ? Colors.AccentPurple : Colors.Gray;
|
||||
const textColor = isActive ? theme.text.accent : theme.text.secondary;
|
||||
const labelElement = (
|
||||
<PrepareLabel
|
||||
label={suggestion.label}
|
||||
@@ -82,7 +82,7 @@ export function SuggestionsDisplay({
|
||||
<Box flexShrink={0} paddingRight={2}>
|
||||
{labelElement}
|
||||
{suggestion.commandKind === CommandKind.MCP_PROMPT && (
|
||||
<Text color={Colors.Gray}> [MCP]</Text>
|
||||
<Text color={theme.text.secondary}> [MCP]</Text>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { DiffRenderer } from './messages/DiffRenderer.js';
|
||||
@@ -183,7 +183,7 @@ export function ThemeDialog({
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
paddingTop={includePadding ? 1 : 0}
|
||||
paddingBottom={includePadding ? 1 : 0}
|
||||
@@ -197,7 +197,9 @@ export function ThemeDialog({
|
||||
<Box flexDirection="column" width="45%" paddingRight={2}>
|
||||
<Text bold={mode === 'theme'} wrap="truncate">
|
||||
{mode === 'theme' ? '> ' : ' '}Select Theme{' '}
|
||||
<Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
{otherScopeModifiedMessage}
|
||||
</Text>
|
||||
</Text>
|
||||
<RadioButtonSelect
|
||||
items={themeItems}
|
||||
@@ -223,7 +225,7 @@ export function ThemeDialog({
|
||||
return (
|
||||
<Box
|
||||
borderStyle="single"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
paddingTop={includePadding ? 1 : 0}
|
||||
paddingBottom={includePadding ? 1 : 0}
|
||||
paddingLeft={1}
|
||||
@@ -267,7 +269,7 @@ def fibonacci(n):
|
||||
/>
|
||||
)}
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.Gray} wrap="truncate">
|
||||
<Text color={theme.text.secondary} wrap="truncate">
|
||||
(Use Enter to {mode === 'theme' ? 'select' : 'apply scope'}, Tab to{' '}
|
||||
{mode === 'theme' ? 'configure scope' : 'select theme'})
|
||||
</Text>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { type Config } from '@google/gemini-cli-core';
|
||||
|
||||
interface TipsProps {
|
||||
@@ -17,25 +17,25 @@ export const Tips: React.FC<TipsProps> = ({ config }) => {
|
||||
const geminiMdFileCount = config.getGeminiMdFileCount();
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text color={Colors.Foreground}>Tips for getting started:</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={theme.text.primary}>Tips for getting started:</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
1. Ask questions, edit files, or run commands.
|
||||
</Text>
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={theme.text.primary}>
|
||||
2. Be specific for the best results.
|
||||
</Text>
|
||||
{geminiMdFileCount === 0 && (
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={theme.text.primary}>
|
||||
3. Create{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
GEMINI.md
|
||||
</Text>{' '}
|
||||
files to customize your interactions with Gemini.
|
||||
</Text>
|
||||
)}
|
||||
<Text color={Colors.Foreground}>
|
||||
<Text color={theme.text.primary}>
|
||||
{geminiMdFileCount === 0 ? '4.' : '3.'}{' '}
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
/help
|
||||
</Text>{' '}
|
||||
for more information.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import {
|
||||
getStatusColor,
|
||||
@@ -37,16 +37,16 @@ const StatRow: React.FC<{
|
||||
return (
|
||||
<Box>
|
||||
<Box width={TOOL_NAME_COL_WIDTH}>
|
||||
<Text color={Colors.LightBlue}>{name}</Text>
|
||||
<Text color={theme.text.link}>{name}</Text>
|
||||
</Box>
|
||||
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text>{stats.count}</Text>
|
||||
<Text color={theme.text.primary}>{stats.count}</Text>
|
||||
</Box>
|
||||
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text color={successColor}>{successRate.toFixed(1)}%</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text>{formatDuration(avgDuration)}</Text>
|
||||
<Text color={theme.text.primary}>{formatDuration(avgDuration)}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
@@ -63,11 +63,13 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
paddingY={1}
|
||||
paddingX={2}
|
||||
>
|
||||
<Text>No tool calls have been made in this session.</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
No tool calls have been made in this session.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -94,13 +96,13 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
flexDirection="column"
|
||||
paddingY={1}
|
||||
paddingX={2}
|
||||
width={70}
|
||||
>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Tool Stats For Nerds
|
||||
</Text>
|
||||
<Box height={1} />
|
||||
@@ -108,16 +110,24 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
{/* Header */}
|
||||
<Box>
|
||||
<Box width={TOOL_NAME_COL_WIDTH}>
|
||||
<Text bold>Tool Name</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Tool Name
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text bold>Calls</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Calls
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text bold>Success Rate</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Success Rate
|
||||
</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text bold>Avg Duration</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Avg Duration
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -128,6 +138,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
borderTop={false}
|
||||
borderLeft={false}
|
||||
borderRight={false}
|
||||
borderColor={theme.border.default}
|
||||
width="100%"
|
||||
/>
|
||||
|
||||
@@ -139,45 +150,47 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
<Box height={1} />
|
||||
|
||||
{/* User Decision Summary */}
|
||||
<Text bold>User Decision Summary</Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
User Decision Summary
|
||||
</Text>
|
||||
<Box>
|
||||
<Box
|
||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||
>
|
||||
<Text color={Colors.LightBlue}>Total Reviewed Suggestions:</Text>
|
||||
<Text color={theme.text.link}>Total Reviewed Suggestions:</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text>{totalReviewed}</Text>
|
||||
<Text color={theme.text.primary}>{totalReviewed}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Box
|
||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||
>
|
||||
<Text> » Accepted:</Text>
|
||||
<Text color={theme.text.primary}> » Accepted:</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text color={Colors.AccentGreen}>{totalDecisions.accept}</Text>
|
||||
<Text color={theme.status.success}>{totalDecisions.accept}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Box
|
||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||
>
|
||||
<Text> » Rejected:</Text>
|
||||
<Text color={theme.text.primary}> » Rejected:</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text color={Colors.AccentRed}>{totalDecisions.reject}</Text>
|
||||
<Text color={theme.status.error}>{totalDecisions.reject}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Box
|
||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||
>
|
||||
<Text> » Modified:</Text>
|
||||
<Text color={theme.text.primary}> » Modified:</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text color={Colors.AccentYellow}>{totalDecisions.modify}</Text>
|
||||
<Text color={theme.status.warning}>{totalDecisions.modify}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -188,6 +201,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
borderTop={false}
|
||||
borderLeft={false}
|
||||
borderRight={false}
|
||||
borderColor={theme.border.default}
|
||||
width="100%"
|
||||
/>
|
||||
|
||||
@@ -195,7 +209,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
<Box
|
||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||
>
|
||||
<Text> Overall Agreement Rate:</Text>
|
||||
<Text color={theme.text.primary}> Overall Agreement Rate:</Text>
|
||||
</Box>
|
||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||
<Text bold color={totalReviewed > 0 ? agreementColor : undefined}>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
interface UpdateNotificationProps {
|
||||
message: string;
|
||||
@@ -14,10 +14,10 @@ interface UpdateNotificationProps {
|
||||
export const UpdateNotification = ({ message }: UpdateNotificationProps) => (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
borderColor={theme.status.warning}
|
||||
paddingX={1}
|
||||
marginY={1}
|
||||
>
|
||||
<Text color={Colors.AccentYellow}>{message}</Text>
|
||||
<Text color={theme.status.warning}>{message}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
performWorkspaceExtensionMigration,
|
||||
} from '../../config/extension.js';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function WorkspaceMigrationDialog(props: {
|
||||
@@ -40,15 +40,15 @@ export function WorkspaceMigrationDialog(props: {
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
>
|
||||
{failedExtensions.length > 0 ? (
|
||||
<>
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
The following extensions failed to migrate. Please try installing
|
||||
them manually. To see other changes, Gemini CLI must be restarted.
|
||||
Press {"'q'"} to quit.
|
||||
Press 'q' to quit.
|
||||
</Text>
|
||||
<Box flexDirection="column" marginTop={1} marginLeft={2}>
|
||||
{failedExtensions.map((failed) => (
|
||||
@@ -57,9 +57,9 @@ export function WorkspaceMigrationDialog(props: {
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Migration complete. To see changes, Gemini CLI must be restarted.
|
||||
Press {"'q'"} to quit.
|
||||
Press 'q' to quit.
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
@@ -70,15 +70,19 @@ export function WorkspaceMigrationDialog(props: {
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
>
|
||||
<Text bold>Workspace-level extensions are deprecated{'\n'}</Text>
|
||||
<Text>Would you like to install them at the user level?</Text>
|
||||
<Text>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Workspace-level extensions are deprecated{'\n'}
|
||||
</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Would you like to install them at the user level?
|
||||
</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
The extension definition will remain in your workspace directory.
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
If you opt to skip, you can install them manually using the extensions
|
||||
install command.
|
||||
</Text>
|
||||
|
||||
@@ -8,7 +8,7 @@ import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import type { CompressionProps } from '../../types.js';
|
||||
import Spinner from 'ink-spinner';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { SCREEN_READER_MODEL_PREFIX } from '../../textConstants.js';
|
||||
|
||||
export interface CompressionDisplayProps {
|
||||
@@ -33,13 +33,13 @@ export const CompressionMessage: React.FC<CompressionDisplayProps> = ({
|
||||
{compression.isPending ? (
|
||||
<Spinner type="dots" />
|
||||
) : (
|
||||
<Text color={Colors.AccentPurple}>✦</Text>
|
||||
<Text color={theme.text.accent}>✦</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text
|
||||
color={
|
||||
compression.isPending ? Colors.AccentPurple : Colors.AccentGreen
|
||||
compression.isPending ? theme.text.accent : theme.status.success
|
||||
}
|
||||
aria-label={SCREEN_READER_MODEL_PREFIX}
|
||||
>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import crypto from 'node:crypto';
|
||||
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { theme as semanticTheme } from '../../semantic-colors.js';
|
||||
import type { Theme } from '../../themes/theme.js';
|
||||
|
||||
interface DiffLine {
|
||||
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
||||
@@ -42,18 +42,9 @@ function parseDiffWithLineNumbers(diffContent: string): DiffLine[] {
|
||||
}
|
||||
if (!inHunk) {
|
||||
// Skip standard Git header lines more robustly
|
||||
if (
|
||||
line.startsWith('--- ') ||
|
||||
line.startsWith('+++ ') ||
|
||||
line.startsWith('diff --git') ||
|
||||
line.startsWith('index ') ||
|
||||
line.startsWith('similarity index') ||
|
||||
line.startsWith('rename from') ||
|
||||
line.startsWith('rename to') ||
|
||||
line.startsWith('new file mode') ||
|
||||
line.startsWith('deleted file mode')
|
||||
)
|
||||
if (line.startsWith('--- ')) {
|
||||
continue;
|
||||
}
|
||||
// If it's not a hunk or header, skip (or handle as 'other' if needed)
|
||||
continue;
|
||||
}
|
||||
@@ -94,7 +85,7 @@ interface DiffRendererProps {
|
||||
tabWidth?: number;
|
||||
availableTerminalHeight?: number;
|
||||
terminalWidth: number;
|
||||
theme?: import('../../themes/theme.js').Theme;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
const DEFAULT_TAB_WIDTH = 4; // Spaces per tab for normalization
|
||||
@@ -109,14 +100,18 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||
}) => {
|
||||
const screenReaderEnabled = useIsScreenReaderEnabled();
|
||||
if (!diffContent || typeof diffContent !== 'string') {
|
||||
return <Text color={Colors.AccentYellow}>No diff content.</Text>;
|
||||
return <Text color={semanticTheme.status.warning}>No diff content.</Text>;
|
||||
}
|
||||
|
||||
const parsedLines = parseDiffWithLineNumbers(diffContent);
|
||||
|
||||
if (parsedLines.length === 0) {
|
||||
return (
|
||||
<Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={semanticTheme.border.default}
|
||||
padding={1}
|
||||
>
|
||||
<Text dimColor>No changes detected.</Text>
|
||||
</Box>
|
||||
);
|
||||
@@ -196,7 +191,11 @@ const renderDiffContent = (
|
||||
|
||||
if (displayableLines.length === 0) {
|
||||
return (
|
||||
<Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={semanticTheme.border.default}
|
||||
padding={1}
|
||||
>
|
||||
<Text dimColor>No changes detected.</Text>
|
||||
</Box>
|
||||
);
|
||||
@@ -260,7 +259,7 @@ const renderDiffContent = (
|
||||
) {
|
||||
acc.push(
|
||||
<Box key={`gap-${index}`}>
|
||||
<Text wrap="truncate" color={Colors.Gray}>
|
||||
<Text wrap="truncate" color={semanticTheme.text.secondary}>
|
||||
{'═'.repeat(terminalWidth)}
|
||||
</Text>
|
||||
</Box>,
|
||||
@@ -301,12 +300,12 @@ const renderDiffContent = (
|
||||
acc.push(
|
||||
<Box key={lineKey} flexDirection="row">
|
||||
<Text
|
||||
color={theme.text.secondary}
|
||||
color={semanticTheme.text.secondary}
|
||||
backgroundColor={
|
||||
line.type === 'add'
|
||||
? theme.background.diff.added
|
||||
? semanticTheme.background.diff.added
|
||||
: line.type === 'del'
|
||||
? theme.background.diff.removed
|
||||
? semanticTheme.background.diff.removed
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
@@ -323,16 +322,16 @@ const renderDiffContent = (
|
||||
<Text
|
||||
backgroundColor={
|
||||
line.type === 'add'
|
||||
? theme.background.diff.added
|
||||
: theme.background.diff.removed
|
||||
? semanticTheme.background.diff.added
|
||||
: semanticTheme.background.diff.removed
|
||||
}
|
||||
wrap="wrap"
|
||||
>
|
||||
<Text
|
||||
color={
|
||||
line.type === 'add'
|
||||
? theme.status.success
|
||||
: theme.status.error
|
||||
? semanticTheme.status.success
|
||||
: semanticTheme.status.error
|
||||
}
|
||||
>
|
||||
{prefixSymbol}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
|
||||
interface ErrorMessageProps {
|
||||
text: string;
|
||||
@@ -19,10 +19,10 @@ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
|
||||
return (
|
||||
<Box flexDirection="row" marginBottom={1}>
|
||||
<Box width={prefixWidth}>
|
||||
<Text color={Colors.AccentRed}>{prefix}</Text>
|
||||
<Text color={theme.status.error}>{prefix}</Text>
|
||||
</Box>
|
||||
<Box flexGrow={1}>
|
||||
<Text wrap="wrap" color={Colors.AccentRed}>
|
||||
<Text wrap="wrap" color={theme.status.error}>
|
||||
{text}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { SCREEN_READER_MODEL_PREFIX } from '../../textConstants.js';
|
||||
|
||||
interface GeminiMessageProps {
|
||||
@@ -29,10 +29,7 @@ export const GeminiMessage: React.FC<GeminiMessageProps> = ({
|
||||
return (
|
||||
<Box flexDirection="row">
|
||||
<Box width={prefixWidth}>
|
||||
<Text
|
||||
color={Colors.AccentPurple}
|
||||
aria-label={SCREEN_READER_MODEL_PREFIX}
|
||||
>
|
||||
<Text color={theme.text.accent} aria-label={SCREEN_READER_MODEL_PREFIX}>
|
||||
{prefix}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { RenderInline } from '../../utils/InlineMarkdownRenderer.js';
|
||||
|
||||
interface InfoMessageProps {
|
||||
@@ -20,10 +20,10 @@ export const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
|
||||
return (
|
||||
<Box flexDirection="row" marginTop={1}>
|
||||
<Box width={prefixWidth}>
|
||||
<Text color={Colors.AccentYellow}>{prefix}</Text>
|
||||
<Text color={theme.status.warning}>{prefix}</Text>
|
||||
</Box>
|
||||
<Box flexGrow={1}>
|
||||
<Text wrap="wrap" color={Colors.AccentYellow}>
|
||||
<Text wrap="wrap" color={theme.status.warning}>
|
||||
<RenderInline text={text} />
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { DiffRenderer } from './DiffRenderer.js';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { RenderInline } from '../../utils/InlineMarkdownRenderer.js';
|
||||
import type {
|
||||
ToolCallConfirmationDetails,
|
||||
@@ -20,6 +19,7 @@ import type { RadioSelectItem } from '../shared/RadioButtonSelect.js';
|
||||
import { RadioButtonSelect } from '../shared/RadioButtonSelect.js';
|
||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||
import { useKeypress } from '../../hooks/useKeypress.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
|
||||
export interface ToolConfirmationMessageProps {
|
||||
confirmationDetails: ToolCallConfirmationDetails;
|
||||
@@ -113,13 +113,13 @@ export const ToolConfirmationMessage: React.FC<
|
||||
<Box
|
||||
minWidth="90%"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
borderColor={theme.border.default}
|
||||
justifyContent="space-around"
|
||||
padding={1}
|
||||
overflow="hidden"
|
||||
>
|
||||
<Text>Modify in progress: </Text>
|
||||
<Text color={Colors.AccentGreen}>
|
||||
<Text color={theme.text.primary}>Modify in progress: </Text>
|
||||
<Text color={theme.status.success}>
|
||||
Save and close external editor to continue
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -193,7 +193,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||
maxWidth={Math.max(childWidth - 4, 1)}
|
||||
>
|
||||
<Box>
|
||||
<Text color={Colors.AccentCyan}>{executionProps.command}</Text>
|
||||
<Text color={theme.text.link}>{executionProps.command}</Text>
|
||||
</Box>
|
||||
</MaxSizedBox>
|
||||
</Box>
|
||||
@@ -223,12 +223,12 @@ export const ToolConfirmationMessage: React.FC<
|
||||
|
||||
bodyContent = (
|
||||
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
||||
<Text color={Colors.AccentCyan}>
|
||||
<Text color={theme.text.link}>
|
||||
<RenderInline text={infoProps.prompt} />
|
||||
</Text>
|
||||
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Text>URLs to fetch:</Text>
|
||||
<Text color={theme.text.primary}>URLs to fetch:</Text>
|
||||
{infoProps.urls.map((url) => (
|
||||
<Text key={url}>
|
||||
{' '}
|
||||
@@ -245,8 +245,8 @@ export const ToolConfirmationMessage: React.FC<
|
||||
|
||||
bodyContent = (
|
||||
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
||||
<Text color={Colors.AccentCyan}>MCP Server: {mcpProps.serverName}</Text>
|
||||
<Text color={Colors.AccentCyan}>Tool: {mcpProps.toolName}</Text>
|
||||
<Text color={theme.text.link}>MCP Server: {mcpProps.serverName}</Text>
|
||||
<Text color={theme.text.link}>Tool: {mcpProps.toolName}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -281,7 +281,9 @@ export const ToolConfirmationMessage: React.FC<
|
||||
|
||||
{/* Confirmation Question */}
|
||||
<Box marginBottom={1} flexShrink={0}>
|
||||
<Text wrap="truncate">{question}</Text>
|
||||
<Text color={theme.text.primary} wrap="truncate">
|
||||
{question}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{/* Select Input for Options */}
|
||||
|
||||
@@ -122,7 +122,9 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
||||
)}
|
||||
{tool.outputFile && (
|
||||
<Box marginX={1}>
|
||||
<Text>Output too long and was saved to: {tool.outputFile}</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Output too long and was saved to: {tool.outputFile}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -9,11 +9,11 @@ import { Box, Text } from 'ink';
|
||||
import type { IndividualToolCallDisplay } from '../../types.js';
|
||||
import { ToolCallStatus } from '../../types.js';
|
||||
import { DiffRenderer } from './DiffRenderer.js';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
|
||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||
import { TOOL_STATUS } from '../../constants.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
|
||||
const STATIC_HEIGHT = 1;
|
||||
const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc.
|
||||
@@ -120,7 +120,7 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
|
||||
}) => (
|
||||
<Box minWidth={STATUS_INDICATOR_WIDTH}>
|
||||
{status === ToolCallStatus.Pending && (
|
||||
<Text color={Colors.AccentGreen}>{TOOL_STATUS.PENDING}</Text>
|
||||
<Text color={theme.status.success}>{TOOL_STATUS.PENDING}</Text>
|
||||
)}
|
||||
{status === ToolCallStatus.Executing && (
|
||||
<GeminiRespondingSpinner
|
||||
@@ -129,22 +129,22 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
|
||||
/>
|
||||
)}
|
||||
{status === ToolCallStatus.Success && (
|
||||
<Text color={Colors.AccentGreen} aria-label={'Success:'}>
|
||||
<Text color={theme.status.success} aria-label={'Success:'}>
|
||||
{TOOL_STATUS.SUCCESS}
|
||||
</Text>
|
||||
)}
|
||||
{status === ToolCallStatus.Confirming && (
|
||||
<Text color={Colors.AccentYellow} aria-label={'Confirming:'}>
|
||||
<Text color={theme.status.warning} aria-label={'Confirming:'}>
|
||||
{TOOL_STATUS.CONFIRMING}
|
||||
</Text>
|
||||
)}
|
||||
{status === ToolCallStatus.Canceled && (
|
||||
<Text color={Colors.AccentYellow} aria-label={'Canceled:'} bold>
|
||||
<Text color={theme.status.warning} aria-label={'Canceled:'} bold>
|
||||
{TOOL_STATUS.CANCELED}
|
||||
</Text>
|
||||
)}
|
||||
{status === ToolCallStatus.Error && (
|
||||
<Text color={Colors.AccentRed} aria-label={'Error:'} bold>
|
||||
<Text color={theme.status.error} aria-label={'Error:'} bold>
|
||||
{TOOL_STATUS.ERROR}
|
||||
</Text>
|
||||
)}
|
||||
@@ -166,11 +166,11 @@ const ToolInfo: React.FC<ToolInfo> = ({
|
||||
const nameColor = React.useMemo<string>(() => {
|
||||
switch (emphasis) {
|
||||
case 'high':
|
||||
return Colors.Foreground;
|
||||
return theme.text.primary;
|
||||
case 'medium':
|
||||
return Colors.Foreground;
|
||||
return theme.text.primary;
|
||||
case 'low':
|
||||
return Colors.Gray;
|
||||
return theme.text.secondary;
|
||||
default: {
|
||||
const exhaustiveCheck: never = emphasis;
|
||||
return exhaustiveCheck;
|
||||
@@ -186,14 +186,14 @@ const ToolInfo: React.FC<ToolInfo> = ({
|
||||
<Text color={nameColor} bold>
|
||||
{name}
|
||||
</Text>{' '}
|
||||
<Text color={Colors.Gray}>{description}</Text>
|
||||
<Text color={theme.text.secondary}>{description}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const TrailingIndicator: React.FC = () => (
|
||||
<Text color={Colors.Foreground} wrap="truncate">
|
||||
<Text color={theme.text.primary} wrap="truncate">
|
||||
{' '}
|
||||
←
|
||||
</Text>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { SCREEN_READER_USER_PREFIX } from '../../textConstants.js';
|
||||
import { isSlashCommand as checkIsSlashCommand } from '../../utils/commandUtils.js';
|
||||
|
||||
@@ -19,8 +19,8 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
|
||||
const prefixWidth = prefix.length;
|
||||
const isSlashCommand = checkIsSlashCommand(text);
|
||||
|
||||
const textColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
|
||||
const borderColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
|
||||
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
|
||||
const borderColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
|
||||
interface UserShellMessageProps {
|
||||
text: string;
|
||||
@@ -18,8 +18,8 @@ export const UserShellMessage: React.FC<UserShellMessageProps> = ({ text }) => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text color={Colors.AccentCyan}>$ </Text>
|
||||
<Text>{commandToDisplay}</Text>
|
||||
<Text color={theme.text.link}>$ </Text>
|
||||
<Text color={theme.text.primary}>{commandToDisplay}</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import React, { Fragment, useEffect, useId } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import stringWidth from 'string-width';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { toCodePoints } from '../../utils/textUtils.js';
|
||||
import { useOverflowActions } from '../../contexts/OverflowContext.js';
|
||||
|
||||
@@ -186,14 +186,14 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
|
||||
return (
|
||||
<Box flexDirection="column" width={maxWidth} flexShrink={0}>
|
||||
{totalHiddenLines > 0 && overflowDirection === 'top' && (
|
||||
<Text color={Colors.Gray} wrap="truncate">
|
||||
<Text color={theme.text.secondary} wrap="truncate">
|
||||
... first {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
||||
hidden ...
|
||||
</Text>
|
||||
)}
|
||||
{visibleLines}
|
||||
{totalHiddenLines > 0 && overflowDirection === 'bottom' && (
|
||||
<Text color={Colors.Gray} wrap="truncate">
|
||||
<Text color={theme.text.secondary} wrap="truncate">
|
||||
... last {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
||||
hidden ...
|
||||
</Text>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type React from 'react';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { useKeypress } from '../../hooks/useKeypress.js';
|
||||
|
||||
/**
|
||||
@@ -164,7 +164,9 @@ export function RadioButtonSelect<T>({
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{showScrollArrows && (
|
||||
<Text color={scrollOffset > 0 ? Colors.Foreground : Colors.Gray}>
|
||||
<Text
|
||||
color={scrollOffset > 0 ? theme.text.primary : theme.text.secondary}
|
||||
>
|
||||
▲
|
||||
</Text>
|
||||
)}
|
||||
@@ -172,18 +174,18 @@ export function RadioButtonSelect<T>({
|
||||
const itemIndex = scrollOffset + index;
|
||||
const isSelected = activeIndex === itemIndex;
|
||||
|
||||
let textColor = Colors.Foreground;
|
||||
let numberColor = Colors.Foreground;
|
||||
let textColor = theme.text.primary;
|
||||
let numberColor = theme.text.primary;
|
||||
if (isSelected) {
|
||||
textColor = Colors.AccentGreen;
|
||||
numberColor = Colors.AccentGreen;
|
||||
textColor = theme.status.success;
|
||||
numberColor = theme.status.success;
|
||||
} else if (item.disabled) {
|
||||
textColor = Colors.Gray;
|
||||
numberColor = Colors.Gray;
|
||||
textColor = theme.text.secondary;
|
||||
numberColor = theme.text.secondary;
|
||||
}
|
||||
|
||||
if (!showNumbers) {
|
||||
numberColor = Colors.Gray;
|
||||
numberColor = theme.text.secondary;
|
||||
}
|
||||
|
||||
const numberColumnWidth = String(items.length).length;
|
||||
@@ -195,7 +197,7 @@ export function RadioButtonSelect<T>({
|
||||
<Box key={item.label} alignItems="center">
|
||||
<Box minWidth={2} flexShrink={0}>
|
||||
<Text
|
||||
color={isSelected ? Colors.AccentGreen : Colors.Foreground}
|
||||
color={isSelected ? theme.status.success : theme.text.primary}
|
||||
aria-hidden
|
||||
>
|
||||
{isSelected ? '●' : ' '}
|
||||
@@ -212,7 +214,9 @@ export function RadioButtonSelect<T>({
|
||||
{item.themeNameDisplay && item.themeTypeDisplay ? (
|
||||
<Text color={textColor} wrap="truncate">
|
||||
{item.themeNameDisplay}{' '}
|
||||
<Text color={Colors.Gray}>{item.themeTypeDisplay}</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
{item.themeTypeDisplay}
|
||||
</Text>
|
||||
</Text>
|
||||
) : (
|
||||
<Text color={textColor} wrap="truncate">
|
||||
@@ -226,8 +230,8 @@ export function RadioButtonSelect<T>({
|
||||
<Text
|
||||
color={
|
||||
scrollOffset + maxItemsToShow < items.length
|
||||
? Colors.Foreground
|
||||
: Colors.Gray
|
||||
? theme.text.primary
|
||||
: theme.text.secondary
|
||||
}
|
||||
>
|
||||
▼
|
||||
|
||||
@@ -9,7 +9,7 @@ import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
|
||||
import { usePrivacySettings } from '../hooks/usePrivacySettings.js';
|
||||
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
|
||||
import type { Config } from '@google/gemini-cli-core';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
|
||||
interface CloudFreePrivacyNoticeProps {
|
||||
@@ -34,16 +34,16 @@ export const CloudFreePrivacyNotice = ({
|
||||
);
|
||||
|
||||
if (privacyState.isLoading) {
|
||||
return <Text color={Colors.Gray}>Loading...</Text>;
|
||||
return <Text color={theme.text.secondary}>Loading...</Text>;
|
||||
}
|
||||
|
||||
if (privacyState.error) {
|
||||
return (
|
||||
<Box flexDirection="column" marginY={1}>
|
||||
<Text color={Colors.AccentRed}>
|
||||
<Text color={theme.status.error}>
|
||||
Error loading Opt-in settings: {privacyState.error}
|
||||
</Text>
|
||||
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -59,17 +59,17 @@ export const CloudFreePrivacyNotice = ({
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginY={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Gemini Code Assist for Individuals Privacy Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
This notice and our Privacy Policy
|
||||
<Text color={Colors.AccentBlue}>[1]</Text> describe how Gemini Code
|
||||
Assist handles your data. Please read them carefully.
|
||||
<Text color={theme.text.link}>[1]</Text> describe how Gemini Code Assist
|
||||
handles your data. Please read them carefully.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
When you use Gemini Code Assist for individuals with Gemini CLI, Google
|
||||
collects your prompts, related code, generated output, code edits,
|
||||
related feature usage information, and your feedback to provide,
|
||||
@@ -77,7 +77,7 @@ export const CloudFreePrivacyNotice = ({
|
||||
technologies.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
To help with quality and improve our products (such as generative
|
||||
machine-learning models), human reviewers may read, annotate, and
|
||||
process the data collected above. We take steps to protect your privacy
|
||||
@@ -90,7 +90,7 @@ export const CloudFreePrivacyNotice = ({
|
||||
</Text>
|
||||
<Newline />
|
||||
<Box flexDirection="column">
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
Allow Google to use this data to develop and improve our products?
|
||||
</Text>
|
||||
<RadioButtonSelect
|
||||
@@ -107,11 +107,13 @@ export const CloudFreePrivacyNotice = ({
|
||||
</Box>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
||||
https://policies.google.com/privacy
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Enter to choose an option and exit.</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
Press Enter to choose an option and exit.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Box, Newline, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
|
||||
interface CloudPaidPrivacyNoticeProps {
|
||||
@@ -26,14 +26,14 @@ export const CloudPaidPrivacyNotice = ({
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Vertex AI Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
Service Specific Terms<Text color={Colors.AccentBlue}>[1]</Text> are
|
||||
<Text color={theme.text.primary}>
|
||||
Service Specific Terms<Text color={theme.text.link}>[1]</Text> are
|
||||
incorporated into the agreement under which Google has agreed to provide
|
||||
Google Cloud Platform<Text color={Colors.AccentGreen}>[2]</Text> to
|
||||
Google Cloud Platform<Text color={theme.status.success}>[2]</Text> to
|
||||
Customer (the “Agreement”). If the Agreement authorizes the resale or
|
||||
supply of Google Cloud Platform under a Google Cloud partner or reseller
|
||||
program, then except for in the section entitled “Partner-Specific
|
||||
@@ -44,16 +44,16 @@ export const CloudPaidPrivacyNotice = ({
|
||||
them in the Agreement.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
||||
https://cloud.google.com/terms/service-terms
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentGreen}>[2]</Text>{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.status.success}>[2]</Text>{' '}
|
||||
https://cloud.google.com/terms/services
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Box, Newline, Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
|
||||
interface GeminiPrivacyNoticeProps {
|
||||
@@ -24,39 +24,39 @@ export const GeminiPrivacyNotice = ({ onExit }: GeminiPrivacyNoticeProps) => {
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
<Text bold color={theme.text.accent}>
|
||||
Gemini API Key Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
By using the Gemini API<Text color={Colors.AccentBlue}>[1]</Text>,
|
||||
Google AI Studio
|
||||
<Text color={Colors.AccentRed}>[2]</Text>, and the other Google
|
||||
<Text color={theme.text.primary}>
|
||||
By using the Gemini API<Text color={theme.text.link}>[1]</Text>, Google
|
||||
AI Studio
|
||||
<Text color={theme.status.error}>[2]</Text>, and the other Google
|
||||
developer services that reference these terms (collectively, the
|
||||
"APIs" or "Services"), you are agreeing to Google
|
||||
APIs Terms of Service (the "API Terms")
|
||||
<Text color={Colors.AccentGreen}>[3]</Text>, and the Gemini API
|
||||
<Text color={theme.status.success}>[3]</Text>, and the Gemini API
|
||||
Additional Terms of Service (the "Additional Terms")
|
||||
<Text color={Colors.AccentPurple}>[4]</Text>.
|
||||
<Text color={theme.text.accent}>[4]</Text>.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
||||
https://ai.google.dev/docs/gemini_api_overview
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentRed}>[2]</Text> https://aistudio.google.com/
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.status.error}>[2]</Text> https://aistudio.google.com/
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentGreen}>[3]</Text>{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.status.success}>[3]</Text>{' '}
|
||||
https://developers.google.com/terms
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentPurple}>[4]</Text>{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
<Text color={theme.text.accent}>[4]</Text>{' '}
|
||||
https://ai.google.dev/gemini-api/terms
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -410,31 +410,31 @@ export function createCustomTheme(customTheme: CustomTheme): Theme {
|
||||
|
||||
const semanticColors: SemanticColors = {
|
||||
text: {
|
||||
primary: colors.Foreground,
|
||||
secondary: colors.Gray,
|
||||
link: colors.AccentBlue,
|
||||
accent: colors.AccentPurple,
|
||||
primary: customTheme.text?.primary ?? colors.Foreground,
|
||||
secondary: customTheme.text?.secondary ?? colors.Gray,
|
||||
link: customTheme.text?.link ?? colors.AccentBlue,
|
||||
accent: customTheme.text?.accent ?? colors.AccentPurple,
|
||||
},
|
||||
background: {
|
||||
primary: colors.Background,
|
||||
primary: customTheme.background?.primary ?? colors.Background,
|
||||
diff: {
|
||||
added: colors.DiffAdded,
|
||||
removed: colors.DiffRemoved,
|
||||
added: customTheme.background?.diff?.added ?? colors.DiffAdded,
|
||||
removed: customTheme.background?.diff?.removed ?? colors.DiffRemoved,
|
||||
},
|
||||
},
|
||||
border: {
|
||||
default: colors.Gray,
|
||||
focused: colors.AccentBlue,
|
||||
default: customTheme.border?.default ?? colors.Gray,
|
||||
focused: customTheme.border?.focused ?? colors.AccentBlue,
|
||||
},
|
||||
ui: {
|
||||
comment: colors.Comment,
|
||||
symbol: colors.Gray,
|
||||
gradient: colors.GradientColors,
|
||||
comment: customTheme.ui?.comment ?? colors.Comment,
|
||||
symbol: customTheme.ui?.symbol ?? colors.Gray,
|
||||
gradient: customTheme.ui?.gradient ?? colors.GradientColors,
|
||||
},
|
||||
status: {
|
||||
error: colors.AccentRed,
|
||||
success: colors.AccentGreen,
|
||||
warning: colors.AccentYellow,
|
||||
error: customTheme.status?.error ?? colors.AccentRed,
|
||||
success: customTheme.status?.success ?? colors.AccentGreen,
|
||||
warning: customTheme.status?.warning ?? colors.AccentYellow,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import stringWidth from 'string-width';
|
||||
|
||||
// Constants for Markdown parsing
|
||||
const BOLD_MARKER_LENGTH = 2; // For "**"
|
||||
const ITALIC_MARKER_LENGTH = 1; // For "*" or "_"
|
||||
const STRIKETHROUGH_MARKER_LENGTH = 2; // For "~~"
|
||||
const STRIKETHROUGH_MARKER_LENGTH = 2; // For "~~")
|
||||
const INLINE_CODE_MARKER_LENGTH = 1; // For "`"
|
||||
const UNDERLINE_TAG_START_LENGTH = 3; // For "<u>"
|
||||
const UNDERLINE_TAG_END_LENGTH = 4; // For "</u>"
|
||||
@@ -24,7 +24,7 @@ interface RenderInlineProps {
|
||||
const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
// Early return for plain text without markdown or URLs
|
||||
if (!/[*_~`<[https?:]/.test(text)) {
|
||||
return <Text>{text}</Text>;
|
||||
return <Text color={theme.text.primary}>{text}</Text>;
|
||||
}
|
||||
|
||||
const nodes: React.ReactNode[] = [];
|
||||
@@ -96,7 +96,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
|
||||
if (codeMatch && codeMatch[2]) {
|
||||
renderedNode = (
|
||||
<Text key={key} color={Colors.AccentPurple}>
|
||||
<Text key={key} color={theme.text.accent}>
|
||||
{codeMatch[2]}
|
||||
</Text>
|
||||
);
|
||||
@@ -113,7 +113,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
renderedNode = (
|
||||
<Text key={key}>
|
||||
{linkText}
|
||||
<Text color={Colors.AccentBlue}> ({url})</Text>
|
||||
<Text color={theme.text.link}> ({url})</Text>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@@ -133,7 +133,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
);
|
||||
} else if (fullMatch.match(/^https?:\/\//)) {
|
||||
renderedNode = (
|
||||
<Text key={key} color={Colors.AccentBlue}>
|
||||
<Text key={key} color={theme.text.link}>
|
||||
{fullMatch}
|
||||
</Text>
|
||||
);
|
||||
@@ -168,6 +168,6 @@ export const getPlainTextLength = (text: string): number => {
|
||||
.replace(/~~(.*?)~~/g, '$1')
|
||||
.replace(/`(.*?)`/g, '$1')
|
||||
.replace(/<u>(.*?)<\/u>/g, '$1')
|
||||
.replace(/\[(.*?)\]\(.*?\)/g, '$1');
|
||||
.replace(/.*\[(.*?)\]\(.*\)/g, '$1');
|
||||
return stringWidth(cleanText);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { EOL } from 'node:os';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { colorizeCode } from './CodeColorizer.js';
|
||||
import { TableRenderer } from './TableRenderer.js';
|
||||
import { RenderInline } from './InlineMarkdownRenderer.js';
|
||||
@@ -174,14 +174,14 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
switch (level) {
|
||||
case 1:
|
||||
headerNode = (
|
||||
<Text bold color={Colors.AccentCyan}>
|
||||
<Text bold color={theme.text.link}>
|
||||
<RenderInline text={headerText} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
headerNode = (
|
||||
<Text bold color={Colors.AccentBlue}>
|
||||
<Text bold color={theme.text.link}>
|
||||
<RenderInline text={headerText} />
|
||||
</Text>
|
||||
);
|
||||
@@ -195,14 +195,14 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
break;
|
||||
case 4:
|
||||
headerNode = (
|
||||
<Text italic color={Colors.Gray}>
|
||||
<Text italic color={theme.text.secondary}>
|
||||
<RenderInline text={headerText} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
headerNode = (
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
<RenderInline text={headerText} />
|
||||
</Text>
|
||||
);
|
||||
@@ -246,7 +246,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
} else {
|
||||
addContentBlock(
|
||||
<Box key={key}>
|
||||
<Text wrap="wrap">
|
||||
<Text wrap="wrap" color={theme.text.primary}>
|
||||
<RenderInline text={line} />
|
||||
</Text>
|
||||
</Box>,
|
||||
@@ -315,7 +315,9 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
// Not enough space to even show the message meaningfully
|
||||
return (
|
||||
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING}>
|
||||
<Text color={Colors.Gray}>... code is being written ...</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
... code is being written ...
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -331,7 +333,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
return (
|
||||
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column">
|
||||
{colorizedTruncatedCode}
|
||||
<Text color={Colors.Gray}>... generating more ...</Text>
|
||||
<Text color={theme.text.secondary}>... generating more ...</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
|
||||
|
||||
interface TableRendererProps {
|
||||
@@ -89,7 +89,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
return (
|
||||
<Text>
|
||||
{isHeader ? (
|
||||
<Text bold color={Colors.AccentCyan}>
|
||||
<Text bold color={theme.text.link}>
|
||||
<RenderInline text={cellContent} />
|
||||
</Text>
|
||||
) : (
|
||||
@@ -112,7 +112,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w));
|
||||
const border = char.left + borderParts.join(char.middle) + char.right;
|
||||
|
||||
return <Text>{border}</Text>;
|
||||
return <Text color={theme.border.default}>{border}</Text>;
|
||||
};
|
||||
|
||||
// Helper function to render a table row
|
||||
@@ -123,7 +123,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<Text>
|
||||
<Text color={theme.text.primary}>
|
||||
│{' '}
|
||||
{renderedCells.map((cell, index) => (
|
||||
<React.Fragment key={index}>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Colors } from '../colors.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
// --- Thresholds ---
|
||||
export const TOOL_SUCCESS_RATE_HIGH = 95;
|
||||
@@ -23,10 +23,10 @@ export const getStatusColor = (
|
||||
options: { defaultColor?: string } = {},
|
||||
) => {
|
||||
if (value >= thresholds.green) {
|
||||
return Colors.AccentGreen;
|
||||
return theme.status.success;
|
||||
}
|
||||
if (value >= thresholds.yellow) {
|
||||
return Colors.AccentYellow;
|
||||
return theme.status.warning;
|
||||
}
|
||||
return options.defaultColor || Colors.AccentRed;
|
||||
return options.defaultColor || theme.status.error;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user