Add insertTextAtCursorInternal() to properly handle newlines by splitting
lines at the cursor position.
- Normalize line endings (CRLF/CR to LF)
- Handle single-line and multi-line insertion
- Position cursor at end of inserted text
- Call onChange only once at the end
Refactor handlePaste() to use insertTextAtCursorInternal() for
multi-line pastes, while retaining character-by-character insertion for
single-line pastes to preserve autocomplete triggering.
Undo snapshots are captured for all edit operations:
- Word insertion, backspace, forward delete
- Word/line deletion (Ctrl+W, Ctrl+U, Ctrl+K, Alt+D)
- Yank/yank-pop, paste, autocomplete completion
- Cursor movement starts a new undo unit
- setText() pushes snapshot when content changes
Additionally, history browsing captures the undo state on first entry.
Adds deleteWordForward action bound to Alt+D, which deletes from cursor
to the end of the current word and saves to kill ring for later yanking.
Consecutive forward kills append to the same kill ring entry.
Add kill ring functionality with:
- Ctrl+W/U/K save deleted text to kill ring
- Ctrl+Y yanks (pastes) most recent deletion
- Alt+Y cycles through kill ring (after Ctrl+Y)
- Consecutive deletions accumulate into single entry
The Editor component now accepts TUI as the first constructor parameter,
enabling it to query terminal dimensions. When content exceeds available
height, the editor scrolls vertically keeping the cursor visible.
Features:
- Max editor height is 30% of terminal rows (minimum 5 lines)
- Page Up/Down keys scroll by page size
- Scroll indicators show lines above/below: ─── ↑ 5 more ───
Breaking change: Editor constructor signature changed from
new Editor(theme)
to
new Editor(tui, theme)
fixes#732
Previously, the Editor component used character-level (grapheme-level)
wrapping, which broke words mid-character at line boundaries. This
created an ugly visual experience when typing or pasting long text.
Now the Editor uses word-aware wrapping:
- Wraps at word boundaries when possible
- Falls back to character-level wrapping for tokens wider than the
available width (e.g., long URLs)
- Strips leading whitespace at line starts
- Preserves multiple spaces within lines
Added wordWrapLine() helper function that tokenizes text into words
and whitespace runs, then builds chunks that fit within the specified
width while respecting word boundaries.
Also updated buildVisualLineMap() to use the same word wrapping logic
for consistent cursor navigation.
Added tests for:
- Word boundary wrapping
- Leading whitespace stripping
- Long token (URL) character-level fallback
- Multiple space preservation
- Edge cases (empty string, exact fit)
- Skip trailing whitespace before deleting word (readline behavior)
- Make word navigation grapheme-aware using Intl.Segmenter
- Add Ctrl+Left/Right and Alt+Left/Right word navigation to Input
- Accept full Unicode input while rejecting control characters (C0/C1/DEL)
- Extract shared utilities to utils.ts (getSegmenter, isWhitespaceChar, isPunctuationChar)
- Fix unsafe cast in Editor.forceFileAutocomplete with runtime type check
- Add comprehensive tests for word deletion and navigation
Editor text wrapping now uses grapheme-aware width calculation instead
of string length. Fixes crash when pasting text containing emojis like
✅ or CJK characters that are 2 terminal columns wide.
Browse previously submitted prompts using Up/Down arrow keys, similar to
shell history and Claude Code's prompt history feature.
- Up arrow when editor is empty: browse to older prompts
- Down arrow when browsing: return to newer prompts or clear editor
- Cursor movement within multi-line history entries supported
- History is session-scoped, stores up to 100 entries
- Consecutive duplicates are not added to history
Includes 15 new tests for history navigation behavior.