diff options
Diffstat (limited to 'packages/cli/src/ui/components/InputPrompt.tsx')
| -rw-r--r-- | packages/cli/src/ui/components/InputPrompt.tsx | 67 |
1 files changed, 65 insertions, 2 deletions
diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 78b3b96b..7eb1905d 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState, useRef } from 'react'; import { Box, Text } from 'ink'; import { theme } from '../semantic-colors.js'; import { SuggestionsDisplay } from './SuggestionsDisplay.js'; @@ -41,6 +41,7 @@ export interface InputPromptProps { suggestionsWidth: number; shellModeActive: boolean; setShellModeActive: (value: boolean) => void; + onEscapePromptChange?: (showPrompt: boolean) => void; vimHandleInput?: (key: Key) => boolean; } @@ -58,9 +59,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ suggestionsWidth, shellModeActive, setShellModeActive, + onEscapePromptChange, vimHandleInput, }) => { const [justNavigatedHistory, setJustNavigatedHistory] = useState(false); + const [escPressCount, setEscPressCount] = useState(0); + const [showEscapePrompt, setShowEscapePrompt] = useState(false); + const escapeTimerRef = useRef<NodeJS.Timeout | null>(null); const [dirs, setDirs] = useState<readonly string[]>( config.getWorkspaceContext().getDirectories(), @@ -98,6 +103,32 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ const resetReverseSearchCompletionState = reverseSearchCompletion.resetCompletionState; + const resetEscapeState = useCallback(() => { + if (escapeTimerRef.current) { + clearTimeout(escapeTimerRef.current); + escapeTimerRef.current = null; + } + setEscPressCount(0); + setShowEscapePrompt(false); + }, []); + + // Notify parent component about escape prompt state changes + useEffect(() => { + if (onEscapePromptChange) { + onEscapePromptChange(showEscapePrompt); + } + }, [showEscapePrompt, onEscapePromptChange]); + + // Clear escape prompt timer on unmount + useEffect( + () => () => { + if (escapeTimerRef.current) { + clearTimeout(escapeTimerRef.current); + } + }, + [], + ); + const handleSubmitAndClear = useCallback( (submittedValue: string) => { if (shellModeActive) { @@ -212,6 +243,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ return; } + // Reset ESC count and hide prompt on any non-ESC key + if (key.name !== 'escape') { + if (escPressCount > 0 || showEscapePrompt) { + resetEscapeState(); + } + } + if ( key.sequence === '!' && buffer.text === '' && @@ -237,13 +275,36 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ } if (shellModeActive) { setShellModeActive(false); + resetEscapeState(); return; } if (completion.showSuggestions) { completion.resetCompletionState(); + resetEscapeState(); return; } + + // Handle double ESC for clearing input + if (escPressCount === 0) { + if (buffer.text === '') { + return; + } + setEscPressCount(1); + setShowEscapePrompt(true); + if (escapeTimerRef.current) { + clearTimeout(escapeTimerRef.current); + } + escapeTimerRef.current = setTimeout(() => { + resetEscapeState(); + }, 500); + } else { + // clear input and immediately reset state + buffer.setText(''); + resetCompletionState(); + resetEscapeState(); + } + return; } if (shellModeActive && keyMatchers[Command.REVERSE_SEARCH](key)) { @@ -418,7 +479,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ if (buffer.text.length > 0) { buffer.setText(''); resetCompletionState(); - return; } return; } @@ -461,6 +521,9 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ reverseSearchCompletion, handleClipboardImage, resetCompletionState, + escPressCount, + showEscapePrompt, + resetEscapeState, vimHandleInput, reverseSearchActive, textBeforeReverseSearch, |
