fix: arrow key cursor not updating + Shift+Enter not inserting newlines (#485)

Fix two editor input bugs:

1. Arrow key cursor movement not visually updating (fixes #464)
   The layout cache key only included {width, textVersion}. Cursor-only
   moves don't change textVersion, so stale cached layout was returned
   and the diff renderer skipped repaint. Added cursorLine and cursorCol
   to the cache key so cursor movements invalidate the cache.

2. Shift+Enter not inserting newlines in non-kitty terminals (Zed, VS Code, etc.)
   The /terminal-setup command configures terminals to send ESC+CR (\x1b\r)
   for Shift+Enter. But the followUp app action (bound to alt+enter) was
   intercepting \x1b\r in CustomEditor.handleInput before the editor's
   newLine handler could see it — because in non-kitty terminals, \x1b\r
   matches alt+enter. Now when kitty protocol is not active and \x1b\r is
   received, the followUp match is skipped so it falls through to newLine.
   Alt+Enter followUp still works in kitty-protocol terminals (iTerm2,
   Ghostty, Kitty, WezTerm) where the key combos are distinguishable.

Co-authored-by: TÂCHES <afromanguy@me.com>
This commit is contained in:
78slogs 2026-03-16 03:47:49 +02:00 committed by GitHub
parent a3c52b2a1b
commit 6d84d1c317
2 changed files with 13 additions and 4 deletions

View file

@ -1,4 +1,4 @@
import { Editor, type EditorOptions, type EditorTheme, type TUI } from "@gsd/pi-tui";
import { Editor, type EditorOptions, type EditorTheme, type TUI, isKittyProtocolActive } from "@gsd/pi-tui";
import type { AppAction, KeybindingsManager } from "../../../core/keybindings.js";
/**
@ -69,6 +69,13 @@ export class CustomEditor extends Editor {
// Check all other app actions
for (const [action, handler] of this.actionHandlers) {
if (action !== "interrupt" && action !== "exit" && this.keybindings.matches(data, action)) {
// When kitty protocol is not active, \x1b\r is ambiguous:
// it could be alt+enter (followUp) or shift+enter mapped via /terminal-setup.
// Prioritize newLine since that's what terminal-setup configures.
// Alt+enter followUp still works in kitty-protocol terminals.
if (action === "followUp" && !isKittyProtocolActive() && data === "\x1b\r") {
break; // Fall through to parent editor's newLine handling
}
handler();
return;
}

View file

@ -182,7 +182,7 @@ export class Editor implements Component, Focusable {
private undoStack = new UndoStack<EditorState>();
private textVersion = 0;
private cachedText: string | null = null;
private layoutCache: { width: number; textVersion: number; lines: LayoutLine[] } | null = null;
private layoutCache: { width: number; textVersion: number; cursorLine: number; cursorCol: number; lines: LayoutLine[] } | null = null;
private visualLineMapCache: { width: number; textVersion: number; lines: VisualLine[] } | null = null;
public onSubmit?: (text: string) => void;
@ -243,12 +243,14 @@ export class Editor implements Component, Focusable {
private getLayoutLines(width: number): LayoutLine[] {
const cached = this.layoutCache;
if (cached && cached.width === width && cached.textVersion === this.textVersion) {
if (cached && cached.width === width && cached.textVersion === this.textVersion
&& cached.cursorLine === this.state.cursorLine && cached.cursorCol === this.state.cursorCol) {
return cached.lines;
}
const lines = this.layoutText(width);
this.layoutCache = { width, textVersion: this.textVersion, lines };
this.layoutCache = { width, textVersion: this.textVersion, lines,
cursorLine: this.state.cursorLine, cursorCol: this.state.cursorCol };
return lines;
}