Commit Graph

250 Commits

Author SHA1 Message Date
9492e00318 Phase 4 — sidebar, input, chat cleanup
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.
2026-05-20 11:11:00 -04:00
ef36854822 cleanup — remove dead markdown code, migrate theme to cl-tty, fix dialog navigation
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
2026-05-20 11:05:21 -04:00
8dd94f6d3c v0.8.3: remove startup info messages from chat history
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.
2026-05-20 10:04:24 -04:00
8eb866dee3 v0.8.2: fix grey background/text cosmetic issues
- 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)
2026-05-20 09:57:59 -04:00
b61191bec2 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.
2026-05-18 20:55:22 -04:00
f783b45ac7 v0.8.0: use exported text-input symbols (remove :: access)
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 2m1s
2026-05-18 15:59:02 -04:00
ab8ffb6a64 v0.8.0: integrate cl-tty text-input widget for buffer management 2026-05-18 15:58:12 -04:00
fd99099258 v0.8.0: replace inline read-raw-byte reader with cl-tty.input:read-event
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.
2026-05-18 15:56:07 -04:00
d157a837a9 v0.8.0: use cl-tty.box:char-width and cl-tty.markdown:search-highlight
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.
2026-05-18 15:50:29 -04:00
126e104854 v0.8.0: remove position-cursor from dirty-guarded redraw (runs every frame from main loop already) 2026-05-18 15:35:10 -04:00
5c4edb3d98 v0.8.0: revert to stable e04b12c (undo typecase guard, navigation refactor, cursor calc changes)
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.
2026-05-18 15:33:39 -04:00
7eab3c93d2 v0.8.0: move position-cursor out of dirty-guarded redraw and out of dialog-guarded let — runs every frame unconditional
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.
2026-05-18 15:16:58 -04:00
2f1abee930 v0.8.0: fix cursor off-by-one in word-wrap — use < instead of <=, incf accum len instead of 1+len
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.
2026-05-18 15:06:13 -04:00
1427e662e2 v0.8.0: use typecase guard instead of characterp — integers from reader must be converted via code-char
(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.
2026-05-18 14:56:45 -04:00
8c29c228cd v0.8.0: guard key dispatch with (when (characterp ch) ...) to prevent ctrl-byte keywords matching defkeymap keymaps
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.
2026-05-18 14:54:21 -04:00
a65374e120 v0.8.0: add cursor position recovery — if text exists but pos is 0, place cursor at end
position-cursor now also clamps cursor-line to valid range and
recovers from cursor-pos resets by moving to end of text.
2026-05-18 14:43:30 -04:00
46cac554ab v0.8.0: fix multiline cursor — position-cursor now computes its own word-wrap
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.
2026-05-18 14:38:22 -04:00
b1aafc56b2 v0.8.0: fix extra close paren in view-input, balance check
Discovered and fixed an extra close parenthesis on the hint bar's
last draw-text line that caused a compile error.
2026-05-18 14:34:55 -04:00
05aec4d028 v0.8.0: fix cursor position on multi-line input
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.
2026-05-18 14:32:23 -04:00
2c6e38f32d v0.8.0: force initial redraw before entering input loop
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.
2026-05-18 14:26:56 -04:00
53ca5af17e v0.8.0: add startup banner before entering alternate screen
Prints ';; Passepartout TUI starting...' before entering alternate screen,
so even if ANSI rendering fails, the user sees something.
2026-05-18 14:05:24 -04:00
5797e43cd8 v0.8.0: replace custom dialog stack with cl-tty.dialog:*dialog-stack*
- (st :dialog-stack) → cl-tty.dialog:*dialog-stack*
- (pop (st :dialog-stack)) → (cl-tty.dialog:pop-dialog)
- (push dlg (st :dialog-stack)) → (cl-tty.dialog:push-dialog dlg)
- All 10 references replaced across on-key, unified-menu-show, main loop, render, and tests
2026-05-18 13:28:31 -04:00
5524b4de06 v0.8.0: remove dead code duplicates — use cl-tty.box:word-wrap, delete local markdown/syntax-highlight/char-width
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.
2026-05-18 13:17:26 -04:00
73d42a812a v0.8.0: cl-tty input primitives, on-key keyword dispatch, XDG tangle paths, remove croatoan
- read-raw-byte: sb-unix:unix-read instead of read-char-no-hang
- raw-mode: sb-posix:tcsetattr instead of stty
- read-event: no probe-file /dev/stdin guard
- on-key: accepts &key ctrl alt shift code
- .asd: :croatoan dropped, :cl-tty added
- script: detection fix (empty lisp/ -> XDG)
2026-05-18 13:04:26 -04:00
e04b12c31c 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
2026-05-17 15:37:40 -04:00
2fedbbcb3b fix: solid amber █ cursor with finish-output flush
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.
2026-05-16 18:29:49 -04:00
c568ac6842 feat: Emacs-style reverse-video cursor (solid, no blink)
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
2026-05-16 18:22:01 -04:00
aca3f9e314 fix: draw cursor immediately in redraw (not 100ms later)
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.
2026-05-16 18:08:30 -04:00
5444322bf9 fix: software █ cursor blinking at 2Hz independently of typing
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.
2026-05-16 17:56:42 -04:00
f8ae4ac817 fix: terminal cursor instead of software-drawn █
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.
2026-05-16 17:50:08 -04:00
7e9da0f867 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.
2026-05-16 11:01:05 -04:00
bb98b486e4 v0.10.4: spacer rows between messages in history area
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.
2026-05-16 09:54:34 -04:00
bcab429dd7 Revert "v0.10.4: spacer rows between messages in history area"
This reverts commit 2513466576.
2026-05-16 09:31:52 -04:00
2513466576 v0.10.4: spacer rows between messages in history area
Each message block gets a trailing empty line (no speaker, no text)
to improve visual separation between turns.
2026-05-16 09:24:42 -04:00
f6dbd6dbd0 v0.10.3: input area speaker line — 4 rows get │ in :input-prompt color
Consistent with history area: user │ (amber), agent │ (tan), gate │ (grey),
and the 4-row input box │ (:input-prompt).
2026-05-16 09:14:51 -04:00
bad7686d4e v0.10.2: voice system — all speakers use │, neuro-thinking bg bar, blinking cursor
- 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.
2026-05-16 09:10:39 -04:00
2189745f40 v0.10.1: architectural cleanup — full-frame redraw, explicit bg everywhere, :bg-input fallback
- 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).
2026-05-16 09:03:59 -04:00
0a0478f502 v0.10.0: TUI visual overhaul — dark-neutral theme, left-border messages, sidebar auto-show, cl-tty style-reset
- 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.
2026-05-16 08:02:53 -04:00
3bc1977632 fix: Reader error loop from (= nil 27), Swank *standard-output* redirect
- 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.
2026-05-15 16:12:43 -04:00
13b6edab32 fix: nil check in CSI detection (= b2 91), Swank *standard-output* redirect
- (= 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.
2026-05-15 16:10:11 -04:00
8d9520a9cb fix: replace cl-tty.input:read-event with direct read-raw-byte + inline CSI detection
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.
2026-05-15 15:23:12 -04:00
bd72175d5b fix: use cl-tty.input:read-event for keyboard input (proper CSI handling)
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.
2026-05-15 13:09:17 -04:00
cc38e67d7c fix: Swank stderr leak, CSI detection with progn wrapper
- 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
2026-05-15 12:40:07 -04:00
df33e8d6db fix: main loop never closed, disconnect-daemon ran every iteration
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.
2026-05-15 12:08:40 -04:00
9fb4393c9c fix: runtime crash (sb-ext:timeout undefined), replace with listen-based polling
- Remove handler-case + sb-ext:with-timeout 0.1 pattern entirely.
  sb-ext:timeout is a condition class, not a recognized type in the
  compilation environment, causing runtime 'undefined function' crash.
- Replace with dotimes 10 * (listen *standard-input*) + sleep 0.01
  polling loop. Same 0.1s timeout, no condition type dependencies.
- Also fix handler-bind → handler-case in tui-load.lisp so the stack
  unwinds properly (running with-terminal's shutdown-backend cleanup)
  before the crash handler runs, restoring terminal to normal state.
- Fix paren imbalance (off by 1) in the new listen-based reader code.
2026-05-15 11:36:46 -04:00
c1f4ad40d2 fix: disconnect-daemon missing close paren, compilation now succeeds
Major bug: defun disconnect-daemon in channel-tui-main.org was missing
its closing paren. Every form after disconnect-daemon (tui-main, tests,
etc.) was inside the unclosed defun, causing 'end of file' compile errors.
Adding the missing ) fixed all compilation errors.

Also revert handler-case change: keep sb-ext:timeout condition type.
2026-05-15 11:27:57 -04:00
d14ff3a316 fix: daemon port conflict handling, multi-port TUI connect
- start-daemon: handle ADDRESS-IN-USE-ERROR by trying ports 9105-9115
  instead of crashing. Logs which port is used.
- Add *daemon-port* defvar to track actual listening port
- main: wrap start-daemon in handler-case so the daemon doesn't
  crash if all ports are in use
- connect-daemon (TUI): try ports 9105-9115 with 2s timeout each
  instead of retrying the same port 3 times
- Add debug messages for connection success and disconnection timestamp
2026-05-15 10:56:09 -04:00
5924994202 fix: CSI escape detection for arrow keys, fix paren balance
- Add CSI escape sequence detection: when ESC (27) is received, poll
  for up to 20ms for the next bytes to detect arrow/home/end keys
- Use listen+read-char polling (not nested with-timeout) to reliably
  collect multi-byte sequences while keeping standalone ESC responsive
- Fix paren balance in main code block (2 extra opens from nested
  esc-seq forms needed matching closes)
2026-05-15 09:43:03 -04:00
53aa471a51 fix: revert to blocking connect-daemon, daemon connection now reliable
- Revert async connect-daemon thread (bt:make-thread unreliable — errors
  in the thread cause silent failure, no connection, no error message)
- Restore blocking connect-daemon before with-terminal (original pattern
  that was working)
- Revert /reconnect to synchronous call
- Remove stale async thread code and error messages
2026-05-15 09:21:54 -04:00
c148570d4c fix: multi-value backend-size, minibuffer border+width, pre-existing warnings
- backend-size: nested multiple-value-bind/values instead of or+mv-bind
  (or discards secondary values), remove stale env-var pre-check
- Minibuffer: full chat-w width (respects sidebar), horizontal rule
  border, clear filter prompt line to avoid text overlap
- Filter prompt: (or filter "") prevents "NIL" display
- Dirty-flag redraw: skip when dialog-stack is non-nil (minibuffer
  covers the area, prevents flicker)
- Remove 3 unused variables: FOCUS, SENSOR, C (pre-existing warnings)
2026-05-15 08:51:19 -04:00