mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-29 20:20:57 +00:00
fix(paste): move thresholds to module level and improve placeholder expansion
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
21ae35b221
commit
3b4b5d874d
2 changed files with 81 additions and 9 deletions
|
|
@ -2244,6 +2244,67 @@ describe('InputPrompt', () => {
|
|||
unmount();
|
||||
});
|
||||
|
||||
it('should expand same-size placeholders correctly when #2 appears first', async () => {
|
||||
const firstPaste = 'x'.repeat(1001);
|
||||
const secondPaste = 'y'.repeat(1001);
|
||||
|
||||
const { stdin, unmount } = renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await wait();
|
||||
|
||||
stdin.write(`\x1b[200~${firstPaste}\x1b[201~`);
|
||||
await wait();
|
||||
stdin.write(`\x1b[200~${secondPaste}\x1b[201~`);
|
||||
await wait();
|
||||
|
||||
mockBuffer.text =
|
||||
'[Pasted Content 1001 chars] #2\n[Pasted Content 1001 chars]';
|
||||
mockBuffer.lines = mockBuffer.text.split('\n');
|
||||
mockBuffer.cursor = [1, '[Pasted Content 1001 chars]'.length];
|
||||
|
||||
// Wait for paste protection to expire
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
expect(props.onSubmit).toHaveBeenCalledWith(
|
||||
`${secondPaste}\n${firstPaste}`,
|
||||
);
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should write expanded placeholder content to shell history', async () => {
|
||||
props.shellModeActive = true;
|
||||
const largeContent = 'x'.repeat(1001);
|
||||
mockBuffer.text = '[Pasted Content 1001 chars]';
|
||||
mockBuffer.lines = [mockBuffer.text];
|
||||
mockBuffer.cursor = [0, mockBuffer.text.length];
|
||||
|
||||
const { stdin, unmount } = renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await wait();
|
||||
|
||||
stdin.write(`\x1b[200~${largeContent}\x1b[201~`);
|
||||
await wait();
|
||||
|
||||
// Wait for paste protection to expire
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
expect(mockShellHistory.addCommandToHistory).toHaveBeenCalledWith(
|
||||
largeContent,
|
||||
);
|
||||
expect(props.onSubmit).toHaveBeenCalledWith(largeContent);
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should delete entire placeholder on backspace', async () => {
|
||||
const placeholderText = '[Pasted Content 1001 chars]';
|
||||
mockBuffer.text = placeholderText;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@ export const calculatePromptWidths = (terminalWidth: number) => {
|
|||
} as const;
|
||||
};
|
||||
|
||||
// Large paste placeholder thresholds
|
||||
const LARGE_PASTE_CHAR_THRESHOLD = 1000;
|
||||
const LARGE_PASTE_LINE_THRESHOLD = 10;
|
||||
|
||||
export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
buffer,
|
||||
onSubmit,
|
||||
|
|
@ -121,7 +125,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
const pasteTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// Large paste placeholder handling
|
||||
const LARGE_PASTE_CHAR_THRESHOLD = 1000;
|
||||
const [pendingPastes, setPendingPastes] = useState<Map<string, string>>(
|
||||
new Map(),
|
||||
);
|
||||
|
|
@ -255,16 +258,26 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
|
||||
const handleSubmitAndClear = useCallback(
|
||||
(submittedValue: string) => {
|
||||
if (shellModeActive) {
|
||||
shellHistory.addCommandToHistory(submittedValue);
|
||||
}
|
||||
// Expand any large paste placeholders to their full content before submitting
|
||||
let finalValue = submittedValue;
|
||||
if (pendingPastes.size > 0) {
|
||||
pendingPastes.forEach((fullContent, placeholder) => {
|
||||
finalValue = finalValue.replace(placeholder, fullContent);
|
||||
});
|
||||
const placeholders = Array.from(pendingPastes.keys()).sort(
|
||||
(a, b) => b.length - a.length,
|
||||
);
|
||||
const escapedPlaceholders = placeholders.map((placeholderValue) =>
|
||||
placeholderValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
||||
);
|
||||
const placeholderRegex = new RegExp(escapedPlaceholders.join('|'), 'g');
|
||||
finalValue = finalValue.replace(
|
||||
placeholderRegex,
|
||||
(matchedPlaceholder) =>
|
||||
pendingPastes.get(matchedPlaceholder) ?? matchedPlaceholder,
|
||||
);
|
||||
setPendingPastes(new Map());
|
||||
activePlaceholderIds.current.clear();
|
||||
}
|
||||
if (shellModeActive) {
|
||||
shellHistory.addCommandToHistory(finalValue);
|
||||
}
|
||||
// Clear the buffer *before* calling onSubmit to prevent potential re-submission
|
||||
// if onSubmit triggers a re-render while the buffer still holds the old value.
|
||||
|
|
@ -397,7 +410,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
const pasted = key.sequence.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
const charCount = [...pasted].length; // Proper Unicode char count
|
||||
const lineCount = pasted.split('\n').length;
|
||||
const LARGE_PASTE_LINE_THRESHOLD = 10;
|
||||
if (
|
||||
charCount > LARGE_PASTE_CHAR_THRESHOLD ||
|
||||
lineCount > LARGE_PASTE_LINE_THRESHOLD
|
||||
|
|
@ -852,7 +864,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
uiState,
|
||||
uiActions,
|
||||
pasteWorkaround,
|
||||
LARGE_PASTE_CHAR_THRESHOLD,
|
||||
nextLargePastePlaceholder,
|
||||
pendingPastes,
|
||||
parsePlaceholder,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue