diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx index 3608f00e3d..4e7e10b34c 100644 --- a/packages/cli/src/ui/components/InputPrompt.test.tsx +++ b/packages/cli/src/ui/components/InputPrompt.test.tsx @@ -5332,6 +5332,34 @@ describe('InputPrompt', () => { }); }); }); + + describe('terminal buffer rendering', () => { + it('does not clip the last char of a visual line whose width equals inputWidth', async () => { + const fullLine = '1234567890'; // 10 chars, exactly props.inputWidth + props.inputWidth = 10; + props.suggestionsWidth = 10; + vi.spyOn(props.config, 'getUseTerminalBuffer').mockReturnValue(true); + mockBuffer.text = fullLine; + mockBuffer.lines = [fullLine]; + mockBuffer.allVisualLines = [fullLine]; + mockBuffer.viewportVisualLines = [fullLine]; + mockBuffer.visualToLogicalMap = [[0, 0]]; + mockBuffer.visualToTransformedMap = [0]; + mockBuffer.transformationsByLine = [[]]; + mockBuffer.cursor = [0, fullLine.length]; + mockBuffer.visualCursor = [0, fullLine.length]; + + const { lastFrame, unmount } = await renderWithProviders( + , + { uiActions }, + ); + + await waitFor(() => { + expect(clean(lastFrame())).toContain(fullLine); + }); + unmount(); + }); + }); }); function clean(str: string | undefined): string { diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 67fefe0656..cd37e56abd 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -93,6 +93,8 @@ import { useIsHelpDismissKey } from '../utils/shortcutsHelp.js'; import { useRepeatedKeyPress } from '../hooks/useRepeatedKeyPress.js'; import { useKeyMatchers } from '../hooks/useKeyMatchers.js'; +const SCROLLBAR_GUTTER_WIDTH = 1; + /** * Returns if the terminal can be trusted to handle paste events atomically * rather than potentially sending multiple paste events separated by line @@ -1868,7 +1870,7 @@ export const InputPrompt: React.FC = ({ ? `line-${item.absoluteVisualIdx}` : `ghost-${item.index}` } - width={inputWidth} + width={inputWidth + SCROLLBAR_GUTTER_WIDTH} backgroundColor={listBackgroundColor} containerHeight={Math.min( buffer.viewportHeight, diff --git a/packages/cli/src/ui/components/__snapshots__/InputPrompt-InputPrompt-Highlighting-and-Cursor-Display-single-line-scenarios-should-display-cursor-correctly-at-the-end-of-the-line-2.snap.svg b/packages/cli/src/ui/components/__snapshots__/InputPrompt-InputPrompt-Highlighting-and-Cursor-Display-single-line-scenarios-should-display-cursor-correctly-at-the-end-of-the-line-2.snap.svg new file mode 100644 index 0000000000..2f9e66a77a --- /dev/null +++ b/packages/cli/src/ui/components/__snapshots__/InputPrompt-InputPrompt-Highlighting-and-Cursor-Display-single-line-scenarios-should-display-cursor-correctly-at-the-end-of-the-line-2.snap.svg @@ -0,0 +1,11 @@ + + + + + ──────────────────────────────────────────────────────────────────────────────────────────────────── + > hello + ──────────────────────────────────────────────────────────────────────────────────────────────────── + + \ No newline at end of file diff --git a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap index db449ce4d7..04cba9385d 100644 --- a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap @@ -60,6 +60,12 @@ exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios > ────────────────────────────────────────────────────────────────────────────────────────────────────" `; +exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios > should display cursor correctly 'at the end of the line' 2`] = ` +"──────────────────────────────────────────────────────────────────────────────────────────────────── + > hello +────────────────────────────────────────────────────────────────────────────────────────────────────" +`; + exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios > should display cursor correctly 'for multi-byte unicode characters' 1`] = ` "──────────────────────────────────────────────────────────────────────────────────────────────────── > hello 👍 world @@ -168,6 +174,27 @@ exports[`InputPrompt > mouse interaction > should toggle paste expansion on doub " `; +exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 4`] = ` +"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + > [Pasted Text: 10 lines] +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +" +`; + +exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 5`] = ` +"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + > [Pasted Text: 10 lines] +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +" +`; + +exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 6`] = ` +"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + > [Pasted Text: 10 lines] +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +" +`; + exports[`InputPrompt > multiline rendering > should correctly render multiline input including blank lines 1`] = ` "──────────────────────────────────────────────────────────────────────────────────────────────────── > hello