v0.3.3: left/right cursor movement in input
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 2s

Adds :cursor-pos to TUI state. New functions:
- input-insert-char(ch): insert at cursor position, advance cursor
- input-delete-char(): delete char before cursor (standard backspace)

on-key handlers:
- Left arrow: decrement cursor-pos (clamped >= 0)
- Right arrow: increment cursor-pos (clamped <= buffer-len)
- Character input: input-insert-char at cursor position
- Backspace: input-delete-char at cursor position
- Enter: reset cursor-pos to 0

view-input: cursor at visual position matching cursor-pos

Test: (init-state) → (input-insert-char #\h) → (input-insert-char #\i)
→ (setf cursor-pos 1) → (input-insert-char #\X) → 'hXi' at pos 2
This commit is contained in:
2026-05-06 17:46:49 -04:00
parent 0861ac26f1
commit 9350cb855e
6 changed files with 104 additions and 40 deletions

View File

@@ -28,7 +28,7 @@
(setf *state*
(list :running t :mode :chat :connected nil :stream nil
:input-buffer nil :input-history nil :input-hpos 0
:messages nil :scroll-offset 0 :busy nil
:messages nil :scroll-offset 0 :busy nil :cursor-pos 0
:dirty (list nil nil nil))))
(defun now ()
@@ -39,6 +39,25 @@
(defun input-string ()
(coerce (reverse (st :input-buffer)) 'string))
(defun input-insert-char (ch)
"Insert character at cursor position into the input buffer."
(let* ((buf (st :input-buffer))
(pos (or (st :cursor-pos) 0))
(s (coerce (reverse buf) 'string))
(new (concatenate 'string (subseq s 0 pos) (string ch) (subseq s pos))))
(setf (st :input-buffer) (reverse (coerce new 'list)))
(setf (st :cursor-pos) (1+ pos))))
(defun input-delete-char ()
"Delete character before cursor position (standard backspace)."
(let* ((buf (st :input-buffer))
(pos (or (st :cursor-pos) 0)))
(when (and buf (> pos 0))
(let* ((s (coerce (reverse buf) 'string))
(new (concatenate 'string (subseq s 0 (1- pos)) (subseq s pos))))
(setf (st :input-buffer) (reverse (coerce new 'list)))
(setf (st :cursor-pos) (1- pos))))))
(defun add-msg (role content)
(push (list :role role :content content :time (now)) (st :messages))
(setf (st :dirty) (list t t nil)))