-
Notifications
You must be signed in to change notification settings - Fork 15.5k
Feature/add auto mode settings and fix bug #368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dff6789
9a5998e
e7e1f7a
de9494c
0a90b21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import { | |
| import chalk from 'chalk'; | ||
| import { | ||
| permissionModeTitle, | ||
| permissionModeShortTitle, | ||
| permissionModeFromString, | ||
| toExternalPermissionMode, | ||
| isExternalPermissionMode, | ||
|
|
@@ -153,7 +154,7 @@ export function Config({ | |
| const initialLanguage = React.useRef(currentLanguage); | ||
| const [selectedIndex, setSelectedIndex] = useState(0); | ||
| const [scrollOffset, setScrollOffset] = useState(0); | ||
| const [isSearchMode, setIsSearchMode] = useState(true); | ||
| const [isSearchMode, setIsSearchMode] = useState(false); | ||
| const isTerminalFocused = useTerminalFocus(); | ||
| const { rows } = useTerminalSize(); | ||
| // contentHeight is set by Settings.tsx (same value passed to Tabs to fix | ||
|
|
@@ -167,6 +168,9 @@ export function Config({ | |
| const thinkingEnabled = useAppState(s => s.thinkingEnabled); | ||
| const isFastMode = useAppState(s => (isFastModeEnabled() ? s.fastMode : false)); | ||
| const promptSuggestionEnabled = useAppState(s => s.promptSuggestionEnabled); | ||
| const currentDefaultPermissionMode = permissionModeFromString( | ||
| settingsData?.permissions?.defaultMode ?? 'default', | ||
| ); | ||
| // Show auto in the default-mode dropdown when the user has opted in OR the | ||
| // config is fully 'enabled' — even if currently circuit-broken ('disabled'), | ||
| // an opted-in user should still see it in settings (it's a temporary state). | ||
|
|
@@ -558,27 +562,23 @@ export function Config({ | |
| { | ||
| id: 'defaultPermissionMode', | ||
| label: 'Default permission mode', | ||
| value: settingsData?.permissions?.defaultMode || 'default', | ||
| value: currentDefaultPermissionMode, | ||
| options: (() => { | ||
| const priorityOrder: PermissionMode[] = ['default', 'plan']; | ||
| const allModes: readonly PermissionMode[] = feature('TRANSCRIPT_CLASSIFIER') | ||
| ? PERMISSION_MODES | ||
| : EXTERNAL_PERMISSION_MODES; | ||
| const excluded: PermissionMode[] = ['bypassPermissions']; | ||
| if (feature('TRANSCRIPT_CLASSIFIER') && !showAutoInDefaultModePicker) { | ||
| excluded.push('auto'); | ||
| } | ||
| return [...priorityOrder, ...allModes.filter(m => !priorityOrder.includes(m) && !excluded.includes(m))]; | ||
| return [...priorityOrder, ...PERMISSION_MODES.filter(m => !priorityOrder.includes(m))]; | ||
| })(), | ||
| type: 'enum' as const, | ||
| onChange(mode: string) { | ||
| const parsedMode = permissionModeFromString(mode); | ||
| // Internal modes (e.g. auto) are stored directly | ||
| const validatedMode = isExternalPermissionMode(parsedMode) ? toExternalPermissionMode(parsedMode) : parsedMode; | ||
| // auto is an internal-only mode — store it directly, don't convert | ||
| // to its external mapping ('default') which would make it invisible. | ||
| const validatedMode = parsedMode === 'auto' | ||
| ? parsedMode | ||
| : (isExternalPermissionMode(parsedMode) ? toExternalPermissionMode(parsedMode) : parsedMode); | ||
| const result = updateSettingsForSource('userSettings', { | ||
| permissions: { | ||
| ...settingsData?.permissions, | ||
| defaultMode: validatedMode as ExternalPermissionMode, | ||
| defaultMode: validatedMode as (typeof PERMISSION_MODES)[number], | ||
| }, | ||
| }); | ||
|
|
||
|
|
@@ -1548,6 +1548,8 @@ export function Config({ | |
| 'scroll:lineUp': () => moveSelection(-1), | ||
| 'scroll:lineDown': () => moveSelection(1), | ||
| 'select:accept': toggleSetting, | ||
| 'select:previousValue': () => toggleSetting(), | ||
| 'select:nextValue': () => toggleSetting(), | ||
|
Comment on lines
+1551
to
+1552
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This preserves the prior 🐛 Proposed fixAdd a direction arg to - const toggleSetting = useCallback(() => {
+ const toggleSetting = useCallback((direction: 1 | -1 = 1) => {
const setting = filteredSettingsItems[selectedIndex];
...
if (setting.type === 'enum') {
isDirty.current = true;
const currentIndex = setting.options.indexOf(setting.value);
- const nextIndex = (currentIndex + 1) % setting.options.length;
+ const len = setting.options.length;
+ const nextIndex = (currentIndex + direction + len) % len;
setting.onChange(setting.options[nextIndex]!);
return;
}
}, [...]);- 'select:previousValue': () => toggleSetting(),
- 'select:nextValue': () => toggleSetting(),
+ 'select:previousValue': () => toggleSetting(-1),
+ 'select:nextValue': () => toggleSetting(1),( 🤖 Prompt for AI Agents |
||
| 'settings:search': () => { | ||
| setIsSearchMode(true); | ||
| setSearchQuery(''); | ||
|
|
@@ -1936,13 +1938,13 @@ export function Config({ | |
|
|
||
| return ( | ||
| <React.Fragment key={setting.id}> | ||
| <Box> | ||
| <Box width="100%"> | ||
| <Box width={44}> | ||
| <Text color={isSelected ? 'suggestion' : undefined}> | ||
| {isSelected ? figures.pointer : ' '} {setting.label} | ||
| </Text> | ||
| </Box> | ||
| <Box key={isSelected ? 'selected' : 'unselected'}> | ||
| <Box flexGrow={1}> | ||
| {setting.type === 'boolean' ? ( | ||
| <> | ||
| <Text color={isSelected ? 'suggestion' : undefined}>{setting.value.toString()}</Text> | ||
|
|
@@ -1963,7 +1965,7 @@ export function Config({ | |
| </Text> | ||
| ) : setting.id === 'defaultPermissionMode' ? ( | ||
| <Text color={isSelected ? 'suggestion' : undefined}> | ||
| {permissionModeTitle(setting.value as PermissionMode)} | ||
| {permissionModeShortTitle(setting.value as PermissionMode)} | ||
| </Text> | ||
| ) : setting.id === 'autoUpdatesChannel' && autoUpdaterDisabledReason ? ( | ||
| <Box flexDirection="column"> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always-on RSS entry breaks the empty-state fallbacks.
useRssDisplay()callsupdate()synchronously in itsuseEffect, sorssStatebecomes non-null on the second render and stays non-null for the rest of the session. That meanspartswill essentially always contain ≥1 element, which silently disables two existing branches:if (parts.length === 0 && !tasksPart && !modePart && showHint)— the "? for shortcuts" hint will no longer be shown to new users on an otherwise empty footer.if (parts.length === 0 && !tasksPart && !modePart)— the fullscreen row-reservation fallback (the deliberately-rendered<Text> </Text>) is now dead code; the component will instead render the truncating Box. The comment block above line 632 explicitly calls out that this empty-parts path matters for stable footer height.Either gate the RSS push so it doesn't count for these checks, or push it last and re-evaluate emptiness against a "real content" subset.
🤖 Prompt for AI Agents
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 946
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 585
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 100
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 100
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 100
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 100
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 2259
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 1975
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 2409
🏁 Script executed:
# Check the imports and context of the PromptInputFooterLeftSide file head -n 50 src/components/PromptInput/PromptInputFooterLeftSide.tsxRepository: claude-code-best/claude-code
Length of output: 2573
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 2451
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 4131
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 54
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 243
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 5876
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 285
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 18408
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 2073
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 1015
Gate RSS footer behind config option, similar to
prStatusFooterEnabled.The RSS memory indicator currently displays for all users every 5 seconds with no way to disable it. This differs from the PR status footer, which respects
getGlobalConfig().prStatusFooterEnabled. Memory bytes ticking in the footer may confuse non-developers and appears to be intended as a debugging aid rather than a default-on feature.Apply the same gating pattern: add
rssFooterEnabled?: booleantoGlobalConfigand check it in the render condition alongside the existence ofrssState.🤖 Prompt for AI Agents