fix: input keyboard shortcuts (#676)

Fixes keyboard shortcuts:
- ctrl+a/e
- opt+arrow keys
This commit is contained in:
Fouad Matin
2025-04-25 16:58:09 -07:00
committed by GitHub
parent f3ee933a74
commit 3f4762d969
4 changed files with 66 additions and 21 deletions

View File

@@ -106,11 +106,16 @@ export default function TerminalChatInputThinking({
return ( return (
<Box flexDirection="column" gap={1}> <Box flexDirection="column" gap={1}>
<Box gap={2}> <Box justifyContent="space-between">
<Text>{frameWithSeconds}</Text> <Box gap={2}>
<Text>{frameWithSeconds}</Text>
<Text>
Thinking
{dots}
</Text>
</Box>
<Text> <Text>
Thinking Press <Text bold>Esc</Text> twice to interrupt
{dots}
</Text> </Text>
</Box> </Box>
{awaitingConfirm && ( {awaitingConfirm && (

View File

@@ -412,7 +412,7 @@ export default function TerminalChatInput({
setInput(""); setInput("");
openApprovalOverlay(); openApprovalOverlay();
return; return;
} else if (inputValue === "exit") { } else if (["exit", "q", ":q"].includes(inputValue)) {
setInput(""); setInput("");
setTimeout(() => { setTimeout(() => {
app.exit(); app.exit();
@@ -881,20 +881,30 @@ function TerminalChatInputThinking({
); );
return ( return (
<Box flexDirection="column" gap={1}> <Box width="100%" flexDirection="column" gap={1}>
<Box gap={2}> <Box
<Text>{frameWithSeconds}</Text> flexDirection="row"
width="100%"
justifyContent="space-between"
paddingRight={1}
>
<Box gap={2}>
<Text>{frameWithSeconds}</Text>
<Text>
Thinking
{dots}
</Text>
</Box>
<Text> <Text>
Thinking <Text dimColor>press</Text> <Text bold>Esc</Text>{" "}
{dots} {awaitingConfirm ? (
<Text bold>again</Text>
) : (
<Text dimColor>twice</Text>
)}{" "}
<Text dimColor>to interrupt</Text>
</Text> </Text>
</Box> </Box>
{awaitingConfirm && (
<Text dimColor>
Press <Text bold>Esc</Text> again to interrupt and enter a new
instruction
</Text>
)}
</Box> </Box>
); );
} }

View File

@@ -73,7 +73,7 @@ const TerminalHeader: React.FC<TerminalHeaderProps> = ({
</Text> </Text>
<Text dimColor> <Text dimColor>
<Text color="blueBright"></Text> approval:{" "} <Text color="blueBright"></Text> approval:{" "}
<Text bold color={colorsByPolicy[approvalPolicy]} dimColor> <Text bold color={colorsByPolicy[approvalPolicy]}>
{approvalPolicy} {approvalPolicy}
</Text> </Text>
</Text> </Text>

View File

@@ -610,6 +610,24 @@ export default class TextBuffer {
} }
} }
/* ------------------------------------------------------------------
* Document-level navigation helpers
* ---------------------------------------------------------------- */
/** Move caret to *absolute* beginning of the buffer (row-0, col-0). */
private moveToStartOfDocument(): void {
this.preferredCol = null;
this.cursorRow = 0;
this.cursorCol = 0;
}
/** Move caret to *absolute* end of the buffer (last row, last column). */
private moveToEndOfDocument(): void {
this.preferredCol = null;
this.cursorRow = this.lines.length - 1;
this.cursorCol = this.lineLen(this.cursorRow);
}
/* ===================================================================== /* =====================================================================
* Higherlevel helpers * Higherlevel helpers
* =================================================================== */ * =================================================================== */
@@ -780,6 +798,18 @@ export default class TextBuffer {
key["rightArrow"] key["rightArrow"]
) { ) {
this.move("wordRight"); this.move("wordRight");
}
// Many terminal/OS combinations (e.g. macOS Terminal.app & iTerm2 with
// the default key-bindings) translate ⌥← / ⌥→ into the classic readline
// shortcuts ESC-b / ESC-f rather than an ANSI arrow sequence that Ink
// would tag with `leftArrow` / `rightArrow`. Ink parses those 2-byte
// escape sequences into `input === "b"|"f"` with `key.meta === true`.
// Handle this variant explicitly so that Option+Arrow performs word
// navigation consistently across environments.
else if (key["meta"] && (input === "b" || input === "B")) {
this.move("wordLeft");
} else if (key["meta"] && (input === "f" || input === "F")) {
this.move("wordRight");
} else if (key["home"]) { } else if (key["home"]) {
this.move("home"); this.move("home");
} else if (key["end"]) { } else if (key["end"]) {
@@ -823,11 +853,11 @@ export default class TextBuffer {
// Emacs/readline-style shortcuts // Emacs/readline-style shortcuts
else if (key["ctrl"] && (input === "a" || input === "\x01")) { else if (key["ctrl"] && (input === "a" || input === "\x01")) {
// Ctrl+A or ⌥← → start of line // Ctrl+A → start of input (first row, first column)
this.move("home"); this.moveToStartOfDocument();
} else if (key["ctrl"] && (input === "e" || input === "\x05")) { } else if (key["ctrl"] && (input === "e" || input === "\x05")) {
// Ctrl+E or ⌥→ → end of line // Ctrl+E → end of input (last row, last column)
this.move("end"); this.moveToEndOfDocument();
} else if (key["ctrl"] && (input === "b" || input === "\x02")) { } else if (key["ctrl"] && (input === "b" || input === "\x02")) {
// Ctrl+B → char left // Ctrl+B → char left
this.move("left"); this.move("left");