v0.8.0: TUI stabilization, command palette reverse-video highlight, hint bar redesign
- ROADMAP: consolidate all TUI work under v0.8.0 (removed premature v0.9.0/v0.10.x labels), restored original v0.9.0 eval harness plan - channel-tui-view.org: Emacs-style reverse-video cursor (swap fg/bg instead of drawing █), hint bar now shows F:focus/MCP:count on left and token gauge + keybindings on right, sidebar reorganized to show GATE TRACE, RULES + BLOCK COUNT, COST, FILES panels - channel-tui-main.org: command palette selection now uses reverse-video highlight (bg-input fg on input-fg bg, matching cursor style), fixed cond order so sel-p is checked before cat (all items had :category making sel-p unreachable), added session-cost extraction from daemon - passepartout: export COLORTERM=truecolor for modern backend detection
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
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 expanding light grey input box,
|
||||
multi-line word-wrapped prompt, software blinking cursor (█),
|
||||
multi-line word-wrapped prompt, Emacs-style reverse-video 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.
|
||||
@@ -243,57 +243,93 @@ Returns a list of strings, one per line."
|
||||
(setf cursor-line i
|
||||
cursor-col (- pos accum)))
|
||||
(incf accum (1+ len))))
|
||||
;; 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))))))
|
||||
;; Hint bar at h-2: F:/MCP: on left, token gauge + keybindings on right
|
||||
(let* ((focal (or (st :foveal-id) "-"))
|
||||
(focal-str (format nil "F:~a" focal))
|
||||
(mcp-str (format nil "MCP:~d" (or (st :mcp-count) 0)))
|
||||
(left-str (format nil "~a ~a" focal-str mcp-str))
|
||||
(msg-count (max 1 (length (st :messages))))
|
||||
(ctx-est (* msg-count 60))
|
||||
(ctx-limit 8192)
|
||||
(ctx-pct (min 100 (floor (* 100 ctx-est) ctx-limit)))
|
||||
(ctx-tok (if (< ctx-est 1000)
|
||||
(format nil "~d" ctx-est)
|
||||
(format nil "~dK" (floor ctx-est 1000))))
|
||||
(ctx-str (format nil "~a (~d%%)" ctx-tok ctx-pct))
|
||||
(hint-str "ctrl+p | /help")
|
||||
(ctx-fg (cond ((< ctx-pct 50) (theme-color :tool-done))
|
||||
((< ctx-pct 80) (theme-color :input-prompt))
|
||||
(t (theme-color :error))))
|
||||
(hint-x (- chat-w (length hint-str) 2))
|
||||
(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))))))
|
||||
#+end_src
|
||||
|
||||
** Sidebar
|
||||
#+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-view.lisp
|
||||
(defun view-sidebar (fb w h)
|
||||
"Render the right-side sidebar panel."
|
||||
(let* ((w (or (and (numberp w) (> w 0) w) 80))
|
||||
(h (or (and (numberp h) (> h 0) h) 24))
|
||||
(x (- w (or (st :sidebar-width) 42)))
|
||||
(bg-panel (theme-color :bg-panel))
|
||||
(y 0))
|
||||
;; Fill sidebar background (h-1 done separately to avoid scroll)
|
||||
(cl-tty.backend:draw-rect fb x 0 (- w x) (1- h) :bg bg-panel)
|
||||
(cl-tty.backend:draw-text fb x (1- h) (make-string (- w x) :initial-element #\Space) nil bg-panel)
|
||||
;; Focus panel
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "FOCUS" (theme-color :accent) bg-panel)
|
||||
;; Gate Trace — from latest agent message
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "GATE TRACE" (theme-color :accent) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) (format nil " ~a" (or (st :foveal-id) "none"))
|
||||
(theme-color :agent-fg) bg-panel)
|
||||
(incf y 2)
|
||||
;; Rules panel
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "RULES" (theme-color :accent) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) (format nil " ~d active" (or (st :rule-count) 0))
|
||||
(theme-color :agent-fg) bg-panel)
|
||||
(incf y 2)
|
||||
;; Context panel — token gauge
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "CONTEXT" (theme-color :accent) bg-panel)
|
||||
(let* ((msg-count (max 1 (length (st :messages))))
|
||||
(est (* msg-count 60))
|
||||
(limit 8192)
|
||||
(pct (min 100 (floor (* 100 est) limit)))
|
||||
(bar-len (floor pct 10))
|
||||
(bar (make-string bar-len :initial-element #\#)))
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y)
|
||||
(format nil " [~a~a]" bar
|
||||
(make-string (- 10 bar-len) :initial-element #\Space))
|
||||
(theme-color :dim) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) (format nil " ~d%" pct)
|
||||
(theme-color :status-fg) bg-panel)
|
||||
(let* ((msgs (st :messages))
|
||||
(last-gt (loop for i from (1- (length msgs)) downto 0
|
||||
for m = (aref msgs i)
|
||||
when (getf m :gate-trace)
|
||||
return (getf m :gate-trace))))
|
||||
(if last-gt
|
||||
(dolist (g last-gt)
|
||||
(let* ((name (getf g :gate))
|
||||
(result (getf g :result))
|
||||
(reason (getf g :reason))
|
||||
(glyph (case result (:passed "✓") (:blocked "✗") (:approval "→") (t "?")))
|
||||
(color (case result
|
||||
(:passed (theme-color :tool-done))
|
||||
(:blocked (theme-color :error))
|
||||
(:approval (theme-color :input-prompt))
|
||||
(t (theme-color :dim)))))
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) (format nil " ~a ~a" glyph name) color bg-panel)
|
||||
(when reason
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 4) (incf y) reason (theme-color :dim) bg-panel))))
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) " (none)" (theme-color :dim) bg-panel))
|
||||
(incf y 2))
|
||||
;; MCP panel
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "MCP" (theme-color :accent) bg-panel)
|
||||
;; Rules + Block Count
|
||||
(let ((blocked (loop for i below (length (st :messages))
|
||||
for m = (aref (st :messages) i)
|
||||
sum (loop for g in (getf m :gate-trace)
|
||||
count (eq (getf g :result) :blocked)))))
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "RULES" (theme-color :accent) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y)
|
||||
(format nil " ~d active" (or (st :rule-count) 0))
|
||||
(theme-color :agent-fg) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y)
|
||||
(format nil " ~d blocked" blocked)
|
||||
(if (> blocked 0) (theme-color :error) (theme-color :dim)) bg-panel)
|
||||
(incf y 2))
|
||||
;; Cost
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "COST" (theme-color :accent) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) (format nil " ~d server~:p" (or (st :mcp-count) 0))
|
||||
(theme-color :agent-fg) bg-panel)
|
||||
;; Version footer at bottom with connection dot
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y)
|
||||
(format nil " $~,2f" (or (st :session-cost) 0.0))
|
||||
(theme-color :status-fg) bg-panel)
|
||||
(incf y 2)
|
||||
;; Files (stub)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) "FILES" (theme-color :accent) bg-panel)
|
||||
(incf y)
|
||||
(cl-tty.backend:draw-text fb (+ x 2) (incf y) " (not yet)" (theme-color :dim) bg-panel)
|
||||
(incf y 2)
|
||||
;; Version footer
|
||||
(let* ((ver (or (st :daemon-version) ""))
|
||||
(ver-label (if (> (length ver) 0) (format nil "passepartout ~a" ver) "passepartout"))
|
||||
(dot (if (st :connected) "●" "○"))
|
||||
@@ -320,17 +356,27 @@ Returns a list of strings, one per line."
|
||||
(setf (st :dirty) (list nil nil nil))))
|
||||
|
||||
(defun position-cursor (fb w h)
|
||||
"Draw a solid block cursor █ at the input insertion point."
|
||||
"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)))
|
||||
(cl-tty.backend:draw-text fb cx cy "█" (theme-color :input-prompt) nil)
|
||||
(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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user