({
messages,
onPreRestore,
onRestoreMessage,
onRestoreCode,
onSummarize,
onClose,
preselectedMessage
}: Props)
| 44 | }; |
| 45 | const MAX_VISIBLE_MESSAGES = 7; |
| 46 | export function MessageSelector({ |
| 47 | messages, |
| 48 | onPreRestore, |
| 49 | onRestoreMessage, |
| 50 | onRestoreCode, |
| 51 | onSummarize, |
| 52 | onClose, |
| 53 | preselectedMessage |
| 54 | }: Props): React.ReactNode { |
| 55 | const fileHistory = useAppState(s => s.fileHistory); |
| 56 | const [error, setError] = useState<string | undefined>(undefined); |
| 57 | const isFileHistoryEnabled = fileHistoryEnabled(); |
| 58 | |
| 59 | // Add current prompt as a virtual message |
| 60 | const currentUUID = useMemo(randomUUID, []); |
| 61 | const messageOptions = useMemo(() => [...messages.filter(selectableUserMessagesFilter), { |
| 62 | ...createUserMessage({ |
| 63 | content: '' |
| 64 | }), |
| 65 | uuid: currentUUID |
| 66 | } as UserMessage], [messages, currentUUID]); |
| 67 | const [selectedIndex, setSelectedIndex] = useState(messageOptions.length - 1); |
| 68 | |
| 69 | // Orient the selected message as the middle of the visible options |
| 70 | const firstVisibleIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_MESSAGES / 2), messageOptions.length - MAX_VISIBLE_MESSAGES)); |
| 71 | const hasMessagesToSelect = messageOptions.length > 1; |
| 72 | const [messageToRestore, setMessageToRestore] = useState<UserMessage | undefined>(preselectedMessage); |
| 73 | const [diffStatsForRestore, setDiffStatsForRestore] = useState<DiffStats | undefined>(undefined); |
| 74 | useEffect(() => { |
| 75 | if (!preselectedMessage || !isFileHistoryEnabled) return; |
| 76 | let cancelled = false; |
| 77 | void fileHistoryGetDiffStats(fileHistory, preselectedMessage.uuid).then(stats => { |
| 78 | if (!cancelled) setDiffStatsForRestore(stats); |
| 79 | }); |
| 80 | return () => { |
| 81 | cancelled = true; |
| 82 | }; |
| 83 | }, [preselectedMessage, isFileHistoryEnabled, fileHistory]); |
| 84 | const [isRestoring, setIsRestoring] = useState(false); |
| 85 | const [restoringOption, setRestoringOption] = useState<RestoreOption | null>(null); |
| 86 | const [selectedRestoreOption, setSelectedRestoreOption] = useState<RestoreOption>('both'); |
| 87 | // Per-option feedback state; Select's internal inputValues Map persists |
| 88 | // per-option text independently, so sharing one variable would desync. |
| 89 | const [summarizeFromFeedback, setSummarizeFromFeedback] = useState(''); |
| 90 | const [summarizeUpToFeedback, setSummarizeUpToFeedback] = useState(''); |
| 91 | |
| 92 | // Generate options with summarize as input type for inline context |
| 93 | function getRestoreOptions(canRestoreCode: boolean): OptionWithDescription<RestoreOption>[] { |
| 94 | const baseOptions: OptionWithDescription<RestoreOption>[] = canRestoreCode ? [{ |
| 95 | value: 'both', |
| 96 | label: 'Restore code and conversation' |
| 97 | }, { |
| 98 | value: 'conversation', |
| 99 | label: 'Restore conversation' |
| 100 | }, { |
| 101 | value: 'code', |
| 102 | label: 'Restore code' |
| 103 | }] : [{ |
nothing calls this directly
no test coverage detected