From 7fe717cdccd907c92f27e0b5955ca6ba764e274c Mon Sep 17 00:00:00 2001 From: phranck Date: Mon, 9 Feb 2026 22:21:05 +0100 Subject: [PATCH] Docs: Add TextField Selection plan - Shift+Arrow selection support for TextField/SecureField - CSI parser modifier support (Shift, Ctrl, Alt) - Selection rendering with highlight - Delete/replace selection behavior --- plans/open/2026-02-09-textfield-selection.md | 229 +++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 plans/open/2026-02-09-textfield-selection.md diff --git a/plans/open/2026-02-09-textfield-selection.md b/plans/open/2026-02-09-textfield-selection.md new file mode 100644 index 0000000..3bdea86 --- /dev/null +++ b/plans/open/2026-02-09-textfield-selection.md @@ -0,0 +1,229 @@ +# TextField Selection Support + +## Preface + +This plan adds text selection to TextField and SecureField. Users can select text using Shift+Arrow keys, with Shift+Left/Right extending character-by-character and Shift+Up/Down selecting to start/end (since single-line fields have no vertical navigation). Selected text is visually highlighted and can be deleted with Backspace/Delete or replaced by typing. This requires extending the CSI parser to recognize modifier codes in escape sequences and adding selection state to TextFieldHandler. + +## Checklist + +### Phase 1: CSI Parser Modifier Support +- [ ] Parse modifier codes in CSI sequences (`;2` = Shift, `;5` = Ctrl) +- [ ] Support `ESC [1;2A` format (Shift+Arrow) +- [ ] Support `ESC [1;2H` / `ESC [1;2F` (Shift+Home/End) +- [ ] Add tests for modifier parsing + +### Phase 2: Selection State in TextFieldHandler +- [ ] Add `selectionAnchor: Int?` property (nil = no selection) +- [ ] Computed property `selectionRange` returns Range? +- [ ] `clearSelection()` helper method +- [ ] Update `clampCursorPosition()` to also clamp anchor + +### Phase 3: Selection Keyboard Handling +- [ ] Shift+Left: extend selection left by 1 character +- [ ] Shift+Right: extend selection right by 1 character +- [ ] Shift+Up: extend selection to text start +- [ ] Shift+Down: extend selection to text end +- [ ] Shift+Home: extend selection to text start (alternative) +- [ ] Shift+End: extend selection to text end (alternative) +- [ ] Any arrow without Shift: clear selection, move cursor + +### Phase 4: Selection Rendering +- [ ] Render selected text with highlight background +- [ ] Selection works with horizontal scrolling +- [ ] SecureField shows selected bullets with highlight + +### Phase 5: Selection Editing +- [ ] Backspace with selection: delete selected text +- [ ] Delete with selection: delete selected text +- [ ] Typing with selection: replace selected text +- [ ] Paste (if implemented): replace selected text + +### Phase 6: Testing & Polish +- [ ] TextFieldHandler selection tests +- [ ] TextField rendering tests with selection +- [ ] SecureField rendering tests with selection +- [ ] Update example pages to demonstrate selection + +## Context / Problem + +TextField and SecureField currently support only cursor movement without selection. Users cannot: +- Select a portion of text to delete it +- Select text to replace it by typing +- Visually see what text will be affected by delete operations + +This is a standard text editing feature that users expect from any text input. + +## Specification / Goal + +### Success Criteria + +1. **Shift+Arrow selection**: Extend selection in the direction pressed +2. **Visual feedback**: Selected text has distinct background color +3. **Delete selection**: Backspace/Delete removes entire selection +4. **Replace selection**: Typing replaces selected text +5. **Works in both**: TextField and SecureField (masked bullets) +6. **All tests pass** + +### Terminal Considerations + +- Modifier keys in escape sequences vary by terminal +- Most modern terminals support xterm-style modifier encoding +- Format: `ESC [ 1 ; ` where modifier 2 = Shift + +## Design + +### CSI Modifier Codes + +Standard xterm modifier encoding in CSI sequences: + +| Modifier | Code | Example | +|----------|------|---------| +| Shift | 2 | `ESC [1;2A` = Shift+Up | +| Alt | 3 | `ESC [1;3A` = Alt+Up | +| Shift+Alt | 4 | `ESC [1;4A` = Shift+Alt+Up | +| Ctrl | 5 | `ESC [1;5A` = Ctrl+Up | +| Shift+Ctrl | 6 | `ESC [1;6A` = Shift+Ctrl+Up | + +Format: `ESC [ ; ` + +For arrows: `ESC [1;2A` (Up), `ESC [1;2B` (Down), `ESC [1;2C` (Right), `ESC [1;2D` (Left) +For Home/End: `ESC [1;2H` (Home), `ESC [1;2F` (End) + +### Selection State + +```swift +final class TextFieldHandler: Focusable { + var cursorPosition: Int + var selectionAnchor: Int? // nil = no selection + + /// Returns the selection range, or nil if no selection. + var selectionRange: Range? { + guard let anchor = selectionAnchor else { return nil } + let start = min(anchor, cursorPosition) + let end = max(anchor, cursorPosition) + return start..