SIGWINCH: handle KEY_RESIZE (410) in main loop — re-measure screen,
re-create status/chat/input windows at new dimensions, force redraw.
Scroll clamp: PageUp clamped to (max 0 (- total 1)), prevents scrolling
past message list end. Status bar shows 'msgs:N scroll:0'.
/quit: saves :input-history to ~/.cache/passepartout/history (one line
per entry, most recent first), sends goodbye handshake, sets :running nil.
/reconnect: closes stale socket via disconnect-daemon, re-runs
connect-daemon with retry backoff. Connection-loss detection: reader-loop
counts consecutive nils; after 10, queues :disconnected event. Handler
clears :connected/:busy, shows red system message.
Load-history: reads ~/.cache/passepartout/history on startup, populates
:input-history for up-arrow recall.
Message vector: :messages init as adjustable vector with fill pointer.
add-msg uses vector-push-extend (O(1) append). view-chat uses aref
(O(1) access) instead of nth (O(n) for lists).
Croatoan child windows don't inherit the screen's :input-blocking nil.
Without explicit (setf (input-blocking iw) nil), get-char blocks the
main loop indefinitely, preventing redraw from running. New agent
messages queued by the reader-loop thread were never rendered until
the user pressed a key.
Now the loop runs at 30fps and responses appear immediately.
- connect-daemon: retry up to 3 times with 3s backoff instead of
single 10s attempt. Shows 'Connecting...' while retrying.
- Failed attempts show attempt count and error detail.
- After all retries exhausted: shows TIP to start daemon first.
- Connection status bar already shows Connected/Disconnected.
- passepartout tui already auto-starts daemon if port 9105 is closed.
TUI integration: 7/7 pass.
- recv-daemon: catch all errors silently (not just usocket:timeout-error
which doesn't match SBCL's SB-INT:IO-TIMEOUT). Prevents log-message
from bleeding through to the Croatoan screen.
- reader-loop: add (sleep 0.5) when recv-daemon returns nil, preventing
tight spin on repeated I/O timeouts during idle periods.
- Backspace: get-char returns raw ncurses integers (263=KEY_BACKSPACE),
not key structs. Use code-key + key-name to normalize codes >255
to keywords, so (eq ch :backspace) actually matches.
- TUI blank screen: add initial redraw+refresh before the main loop.
get-char blocks, so the first frame was never drawn on startup.
- connect-daemon: remove :element-type character (daemon listens in
binary mode, mismatch caused hang). Add :timeout 10.
- Tests: use actual ncurses codes (343=KEY_ENTER, 263=KEY_BACKSPACE,
9=TAB) instead of make-key or raw ascii codes.
TUI: 45/45 pass.
Croatoan returns key structs (make-key :name :backspace) for special
keys. The on-key handler was comparing these structs to keywords like
:backspace with eq, which always failed. Keys like Enter (returned as
13) worked, but Backspace/Tab/arrows didn't. Actually, the user couldn't
delete typed characters.
Fix: normalize at the top of on-key — if the input is a key struct,
extract the :name keyword. This allows the existing keyword-based
cond dispatches to work for all keys.
Updated all tests to use (make-key :name :enter/backspace/tab) instead
of raw integer codes, matching what Croatoan actually sends.
TUI: 43/43 pass.