Replace the 400-line on-key function with cl-tty text-input callbacks.
Add on-cancel, on-tab, on-history slots to cl-tty's text-input widget.
Remove defkeymap :local up/down/escape handlers.
Remove (member k '(:enter :tab :escape :up :down)) from process-key-event.
PageUp/PageDown stay in keymap, routed to handle-ppage/handle-npage.
Fix XDG cl-tty.asd to remove stale select-package/select references
and add missing markdown-package/markdown entries.
Fix #\) character literal (not valid in all contexts).
Fix several missing closing parentheses in handle-tab and command-dispatch.
Dialog: use consistent cl-tty.dialog: prefix for all select accessors.
The :cl-tty.select and :cl-tty.dialog packages each define their own
SELECT class with separate accessor generic functions. Mixing prefixes
caused "no applicable method" errors. Now all 14 references use
cl-tty.dialog: (make-select, select-filter, select-next, etc.)
Sidebar: fix sidebar-lines append arguments. Each item must be a
proper list of cons cells, not a bare cons. Replaced all quoted
'("x" . :y) with (list (cons "x" :y)). Also fixed the quoted
cons call that was never evaluated.
Bash script: add --disable-debugger and --eval '(uiop:quit 0)' to
the tui sbcl invocation. Prevents the debugger from entering raw
terminal mode on error and ensures clean exit.
cl-tty: delete stale select-package.lisp and select.lisp orphan files
(not tangled by any current org file).
Sidebar: replace manual (incf y) tracking with flat list construction.
sidebar-lines returns (text . color-key) pairs; view-sidebar loops
over them. Version footer stays at h-2. No more fragile y arithmetic.
Input panel: use cl-tty.input text-input's render method instead of
manual word-wrap + cursor-position computation. Layout node set each
frame for dynamic position.
Chat: extract msg->pairs (message to renderable lines) and render-pair
(draw one line pair) as separate functions. Replace reverse-iteration
scroll culling with forward scan that skips by scroll-offset. Same
behavior, less nesting.
Phases 1-3 of library/application boundary cleanup:
Phase 1: Remove dead code (150 lines)
- Delete local word-wrap (all callers already used cl-tty.box:word-wrap)
- Delete parse-markdown-spans, render-styled, parse-markdown-blocks,
syntax-highlight (all unused — view uses cl-tty.markdown directly)
- Replace tests with cl-tty.markdown equivalents
Phase 2: Migrate theme to cl-tty.theme (250 lines removed)
- Replace *tui-theme*/*tui-theme-presets* with *theme* + define-preset
- theme-switch/theme-save/theme-load delegate to cl-tty.theme
- theme-color is now a 3-line wrapper
- Added save-theme/load-theme to cl-tty.theme (38 lines added there)
Phase 3: Fix dialog arrow navigation with select-handle-key
- Replace broken manual key dispatch with cl-tty.dialog:select-handle-key
- The old code had a dead (and ch (graphic-char-p ch)) — the and result
was discarded, so every unhandled key ran (code-char key-code) against
the filter unconditionally, inserting garbage on arrow keys
Remove the add-msg :system calls that printed * Swank <port> * and
* <backend> backend WxH * — purely informational/debug messages
that cluttered the first lines of the chat window.
- view-chat draw-text calls: change nil bg to (theme-color :bg)
so text characters sit on explicit near-black instead of terminal
default background (which appears as grey highlight)
- cl-tty bump (v1.2.0: remove \n from draw-rect that caused scroll
leaving the last row blank)
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.
The old inline reader only handled basic CSI sequences (up/down/left/right/
home/end) and treated everything else as Escape. cl-tty.input:read-event
handles CSI sequences with modifiers, SS3 function keys, kitty keyboard
protocol (disambiguate escape codes), UTF-8 input, and terminal resize.
Key-event structs are converted back to the keyword/integer format that
the existing case ch dispatch and on-key expect:
- Ctrl+letter → :CTRL-X keyword
- :page-up/:page-down → :ppage/:npage
- Single-char keywords → printable character via code-char
- Everything else → pass keyword through
Removed the separate resize check block since read-event handles it.
Removed local definitions of char-width (dead code) and search-highlight
(now uses cl-tty.markdown:search-highlight). Moved char-width tests to
cl-tty's box-tests.
The typecase guard, explicit navigation keyword dispatch, and word-wrap
cursor calculation changes introduced two regressions:
1. Cursor shows letter before instead of on (off-by-one)
2. Right arrow sometimes moves backward
Reverting to e04b12c which had working arrow keys and single-line cursor.
The unconditional position-cursor fix will be re-applied separately.
Removed (position-cursor fb w h) from inside redraw (which is gated by dirty
flags), and from inside the dialog-guarded (unless dialog* ...) block in the
main loop. Added unconditional (position-cursor be w h) at loop body level so
it runs every single iteration regardless of dirty state or dialog activity.
This ensures the cursor highlight always tracks cursor-pos correctly.
The position-cursor function now uses cursor-line/cursor-col stored by
view-input instead of recomputing from scratch, guaranteeing alignment
with the rendered text. The boundary check uses (< pos (+ accum len))
to avoid falsely matching the first character of the next wrapped line.
Removed the speculative reset-recovery code that sent cursor to end
when pos was 0, since that broke legitimate navigation to the
beginning of the input.
(characterp 97) is NIL so (when (characterp ch) ...) broke all printable
input. The typecase ((integer 32 126) ...) converts printable integer
bytes to characters while rejecting keywords like :CTRL-A.
Maps known navigation keywords (:up :down :left :right :enter :backspace :tab
:escape :home :end :ppage :npage) explicitly in the case to bypass the guard,
so only non-keyword, non-navigation values are filtered. This prevents :CTRL-A
(byte 1/SOH) from ever reaching on-key or dispatch-key-event and resetting
cursor-pos to 0 via the :ctrl+a keymap binding.
Removed dependency on (st :cursor-line) and (st :cursor-col) state.
position-cursor now does its own word-wrap and accum tracking to
determine which wrapped line and column the cursor is on. This makes
it independent of view-input's rendering state.
view-input now stores cursor-line and cursor-col in state after
word-wrapping. position-cursor uses these to place the cursor on
the correct wrapped line, instead of the hardcoded row (- h 6)
which always put the cursor on the first line.
Add explicit (redraw be w h) call before the main loop so the TUI
renders immediately on startup, without waiting for the first
100ms input poll cycle to complete.
The generated temp script manually compiled and loaded TUI files from
/lisp/, silently swallowing compile errors and
leaving the user with a blank screen. Replaced with a direct ASDF load
that gives proper error messages and lets ASDF handle compilation.
Phase 1 of cl-tty abstraction: remove 5 dead functions (word-wrap,
char-width, parse-markdown-spans, parse-markdown-blocks, render-styled,
syntax-highlight) and their tests. Switch 2 remaining word-wrap calls
to cl-tty.box:word-wrap.
- 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
Replaced reverse-video cursor with a solid █ block in :input-prompt
color (amber). Drawn at the insertion point every frame from redraw
and main loop. finish-output ensures the cursor escape reaches the
terminal immediately.
Replaced the software blinking █ cursor with a reverse-video cursor
that swaps foreground and background colors at the insertion point.
Solid at all times — no blink logic, no state tracking, no flicker.
- Removed duplicate cursor-visible-p functions
- Removed software cursor draw from view-input
- Removed terminal cursor style/show from initialize-backend
- position-cursor draws character at cursor with :bg fg + :input-fg bg
position-cursor now called at end of redraw so the cursor appears
on the very first frame and after every keypress without a 100ms
delay. Also still called from main loop between sleep for blinking.
position-cursor runs every frame from main loop (after sleep 0.1),
drawing █ when cursor-visible-p returns T, space when NIL.
This creates a true 2Hz blink that toggles independently of keypresses.
Terminal cursor also set to blinking block as fallback.
Replaced software cursor (draw-text █ every frame) with native terminal
cursor (position-cursor using cursor-move + cursor-style). Terminal handles
blinking natively at 500ms — no redraw needed for cursor updates.
- position-cursor: computed input insertion point from state, calls
cursor-move + cursor-style (:block :blink t) + cursor-show.
- Called from main loop every frame after (sleep 0.1), outside
redraw's begin-sync/end-sync. No flicker.
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.
Clean implementation: spacer inserted in the rendering loop as an
(incf y) between message blocks, tracked in scroll-fitting loop
via spacer variable. No data structure changes.
Also: fixed premature let close in spacer binding, fixed view-input
closing paren count, and re-applied speaker alignment fixes lost in
revert.
- Theme: added :agent-border, :thinking-bg, :symbolic-border to all 13
presets with theme-load fallback for saved themes.
- Agent output now draws │ with :agent-border color (muted tan).
- Neuro-thinking (streaming): draw-rect at column 0 with :thinking-bg
(dark grey block) instead of a grey │ character. No border text.
- Gate traces: │ with :symbolic-border (was ╎ with :dim).
- Tool calls: │ with tool status color (was ╎).
- Removed > prompt prefix from input line.
- Added position-cursor function: blinking block cursor at insertion
point, called every frame from the main loop after sleep.
- redraw: always draws all three views (status/chat/input) when any
dirty flag is set. Dirty flags only gate frame rendering, not
which parts render. Fixes disappearing input/history.
- Added :bg-input to all 13 presets with #2e2e2e (dark) / #d4d4d4
(light-amber). theme-load fills missing keys from current preset
defaults for backward compatibility.
- Removed unused *sidebar-panels* defvar and obsolete contract docs.
- Renamed dim-bg → dim-fg (foreground color, not background).
- All draw-text calls in sidebar and dialog minibuffer now pass
explicit bg-panel, preventing background leaks.
- render-styled (markdown renderer) passes explicit (theme-color :bg).
- Fix h shadowing in view-chat scroll loop (h → mh).
- Theme: near-black (#0a0a0a) backgrounds, dark-grey panels (#141414),
warm amber (#fab283) accent only. New keys: :bg, :bg-panel, :bg-element,
:text-muted. All 13 presets updated.
- Messages: No background fills (sit on global black). User messages get
amber left border (│). Agent response has no border (invisible).
Streaming agent messages get grey left border. Gate traces and tool
calls use grey ╎ prefix. No label lines, no time separators.
- Sidebar: :sidebar-mode with :auto/:visible/:hidden. Auto-shows at >120
cols (opencode-style). Width 42 with version + connection dot footer.
- Input: 2-char hpad on each side. Grey panel (2 rows: separator +
prompt). Hint right-aligned at bottom on black.
- Status bar: empty (clean black line).
- cl-tty backend: draw-text, draw-rect, draw-link, draw-border now use
\e[22;23;24;25;27m (style-only reset) instead of \e[0m (full reset),
preserving foreground/background across draw calls.
- Fix: all sidebar text draws pass explicit bg-panel background.
- Fix: hint at h-1 passes explicit (theme-color :bg).
- Fix: sidebar bottom row uses draw-text (no \n) to prevent scroll at h-1.
- Remove 'code' variable binding (redundant with b). esc-seq now
starts with (and b (= b 27) ...) so when b is nil (timeout), the
and short-circuits before (= b 27) can error with 'NIL is not
of the type NUMBER'.
- Swank prints to *standard-output*, not *error-output*. Bind both
to string output streams to prevent ';; Swank started' leak.
- (= b2 91) errors when b2 is nil (read-raw-byte timeout). Add
(and b2 (= b2 91)) to guard against nil.
- Swank writes ';; Swark started at port:...' to *standard-output*,
not *error-output*. Bind *standard-output* to string stream too.
cl-tty.input:read-event has bugs (CSI parser timeout causes :escape
to be returned for arrow keys). Replace with direct read-raw-byte
calls that are proven to work for CSI sequences. The inline detection:
- Read first byte with 100ms timeout
- If ESC (27), read two more bytes with 150ms timeout each
- Map 65→:up, 66→:down, 67→:right, 68→:left, etc.
- Other bytes converted via the same cond chain as before
Also re-add resize check (was handled by read-event).
Use handler-case around the reader to prevent any reader errors
from crashing the TUI. Re-add Swank *error-output* redirect.
Replace the inline raw byte reader + CSI detection with
cl-tty.input:read-event which uses read-raw-byte (direct fd reads)
and properly parses CSI escape sequences, UTF-8, mouse events, etc.
Also fix: remove extra ) in (t nil) clause that was prematurely
closing the let* binding, causing the if form to receive 4 args.
- Swank: bind *error-output* to string stream to prevent 'Swank started
at port: 4006.' from leaking to terminal on exit
- CSI detection: wrap inner dotimes in (progn ... t) so the and form
doesn't short-circuit (dotimes returns nil, breaking the chain)
- Add debug add-msg for CSI detection results
The main loop's closing paren was missing — (sleep 0.1)) only closed
sleep and the minibuffer let, but NOT the loop itself. The next form
(progn (disconnect-daemon)) was INSIDE the loop body, called on every
iteration. On first call it added '* Disconnected *' and cleared the
daemon stream, making the TUI permanently disconnected.
Fix: add ) to close the loop. Also:
- Connect-daemon runs synchronously BEFORE with-terminal (3 ports, 6s
max). If daemon is already running, the TUI starts connected.
- If sync connect fails, background thread retries every 5 seconds.
- start-daemon in background (no blocking wait for daemon startup),
so TUI appears immediately.
set -e on line 2 causes the bash script to exit immediately when sbcl
returns non-zero, before the stty icanon echo ixon restore runs.
Add trap cleanup EXIT to guarantee terminal restore on any exit path.