From aec6c0861e8244cdaefda14840844e922705c8fa Mon Sep 17 00:00:00 2001
From: DeWitt Clinton <dclinton@gmail.com>
Date: Wed, 14 May 2025 17:33:37 -0700
Subject: Add readline-like keybindings to the input prompts. (#354)

New keybindings in the main input prompt (when auto-suggestions are not active):

  - `Ctrl+L`: Clears the entire screen.
  - `Ctrl+A`: Moves the cursor to the beginning of the current input line.
  - `Ctrl+E`: Moves the cursor to the end of the current input line.
  - `Ctrl+P`: Navigates to the previous command in the input history.
  - `Ctrl+N`: Navigates to the next command in the input history.

In the multiline text editor (e.g., when editing a previous message):
   - `Ctrl+K`: Deletes text from the current cursor position to the end of the line ("kill line right").
---
 packages/cli/src/ui/components/InputPrompt.tsx | 39 +++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

(limited to 'packages/cli/src/ui/components/InputPrompt.tsx')

diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx
index 1c3d2a07..b1e05554 100644
--- a/packages/cli/src/ui/components/InputPrompt.tsx
+++ b/packages/cli/src/ui/components/InputPrompt.tsx
@@ -24,6 +24,8 @@ interface InputPromptProps {
   userMessages: readonly string[];
   navigateSuggestionUp: () => void;
   navigateSuggestionDown: () => void;
+  setEditorState: (updater: (prevState: EditorState) => EditorState) => void;
+  onClearScreen: () => void;
 }
 
 export interface EditorState {
@@ -44,6 +46,8 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
   navigateSuggestionUp,
   navigateSuggestionDown,
   resetCompletion,
+  setEditorState,
+  onClearScreen,
 }) => {
   const handleSubmit = useCallback(
     (submittedValue: string) => {
@@ -106,7 +110,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
   );
 
   const inputPreprocessor = useCallback(
-    (input: string, key: Key) => {
+    (
+      input: string,
+      key: Key,
+      _currentText?: string,
+      _cursorOffset?: number,
+    ) => {
       if (showSuggestions) {
         if (key.upArrow) {
           navigateSuggestionUp();
@@ -136,6 +145,31 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
           resetCompletion();
           return true;
         }
+      } else {
+        // Keybindings when suggestions are not shown
+        if (key.ctrl && input === 'a') {
+          setEditorState((s) => ({ key: s.key + 1, initialCursorOffset: 0 }));
+          return true;
+        }
+        if (key.ctrl && input === 'e') {
+          setEditorState((s) => ({
+            key: s.key + 1,
+            initialCursorOffset: query.length,
+          }));
+          return true;
+        }
+        if (key.ctrl && input === 'l') {
+          onClearScreen();
+          return true;
+        }
+        if (key.ctrl && input === 'p') {
+          inputHistory.navigateUp();
+          return true;
+        }
+        if (key.ctrl && input === 'n') {
+          inputHistory.navigateDown();
+          return true;
+        }
       }
       return false;
     },
@@ -149,6 +183,9 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
       resetCompletion,
       activeSuggestionIndex,
       handleSubmit,
+      inputHistory,
+      setEditorState,
+      onClearScreen,
     ],
   );
 
-- 
cgit v1.2.3

