feat(settings): Add availableTerminalHeight prop and optimize settings dialog height management (#7697)

Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Arya Gummadi <aryagummadi@google.com>
This commit is contained in:
Ali Al Jufairi
2025-09-18 02:51:46 +09:00
committed by GitHub
parent 79e72a94fe
commit efb57e1cef
5 changed files with 820 additions and 26 deletions

View File

@@ -643,8 +643,10 @@ Logging in with Google... Please restart Gemini CLI to continue.
}, [buffer, terminalWidth, terminalHeight]);
// Compute available terminal height based on controls measurement
const availableTerminalHeight =
terminalHeight - controlsHeight - staticExtraHeight;
const availableTerminalHeight = Math.max(
0,
terminalHeight - controlsHeight - staticExtraHeight - 2,
);
config.setShellExecutionConfig({
terminalWidth: Math.floor(terminalWidth * SHELL_WIDTH_FRACTION),

View File

@@ -136,6 +136,7 @@ export const DialogManager = () => {
settings={settings}
onSelect={() => uiActions.closeSettingsDialog()}
onRestartRequest={() => process.exit(0)}
availableTerminalHeight={terminalHeight - staticExtraHeight}
/>
</Box>
);

View File

@@ -191,6 +191,26 @@ describe('SettingsDialog', () => {
expect(output).toContain('Use Enter to select, Tab to change focus');
});
it('should accept availableTerminalHeight prop without errors', () => {
const settings = createMockSettings();
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog
settings={settings}
onSelect={onSelect}
availableTerminalHeight={20}
/>
</KeypressProvider>,
);
const output = lastFrame();
// Should still render properly with the height prop
expect(output).toContain('Settings');
expect(output).toContain('Use Enter to select');
});
it('should show settings list with default values', () => {
const settings = createMockSettings();
const onSelect = vi.fn();
@@ -1178,4 +1198,335 @@ describe('SettingsDialog', () => {
unmount();
});
});
describe('Snapshot Tests', () => {
/**
* Snapshot tests for SettingsDialog component using ink-testing-library.
* These tests capture the visual output of the component in various states:
*
* - Default rendering with no custom settings
* - Various combinations of boolean settings (enabled/disabled)
* - Mixed boolean and number settings configurations
* - Different focus states (settings vs scope selector)
* - Different scope selections (User, System, Workspace)
* - Accessibility settings enabled
* - File filtering configurations
* - Tools and security settings
* - All settings disabled state
*
* The snapshots help ensure UI consistency and catch unintended visual changes.
*/
it('should render default state correctly', () => {
const settings = createMockSettings();
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render with various boolean settings enabled', () => {
const settings = createMockSettings({
general: {
vimMode: true,
disableAutoUpdate: true,
debugKeystrokeLogging: true,
enablePromptCompletion: true,
},
ui: {
hideWindowTitle: true,
hideTips: true,
showMemoryUsage: true,
showLineNumbers: true,
showCitations: true,
accessibility: {
disableLoadingPhrases: true,
screenReader: true,
},
},
ide: {
enabled: true,
},
context: {
loadMemoryFromIncludeDirectories: true,
fileFiltering: {
respectGitIgnore: true,
respectGeminiIgnore: true,
enableRecursiveFileSearch: true,
disableFuzzySearch: false,
},
},
tools: {
usePty: true,
autoAccept: true,
useRipgrep: true,
},
security: {
folderTrust: {
enabled: true,
},
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render with mixed boolean and number settings', () => {
const settings = createMockSettings({
general: {
vimMode: false,
disableAutoUpdate: true,
},
ui: {
showMemoryUsage: true,
hideWindowTitle: false,
},
tools: {
truncateToolOutputThreshold: 50000,
truncateToolOutputLines: 1000,
},
context: {
discoveryMaxDirs: 500,
},
model: {
maxSessionTurns: 100,
skipNextSpeakerCheck: false,
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render focused on scope selector', () => {
const settings = createMockSettings();
const onSelect = vi.fn();
const { lastFrame, stdin } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
// Switch focus to scope selector with Tab
stdin.write('\t');
expect(lastFrame()).toMatchSnapshot();
});
it('should render with different scope selected (System)', () => {
const settings = createMockSettings(
{}, // userSettings
{
// systemSettings
general: {
vimMode: true,
disableAutoUpdate: false,
},
ui: {
showMemoryUsage: true,
},
},
);
const onSelect = vi.fn();
const { lastFrame, stdin } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
// Switch to scope selector
stdin.write('\t');
// Navigate to System scope
stdin.write('ArrowDown');
stdin.write('\r'); // Enter to select
expect(lastFrame()).toMatchSnapshot();
});
it('should render with different scope selected (Workspace)', () => {
const settings = createMockSettings(
{}, // userSettings
{}, // systemSettings
{
// workspaceSettings
general: {
vimMode: false,
debugKeystrokeLogging: true,
},
tools: {
useRipgrep: true,
usePty: false,
},
},
);
const onSelect = vi.fn();
const { lastFrame, stdin } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
// Switch to scope selector
stdin.write('\t');
// Navigate to Workspace scope (down twice)
stdin.write('ArrowDown');
stdin.write('ArrowDown');
stdin.write('\r'); // Enter to select
expect(lastFrame()).toMatchSnapshot();
});
it('should render with accessibility settings enabled', () => {
const settings = createMockSettings({
ui: {
accessibility: {
disableLoadingPhrases: true,
screenReader: true,
},
showMemoryUsage: true,
showLineNumbers: true,
},
general: {
vimMode: true,
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render with file filtering settings configured', () => {
const settings = createMockSettings({
context: {
fileFiltering: {
respectGitIgnore: false,
respectGeminiIgnore: true,
enableRecursiveFileSearch: false,
disableFuzzySearch: true,
},
loadMemoryFromIncludeDirectories: true,
discoveryMaxDirs: 100,
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render with tools and security settings', () => {
const settings = createMockSettings({
tools: {
usePty: true,
autoAccept: false,
useRipgrep: true,
truncateToolOutputThreshold: 25000,
truncateToolOutputLines: 500,
},
security: {
folderTrust: {
enabled: true,
},
},
model: {
maxSessionTurns: 50,
skipNextSpeakerCheck: true,
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
it('should render with all boolean settings disabled', () => {
const settings = createMockSettings({
general: {
vimMode: false,
disableAutoUpdate: false,
debugKeystrokeLogging: false,
enablePromptCompletion: false,
},
ui: {
hideWindowTitle: false,
hideTips: false,
showMemoryUsage: false,
showLineNumbers: false,
showCitations: false,
accessibility: {
disableLoadingPhrases: false,
screenReader: false,
},
},
ide: {
enabled: false,
},
context: {
loadMemoryFromIncludeDirectories: false,
fileFiltering: {
respectGitIgnore: false,
respectGeminiIgnore: false,
enableRecursiveFileSearch: false,
disableFuzzySearch: false,
},
},
tools: {
usePty: false,
autoAccept: false,
useRipgrep: false,
},
security: {
folderTrust: {
enabled: false,
},
},
});
const onSelect = vi.fn();
const { lastFrame } = render(
<KeypressProvider kittyProtocolEnabled={false}>
<SettingsDialog settings={settings} onSelect={onSelect} />
</KeypressProvider>,
);
expect(lastFrame()).toMatchSnapshot();
});
});
});

View File

@@ -42,6 +42,7 @@ interface SettingsDialogProps {
settings: LoadedSettings;
onSelect: (settingName: string | undefined, scope: SettingScope) => void;
onRestartRequest?: () => void;
availableTerminalHeight?: number;
}
const maxItemsToShow = 8;
@@ -50,6 +51,7 @@ export function SettingsDialog({
settings,
onSelect,
onRestartRequest,
availableTerminalHeight,
}: SettingsDialogProps): React.JSX.Element {
// Get vim mode context to sync vim mode changes
const { vimEnabled, toggleVimEnabled } = useVimMode();
@@ -367,16 +369,97 @@ export function SettingsDialog({
setFocusSection('settings');
};
// Height constraint calculations similar to ThemeDialog
const DIALOG_PADDING = 2;
const SETTINGS_TITLE_HEIGHT = 2; // "Settings" title + spacing
const SCROLL_ARROWS_HEIGHT = 2; // Up and down arrows
const SPACING_HEIGHT = 1; // Space between settings list and scope
const SCOPE_SELECTION_HEIGHT = 4; // Apply To section height
const BOTTOM_HELP_TEXT_HEIGHT = 1; // Help text
const RESTART_PROMPT_HEIGHT = showRestartPrompt ? 1 : 0;
let currentAvailableTerminalHeight =
availableTerminalHeight ?? Number.MAX_SAFE_INTEGER;
currentAvailableTerminalHeight -= 2; // Top and bottom borders
// Start with basic fixed height (without scope selection)
let totalFixedHeight =
DIALOG_PADDING +
SETTINGS_TITLE_HEIGHT +
SCROLL_ARROWS_HEIGHT +
SPACING_HEIGHT +
BOTTOM_HELP_TEXT_HEIGHT +
RESTART_PROMPT_HEIGHT;
// Calculate how much space we have for settings
let availableHeightForSettings = Math.max(
1,
currentAvailableTerminalHeight - totalFixedHeight,
);
// Each setting item takes 2 lines (the setting row + spacing)
let maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
// Decide whether to show scope selection based on remaining space
let showScopeSelection = true;
// If we have limited height, prioritize showing more settings over scope selection
if (availableTerminalHeight && availableTerminalHeight < 25) {
// For very limited height, hide scope selection to show more settings
const totalWithScope = totalFixedHeight + SCOPE_SELECTION_HEIGHT;
const availableWithScope = Math.max(
1,
currentAvailableTerminalHeight - totalWithScope,
);
const maxItemsWithScope = Math.max(1, Math.floor(availableWithScope / 2));
// If hiding scope selection allows us to show significantly more settings, do it
if (maxVisibleItems > maxItemsWithScope + 1) {
showScopeSelection = false;
} else {
// Otherwise include scope selection and recalculate
totalFixedHeight += SCOPE_SELECTION_HEIGHT;
availableHeightForSettings = Math.max(
1,
currentAvailableTerminalHeight - totalFixedHeight,
);
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
}
} else {
// For normal height, include scope selection
totalFixedHeight += SCOPE_SELECTION_HEIGHT;
availableHeightForSettings = Math.max(
1,
currentAvailableTerminalHeight - totalFixedHeight,
);
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
}
// Use the calculated maxVisibleItems or fall back to the original maxItemsToShow
const effectiveMaxItemsToShow = availableTerminalHeight
? Math.min(maxVisibleItems, items.length)
: maxItemsToShow;
// Ensure focus stays on settings when scope selection is hidden
React.useEffect(() => {
if (!showScopeSelection && focusSection === 'scope') {
setFocusSection('settings');
}
}, [showScopeSelection, focusSection]);
// Scroll logic for settings
const visibleItems = items.slice(scrollOffset, scrollOffset + maxItemsToShow);
// Always show arrows for consistent UI and to indicate circular navigation
const showScrollUp = true;
const showScrollDown = true;
const visibleItems = items.slice(
scrollOffset,
scrollOffset + effectiveMaxItemsToShow,
);
// Show arrows if there are more items than can be displayed
const showScrollUp = items.length > effectiveMaxItemsToShow;
const showScrollDown = items.length > effectiveMaxItemsToShow;
useKeypress(
(key) => {
const { name, ctrl } = key;
if (name === 'tab') {
if (name === 'tab' && showScopeSelection) {
setFocusSection((prev) => (prev === 'settings' ? 'scope' : 'settings'));
}
if (focusSection === 'settings') {
@@ -480,7 +563,9 @@ export function SettingsDialog({
setActiveSettingIndex(newIndex);
// Adjust scroll offset for wrap-around
if (newIndex === items.length - 1) {
setScrollOffset(Math.max(0, items.length - maxItemsToShow));
setScrollOffset(
Math.max(0, items.length - effectiveMaxItemsToShow),
);
} else if (newIndex < scrollOffset) {
setScrollOffset(newIndex);
}
@@ -495,8 +580,8 @@ export function SettingsDialog({
// Adjust scroll offset for wrap-around
if (newIndex === 0) {
setScrollOffset(0);
} else if (newIndex >= scrollOffset + maxItemsToShow) {
setScrollOffset(newIndex - maxItemsToShow + 1);
} else if (newIndex >= scrollOffset + effectiveMaxItemsToShow) {
setScrollOffset(newIndex - effectiveMaxItemsToShow + 1);
}
} else if (name === 'return' || name === 'space') {
const currentItem = items[activeSettingIndex];
@@ -663,8 +748,8 @@ export function SettingsDialog({
height="100%"
>
<Box flexDirection="column" flexGrow={1}>
<Text bold color={theme.text.link}>
Settings
<Text bold={focusSection === 'settings'} wrap="truncate">
{focusSection === 'settings' ? '> ' : ' '}Settings
</Text>
<Box height={1} />
{showScrollUp && <Text color={theme.text.secondary}></Text>}
@@ -787,23 +872,27 @@ export function SettingsDialog({
<Box height={1} />
<Box marginTop={1} flexDirection="column">
<Text bold={focusSection === 'scope'} wrap="truncate">
{focusSection === 'scope' ? '> ' : ' '}Apply To
</Text>
<RadioButtonSelect
items={scopeItems}
initialIndex={0}
onSelect={handleScopeSelect}
onHighlight={handleScopeHighlight}
isFocused={focusSection === 'scope'}
showNumbers={focusSection === 'scope'}
/>
</Box>
{/* Scope Selection - conditionally visible based on height constraints */}
{showScopeSelection && (
<Box marginTop={1} flexDirection="column">
<Text bold={focusSection === 'scope'} wrap="truncate">
{focusSection === 'scope' ? '> ' : ' '}Apply To
</Text>
<RadioButtonSelect
items={scopeItems}
initialIndex={0}
onSelect={handleScopeSelect}
onHighlight={handleScopeHighlight}
isFocused={focusSection === 'scope'}
showNumbers={focusSection === 'scope'}
/>
</Box>
)}
<Box height={1} />
<Text color={theme.text.secondary}>
(Use Enter to select, Tab to change focus)
(Use Enter to select
{showScopeSelection ? ', Tab to change focus' : ''})
</Text>
{showRestartPrompt && (
<Text color={theme.status.warning}>

View File

@@ -0,0 +1,351 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`SettingsDialog > Snapshot Tests > should render default state correctly 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render focused on scope selector 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with accessibility settings enabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode true* │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with all boolean settings disabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false* │
│ │
│ Disable Auto Update false* │
│ │
│ Enable Prompt Completion false* │
│ │
│ Debug Keystroke Logging false* │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false* │
│ │
│ Hide Tips false* │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with different scope selected (System) 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode (Modified in System) false │
│ │
│ Disable Auto Update (Modified in System) false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with different scope selected (Workspace) 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode (Modified in Workspace) false │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging (Modified in Workspace) false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with file filtering settings configured 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with mixed boolean and number settings 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false* │
│ │
│ Disable Auto Update true* │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false* │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with tools and security settings 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode false │
│ │
│ Disable Auto Update false │
│ │
│ Enable Prompt Completion false │
│ │
│ Debug Keystroke Logging false │
│ │
│ Output Format Text │
│ │
│ Hide Window Title false │
│ │
│ Hide Tips false │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`SettingsDialog > Snapshot Tests > should render with various boolean settings enabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Settings │
│ │
│ ▲ │
│ ● Vim Mode true* │
│ │
│ Disable Auto Update true* │
│ │
│ Enable Prompt Completion true* │
│ │
│ Debug Keystroke Logging true* │
│ │
│ Output Format Text │
│ │
│ Hide Window Title true* │
│ │
│ Hide Tips true* │
│ │
│ Hide Banner false │
│ │
│ ▼ │
│ │
│ │
│ Apply To │
│ ● 1. User Settings │
│ 2. Workspace Settings │
│ 3. System Settings │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
`;