diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index cc440392..cda748f4 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from 'react'; -import { Box, Text } from 'ink'; +import React, { useState, useEffect, useMemo } from 'react'; +import { Box, Text, useInput } from 'ink'; import fs from 'fs'; import path from 'path'; import os from 'os'; @@ -25,11 +25,21 @@ const App = ({ directory }: AppProps) => { const [query, setQuery] = useState(''); const [history, setHistory] = useState([]); const [startupWarnings, setStartupWarnings] = useState([]); + const [historyIndex, setHistoryIndex] = useState(-1); + const [originalQueryBeforeNav, setOriginalQueryBeforeNav] = useState(''); const { streamingState, submitQuery, initError } = useGeminiStream(setHistory); const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState); + const userMessages = useMemo(() => { + return history + .filter((item): item is HistoryItem & { type: 'user'; text: string } => + item.type === 'user' && typeof item.text === 'string' && item.text.trim() !== '' + ) + .map(item => item.text); + }, [history]); + useEffect(() => { try { if (fs.existsSync(warningsFilePath)) { @@ -50,6 +60,8 @@ const App = ({ directory }: AppProps) => { }, []); const handleInputSubmit = (value: PartListUnion) => { + setHistoryIndex(-1); + setOriginalQueryBeforeNav(''); submitQuery(value) .then(() => { setQuery(''); @@ -84,6 +96,40 @@ const App = ({ directory }: AppProps) => { ); const isInputActive = streamingState === StreamingState.Idle && !initError; + useInput((input, key) => { + if (!isInputActive || isWaitingForToolConfirmation) { + return; + } + + if (key.upArrow) { + if (userMessages.length === 0) return; + if (historyIndex === -1) { + setOriginalQueryBeforeNav(query); + } + const nextIndex = Math.min(historyIndex + 1, userMessages.length - 1); + if (nextIndex !== historyIndex) { + setHistoryIndex(nextIndex); + setQuery(userMessages[userMessages.length - 1 - nextIndex]); + } + } else if (key.downArrow) { + if (historyIndex < 0) return; + const nextIndex = Math.max(historyIndex - 1, -1); + setHistoryIndex(nextIndex); + if (nextIndex === -1) { + setQuery(originalQueryBeforeNav); + } else { + setQuery(userMessages[userMessages.length - 1 - nextIndex]); + } + } else { + if (input || key.backspace || key.delete || key.leftArrow || key.rightArrow) { + if (historyIndex !== -1) { + setHistoryIndex(-1); + setOriginalQueryBeforeNav(''); + } + } + } + }, { isActive: isInputActive }); + return (