Polish Theme Dialog (#1356)

This commit is contained in:
Jacob Richman 2025-06-23 23:43:17 +00:00 committed by GitHub
parent 8c6545bf9d
commit f741630572
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 144 additions and 91 deletions

View File

@ -30,9 +30,15 @@ export const Header: React.FC<HeaderProps> = ({
terminalWidth >= widthOfLongLogo ? longAsciiLogo : shortAsciiLogo; terminalWidth >= widthOfLongLogo ? longAsciiLogo : shortAsciiLogo;
} }
const artWidth = getAsciiArtWidth(displayTitle);
return ( return (
<> <Box
<Box marginBottom={1} alignItems="flex-start"> marginBottom={1}
alignItems="flex-start"
width={artWidth}
flexShrink={0}
>
{Colors.GradientColors ? ( {Colors.GradientColors ? (
<Gradient colors={Colors.GradientColors}> <Gradient colors={Colors.GradientColors}>
<Text>{displayTitle}</Text> <Text>{displayTitle}</Text>
@ -41,6 +47,5 @@ export const Header: React.FC<HeaderProps> = ({
<Text>{displayTitle}</Text> <Text>{displayTitle}</Text>
)} )}
</Box> </Box>
</>
); );
}; };

View File

@ -115,29 +115,72 @@ export function ThemeDialog({
1, 1,
); );
const DAILOG_PADDING = 2;
const selectThemeHeight = themeItems.length + 1;
const SCOPE_SELECTION_HEIGHT = 4; // Height for the scope selection section + margin.
const SPACE_BETWEEN_THEME_SELECTION_AND_APPLY_TO = 1;
const TAB_TO_SELECT_HEIGHT = 2;
availableTerminalHeight = availableTerminalHeight ?? Number.MAX_SAFE_INTEGER;
availableTerminalHeight -= 2; // Top and bottom borders.
availableTerminalHeight -= TAB_TO_SELECT_HEIGHT;
let totalLeftHandSideHeight =
DAILOG_PADDING +
selectThemeHeight +
SCOPE_SELECTION_HEIGHT +
SPACE_BETWEEN_THEME_SELECTION_AND_APPLY_TO;
let showScopeSelection = true;
let includePadding = true;
// Remove content from the LHS that can be ommitted if it exceeds the available height.
if (totalLeftHandSideHeight > availableTerminalHeight) {
includePadding = false;
totalLeftHandSideHeight -= DAILOG_PADDING;
}
if (totalLeftHandSideHeight > availableTerminalHeight) {
// First, try hiding the scope selection
totalLeftHandSideHeight -= SCOPE_SELECTION_HEIGHT;
showScopeSelection = false;
}
// Don't focus the scope selection if it is hidden due to height constraints.
const currenFocusedSection = !showScopeSelection ? 'theme' : focusedSection;
// Vertical space taken by elements other than the two code blocks in the preview pane. // Vertical space taken by elements other than the two code blocks in the preview pane.
// Includes "Preview" title, borders, padding, and margin between blocks. // Includes "Preview" title, borders, and margin between blocks.
const PREVIEW_PANE_FIXED_VERTICAL_SPACE = 7; const PREVIEW_PANE_FIXED_VERTICAL_SPACE = 8;
const availableTerminalHeightCodeBlock = availableTerminalHeight
? Math.max( // The right column doesn't need to ever be shorter than the left column.
Math.floor( availableTerminalHeight = Math.max(
(availableTerminalHeight - PREVIEW_PANE_FIXED_VERTICAL_SPACE) / 2, availableTerminalHeight,
), totalLeftHandSideHeight,
2, );
) const availableTerminalHeightCodeBlock =
: undefined; availableTerminalHeight -
PREVIEW_PANE_FIXED_VERTICAL_SPACE -
(includePadding ? 2 : 0) * 2;
// Give slightly more space to the code block as it is 3 lines longer.
const diffHeight = Math.floor(availableTerminalHeightCodeBlock / 2) - 1;
const codeBlockHeight = Math.ceil(availableTerminalHeightCodeBlock / 2) + 1;
return ( return (
<Box <Box
borderStyle="round" borderStyle="round"
borderColor={Colors.Gray} borderColor={Colors.Gray}
flexDirection="row" flexDirection="column"
padding={1} paddingTop={includePadding ? 1 : 0}
paddingBottom={includePadding ? 1 : 0}
paddingLeft={1}
paddingRight={1}
width="100%" width="100%"
> >
<Box flexDirection="row">
{/* Left Column: Selection */} {/* Left Column: Selection */}
<Box flexDirection="column" width="45%" paddingRight={2}> <Box flexDirection="column" width="45%" paddingRight={2}>
<Text bold={focusedSection === 'theme'}> <Text bold={currenFocusedSection === 'theme'} wrap="truncate">
{focusedSection === 'theme' ? '> ' : ' '}Select Theme{' '} {currenFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
<Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text> <Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
@ -146,28 +189,24 @@ export function ThemeDialog({
initialIndex={initialThemeIndex} initialIndex={initialThemeIndex}
onSelect={handleThemeSelect} onSelect={handleThemeSelect}
onHighlight={onHighlight} onHighlight={onHighlight}
isFocused={focusedSection === 'theme'} isFocused={currenFocusedSection === 'theme'}
/> />
{/* Scope Selection */} {/* Scope Selection */}
{showScopeSelection && (
<Box marginTop={1} flexDirection="column"> <Box marginTop={1} flexDirection="column">
<Text bold={focusedSection === 'scope'}> <Text bold={currenFocusedSection === 'scope'} wrap="truncate">
{focusedSection === 'scope' ? '> ' : ' '}Apply To {currenFocusedSection === 'scope' ? '> ' : ' '}Apply To
</Text> </Text>
<RadioButtonSelect <RadioButtonSelect
items={scopeItems} items={scopeItems}
initialIndex={0} // Default to User Settings initialIndex={0} // Default to User Settings
onSelect={handleScopeSelect} onSelect={handleScopeSelect}
onHighlight={handleScopeHighlight} onHighlight={handleScopeHighlight}
isFocused={focusedSection === 'scope'} isFocused={currenFocusedSection === 'scope'}
/> />
</Box> </Box>
)}
<Box marginTop={1}>
<Text color={Colors.Gray}>
(Use Enter to select, Tab to change focus)
</Text>
</Box>
</Box> </Box>
{/* Right Column: Preview */} {/* Right Column: Preview */}
@ -176,34 +215,44 @@ export function ThemeDialog({
<Box <Box
borderStyle="single" borderStyle="single"
borderColor={Colors.Gray} borderColor={Colors.Gray}
padding={1} paddingTop={includePadding ? 1 : 0}
paddingBottom={includePadding ? 1 : 0}
paddingLeft={1}
paddingRight={1}
flexDirection="column" flexDirection="column"
> >
{colorizeCode( {colorizeCode(
`# function `# function
def fibonacci(n): -def fibonacci(n):
a, b = 0, 1 - a, b = 0, 1
for _ in range(n): - for _ in range(n):
a, b = b, a + b - a, b = b, a + b
return a`, - return a`,
'python', 'python',
availableTerminalHeightCodeBlock, codeBlockHeight,
colorizeCodeWidth, colorizeCodeWidth,
)} )}
<Box marginTop={1} /> <Box marginTop={1} />
<DiffRenderer <DiffRenderer
diffContent={`--- a/old_file.txt diffContent={`--- a/old_file.txt
+++ b/new_file.txt -+++ b/new_file.txt
@@ -1,4 +1,5 @@ -@@ -1,4 +1,5 @@
This is a context line. - This is a context line.
-This line was deleted. --This line was deleted.
+This line was added. -+This line was added.
`} -`}
availableTerminalHeight={availableTerminalHeightCodeBlock} availableTerminalHeight={diffHeight}
terminalWidth={colorizeCodeWidth} terminalWidth={colorizeCodeWidth}
/> />
</Box> </Box>
</Box> </Box>
</Box> </Box>
<Box marginTop={1}>
<Text color={Colors.Gray} wrap="truncate">
(Use Enter to select
{showScopeSelection ? ', Tab to change focus' : ''})
</Text>
</Box>
</Box>
); );
} }

View File

@ -5,7 +5,7 @@
*/ */
import React from 'react'; import React from 'react';
import { Text } from 'ink'; import { Text, Box } from 'ink';
import SelectInput, { import SelectInput, {
type ItemProps as InkSelectItemProps, type ItemProps as InkSelectItemProps,
type IndicatorProps as InkSelectIndicatorProps, type IndicatorProps as InkSelectIndicatorProps,
@ -78,12 +78,11 @@ export function RadioButtonSelect<T>({
isSelected = false, isSelected = false,
}: InkSelectIndicatorProps): React.JSX.Element { }: InkSelectIndicatorProps): React.JSX.Element {
return ( return (
<Text <Box minWidth={2} flexShrink={0}>
color={isSelected ? Colors.AccentGreen : Colors.Foreground} <Text color={isSelected ? Colors.AccentGreen : Colors.Foreground}>
wrap="truncate" {isSelected ? '●' : '○'}
>
{isSelected ? '● ' : '○ '}
</Text> </Text>
</Box>
); );
} }