v0.10.5: multi-line expanding input box with software blinking cursor
view-input word-wraps input at prompt-w, expanding the grey panel upward as needed. Uses software cursor (█) in :input-fg blinking at 2Hz via get-internal-real-time. view-chat max-lines adapts to variable panel height via input-panel-top. Removed terminal cursor (position-cursor, cursor-show, cursor-style). Dialog minibuffer top now computed from input-panel-top.
This commit is contained in:
@@ -1029,7 +1029,8 @@ Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
(cnt (length filtered))
|
||||
(filter (cl-tty.select:select-filter sel))
|
||||
(mh (min 15 (+ 1 cnt)))
|
||||
(top (max 0 (- h 7 mh)))
|
||||
(panel-top (passepartout.channel-tui:input-panel-top chat-w h))
|
||||
(top (max 0 (- panel-top mh)))
|
||||
(bg-p (theme-color :bg-panel))
|
||||
(sep-c (theme-color :separator)))
|
||||
;; Fill minibuffer area with panel bg
|
||||
@@ -1066,9 +1067,7 @@ Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
(format nil "> ~a" (or filter ""))
|
||||
(theme-color :input-prompt) bg-p))
|
||||
(cl-tty.backend:end-sync be))
|
||||
(sleep 0.1)
|
||||
;; Show cursor at input position every frame
|
||||
(passepartout.channel-tui:position-cursor be w h)))
|
||||
(sleep 0.1)))
|
||||
(progn (disconnect-daemon)))))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ All state mutation flows through event handlers in the controller.
|
||||
(:export :tui-main :st :add-msg :now :input-string
|
||||
:queue-event :drain-queue :init-state
|
||||
:view-status :view-chat :view-input :redraw
|
||||
:position-cursor
|
||||
:input-panel-top
|
||||
:on-key :on-daemon-msg :send-daemon
|
||||
:connect-daemon :disconnect-daemon
|
||||
:*tui-theme* :theme-color))
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
2. (view-chat fb w h): renders scrolled chat messages. User messages
|
||||
get amber left border (│), agent messages no border, streaming
|
||||
agent gets grey left border. Gate traces/tool calls use ╎ prefix.
|
||||
3. (view-input fb w h): renders light grey input box (h-7 to h-4),
|
||||
prompt at h-6, right-aligned lowercase hint at h-2.
|
||||
3. (view-input fb w h): renders expanding light grey input box,
|
||||
multi-line word-wrapped prompt, software blinking cursor (█),
|
||||
right-aligned lowercase hint at h-2.
|
||||
4. (redraw fb w h): wraps view-status/chat/input in begin-sync/end-sync,
|
||||
dispatches per dirty flags, fills global :bg first.
|
||||
5. (char-width ch): returns terminal column width of character CH.
|
||||
@@ -69,6 +70,22 @@ Returns a list of strings, one per line."
|
||||
;; No clock, no dot, no text. Everything clean.
|
||||
)
|
||||
|
||||
(defun cursor-visible-p ()
|
||||
"Returns T if the blinking cursor should be visible this frame (2Hz)."
|
||||
(evenp (floor (get-internal-real-time)
|
||||
(floor internal-time-units-per-second 2))))
|
||||
|
||||
(defun input-panel-top (chat-w h)
|
||||
"Compute the top row of the input panel based on current input buffer."
|
||||
(let* ((hpad 2)
|
||||
(inner-w (- chat-w (* 2 hpad)))
|
||||
(prompt-w (- inner-w 2))
|
||||
(text (input-string))
|
||||
(lines (word-wrap text prompt-w))
|
||||
(n-lines (max 1 (length lines)))
|
||||
(panel-rows (max 4 (+ n-lines 2))))
|
||||
(- h 4 panel-rows -1)))
|
||||
|
||||
|
||||
;; v0.7.2: search-highlight — wrap matching text in **bold** for markdown
|
||||
(defun search-highlight (content query)
|
||||
@@ -94,7 +111,8 @@ Returns a list of strings, one per line."
|
||||
(sidebar-w (if (sidebar-visible-p w) (or (st :sidebar-width) 42) 0))
|
||||
(chat-w (- w sidebar-w))
|
||||
(msgs (st :messages)) (total (length msgs))
|
||||
(max-lines (- h 7)) (is-search (st :search-mode))
|
||||
(panel-top (input-panel-top chat-w h))
|
||||
(max-lines (max 0 panel-top)) (is-search (st :search-mode))
|
||||
(bordered-w (- chat-w (* 2 hpad) 2))
|
||||
(unbordered-w (- chat-w (* 2 hpad)))
|
||||
(y 0))
|
||||
@@ -177,10 +195,10 @@ Returns a list of strings, one per line."
|
||||
(setf lines-remaining 0))))
|
||||
(let* ((scroll-skip (st :scroll-offset))
|
||||
(start (max 0 (- total msg-count scroll-skip))))
|
||||
(loop for i from start below total while (< y (- h 7))
|
||||
(loop for i from start below total while (< y panel-top)
|
||||
do (let ((pairs (aref msg-lines i)))
|
||||
(dolist (pair pairs)
|
||||
(when (>= y (- h 7)) (return))
|
||||
(dolist (pair pairs)
|
||||
(when (>= y panel-top) (return))
|
||||
(destructuring-bind (bstr bcolor tstr tcolor &optional rect-bg) pair
|
||||
(when rect-bg
|
||||
(cl-tty.backend:draw-rect fb 0 y 1 1 :bg rect-bg))
|
||||
@@ -206,18 +224,34 @@ Returns a list of strings, one per line."
|
||||
(prompt-w (- inner-w 2))
|
||||
(text (input-string))
|
||||
(pos (or (st :cursor-pos) 0))
|
||||
(display-start (max 0 (- pos (1- prompt-w))))
|
||||
(visible (subseq text display-start (min (length text) (+ display-start prompt-w))))
|
||||
(bg-i (theme-color :bg-input))
|
||||
(input-fg (theme-color :input-fg))
|
||||
(hint-fg (theme-color :hint)))
|
||||
;; Light grey input panel: h-7 to h-4 (4 rows), indented by hpad
|
||||
(cl-tty.backend:draw-rect fb hpad (- h 7) inner-w 4 :bg bg-i)
|
||||
;; Speaker line for all 4 input rows (at hpad, matching history)
|
||||
(dolist (r (list (- h 7) (- h 6) (- h 5) (- h 4)))
|
||||
(cl-tty.backend:draw-text fb hpad r "│" (theme-color :input-prompt) nil))
|
||||
;; Prompt at h-6, second row at h-5 — text at hpad+2 matching history
|
||||
(cl-tty.backend:draw-text fb (+ hpad 2) (- h 6) visible input-fg nil)
|
||||
(lines (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
|
||||
(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)
|
||||
(cl-tty.backend:draw-text fb hpad (+ panel-top r) "│" (theme-color :input-prompt) nil))
|
||||
;; Draw each wrapped input line
|
||||
(let ((accum 0) (cursor-line 0) (cursor-col 0))
|
||||
(dotimes (i n-lines)
|
||||
(let* ((line (nth i lines))
|
||||
(row (+ panel-top 1 i))
|
||||
(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)))
|
||||
(setf cursor-line i
|
||||
cursor-col (- pos accum)))
|
||||
(incf accum (1+ len))))
|
||||
;; Draw software blinking cursor at insertion point
|
||||
(when (cursor-visible-p)
|
||||
(let ((cursor-row (+ panel-top 1 cursor-line)))
|
||||
(cl-tty.backend:draw-text fb (+ hpad 2 cursor-col) cursor-row "█" input-fg nil))))
|
||||
;; Hint — lowercase, right-aligned at h-2
|
||||
(let ((hint "ctrl+p | /help"))
|
||||
(cl-tty.backend:draw-text fb (- chat-w (length hint) 2) (- h 2) hint hint-fg (theme-color :bg)))))
|
||||
@@ -291,23 +325,7 @@ Returns a list of strings, one per line."
|
||||
(when (sidebar-visible-p w)
|
||||
(view-sidebar fb w h))
|
||||
(cl-tty.backend:end-sync fb)
|
||||
(position-cursor fb w h)
|
||||
(setf (st :dirty) (list nil nil nil))))
|
||||
|
||||
(defun position-cursor (fb w h)
|
||||
"Move blinking block cursor to the input insertion point."
|
||||
(let* ((sw (if (sidebar-visible-p w) (or (st :sidebar-width) 42) 0))
|
||||
(cw (- w sw))
|
||||
(hpad 2)
|
||||
(text (input-string))
|
||||
(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)))
|
||||
(cl-tty.backend:cursor-move fb cx cy)
|
||||
(cl-tty.backend:cursor-style fb :block :blink t)
|
||||
(cl-tty.backend:cursor-show fb)))
|
||||
#+END_SRC
|
||||
|
||||
* Implementation — v0.7.0 additions
|
||||
|
||||
Reference in New Issue
Block a user