UNPKG

@vibe-kit/grok-cli

Version:

An open-source AI agent that brings the power of Grok directly into your terminal.

214 lines 9.16 kB
import { useState, useCallback, useRef } from "react"; import { deleteCharBefore, deleteCharAfter, deleteWordBefore, deleteWordAfter, insertText, moveToLineStart, moveToLineEnd, moveToPreviousWord, moveToNextWord, } from "../utils/text-utils.js"; import { useInputHistory } from "./use-input-history.js"; export function useEnhancedInput({ onSubmit, onEscape, onSpecialKey, disabled = false, multiline = false, } = {}) { const [input, setInputState] = useState(""); const [cursorPosition, setCursorPositionState] = useState(0); const isMultilineRef = useRef(multiline); const { addToHistory, navigateHistory, resetHistory, setOriginalInput, isNavigatingHistory, } = useInputHistory(); const setInput = useCallback((text) => { setInputState(text); setCursorPositionState(Math.min(text.length, cursorPosition)); if (!isNavigatingHistory()) { setOriginalInput(text); } }, [cursorPosition, isNavigatingHistory, setOriginalInput]); const setCursorPosition = useCallback((position) => { setCursorPositionState(Math.max(0, Math.min(input.length, position))); }, [input.length]); const clearInput = useCallback(() => { setInputState(""); setCursorPositionState(0); setOriginalInput(""); }, [setOriginalInput]); const insertAtCursor = useCallback((text) => { const result = insertText(input, cursorPosition, text); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); }, [input, cursorPosition, setOriginalInput]); const handleSubmit = useCallback(() => { if (input.trim()) { addToHistory(input); onSubmit?.(input); clearInput(); } }, [input, addToHistory, onSubmit, clearInput]); const handleInput = useCallback((inputChar, key) => { if (disabled) return; // Handle Ctrl+C - check multiple ways it could be detected if ((key.ctrl && inputChar === "c") || inputChar === "\x03") { setInputState(""); setCursorPositionState(0); setOriginalInput(""); return; } // Allow special key handler to override default behavior if (onSpecialKey?.(key)) { return; } // Handle Escape if (key.escape) { onEscape?.(); return; } // Handle Enter/Return if (key.return) { if (multiline && key.shift) { // Shift+Enter in multiline mode inserts newline const result = insertText(input, cursorPosition, "\n"); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } else { handleSubmit(); } return; } // Handle history navigation if ((key.upArrow || key.name === 'up') && !key.ctrl && !key.meta) { const historyInput = navigateHistory("up"); if (historyInput !== null) { setInputState(historyInput); setCursorPositionState(historyInput.length); } return; } if ((key.downArrow || key.name === 'down') && !key.ctrl && !key.meta) { const historyInput = navigateHistory("down"); if (historyInput !== null) { setInputState(historyInput); setCursorPositionState(historyInput.length); } return; } // Handle cursor movement - ignore meta flag for arrows as it's unreliable in terminals // Only do word movement if ctrl is pressed AND no arrow escape sequence is in inputChar if ((key.leftArrow || key.name === 'left') && key.ctrl && !inputChar.includes('[')) { const newPos = moveToPreviousWord(input, cursorPosition); setCursorPositionState(newPos); return; } if ((key.rightArrow || key.name === 'right') && key.ctrl && !inputChar.includes('[')) { const newPos = moveToNextWord(input, cursorPosition); setCursorPositionState(newPos); return; } // Handle regular cursor movement - single character (ignore meta flag) if (key.leftArrow || key.name === 'left') { const newPos = Math.max(0, cursorPosition - 1); setCursorPositionState(newPos); return; } if (key.rightArrow || key.name === 'right') { const newPos = Math.min(input.length, cursorPosition + 1); setCursorPositionState(newPos); return; } // Handle Home/End keys or Ctrl+A/E if ((key.ctrl && inputChar === "a") || key.name === "home") { setCursorPositionState(0); // Simple start of input return; } if ((key.ctrl && inputChar === "e") || key.name === "end") { setCursorPositionState(input.length); // Simple end of input return; } // Handle deletion - check multiple ways backspace might be detected // Backspace can be detected in different ways depending on terminal // In some terminals, backspace shows up as delete:true with empty inputChar const isBackspace = key.backspace || key.name === 'backspace' || inputChar === '\b' || inputChar === '\x7f' || (key.delete && inputChar === '' && !key.shift); if (isBackspace) { if (key.ctrl || key.meta) { // Ctrl/Cmd + Backspace: Delete word before cursor const result = deleteWordBefore(input, cursorPosition); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } else { // Regular backspace const result = deleteCharBefore(input, cursorPosition); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } return; } // Handle forward delete (Del key) - but not if it was already handled as backspace above if ((key.delete && inputChar !== '') || (key.ctrl && inputChar === "d")) { if (key.ctrl || key.meta) { // Ctrl/Cmd + Delete: Delete word after cursor const result = deleteWordAfter(input, cursorPosition); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } else { // Regular delete const result = deleteCharAfter(input, cursorPosition); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } return; } // Handle Ctrl+K: Delete from cursor to end of line if (key.ctrl && inputChar === "k") { const lineEnd = moveToLineEnd(input, cursorPosition); const newText = input.slice(0, cursorPosition) + input.slice(lineEnd); setInputState(newText); setOriginalInput(newText); return; } // Handle Ctrl+U: Delete from cursor to start of line if (key.ctrl && inputChar === "u") { const lineStart = moveToLineStart(input, cursorPosition); const newText = input.slice(0, lineStart) + input.slice(cursorPosition); setInputState(newText); setCursorPositionState(lineStart); setOriginalInput(newText); return; } // Handle Ctrl+W: Delete word before cursor if (key.ctrl && inputChar === "w") { const result = deleteWordBefore(input, cursorPosition); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); return; } // Handle Ctrl+X: Clear entire input if (key.ctrl && inputChar === "x") { setInputState(""); setCursorPositionState(0); setOriginalInput(""); return; } // Handle regular character input if (inputChar && !key.ctrl && !key.meta) { const result = insertText(input, cursorPosition, inputChar); setInputState(result.text); setCursorPositionState(result.position); setOriginalInput(result.text); } }, [disabled, onSpecialKey, input, cursorPosition, multiline, handleSubmit, navigateHistory, setOriginalInput]); return { input, cursorPosition, isMultiline: isMultilineRef.current, setInput, setCursorPosition, clearInput, insertAtCursor, resetHistory, handleInput, }; } //# sourceMappingURL=use-enhanced-input.js.map