Merge tag 'v0.1.15' into feature/yiheng/sync-gemini-cli-0.1.15

This commit is contained in:
奕桁 2025-08-01 23:06:11 +08:00
commit b69b2ce376
340 changed files with 36528 additions and 22931 deletions

View file

@ -44,6 +44,7 @@ index 0000000..e69de29
'python',
undefined,
80,
undefined,
);
});
@ -71,6 +72,7 @@ index 0000000..e69de29
null,
undefined,
80,
undefined,
);
});
@ -94,6 +96,7 @@ index 0000000..e69de29
null,
undefined,
80,
undefined,
);
});
@ -127,8 +130,8 @@ index 0000001..0000002 100644
);
const output = lastFrame();
const lines = output!.split('\n');
expect(lines[0]).toBe('1 - old line');
expect(lines[1]).toBe('1 + new line');
expect(lines[0]).toBe('1 - old line');
expect(lines[1]).toBe('1 + new line');
});
it('should handle diff with only header and no changes', () => {
@ -250,35 +253,35 @@ index 123..789 100644
{
terminalWidth: 80,
height: undefined,
expected: `1 console.log('first hunk');
2 - const oldVar = 1;
2 + const newVar = 1;
3 console.log('end of first hunk');
expected: ` 1 console.log('first hunk');
2 - const oldVar = 1;
2 + const newVar = 1;
3 console.log('end of first hunk');
20 console.log('second hunk');
21 - const anotherOld = 'test';
21 + const anotherNew = 'test';
22 console.log('end of second hunk');`,
20 console.log('second hunk');
21 - const anotherOld = 'test';
21 + const anotherNew = 'test';
22 console.log('end of second hunk');`,
},
{
terminalWidth: 80,
height: 6,
expected: `... first 4 lines hidden ...
20 console.log('second hunk');
21 - const anotherOld = 'test';
21 + const anotherNew = 'test';
22 console.log('end of second hunk');`,
20 console.log('second hunk');
21 - const anotherOld = 'test';
21 + const anotherNew = 'test';
22 console.log('end of second hunk');`,
},
{
terminalWidth: 30,
height: 6,
expected: `... first 10 lines hidden ...
'test';
21 + const anotherNew =
'test';
22 console.log('end of
second hunk');`,
;
21 + const anotherNew = 'test'
;
22 console.log('end of
second hunk');`,
},
])(
'with terminalWidth $terminalWidth and height $height',
@ -326,11 +329,11 @@ fileDiff Index: file.txt
);
const output = lastFrame();
expect(output).toEqual(`1 - const oldVar = 1;
1 + const newVar = 1;
expect(output).toEqual(` 1 - const oldVar = 1;
1 + const newVar = 1;
20 - const anotherOld = 'test';
20 + const anotherNew = 'test';`);
20 - const anotherOld = 'test';
20 + const anotherNew = 'test';`);
});
it('should correctly render a new file with no file extension correctly', () => {

View file

@ -8,7 +8,7 @@ import React from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../../colors.js';
import crypto from 'crypto';
import { colorizeCode } from '../../utils/CodeColorizer.js';
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
interface DiffLine {
@ -93,6 +93,7 @@ interface DiffRendererProps {
tabWidth?: number;
availableTerminalHeight?: number;
terminalWidth: number;
theme?: import('../../themes/theme.js').Theme;
}
const DEFAULT_TAB_WIDTH = 4; // Spaces per tab for normalization
@ -103,6 +104,7 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
tabWidth = DEFAULT_TAB_WIDTH,
availableTerminalHeight,
terminalWidth,
theme,
}) => {
if (!diffContent || typeof diffContent !== 'string') {
return <Text color={Colors.AccentYellow}>No diff content.</Text>;
@ -146,6 +148,7 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
language,
availableTerminalHeight,
terminalWidth,
theme,
);
} else {
renderedOutput = renderDiffContent(
@ -186,6 +189,18 @@ const renderDiffContent = (
);
}
const maxLineNumber = Math.max(
0,
...displayableLines.map((l) => l.oldLine ?? 0),
...displayableLines.map((l) => l.newLine ?? 0),
);
const gutterWidth = Math.max(1, maxLineNumber.toString().length);
const fileExtension = filename?.split('.').pop() || null;
const language = fileExtension
? getLanguageFromExtension(fileExtension)
: null;
// Calculate the minimum indentation across all displayable lines
let baseIndentation = Infinity; // Start high to find the minimum
for (const line of displayableLines) {
@ -232,27 +247,25 @@ const renderDiffContent = (
) {
acc.push(
<Box key={`gap-${index}`}>
<Text wrap="truncate">{'═'.repeat(terminalWidth)}</Text>
<Text wrap="truncate" color={Colors.Gray}>
{'═'.repeat(terminalWidth)}
</Text>
</Box>,
);
}
const lineKey = `diff-line-${index}`;
let gutterNumStr = '';
let color: string | undefined = undefined;
let prefixSymbol = ' ';
let dim = false;
switch (line.type) {
case 'add':
gutterNumStr = (line.newLine ?? '').toString();
color = 'green';
prefixSymbol = '+';
lastLineNumber = line.newLine ?? null;
break;
case 'del':
gutterNumStr = (line.oldLine ?? '').toString();
color = 'red';
prefixSymbol = '-';
// For deletions, update lastLineNumber based on oldLine if it's advancing.
// This helps manage gaps correctly if there are multiple consecutive deletions
@ -263,7 +276,6 @@ const renderDiffContent = (
break;
case 'context':
gutterNumStr = (line.newLine ?? '').toString();
dim = true;
prefixSymbol = ' ';
lastLineNumber = line.newLine ?? null;
break;
@ -275,13 +287,26 @@ const renderDiffContent = (
acc.push(
<Box key={lineKey} flexDirection="row">
<Text color={Colors.Gray}>{gutterNumStr.padEnd(4)} </Text>
<Text color={color} dimColor={dim}>
{prefixSymbol}{' '}
</Text>
<Text color={color} dimColor={dim} wrap="wrap">
{displayContent}
<Text color={Colors.Gray}>
{gutterNumStr.padStart(gutterWidth)}{' '}
</Text>
{line.type === 'context' ? (
<>
<Text>{prefixSymbol} </Text>
<Text wrap="wrap">
{colorizeLine(displayContent, language)}
</Text>
</>
) : (
<Text
backgroundColor={
line.type === 'add' ? Colors.DiffAdded : Colors.DiffRemoved
}
wrap="wrap"
>
{prefixSymbol} {colorizeLine(displayContent, language)}
</Text>
)}
</Box>,
);
return acc;

View file

@ -132,19 +132,20 @@ export const ToolConfirmationMessage: React.FC<
const executionProps =
confirmationDetails as ToolExecuteConfirmationDetails;
question = `Allow execution?`;
question = `Allow execution of: '${executionProps.rootCommand}'?`;
options.push(
{
label: 'Yes, allow once',
label: `Yes, allow once`,
value: ToolConfirmationOutcome.ProceedOnce,
},
{
label: `Yes, allow always "${executionProps.rootCommand} ..."`,
label: `Yes, allow always ...`,
value: ToolConfirmationOutcome.ProceedAlways,
},
{ label: 'No (esc)', value: ToolConfirmationOutcome.Cancel },
);
options.push({ label: 'No (esc)', value: ToolConfirmationOutcome.Cancel });
let bodyContentHeight = availableBodyContentHeight();
if (bodyContentHeight !== undefined) {
bodyContentHeight -= 2; // Account for padding;

View file

@ -11,6 +11,7 @@ import { ToolMessage } from './ToolMessage.js';
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
import { Colors } from '../../colors.js';
import { Config } from '@qwen-code/qwen-code-core';
import { SHELL_COMMAND_NAME } from '../../constants.js';
interface ToolGroupMessageProps {
groupId: number;
@ -32,7 +33,9 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
const hasPending = !toolCalls.every(
(t) => t.status === ToolCallStatus.Success,
);
const borderColor = hasPending ? Colors.AccentYellow : Colors.Gray;
const isShellCommand = toolCalls.some((t) => t.name === SHELL_COMMAND_NAME);
const borderColor =
hasPending || isShellCommand ? Colors.AccentYellow : Colors.Gray;
const staticHeight = /* border */ 2 + /* marginBottom */ 1;
// This is a bit of a magic number, but it accounts for the border and

View file

@ -152,6 +152,8 @@ describe('<ToolMessage />', () => {
const diffResult = {
fileDiff: '--- a/file.txt\n+++ b/file.txt\n@@ -1 +1 @@\n-old\n+new',
fileName: 'file.txt',
originalContent: 'old',
newContent: 'new',
};
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} resultDisplay={diffResult} />,