fix(tui): align prompt with status bar and capture full input row

Drop the leading prompt column from 3 to 2 so the input first character lines up with the status bar text. Wrap the prompt+input row in a single mouse-capture box and stop event propagation from TextInput's own handlers so any drag in that row delegates to composer selection without leaking to terminal-level selection.

Made-with: Cursor
This commit is contained in:
Brooklyn Nicholson
2026-04-27 17:26:50 -05:00
parent da22b28bac
commit 96df9c87c4
2 changed files with 30 additions and 12 deletions

View File

@@ -124,10 +124,11 @@ const ComposerPane = memo(function ComposerPane({
const ui = useStore($uiState)
const isBlocked = useStore($isBlocked)
const sh = (composer.inputBuf[0] ?? composer.input).startsWith('!')
const pw = sh ? 2 : 3
const pw = 2
const inputColumns = stableComposerColumns(composer.cols, pw)
const inputHeight = inputVisualHeight(composer.input, inputColumns)
const inputMouseRef = useRef<null | TextInputMouseApi>(null)
const captureInputDrag = (e: { button: number; localCol?: number; localRow?: number; stopImmediatePropagation?: () => void }) => {
if (e.button !== 0) {
return
@@ -136,6 +137,7 @@ const ComposerPane = memo(function ComposerPane({
e.stopImmediatePropagation?.()
inputMouseRef.current?.startAtBeginning()
}
const dragIntoInput = (e: { button: number; localCol?: number; localRow?: number; stopImmediatePropagation?: () => void }) => {
if (e.button !== 0) {
return
@@ -201,7 +203,7 @@ const ComposerPane = memo(function ComposerPane({
<>
{composer.inputBuf.map((line, i) => (
<Box key={i}>
<Box width={3}>
<Box width={2}>
<Text color={ui.theme.color.dim}>{i === 0 ? `${ui.theme.brand.prompt} ` : ' '}</Text>
</Box>
@@ -209,13 +211,13 @@ const ComposerPane = memo(function ComposerPane({
</Box>
))}
<Box position="relative">
<Box
onMouseDown={captureInputDrag}
onMouseDrag={dragIntoInput}
onMouseUp={() => inputMouseRef.current?.end()}
width={pw}
>
<Box
onMouseDown={captureInputDrag}
onMouseDrag={dragIntoInput}
onMouseUp={() => inputMouseRef.current?.end()}
position="relative"
>
<Box width={pw}>
{sh ? (
<Text color={ui.theme.color.shellDollar}>$ </Text>
) : (

View File

@@ -974,7 +974,12 @@ export function TextInput({
setCur(next)
curRef.current = next
}}
onMouseDown={(e: { button: number; localCol?: number; localRow?: number }) => {
onMouseDown={(e: {
button: number
localCol?: number
localRow?: number
stopImmediatePropagation?: () => void
}) => {
if (!focus) {
return
}
@@ -982,6 +987,7 @@ export function TextInput({
// Right-click to paste: route through the same hotkey path as
// Alt+V so the composer's clipboard RPC (text or image) handles it.
if (e.button === 2) {
e.stopImmediatePropagation?.()
emitPaste({ cursor: curRef.current, hotkey: true, text: '', value: vRef.current })
return
@@ -991,20 +997,30 @@ export function TextInput({
return
}
e.stopImmediatePropagation?.()
const pos = mouseOffset(e)
const next = offsetFromPosition(display, pos.row, pos.col, columns)
startMouseSelection(next)
}}
onMouseDrag={(e: { button: number; localCol?: number; localRow?: number }) => {
onMouseDrag={(e: {
button: number
localCol?: number
localRow?: number
stopImmediatePropagation?: () => void
}) => {
if (!focus || e.button !== 0 || mouseAnchorRef.current === null) {
return
}
e.stopImmediatePropagation?.()
const pos = mouseOffset(e)
const next = offsetFromPosition(display, pos.row, pos.col, columns)
dragMouseSelection(next)
}}
onMouseUp={endMouseSelection}
onMouseUp={(e: { stopImmediatePropagation?: () => void }) => {
e.stopImmediatePropagation?.()
endMouseSelection()
}}
marginLeft={capturePad ? -capturePad : undefined}
paddingLeft={capturePad || undefined}
ref={boxRef}