Merge branch 'main' into feat/multimodal-input-support

This commit is contained in:
tanzhenxin 2026-01-21 19:49:31 +08:00
commit 2ec3ec2c38
159 changed files with 4391 additions and 3303 deletions

View file

@ -12,7 +12,7 @@ import { renderWithProviders } from '../../test-utils/render.js';
describe('<MarkdownDisplay />', () => {
const baseProps = {
isPending: false,
terminalWidth: 80,
contentWidth: 80,
availableTerminalHeight: 40,
};

View file

@ -16,7 +16,7 @@ interface MarkdownDisplayProps {
text: string;
isPending: boolean;
availableTerminalHeight?: number;
terminalWidth: number;
contentWidth: number;
textColor?: string;
}
@ -31,7 +31,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
text,
isPending,
availableTerminalHeight,
terminalWidth,
contentWidth,
textColor = theme.text.primary,
}) => {
if (!text) return <></>;
@ -79,7 +79,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
lang={codeBlockLang}
isPending={isPending}
availableTerminalHeight={availableTerminalHeight}
terminalWidth={terminalWidth}
contentWidth={contentWidth}
/>,
);
inCodeBlock = false;
@ -144,7 +144,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
key={`table-${contentBlocks.length}`}
headers={tableHeaders}
rows={tableRows}
terminalWidth={terminalWidth}
contentWidth={contentWidth}
/>,
);
}
@ -266,7 +266,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
lang={codeBlockLang}
isPending={isPending}
availableTerminalHeight={availableTerminalHeight}
terminalWidth={terminalWidth}
contentWidth={contentWidth}
/>,
);
}
@ -278,7 +278,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
key={`table-${contentBlocks.length}`}
headers={tableHeaders}
rows={tableRows}
terminalWidth={terminalWidth}
contentWidth={contentWidth}
/>,
);
}
@ -293,7 +293,7 @@ interface RenderCodeBlockProps {
lang: string | null;
isPending: boolean;
availableTerminalHeight?: number;
terminalWidth: number;
contentWidth: number;
}
const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
@ -301,7 +301,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
lang,
isPending,
availableTerminalHeight,
terminalWidth,
contentWidth,
}) => {
const settings = useSettings();
const MIN_LINES_FOR_MESSAGE = 1; // Minimum lines to show before the "generating more" message
@ -329,7 +329,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
truncatedContent.join('\n'),
lang,
availableTerminalHeight,
terminalWidth - CODE_BLOCK_PREFIX_PADDING,
contentWidth - CODE_BLOCK_PREFIX_PADDING,
undefined,
settings,
);
@ -347,7 +347,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
fullContent,
lang,
availableTerminalHeight,
terminalWidth - CODE_BLOCK_PREFIX_PADDING,
contentWidth - CODE_BLOCK_PREFIX_PADDING,
undefined,
settings,
);
@ -356,7 +356,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
<Box
paddingLeft={CODE_BLOCK_PREFIX_PADDING}
flexDirection="column"
width={terminalWidth}
width={contentWidth}
flexShrink={0}
>
{colorizedCode}
@ -407,15 +407,15 @@ const RenderListItem = React.memo(RenderListItemInternal);
interface RenderTableProps {
headers: string[];
rows: string[][];
terminalWidth: number;
contentWidth: number;
}
const RenderTableInternal: React.FC<RenderTableProps> = ({
headers,
rows,
terminalWidth,
contentWidth,
}) => (
<TableRenderer headers={headers} rows={rows} terminalWidth={terminalWidth} />
<TableRenderer headers={headers} rows={rows} contentWidth={contentWidth} />
);
const RenderTable = React.memo(RenderTableInternal);

View file

@ -12,7 +12,7 @@ import { RenderInline, getPlainTextLength } from './InlineMarkdownRenderer.js';
interface TableRendererProps {
headers: string[];
rows: string[][];
terminalWidth: number;
contentWidth: number;
}
/**
@ -22,7 +22,7 @@ interface TableRendererProps {
export const TableRenderer: React.FC<TableRendererProps> = ({
headers,
rows,
terminalWidth,
contentWidth,
}) => {
// Calculate column widths using actual display width after markdown processing
const columnWidths = headers.map((header, index) => {
@ -35,8 +35,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
// Ensure table fits within terminal width
const totalWidth = columnWidths.reduce((sum, width) => sum + width + 1, 1);
const scaleFactor =
totalWidth > terminalWidth ? terminalWidth / totalWidth : 1;
const scaleFactor = totalWidth > contentWidth ? contentWidth / totalWidth : 1;
const adjustedWidths = columnWidths.map((width) =>
Math.floor(width * scaleFactor),
);

View file

@ -8,6 +8,8 @@ import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { execCommand } from '@qwen-code/qwen-code-core';
const MACOS_CLIPBOARD_TIMEOUT_MS = 1500;
/**
* Checks if the system clipboard contains an image (macOS only for now)
* @returns true if clipboard contains an image
@ -19,7 +21,13 @@ export async function clipboardHasImage(): Promise<boolean> {
try {
// Use osascript to check clipboard type
const { stdout } = await execCommand('osascript', ['-e', 'clipboard info']);
const { stdout } = await execCommand(
'osascript',
['-e', 'clipboard info'],
{
timeout: MACOS_CLIPBOARD_TIMEOUT_MS,
},
);
const imageRegex =
/«class PNGf»|TIFF picture|JPEG picture|GIF picture|«class JPEG»|«class TIFF»/;
return imageRegex.test(stdout);
@ -80,7 +88,9 @@ export async function saveClipboardImage(
end try
`;
const { stdout } = await execCommand('osascript', ['-e', script]);
const { stdout } = await execCommand('osascript', ['-e', script], {
timeout: MACOS_CLIPBOARD_TIMEOUT_MS,
});
if (stdout.trim() === 'success') {
// Verify the file was created and has content