@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
JavaScript
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