From f741630572824d4d5f25a0849b2cd78a6b874c47 Mon Sep 17 00:00:00 2001 From: Jacob Richman Date: Mon, 23 Jun 2025 23:43:17 +0000 Subject: [PATCH] Polish Theme Dialog (#1356) --- packages/cli/src/ui/components/Header.tsx | 25 ++- .../cli/src/ui/components/ThemeDialog.tsx | 197 +++++++++++------- .../components/shared/RadioButtonSelect.tsx | 13 +- 3 files changed, 144 insertions(+), 91 deletions(-) diff --git a/packages/cli/src/ui/components/Header.tsx b/packages/cli/src/ui/components/Header.tsx index 4a632142..375faf07 100644 --- a/packages/cli/src/ui/components/Header.tsx +++ b/packages/cli/src/ui/components/Header.tsx @@ -30,17 +30,22 @@ export const Header: React.FC = ({ terminalWidth >= widthOfLongLogo ? longAsciiLogo : shortAsciiLogo; } + const artWidth = getAsciiArtWidth(displayTitle); + return ( - <> - - {Colors.GradientColors ? ( - - {displayTitle} - - ) : ( + + {Colors.GradientColors ? ( + {displayTitle} - )} - - + + ) : ( + {displayTitle} + )} + ); }; diff --git a/packages/cli/src/ui/components/ThemeDialog.tsx b/packages/cli/src/ui/components/ThemeDialog.tsx index 1fa6bee8..ba7ec1bb 100644 --- a/packages/cli/src/ui/components/ThemeDialog.tsx +++ b/packages/cli/src/ui/components/ThemeDialog.tsx @@ -115,94 +115,143 @@ export function ThemeDialog({ 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. - // Includes "Preview" title, borders, padding, and margin between blocks. - const PREVIEW_PANE_FIXED_VERTICAL_SPACE = 7; - const availableTerminalHeightCodeBlock = availableTerminalHeight - ? Math.max( - Math.floor( - (availableTerminalHeight - PREVIEW_PANE_FIXED_VERTICAL_SPACE) / 2, - ), - 2, - ) - : undefined; + // Includes "Preview" title, borders, and margin between blocks. + const PREVIEW_PANE_FIXED_VERTICAL_SPACE = 8; + + // The right column doesn't need to ever be shorter than the left column. + availableTerminalHeight = Math.max( + availableTerminalHeight, + totalLeftHandSideHeight, + ); + const availableTerminalHeightCodeBlock = + 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 ( - {/* Left Column: Selection */} - - - {focusedSection === 'theme' ? '> ' : ' '}Select Theme{' '} - {otherScopeModifiedMessage} - - - - {/* Scope Selection */} - - - {focusedSection === 'scope' ? '> ' : ' '}Apply To + + {/* Left Column: Selection */} + + + {currenFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '} + {otherScopeModifiedMessage} + + {/* Scope Selection */} + {showScopeSelection && ( + + + {currenFocusedSection === 'scope' ? '> ' : ' '}Apply To + + + + )} - - - (Use Enter to select, Tab to change focus) - + {/* Right Column: Preview */} + + Preview + + {colorizeCode( + `# function +-def fibonacci(n): +- a, b = 0, 1 +- for _ in range(n): +- a, b = b, a + b +- return a`, + 'python', + codeBlockHeight, + colorizeCodeWidth, + )} + + + - - {/* Right Column: Preview */} - - Preview - - {colorizeCode( - `# function -def fibonacci(n): - a, b = 0, 1 - for _ in range(n): - a, b = b, a + b - return a`, - 'python', - availableTerminalHeightCodeBlock, - colorizeCodeWidth, - )} - - - + + + (Use Enter to select + {showScopeSelection ? ', Tab to change focus' : ''}) + ); diff --git a/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx b/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx index 71077f1c..fab0615c 100644 --- a/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx +++ b/packages/cli/src/ui/components/shared/RadioButtonSelect.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Text } from 'ink'; +import { Text, Box } from 'ink'; import SelectInput, { type ItemProps as InkSelectItemProps, type IndicatorProps as InkSelectIndicatorProps, @@ -78,12 +78,11 @@ export function RadioButtonSelect({ isSelected = false, }: InkSelectIndicatorProps): React.JSX.Element { return ( - - {isSelected ? '● ' : '○ '} - + + + {isSelected ? '●' : '○'} + + ); }