Skip to content

Commit 5171b33

Browse files
committed
fix: prevent stale closure in handlePaste for rapid paste events
Use refs to track latest value and cursorPosition instead of relying on closure-captured values. This fixes an issue where only the last character would be pasted when multiple paste events fire rapidly before React has a chance to re-render. The refs are also updated synchronously in the handler so subsequent events see the updated state.
1 parent b2e7516 commit 5171b33

File tree

1 file changed

+24
-3
lines changed

1 file changed

+24
-3
lines changed

cli/src/components/multiline-input.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ export const MultilineInput = forwardRef<
136136
const [measuredCols, setMeasuredCols] = useState<number | null>(null)
137137
const [lastActivity, setLastActivity] = useState(Date.now())
138138

139+
// Refs to track latest values for paste handler (prevents stale closure issues)
140+
const valueRef = useRef(value)
141+
const cursorPositionRef = useRef(cursorPosition)
142+
143+
// Keep refs in sync with props
144+
useEffect(() => {
145+
valueRef.current = value
146+
cursorPositionRef.current = cursorPosition
147+
}, [value, cursorPosition])
148+
139149
// Update last activity on value or cursor changes
140150
useEffect(() => {
141151
setLastActivity(Date.now())
@@ -170,15 +180,26 @@ export const MultilineInput = forwardRef<
170180
const text = event.text ?? ''
171181
if (!text) return
172182

183+
// Use refs to get the latest values, avoiding stale closure issues
184+
// when multiple paste events fire rapidly before React re-renders
185+
const currentValue = valueRef.current
186+
const currentCursor = cursorPositionRef.current
187+
173188
const newValue =
174-
value.slice(0, cursorPosition) + text + value.slice(cursorPosition)
189+
currentValue.slice(0, currentCursor) + text + currentValue.slice(currentCursor)
190+
const newCursor = currentCursor + text.length
191+
192+
// Update refs immediately so subsequent rapid events see the new state
193+
valueRef.current = newValue
194+
cursorPositionRef.current = newCursor
195+
175196
onChange({
176197
text: newValue,
177-
cursorPosition: cursorPosition + text.length,
198+
cursorPosition: newCursor,
178199
lastEditDueToNav: false,
179200
})
180201
},
181-
[focused, value, cursorPosition, onChange],
202+
[focused, onChange],
182203
)
183204

184205
const cursorRow = lineInfo

0 commit comments

Comments
 (0)