({
onSubmit,
isWaitingForResponse,
isCompacting = false,
inputMode,
onInterrupt,
assistant,
wasInterrupted = false,
onFileAttached,
disabled = false,
placeholder,
hideNormalUI = false,
isRemoteMode = false,
onImageInClipboardChange,
onShowEditSelector,
})
| 116 | } |
| 117 | |
| 118 | const UserInput: React.FC<UserInputProps> = ({ |
| 119 | onSubmit, |
| 120 | isWaitingForResponse, |
| 121 | isCompacting = false, |
| 122 | inputMode, |
| 123 | onInterrupt, |
| 124 | assistant, |
| 125 | wasInterrupted = false, |
| 126 | onFileAttached, |
| 127 | disabled = false, |
| 128 | placeholder, |
| 129 | hideNormalUI = false, |
| 130 | isRemoteMode = false, |
| 131 | onImageInClipboardChange, |
| 132 | onShowEditSelector, |
| 133 | }) => { |
| 134 | const [textBuffer] = useState(() => new TextBuffer()); |
| 135 | const [inputHistory] = useState(() => new InputHistory()); |
| 136 | const lastEscapePressRef = useRef<number>(0); |
| 137 | |
| 138 | // Stable callback for TextBuffer state changes to prevent race conditions |
| 139 | const onStateChange = useCallback(() => { |
| 140 | setInputText(textBuffer.text); |
| 141 | setCursorPosition(textBuffer.cursor); |
| 142 | }, [textBuffer]); |
| 143 | |
| 144 | // Set up callback for when TextBuffer state changes (e.g., rapid input finalized) |
| 145 | React.useEffect(() => { |
| 146 | textBuffer.setStateChangeCallback(onStateChange); |
| 147 | |
| 148 | // Cleanup on unmount: clear any pending timers |
| 149 | return () => { |
| 150 | textBuffer.clear(); |
| 151 | }; |
| 152 | }, [textBuffer, onStateChange]); |
| 153 | const [inputText, setInputText] = useState(""); |
| 154 | const [cursorPosition, setCursorPosition] = useState(0); |
| 155 | const [showSlashCommands, setShowSlashCommands] = useState(false); |
| 156 | |
| 157 | // Clear input function for Ctrl+C |
| 158 | const clearInput = useCallback(() => { |
| 159 | textBuffer.clear(); |
| 160 | setInputText(""); |
| 161 | setCursorPosition(0); |
| 162 | setShowSlashCommands(false); |
| 163 | setShowFileSearch(false); |
| 164 | setShowBashMode(false); |
| 165 | inputHistory.resetNavigation(); |
| 166 | }, [textBuffer]); |
| 167 | |
| 168 | const [slashCommandFilter, setSlashCommandFilter] = useState(""); |
| 169 | const [selectedCommandIndex, setSelectedCommandIndex] = useState(0); |
| 170 | const [showBashMode, setShowBashMode] = useState(false); |
| 171 | const [showFileSearch, setShowFileSearch] = useState(false); |
| 172 | const [fileSearchFilter, setFileSearchFilter] = useState(""); |
| 173 | const [selectedFileIndex, setSelectedFileIndex] = useState(0); |
| 174 | const [currentFiles, setCurrentFiles] = useState< |
| 175 | Array<{ path: string; displayName: string }> |
nothing calls this directly
no test coverage detected