summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/components')
-rw-r--r--packages/cli/src/ui/components/SuggestionsDisplay.tsx7
-rw-r--r--packages/cli/src/ui/components/ThemeDialog.tsx86
-rw-r--r--packages/cli/src/ui/components/messages/ToolGroupMessage.tsx2
-rw-r--r--packages/cli/src/ui/components/shared/RadioButtonSelect.tsx101
4 files changed, 123 insertions, 73 deletions
diff --git a/packages/cli/src/ui/components/SuggestionsDisplay.tsx b/packages/cli/src/ui/components/SuggestionsDisplay.tsx
index f0626fa9..ba25f2b6 100644
--- a/packages/cli/src/ui/components/SuggestionsDisplay.tsx
+++ b/packages/cli/src/ui/components/SuggestionsDisplay.tsx
@@ -5,6 +5,7 @@
*/
import { Box, Text } from 'ink';
+import { Colors } from '../colors.js';
export interface Suggestion {
label: string;
value: string;
@@ -48,7 +49,7 @@ export function SuggestionsDisplay({
return (
<Box borderStyle="round" flexDirection="column" paddingX={1} width={width}>
- {scrollOffset > 0 && <Text color="gray">▲</Text>}
+ {scrollOffset > 0 && <Text color={Colors.Foreground}>▲</Text>}
{visibleSuggestions.map((suggestion, index) => {
const originalIndex = startIndex + index;
@@ -56,8 +57,8 @@ export function SuggestionsDisplay({
return (
<Text
key={`${suggestion}-${originalIndex}`}
- color={isActive ? 'black' : 'white'}
- backgroundColor={isActive ? 'blue' : undefined}
+ color={isActive ? Colors.Background : Colors.Foreground}
+ backgroundColor={isActive ? Colors.AccentBlue : undefined}
>
{suggestion.label}
</Text>
diff --git a/packages/cli/src/ui/components/ThemeDialog.tsx b/packages/cli/src/ui/components/ThemeDialog.tsx
index 7e8c5afd..20686040 100644
--- a/packages/cli/src/ui/components/ThemeDialog.tsx
+++ b/packages/cli/src/ui/components/ThemeDialog.tsx
@@ -32,16 +32,22 @@ export function ThemeDialog({
SettingScope.User,
);
- const themeItems = themeManager.getAvailableThemes().map((theme) => ({
- label: theme.active ? `${theme.name} (Active)` : theme.name,
- value: theme.name,
- }));
+ // Generate theme items
+ const themeItems = themeManager.getAvailableThemes().map((theme) => {
+ const typeString = theme.type.charAt(0).toUpperCase() + theme.type.slice(1);
+ return {
+ label: theme.name,
+ value: theme.name,
+ themeNameDisplay: theme.name,
+ themeTypeDisplay: typeString,
+ };
+ });
const [selectInputKey, setSelectInputKey] = useState(Date.now());
+ // Determine which radio button should be initially selected in the theme list
+ // This should reflect the theme *saved* for the selected scope, or the default
const initialThemeIndex = themeItems.findIndex(
- (item) =>
- item.value ===
- (settings.forScope(selectedScope).settings.theme || DEFAULT_THEME.name),
+ (item) => item.value === (settings.merged.theme || DEFAULT_THEME.name),
);
const scopeItems = [
@@ -88,45 +94,49 @@ export function ThemeDialog({
return (
<Box
borderStyle="round"
- borderColor={Colors.AccentCyan}
- flexDirection="column"
+ borderColor={Colors.AccentPurple}
+ flexDirection="row"
padding={1}
- width="50%"
+ width="100%"
>
- <Text bold={focusedSection === 'theme'}>
- {focusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
- <Text color={Colors.SubtleComment}>{otherScopeModifiedMessage}</Text>
- </Text>
-
- <RadioButtonSelect
- key={selectInputKey}
- items={themeItems}
- initialIndex={initialThemeIndex}
- onSelect={handleThemeSelect} // Use the wrapper handler
- onHighlight={onHighlight}
- isFocused={focusedSection === 'theme'}
- />
- {/* Scope Selection */}
- <Box marginTop={1} flexDirection="column">
- <Text bold={focusedSection === 'scope'}>
- {focusedSection === 'scope' ? '> ' : ' '}Apply To
+ {/* Left Column: Selection */}
+ <Box flexDirection="column" width="50%" paddingRight={2}>
+ <Text bold={focusedSection === 'theme'}>
+ {focusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
+ <Text color={Colors.SubtleComment}>{otherScopeModifiedMessage}</Text>
</Text>
<RadioButtonSelect
- items={scopeItems}
- initialIndex={0} // Default to User Settings
- onSelect={handleScopeSelect}
- onHighlight={handleScopeHighlight}
- isFocused={focusedSection === 'scope'}
+ key={selectInputKey}
+ items={themeItems}
+ initialIndex={initialThemeIndex}
+ onSelect={handleThemeSelect}
+ onHighlight={onHighlight}
+ isFocused={focusedSection === 'theme'}
/>
- </Box>
- <Box marginTop={1}>
- <Text color={Colors.SubtleComment}>
- (Use ↑/↓ arrows and Enter to select, Tab to change focus)
- </Text>
+ {/* Scope Selection */}
+ <Box marginTop={1} flexDirection="column">
+ <Text bold={focusedSection === 'scope'}>
+ {focusedSection === 'scope' ? '> ' : ' '}Apply To
+ </Text>
+ <RadioButtonSelect
+ items={scopeItems}
+ initialIndex={0} // Default to User Settings
+ onSelect={handleScopeSelect}
+ onHighlight={handleScopeHighlight}
+ isFocused={focusedSection === 'scope'}
+ />
+ </Box>
+
+ <Box marginTop={1}>
+ <Text color={Colors.SubtleComment}>
+ (Use ↑/↓ arrows and Enter to select, Tab to change focus)
+ </Text>
+ </Box>
</Box>
- <Box marginTop={1} flexDirection="column">
+ {/* Right Column: Preview */}
+ <Box flexDirection="column" width="50%" paddingLeft={3}>
<Text bold>Preview</Text>
<Box
borderStyle="single"
diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
index 401b8ee0..a9a51232 100644
--- a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
+++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
@@ -27,7 +27,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
const hasPending = !toolCalls.every(
(t) => t.status === ToolCallStatus.Success,
);
- const borderColor = hasPending ? Colors.AccentYellow : Colors.AccentCyan;
+ const borderColor = hasPending ? Colors.AccentYellow : Colors.AccentPurple;
return (
<Box
diff --git a/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx b/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx
index 3db8b678..377be3e3 100644
--- a/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx
+++ b/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx
@@ -27,7 +27,12 @@ export interface RadioSelectItem<T> {
*/
export interface RadioButtonSelectProps<T> {
/** An array of items to display as radio options. */
- items: Array<RadioSelectItem<T>>;
+ items: Array<
+ RadioSelectItem<T> & {
+ themeNameDisplay?: string;
+ themeTypeDisplay?: string;
+ }
+ >;
/** The initial index selected */
initialIndex?: number;
@@ -43,33 +48,6 @@ export interface RadioButtonSelectProps<T> {
}
/**
- * Custom indicator component displaying radio button style (◉/○).
- */
-function RadioIndicator({
- isSelected = false,
-}: InkSelectIndicatorProps): React.JSX.Element {
- return (
- <Box marginRight={1}>
- <Text color={isSelected ? Colors.AccentGreen : Colors.Gray}>
- {isSelected ? '◉' : '○'}
- </Text>
- </Box>
- );
-}
-
-/**
- * Custom item component for displaying the label with appropriate color.
- */
-function RadioItem({
- isSelected = false,
- label,
-}: InkSelectItemProps): React.JSX.Element {
- return (
- <Text color={isSelected ? Colors.AccentGreen : Colors.Gray}>{label}</Text>
- );
-}
-
-/**
* A specialized SelectInput component styled to look like radio buttons.
* It uses '◉' for selected and '○' for unselected items.
*
@@ -80,7 +58,7 @@ export function RadioButtonSelect<T>({
initialIndex,
onSelect,
onHighlight,
- isFocused,
+ isFocused, // This prop indicates if the current RadioButtonSelect group is focused
}: RadioButtonSelectProps<T>): React.JSX.Element {
const handleSelect = (item: RadioSelectItem<T>) => {
onSelect(item.value);
@@ -90,11 +68,72 @@ export function RadioButtonSelect<T>({
onHighlight(item.value);
}
};
+
+ /**
+ * Custom indicator component displaying radio button style (◉/○).
+ * Color changes based on whether the item is selected and if its group is focused.
+ */
+ function DynamicRadioIndicator({
+ isSelected = false,
+ }: InkSelectIndicatorProps): React.JSX.Element {
+ let indicatorColor = Colors.Foreground; // Default for not selected
+ if (isSelected) {
+ if (isFocused) {
+ // Group is focused, selected item is AccentGreen
+ indicatorColor = Colors.AccentGreen;
+ } else {
+ // Group is NOT focused, selected item is Foreground
+ indicatorColor = Colors.Foreground;
+ }
+ }
+ return (
+ <Box marginRight={1}>
+ <Text color={indicatorColor}>{isSelected ? '●' : '○'}</Text>
+ </Box>
+ );
+ }
+
+ /**
+ * Custom item component for displaying the label.
+ * Color changes based on whether the item is selected and if its group is focused.
+ * Now also handles displaying theme type with custom color.
+ */
+ function CustomThemeItemComponent(
+ props: InkSelectItemProps,
+ ): React.JSX.Element {
+ const { isSelected = false, label } = props;
+ const itemWithThemeProps = props as typeof props & {
+ themeNameDisplay?: string;
+ themeTypeDisplay?: string;
+ };
+
+ let textColor = Colors.Foreground;
+ if (isSelected) {
+ textColor = isFocused ? Colors.AccentGreen : Colors.Foreground;
+ }
+
+ if (
+ itemWithThemeProps.themeNameDisplay &&
+ itemWithThemeProps.themeTypeDisplay
+ ) {
+ return (
+ <Text color={textColor}>
+ {itemWithThemeProps.themeNameDisplay}{' '}
+ <Text color={Colors.SubtleComment}>
+ {itemWithThemeProps.themeTypeDisplay}
+ </Text>
+ </Text>
+ );
+ }
+
+ return <Text color={textColor}>{label}</Text>;
+ }
+
initialIndex = initialIndex ?? 0;
return (
<SelectInput
- indicatorComponent={RadioIndicator}
- itemComponent={RadioItem}
+ indicatorComponent={DynamicRadioIndicator}
+ itemComponent={CustomThemeItemComponent}
items={items}
initialIndex={initialIndex}
onSelect={handleSelect}