mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
feat: to #2767, support verbose and compact mode swither with ctrl-o
This commit is contained in:
1 parent
1b1a029fd7
commit
b9c17d13ff
17 files changed
+166
-45
No files matched your search
@@ -51,6 +51,7 @@ export enum Command {
|
||||
EXIT = 'exit',
|
||||
SHOW_MORE_LINES = 'showMoreLines',
|
||||
RETRY_LAST = 'retryLast',
|
||||
TOGGLE_VERBOSE_MODE = 'toggleVerboseMode',
|
||||
|
||||
// Shell commands
|
||||
REVERSE_SEARCH = 'reverseSearch',
|
||||
@@ -172,6 +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 }],
|
||||
|
||||
// Shell commands
|
||||
[Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }],
|
||||
|
||||
@@ -540,6 +540,16 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'The last time the feedback dialog was shown.',
|
||||
showInDialog: false,
|
||||
},
|
||||
verboseMode: {
|
||||
type: 'boolean',
|
||||
label: 'Verbose Mode',
|
||||
category: 'UI',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Show full tool output and thinking in verbose mode (toggle with ctrl+o).',
|
||||
showInDialog: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -1948,4 +1948,5 @@ 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: 'verbose',
|
||||
};
|
||||
@@ -1989,4 +1989,5 @@ 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',
|
||||
};
|
||||
@@ -1441,4 +1441,5 @@ export default {
|
||||
'Rawモードが利用できません。インタラクティブターミナルで実行してください。',
|
||||
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
|
||||
'(↑ ↓ 矢印キーで移動、Enter で選択、Ctrl+C で終了)\n',
|
||||
verbose: 'verbose',
|
||||
};
|
||||
@@ -1938,4 +1938,5 @@ 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: 'verbose',
|
||||
};
|
||||
@@ -1945,4 +1945,5 @@ export default {
|
||||
'Raw-режим недоступен. Пожалуйста, запустите в интерактивном терминале.',
|
||||
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
|
||||
'(↑ ↓ стрелки для навигации, Enter для выбора, Ctrl+C для выхода)\n',
|
||||
verbose: 'verbose',
|
||||
};
|
||||
@@ -1795,4 +1795,5 @@ export default {
|
||||
'原始模式不可用。请在交互式终端中运行。',
|
||||
'(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n':
|
||||
'(使用 ↑ ↓ 箭头导航,Enter 选择,Ctrl+C 退出)\n',
|
||||
verbose: '详细模式',
|
||||
};
|
||||
@@ -60,6 +60,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 { useTerminalSize } from './hooks/useTerminalSize.js';
|
||||
import { calculatePromptWidths } from './components/InputPrompt.js';
|
||||
import { useStdin, useStdout } from 'ink';
|
||||
@@ -792,6 +793,11 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
handleWelcomeBackClose,
|
||||
} = useWelcomeBack(config, handleFinalSubmit, buffer, settings.merged);
|
||||
|
||||
const pendingHistoryItems = useMemo(
|
||||
() => [...pendingSlashCommandHistoryItems, ...pendingGeminiHistoryItems],
|
||||
[pendingSlashCommandHistoryItems, pendingGeminiHistoryItems],
|
||||
);
|
||||
|
||||
cancelHandlerRef.current = useCallback(() => {
|
||||
const pendingHistoryItems = [
|
||||
...pendingSlashCommandHistoryItems,
|
||||
@@ -963,6 +969,13 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
const [showToolDescriptions, setShowToolDescriptions] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [verboseMode, setVerboseMode] = useState<boolean>(
|
||||
settings.merged.ui?.verboseMode ?? false,
|
||||
);
|
||||
const [frozenSnapshot, setFrozenSnapshot] = useState<
|
||||
HistoryItemWithoutId[] | null
|
||||
>(null);
|
||||
|
||||
const [ctrlCPressedOnce, setCtrlCPressedOnce] = useState(false);
|
||||
const ctrlCTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
|
||||
@@ -977,6 +990,12 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
|
||||
const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (streamingState === StreamingState.Idle) {
|
||||
setFrozenSnapshot(null);
|
||||
}
|
||||
}, [streamingState]);
|
||||
|
||||
const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } =
|
||||
useFolderTrust(settings, setIsTrustedFolder);
|
||||
const {
|
||||
@@ -1347,6 +1366,16 @@ 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);
|
||||
refreshStatic();
|
||||
if (newValue && streamingState !== StreamingState.Idle) {
|
||||
setFrozenSnapshot([...pendingHistoryItems]);
|
||||
} else {
|
||||
setFrozenSnapshot(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
@@ -1375,8 +1404,13 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
btwItem,
|
||||
setBtwItem,
|
||||
cancelBtw,
|
||||
settings.merged.general?.debugKeystrokeLogging,
|
||||
settings,
|
||||
isAuthenticating,
|
||||
verboseMode,
|
||||
setVerboseMode,
|
||||
setFrozenSnapshot,
|
||||
pendingHistoryItems,
|
||||
refreshStatic,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1465,11 +1499,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
sessionStats,
|
||||
});
|
||||
|
||||
const pendingHistoryItems = useMemo(
|
||||
() => [...pendingSlashCommandHistoryItems, ...pendingGeminiHistoryItems],
|
||||
[pendingSlashCommandHistoryItems, pendingGeminiHistoryItems],
|
||||
);
|
||||
|
||||
const uiState: UIState = useMemo(
|
||||
() => ({
|
||||
history: historyManager.history,
|
||||
@@ -1807,9 +1836,11 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
startupWarnings: props.startupWarnings || [],
|
||||
}}
|
||||
>
|
||||
<ShellFocusContext.Provider value={isFocused}>
|
||||
<App />
|
||||
</ShellFocusContext.Provider>
|
||||
<VerboseModeProvider value={{ verboseMode, frozenSnapshot }}>
|
||||
<ShellFocusContext.Provider value={isFocused}>
|
||||
<App />
|
||||
</ShellFocusContext.Provider>
|
||||
</VerboseModeProvider>
|
||||
</AppContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</UIActionsContext.Provider>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { isNarrowWidth } from '../utils/isNarrowWidth.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 { ApprovalMode } from '@qwen-code/qwen-code-core';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
@@ -23,6 +24,7 @@ export const Footer: React.FC = () => {
|
||||
const uiState = useUIState();
|
||||
const config = useConfig();
|
||||
const { vimEnabled, vimMode } = useVimMode();
|
||||
const { verboseMode } = useVerboseMode();
|
||||
|
||||
const { promptTokenCount, showAutoAcceptIndicator } = {
|
||||
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
|
||||
@@ -93,6 +95,12 @@ export const Footer: React.FC = () => {
|
||||
),
|
||||
});
|
||||
}
|
||||
if (verboseMode) {
|
||||
rightItems.push({
|
||||
key: 'verbose',
|
||||
node: <Text color={theme.text.accent}>{t('verbose')}</Text>,
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
justifyContent="space-between"
|
||||
|
||||
@@ -43,6 +43,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';
|
||||
|
||||
interface HistoryItemDisplayProps {
|
||||
item: HistoryItem;
|
||||
@@ -74,6 +75,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
|
||||
? 0
|
||||
: 1;
|
||||
|
||||
const { verboseMode } = useVerboseMode();
|
||||
const itemForDisplay = useMemo(() => escapeAnsiCtrlCodes(item), [item]);
|
||||
const contentWidth = terminalWidth - 4;
|
||||
const boxWidth = mainAreaWidth || contentWidth;
|
||||
@@ -113,7 +115,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
|
||||
contentWidth={contentWidth}
|
||||
/>
|
||||
)}
|
||||
{itemForDisplay.type === 'gemini_thought' && (
|
||||
{verboseMode && itemForDisplay.type === 'gemini_thought' && (
|
||||
<ThinkMessage
|
||||
text={itemForDisplay.text}
|
||||
isPending={isPending}
|
||||
@@ -123,7 +125,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
|
||||
contentWidth={contentWidth}
|
||||
/>
|
||||
)}
|
||||
{itemForDisplay.type === 'gemini_thought_content' && (
|
||||
{verboseMode && itemForDisplay.type === 'gemini_thought_content' && (
|
||||
<ThinkMessageContent
|
||||
text={itemForDisplay.text}
|
||||
isPending={isPending}
|
||||
|
||||
@@ -13,6 +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';
|
||||
|
||||
// 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.
|
||||
@@ -23,6 +24,7 @@ const MAX_GEMINI_MESSAGE_LINES = 65536;
|
||||
export const MainContent = () => {
|
||||
const { version } = useAppContext();
|
||||
const uiState = useUIState();
|
||||
const { frozenSnapshot } = useVerboseMode();
|
||||
const {
|
||||
pendingHistoryItems,
|
||||
terminalWidth,
|
||||
@@ -57,21 +59,26 @@ export const MainContent = () => {
|
||||
</Static>
|
||||
<OverflowProvider>
|
||||
<Box flexDirection="column">
|
||||
{pendingHistoryItems.map((item, i) => (
|
||||
<HistoryItemDisplay
|
||||
key={i}
|
||||
availableTerminalHeight={
|
||||
uiState.constrainHeight ? availableTerminalHeight : undefined
|
||||
}
|
||||
terminalWidth={terminalWidth}
|
||||
mainAreaWidth={mainAreaWidth}
|
||||
item={{ ...item, id: 0 }}
|
||||
isPending={true}
|
||||
isFocused={!uiState.isEditorDialogOpen}
|
||||
activeShellPtyId={uiState.activePtyId}
|
||||
embeddedShellFocused={uiState.embeddedShellFocused}
|
||||
/>
|
||||
))}
|
||||
{(frozenSnapshot ?? pendingHistoryItems).map((item, i) => {
|
||||
const isFrozen = frozenSnapshot !== null;
|
||||
return (
|
||||
<HistoryItemDisplay
|
||||
key={i}
|
||||
availableTerminalHeight={
|
||||
uiState.constrainHeight ? availableTerminalHeight : undefined
|
||||
}
|
||||
terminalWidth={terminalWidth}
|
||||
mainAreaWidth={mainAreaWidth}
|
||||
item={{ ...item, id: 0 }}
|
||||
isPending={true}
|
||||
isFocused={isFrozen ? false : !uiState.isEditorDialogOpen}
|
||||
activeShellPtyId={isFrozen ? undefined : uiState.activePtyId}
|
||||
embeddedShellFocused={
|
||||
isFrozen ? false : uiState.embeddedShellFocused
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ShowMoreLines constrainHeight={uiState.constrainHeight} />
|
||||
</Box>
|
||||
</OverflowProvider>
|
||||
|
||||
@@ -12,6 +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 type {
|
||||
AnsiOutput,
|
||||
AnsiOutputDisplay,
|
||||
@@ -101,18 +102,21 @@ const mockSettings: LoadedSettings = {
|
||||
},
|
||||
} as LoadedSettings;
|
||||
|
||||
// Helper to render with context
|
||||
// Helper to render with context (verbose=true by default to show tool output)
|
||||
const renderWithContext = (
|
||||
ui: React.ReactElement,
|
||||
streamingState: StreamingState,
|
||||
verboseMode = true,
|
||||
) => {
|
||||
const contextValue: StreamingState = streamingState;
|
||||
return render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<StreamingContext.Provider value={contextValue}>
|
||||
{ui}
|
||||
</StreamingContext.Provider>
|
||||
</SettingsContext.Provider>,
|
||||
<VerboseModeProvider value={{ verboseMode, frozenSnapshot: null }}>
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<StreamingContext.Provider value={contextValue}>
|
||||
{ui}
|
||||
</StreamingContext.Provider>
|
||||
</SettingsContext.Provider>
|
||||
</VerboseModeProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -143,6 +147,18 @@ describe('<ToolMessage />', () => {
|
||||
expect(output).toContain('MockMarkdown:Test result');
|
||||
});
|
||||
|
||||
it('hides result output in compact mode (verboseMode=false)', () => {
|
||||
const { lastFrame } = renderWithContext(
|
||||
<ToolMessage {...baseProps} />,
|
||||
StreamingState.Idle,
|
||||
false, // compact mode
|
||||
);
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('✓'); // status indicator still visible
|
||||
expect(output).toContain('test-tool'); // tool name still visible
|
||||
expect(output).not.toContain('MockMarkdown:Test result'); // result hidden
|
||||
});
|
||||
|
||||
describe('ToolStatusIndicator rendering', () => {
|
||||
it('shows ✓ for Success status', () => {
|
||||
const { lastFrame } = renderWithContext(
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
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';
|
||||
|
||||
const STATIC_HEIGHT = 1;
|
||||
const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc.
|
||||
@@ -324,6 +325,10 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
||||
|
||||
// Use the custom hook to determine the display type
|
||||
const displayRenderer = useResultDisplayRenderer(resultDisplay);
|
||||
const { verboseMode } = useVerboseMode();
|
||||
const effectiveDisplayRenderer = verboseMode
|
||||
? displayRenderer
|
||||
: { type: 'none' as const };
|
||||
|
||||
return (
|
||||
<Box paddingX={1} paddingY={0} flexDirection="column">
|
||||
@@ -344,44 +349,44 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
||||
)}
|
||||
{emphasis === 'high' && <TrailingIndicator />}
|
||||
</Box>
|
||||
{displayRenderer.type !== 'none' && (
|
||||
{effectiveDisplayRenderer.type !== 'none' && (
|
||||
<Box paddingLeft={STATUS_INDICATOR_WIDTH} width="100%" marginTop={1}>
|
||||
<Box flexDirection="column">
|
||||
{displayRenderer.type === 'todo' && (
|
||||
<TodoResultRenderer data={displayRenderer.data} />
|
||||
{effectiveDisplayRenderer.type === 'todo' && (
|
||||
<TodoResultRenderer data={effectiveDisplayRenderer.data} />
|
||||
)}
|
||||
{displayRenderer.type === 'plan' && (
|
||||
{effectiveDisplayRenderer.type === 'plan' && (
|
||||
<PlanResultRenderer
|
||||
data={displayRenderer.data}
|
||||
data={effectiveDisplayRenderer.data}
|
||||
availableHeight={availableHeight}
|
||||
childWidth={innerWidth}
|
||||
/>
|
||||
)}
|
||||
{displayRenderer.type === 'task' && config && (
|
||||
{effectiveDisplayRenderer.type === 'task' && config && (
|
||||
<SubagentExecutionRenderer
|
||||
data={displayRenderer.data}
|
||||
data={effectiveDisplayRenderer.data}
|
||||
availableHeight={availableHeight}
|
||||
childWidth={innerWidth}
|
||||
config={config}
|
||||
/>
|
||||
)}
|
||||
{displayRenderer.type === 'diff' && (
|
||||
{effectiveDisplayRenderer.type === 'diff' && (
|
||||
<DiffResultRenderer
|
||||
data={displayRenderer.data}
|
||||
data={effectiveDisplayRenderer.data}
|
||||
availableHeight={availableHeight}
|
||||
childWidth={innerWidth}
|
||||
settings={settings}
|
||||
/>
|
||||
)}
|
||||
{displayRenderer.type === 'ansi' && (
|
||||
{effectiveDisplayRenderer.type === 'ansi' && (
|
||||
<AnsiOutputText
|
||||
data={displayRenderer.data}
|
||||
data={effectiveDisplayRenderer.data}
|
||||
availableTerminalHeight={availableHeight}
|
||||
/>
|
||||
)}
|
||||
{displayRenderer.type === 'string' && (
|
||||
{effectiveDisplayRenderer.type === 'string' && (
|
||||
<StringResultRenderer
|
||||
data={displayRenderer.data}
|
||||
data={effectiveDisplayRenderer.data}
|
||||
renderAsMarkdown={renderOutputAsMarkdown}
|
||||
availableHeight={availableHeight}
|
||||
childWidth={innerWidth}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* 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: false,
|
||||
frozenSnapshot: null,
|
||||
});
|
||||
|
||||
export const useVerboseMode = (): VerboseModeContextType =>
|
||||
useContext(VerboseModeContext);
|
||||
|
||||
export const VerboseModeProvider = VerboseModeContext.Provider;
|
||||
@@ -60,6 +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.REVERSE_SEARCH]: (key: Key) => key.ctrl && key.name === 'r',
|
||||
[Command.SUBMIT_REVERSE_SEARCH]: (key: Key) =>
|
||||
key.name === 'return' && !key.ctrl,
|
||||
@@ -258,6 +259,11 @@ describe('keyMatchers', () => {
|
||||
positive: [createKey('y', { ctrl: true })],
|
||||
negative: [createKey('y'), createKey('r', { ctrl: true })],
|
||||
},
|
||||
{
|
||||
command: Command.TOGGLE_VERBOSE_MODE,
|
||||
positive: [createKey('o', { ctrl: true })],
|
||||
negative: [createKey('o'), createKey('p', { ctrl: true })],
|
||||
},
|
||||
|
||||
// Shell commands
|
||||
{
|
||||
|
||||
@@ -194,6 +194,11 @@
|
||||
"description": "The last time the feedback dialog was shown.",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"verboseMode": {
|
||||
"description": "Show full tool output and thinking in verbose mode (toggle with ctrl+o).",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user