Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(user-input): extract adoptDomValue; drop flushSync pre-paint…
… reconcile
  • Loading branch information
waleedlatif1 committed Jun 7, 2026
commit 5b027985ea6f6e0dd2de34eb7d8a32e1c767d7af
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,16 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
}
}, [onSubmit, textareaRef, resetTranscript])

/**
* Adopts the textarea's DOM value into state. State and DOM can only drift
* when an edit's input event is lost (programmatic edits pair setValue
* synchronously) — the DOM is the user's intent.
*/
const adoptDomValue = useCallback((textarea: HTMLTextAreaElement) => {
valueRef.current = textarea.value
setValue(textarea.value)
}, [])

const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (mentionRangeRef.current && !e.nativeEvent.isComposing) {
Expand Down Expand Up @@ -1060,13 +1070,10 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
// may get superseded — so edge-movement inference stays true to the DOM.
lastSelectionRef.current = { start, end }

// Controlled-value reconciliation: state and DOM can only drift when an
// edit's change event is lost (programmatic edits pair setValue
// synchronously). The DOM is the user's intent — adopt it and let the
// render rebuild the overlay; selection logic resumes on the next event.
// Reconciliation backstop for non-keyboard mutations; the render rebuilds
// the overlay and selection logic resumes on the next event.
if (textarea.value !== valueRef.current) {
valueRef.current = textarea.value
setValue(textarea.value)
adoptDomValue(textarea)
return
}

Expand Down Expand Up @@ -1117,7 +1124,7 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
const focusPos = textarea.selectionDirection === 'backward' ? start : end
syncMentionState(textarea, textarea.value, focusPos)
syncSlashState(textarea, textarea.value, focusPos)
}, [textareaRef, mentionTokensWithContext, syncMentionState, syncSlashState])
}, [textareaRef, mentionTokensWithContext, adoptDomValue, syncMentionState, syncSlashState])

const handleInput = useCallback(
(e: React.FormEvent<HTMLTextAreaElement>) => {
Expand Down
Loading