refactor: rename verboseMode to compactMode for better UX clarity (#3075)

The "Compact Mode" label is more intuitive than "Verbose Mode" for users,
as it directly describes the default compact view experience. This change
inverts the boolean semantics (compactMode=false means show full output)
and exposes the setting in the /settings dialog (showInDialog: true).

- Rename ui.verboseMode → ui.compactMode with inverted default (false)
- Rename VerboseModeContext → CompactModeContext (file and exports)
- Rename TOGGLE_VERBOSE_MODE → TOGGLE_COMPACT_MODE in key bindings
- Update all consumer components with inverted logic
- Update i18n keys across 6 locales (verbose → compact)
- Update VS Code settings schema
- Add ui.compactMode documentation to settings.md
- Fix Ctrl+O description in keyboard-shortcuts.md
This commit is contained in:
Shaojin Wen 2026-04-10 11:55:50 +08:00 committed by GitHub
parent 98a0f78c4b
commit 746f67f436
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 95 additions and 94 deletions

View file

@ -106,6 +106,7 @@ Settings are organized into categories. All settings should be placed within the
| `ui.showMemoryUsage` | boolean | Display memory usage information in the UI. | `false` |
| `ui.showLineNumbers` | boolean | Show line numbers in code blocks in the CLI output. | `true` |
| `ui.showCitations` | boolean | Show citations for generated text in the chat. | `true` |
| `ui.compactMode` | boolean | Hide tool output and thinking for a cleaner view. Toggle with `Ctrl+O` during a session. When enabled, a `compact` indicator appears in the footer. The setting persists across sessions. | `false` |
| `enableWelcomeBack` | boolean | Show welcome back dialog when returning to a project with conversation history. When enabled, Qwen Code will automatically detect if you're returning to a project with a previously generated project summary (`.qwen/PROJECT_SUMMARY.md`) and show a dialog allowing you to continue your previous conversation or start fresh. This feature integrates with the `/summary` command and quit confirmation dialog. | `true` |
| `ui.accessibility.enableLoadingPhrases` | boolean | Enable loading phrases (disable for accessibility). | `true` |
| `ui.accessibility.screenReader` | boolean | Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers. | `false` |

View file

@ -10,7 +10,7 @@ This document lists the available keyboard shortcuts in Qwen Code.
| `Ctrl+C` | Cancel the ongoing request and clear the input. Press twice to exit the application. |
| `Ctrl+D` | Exit the application if the input is empty. Press twice to confirm. |
| `Ctrl+L` | Clear the screen. |
| `Ctrl+O` | Toggle the display of the debug console. |
| `Ctrl+O` | Toggle compact mode (hide/show tool output and thinking). |
| `Ctrl+S` | Allows long responses to print fully, disabling truncation. Use your terminal's scrollback to view the entire output. |
| `Ctrl+T` | Toggle the display of tool descriptions. |
| `Shift+Tab` (`Tab` on Windows) | Cycle approval modes (`plan``default``auto-edit``yolo`) |

View file

@ -51,7 +51,7 @@ export enum Command {
EXIT = 'exit',
SHOW_MORE_LINES = 'showMoreLines',
RETRY_LAST = 'retryLast',
TOGGLE_VERBOSE_MODE = 'toggleVerboseMode',
TOGGLE_COMPACT_MODE = 'toggleCompactMode',
// Shell commands
REVERSE_SEARCH = 'reverseSearch',
@ -173,7 +173,7 @@ export const defaultKeyBindings: KeyBindingConfig = {
[Command.EXIT]: [{ key: 'd', ctrl: true }],
[Command.SHOW_MORE_LINES]: [{ key: 's', ctrl: true }],
[Command.RETRY_LAST]: [{ key: 'y', ctrl: true }],
[Command.TOGGLE_VERBOSE_MODE]: [{ key: 'o', ctrl: true }],
[Command.TOGGLE_COMPACT_MODE]: [{ key: 'o', ctrl: true }],
// Shell commands
[Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }],

View file

@ -591,15 +591,15 @@ const SETTINGS_SCHEMA = {
description: 'The last time the feedback dialog was shown.',
showInDialog: false,
},
verboseMode: {
compactMode: {
type: 'boolean',
label: 'Verbose Mode',
label: 'Compact Mode',
category: 'UI',
requiresRestart: false,
default: true,
default: false,
description:
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).',
showInDialog: false,
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).',
showInDialog: true,
},
},
},

View file

@ -1970,9 +1970,9 @@ export default {
'Raw-Modus nicht verfügbar. Bitte in einem interaktiven Terminal ausführen.',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(↑ ↓ Pfeiltasten zum Navigieren, Enter zum Auswählen, Strg+C zum Beenden)\n',
verbose: 'ausführlich',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'Vollständige Tool-Ausgabe und Denkprozess im ausführlichen Modus anzeigen (mit Strg+O umschalten).',
compact: 'kompakt',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'Tool-Ausgabe und Denkprozess ausblenden für eine übersichtlichere Ansicht (mit Strg+O umschalten).',
'Press Ctrl+O to show full tool output':
'Strg+O für vollständige Tool-Ausgabe drücken',

View file

@ -2010,9 +2010,9 @@ export default {
'Raw mode not available. Please run in an interactive terminal.',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n',
verbose: 'verbose',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).',
compact: 'compact',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Press Ctrl+O to show full tool output',

View file

@ -1462,9 +1462,9 @@ export default {
'Rawモードが利用できません。インタラクティブターミナルで実行してください。',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(↑ ↓ 矢印キーで移動、Enter で選択、Ctrl+C で終了)\n',
verbose: '詳細',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'詳細モードで完全なツール出力と思考を表示しますCtrl+O で切り替え)。',
compact: 'コンパクト',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'コンパクトモードでツール出力と思考を非表示にしますCtrl+O で切り替え)。',
'Press Ctrl+O to show full tool output': 'Ctrl+O で完全なツール出力を表示',
'Switch to plan mode or exit plan mode':

View file

@ -1960,9 +1960,9 @@ export default {
'Modo raw não disponível. Execute em um terminal interativo.',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(Use ↑ ↓ para navegar, Enter para selecionar, Ctrl+C para sair)\n',
verbose: 'detalhado',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'Mostrar saída completa da ferramenta e raciocínio no modo detalhado (alternar com Ctrl+O).',
compact: 'compacto',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'Ocultar saída da ferramenta e raciocínio para uma visualização mais limpa (alternar com Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Pressione Ctrl+O para exibir a saída completa da ferramenta',

View file

@ -1967,9 +1967,9 @@ export default {
'Raw-режим недоступен. Пожалуйста, запустите в интерактивном терминале.',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(↑ ↓ стрелки для навигации, Enter для выбора, Ctrl+C для выхода)\n',
verbose: 'подробный',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'Показывать полный вывод инструментов и процесс рассуждений в подробном режиме (переключить с помощью Ctrl+O).',
compact: 'компактный',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'Скрывать вывод инструментов и процесс рассуждений для более чистого вида (переключить с помощью Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Нажмите Ctrl+O для показа полного вывода инструментов',

View file

@ -1814,9 +1814,9 @@ export default {
'原始模式不可用。请在交互式终端中运行。',
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
'(使用 ↑ ↓ 箭头导航Enter 选择Ctrl+C 退出)\n',
verbose: '详细',
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'详细模式下显示完整工具输出和思考过程Ctrl+O 切换)。',
compact: '紧凑',
'Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).':
'紧凑模式下隐藏工具输出和思考过程,界面更简洁Ctrl+O 切换)。',
'Press Ctrl+O to show full tool output': '按 Ctrl+O 查看详细工具调用结果',
'Switch to plan mode or exit plan mode': '切换到计划模式或退出计划模式',

View file

@ -71,7 +71,7 @@ import { useApprovalModeCommand } from './hooks/useApprovalModeCommand.js';
import { useResumeCommand } from './hooks/useResumeCommand.js';
import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js';
import { useVimMode } from './contexts/VimModeContext.js';
import { VerboseModeProvider } from './contexts/VerboseModeContext.js';
import { CompactModeProvider } from './contexts/CompactModeContext.js';
import { useTerminalSize } from './hooks/useTerminalSize.js';
import { calculatePromptWidths } from './components/InputPrompt.js';
import { useStdin, useStdout } from 'ink';
@ -1262,8 +1262,8 @@ export const AppContainer = (props: AppContainerProps) => {
const [showToolDescriptions, setShowToolDescriptions] =
useState<boolean>(false);
const [verboseMode, setVerboseMode] = useState<boolean>(
settings.merged.ui?.verboseMode ?? true,
const [compactMode, setCompactMode] = useState<boolean>(
settings.merged.ui?.compactMode ?? false,
);
const [frozenSnapshot, setFrozenSnapshot] = useState<
HistoryItemWithoutId[] | null
@ -1681,10 +1681,10 @@ export const AppContainer = (props: AppContainerProps) => {
if (activePtyId || embeddedShellFocused) {
setEmbeddedShellFocused((prev) => !prev);
}
} else if (keyMatchers[Command.TOGGLE_VERBOSE_MODE](key)) {
const newValue = !verboseMode;
setVerboseMode(newValue);
void settings.setValue(SettingScope.User, 'ui.verboseMode', newValue);
} else if (keyMatchers[Command.TOGGLE_COMPACT_MODE](key)) {
const newValue = !compactMode;
setCompactMode(newValue);
void settings.setValue(SettingScope.User, 'ui.compactMode', newValue);
refreshStatic();
// Only freeze during the actual responding phase. WaitingForConfirmation
// must keep focus so the user can approve/cancel tool confirmation UI.
@ -1726,8 +1726,8 @@ export const AppContainer = (props: AppContainerProps) => {
// debugKeystrokeLogging is read at call time, so no stale closure risk.
settings,
isAuthenticating,
verboseMode,
setVerboseMode,
compactMode,
setCompactMode,
setFrozenSnapshot,
pendingHistoryItems,
refreshStatic,
@ -2154,9 +2154,9 @@ export const AppContainer = (props: AppContainerProps) => {
],
);
const verboseModeValue = useMemo(
() => ({ verboseMode, frozenSnapshot }),
[verboseMode, frozenSnapshot],
const compactModeValue = useMemo(
() => ({ compactMode, frozenSnapshot }),
[compactMode, frozenSnapshot],
);
return (
@ -2169,11 +2169,11 @@ export const AppContainer = (props: AppContainerProps) => {
startupWarnings: props.startupWarnings || [],
}}
>
<VerboseModeProvider value={verboseModeValue}>
<CompactModeProvider value={compactModeValue}>
<ShellFocusContext.Provider value={isFocused}>
<App />
</ShellFocusContext.Provider>
</VerboseModeProvider>
</CompactModeProvider>
</AppContext.Provider>
</ConfigContext.Provider>
</UIActionsContext.Provider>

View file

@ -17,7 +17,7 @@ import { useStatusLine } from '../hooks/useStatusLine.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import { useVimMode } from '../contexts/VimModeContext.js';
import { useVerboseMode } from '../contexts/VerboseModeContext.js';
import { useCompactMode } from '../contexts/CompactModeContext.js';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { t } from '../../i18n/index.js';
@ -26,7 +26,7 @@ export const Footer: React.FC = () => {
const config = useConfig();
const { vimEnabled, vimMode } = useVimMode();
const { text: statusLineText } = useStatusLine();
const { verboseMode } = useVerboseMode();
const { compactMode } = useCompactMode();
const { promptTokenCount, showAutoAcceptIndicator } = {
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
@ -101,10 +101,10 @@ export const Footer: React.FC = () => {
),
});
}
if (verboseMode) {
if (compactMode) {
rightItems.push({
key: 'verbose',
node: <Text color={theme.text.accent}>{t('verbose')}</Text>,
key: 'compact',
node: <Text color={theme.text.accent}>{t('compact')}</Text>,
});
}

View file

@ -48,7 +48,7 @@ import { ContextUsage } from './views/ContextUsage.js';
import { ArenaAgentCard, ArenaSessionCard } from './arena/ArenaCards.js';
import { InsightProgressMessage } from './messages/InsightProgressMessage.js';
import { BtwMessage } from './messages/BtwMessage.js';
import { useVerboseMode } from '../contexts/VerboseModeContext.js';
import { useCompactMode } from '../contexts/CompactModeContext.js';
interface HistoryItemDisplayProps {
item: HistoryItem;
@ -80,7 +80,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
? 0
: 1;
const { verboseMode } = useVerboseMode();
const { compactMode } = useCompactMode();
const itemForDisplay = useMemo(() => escapeAnsiCtrlCodes(item), [item]);
const contentWidth = terminalWidth - 4;
const boxWidth = mainAreaWidth || contentWidth;
@ -120,7 +120,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
contentWidth={contentWidth}
/>
)}
{verboseMode && itemForDisplay.type === 'gemini_thought' && (
{!compactMode && itemForDisplay.type === 'gemini_thought' && (
<ThinkMessage
text={itemForDisplay.text}
isPending={isPending}
@ -130,7 +130,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
contentWidth={contentWidth}
/>
)}
{verboseMode && itemForDisplay.type === 'gemini_thought_content' && (
{!compactMode && itemForDisplay.type === 'gemini_thought_content' && (
<ThinkMessageContent
text={itemForDisplay.text}
isPending={isPending}

View file

@ -13,7 +13,7 @@ import { useUIState } from '../contexts/UIStateContext.js';
import { useAppContext } from '../contexts/AppContext.js';
import { AppHeader } from './AppHeader.js';
import { DebugModeNotification } from './DebugModeNotification.js';
import { useVerboseMode } from '../contexts/VerboseModeContext.js';
import { useCompactMode } from '../contexts/CompactModeContext.js';
// Limit Gemini messages to a very high number of lines to mitigate performance
// issues in the worst case if we somehow get an enormous response from Gemini.
@ -24,7 +24,7 @@ const MAX_GEMINI_MESSAGE_LINES = 65536;
export const MainContent = () => {
const { version } = useAppContext();
const uiState = useUIState();
const { frozenSnapshot } = useVerboseMode();
const { frozenSnapshot } = useCompactMode();
const {
pendingHistoryItems,
terminalWidth,

View file

@ -2,7 +2,7 @@
exports[`<Footer /> > footer rendering (golden snapshots) > renders complete footer on narrow terminal > complete-footer-narrow 1`] = `
" ? for shortcuts
0.1% used | verbose"
0.1% used"
`;
exports[`<Footer /> > footer rendering (golden snapshots) > renders complete footer on wide terminal > complete-footer-wide 1`] = `" ? for shortcuts 0.1% context used | verbose"`;
exports[`<Footer /> > footer rendering (golden snapshots) > renders complete footer on wide terminal > complete-footer-wide 1`] = `" ? for shortcuts 0.1% context used"`;

View file

@ -15,7 +15,7 @@ import { CompactToolGroupDisplay } from './CompactToolGroupDisplay.js';
import { theme } from '../../semantic-colors.js';
import { SHELL_COMMAND_NAME, SHELL_NAME } from '../../constants.js';
import { useConfig } from '../../contexts/ConfigContext.js';
import { useVerboseMode } from '../../contexts/VerboseModeContext.js';
import { useCompactMode } from '../../contexts/CompactModeContext.js';
import type { AgentResultDisplay } from '@qwen-code/qwen-code-core';
function isAgentWithPendingConfirmation(
@ -53,7 +53,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
isUserInitiated,
}) => {
const config = useConfig();
const { verboseMode } = useVerboseMode();
const { compactMode } = useCompactMode();
const hasConfirmingTool = toolCalls.some(
(t) => t.status === ToolCallStatus.Confirming,
@ -103,7 +103,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
// Force-expand when: user must interact (Confirming), tool errored,
// shell is focused, or user-initiated
const showCompact =
!verboseMode &&
compactMode &&
!hasConfirmingTool &&
!hasErrorTool &&
!isEmbeddedShellFocused &&

View file

@ -12,7 +12,7 @@ import { StreamingState, ToolCallStatus } from '../../types.js';
import { Text } from 'ink';
import { StreamingContext } from '../../contexts/StreamingContext.js';
import { SettingsContext } from '../../contexts/SettingsContext.js';
import { VerboseModeProvider } from '../../contexts/VerboseModeContext.js';
import { CompactModeProvider } from '../../contexts/CompactModeContext.js';
import type {
AnsiOutput,
AnsiOutputDisplay,
@ -102,21 +102,21 @@ const mockSettings: LoadedSettings = {
},
} as LoadedSettings;
// Helper to render with context (verbose=true by default to show tool output)
// Helper to render with context (compactMode=false by default to show tool output)
const renderWithContext = (
ui: React.ReactElement,
streamingState: StreamingState,
verboseMode = true,
compactMode = false,
) => {
const contextValue: StreamingState = streamingState;
return render(
<VerboseModeProvider value={{ verboseMode, frozenSnapshot: null }}>
<CompactModeProvider value={{ compactMode, frozenSnapshot: null }}>
<SettingsContext.Provider value={mockSettings}>
<StreamingContext.Provider value={contextValue}>
{ui}
</StreamingContext.Provider>
</SettingsContext.Provider>
</VerboseModeProvider>,
</CompactModeProvider>,
);
};
@ -147,11 +147,11 @@ describe('<ToolMessage />', () => {
expect(output).toContain('MockMarkdown:Test result');
});
it('hides result output in compact mode (verboseMode=false)', () => {
it('hides result output in compact mode (compactMode=true)', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} />,
StreamingState.Idle,
false, // compact mode
true, // compact mode
);
const output = lastFrame();
expect(output).toContain('✓'); // status indicator still visible

View file

@ -28,7 +28,7 @@ import { SHELL_COMMAND_NAME, SHELL_NAME } from '../../constants.js';
import { theme } from '../../semantic-colors.js';
import { useSettings } from '../../contexts/SettingsContext.js';
import type { LoadedSettings } from '../../../config/settings.js';
import { useVerboseMode } from '../../contexts/VerboseModeContext.js';
import { useCompactMode } from '../../contexts/CompactModeContext.js';
import {
ToolStatusIndicator,
@ -343,9 +343,9 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
// Use the custom hook to determine the display type
const displayRenderer = useResultDisplayRenderer(resultDisplay);
const { verboseMode } = useVerboseMode();
const { compactMode } = useCompactMode();
const effectiveDisplayRenderer =
verboseMode || forceShowResult
!compactMode || forceShowResult
? displayRenderer
: { type: 'none' as const };

View file

@ -0,0 +1,23 @@
/**
* @license
* Copyright 2025 Qwen
* SPDX-License-Identifier: Apache-2.0
*/
import { createContext, useContext } from 'react';
import type { HistoryItemWithoutId } from '../types.js';
interface CompactModeContextType {
compactMode: boolean;
frozenSnapshot: HistoryItemWithoutId[] | null;
}
const CompactModeContext = createContext<CompactModeContextType>({
compactMode: false,
frozenSnapshot: null,
});
export const useCompactMode = (): CompactModeContextType =>
useContext(CompactModeContext);
export const CompactModeProvider = CompactModeContext.Provider;

View file

@ -1,23 +0,0 @@
/**
* @license
* Copyright 2025 Qwen
* SPDX-License-Identifier: Apache-2.0
*/
import { createContext, useContext } from 'react';
import type { HistoryItemWithoutId } from '../types.js';
interface VerboseModeContextType {
verboseMode: boolean;
frozenSnapshot: HistoryItemWithoutId[] | null;
}
const VerboseModeContext = createContext<VerboseModeContextType>({
verboseMode: true,
frozenSnapshot: null,
});
export const useVerboseMode = (): VerboseModeContextType =>
useContext(VerboseModeContext);
export const VerboseModeProvider = VerboseModeContext.Provider;

View file

@ -60,7 +60,7 @@ describe('keyMatchers', () => {
[Command.EXIT]: (key: Key) => key.ctrl && key.name === 'd',
[Command.SHOW_MORE_LINES]: (key: Key) => key.ctrl && key.name === 's',
[Command.RETRY_LAST]: (key: Key) => key.ctrl && key.name === 'y',
[Command.TOGGLE_VERBOSE_MODE]: (key: Key) => key.ctrl && key.name === 'o',
[Command.TOGGLE_COMPACT_MODE]: (key: Key) => key.ctrl && key.name === 'o',
[Command.REVERSE_SEARCH]: (key: Key) => key.ctrl && key.name === 'r',
[Command.SUBMIT_REVERSE_SEARCH]: (key: Key) =>
key.name === 'return' && !key.ctrl,
@ -260,7 +260,7 @@ describe('keyMatchers', () => {
negative: [createKey('y'), createKey('r', { ctrl: true })],
},
{
command: Command.TOGGLE_VERBOSE_MODE,
command: Command.TOGGLE_COMPACT_MODE,
positive: [createKey('o', { ctrl: true })],
negative: [createKey('o'), createKey('p', { ctrl: true })],
},

View file

@ -220,10 +220,10 @@
"type": "number",
"default": 0
},
"verboseMode": {
"description": "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).",
"compactMode": {
"description": "Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).",
"type": "boolean",
"default": true
"default": false
}
}
},