Add a theme preview and update the theme when highlight changes. (#151)
This commit is contained in:
parent
d8c0587346
commit
5790a5d7cf
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue