mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
Adding in a history buffer (#38)
Up and down arrows traverse the command history.
This commit is contained in:
@@ -112,7 +112,7 @@ export const useGeminiStream = (
|
||||
|
||||
const maybeCommand = trimmedQuery.split(/\s+/)[0];
|
||||
if (allowlistedCommands.includes(maybeCommand)) {
|
||||
exec(trimmedQuery, (error, stdout, stderr) => {
|
||||
exec(trimmedQuery, (error, stdout) => {
|
||||
const timestamp = getNextMessageId(userMessageTimestamp);
|
||||
// TODO: handle stderr, error
|
||||
addHistoryItem(
|
||||
|
||||
125
packages/cli/src/ui/hooks/useInputHistory.ts
Normal file
125
packages/cli/src/ui/hooks/useInputHistory.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useInput } from 'ink';
|
||||
|
||||
// Props for the hook
|
||||
interface UseInputHistoryProps {
|
||||
userMessages: readonly string[]; // History of user messages
|
||||
onSubmit: (value: string) => void; // Original submit function from App
|
||||
isActive: boolean; // To enable/disable the useInput hook
|
||||
}
|
||||
|
||||
// Return type of the hook
|
||||
interface UseInputHistoryReturn {
|
||||
query: string; // The current input query managed by the hook
|
||||
setQuery: React.Dispatch<React.SetStateAction<string>>; // Setter for the query
|
||||
handleSubmit: (value: string) => void; // Wrapped submit handler
|
||||
inputKey: number; // Key to force input reset
|
||||
}
|
||||
|
||||
export function useInputHistory({
|
||||
userMessages,
|
||||
onSubmit,
|
||||
isActive,
|
||||
}: UseInputHistoryProps): UseInputHistoryReturn {
|
||||
const [query, setQuery] = useState(''); // Hook manages its own query state
|
||||
const [historyIndex, setHistoryIndex] = useState<number>(-1); // -1 means current query
|
||||
const [originalQueryBeforeNav, setOriginalQueryBeforeNav] =
|
||||
useState<string>('');
|
||||
const [inputKey, setInputKey] = useState<number>(0); // Key for forcing input reset
|
||||
|
||||
// Function to reset navigation state, called on submit or manual reset
|
||||
const resetHistoryNav = useCallback(() => {
|
||||
setHistoryIndex(-1);
|
||||
setOriginalQueryBeforeNav('');
|
||||
}, []);
|
||||
|
||||
// Wrapper for the onSubmit prop to include resetting history navigation
|
||||
const handleSubmit = useCallback(
|
||||
(value: string) => {
|
||||
const trimmedValue = value.trim();
|
||||
if (trimmedValue) {
|
||||
// Only submit non-empty values
|
||||
onSubmit(trimmedValue); // Call the original submit function
|
||||
}
|
||||
setQuery(''); // Clear the input field managed by this hook
|
||||
resetHistoryNav(); // Reset history state
|
||||
// Don't increment inputKey here, only on nav changes
|
||||
},
|
||||
[onSubmit, resetHistoryNav],
|
||||
);
|
||||
|
||||
useInput(
|
||||
(input, key) => {
|
||||
// Do nothing if the hook is not active
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
let didNavigate = false;
|
||||
|
||||
if (key.upArrow) {
|
||||
if (userMessages.length === 0) return;
|
||||
|
||||
let nextIndex = historyIndex;
|
||||
if (historyIndex === -1) {
|
||||
// Starting navigation UP, save current input
|
||||
setOriginalQueryBeforeNav(query);
|
||||
nextIndex = 0; // Go to the most recent item (index 0 in reversed view)
|
||||
} else if (historyIndex < userMessages.length - 1) {
|
||||
// Continue navigating UP (towards older items)
|
||||
nextIndex = historyIndex + 1;
|
||||
} else {
|
||||
return; // Already at the oldest item
|
||||
}
|
||||
|
||||
if (nextIndex !== historyIndex) {
|
||||
setHistoryIndex(nextIndex);
|
||||
// History is ordered newest to oldest, so access from the end
|
||||
const newValue = userMessages[userMessages.length - 1 - nextIndex];
|
||||
setQuery(newValue);
|
||||
setInputKey((k) => k + 1); // Increment key on navigation change
|
||||
didNavigate = true;
|
||||
}
|
||||
} else if (key.downArrow) {
|
||||
if (historyIndex === -1) return; // Already at the bottom (current input)
|
||||
|
||||
const nextIndex = historyIndex - 1; // Move towards more recent items / current input
|
||||
setHistoryIndex(nextIndex);
|
||||
|
||||
if (nextIndex === -1) {
|
||||
// Restore original query
|
||||
setQuery(originalQueryBeforeNav);
|
||||
} else {
|
||||
// Set query based on reversed index
|
||||
const newValue = userMessages[userMessages.length - 1 - nextIndex];
|
||||
setQuery(newValue);
|
||||
}
|
||||
setInputKey((k) => k + 1); // Increment key on navigation change
|
||||
didNavigate = true;
|
||||
} else {
|
||||
// If user types anything other than arrows while navigating, reset history navigation state
|
||||
if (historyIndex !== -1 && !didNavigate) {
|
||||
// Check if it's a key that modifies input content
|
||||
if (input || key.backspace || key.delete) {
|
||||
resetHistoryNav();
|
||||
// The actual query state update for typing is handled by the component's onChange calling setQuery
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ isActive }, // Pass isActive to useInput
|
||||
);
|
||||
|
||||
return {
|
||||
query,
|
||||
setQuery, // Return the hook's setQuery
|
||||
handleSubmit, // Return the wrapped submit handler
|
||||
inputKey, // Return the key
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user