v0.8.0: TUI simplification — process-key-event, with-frame, inline reader

Replace queue-based key dispatch with process-key-event (inline in reader,
zero latency between keypress and render).
Add with-frame to cl-tty.backend (error-safe begin-sync/end-sync wrapper).
Use with-frame in redraw instead of manual begin-sync/end-sync.
Add initial render before main loop (UI appears before first read-event).
Remove position-cursor (replaced by inline block cursor in view-input).
Remove input-string/input-insert-char/input-delete-char wrappers.
Remove :input-buffer/:cursor-pos from state (managed by text-input widget).
passepartout script: set *debugger-hook* nil and failure-behaviour :warn
before quickload to survive compile warnings; remove cache-clear line.
This commit is contained in:
2026-05-18 20:55:22 -04:00
parent f783b45ac7
commit b61191bec2
4 changed files with 91 additions and 86 deletions

View File

@@ -71,16 +71,16 @@ Returns a list of strings, one per line."
)
(defun input-panel-top (chat-w h)
"Compute the top row of the input panel based on current input buffer."
"Compute the top row of the input panel based on current input text."
(let* ((hpad 2)
(inner-w (- chat-w (* 2 hpad)))
(prompt-w (- inner-w 2))
(text (input-string))
(lines (word-wrap text prompt-w))
(text (cl-tty.input:text-input-value (st :text-input)))
(lines (cl-tty.box:word-wrap text prompt-w))
(n-lines (max 1 (length lines)))
(panel-rows (max 4 (+ n-lines 2))))
(- h 4 panel-rows -1)))
#+end_src
;; Build simple tab-like blocks
#+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-view.lisp
@@ -204,16 +204,17 @@ Returns a list of strings, one per line."
(chat-w (- w sidebar-w))
(inner-w (- chat-w (* 2 hpad)))
(prompt-w (- inner-w 2))
(text (input-string))
(pos (or (st :cursor-pos) 0))
(lines (word-wrap text prompt-w))
(input (st :text-input))
(text (cl-tty.input:text-input-value input))
(pos (cl-tty.input:text-input-cursor input))
(lines (cl-tty.box:word-wrap text prompt-w))
(n-lines (max 1 (length lines)))
(panel-rows (max 4 (+ n-lines 2)))
(panel-top (input-panel-top chat-w h))
(bg-i (theme-color :bg-input))
(input-fg (theme-color :input-fg))
(hint-fg (theme-color :hint)))
;; Fill input panel: panel-top to h-4, indented by hpad
;; Fill input panel
(cl-tty.backend:draw-rect fb hpad panel-top inner-w panel-rows :bg bg-i)
;; Speaker lines for all input rows
(dotimes (r panel-rows)
@@ -226,11 +227,15 @@ Returns a list of strings, one per line."
(len (length line)))
(when (>= row (- h 4)) (return))
(cl-tty.backend:draw-text fb (+ hpad 2) row line input-fg nil)
(when (and (>= pos accum) (<= pos (+ accum len)))
(when (and (>= pos accum) (or (< pos (+ accum len)) (= i (1- n-lines))))
(setf cursor-line i
cursor-col (- pos accum)))
(incf accum (1+ len))))
;; Hint bar at h-2: F:/MCP: on left, token gauge + keybindings on right
;; Draw block cursor at insertion point
(let* ((cx (+ hpad 2 cursor-col))
(cy (+ panel-top 1 cursor-line)))
(cl-tty.backend:draw-text fb cx cy "█" :bright-white nil)))
;; Hint bar at h-2
(let* ((focal (or (st :foveal-id) "-"))
(focal-str (format nil "F:~a" focal))
(mcp-str (format nil "MCP:~d" (or (st :mcp-count) 0)))
@@ -251,7 +256,7 @@ Returns a list of strings, one per line."
(ctx-x (- hint-x 1 (length ctx-str))))
(cl-tty.backend:draw-text fb hpad (- h 2) left-str hint-fg (theme-color :bg))
(cl-tty.backend:draw-text fb ctx-x (- h 2) ctx-str ctx-fg (theme-color :bg))
(cl-tty.backend:draw-text fb hint-x (- h 2) hint-str hint-fg (theme-color :bg))))))
(cl-tty.backend:draw-text fb hint-x (- h 2) hint-str hint-fg (theme-color :bg)))))
#+end_src
** Sidebar
@@ -331,39 +336,19 @@ Returns a list of strings, one per line."
(setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24))
(when (or (first (st :dirty)) (second (st :dirty)) (third (st :dirty)))
(cl-tty.backend:begin-sync fb)
(cl-tty.backend:draw-rect fb 0 0 w h :bg (theme-color :bg))
(view-status fb w h)
(view-chat fb w h)
(view-input fb w h)
(when (sidebar-visible-p w)
(view-sidebar fb w h))
(cl-tty.backend:end-sync fb)
(setf (st :dirty) (list nil nil nil))))
(handler-case
(progn
(cl-tty.backend:with-frame (fb)
(cl-tty.backend:draw-rect fb 0 0 w h :bg (theme-color :bg))
(view-status fb w h)
(view-chat fb w h)
(view-input fb w h)
(when (sidebar-visible-p w)
(view-sidebar fb w h)))
(setf (st :dirty) (list nil nil nil)))
(error (c)
(add-msg :system (format nil "* Render error: ~a *" c))))))
(defun position-cursor (fb w h)
"Draw cursor at the input insertion point using reverse video (Emacs-style).
The character under the cursor is redrawn with foreground and background
swapped. If the cursor is past the end of the input string, a reversed
space is drawn."
(let* ((sw (if (sidebar-visible-p w) (or (st :sidebar-width) 42) 0))
(cw (- w sw))
(hpad 2)
(text (input-string))
(text-len (length text))
(pos (or (st :cursor-pos) 0))
(prompt-w (- cw (* 2 hpad) 2))
(display-start (max 0 (- pos (1- prompt-w))))
(cx (+ hpad 2 (- pos display-start)))
(cy (- h 6))
(bg-i (theme-color :bg-input))
(input-fg (theme-color :input-fg)))
(if (< pos text-len)
(let ((ch (char text pos)))
(cl-tty.backend:draw-text fb cx cy (string ch) bg-i input-fg))
(cl-tty.backend:draw-text fb cx cy " " bg-i input-fg))
(finish-output (cl-tty.backend::backend-output-stream fb))))
#+END_SRC
* Implementation — v0.7.0 additions