Add a theme preview and update the theme when highlight changes. (#151)

This commit is contained in:
Jacob Richman 2025-04-24 11:36:34 -07:00 committed by GitHub
parent d8c0587346
commit 5790a5d7cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 5 deletions

View File

@ -39,8 +39,12 @@ export const App = ({ config, cliVersion }: AppProps) => {
const { elapsedTime, currentLoadingPhrase } =
useLoadingIndicator(streamingState);
const { isThemeDialogOpen, openThemeDialog, handleThemeSelect } =
useThemeCommand();
const {
isThemeDialogOpen,
openThemeDialog,
handleThemeSelect,
handleThemeHighlight,
} = useThemeCommand();
useStartupWarnings(setStartupWarnings);
useInitializationErrorEffect(initError, history, setHistory);
@ -134,7 +138,10 @@ export const App = ({ config, cliVersion }: AppProps) => {
)}
{isThemeDialogOpen ? (
<ThemeDialog onSelect={handleThemeSelect} />
<ThemeDialog
onSelect={handleThemeSelect}
onHighlight={handleThemeHighlight}
/>
) : (
<>
<Box flexDirection="column">

View File

@ -9,13 +9,21 @@ import { Box, Text } from 'ink';
import { Colors } from '../colors.js';
import { themeManager } from '../themes/theme-manager.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { DiffRenderer } from './messages/DiffRenderer.js';
import { colorizeCode } from '../utils/CodeColorizer.js';
interface ThemeDialogProps {
/** Callback function when a theme is selected */
onSelect: (themeName: string) => void;
/** Callback function when a theme is highlighted */
onHighlight: (themeName: string) => void;
}
export function ThemeDialog({ onSelect }: ThemeDialogProps): React.JSX.Element {
export function ThemeDialog({
onSelect,
onHighlight,
}: ThemeDialogProps): React.JSX.Element {
const themeItems = themeManager.getAvailableThemes().map((theme) => ({
label: theme.active ? `${theme.name} (Active)` : theme.name,
value: theme.name,
@ -38,12 +46,40 @@ export function ThemeDialog({ onSelect }: ThemeDialogProps): React.JSX.Element {
items={themeItems}
initialIndex={initialIndex}
onSelect={onSelect}
onHighlight={onHighlight}
/>
<Box marginTop={1}>
<Text color={Colors.SubtleComment}>
(Use / arrows and Enter to select)
</Text>
</Box>
<Box marginTop={1} flexDirection="column">
<Text bold>Preview</Text>
<Box
borderStyle="single"
borderColor={Colors.SubtleComment}
padding={1}
flexDirection="column"
>
{colorizeCode(
`# Source code
print("Hello, World!")
`,
'python',
)}
<Box marginTop={1} />
<DiffRenderer
diffContent={`--- a/old_file.txt
+++ b/new_file.txt
@@ -1,4 +1,5 @@
This is a context line.
-This line was deleted.
+This line was added.
`}
/>
</Box>
</Box>
</Box>
);
}

View File

@ -34,6 +34,9 @@ export interface RadioButtonSelectProps<T> {
/** Function called when an item is selected. Receives the `value` of the selected item. */
onSelect: (value: T) => void;
/** Function called when an item is highlighted. Receives the `value` of the selected item. */
onHighlight?: (value: T) => void;
}
/**
@ -73,10 +76,16 @@ export function RadioButtonSelect<T>({
items,
initialIndex,
onSelect,
onHighlight,
}: RadioButtonSelectProps<T>): React.JSX.Element {
const handleSelect = (item: RadioSelectItem<T>) => {
onSelect(item.value);
};
const handleHighlight = (item: RadioSelectItem<T>) => {
if (onHighlight) {
onHighlight(item.value);
}
};
initialIndex = initialIndex ?? 0;
return (
<SelectInput
@ -85,6 +94,7 @@ export function RadioButtonSelect<T>({
items={items}
initialIndex={initialIndex}
onSelect={handleSelect}
onHighlight={handleHighlight}
/>
);
}

View File

@ -11,6 +11,7 @@ interface UseThemeCommandReturn {
isThemeDialogOpen: boolean;
openThemeDialog: () => void;
handleThemeSelect: (themeName: string) => void;
handleThemeHighlight: (themeName: string) => void;
}
export const useThemeCommand = (): UseThemeCommandReturn => {
@ -21,12 +22,22 @@ export const useThemeCommand = (): UseThemeCommandReturn => {
setIsThemeDialogOpen(true);
}, []);
const handleThemeSelect = useCallback((themeName: string) => {
function applyTheme(themeName: string) {
try {
themeManager.setActiveTheme(themeName);
setForceRender((v) => v + 1); // Trigger potential re-render
} catch (error) {
console.error(`Error setting theme: ${error}`);
}
}
const handleThemeHighlight = useCallback((themeName: string) => {
applyTheme(themeName);
}, []);
const handleThemeSelect = useCallback((themeName: string) => {
try {
applyTheme(themeName);
} finally {
setIsThemeDialogOpen(false); // Close the dialog
}
@ -36,5 +47,6 @@ export const useThemeCommand = (): UseThemeCommandReturn => {
isThemeDialogOpen,
openThemeDialog,
handleThemeSelect,
handleThemeHighlight,
};
};