Add description for each settings item in /settings (#15936)

This commit is contained in:
Sehoon Shon
2026-01-06 14:06:15 -05:00
committed by GitHub
parent 6f4b2ad0b9
commit 9172e28315
3 changed files with 243 additions and 107 deletions

View File

@@ -37,7 +37,12 @@ import {
import { useVimMode } from '../contexts/VimModeContext.js';
import { useKeypress } from '../hooks/useKeypress.js';
import chalk from 'chalk';
import { cpSlice, cpLen, stripUnsafeCharacters } from '../utils/textUtils.js';
import {
cpSlice,
cpLen,
stripUnsafeCharacters,
getCachedStringWidth,
} from '../utils/textUtils.js';
import {
type SettingsValue,
TOGGLE_TYPES,
@@ -65,7 +70,7 @@ interface SettingsDialogProps {
config?: Config;
}
const maxItemsToShow = 8;
const MAX_ITEMS_TO_SHOW = 8;
export function SettingsDialog({
settings,
@@ -194,6 +199,31 @@ export function SettingsDialog({
setShowRestartPrompt(newRestartRequired.size > 0);
}, [selectedScope, settings, globalPendingChanges]);
// Calculate max width for the left column (Label/Description) to keep values aligned or close
const maxLabelOrDescriptionWidth = useMemo(() => {
const allKeys = getDialogSettingKeys();
let max = 0;
for (const key of allKeys) {
const def = getSettingDefinition(key);
if (!def) continue;
const scopeMessage = getScopeMessageForSetting(
key,
selectedScope,
settings,
);
const label = def.label || key;
const labelFull = label + (scopeMessage ? ` ${scopeMessage}` : '');
const lWidth = getCachedStringWidth(labelFull);
const dWidth = def.description
? getCachedStringWidth(def.description)
: 0;
max = Math.max(max, lWidth, dWidth);
}
return max;
}, [selectedScope, settings]);
const generateSettingsItems = () => {
const settingKeys = searchQuery ? filteredKeys : getDialogSettingKeys();
@@ -202,6 +232,7 @@ export function SettingsDialog({
return {
label: definition?.label || key,
description: definition?.description,
value: key,
type: definition?.type,
toggle: () => {
@@ -478,8 +509,8 @@ export function SettingsDialog({
currentAvailableTerminalHeight - totalFixedHeight,
);
// Each setting item takes 2 lines (the setting row + spacing)
let maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
// Each setting item takes up to 3 lines (label/value row, description row, and spacing)
let maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 3));
// Decide whether to show scope selection based on remaining space
let showScopeSelection = true;
@@ -492,7 +523,7 @@ export function SettingsDialog({
1,
currentAvailableTerminalHeight - totalWithScope,
);
const maxItemsWithScope = Math.max(1, Math.floor(availableWithScope / 2));
const maxItemsWithScope = Math.max(1, Math.floor(availableWithScope / 3));
// If hiding scope selection allows us to show significantly more settings, do it
if (maxVisibleItems > maxItemsWithScope + 1) {
@@ -504,7 +535,7 @@ export function SettingsDialog({
1,
currentAvailableTerminalHeight - totalFixedHeight,
);
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 3));
}
} else {
// For normal height, include scope selection
@@ -513,13 +544,13 @@ export function SettingsDialog({
1,
currentAvailableTerminalHeight - totalFixedHeight,
);
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 2));
maxVisibleItems = Math.max(1, Math.floor(availableHeightForSettings / 3));
}
// Use the calculated maxVisibleItems or fall back to the original maxItemsToShow
const effectiveMaxItemsToShow = availableTerminalHeight
? Math.min(maxVisibleItems, items.length)
: maxItemsToShow;
: MAX_ITEMS_TO_SHOW;
// Ensure focus stays on settings when scope selection is hidden
React.useEffect(() => {
@@ -979,7 +1010,7 @@ export function SettingsDialog({
return (
<React.Fragment key={item.value}>
<Box marginX={1} flexDirection="row" alignItems="center">
<Box marginX={1} flexDirection="row" alignItems="flex-start">
<Box minWidth={2} flexShrink={0}>
<Text
color={
@@ -989,33 +1020,49 @@ export function SettingsDialog({
{isActive ? '●' : ''}
</Text>
</Box>
<Box minWidth={50}>
<Text
color={
isActive ? theme.status.success : theme.text.primary
}
>
{item.label}
{scopeMessage && (
<Text color={theme.text.secondary}>
{' '}
{scopeMessage}
</Text>
)}
</Text>
</Box>
<Box minWidth={3} />
<Text
color={
isActive
? theme.status.success
: shouldBeGreyedOut
? theme.text.secondary
: theme.text.primary
}
<Box
flexDirection="row"
flexGrow={1}
minWidth={0}
alignItems="flex-start"
>
{displayValue}
</Text>
<Box
flexDirection="column"
width={maxLabelOrDescriptionWidth}
minWidth={0}
>
<Text
color={
isActive ? theme.status.success : theme.text.primary
}
>
{item.label}
{scopeMessage && (
<Text color={theme.text.secondary}>
{' '}
{scopeMessage}
</Text>
)}
</Text>
<Text color={theme.text.secondary} wrap="truncate">
{item.description ?? ''}
</Text>
</Box>
<Box minWidth={3} />
<Box flexShrink={0}>
<Text
color={
isActive
? theme.status.success
: shouldBeGreyedOut
? theme.text.secondary
: theme.text.primary
}
>
{displayValue}
</Text>
</Box>
</Box>
</Box>
<Box height={1} />
</React.Fragment>