From ab884a2c37b924432dc6a17a749ea94a69c8892a Mon Sep 17 00:00:00 2001 From: Ismail Pelaseyed Date: Mon, 4 Aug 2025 20:20:28 +0200 Subject: [PATCH] fix cursor movement in chat input --- package.json | 2 +- src/hooks/use-input-handler.ts | 40 +++++++++++++++++++++++----- src/ui/components/chat-input.tsx | 8 +++++- src/ui/components/chat-interface.tsx | 2 ++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 4710bad..044f710 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vibe-kit/grok-cli", - "version": "0.0.19", + "version": "0.0.20", "description": "An open-source AI agent that brings the power of Grok directly into your terminal.", "main": "dist/index.js", "bin": { diff --git a/src/hooks/use-input-handler.ts b/src/hooks/use-input-handler.ts index 6a95227..e9d1948 100644 --- a/src/hooks/use-input-handler.ts +++ b/src/hooks/use-input-handler.ts @@ -43,6 +43,7 @@ export function useInputHandler({ isConfirmationActive = false, }: UseInputHandlerProps) { const [input, setInput] = useState(""); + const [cursorPosition, setCursorPosition] = useState(0); const [showCommandSuggestions, setShowCommandSuggestions] = useState(false); const [selectedCommandIndex, setSelectedCommandIndex] = useState(0); const [showModelSelection, setShowModelSelection] = useState(false); @@ -86,6 +87,7 @@ export function useInputHandler({ confirmationService.resetSession(); setInput(""); + setCursorPosition(0); return true; } @@ -127,6 +129,7 @@ Examples: }; setChatHistory((prev) => [...prev, helpEntry]); setInput(""); + setCursorPosition(0); return true; } @@ -139,6 +142,7 @@ Examples: setShowModelSelection(true); setSelectedModelIndex(0); setInput(""); + setCursorPosition(0); return true; } @@ -167,6 +171,7 @@ Available models: ${modelNames.join(", ")}`, } setInput(""); + setCursorPosition(0); return true; } @@ -371,6 +376,7 @@ Respond with ONLY the commit message, no additional text.`; setIsProcessing(false); setIsStreaming(false); setInput(""); + setCursorPosition(0); return true; } @@ -428,6 +434,7 @@ Respond with ONLY the commit message, no additional text.`; } setInput(""); + setCursorPosition(0); return true; } @@ -444,6 +451,7 @@ Respond with ONLY the commit message, no additional text.`; setIsProcessing(true); setInput(""); + setCursorPosition(0); try { setIsStreaming(true); @@ -642,7 +650,9 @@ Respond with ONLY the commit message, no additional text.`; filteredSuggestions.length - 1 ); const selectedCommand = filteredSuggestions[safeIndex]; - setInput(selectedCommand.command + " "); + const newInput = selectedCommand.command + " "; + setInput(newInput); + setCursorPosition(newInput.length); setShowCommandSuggestions(false); setSelectedCommandIndex(0); return; @@ -693,20 +703,35 @@ Respond with ONLY the commit message, no additional text.`; return; } + // Handle left and right arrow keys for cursor movement + if (key.leftArrow && !showCommandSuggestions && !showModelSelection) { + setCursorPosition(prev => Math.max(0, prev - 1)); + return; + } + + if (key.rightArrow && !showCommandSuggestions && !showModelSelection) { + setCursorPosition(prev => Math.min(input.length, prev + 1)); + return; + } + if (key.backspace || key.delete) { - const newInput = input.slice(0, -1); - setInput(newInput); + if (cursorPosition > 0) { + const newInput = input.slice(0, cursorPosition - 1) + input.slice(cursorPosition); + setInput(newInput); + setCursorPosition(prev => prev - 1); - if (!newInput.startsWith("/")) { - setShowCommandSuggestions(false); - setSelectedCommandIndex(0); + if (!newInput.startsWith("/")) { + setShowCommandSuggestions(false); + setSelectedCommandIndex(0); + } } return; } if (inputChar && !key.ctrl && !key.meta) { - const newInput = input + inputChar; + const newInput = input.slice(0, cursorPosition) + inputChar + input.slice(cursorPosition); setInput(newInput); + setCursorPosition(prev => prev + 1); if (newInput.startsWith("/")) { setShowCommandSuggestions(true); @@ -720,6 +745,7 @@ Respond with ONLY the commit message, no additional text.`; return { input, + cursorPosition, showCommandSuggestions, selectedCommandIndex, showModelSelection, diff --git a/src/ui/components/chat-input.tsx b/src/ui/components/chat-input.tsx index 5e7d5a5..8fc2cab 100644 --- a/src/ui/components/chat-input.tsx +++ b/src/ui/components/chat-input.tsx @@ -3,21 +3,27 @@ import { Box, Text } from "ink"; interface ChatInputProps { input: string; + cursorPosition: number; isProcessing: boolean; isStreaming: boolean; } export function ChatInput({ input, + cursorPosition, isProcessing, isStreaming, }: ChatInputProps) { + const beforeCursor = input.slice(0, cursorPosition); + const afterCursor = input.slice(cursorPosition); + return ( - {input} + {beforeCursor} {!isProcessing && !isStreaming && } + {afterCursor} ); diff --git a/src/ui/components/chat-interface.tsx b/src/ui/components/chat-interface.tsx index 2cc10d6..0bbdf31 100644 --- a/src/ui/components/chat-interface.tsx +++ b/src/ui/components/chat-interface.tsx @@ -36,6 +36,7 @@ function ChatInterfaceWithAgent({ agent }: { agent: GrokAgent }) { const { input, + cursorPosition, showCommandSuggestions, selectedCommandIndex, showModelSelection, @@ -210,6 +211,7 @@ function ChatInterfaceWithAgent({ agent }: { agent: GrokAgent }) {