Revert "Update semantic color tokens" (#6365)
This commit is contained in:
parent
d57cc0b930
commit
6c1373c332
|
@ -38,7 +38,7 @@ import { EditorSettingsDialog } from './components/EditorSettingsDialog.js';
|
||||||
import { FolderTrustDialog } from './components/FolderTrustDialog.js';
|
import { FolderTrustDialog } from './components/FolderTrustDialog.js';
|
||||||
import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js';
|
import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js';
|
||||||
import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
|
||||||
import { theme } from './semantic-colors.js';
|
import { Colors } from './colors.js';
|
||||||
import { loadHierarchicalGeminiMemory } from '../config/config.js';
|
import { loadHierarchicalGeminiMemory } from '../config/config.js';
|
||||||
import { LoadedSettings, SettingScope } from '../config/settings.js';
|
import { LoadedSettings, SettingScope } from '../config/settings.js';
|
||||||
import { Tips } from './components/Tips.js';
|
import { Tips } from './components/Tips.js';
|
||||||
|
@ -949,13 +949,13 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
{startupWarnings.length > 0 && (
|
{startupWarnings.length > 0 && (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.status.warning}
|
borderColor={Colors.AccentYellow}
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
marginY={1}
|
marginY={1}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
{startupWarnings.map((warning, index) => (
|
{startupWarnings.map((warning, index) => (
|
||||||
<Text key={index} color={theme.status.warning}>
|
<Text key={index} color={Colors.AccentYellow}>
|
||||||
{warning}
|
{warning}
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
|
@ -991,7 +991,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
{themeError && (
|
{themeError && (
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text color={theme.status.error}>{themeError}</Text>
|
<Text color={Colors.AccentRed}>{themeError}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<ThemeDialog
|
<ThemeDialog
|
||||||
|
@ -1050,7 +1050,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
{editorError && (
|
{editorError && (
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text color={theme.status.error}>{editorError}</Text>
|
<Text color={Colors.AccentRed}>{editorError}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<EditorSettingsDialog
|
<EditorSettingsDialog
|
||||||
|
@ -1090,20 +1090,18 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
{process.env.GEMINI_SYSTEM_MD && (
|
{process.env.GEMINI_SYSTEM_MD && (
|
||||||
<Text color={theme.status.error}>|⌐■_■| </Text>
|
<Text color={Colors.AccentRed}>|⌐■_■| </Text>
|
||||||
)}
|
)}
|
||||||
{ctrlCPressedOnce ? (
|
{ctrlCPressedOnce ? (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
Press Ctrl+C again to exit.
|
Press Ctrl+C again to exit.
|
||||||
</Text>
|
</Text>
|
||||||
) : ctrlDPressedOnce ? (
|
) : ctrlDPressedOnce ? (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
Press Ctrl+D again to exit.
|
Press Ctrl+D again to exit.
|
||||||
</Text>
|
</Text>
|
||||||
) : showEscapePrompt ? (
|
) : showEscapePrompt ? (
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>Press Esc again to clear.</Text>
|
||||||
Press Esc again to clear.
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<ContextSummaryDisplay
|
<ContextSummaryDisplay
|
||||||
ideContext={ideContextState}
|
ideContext={ideContextState}
|
||||||
|
@ -1166,7 +1164,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
{initError && streamingState !== StreamingState.Responding && (
|
{initError && streamingState !== StreamingState.Responding && (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.status.error}
|
borderColor={Colors.AccentRed}
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
marginBottom={1}
|
marginBottom={1}
|
||||||
>
|
>
|
||||||
|
@ -1174,7 +1172,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
(item) =>
|
(item) =>
|
||||||
item.type === 'error' && item.text?.includes(initError),
|
item.type === 'error' && item.text?.includes(initError),
|
||||||
)?.text ? (
|
)?.text ? (
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
{
|
{
|
||||||
history.find(
|
history.find(
|
||||||
(item) =>
|
(item) =>
|
||||||
|
@ -1184,10 +1182,10 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
Initialization Error: {initError}
|
Initialization Error: {initError}
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
{' '}
|
{' '}
|
||||||
Please check API key and configuration.
|
Please check API key and configuration.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import * as fsPromises from 'fs/promises';
|
import * as fsPromises from 'fs/promises';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'ink';
|
import { Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
CommandContext,
|
CommandContext,
|
||||||
SlashCommand,
|
SlashCommand,
|
||||||
|
@ -124,7 +124,7 @@ const saveCommand: SlashCommand = {
|
||||||
Text,
|
Text,
|
||||||
null,
|
null,
|
||||||
'A checkpoint with the tag ',
|
'A checkpoint with the tag ',
|
||||||
React.createElement(Text, { color: theme.text.accent }, tag),
|
React.createElement(Text, { color: Colors.AccentPurple }, tag),
|
||||||
' already exists. Do you want to overwrite it?',
|
' already exists. Do you want to overwrite it?',
|
||||||
),
|
),
|
||||||
originalInvocation: {
|
originalInvocation: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||||
|
|
||||||
interface AboutBoxProps {
|
interface AboutBoxProps {
|
||||||
|
@ -30,77 +30,77 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
|
||||||
}) => (
|
}) => (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
padding={1}
|
padding={1}
|
||||||
marginY={1}
|
marginY={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
About Gemini CLI
|
About Gemini CLI
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
CLI Version
|
CLI Version
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{cliVersion}</Text>
|
<Text>{cliVersion}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && (
|
{GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && (
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
Git Commit
|
Git Commit
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{GIT_COMMIT_INFO}</Text>
|
<Text>{GIT_COMMIT_INFO}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
Model
|
Model
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{modelVersion}</Text>
|
<Text>{modelVersion}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
Sandbox
|
Sandbox
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{sandboxEnv}</Text>
|
<Text>{sandboxEnv}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
OS
|
OS
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{osVersion}</Text>
|
<Text>{osVersion}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
Auth Method
|
Auth Method
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType}
|
{selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -108,19 +108,19 @@ export const AboutBox: React.FC<AboutBoxProps> = ({
|
||||||
{gcpProject && (
|
{gcpProject && (
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
GCP Project
|
GCP Project
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>{gcpProject}</Text>
|
<Text>{gcpProject}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{ideClient && (
|
{ideClient && (
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width="35%">
|
<Box width="35%">
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.LightBlue}>
|
||||||
IDE Client
|
IDE Client
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||||
import { LoadedSettings, SettingScope } from '../../config/settings.js';
|
import { LoadedSettings, SettingScope } from '../../config/settings.js';
|
||||||
import { AuthType } from '@google/gemini-cli-core';
|
import { AuthType } from '@google/gemini-cli-core';
|
||||||
|
@ -133,18 +133,14 @@ export function AuthDialog({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Get started</Text>
|
||||||
Get started
|
|
||||||
</Text>
|
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.primary}>
|
<Text>How would you like to authenticate for this project?</Text>
|
||||||
How would you like to authenticate for this project?
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
|
@ -155,19 +151,17 @@ export function AuthDialog({
|
||||||
</Box>
|
</Box>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.status.error}>{errorMessage}</Text>
|
<Text color={Colors.AccentRed}>{errorMessage}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.secondary}>(Use Enter to select)</Text>
|
<Text color={Colors.Gray}>(Use Enter to select)</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.primary}>
|
<Text>Terms of Services and Privacy Notice for Gemini CLI</Text>
|
||||||
Terms of Services and Privacy Notice for Gemini CLI
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.link}>
|
<Text color={Colors.AccentBlue}>
|
||||||
{
|
{
|
||||||
'https://github.com/google-gemini/gemini-cli/blob/main/docs/tos-privacy.md'
|
'https://github.com/google-gemini/gemini-cli/blob/main/docs/tos-privacy.md'
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import Spinner from 'ink-spinner';
|
import Spinner from 'ink-spinner';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
interface AuthInProgressProps {
|
interface AuthInProgressProps {
|
||||||
|
@ -40,18 +40,18 @@ export function AuthInProgress({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
{timedOut ? (
|
{timedOut ? (
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
Authentication timed out. Please try again.
|
Authentication timed out. Please try again.
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Spinner type="dots" /> Waiting for auth... (Press ESC or CTRL+C to
|
<Spinner type="dots" /> Waiting for auth... (Press ESC or CTRL+C to
|
||||||
cancel)
|
cancel)
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { ApprovalMode } from '@google/gemini-cli-core';
|
import { ApprovalMode } from '@google/gemini-cli-core';
|
||||||
|
|
||||||
interface AutoAcceptIndicatorProps {
|
interface AutoAcceptIndicatorProps {
|
||||||
|
@ -22,12 +22,12 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
|
||||||
|
|
||||||
switch (approvalMode) {
|
switch (approvalMode) {
|
||||||
case ApprovalMode.AUTO_EDIT:
|
case ApprovalMode.AUTO_EDIT:
|
||||||
textColor = theme.status.success;
|
textColor = Colors.AccentGreen;
|
||||||
textContent = 'accepting edits';
|
textContent = 'accepting edits';
|
||||||
subText = ' (shift + tab to toggle)';
|
subText = ' (shift + tab to toggle)';
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.YOLO:
|
case ApprovalMode.YOLO:
|
||||||
textColor = theme.status.error;
|
textColor = Colors.AccentRed;
|
||||||
textContent = 'YOLO mode';
|
textContent = 'YOLO mode';
|
||||||
subText = ' (ctrl + y to toggle)';
|
subText = ' (ctrl + y to toggle)';
|
||||||
break;
|
break;
|
||||||
|
@ -40,7 +40,7 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={textColor}>
|
<Text color={textColor}>
|
||||||
{textContent}
|
{textContent}
|
||||||
{subText && <Text color={theme.text.secondary}>{subText}</Text>}
|
{subText && <Text color={Colors.Gray}>{subText}</Text>}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
interface ConsoleSummaryDisplayProps {
|
interface ConsoleSummaryDisplayProps {
|
||||||
errorCount: number;
|
errorCount: number;
|
||||||
|
@ -25,9 +25,9 @@ export const ConsoleSummaryDisplay: React.FC<ConsoleSummaryDisplayProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{errorCount > 0 && (
|
{errorCount > 0 && (
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
{errorIcon} {errorCount} error{errorCount > 1 ? 's' : ''}{' '}
|
{errorIcon} {errorCount} error{errorCount > 1 ? 's' : ''}{' '}
|
||||||
<Text color={theme.text.secondary}>(ctrl+o for details)</Text>
|
<Text color={Colors.Gray}>(ctrl+o for details)</Text>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core';
|
import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core';
|
||||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||||
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
||||||
|
@ -99,9 +99,9 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
|
||||||
if (isNarrow) {
|
if (isNarrow) {
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
<Text color={theme.text.secondary}>Using:</Text>
|
<Text color={Colors.Gray}>Using:</Text>
|
||||||
{summaryParts.map((part, index) => (
|
{summaryParts.map((part, index) => (
|
||||||
<Text key={index} color={theme.text.secondary}>
|
<Text key={index} color={Colors.Gray}>
|
||||||
{' '}- {part}
|
{' '}- {part}
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
|
@ -111,9 +111,7 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>Using: {summaryParts.join(' | ')}</Text>
|
||||||
Using: {summaryParts.join(' | ')}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Text } from 'ink';
|
import { Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { tokenLimit } from '@google/gemini-cli-core';
|
import { tokenLimit } from '@google/gemini-cli-core';
|
||||||
|
|
||||||
export const ContextUsageDisplay = ({
|
export const ContextUsageDisplay = ({
|
||||||
|
@ -18,7 +18,7 @@ export const ContextUsageDisplay = ({
|
||||||
const percentage = promptTokenCount / tokenLimit(model);
|
const percentage = promptTokenCount / tokenLimit(model);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
({((1 - percentage) * 100).toFixed(0)}% context left)
|
({((1 - percentage) * 100).toFixed(0)}% context left)
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { Text } from 'ink';
|
import { Text } from 'ink';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
export const DebugProfiler = () => {
|
export const DebugProfiler = () => {
|
||||||
|
@ -31,6 +31,6 @@ export const DebugProfiler = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text color={theme.status.warning}>Renders: {numRenders.current} </Text>
|
<Text color={Colors.AccentYellow}>Renders: {numRenders.current} </Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { ConsoleMessageItem } from '../types.js';
|
import { ConsoleMessageItem } from '../types.js';
|
||||||
import { MaxSizedBox } from './shared/MaxSizedBox.js';
|
import { MaxSizedBox } from './shared/MaxSizedBox.js';
|
||||||
|
|
||||||
|
@ -31,32 +31,31 @@ export const DetailedMessagesDisplay: React.FC<
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
marginTop={1}
|
marginTop={1}
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
width={width}
|
width={width}
|
||||||
>
|
>
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={Colors.Foreground}>
|
||||||
Debug Console{' '}
|
Debug Console <Text color={Colors.Gray}>(ctrl+o to close)</Text>
|
||||||
<Text color={theme.text.secondary}>(ctrl+o to close)</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<MaxSizedBox maxHeight={maxHeight} maxWidth={width - borderAndPadding}>
|
<MaxSizedBox maxHeight={maxHeight} maxWidth={width - borderAndPadding}>
|
||||||
{messages.map((msg, index) => {
|
{messages.map((msg, index) => {
|
||||||
let textColor = theme.text.primary;
|
let textColor = Colors.Foreground;
|
||||||
let icon = '\u2139'; // Information source (ℹ)
|
let icon = '\u2139'; // Information source (ℹ)
|
||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'warn':
|
case 'warn':
|
||||||
textColor = theme.status.warning;
|
textColor = Colors.AccentYellow;
|
||||||
icon = '\u26A0'; // Warning sign (⚠)
|
icon = '\u26A0'; // Warning sign (⚠)
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
textColor = theme.status.error;
|
textColor = Colors.AccentRed;
|
||||||
icon = '\u2716'; // Heavy multiplication x (✖)
|
icon = '\u2716'; // Heavy multiplication x (✖)
|
||||||
break;
|
break;
|
||||||
case 'debug':
|
case 'debug':
|
||||||
textColor = theme.text.secondary;
|
textColor = Colors.Gray; // Or Colors.Gray
|
||||||
icon = '\u1F50D'; // Left-pointing magnifying glass (????)
|
icon = '\u1F50D'; // Left-pointing magnifying glass (????)
|
||||||
break;
|
break;
|
||||||
case 'log':
|
case 'log':
|
||||||
|
@ -71,7 +70,7 @@ export const DetailedMessagesDisplay: React.FC<
|
||||||
<Text color={textColor} wrap="wrap">
|
<Text color={textColor} wrap="wrap">
|
||||||
{msg.content}
|
{msg.content}
|
||||||
{msg.count && msg.count > 1 && (
|
{msg.count && msg.count > 1 && (
|
||||||
<Text color={theme.text.secondary}> (x{msg.count})</Text>
|
<Text color={Colors.Gray}> (x{msg.count})</Text>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
EDITOR_DISPLAY_NAMES,
|
EDITOR_DISPLAY_NAMES,
|
||||||
editorSettingsManager,
|
editorSettingsManager,
|
||||||
|
@ -103,7 +103,7 @@ export function EditorSettingsDialog({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -111,7 +111,7 @@ export function EditorSettingsDialog({
|
||||||
<Box flexDirection="column" width="45%" paddingRight={2}>
|
<Box flexDirection="column" width="45%" paddingRight={2}>
|
||||||
<Text bold={focusedSection === 'editor'}>
|
<Text bold={focusedSection === 'editor'}>
|
||||||
{focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '}
|
{focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '}
|
||||||
<Text color={theme.text.secondary}>{otherScopeModifiedMessage}</Text>
|
<Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
items={editorItems.map((item) => ({
|
items={editorItems.map((item) => ({
|
||||||
|
@ -138,7 +138,7 @@ export function EditorSettingsDialog({
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
(Use Enter to select, Tab to change focus)
|
(Use Enter to select, Tab to change focus)
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -147,17 +147,17 @@ export function EditorSettingsDialog({
|
||||||
<Box flexDirection="column" width="55%" paddingLeft={2}>
|
<Box flexDirection="column" width="55%" paddingLeft={2}>
|
||||||
<Text bold>Editor Preference</Text>
|
<Text bold>Editor Preference</Text>
|
||||||
<Box flexDirection="column" gap={1} marginTop={1}>
|
<Box flexDirection="column" gap={1} marginTop={1}>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
These editors are currently supported. Please note that some editors
|
These editors are currently supported. Please note that some editors
|
||||||
cannot be used in sandbox mode.
|
cannot be used in sandbox mode.
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
Your preferred editor is:{' '}
|
Your preferred editor is:{' '}
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
mergedEditorName === 'None'
|
mergedEditorName === 'None'
|
||||||
? theme.status.error
|
? Colors.AccentRed
|
||||||
: theme.text.accent
|
: Colors.AccentCyan
|
||||||
}
|
}
|
||||||
bold
|
bold
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
RadioButtonSelect,
|
RadioButtonSelect,
|
||||||
RadioSelectItem,
|
RadioSelectItem,
|
||||||
|
@ -54,16 +54,14 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.status.warning}
|
borderColor={Colors.AccentYellow}
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
marginLeft={1}
|
marginLeft={1}
|
||||||
>
|
>
|
||||||
<Box flexDirection="column" marginBottom={1}>
|
<Box flexDirection="column" marginBottom={1}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Do you trust this folder?</Text>
|
||||||
Do you trust this folder?
|
<Text>
|
||||||
</Text>
|
|
||||||
<Text color={theme.text.primary}>
|
|
||||||
Trusting a folder allows Gemini to execute commands it suggests. This
|
Trusting a folder allows Gemini to execute commands it suggests. This
|
||||||
is a security feature to prevent accidental execution in untrusted
|
is a security feature to prevent accidental execution in untrusted
|
||||||
directories.
|
directories.
|
||||||
|
|
|
@ -72,7 +72,7 @@ export const Footer: React.FC<FooterProps> = ({
|
||||||
{vimMode && <Text color={theme.text.secondary}>[{vimMode}] </Text>}
|
{vimMode && <Text color={theme.text.secondary}>[{vimMode}] </Text>}
|
||||||
{nightly ? (
|
{nightly ? (
|
||||||
<Gradient colors={theme.ui.gradient}>
|
<Gradient colors={theme.ui.gradient}>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{displayPath}
|
{displayPath}
|
||||||
{branchName && <Text> ({branchName}*)</Text>}
|
{branchName && <Text> ({branchName}*)</Text>}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -132,8 +132,8 @@ export const Footer: React.FC<FooterProps> = ({
|
||||||
/>
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
{corgiMode && (
|
{corgiMode && (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.text.secondary}>| </Text>
|
<Text color={theme.ui.symbol}>| </Text>
|
||||||
<Text color={theme.status.error}>▼</Text>
|
<Text color={theme.status.error}>▼</Text>
|
||||||
<Text color={theme.text.primary}>(´</Text>
|
<Text color={theme.text.primary}>(´</Text>
|
||||||
<Text color={theme.status.error}>ᴥ</Text>
|
<Text color={theme.status.error}>ᴥ</Text>
|
||||||
|
@ -143,7 +143,7 @@ export const Footer: React.FC<FooterProps> = ({
|
||||||
)}
|
)}
|
||||||
{!showErrorDetails && errorCount > 0 && (
|
{!showErrorDetails && errorCount > 0 && (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.secondary}>| </Text>
|
<Text color={theme.ui.symbol}>| </Text>
|
||||||
<ConsoleSummaryDisplay errorCount={errorCount} />
|
<ConsoleSummaryDisplay errorCount={errorCount} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import Spinner from 'ink-spinner';
|
||||||
import type { SpinnerName } from 'cli-spinners';
|
import type { SpinnerName } from 'cli-spinners';
|
||||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||||
import { StreamingState } from '../types.js';
|
import { StreamingState } from '../types.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
|
||||||
|
|
||||||
interface GeminiRespondingSpinnerProps {
|
interface GeminiRespondingSpinnerProps {
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +28,7 @@ export const GeminiRespondingSpinner: React.FC<
|
||||||
if (streamingState === StreamingState.Responding) {
|
if (streamingState === StreamingState.Responding) {
|
||||||
return <Spinner type={spinnerType} />;
|
return <Spinner type={spinnerType} />;
|
||||||
} else if (nonRespondingDisplay) {
|
} else if (nonRespondingDisplay) {
|
||||||
return <Text color={theme.text.primary}>{nonRespondingDisplay}</Text>;
|
return <Text>{nonRespondingDisplay}</Text>;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render } from 'ink-testing-library';
|
import { render } from 'ink-testing-library';
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { Header } from './Header.js';
|
import { Header } from './Header.js';
|
||||||
import * as useTerminalSize from '../hooks/useTerminalSize.js';
|
import * as useTerminalSize from '../hooks/useTerminalSize.js';
|
||||||
import { longAsciiLogo } from './AsciiArt.js';
|
import { longAsciiLogo } from './AsciiArt.js';
|
||||||
|
@ -13,13 +13,15 @@ import { longAsciiLogo } from './AsciiArt.js';
|
||||||
vi.mock('../hooks/useTerminalSize.js');
|
vi.mock('../hooks/useTerminalSize.js');
|
||||||
|
|
||||||
describe('<Header />', () => {
|
describe('<Header />', () => {
|
||||||
|
beforeEach(() => {});
|
||||||
|
|
||||||
it('renders the long logo on a wide terminal', () => {
|
it('renders the long logo on a wide terminal', () => {
|
||||||
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
|
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
|
||||||
columns: 120,
|
columns: 120,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
});
|
});
|
||||||
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
|
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
|
||||||
expect(lastFrame()?.trim()).toContain(longAsciiLogo.trim());
|
expect(lastFrame()).toContain(longAsciiLogo);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders custom ASCII art when provided', () => {
|
it('renders custom ASCII art when provided', () => {
|
||||||
|
@ -27,20 +29,16 @@ describe('<Header />', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<Header version="1.0.0" nightly={false} customAsciiArt={customArt} />,
|
<Header version="1.0.0" nightly={false} customAsciiArt={customArt} />,
|
||||||
);
|
);
|
||||||
expect(lastFrame()?.trim()).toContain(customArt);
|
expect(lastFrame()).toContain(customArt);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays the version number when nightly is true', () => {
|
it('displays the version number when nightly is true', () => {
|
||||||
const { lastFrame } = render(<Header version="1.0.0" nightly={true} />);
|
const { lastFrame } = render(<Header version="1.0.0" nightly={true} />);
|
||||||
expect(lastFrame()?.trim()).toContain('v1.0.0');
|
expect(lastFrame()).toContain('v1.0.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not display the version number when nightly is false', () => {
|
it('does not display the version number when nightly is false', () => {
|
||||||
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
|
|
||||||
columns: 40,
|
|
||||||
rows: 20,
|
|
||||||
});
|
|
||||||
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
|
const { lastFrame } = render(<Header version="1.0.0" nightly={false} />);
|
||||||
expect(lastFrame()?.trim()).not.toContain('v1.0.0');
|
expect(lastFrame()).not.toContain('v1.0.0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import Gradient from 'ink-gradient';
|
import Gradient from 'ink-gradient';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js';
|
import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js';
|
||||||
import { getAsciiArtWidth } from '../utils/textUtils.js';
|
import { getAsciiArtWidth } from '../utils/textUtils.js';
|
||||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||||
|
@ -18,16 +18,6 @@ interface HeaderProps {
|
||||||
nightly: boolean;
|
nightly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GradientText: React.FC<{ children: React.ReactNode }> = ({
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
const textElement = <Text color={theme.text.primary}>{children}</Text>;
|
|
||||||
if (theme.ui.gradient && theme.ui.gradient.length > 0) {
|
|
||||||
return <Gradient colors={theme.ui.gradient}>{textElement}</Gradient>;
|
|
||||||
}
|
|
||||||
return textElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Header: React.FC<HeaderProps> = ({
|
export const Header: React.FC<HeaderProps> = ({
|
||||||
customAsciiArt,
|
customAsciiArt,
|
||||||
version,
|
version,
|
||||||
|
@ -57,12 +47,22 @@ export const Header: React.FC<HeaderProps> = ({
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
<Box>
|
{Colors.GradientColors ? (
|
||||||
<GradientText>{displayTitle}</GradientText>
|
<Gradient colors={Colors.GradientColors}>
|
||||||
</Box>
|
<Text>{displayTitle}</Text>
|
||||||
|
</Gradient>
|
||||||
|
) : (
|
||||||
|
<Text>{displayTitle}</Text>
|
||||||
|
)}
|
||||||
{nightly && (
|
{nightly && (
|
||||||
<Box width="100%" flexDirection="row" justifyContent="flex-end">
|
<Box width="100%" flexDirection="row" justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>v{version}</Text>
|
{Colors.GradientColors ? (
|
||||||
|
<Gradient colors={Colors.GradientColors}>
|
||||||
|
<Text>v{version}</Text>
|
||||||
|
</Gradient>
|
||||||
|
) : (
|
||||||
|
<Text>v{version}</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { SlashCommand } from '../commands/types.js';
|
import { SlashCommand } from '../commands/types.js';
|
||||||
|
|
||||||
interface Help {
|
interface Help {
|
||||||
|
@ -17,42 +17,42 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
marginBottom={1}
|
marginBottom={1}
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
padding={1}
|
padding={1}
|
||||||
>
|
>
|
||||||
{/* Basics */}
|
{/* Basics */}
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={Colors.Foreground}>
|
||||||
Basics:
|
Basics:
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Add context
|
Add context
|
||||||
</Text>
|
</Text>
|
||||||
: Use{' '}
|
: Use{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
@
|
@
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
to specify files for context (e.g.,{' '}
|
to specify files for context (e.g.,{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
@src/myFile.ts
|
@src/myFile.ts
|
||||||
</Text>
|
</Text>
|
||||||
) to target specific files or folders.
|
) to target specific files or folders.
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Shell mode
|
Shell mode
|
||||||
</Text>
|
</Text>
|
||||||
: Execute shell commands via{' '}
|
: Execute shell commands via{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
!
|
!
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
(e.g.,{' '}
|
(e.g.,{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
!npm run start
|
!npm run start
|
||||||
</Text>
|
</Text>
|
||||||
) or use natural language (e.g.{' '}
|
) or use natural language (e.g.{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
start server
|
start server
|
||||||
</Text>
|
</Text>
|
||||||
).
|
).
|
||||||
|
@ -61,15 +61,15 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{/* Commands */}
|
{/* Commands */}
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={Colors.Foreground}>
|
||||||
Commands:
|
Commands:
|
||||||
</Text>
|
</Text>
|
||||||
{commands
|
{commands
|
||||||
.filter((command) => command.description)
|
.filter((command) => command.description)
|
||||||
.map((command: SlashCommand) => (
|
.map((command: SlashCommand) => (
|
||||||
<Box key={command.name} flexDirection="column">
|
<Box key={command.name} flexDirection="column">
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{' '}
|
{' '}
|
||||||
/{command.name}
|
/{command.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -77,8 +77,8 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||||
</Text>
|
</Text>
|
||||||
{command.subCommands &&
|
{command.subCommands &&
|
||||||
command.subCommands.map((subCommand) => (
|
command.subCommands.map((subCommand) => (
|
||||||
<Text key={subCommand.name} color={theme.text.primary}>
|
<Text key={subCommand.name} color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{' '}
|
{' '}
|
||||||
{subCommand.name}
|
{subCommand.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -87,8 +87,8 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{' '}
|
{' '}
|
||||||
!{' '}
|
!{' '}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -98,75 +98,75 @@ export const Help: React.FC<Help> = ({ commands }) => (
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{/* Shortcuts */}
|
{/* Shortcuts */}
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={Colors.Foreground}>
|
||||||
Keyboard Shortcuts:
|
Keyboard Shortcuts:
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Alt+Left/Right
|
Alt+Left/Right
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Jump through words in the input
|
- Jump through words in the input
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Ctrl+C
|
Ctrl+C
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Quit application
|
- Quit application
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{process.platform === 'win32' ? 'Ctrl+Enter' : 'Ctrl+J'}
|
{process.platform === 'win32' ? 'Ctrl+Enter' : 'Ctrl+J'}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
{process.platform === 'linux'
|
{process.platform === 'linux'
|
||||||
? '- New line (Alt+Enter works for certain linux distros)'
|
? '- New line (Alt+Enter works for certain linux distros)'
|
||||||
: '- New line'}
|
: '- New line'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Ctrl+L
|
Ctrl+L
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Clear the screen
|
- Clear the screen
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'}
|
{process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Open input in external editor
|
- Open input in external editor
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Ctrl+Y
|
Ctrl+Y
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Toggle YOLO mode
|
- Toggle YOLO mode
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Enter
|
Enter
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Send message
|
- Send message
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Esc
|
Esc
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Cancel operation
|
- Cancel operation
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Shift+Tab
|
Shift+Tab
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Toggle auto-accepting edits
|
- Toggle auto-accepting edits
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Up/Down
|
Up/Down
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
- Cycle through your prompt history
|
- Cycle through your prompt history
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
For a full list of shortcuts, see{' '}
|
For a full list of shortcuts, see{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
docs/keyboard-shortcuts.md
|
docs/keyboard-shortcuts.md
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -559,7 +559,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||||
<Box flexGrow={1} flexDirection="column">
|
<Box flexGrow={1} flexDirection="column">
|
||||||
{buffer.text.length === 0 && placeholder ? (
|
{buffer.text.length === 0 && placeholder ? (
|
||||||
focus ? (
|
focus ? (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{chalk.inverse(placeholder.slice(0, 1))}
|
{chalk.inverse(placeholder.slice(0, 1))}
|
||||||
<Text color={theme.text.secondary}>{placeholder.slice(1)}</Text>
|
<Text color={theme.text.secondary}>{placeholder.slice(1)}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -600,12 +600,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text key={`line-${visualIdxInRenderedSet}`}>{display}</Text>
|
||||||
key={`line-${visualIdxInRenderedSet}`}
|
|
||||||
color={theme.text.primary}
|
|
||||||
>
|
|
||||||
{display}
|
|
||||||
</Text>
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { ThoughtSummary } from '@google/gemini-cli-core';
|
import { ThoughtSummary } from '@google/gemini-cli-core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||||
import { StreamingState } from '../types.js';
|
import { StreamingState } from '../types.js';
|
||||||
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
||||||
|
@ -61,9 +61,11 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
{primaryText && <Text color={theme.text.accent}>{primaryText}</Text>}
|
{primaryText && (
|
||||||
|
<Text color={Colors.AccentPurple}>{primaryText}</Text>
|
||||||
|
)}
|
||||||
{!isNarrow && cancelAndTimerContent && (
|
{!isNarrow && cancelAndTimerContent && (
|
||||||
<Text color={theme.text.secondary}> {cancelAndTimerContent}</Text>
|
<Text color={Colors.Gray}> {cancelAndTimerContent}</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
|
{!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
|
||||||
|
@ -71,7 +73,7 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
||||||
</Box>
|
</Box>
|
||||||
{isNarrow && cancelAndTimerContent && (
|
{isNarrow && cancelAndTimerContent && (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.secondary}>{cancelAndTimerContent}</Text>
|
<Text color={Colors.Gray}>{cancelAndTimerContent}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{isNarrow && rightContent && <Box>{rightContent}</Box>}
|
{isNarrow && rightContent && <Box>{rightContent}</Box>}
|
||||||
|
|
|
@ -6,24 +6,20 @@
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { formatMemoryUsage } from '../utils/formatters.js';
|
import { formatMemoryUsage } from '../utils/formatters.js';
|
||||||
|
|
||||||
export const MemoryUsageDisplay: React.FC = () => {
|
export const MemoryUsageDisplay: React.FC = () => {
|
||||||
const [memoryUsage, setMemoryUsage] = useState<string>('');
|
const [memoryUsage, setMemoryUsage] = useState<string>('');
|
||||||
const [memoryUsageColor, setMemoryUsageColor] = useState<string>(
|
const [memoryUsageColor, setMemoryUsageColor] = useState<string>(Colors.Gray);
|
||||||
theme.text.secondary,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateMemory = () => {
|
const updateMemory = () => {
|
||||||
const usage = process.memoryUsage().rss;
|
const usage = process.memoryUsage().rss;
|
||||||
setMemoryUsage(formatMemoryUsage(usage));
|
setMemoryUsage(formatMemoryUsage(usage));
|
||||||
setMemoryUsageColor(
|
setMemoryUsageColor(
|
||||||
usage >= 2 * 1024 * 1024 * 1024
|
usage >= 2 * 1024 * 1024 * 1024 ? Colors.AccentRed : Colors.Gray,
|
||||||
? theme.status.error
|
|
||||||
: theme.text.secondary,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const intervalId = setInterval(updateMemory, 2000);
|
const intervalId = setInterval(updateMemory, 2000);
|
||||||
|
@ -33,7 +29,7 @@ export const MemoryUsageDisplay: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.secondary}>| </Text>
|
<Text color={Colors.Gray}>| </Text>
|
||||||
<Text color={memoryUsageColor}>{memoryUsage}</Text>
|
<Text color={memoryUsageColor}>{memoryUsage}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
import { formatDuration } from '../utils/formatters.js';
|
||||||
import {
|
import {
|
||||||
calculateAverageLatency,
|
calculateAverageLatency,
|
||||||
|
@ -33,13 +33,13 @@ const StatRow: React.FC<StatRowProps> = ({
|
||||||
}) => (
|
}) => (
|
||||||
<Box>
|
<Box>
|
||||||
<Box width={METRIC_COL_WIDTH}>
|
<Box width={METRIC_COL_WIDTH}>
|
||||||
<Text bold color={isSection ? theme.text.primary : theme.text.link}>
|
<Text bold={isSection} color={isSection ? undefined : Colors.LightBlue}>
|
||||||
{isSubtle ? ` ↳ ${title}` : title}
|
{isSubtle ? ` ↳ ${title}` : title}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{values.map((value, index) => (
|
{values.map((value, index) => (
|
||||||
<Box width={MODEL_COL_WIDTH} key={index}>
|
<Box width={MODEL_COL_WIDTH} key={index}>
|
||||||
<Text color={theme.text.primary}>{value}</Text>
|
<Text>{value}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -56,13 +56,11 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}>
|
<Text>No API calls have been made in this session.</Text>
|
||||||
No API calls have been made in this session.
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,12 +82,12 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Model Stats For Nerds
|
Model Stats For Nerds
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
@ -97,15 +95,11 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Box>
|
<Box>
|
||||||
<Box width={METRIC_COL_WIDTH}>
|
<Box width={METRIC_COL_WIDTH}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Metric</Text>
|
||||||
Metric
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
{modelNames.map((name) => (
|
{modelNames.map((name) => (
|
||||||
<Box width={MODEL_COL_WIDTH} key={name}>
|
<Box width={MODEL_COL_WIDTH} key={name}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>{name}</Text>
|
||||||
{name}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -132,7 +126,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
m.api.totalErrors > 0 ? theme.status.error : theme.text.primary
|
m.api.totalErrors > 0 ? Colors.AccentRed : Colors.Foreground
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{m.api.totalErrors.toLocaleString()} ({errorRate.toFixed(1)}%)
|
{m.api.totalErrors.toLocaleString()} ({errorRate.toFixed(1)}%)
|
||||||
|
@ -155,7 +149,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Total"
|
title="Total"
|
||||||
values={getModelValues((m) => (
|
values={getModelValues((m) => (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
{m.tokens.total.toLocaleString()}
|
{m.tokens.total.toLocaleString()}
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
|
@ -172,7 +166,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
||||||
values={getModelValues((m) => {
|
values={getModelValues((m) => {
|
||||||
const cacheHitRate = calculateCacheHitRate(m);
|
const cacheHitRate = calculateCacheHitRate(m);
|
||||||
return (
|
return (
|
||||||
<Text color={theme.status.success}>
|
<Text color={Colors.AccentGreen}>
|
||||||
{m.tokens.cached.toLocaleString()} ({cacheHitRate.toFixed(1)}%)
|
{m.tokens.cached.toLocaleString()} ({cacheHitRate.toFixed(1)}%)
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'ink';
|
import { Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
interface PrepareLabelProps {
|
interface PrepareLabelProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -21,7 +21,7 @@ export const PrepareLabel: React.FC<PrepareLabelProps> = ({
|
||||||
matchedIndex,
|
matchedIndex,
|
||||||
userInput,
|
userInput,
|
||||||
textColor,
|
textColor,
|
||||||
highlightColor = theme.status.warning,
|
highlightColor = Colors.AccentYellow,
|
||||||
}) => {
|
}) => {
|
||||||
if (
|
if (
|
||||||
matchedIndex === undefined ||
|
matchedIndex === undefined ||
|
||||||
|
@ -37,7 +37,7 @@ export const PrepareLabel: React.FC<PrepareLabelProps> = ({
|
||||||
const end = label.slice(matchedIndex + userInput.length);
|
const end = label.slice(matchedIndex + userInput.length);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={textColor}>{start}</Text>
|
<Text color={textColor}>{start}</Text>
|
||||||
<Text color="black" bold backgroundColor={highlightColor}>
|
<Text color="black" bold backgroundColor={highlightColor}>
|
||||||
{match}
|
{match}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
LoadedSettings,
|
LoadedSettings,
|
||||||
SettingScope,
|
SettingScope,
|
||||||
|
@ -366,18 +366,18 @@ export function SettingsDialog({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
>
|
>
|
||||||
<Box flexDirection="column" flexGrow={1}>
|
<Box flexDirection="column" flexGrow={1}>
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.AccentBlue}>
|
||||||
Settings
|
Settings
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
{showScrollUp && <Text color={theme.text.secondary}>▲</Text>}
|
{showScrollUp && <Text color={Colors.Gray}>▲</Text>}
|
||||||
{visibleItems.map((item, idx) => {
|
{visibleItems.map((item, idx) => {
|
||||||
const isActive =
|
const isActive =
|
||||||
focusSection === 'settings' &&
|
focusSection === 'settings' &&
|
||||||
|
@ -405,21 +405,17 @@ export function SettingsDialog({
|
||||||
<React.Fragment key={item.value}>
|
<React.Fragment key={item.value}>
|
||||||
<Box flexDirection="row" alignItems="center">
|
<Box flexDirection="row" alignItems="center">
|
||||||
<Box minWidth={2} flexShrink={0}>
|
<Box minWidth={2} flexShrink={0}>
|
||||||
<Text
|
<Text color={isActive ? Colors.AccentGreen : Colors.Gray}>
|
||||||
color={
|
|
||||||
isActive ? theme.status.success : theme.text.secondary
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isActive ? '●' : ''}
|
{isActive ? '●' : ''}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box minWidth={50}>
|
<Box minWidth={50}>
|
||||||
<Text
|
<Text
|
||||||
color={isActive ? theme.status.success : theme.text.primary}
|
color={isActive ? Colors.AccentGreen : Colors.Foreground}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
{scopeMessage && (
|
{scopeMessage && (
|
||||||
<Text color={theme.text.secondary}> {scopeMessage}</Text>
|
<Text color={Colors.Gray}> {scopeMessage}</Text>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -427,10 +423,10 @@ export function SettingsDialog({
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
isActive
|
isActive
|
||||||
? theme.status.success
|
? Colors.AccentGreen
|
||||||
: shouldBeGreyedOut
|
: shouldBeGreyedOut
|
||||||
? theme.text.secondary
|
? Colors.Gray
|
||||||
: theme.text.primary
|
: Colors.Foreground
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{displayValue}
|
{displayValue}
|
||||||
|
@ -440,16 +436,12 @@ export function SettingsDialog({
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{showScrollDown && <Text color={theme.text.secondary}>▼</Text>}
|
{showScrollDown && <Text color={Colors.Gray}>▼</Text>}
|
||||||
|
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
<Box marginTop={1} flexDirection="column">
|
||||||
<Text
|
<Text bold={focusSection === 'scope'} wrap="truncate">
|
||||||
bold={focusSection === 'scope'}
|
|
||||||
wrap="truncate"
|
|
||||||
color={theme.text.primary}
|
|
||||||
>
|
|
||||||
{focusSection === 'scope' ? '> ' : ' '}Apply To
|
{focusSection === 'scope' ? '> ' : ' '}Apply To
|
||||||
</Text>
|
</Text>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
|
@ -463,11 +455,11 @@ export function SettingsDialog({
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
(Use Enter to select, Tab to change focus)
|
(Use Enter to select, Tab to change focus)
|
||||||
</Text>
|
</Text>
|
||||||
{showRestartPrompt && (
|
{showRestartPrompt && (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
To see changes, Gemini CLI must be restarted. Press r to exit and
|
To see changes, Gemini CLI must be restarted. Press r to exit and
|
||||||
apply changes now.
|
apply changes now.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { ToolConfirmationOutcome } from '@google/gemini-cli-core';
|
import { ToolConfirmationOutcome } from '@google/gemini-cli-core';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
RadioButtonSelect,
|
RadioButtonSelect,
|
||||||
RadioSelectItem,
|
RadioSelectItem,
|
||||||
|
@ -69,27 +69,23 @@ export const ShellConfirmationDialog: React.FC<
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.status.warning}
|
borderColor={Colors.AccentYellow}
|
||||||
padding={1}
|
padding={1}
|
||||||
width="100%"
|
width="100%"
|
||||||
marginLeft={1}
|
marginLeft={1}
|
||||||
>
|
>
|
||||||
<Box flexDirection="column" marginBottom={1}>
|
<Box flexDirection="column" marginBottom={1}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Shell Command Execution</Text>
|
||||||
Shell Command Execution
|
<Text>A custom command wants to run the following shell commands:</Text>
|
||||||
</Text>
|
|
||||||
<Text color={theme.text.primary}>
|
|
||||||
A custom command wants to run the following shell commands:
|
|
||||||
</Text>
|
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
marginTop={1}
|
marginTop={1}
|
||||||
>
|
>
|
||||||
{commands.map((cmd) => (
|
{commands.map((cmd) => (
|
||||||
<Text key={cmd} color={theme.text.accent}>
|
<Text key={cmd} color={Colors.AccentCyan}>
|
||||||
{cmd}
|
{cmd}
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
|
@ -97,7 +93,7 @@ export const ShellConfirmationDialog: React.FC<
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text color={theme.text.primary}>Do you want to proceed?</Text>
|
<Text>Do you want to proceed?</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<RadioButtonSelect items={options} onSelect={handleSelect} isFocused />
|
<RadioButtonSelect items={options} onSelect={handleSelect} isFocused />
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
export const ShellModeIndicator: React.FC = () => (
|
export const ShellModeIndicator: React.FC = () => (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
shell mode enabled
|
shell mode enabled
|
||||||
<Text color={theme.text.secondary}> (esc to disable)</Text>
|
<Text color={Colors.Gray}> (esc to disable)</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { Box, Text } from 'ink';
|
||||||
import { useOverflowState } from '../contexts/OverflowContext.js';
|
import { useOverflowState } from '../contexts/OverflowContext.js';
|
||||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||||
import { StreamingState } from '../types.js';
|
import { StreamingState } from '../types.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
interface ShowMoreLinesProps {
|
interface ShowMoreLinesProps {
|
||||||
constrainHeight: boolean;
|
constrainHeight: boolean;
|
||||||
|
@ -32,7 +32,7 @@ export const ShowMoreLines = ({ constrainHeight }: ShowMoreLinesProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.secondary} wrap="truncate">
|
<Text color={Colors.Gray} wrap="truncate">
|
||||||
Press ctrl-s to show more lines
|
Press ctrl-s to show more lines
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -43,7 +43,7 @@ describe('<StatsDisplay />', () => {
|
||||||
const zeroMetrics: SessionMetrics = {
|
const zeroMetrics: SessionMetrics = {
|
||||||
models: {},
|
models: {},
|
||||||
tools: {
|
tools: {
|
||||||
totalCalls: 1,
|
totalCalls: 0,
|
||||||
totalSuccess: 0,
|
totalSuccess: 0,
|
||||||
totalFail: 0,
|
totalFail: 0,
|
||||||
totalDurationMs: 0,
|
totalDurationMs: 0,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import Gradient from 'ink-gradient';
|
import Gradient from 'ink-gradient';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
import { formatDuration } from '../utils/formatters.js';
|
||||||
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
|
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
|
||||||
import {
|
import {
|
||||||
|
@ -29,7 +29,7 @@ const StatRow: React.FC<StatRowProps> = ({ title, children }) => (
|
||||||
<Box>
|
<Box>
|
||||||
{/* Fixed width for the label creates a clean "gutter" for alignment */}
|
{/* Fixed width for the label creates a clean "gutter" for alignment */}
|
||||||
<Box width={28}>
|
<Box width={28}>
|
||||||
<Text color={theme.text.link}>{title}</Text>
|
<Text color={Colors.LightBlue}>{title}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -45,7 +45,7 @@ const SubStatRow: React.FC<SubStatRowProps> = ({ title, children }) => (
|
||||||
<Box paddingLeft={2}>
|
<Box paddingLeft={2}>
|
||||||
{/* Adjust width for the "» " prefix */}
|
{/* Adjust width for the "» " prefix */}
|
||||||
<Box width={26}>
|
<Box width={26}>
|
||||||
<Text color={theme.text.secondary}>» {title}</Text>
|
<Text>» {title}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -59,9 +59,7 @@ interface SectionProps {
|
||||||
|
|
||||||
const Section: React.FC<SectionProps> = ({ title, children }) => (
|
const Section: React.FC<SectionProps> = ({ title, children }) => (
|
||||||
<Box flexDirection="column" width="100%" marginBottom={1}>
|
<Box flexDirection="column" width="100%" marginBottom={1}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>{title}</Text>
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -81,24 +79,16 @@ const ModelUsageTable: React.FC<{
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Box>
|
<Box>
|
||||||
<Box width={nameWidth}>
|
<Box width={nameWidth}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Model Usage</Text>
|
||||||
Model Usage
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={requestsWidth} justifyContent="flex-end">
|
<Box width={requestsWidth} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Reqs</Text>
|
||||||
Reqs
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={inputTokensWidth} justifyContent="flex-end">
|
<Box width={inputTokensWidth} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Input Tokens</Text>
|
||||||
Input Tokens
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={outputTokensWidth} justifyContent="flex-end">
|
<Box width={outputTokensWidth} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Output Tokens</Text>
|
||||||
Output Tokens
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{/* Divider */}
|
{/* Divider */}
|
||||||
|
@ -108,7 +98,6 @@ const ModelUsageTable: React.FC<{
|
||||||
borderTop={false}
|
borderTop={false}
|
||||||
borderLeft={false}
|
borderLeft={false}
|
||||||
borderRight={false}
|
borderRight={false}
|
||||||
borderColor={theme.border.default}
|
|
||||||
width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth}
|
width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth}
|
||||||
></Box>
|
></Box>
|
||||||
|
|
||||||
|
@ -116,20 +105,18 @@ const ModelUsageTable: React.FC<{
|
||||||
{Object.entries(models).map(([name, modelMetrics]) => (
|
{Object.entries(models).map(([name, modelMetrics]) => (
|
||||||
<Box key={name}>
|
<Box key={name}>
|
||||||
<Box width={nameWidth}>
|
<Box width={nameWidth}>
|
||||||
<Text color={theme.text.primary}>{name.replace('-001', '')}</Text>
|
<Text>{name.replace('-001', '')}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={requestsWidth} justifyContent="flex-end">
|
<Box width={requestsWidth} justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>
|
<Text>{modelMetrics.api.totalRequests}</Text>
|
||||||
{modelMetrics.api.totalRequests}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={inputTokensWidth} justifyContent="flex-end">
|
<Box width={inputTokensWidth} justifyContent="flex-end">
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
{modelMetrics.tokens.prompt.toLocaleString()}
|
{modelMetrics.tokens.prompt.toLocaleString()}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={outputTokensWidth} justifyContent="flex-end">
|
<Box width={outputTokensWidth} justifyContent="flex-end">
|
||||||
<Text color={theme.status.warning}>
|
<Text color={Colors.AccentYellow}>
|
||||||
{modelMetrics.tokens.candidates.toLocaleString()}
|
{modelMetrics.tokens.candidates.toLocaleString()}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -137,13 +124,13 @@ const ModelUsageTable: React.FC<{
|
||||||
))}
|
))}
|
||||||
{cacheEfficiency > 0 && (
|
{cacheEfficiency > 0 && (
|
||||||
<Box flexDirection="column" marginTop={1}>
|
<Box flexDirection="column" marginTop={1}>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.status.success}>Savings Highlight:</Text>{' '}
|
<Text color={Colors.AccentGreen}>Savings Highlight:</Text>{' '}
|
||||||
{totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)}
|
{totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)}
|
||||||
%) of input tokens were served from the cache, reducing costs.
|
%) of input tokens were served from the cache, reducing costs.
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
» Tip: For a full token breakdown, run `/stats model`.
|
» Tip: For a full token breakdown, run `/stats model`.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -182,18 +169,18 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||||
|
|
||||||
const renderTitle = () => {
|
const renderTitle = () => {
|
||||||
if (title) {
|
if (title) {
|
||||||
return theme.ui.gradient && theme.ui.gradient.length > 0 ? (
|
return Colors.GradientColors && Colors.GradientColors.length > 0 ? (
|
||||||
<Gradient colors={theme.ui.gradient}>
|
<Gradient colors={Colors.GradientColors}>
|
||||||
<Text bold>{title}</Text>
|
<Text bold>{title}</Text>
|
||||||
</Gradient>
|
</Gradient>
|
||||||
) : (
|
) : (
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Session Stats
|
Session Stats
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -202,7 +189,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
|
@ -210,55 +197,51 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||||
{renderTitle()}
|
{renderTitle()}
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{tools.totalCalls > 0 && (
|
<Section title="Interaction Summary">
|
||||||
<Section title="Interaction Summary">
|
<StatRow title="Session ID:">
|
||||||
<StatRow title="Session ID:">
|
<Text>{stats.sessionId}</Text>
|
||||||
<Text>{stats.sessionId}</Text>
|
</StatRow>
|
||||||
</StatRow>
|
<StatRow title="Tool Calls:">
|
||||||
<StatRow title="Tool Calls:">
|
<Text>
|
||||||
<Text color={theme.text.primary}>
|
{tools.totalCalls} ({' '}
|
||||||
{tools.totalCalls} ({' '}
|
<Text color={Colors.AccentGreen}>✔ {tools.totalSuccess}</Text>{' '}
|
||||||
<Text color={theme.status.success}>✔ {tools.totalSuccess}</Text>{' '}
|
<Text color={Colors.AccentRed}>✖ {tools.totalFail}</Text> )
|
||||||
<Text color={theme.status.error}>✖ {tools.totalFail}</Text> )
|
</Text>
|
||||||
|
</StatRow>
|
||||||
|
<StatRow title="Success Rate:">
|
||||||
|
<Text color={successColor}>{computed.successRate.toFixed(1)}%</Text>
|
||||||
|
</StatRow>
|
||||||
|
{computed.totalDecisions > 0 && (
|
||||||
|
<StatRow title="User Agreement:">
|
||||||
|
<Text color={agreementColor}>
|
||||||
|
{computed.agreementRate.toFixed(1)}%{' '}
|
||||||
|
<Text color={Colors.Gray}>
|
||||||
|
({computed.totalDecisions} reviewed)
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</StatRow>
|
</StatRow>
|
||||||
<StatRow title="Success Rate:">
|
)}
|
||||||
<Text color={successColor}>{computed.successRate.toFixed(1)}%</Text>
|
</Section>
|
||||||
</StatRow>
|
|
||||||
{computed.totalDecisions > 0 && (
|
|
||||||
<StatRow title="User Agreement:">
|
|
||||||
<Text color={agreementColor}>
|
|
||||||
{computed.agreementRate.toFixed(1)}%{' '}
|
|
||||||
<Text color={theme.text.secondary}>
|
|
||||||
({computed.totalDecisions} reviewed)
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</StatRow>
|
|
||||||
)}
|
|
||||||
</Section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Section title="Performance">
|
<Section title="Performance">
|
||||||
<StatRow title="Wall Time:">
|
<StatRow title="Wall Time:">
|
||||||
<Text color={theme.text.primary}>{duration}</Text>
|
<Text>{duration}</Text>
|
||||||
</StatRow>
|
</StatRow>
|
||||||
<StatRow title="Agent Active:">
|
<StatRow title="Agent Active:">
|
||||||
<Text color={theme.text.primary}>
|
<Text>{formatDuration(computed.agentActiveTime)}</Text>
|
||||||
{formatDuration(computed.agentActiveTime)}
|
|
||||||
</Text>
|
|
||||||
</StatRow>
|
</StatRow>
|
||||||
<SubStatRow title="API Time:">
|
<SubStatRow title="API Time:">
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{formatDuration(computed.totalApiTime)}{' '}
|
{formatDuration(computed.totalApiTime)}{' '}
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
({computed.apiTimePercent.toFixed(1)}%)
|
({computed.apiTimePercent.toFixed(1)}%)
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</SubStatRow>
|
</SubStatRow>
|
||||||
<SubStatRow title="Tool Time:">
|
<SubStatRow title="Tool Time:">
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{formatDuration(computed.totalToolTime)}{' '}
|
{formatDuration(computed.totalToolTime)}{' '}
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>
|
||||||
({computed.toolTimePercent.toFixed(1)}%)
|
({computed.toolTimePercent.toFixed(1)}%)
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { PrepareLabel } from './PrepareLabel.js';
|
import { PrepareLabel } from './PrepareLabel.js';
|
||||||
export interface Suggestion {
|
export interface Suggestion {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -35,7 +35,7 @@ export function SuggestionsDisplay({
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Box paddingX={1} width={width}>
|
<Box paddingX={1} width={width}>
|
||||||
<Text color={theme.text.secondary}>Loading suggestions...</Text>
|
<Text color="gray">Loading suggestions...</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,12 +54,12 @@ export function SuggestionsDisplay({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" paddingX={1} width={width}>
|
<Box flexDirection="column" paddingX={1} width={width}>
|
||||||
{scrollOffset > 0 && <Text color={theme.text.primary}>▲</Text>}
|
{scrollOffset > 0 && <Text color={Colors.Foreground}>▲</Text>}
|
||||||
|
|
||||||
{visibleSuggestions.map((suggestion, index) => {
|
{visibleSuggestions.map((suggestion, index) => {
|
||||||
const originalIndex = startIndex + index;
|
const originalIndex = startIndex + index;
|
||||||
const isActive = originalIndex === activeIndex;
|
const isActive = originalIndex === activeIndex;
|
||||||
const textColor = isActive ? theme.text.accent : theme.text.secondary;
|
const textColor = isActive ? Colors.AccentPurple : Colors.Gray;
|
||||||
const labelElement = (
|
const labelElement = (
|
||||||
<PrepareLabel
|
<PrepareLabel
|
||||||
label={suggestion.label}
|
label={suggestion.label}
|
||||||
|
@ -91,11 +91,9 @@ export function SuggestionsDisplay({
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{endIndex < suggestions.length && (
|
{endIndex < suggestions.length && <Text color="gray">▼</Text>}
|
||||||
<Text color={theme.text.secondary}>▼</Text>
|
|
||||||
)}
|
|
||||||
{suggestions.length > MAX_SUGGESTIONS_TO_SHOW && (
|
{suggestions.length > MAX_SUGGESTIONS_TO_SHOW && (
|
||||||
<Text color={theme.text.secondary}>
|
<Text color="gray">
|
||||||
({activeIndex + 1}/{suggestions.length})
|
({activeIndex + 1}/{suggestions.length})
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
|
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
|
||||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||||
import { DiffRenderer } from './messages/DiffRenderer.js';
|
import { DiffRenderer } from './messages/DiffRenderer.js';
|
||||||
|
@ -207,7 +207,7 @@ export function ThemeDialog({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
paddingTop={includePadding ? 1 : 0}
|
paddingTop={includePadding ? 1 : 0}
|
||||||
paddingBottom={includePadding ? 1 : 0}
|
paddingBottom={includePadding ? 1 : 0}
|
||||||
|
@ -218,19 +218,9 @@ export function ThemeDialog({
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
{/* Left Column: Selection */}
|
{/* Left Column: Selection */}
|
||||||
<Box flexDirection="column" width="45%" paddingRight={2}>
|
<Box flexDirection="column" width="45%" paddingRight={2}>
|
||||||
<Text
|
<Text bold={currentFocusedSection === 'theme'} wrap="truncate">
|
||||||
bold={currentFocusedSection === 'theme'}
|
|
||||||
wrap="truncate"
|
|
||||||
color={
|
|
||||||
currentFocusedSection === 'theme'
|
|
||||||
? theme.text.primary
|
|
||||||
: theme.text.secondary
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{currentFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
|
{currentFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
|
||||||
{otherScopeModifiedMessage}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
key={selectInputKey}
|
key={selectInputKey}
|
||||||
|
@ -247,15 +237,7 @@ export function ThemeDialog({
|
||||||
{/* Scope Selection */}
|
{/* Scope Selection */}
|
||||||
{showScopeSelection && (
|
{showScopeSelection && (
|
||||||
<Box marginTop={1} flexDirection="column">
|
<Box marginTop={1} flexDirection="column">
|
||||||
<Text
|
<Text bold={currentFocusedSection === 'scope'} wrap="truncate">
|
||||||
bold={currentFocusedSection === 'scope'}
|
|
||||||
wrap="truncate"
|
|
||||||
color={
|
|
||||||
currentFocusedSection === 'scope'
|
|
||||||
? theme.text.primary
|
|
||||||
: theme.text.secondary
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{currentFocusedSection === 'scope' ? '> ' : ' '}Apply To
|
{currentFocusedSection === 'scope' ? '> ' : ' '}Apply To
|
||||||
</Text>
|
</Text>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
|
@ -272,9 +254,7 @@ export function ThemeDialog({
|
||||||
|
|
||||||
{/* Right Column: Preview */}
|
{/* Right Column: Preview */}
|
||||||
<Box flexDirection="column" width="55%" paddingLeft={2}>
|
<Box flexDirection="column" width="55%" paddingLeft={2}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Preview</Text>
|
||||||
Preview
|
|
||||||
</Text>
|
|
||||||
{/* Get the Theme object for the highlighted theme, fall back to default if not found */}
|
{/* Get the Theme object for the highlighted theme, fall back to default if not found */}
|
||||||
{(() => {
|
{(() => {
|
||||||
const previewTheme =
|
const previewTheme =
|
||||||
|
@ -284,7 +264,7 @@ export function ThemeDialog({
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="single"
|
borderStyle="single"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
paddingTop={includePadding ? 1 : 0}
|
paddingTop={includePadding ? 1 : 0}
|
||||||
paddingBottom={includePadding ? 1 : 0}
|
paddingBottom={includePadding ? 1 : 0}
|
||||||
paddingLeft={1}
|
paddingLeft={1}
|
||||||
|
@ -301,7 +281,6 @@ def fibonacci(n):
|
||||||
'python',
|
'python',
|
||||||
codeBlockHeight,
|
codeBlockHeight,
|
||||||
colorizeCodeWidth,
|
colorizeCodeWidth,
|
||||||
previewTheme,
|
|
||||||
)}
|
)}
|
||||||
<Box marginTop={1} />
|
<Box marginTop={1} />
|
||||||
<DiffRenderer
|
<DiffRenderer
|
||||||
|
@ -321,7 +300,7 @@ def fibonacci(n):
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.secondary} wrap="truncate">
|
<Text color={Colors.Gray} wrap="truncate">
|
||||||
(Use Enter to select
|
(Use Enter to select
|
||||||
{showScopeSelection ? ', Tab to change focus' : ''})
|
{showScopeSelection ? ', Tab to change focus' : ''})
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { type Config } from '@google/gemini-cli-core';
|
import { type Config } from '@google/gemini-cli-core';
|
||||||
|
|
||||||
interface TipsProps {
|
interface TipsProps {
|
||||||
|
@ -17,25 +17,25 @@ export const Tips: React.FC<TipsProps> = ({ config }) => {
|
||||||
const geminiMdFileCount = config.getGeminiMdFileCount();
|
const geminiMdFileCount = config.getGeminiMdFileCount();
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
<Text color={theme.text.primary}>Tips for getting started:</Text>
|
<Text color={Colors.Foreground}>Tips for getting started:</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
1. Ask questions, edit files, or run commands.
|
1. Ask questions, edit files, or run commands.
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
2. Be specific for the best results.
|
2. Be specific for the best results.
|
||||||
</Text>
|
</Text>
|
||||||
{geminiMdFileCount === 0 && (
|
{geminiMdFileCount === 0 && (
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
3. Create{' '}
|
3. Create{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
GEMINI.md
|
GEMINI.md
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
files to customize your interactions with Gemini.
|
files to customize your interactions with Gemini.
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Text color={theme.text.primary}>
|
<Text color={Colors.Foreground}>
|
||||||
{geminiMdFileCount === 0 ? '4.' : '3.'}{' '}
|
{geminiMdFileCount === 0 ? '4.' : '3.'}{' '}
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
/help
|
/help
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
for more information.
|
for more information.
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
import { formatDuration } from '../utils/formatters.js';
|
||||||
import {
|
import {
|
||||||
getStatusColor,
|
getStatusColor,
|
||||||
|
@ -37,16 +37,16 @@ const StatRow: React.FC<{
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box width={TOOL_NAME_COL_WIDTH}>
|
<Box width={TOOL_NAME_COL_WIDTH}>
|
||||||
<Text color={theme.text.link}>{name}</Text>
|
<Text color={Colors.LightBlue}>{name}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>{stats.count}</Text>
|
<Text>{stats.count}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={successColor}>{successRate.toFixed(1)}%</Text>
|
<Text color={successColor}>{successRate.toFixed(1)}%</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>{formatDuration(avgDuration)}</Text>
|
<Text>{formatDuration(avgDuration)}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -63,13 +63,11 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}>
|
<Text>No tool calls have been made in this session.</Text>
|
||||||
No tool calls have been made in this session.
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -96,13 +94,13 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
width={70}
|
width={70}
|
||||||
>
|
>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Tool Stats For Nerds
|
Tool Stats For Nerds
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
@ -110,24 +108,16 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Box>
|
<Box>
|
||||||
<Box width={TOOL_NAME_COL_WIDTH}>
|
<Box width={TOOL_NAME_COL_WIDTH}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Tool Name</Text>
|
||||||
Tool Name
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Calls</Text>
|
||||||
Calls
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Success Rate</Text>
|
||||||
Success Rate
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>Avg Duration</Text>
|
||||||
Avg Duration
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -149,47 +139,45 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{/* User Decision Summary */}
|
{/* User Decision Summary */}
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>User Decision Summary</Text>
|
||||||
User Decision Summary
|
|
||||||
</Text>
|
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.link}>Total Reviewed Suggestions:</Text>
|
<Text color={Colors.LightBlue}>Total Reviewed Suggestions:</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>{totalReviewed}</Text>
|
<Text>{totalReviewed}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Accepted:</Text>
|
<Text> » Accepted:</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.success}>{totalDecisions.accept}</Text>
|
<Text color={Colors.AccentGreen}>{totalDecisions.accept}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Rejected:</Text>
|
<Text> » Rejected:</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.error}>{totalDecisions.reject}</Text>
|
<Text color={Colors.AccentRed}>{totalDecisions.reject}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Modified:</Text>
|
<Text> » Modified:</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.warning}>{totalDecisions.modify}</Text>
|
<Text color={Colors.AccentYellow}>{totalDecisions.modify}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -207,13 +195,10 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> Overall Agreement Rate:</Text>
|
<Text> Overall Agreement Rate:</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text
|
<Text bold color={totalReviewed > 0 ? agreementColor : undefined}>
|
||||||
bold
|
|
||||||
color={totalReviewed > 0 ? agreementColor : theme.text.primary}
|
|
||||||
>
|
|
||||||
{totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : '--'}
|
{totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : '--'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
interface UpdateNotificationProps {
|
interface UpdateNotificationProps {
|
||||||
message: string;
|
message: string;
|
||||||
|
@ -14,10 +14,10 @@ interface UpdateNotificationProps {
|
||||||
export const UpdateNotification = ({ message }: UpdateNotificationProps) => (
|
export const UpdateNotification = ({ message }: UpdateNotificationProps) => (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.status.warning}
|
borderColor={Colors.AccentYellow}
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
marginY={1}
|
marginY={1}
|
||||||
>
|
>
|
||||||
<Text color={theme.status.warning}>{message}</Text>
|
<Text color={Colors.AccentYellow}>{message}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,11 @@ exports[`<SessionSummaryDisplay /> > renders the summary display with a title 1`
|
||||||
│ │
|
│ │
|
||||||
│ Agent powering down. Goodbye! │
|
│ Agent powering down. Goodbye! │
|
||||||
│ │
|
│ │
|
||||||
|
│ Interaction Summary │
|
||||||
|
│ Session ID: │
|
||||||
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
|
│ Success Rate: 0.0% │
|
||||||
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
│ Wall Time: 1h 23m 45s │
|
│ Wall Time: 1h 23m 45s │
|
||||||
│ Agent Active: 50.2s │
|
│ Agent Active: 50.2s │
|
||||||
|
|
|
@ -65,6 +65,11 @@ exports[`<StatsDisplay /> > Conditional Rendering Tests > hides Efficiency secti
|
||||||
│ │
|
│ │
|
||||||
│ Session Stats │
|
│ Session Stats │
|
||||||
│ │
|
│ │
|
||||||
|
│ Interaction Summary │
|
||||||
|
│ Session ID: test-session-id │
|
||||||
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
|
│ Success Rate: 0.0% │
|
||||||
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
│ Wall Time: 1s │
|
│ Wall Time: 1s │
|
||||||
│ Agent Active: 100ms │
|
│ Agent Active: 100ms │
|
||||||
|
@ -104,6 +109,11 @@ exports[`<StatsDisplay /> > Title Rendering > renders the custom title when a ti
|
||||||
│ │
|
│ │
|
||||||
│ Agent powering down. Goodbye! │
|
│ Agent powering down. Goodbye! │
|
||||||
│ │
|
│ │
|
||||||
|
│ Interaction Summary │
|
||||||
|
│ Session ID: test-session-id │
|
||||||
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
|
│ Success Rate: 0.0% │
|
||||||
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
│ Wall Time: 1s │
|
│ Wall Time: 1s │
|
||||||
│ Agent Active: 0s │
|
│ Agent Active: 0s │
|
||||||
|
@ -119,6 +129,11 @@ exports[`<StatsDisplay /> > Title Rendering > renders the default title when no
|
||||||
│ │
|
│ │
|
||||||
│ Session Stats │
|
│ Session Stats │
|
||||||
│ │
|
│ │
|
||||||
|
│ Interaction Summary │
|
||||||
|
│ Session ID: test-session-id │
|
||||||
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
|
│ Success Rate: 0.0% │
|
||||||
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
│ Wall Time: 1s │
|
│ Wall Time: 1s │
|
||||||
│ Agent Active: 0s │
|
│ Agent Active: 0s │
|
||||||
|
@ -134,6 +149,11 @@ exports[`<StatsDisplay /> > renders a table with two models correctly 1`] = `
|
||||||
│ │
|
│ │
|
||||||
│ Session Stats │
|
│ Session Stats │
|
||||||
│ │
|
│ │
|
||||||
|
│ Interaction Summary │
|
||||||
|
│ Session ID: test-session-id │
|
||||||
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
|
│ Success Rate: 0.0% │
|
||||||
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
│ Wall Time: 1s │
|
│ Wall Time: 1s │
|
||||||
│ Agent Active: 19.5s │
|
│ Agent Active: 19.5s │
|
||||||
|
@ -189,7 +209,7 @@ exports[`<StatsDisplay /> > renders only the Performance section in its zero sta
|
||||||
│ │
|
│ │
|
||||||
│ Interaction Summary │
|
│ Interaction Summary │
|
||||||
│ Session ID: test-session-id │
|
│ Session ID: test-session-id │
|
||||||
│ Tool Calls: 1 ( ✔ 0 ✖ 0 ) │
|
│ Tool Calls: 0 ( ✔ 0 ✖ 0 ) │
|
||||||
│ Success Rate: 0.0% │
|
│ Success Rate: 0.0% │
|
||||||
│ │
|
│ │
|
||||||
│ Performance │
|
│ Performance │
|
||||||
|
|
|
@ -8,7 +8,7 @@ import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { CompressionProps } from '../../types.js';
|
import { CompressionProps } from '../../types.js';
|
||||||
import Spinner from 'ink-spinner';
|
import Spinner from 'ink-spinner';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
export interface CompressionDisplayProps {
|
export interface CompressionDisplayProps {
|
||||||
compression: CompressionProps;
|
compression: CompressionProps;
|
||||||
|
@ -32,13 +32,13 @@ export const CompressionMessage: React.FC<CompressionDisplayProps> = ({
|
||||||
{compression.isPending ? (
|
{compression.isPending ? (
|
||||||
<Spinner type="dots" />
|
<Spinner type="dots" />
|
||||||
) : (
|
) : (
|
||||||
<Text color={theme.text.accent}>✦</Text>
|
<Text color={Colors.AccentPurple}>✦</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
compression.isPending ? theme.text.accent : theme.status.success
|
compression.isPending ? Colors.AccentPurple : Colors.AccentGreen
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
|
import { Colors } from '../../colors.js';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
||||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||||
|
import { theme } from '../../semantic-colors.js';
|
||||||
|
|
||||||
interface DiffLine {
|
interface DiffLine {
|
||||||
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
||||||
|
@ -106,20 +108,14 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||||
theme,
|
theme,
|
||||||
}) => {
|
}) => {
|
||||||
if (!diffContent || typeof diffContent !== 'string') {
|
if (!diffContent || typeof diffContent !== 'string') {
|
||||||
return (
|
return <Text color={Colors.AccentYellow}>No diff content.</Text>;
|
||||||
<Text color={theme?.semanticColors.status.warning}>No diff content.</Text>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedLines = parseDiffWithLineNumbers(diffContent);
|
const parsedLines = parseDiffWithLineNumbers(diffContent);
|
||||||
|
|
||||||
if (parsedLines.length === 0) {
|
if (parsedLines.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
|
||||||
borderStyle="round"
|
|
||||||
borderColor={theme?.semanticColors.border.default}
|
|
||||||
padding={1}
|
|
||||||
>
|
|
||||||
<Text dimColor>No changes detected.</Text>
|
<Text dimColor>No changes detected.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -162,7 +158,6 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||||
tabWidth,
|
tabWidth,
|
||||||
availableTerminalHeight,
|
availableTerminalHeight,
|
||||||
terminalWidth,
|
terminalWidth,
|
||||||
theme,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +170,6 @@ const renderDiffContent = (
|
||||||
tabWidth = DEFAULT_TAB_WIDTH,
|
tabWidth = DEFAULT_TAB_WIDTH,
|
||||||
availableTerminalHeight: number | undefined,
|
availableTerminalHeight: number | undefined,
|
||||||
terminalWidth: number,
|
terminalWidth: number,
|
||||||
theme: import('../../themes/theme.js').Theme | undefined,
|
|
||||||
) => {
|
) => {
|
||||||
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
||||||
const normalizedLines = parsedLines.map((line) => ({
|
const normalizedLines = parsedLines.map((line) => ({
|
||||||
|
@ -190,11 +184,7 @@ const renderDiffContent = (
|
||||||
|
|
||||||
if (displayableLines.length === 0) {
|
if (displayableLines.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box borderStyle="round" borderColor={Colors.Gray} padding={1}>
|
||||||
borderStyle="round"
|
|
||||||
borderColor={theme?.semanticColors.border.default}
|
|
||||||
padding={1}
|
|
||||||
>
|
|
||||||
<Text dimColor>No changes detected.</Text>
|
<Text dimColor>No changes detected.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -258,10 +248,7 @@ const renderDiffContent = (
|
||||||
) {
|
) {
|
||||||
acc.push(
|
acc.push(
|
||||||
<Box key={`gap-${index}`}>
|
<Box key={`gap-${index}`}>
|
||||||
<Text
|
<Text wrap="truncate" color={Colors.Gray}>
|
||||||
wrap="truncate"
|
|
||||||
color={theme?.semanticColors.border.default}
|
|
||||||
>
|
|
||||||
{'═'.repeat(terminalWidth)}
|
{'═'.repeat(terminalWidth)}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>,
|
</Box>,
|
||||||
|
@ -302,12 +289,12 @@ const renderDiffContent = (
|
||||||
acc.push(
|
acc.push(
|
||||||
<Box key={lineKey} flexDirection="row">
|
<Box key={lineKey} flexDirection="row">
|
||||||
<Text
|
<Text
|
||||||
color={theme?.semanticColors.text.secondary}
|
color={theme.text.secondary}
|
||||||
backgroundColor={
|
backgroundColor={
|
||||||
line.type === 'add'
|
line.type === 'add'
|
||||||
? theme?.semanticColors.background.diff.added
|
? theme.background.diff.added
|
||||||
: line.type === 'del'
|
: line.type === 'del'
|
||||||
? theme?.semanticColors.background.diff.removed
|
? theme.background.diff.removed
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -315,32 +302,30 @@ const renderDiffContent = (
|
||||||
</Text>
|
</Text>
|
||||||
{line.type === 'context' ? (
|
{line.type === 'context' ? (
|
||||||
<>
|
<>
|
||||||
<Text color={theme?.semanticColors.text.primary}>
|
<Text>{prefixSymbol} </Text>
|
||||||
{prefixSymbol}{' '}
|
|
||||||
</Text>
|
|
||||||
<Text wrap="wrap">
|
<Text wrap="wrap">
|
||||||
{colorizeLine(displayContent, language, theme)}
|
{colorizeLine(displayContent, language)}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Text
|
<Text
|
||||||
backgroundColor={
|
backgroundColor={
|
||||||
line.type === 'add'
|
line.type === 'add'
|
||||||
? theme?.semanticColors.background.diff.added
|
? theme.background.diff.added
|
||||||
: theme?.semanticColors.background.diff.removed
|
: theme.background.diff.removed
|
||||||
}
|
}
|
||||||
wrap="wrap"
|
wrap="wrap"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
line.type === 'add'
|
line.type === 'add'
|
||||||
? theme?.semanticColors.status.success
|
? theme.status.success
|
||||||
: theme?.semanticColors.status.error
|
: theme.status.error
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{prefixSymbol}
|
{prefixSymbol}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
{colorizeLine(displayContent, language, theme)}
|
{colorizeLine(displayContent, language)}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>,
|
</Box>,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
interface ErrorMessageProps {
|
interface ErrorMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -19,10 +19,10 @@ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="row" marginBottom={1}>
|
<Box flexDirection="row" marginBottom={1}>
|
||||||
<Box width={prefixWidth}>
|
<Box width={prefixWidth}>
|
||||||
<Text color={theme.status.error}>{prefix}</Text>
|
<Text color={Colors.AccentRed}>{prefix}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Text wrap="wrap" color={theme.status.error}>
|
<Text wrap="wrap" color={Colors.AccentRed}>
|
||||||
{text}
|
{text}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
interface GeminiMessageProps {
|
interface GeminiMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -28,7 +28,7 @@ export const GeminiMessage: React.FC<GeminiMessageProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="row">
|
<Box flexDirection="row">
|
||||||
<Box width={prefixWidth}>
|
<Box width={prefixWidth}>
|
||||||
<Text color={theme.text.accent}>{prefix}</Text>
|
<Text color={Colors.AccentPurple}>{prefix}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} flexDirection="column">
|
<Box flexGrow={1} flexDirection="column">
|
||||||
<MarkdownDisplay
|
<MarkdownDisplay
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
interface InfoMessageProps {
|
interface InfoMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -19,10 +19,10 @@ export const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="row" marginTop={1}>
|
<Box flexDirection="row" marginTop={1}>
|
||||||
<Box width={prefixWidth}>
|
<Box width={prefixWidth}>
|
||||||
<Text color={theme.status.warning}>{prefix}</Text>
|
<Text color={Colors.AccentYellow}>{prefix}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Text wrap="wrap" color={theme.status.warning}>
|
<Text wrap="wrap" color={Colors.AccentYellow}>
|
||||||
{text}
|
{text}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { DiffRenderer } from './DiffRenderer.js';
|
import { DiffRenderer } from './DiffRenderer.js';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import {
|
import {
|
||||||
ToolCallConfirmationDetails,
|
ToolCallConfirmationDetails,
|
||||||
ToolConfirmationOutcome,
|
ToolConfirmationOutcome,
|
||||||
|
@ -112,13 +112,13 @@ export const ToolConfirmationMessage: React.FC<
|
||||||
<Box
|
<Box
|
||||||
minWidth="90%"
|
minWidth="90%"
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={Colors.Gray}
|
||||||
justifyContent="space-around"
|
justifyContent="space-around"
|
||||||
padding={1}
|
padding={1}
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}>Modify in progress: </Text>
|
<Text>Modify in progress: </Text>
|
||||||
<Text color={theme.status.success}>
|
<Text color={Colors.AccentGreen}>
|
||||||
Save and close external editor to continue
|
Save and close external editor to continue
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -192,7 +192,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||||
maxWidth={Math.max(childWidth - 4, 1)}
|
maxWidth={Math.max(childWidth - 4, 1)}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.accent}>{executionProps.command}</Text>
|
<Text color={Colors.AccentCyan}>{executionProps.command}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</MaxSizedBox>
|
</MaxSizedBox>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -222,15 +222,12 @@ export const ToolConfirmationMessage: React.FC<
|
||||||
|
|
||||||
bodyContent = (
|
bodyContent = (
|
||||||
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
||||||
<Text color={theme.text.accent}>{infoProps.prompt}</Text>
|
<Text color={Colors.AccentCyan}>{infoProps.prompt}</Text>
|
||||||
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
|
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
|
||||||
<Box flexDirection="column" marginTop={1}>
|
<Box flexDirection="column" marginTop={1}>
|
||||||
<Text color={theme.text.primary}>URLs to fetch:</Text>
|
<Text>URLs to fetch:</Text>
|
||||||
{infoProps.urls.map((url) => (
|
{infoProps.urls.map((url) => (
|
||||||
<Text key={url} color={theme.text.primary}>
|
<Text key={url}> - {url}</Text>
|
||||||
{' '}
|
|
||||||
- {url}
|
|
||||||
</Text>
|
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -242,8 +239,8 @@ export const ToolConfirmationMessage: React.FC<
|
||||||
|
|
||||||
bodyContent = (
|
bodyContent = (
|
||||||
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
<Box flexDirection="column" paddingX={1} marginLeft={1}>
|
||||||
<Text color={theme.text.accent}>MCP Server: {mcpProps.serverName}</Text>
|
<Text color={Colors.AccentCyan}>MCP Server: {mcpProps.serverName}</Text>
|
||||||
<Text color={theme.text.accent}>Tool: {mcpProps.toolName}</Text>
|
<Text color={Colors.AccentCyan}>Tool: {mcpProps.toolName}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -278,9 +275,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||||
|
|
||||||
{/* Confirmation Question */}
|
{/* Confirmation Question */}
|
||||||
<Box marginBottom={1} flexShrink={0}>
|
<Box marginBottom={1} flexShrink={0}>
|
||||||
<Text wrap="truncate" color={theme.text.primary}>
|
<Text wrap="truncate">{question}</Text>
|
||||||
{question}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Select Input for Options */}
|
{/* Select Input for Options */}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Box } from 'ink';
|
||||||
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
|
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
|
||||||
import { ToolMessage } from './ToolMessage.js';
|
import { ToolMessage } from './ToolMessage.js';
|
||||||
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
|
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import { Config } from '@google/gemini-cli-core';
|
import { Config } from '@google/gemini-cli-core';
|
||||||
import { SHELL_COMMAND_NAME } from '../../constants.js';
|
import { SHELL_COMMAND_NAME } from '../../constants.js';
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
||||||
);
|
);
|
||||||
const isShellCommand = toolCalls.some((t) => t.name === SHELL_COMMAND_NAME);
|
const isShellCommand = toolCalls.some((t) => t.name === SHELL_COMMAND_NAME);
|
||||||
const borderColor =
|
const borderColor =
|
||||||
hasPending || isShellCommand ? theme.status.warning : theme.border.default;
|
hasPending || isShellCommand ? Colors.AccentYellow : Colors.Gray;
|
||||||
|
|
||||||
const staticHeight = /* border */ 2 + /* marginBottom */ 1;
|
const staticHeight = /* border */ 2 + /* marginBottom */ 1;
|
||||||
// This is a bit of a magic number, but it accounts for the border and
|
// This is a bit of a magic number, but it accounts for the border and
|
||||||
|
|
|
@ -8,7 +8,7 @@ import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
|
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
|
||||||
import { DiffRenderer } from './DiffRenderer.js';
|
import { DiffRenderer } from './DiffRenderer.js';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||||
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
|
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
|
||||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||||
|
@ -90,9 +90,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
||||||
{typeof resultDisplay === 'string' && !renderOutputAsMarkdown && (
|
{typeof resultDisplay === 'string' && !renderOutputAsMarkdown && (
|
||||||
<MaxSizedBox maxHeight={availableHeight} maxWidth={childWidth}>
|
<MaxSizedBox maxHeight={availableHeight} maxWidth={childWidth}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text wrap="wrap" color={theme.text.primary}>
|
<Text wrap="wrap">{resultDisplay}</Text>
|
||||||
{resultDisplay}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</MaxSizedBox>
|
</MaxSizedBox>
|
||||||
)}
|
)}
|
||||||
|
@ -120,7 +118,7 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
|
||||||
}) => (
|
}) => (
|
||||||
<Box minWidth={STATUS_INDICATOR_WIDTH}>
|
<Box minWidth={STATUS_INDICATOR_WIDTH}>
|
||||||
{status === ToolCallStatus.Pending && (
|
{status === ToolCallStatus.Pending && (
|
||||||
<Text color={theme.status.success}>o</Text>
|
<Text color={Colors.AccentGreen}>o</Text>
|
||||||
)}
|
)}
|
||||||
{status === ToolCallStatus.Executing && (
|
{status === ToolCallStatus.Executing && (
|
||||||
<GeminiRespondingSpinner
|
<GeminiRespondingSpinner
|
||||||
|
@ -129,18 +127,18 @@ const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{status === ToolCallStatus.Success && (
|
{status === ToolCallStatus.Success && (
|
||||||
<Text color={theme.status.success}>✔</Text>
|
<Text color={Colors.AccentGreen}>✔</Text>
|
||||||
)}
|
)}
|
||||||
{status === ToolCallStatus.Confirming && (
|
{status === ToolCallStatus.Confirming && (
|
||||||
<Text color={theme.status.warning}>?</Text>
|
<Text color={Colors.AccentYellow}>?</Text>
|
||||||
)}
|
)}
|
||||||
{status === ToolCallStatus.Canceled && (
|
{status === ToolCallStatus.Canceled && (
|
||||||
<Text color={theme.status.warning} bold>
|
<Text color={Colors.AccentYellow} bold>
|
||||||
-
|
-
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{status === ToolCallStatus.Error && (
|
{status === ToolCallStatus.Error && (
|
||||||
<Text color={theme.status.error} bold>
|
<Text color={Colors.AccentRed} bold>
|
||||||
x
|
x
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
@ -162,11 +160,11 @@ const ToolInfo: React.FC<ToolInfo> = ({
|
||||||
const nameColor = React.useMemo<string>(() => {
|
const nameColor = React.useMemo<string>(() => {
|
||||||
switch (emphasis) {
|
switch (emphasis) {
|
||||||
case 'high':
|
case 'high':
|
||||||
return theme.text.primary;
|
return Colors.Foreground;
|
||||||
case 'medium':
|
case 'medium':
|
||||||
return theme.text.primary;
|
return Colors.Foreground;
|
||||||
case 'low':
|
case 'low':
|
||||||
return theme.text.secondary;
|
return Colors.Gray;
|
||||||
default: {
|
default: {
|
||||||
const exhaustiveCheck: never = emphasis;
|
const exhaustiveCheck: never = emphasis;
|
||||||
return exhaustiveCheck;
|
return exhaustiveCheck;
|
||||||
|
@ -178,19 +176,18 @@ const ToolInfo: React.FC<ToolInfo> = ({
|
||||||
<Text
|
<Text
|
||||||
wrap="truncate-end"
|
wrap="truncate-end"
|
||||||
strikethrough={status === ToolCallStatus.Canceled}
|
strikethrough={status === ToolCallStatus.Canceled}
|
||||||
color={theme.text.primary}
|
|
||||||
>
|
>
|
||||||
<Text color={nameColor} bold>
|
<Text color={nameColor} bold>
|
||||||
{name}
|
{name}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
<Text color={theme.text.secondary}>{description}</Text>
|
<Text color={Colors.Gray}>{description}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TrailingIndicator: React.FC = () => (
|
const TrailingIndicator: React.FC = () => (
|
||||||
<Text color={theme.text.primary} wrap="truncate">
|
<Text color={Colors.Foreground} wrap="truncate">
|
||||||
{' '}
|
{' '}
|
||||||
←
|
←
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
interface UserMessageProps {
|
interface UserMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -17,8 +17,8 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
|
||||||
const prefixWidth = prefix.length;
|
const prefixWidth = prefix.length;
|
||||||
const isSlashCommand = text.startsWith('/');
|
const isSlashCommand = text.startsWith('/');
|
||||||
|
|
||||||
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
|
const textColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
|
||||||
const borderColor = isSlashCommand ? theme.text.accent : theme.border.default;
|
const borderColor = isSlashCommand ? Colors.AccentPurple : Colors.Gray;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
interface UserShellMessageProps {
|
interface UserShellMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -18,8 +18,8 @@ export const UserShellMessage: React.FC<UserShellMessageProps> = ({ text }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text color={theme.text.accent}>$ </Text>
|
<Text color={Colors.AccentCyan}>$ </Text>
|
||||||
<Text color={theme.text.primary}>{commandToDisplay}</Text>
|
<Text>{commandToDisplay}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React, { Fragment, useEffect, useId } from 'react';
|
import React, { Fragment, useEffect, useId } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import stringWidth from 'string-width';
|
import stringWidth from 'string-width';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import { toCodePoints } from '../../utils/textUtils.js';
|
import { toCodePoints } from '../../utils/textUtils.js';
|
||||||
import { useOverflowActions } from '../../contexts/OverflowContext.js';
|
import { useOverflowActions } from '../../contexts/OverflowContext.js';
|
||||||
|
|
||||||
|
@ -173,7 +173,6 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
|
||||||
<Box key={index}>
|
<Box key={index}>
|
||||||
{line.length > 0 ? (
|
{line.length > 0 ? (
|
||||||
line.map((segment, segIndex) => (
|
line.map((segment, segIndex) => (
|
||||||
// Avoid adding color styles to this <Text> element, breaks code colorization
|
|
||||||
<Text key={segIndex} {...segment.props}>
|
<Text key={segIndex} {...segment.props}>
|
||||||
{segment.text}
|
{segment.text}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -187,14 +186,14 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" width={maxWidth} flexShrink={0}>
|
<Box flexDirection="column" width={maxWidth} flexShrink={0}>
|
||||||
{totalHiddenLines > 0 && overflowDirection === 'top' && (
|
{totalHiddenLines > 0 && overflowDirection === 'top' && (
|
||||||
<Text color={theme.text.secondary} wrap="truncate">
|
<Text color={Colors.Gray} wrap="truncate">
|
||||||
... first {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
... first {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
||||||
hidden ...
|
hidden ...
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{visibleLines}
|
{visibleLines}
|
||||||
{totalHiddenLines > 0 && overflowDirection === 'bottom' && (
|
{totalHiddenLines > 0 && overflowDirection === 'bottom' && (
|
||||||
<Text color={theme.text.secondary} wrap="truncate">
|
<Text color={Colors.Gray} wrap="truncate">
|
||||||
... last {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
... last {totalHiddenLines} line{totalHiddenLines === 1 ? '' : 's'}{' '}
|
||||||
hidden ...
|
hidden ...
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import { useKeypress } from '../../hooks/useKeypress.js';
|
import { useKeypress } from '../../hooks/useKeypress.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,9 +164,7 @@ export function RadioButtonSelect<T>({
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
{showScrollArrows && (
|
{showScrollArrows && (
|
||||||
<Text
|
<Text color={scrollOffset > 0 ? Colors.Foreground : Colors.Gray}>
|
||||||
color={scrollOffset > 0 ? theme.text.primary : theme.text.secondary}
|
|
||||||
>
|
|
||||||
▲
|
▲
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
@ -174,18 +172,18 @@ export function RadioButtonSelect<T>({
|
||||||
const itemIndex = scrollOffset + index;
|
const itemIndex = scrollOffset + index;
|
||||||
const isSelected = activeIndex === itemIndex;
|
const isSelected = activeIndex === itemIndex;
|
||||||
|
|
||||||
let textColor = theme.text.primary;
|
let textColor = Colors.Foreground;
|
||||||
let numberColor = theme.text.primary;
|
let numberColor = Colors.Foreground;
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
textColor = theme.status.success;
|
textColor = Colors.AccentGreen;
|
||||||
numberColor = theme.status.success;
|
numberColor = Colors.AccentGreen;
|
||||||
} else if (item.disabled) {
|
} else if (item.disabled) {
|
||||||
textColor = theme.text.secondary;
|
textColor = Colors.Gray;
|
||||||
numberColor = theme.text.secondary;
|
numberColor = Colors.Gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showNumbers) {
|
if (!showNumbers) {
|
||||||
numberColor = theme.text.secondary;
|
numberColor = Colors.Gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberColumnWidth = String(items.length).length;
|
const numberColumnWidth = String(items.length).length;
|
||||||
|
@ -196,9 +194,7 @@ export function RadioButtonSelect<T>({
|
||||||
return (
|
return (
|
||||||
<Box key={item.label} alignItems="center">
|
<Box key={item.label} alignItems="center">
|
||||||
<Box minWidth={2} flexShrink={0}>
|
<Box minWidth={2} flexShrink={0}>
|
||||||
<Text
|
<Text color={isSelected ? Colors.AccentGreen : Colors.Foreground}>
|
||||||
color={isSelected ? theme.status.success : theme.text.primary}
|
|
||||||
>
|
|
||||||
{isSelected ? '●' : ' '}
|
{isSelected ? '●' : ' '}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -212,9 +208,7 @@ export function RadioButtonSelect<T>({
|
||||||
{item.themeNameDisplay && item.themeTypeDisplay ? (
|
{item.themeNameDisplay && item.themeTypeDisplay ? (
|
||||||
<Text color={textColor} wrap="truncate">
|
<Text color={textColor} wrap="truncate">
|
||||||
{item.themeNameDisplay}{' '}
|
{item.themeNameDisplay}{' '}
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>{item.themeTypeDisplay}</Text>
|
||||||
{item.themeTypeDisplay}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text color={textColor} wrap="truncate">
|
<Text color={textColor} wrap="truncate">
|
||||||
|
@ -228,8 +222,8 @@ export function RadioButtonSelect<T>({
|
||||||
<Text
|
<Text
|
||||||
color={
|
color={
|
||||||
scrollOffset + maxItemsToShow < items.length
|
scrollOffset + maxItemsToShow < items.length
|
||||||
? theme.text.primary
|
? Colors.Foreground
|
||||||
: theme.text.secondary
|
: Colors.Gray
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
▼
|
▼
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
|
||||||
import { usePrivacySettings } from '../hooks/usePrivacySettings.js';
|
import { usePrivacySettings } from '../hooks/usePrivacySettings.js';
|
||||||
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
|
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
|
||||||
import { Config } from '@google/gemini-cli-core';
|
import { Config } from '@google/gemini-cli-core';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
interface CloudFreePrivacyNoticeProps {
|
interface CloudFreePrivacyNoticeProps {
|
||||||
|
@ -34,16 +34,16 @@ export const CloudFreePrivacyNotice = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
if (privacyState.isLoading) {
|
if (privacyState.isLoading) {
|
||||||
return <Text color={theme.text.secondary}>Loading...</Text>;
|
return <Text color={Colors.Gray}>Loading...</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (privacyState.error) {
|
if (privacyState.error) {
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" marginY={1}>
|
<Box flexDirection="column" marginY={1}>
|
||||||
<Text color={theme.status.error}>
|
<Text color={Colors.AccentRed}>
|
||||||
Error loading Opt-in settings: {privacyState.error}
|
Error loading Opt-in settings: {privacyState.error}
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -59,17 +59,17 @@ export const CloudFreePrivacyNotice = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" marginY={1}>
|
<Box flexDirection="column" marginY={1}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Gemini Code Assist for Individuals Privacy Notice
|
Gemini Code Assist for Individuals Privacy Notice
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
This notice and our Privacy Policy
|
This notice and our Privacy Policy
|
||||||
<Text color={theme.text.link}>[1]</Text> describe how Gemini Code Assist
|
<Text color={Colors.AccentBlue}>[1]</Text> describe how Gemini Code
|
||||||
handles your data. Please read them carefully.
|
Assist handles your data. Please read them carefully.
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
When you use Gemini Code Assist for individuals with Gemini CLI, Google
|
When you use Gemini Code Assist for individuals with Gemini CLI, Google
|
||||||
collects your prompts, related code, generated output, code edits,
|
collects your prompts, related code, generated output, code edits,
|
||||||
related feature usage information, and your feedback to provide,
|
related feature usage information, and your feedback to provide,
|
||||||
|
@ -77,7 +77,7 @@ export const CloudFreePrivacyNotice = ({
|
||||||
technologies.
|
technologies.
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
To help with quality and improve our products (such as generative
|
To help with quality and improve our products (such as generative
|
||||||
machine-learning models), human reviewers may read, annotate, and
|
machine-learning models), human reviewers may read, annotate, and
|
||||||
process the data collected above. We take steps to protect your privacy
|
process the data collected above. We take steps to protect your privacy
|
||||||
|
@ -90,7 +90,7 @@ export const CloudFreePrivacyNotice = ({
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
Allow Google to use this data to develop and improve our products?
|
Allow Google to use this data to develop and improve our products?
|
||||||
</Text>
|
</Text>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
|
@ -106,14 +106,12 @@ export const CloudFreePrivacyNotice = ({
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||||
https://policies.google.com/privacy
|
https://policies.google.com/privacy
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>Press Enter to choose an option and exit.</Text>
|
||||||
Press Enter to choose an option and exit.
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Newline, Text } from 'ink';
|
import { Box, Newline, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
interface CloudPaidPrivacyNoticeProps {
|
interface CloudPaidPrivacyNoticeProps {
|
||||||
|
@ -26,14 +26,14 @@ export const CloudPaidPrivacyNotice = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" marginBottom={1}>
|
<Box flexDirection="column" marginBottom={1}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Vertex AI Notice
|
Vertex AI Notice
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
Service Specific Terms<Text color={theme.text.link}>[1]</Text> are
|
Service Specific Terms<Text color={Colors.AccentBlue}>[1]</Text> are
|
||||||
incorporated into the agreement under which Google has agreed to provide
|
incorporated into the agreement under which Google has agreed to provide
|
||||||
Google Cloud Platform<Text color={theme.status.success}>[2]</Text> to
|
Google Cloud Platform<Text color={Colors.AccentGreen}>[2]</Text> to
|
||||||
Customer (the “Agreement”). If the Agreement authorizes the resale or
|
Customer (the “Agreement”). If the Agreement authorizes the resale or
|
||||||
supply of Google Cloud Platform under a Google Cloud partner or reseller
|
supply of Google Cloud Platform under a Google Cloud partner or reseller
|
||||||
program, then except for in the section entitled “Partner-Specific
|
program, then except for in the section entitled “Partner-Specific
|
||||||
|
@ -44,16 +44,16 @@ export const CloudPaidPrivacyNotice = ({
|
||||||
them in the Agreement.
|
them in the Agreement.
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||||
https://cloud.google.com/terms/service-terms
|
https://cloud.google.com/terms/service-terms
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.status.success}>[2]</Text>{' '}
|
<Text color={Colors.AccentGreen}>[2]</Text>{' '}
|
||||||
https://cloud.google.com/terms/services
|
https://cloud.google.com/terms/services
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Newline, Text } from 'ink';
|
import { Box, Newline, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
interface GeminiPrivacyNoticeProps {
|
interface GeminiPrivacyNoticeProps {
|
||||||
|
@ -24,39 +24,39 @@ export const GeminiPrivacyNotice = ({ onExit }: GeminiPrivacyNoticeProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" marginBottom={1}>
|
<Box flexDirection="column" marginBottom={1}>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentPurple}>
|
||||||
Gemini API Key Notice
|
Gemini API Key Notice
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
By using the Gemini API<Text color={theme.text.link}>[1]</Text>, Google
|
By using the Gemini API<Text color={Colors.AccentBlue}>[1]</Text>,
|
||||||
AI Studio
|
Google AI Studio
|
||||||
<Text color={theme.status.error}>[2]</Text>, and the other Google
|
<Text color={Colors.AccentRed}>[2]</Text>, and the other Google
|
||||||
developer services that reference these terms (collectively, the
|
developer services that reference these terms (collectively, the
|
||||||
"APIs" or "Services"), you are agreeing to Google
|
"APIs" or "Services"), you are agreeing to Google
|
||||||
APIs Terms of Service (the "API Terms")
|
APIs Terms of Service (the "API Terms")
|
||||||
<Text color={theme.status.success}>[3]</Text>, and the Gemini API
|
<Text color={Colors.AccentGreen}>[3]</Text>, and the Gemini API
|
||||||
Additional Terms of Service (the "Additional Terms")
|
Additional Terms of Service (the "Additional Terms")
|
||||||
<Text color={theme.text.accent}>[4]</Text>.
|
<Text color={Colors.AccentPurple}>[4]</Text>.
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.text.link}>[1]</Text>{' '}
|
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||||
https://ai.google.dev/docs/gemini_api_overview
|
https://ai.google.dev/docs/gemini_api_overview
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.status.error}>[2]</Text> https://aistudio.google.com/
|
<Text color={Colors.AccentRed}>[2]</Text> https://aistudio.google.com/
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.status.success}>[3]</Text>{' '}
|
<Text color={Colors.AccentGreen}>[3]</Text>{' '}
|
||||||
https://developers.google.com/terms
|
https://developers.google.com/terms
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<Text color={theme.text.accent}>[4]</Text>{' '}
|
<Text color={Colors.AccentPurple}>[4]</Text>{' '}
|
||||||
https://ai.google.dev/gemini-api/terms
|
https://ai.google.dev/gemini-api/terms
|
||||||
</Text>
|
</Text>
|
||||||
<Newline />
|
<Newline />
|
||||||
<Text color={theme.text.secondary}>Press Esc to exit.</Text>
|
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,8 @@ const noColorSemanticColors: SemanticColors = {
|
||||||
focused: '',
|
focused: '',
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: '',
|
||||||
|
symbol: '',
|
||||||
gradient: [],
|
gradient: [],
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
|
|
|
@ -25,6 +25,8 @@ export interface SemanticColors {
|
||||||
focused: string;
|
focused: string;
|
||||||
};
|
};
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: string;
|
||||||
|
symbol: string;
|
||||||
gradient: string[] | undefined;
|
gradient: string[] | undefined;
|
||||||
};
|
};
|
||||||
status: {
|
status: {
|
||||||
|
@ -53,6 +55,8 @@ export const lightSemanticColors: SemanticColors = {
|
||||||
focused: lightTheme.AccentBlue,
|
focused: lightTheme.AccentBlue,
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: lightTheme.Comment,
|
||||||
|
symbol: lightTheme.Gray,
|
||||||
gradient: lightTheme.GradientColors,
|
gradient: lightTheme.GradientColors,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
|
@ -81,6 +85,8 @@ export const darkSemanticColors: SemanticColors = {
|
||||||
focused: darkTheme.AccentBlue,
|
focused: darkTheme.AccentBlue,
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: darkTheme.Comment,
|
||||||
|
symbol: darkTheme.Gray,
|
||||||
gradient: darkTheme.GradientColors,
|
gradient: darkTheme.GradientColors,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
|
@ -109,6 +115,8 @@ export const ansiSemanticColors: SemanticColors = {
|
||||||
focused: ansiTheme.AccentBlue,
|
focused: ansiTheme.AccentBlue,
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: ansiTheme.Comment,
|
||||||
|
symbol: ansiTheme.Gray,
|
||||||
gradient: ansiTheme.GradientColors,
|
gradient: ansiTheme.GradientColors,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
|
|
|
@ -235,7 +235,7 @@ export function createCustomTheme(customTheme: CustomTheme): Theme {
|
||||||
customTheme.background?.diff?.added ?? customTheme.DiffAdded ?? '',
|
customTheme.background?.diff?.added ?? customTheme.DiffAdded ?? '',
|
||||||
DiffRemoved:
|
DiffRemoved:
|
||||||
customTheme.background?.diff?.removed ?? customTheme.DiffRemoved ?? '',
|
customTheme.background?.diff?.removed ?? customTheme.DiffRemoved ?? '',
|
||||||
Comment: customTheme.text?.secondary ?? customTheme.Comment ?? '',
|
Comment: customTheme.ui?.comment ?? customTheme.Comment ?? '',
|
||||||
Gray: customTheme.text?.secondary ?? customTheme.Gray ?? '',
|
Gray: customTheme.text?.secondary ?? customTheme.Gray ?? '',
|
||||||
GradientColors: customTheme.ui?.gradient ?? customTheme.GradientColors,
|
GradientColors: customTheme.ui?.gradient ?? customTheme.GradientColors,
|
||||||
};
|
};
|
||||||
|
@ -397,6 +397,8 @@ export function createCustomTheme(customTheme: CustomTheme): Theme {
|
||||||
focused: colors.AccentBlue,
|
focused: colors.AccentBlue,
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
|
comment: colors.Comment,
|
||||||
|
symbol: colors.Gray,
|
||||||
gradient: colors.GradientColors,
|
gradient: colors.GradientColors,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
|
|
|
@ -21,7 +21,6 @@ import {
|
||||||
MINIMUM_MAX_HEIGHT,
|
MINIMUM_MAX_HEIGHT,
|
||||||
} from '../components/shared/MaxSizedBox.js';
|
} from '../components/shared/MaxSizedBox.js';
|
||||||
import { LoadedSettings } from '../../config/settings.js';
|
import { LoadedSettings } from '../../config/settings.js';
|
||||||
import { theme as semanticTheme } from '../semantic-colors.js';
|
|
||||||
|
|
||||||
// Configure theming and parsing utilities.
|
// Configure theming and parsing utilities.
|
||||||
const lowlight = createLowlight(common);
|
const lowlight = createLowlight(common);
|
||||||
|
@ -172,7 +171,7 @@ export function colorizeCode(
|
||||||
return (
|
return (
|
||||||
<Box key={index}>
|
<Box key={index}>
|
||||||
{showLineNumbers && (
|
{showLineNumbers && (
|
||||||
<Text color={semanticTheme.text.secondary}>
|
<Text color={activeTheme.colors.Gray}>
|
||||||
{`${String(index + 1 + hiddenLinesCount).padStart(
|
{`${String(index + 1 + hiddenLinesCount).padStart(
|
||||||
padWidth,
|
padWidth,
|
||||||
' ',
|
' ',
|
||||||
|
@ -209,7 +208,7 @@ export function colorizeCode(
|
||||||
{`${String(index + 1).padStart(padWidth, ' ')} `}
|
{`${String(index + 1).padStart(padWidth, ' ')} `}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Text color={semanticTheme.text.secondary}>{line}</Text>
|
<Text color={activeTheme.colors.Gray}>{line}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</MaxSizedBox>
|
</MaxSizedBox>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'ink';
|
import { Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import stringWidth from 'string-width';
|
import stringWidth from 'string-width';
|
||||||
|
|
||||||
// Constants for Markdown parsing
|
// Constants for Markdown parsing
|
||||||
|
@ -31,7 +31,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
while ((match = inlineRegex.exec(text)) !== null) {
|
while ((match = inlineRegex.exec(text)) !== null) {
|
||||||
if (match.index > lastIndex) {
|
if (match.index > lastIndex) {
|
||||||
nodes.push(
|
nodes.push(
|
||||||
<Text key={`t-${lastIndex}`} color={theme.text.primary}>
|
<Text key={`t-${lastIndex}`}>
|
||||||
{text.slice(lastIndex, match.index)}
|
{text.slice(lastIndex, match.index)}
|
||||||
</Text>,
|
</Text>,
|
||||||
);
|
);
|
||||||
|
@ -48,7 +48,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
fullMatch.length > BOLD_MARKER_LENGTH * 2
|
fullMatch.length > BOLD_MARKER_LENGTH * 2
|
||||||
) {
|
) {
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} bold color={theme.text.primary}>
|
<Text key={key} bold>
|
||||||
{fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH)}
|
{fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH)}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -60,13 +60,13 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
!/\w/.test(
|
!/\w/.test(
|
||||||
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1),
|
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1),
|
||||||
) &&
|
) &&
|
||||||
!/\S[./]/.test(text.substring(match.index - 2, match.index)) &&
|
!/\S[./\\]/.test(text.substring(match.index - 2, match.index)) &&
|
||||||
!/[./]\S/.test(
|
!/[./\\]\S/.test(
|
||||||
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2),
|
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} italic color={theme.text.primary}>
|
<Text key={key} italic>
|
||||||
{fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH)}
|
{fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH)}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -76,7 +76,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2
|
fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2
|
||||||
) {
|
) {
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} strikethrough color={theme.text.primary}>
|
<Text key={key} strikethrough>
|
||||||
{fullMatch.slice(
|
{fullMatch.slice(
|
||||||
STRIKETHROUGH_MARKER_LENGTH,
|
STRIKETHROUGH_MARKER_LENGTH,
|
||||||
-STRIKETHROUGH_MARKER_LENGTH,
|
-STRIKETHROUGH_MARKER_LENGTH,
|
||||||
|
@ -91,7 +91,7 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
|
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
|
||||||
if (codeMatch && codeMatch[2]) {
|
if (codeMatch && codeMatch[2]) {
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} color={theme.text.accent}>
|
<Text key={key} color={Colors.AccentPurple}>
|
||||||
{codeMatch[2]}
|
{codeMatch[2]}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -106,9 +106,9 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
const linkText = linkMatch[1];
|
const linkText = linkMatch[1];
|
||||||
const url = linkMatch[2];
|
const url = linkMatch[2];
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} color={theme.text.primary}>
|
<Text key={key}>
|
||||||
{linkText}
|
{linkText}
|
||||||
<Text color={theme.text.link}> ({url})</Text>
|
<Text color={Colors.AccentBlue}> ({url})</Text>
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -116,10 +116,10 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
fullMatch.startsWith('<u>') &&
|
fullMatch.startsWith('<u>') &&
|
||||||
fullMatch.endsWith('</u>') &&
|
fullMatch.endsWith('</u>') &&
|
||||||
fullMatch.length >
|
fullMatch.length >
|
||||||
UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1
|
UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1 // -1 because length is compared to combined length of start and end tags
|
||||||
) {
|
) {
|
||||||
renderedNode = (
|
renderedNode = (
|
||||||
<Text key={key} underline color={theme.text.primary}>
|
<Text key={key} underline>
|
||||||
{fullMatch.slice(
|
{fullMatch.slice(
|
||||||
UNDERLINE_TAG_START_LENGTH,
|
UNDERLINE_TAG_START_LENGTH,
|
||||||
-UNDERLINE_TAG_END_LENGTH,
|
-UNDERLINE_TAG_END_LENGTH,
|
||||||
|
@ -132,22 +132,12 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||||
renderedNode = null;
|
renderedNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.push(
|
nodes.push(renderedNode ?? <Text key={key}>{fullMatch}</Text>);
|
||||||
renderedNode ?? (
|
|
||||||
<Text key={key} color={theme.text.primary}>
|
|
||||||
{fullMatch}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
);
|
|
||||||
lastIndex = inlineRegex.lastIndex;
|
lastIndex = inlineRegex.lastIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastIndex < text.length) {
|
if (lastIndex < text.length) {
|
||||||
nodes.push(
|
nodes.push(<Text key={`t-${lastIndex}`}>{text.slice(lastIndex)}</Text>);
|
||||||
<Text key={`t-${lastIndex}`} color={theme.text.primary}>
|
|
||||||
{text.slice(lastIndex)}
|
|
||||||
</Text>,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{nodes.filter((node) => node !== null)}</>;
|
return <>{nodes.filter((node) => node !== null)}</>;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { colorizeCode } from './CodeColorizer.js';
|
import { colorizeCode } from './CodeColorizer.js';
|
||||||
import { TableRenderer } from './TableRenderer.js';
|
import { TableRenderer } from './TableRenderer.js';
|
||||||
import { RenderInline } from './InlineMarkdownRenderer.js';
|
import { RenderInline } from './InlineMarkdownRenderer.js';
|
||||||
|
@ -115,7 +115,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||||
// Not a table, treat as regular text
|
// Not a table, treat as regular text
|
||||||
addContentBlock(
|
addContentBlock(
|
||||||
<Box key={key}>
|
<Box key={key}>
|
||||||
<Text wrap="wrap" color={theme.text.primary}>
|
<Text wrap="wrap">
|
||||||
<RenderInline text={line} />
|
<RenderInline text={line} />
|
||||||
</Text>
|
</Text>
|
||||||
</Box>,
|
</Box>,
|
||||||
|
@ -154,7 +154,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||||
if (line.trim().length > 0) {
|
if (line.trim().length > 0) {
|
||||||
addContentBlock(
|
addContentBlock(
|
||||||
<Box key={key}>
|
<Box key={key}>
|
||||||
<Text wrap="wrap" color={theme.text.primary}>
|
<Text wrap="wrap">
|
||||||
<RenderInline text={line} />
|
<RenderInline text={line} />
|
||||||
</Text>
|
</Text>
|
||||||
</Box>,
|
</Box>,
|
||||||
|
@ -173,35 +173,35 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 1:
|
case 1:
|
||||||
headerNode = (
|
headerNode = (
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentCyan}>
|
||||||
<RenderInline text={headerText} />
|
<RenderInline text={headerText} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
headerNode = (
|
headerNode = (
|
||||||
<Text bold color={theme.text.link}>
|
<Text bold color={Colors.AccentBlue}>
|
||||||
<RenderInline text={headerText} />
|
<RenderInline text={headerText} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
headerNode = (
|
headerNode = (
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold>
|
||||||
<RenderInline text={headerText} />
|
<RenderInline text={headerText} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
headerNode = (
|
headerNode = (
|
||||||
<Text italic color={theme.text.secondary}>
|
<Text italic color={Colors.Gray}>
|
||||||
<RenderInline text={headerText} />
|
<RenderInline text={headerText} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
headerNode = (
|
headerNode = (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
<RenderInline text={headerText} />
|
<RenderInline text={headerText} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -245,7 +245,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||||
} else {
|
} else {
|
||||||
addContentBlock(
|
addContentBlock(
|
||||||
<Box key={key}>
|
<Box key={key}>
|
||||||
<Text wrap="wrap" color={theme.text.primary}>
|
<Text wrap="wrap">
|
||||||
<RenderInline text={line} />
|
<RenderInline text={line} />
|
||||||
</Text>
|
</Text>
|
||||||
</Box>,
|
</Box>,
|
||||||
|
@ -314,9 +314,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||||
// Not enough space to even show the message meaningfully
|
// Not enough space to even show the message meaningfully
|
||||||
return (
|
return (
|
||||||
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING}>
|
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING}>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={Colors.Gray}>... code is being written ...</Text>
|
||||||
... code is being written ...
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -332,7 +330,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column">
|
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column">
|
||||||
{colorizedTruncatedCode}
|
{colorizedTruncatedCode}
|
||||||
<Text color={theme.text.secondary}>... generating more ...</Text>
|
<Text color={Colors.Gray}>... generating more ...</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -385,10 +383,10 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
>
|
>
|
||||||
<Box width={prefixWidth}>
|
<Box width={prefixWidth}>
|
||||||
<Text color={theme.text.accent}>{prefix}</Text>
|
<Text>{prefix}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}>
|
<Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}>
|
||||||
<Text wrap="wrap" color={theme.text.primary}>
|
<Text wrap="wrap">
|
||||||
<RenderInline text={itemText} />
|
<RenderInline text={itemText} />
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box } from 'ink';
|
import { Text, Box } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
|
import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
|
||||||
|
|
||||||
interface TableRendererProps {
|
interface TableRendererProps {
|
||||||
|
@ -87,9 +87,9 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||||
const paddingNeeded = Math.max(0, contentWidth - actualDisplayWidth);
|
const paddingNeeded = Math.max(0, contentWidth - actualDisplayWidth);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
{isHeader ? (
|
{isHeader ? (
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={Colors.AccentCyan}>
|
||||||
<RenderInline text={cellContent} />
|
<RenderInline text={cellContent} />
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
|
@ -112,7 +112,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||||
const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w));
|
const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w));
|
||||||
const border = char.left + borderParts.join(char.middle) + char.right;
|
const border = char.left + borderParts.join(char.middle) + char.right;
|
||||||
|
|
||||||
return <Text color={theme.text.primary}>{border}</Text>;
|
return <Text>{border}</Text>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to render a table row
|
// Helper function to render a table row
|
||||||
|
@ -123,7 +123,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text color={theme.text.primary}>
|
<Text>
|
||||||
│{' '}
|
│{' '}
|
||||||
{renderedCells.map((cell, index) => (
|
{renderedCells.map((cell, index) => (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
CACHE_EFFICIENCY_HIGH,
|
CACHE_EFFICIENCY_HIGH,
|
||||||
CACHE_EFFICIENCY_MEDIUM,
|
CACHE_EFFICIENCY_MEDIUM,
|
||||||
} from './displayUtils.js';
|
} from './displayUtils.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
describe('displayUtils', () => {
|
describe('displayUtils', () => {
|
||||||
describe('getStatusColor', () => {
|
describe('getStatusColor', () => {
|
||||||
|
@ -24,24 +24,24 @@ describe('displayUtils', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should return green for values >= green threshold', () => {
|
it('should return green for values >= green threshold', () => {
|
||||||
expect(getStatusColor(90, thresholds)).toBe(theme.status.success);
|
expect(getStatusColor(90, thresholds)).toBe(Colors.AccentGreen);
|
||||||
expect(getStatusColor(80, thresholds)).toBe(theme.status.success);
|
expect(getStatusColor(80, thresholds)).toBe(Colors.AccentGreen);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return yellow for values < green and >= yellow threshold', () => {
|
it('should return yellow for values < green and >= yellow threshold', () => {
|
||||||
expect(getStatusColor(79, thresholds)).toBe(theme.status.warning);
|
expect(getStatusColor(79, thresholds)).toBe(Colors.AccentYellow);
|
||||||
expect(getStatusColor(50, thresholds)).toBe(theme.status.warning);
|
expect(getStatusColor(50, thresholds)).toBe(Colors.AccentYellow);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return red for values < yellow threshold', () => {
|
it('should return red for values < yellow threshold', () => {
|
||||||
expect(getStatusColor(49, thresholds)).toBe(theme.status.error);
|
expect(getStatusColor(49, thresholds)).toBe(Colors.AccentRed);
|
||||||
expect(getStatusColor(0, thresholds)).toBe(theme.status.error);
|
expect(getStatusColor(0, thresholds)).toBe(Colors.AccentRed);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return defaultColor for values < yellow threshold when provided', () => {
|
it('should return defaultColor for values < yellow threshold when provided', () => {
|
||||||
expect(
|
expect(
|
||||||
getStatusColor(49, thresholds, { defaultColor: theme.text.primary }),
|
getStatusColor(49, thresholds, { defaultColor: Colors.Foreground }),
|
||||||
).toBe(theme.text.primary);
|
).toBe(Colors.Foreground);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { theme } from '../semantic-colors.js';
|
import { Colors } from '../colors.js';
|
||||||
|
|
||||||
// --- Thresholds ---
|
// --- Thresholds ---
|
||||||
export const TOOL_SUCCESS_RATE_HIGH = 95;
|
export const TOOL_SUCCESS_RATE_HIGH = 95;
|
||||||
|
@ -23,10 +23,10 @@ export const getStatusColor = (
|
||||||
options: { defaultColor?: string } = {},
|
options: { defaultColor?: string } = {},
|
||||||
) => {
|
) => {
|
||||||
if (value >= thresholds.green) {
|
if (value >= thresholds.green) {
|
||||||
return theme.status.success;
|
return Colors.AccentGreen;
|
||||||
}
|
}
|
||||||
if (value >= thresholds.yellow) {
|
if (value >= thresholds.yellow) {
|
||||||
return theme.status.warning;
|
return Colors.AccentYellow;
|
||||||
}
|
}
|
||||||
return options.defaultColor || theme.status.error;
|
return options.defaultColor || Colors.AccentRed;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue