fix: address PR review feedback for verbose/compact mode toggle

- Change default verboseMode to true (preserving current UX behavior)
- Fix compact mode hiding active shell output (add forceShowResult + isUserInitiated)
- Fix asymmetric frozen snapshot (freeze on ANY toggle during streaming)
- Fix copyright header in VerboseModeContext.tsx (Google LLC → Qwen)
- Add proper translations for all 6 locales (de/ja/pt/ru/zh/en)
- Rewrite CompactToolGroupDisplay with bordered box, i18n hint, shell detection
- Fix Pending status color (theme.text.secondary instead of theme.status.success)
- Fix description casing: ctrl+o → Ctrl+O
- Add explanatory comment for useCallback settings dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chiga0 2026-04-04 20:43:06 +08:00
parent b9c17d13ff
commit 6fd29b698b
18 changed files with 261 additions and 27 deletions

View file

@ -0,0 +1,148 @@
/**
* @license
* Copyright 2025 Qwen
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { Box, Text } from 'ink';
import type { IndividualToolCallDisplay } from '../../types.js';
import { ToolCallStatus } from '../../types.js';
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
import {
TOOL_STATUS,
SHELL_COMMAND_NAME,
SHELL_NAME,
} from '../../constants.js';
import { theme } from '../../semantic-colors.js';
import { t } from '../../../i18n/index.js';
interface CompactToolGroupDisplayProps {
toolCalls: IndividualToolCallDisplay[];
contentWidth: number;
}
// Priority: Confirming > Executing > Error > Canceled > Pending > Success
function getOverallStatus(
toolCalls: IndividualToolCallDisplay[],
): ToolCallStatus {
if (toolCalls.some((t) => t.status === ToolCallStatus.Confirming))
return ToolCallStatus.Confirming;
if (toolCalls.some((t) => t.status === ToolCallStatus.Executing))
return ToolCallStatus.Executing;
if (toolCalls.some((t) => t.status === ToolCallStatus.Error))
return ToolCallStatus.Error;
if (toolCalls.some((t) => t.status === ToolCallStatus.Canceled))
return ToolCallStatus.Canceled;
if (toolCalls.some((t) => t.status === ToolCallStatus.Pending))
return ToolCallStatus.Pending;
return ToolCallStatus.Success;
}
// Active tool priority: Confirming > Executing > last in array
function getActiveTool(
toolCalls: IndividualToolCallDisplay[],
): IndividualToolCallDisplay {
return (
toolCalls.find((t) => t.status === ToolCallStatus.Confirming) ??
toolCalls.find((t) => t.status === ToolCallStatus.Executing) ??
toolCalls[toolCalls.length - 1]
);
}
const STATUS_INDICATOR_WIDTH = 3;
export const CompactToolGroupDisplay: React.FC<
CompactToolGroupDisplayProps
> = ({ toolCalls, contentWidth }) => {
if (toolCalls.length === 0) return null;
const overallStatus = getOverallStatus(toolCalls);
const activeTool = getActiveTool(toolCalls);
const isShellCommand = toolCalls.some(
(t) => t.name === SHELL_COMMAND_NAME || t.name === SHELL_NAME,
);
const hasPending = !toolCalls.every(
(t) => t.status === ToolCallStatus.Success,
);
const borderColor = isShellCommand
? theme.ui.symbol
: hasPending
? theme.status.warning
: theme.border.default;
// Take only the first line of description to prevent multi-line shell scripts
// from expanding the compact view (wrap="truncate-end" only handles width overflow,
// not literal \n characters in the content)
const activeToolDescription = activeTool.description
? activeTool.description.split('\n')[0]
: '';
const renderStatusIcon = () => {
switch (overallStatus) {
case ToolCallStatus.Executing:
return (
<GeminiRespondingSpinner
spinnerType="toggle"
nonRespondingDisplay={TOOL_STATUS.EXECUTING}
/>
);
case ToolCallStatus.Success:
return <Text color={theme.status.success}>{TOOL_STATUS.SUCCESS}</Text>;
case ToolCallStatus.Error:
return (
<Text color={theme.status.error} bold>
{TOOL_STATUS.ERROR}
</Text>
);
case ToolCallStatus.Confirming:
return (
<Text color={theme.status.warning}>{TOOL_STATUS.CONFIRMING}</Text>
);
case ToolCallStatus.Canceled:
return (
<Text color={theme.status.warning} bold>
{TOOL_STATUS.CANCELED}
</Text>
);
case ToolCallStatus.Pending:
return <Text color={theme.text.secondary}>{TOOL_STATUS.PENDING}</Text>;
default:
return <Text>{TOOL_STATUS.PENDING}</Text>;
}
};
return (
<Box
flexDirection="column"
borderStyle="round"
width={contentWidth}
borderDimColor={hasPending}
borderColor={borderColor}
gap={0}
>
{/* Status line: icon + tool name + description */}
<Box flexDirection="row">
<Box minWidth={STATUS_INDICATOR_WIDTH}>{renderStatusIcon()}</Box>
<Box flexGrow={1}>
<Text wrap="truncate-end">
<Text bold>{activeTool.name}</Text>
{activeToolDescription ? (
<Text color={theme.text.secondary}>
{' '}
{activeToolDescription}
</Text>
) : null}
</Text>
</Box>
</Box>
{/* Hint line */}
<Text color={theme.text.secondary}>
{t('Press Ctrl+O to show full tool output')}
</Text>
</Box>
);
};