({
chatHistory,
onEdit,
onExit,
}: EditMessageSelectorProps)
| 17 | } |
| 18 | |
| 19 | export function EditMessageSelector({ |
| 20 | chatHistory, |
| 21 | onEdit, |
| 22 | onExit, |
| 23 | }: EditMessageSelectorProps) { |
| 24 | // Filter to only show user messages |
| 25 | const userMessages = useMemo(() => { |
| 26 | return chatHistory |
| 27 | .map((item, originalIndex) => ({ item, originalIndex })) |
| 28 | .filter(({ item }) => item.message.role === "user"); |
| 29 | }, [chatHistory]); |
| 30 | |
| 31 | const [selectedIndex, setSelectedIndex] = useState( |
| 32 | Math.max(0, userMessages.length - 1), |
| 33 | ); |
| 34 | const [isEditing, setIsEditing] = useState(false); |
| 35 | const [textBuffer] = useState(() => new TextBuffer()); |
| 36 | const [editText, setEditText] = useState(""); |
| 37 | const [cursorPosition, setCursorPosition] = useState(0); |
| 38 | |
| 39 | // Stable callback for TextBuffer state changes (e.g., paste finalization) |
| 40 | const onStateChange = React.useCallback(() => { |
| 41 | setEditText(textBuffer.text); |
| 42 | setCursorPosition(textBuffer.cursor); |
| 43 | }, [textBuffer]); |
| 44 | |
| 45 | // Set up callback for when TextBuffer state changes asynchronously |
| 46 | React.useEffect(() => { |
| 47 | textBuffer.setStateChangeCallback(onStateChange); |
| 48 | |
| 49 | return () => { |
| 50 | // Clear any pending timers on unmount |
| 51 | textBuffer.clear(); |
| 52 | }; |
| 53 | }, [textBuffer, onStateChange]); |
| 54 | |
| 55 | // Initialize edit text when selection changes |
| 56 | React.useEffect(() => { |
| 57 | if (!isEditing && userMessages[selectedIndex]) { |
| 58 | const content = |
| 59 | typeof userMessages[selectedIndex].item.message.content === "string" |
| 60 | ? userMessages[selectedIndex].item.message.content |
| 61 | : ""; |
| 62 | textBuffer.setText(content); |
| 63 | setEditText(content); |
| 64 | setCursorPosition(content.length); |
| 65 | } |
| 66 | }, [selectedIndex, isEditing, userMessages, textBuffer]); |
| 67 | |
| 68 | // Helper to get message content safely |
| 69 | const getMessageContent = React.useCallback( |
| 70 | (index: number): string => { |
| 71 | return typeof userMessages[index]?.item.message.content === "string" |
| 72 | ? userMessages[index].item.message.content |
| 73 | : ""; |
| 74 | }, |
| 75 | [userMessages], |
| 76 | ); |
nothing calls this directly
no test coverage detected