Commit Graph

175 Commits

Author SHA1 Message Date
4e0b825fcc v1.2.0: remove spurious \n from draw-rect
draw-rect wrote \n after each row's fill, including the last row at
the bottom of the frame. This caused a terminal scroll, shifting all
content up by 1 and leaving the last row blank (terminal default bg).
cursor-move-escape at the start of each iteration already repositions
the cursor — the \n was never needed.
2026-05-20 09:57:49 -04:00
e53939844c v1.1.1: fix format No more arguments on CSI key press
parse-csi-params format string had ~C[~{~d~};~d~C — the trailing ~C
had no matching argument (terminator already consumed by ~d). Removed
the spurious ~C. Bug triggered on every arrow key, home, end, etc.
2026-05-20 09:39:56 -04:00
9b8ac8b770 v1.1.0: fix CSI parser destructuring-bind crash on nil params
parse-csi-sequence used destructuring-bind on a single return value,
failing when the CSI sequence had no parameters (e.g. plain arrow keys ESC[A).
Capture multiple return values via multiple-value-list instead of relying
on let* which only captures the primary value.
2026-05-18 20:59:11 -04:00
4c3f5fe65a v1.0.0: extract theme from cl-tty.box to own cl-tty.theme package
The theme system (theme class, define-preset, load-preset, theme-color)
was part of the bloated cl-tty.box package even though it had nothing
to do with boxes, spans, or component rendering. It only used cl-tty.backend
for the *theme-colors* hash table.

Changes:
- added defpackage :cl-tty.theme as the first block in theme.lisp
  (inline defpackage avoids ASDF dependency ordering issues with
   separate package files)
- removed theme exports from cl-tty.box defpackage
- theme tests now run in their own THEME-SUITE (16 tests) instead of
  part of BOX-SUITE
- box suite drops from 64 to 48 tests (16 moved to theme suite)
- updated ASDF, run-all-tests.lisp

All 15 test suites pass at 100%.
2026-05-18 16:50:48 -04:00
ef613927e6 v1.0.0: merge container (scrollbox + tabbar) into cl-tty.box
Eliminates the cl-tty.container package by merging scrollbox and tabbar
components directly into cl-tty.box, where the component system lives.

Changes:
- added scrollbox/tabbar exports to cl-tty.box defpackage in package.org
- changed scrollbox.org in-package from cl-tty.container to cl-tty.box
- changed tabbar.org in-package from cl-tty.container to cl-tty.box
- tabbar's key-event-key references are qualified with cl-tty.input:
  (avoids circular :use dependency with cl-tty.input which :uses cl-tty.box)
- deleted container-package.org
- updated test packages, integration tests, scripts, ASDF
- all 14 test suites pass at 100%
2026-05-18 16:45:50 -04:00
108abd054f v1.0.0: add word-wrap support to text-input.render method
The text-input widget now renders multi-line word-wrapped text using
cl-tty.box:word-wrap instead of single-line truncation. The cursor
position is computed from the wrapped lines using the same algorithm
as position-cursor but now lives in the library where it belongs.

This is the critical step that enables passepartout to replace its
ad-hoc view-input + position-cursor with a simple (render input be) call.

Placeholder text is shown when value is empty, drawn with :dim style.
Block cursor (█) at the correct word-wrapped position. All tests pass
at 100% including integration tests.
2026-05-18 16:30:50 -04:00
d0382f9290 v1.0.0: merge mouse → input — eliminate cl-tty.mouse package
The mouse-event struct was already in cl-tty.input. All mouse handling
logic (mouse-mixin, hit-test, selection, clipboard, link detection)
was in a separate cl-tty.mouse package. Moved everything into the
input package where the struct lives, eliminating one package boundary.

Changes:
- absorb mouse-mixin, handle-mouse-event, hit-test, selection struct,
  selection variables/functions, cell-link-at, open-link-at into
  text-input.org (tangled to input.lisp)
- update cl-tty.input defpackage with mouse exports
- mouse tests merged into INPUT-SUITE (appended to input-tests.lisp)
- delete mouse.org, mouse-package.lisp, mouse.lisp, mouse-tests.lisp
- update ASDF, run-all-tests.lisp, scripts to drop mouse references

All test suites pass at 100% (INPUT-SUITE: 102 tests, +6 from mouse)
2026-05-18 16:18:58 -04:00
9a4d117eee v1.0.0: merge select → dialog — eliminate cl-tty.select package
The select widget (filtered option list) was only used by the dialog
system. Merging removes an entire package boundary, simplifies the
dependency chain, and reduces the library from 12 packages to 11.

Changes:
- absorb select class, accessors, filter, navigation, key handling,
  rendering, fuzzy matching, and all tests into dialog.org
- update cl-tty.dialog package to use cl-tty.box (for dirty-mixin)
  and cl-tty.layout (for layout-node)
- remove select.org, select-package.lisp, select.lisp, select-tests
- update ASDF, run-all-tests.lisp, scripts to drop select references
- update integration tests to use cl-tty.dialog instead of cl-tty.select

All 13 test suites pass at 100%.
2026-05-18 16:12:43 -04:00
ff7eb4d6e1 v1.0.0: export text-input manipulation functions from cl-tty.input 2026-05-18 15:58:53 -04:00
ff5b7a5fea v1.0.0: add char-width tests to box-tests suite 2026-05-18 15:50:35 -04:00
0b076c8def v1.0.0: add char-width and search-highlight to cl-tty library
char-width → cl-tty.box (text.lisp): terminal column width for Unicode
  characters including CJK, emoji, combining marks, and tab.

search-highlight → cl-tty.markdown: wraps query matches in **bold**
  markers for search result emphasis. Pure function, zero dependencies.
2026-05-18 15:48:15 -04:00
af572d5a8c v0.8.0: tangle to XDG (~/.local/share/cl-tty/), remove stale memex .lisp files 2026-05-18 13:04:10 -04:00
e3415cee73 simple backend: ANSI colors, cursor positioning, bold — no longer a no-op
- draw-text: uses cursor-move-escape, sgr-fg/sgr-bg, sgr-attr for
  bold/italic/underline/reverse/dim/blink (was: just dumped string)
- draw-rect: fills with background color (was: complete no-op)
- draw-link: forwards to draw-text with fg/bg (was: ignored them)
- draw-ellipsis: uses positioned draw-text (was: newline+space)
- Added end-sync with finish-output (was: missing, output never flushed)
2026-05-17 15:37:33 -04:00
f76f637548 fix: restore cursor-hide in initialize-backend (no more cursor-style) 2026-05-16 18:22:07 -04:00
e115a88690 fix: cursor-style called before cursor-show to avoid style reset 2026-05-16 17:56:49 -04:00
2785d6913f fix: draw-text/draw-rect use style-only reset (\e[22-27m) instead of full reset (\e[0m)
Preserves foreground and background colors across draw calls.
Without this, every draw-text resets terminal to default grey
background, causing grey-background artifacts in the TUI.
2026-05-16 08:03:00 -04:00
1df078a235 fix: all CSI parser reads need timeout, select-next skips all categorized items
- %read-escape-sequence: increase b1 timeout 0.05→0.1, pass timeout to
  parse-csi-sequence and all read-next calls (OCR branch was using nil
  timeout, blocking forever)
- parse-csi-sequence: accept :timeout keyword, pass to all read-raw-byte
  calls, return :escape on timeout instead of blocking
- %read-digits: accept timeout, check nil from read-raw-byte before (>= b 48)
- %parse-sgr-mouse: accept timeout, return nil if first byte times out
- read-param in parse-csi-sequence: check b for nil before comparing
- parse-csi-params: map Kitty protocol u-terminator cursor codes (1=up,
  2=down, 3=right, 4=left, 5=page-up, 6=page-down) before falling to
  :codepoint. Convert terminator byte to char via code-char for key table
  lookups.
- select-next/select-prev: remove (not (getf opt :category)) check.
  All items have :category in the unified command list, so navigation
  skipped every item and selection stayed at index 0 permanently.
2026-05-15 13:43:42 -04:00
26e55e652f fix: unix-simple-poll returns T not integer, use if poll-result 2026-05-15 13:10:02 -04:00
ce9bf7781a fix: parse-csi-sequence multi-value capture, read-raw-byte timeout, format args
- parse-csi-sequence: use multiple-value-bind to capture both params
  list and terminator byte (let* only takes primary value, discarding
  terminator, causing destructuring-bind to fail on empty list)
- parse-csi-params: convert terminator byte to char via code-char
  for key table lookups and comparisons
- read-raw-byte: check unix-simple-poll result before calling
  unix-read. When poll times out, returns nil immediately instead
  of blocking forever on unix-read
2026-05-15 13:09:09 -04:00
de1864bd94 fix: backend-size returns both cols and rows via multi-value-bind (or discards secondary values)
The OR pattern inside backend-size used (or (multiple-value-bind ...)
...), but multiple-value-bind only returns the primary value of its
body. When the env-var shortcut was removed, both calls to backend-size
(the cols nth-value 0 and rows nth-value 1) returned the same primary
value, making rows always nil.

Restructure with nested multiple-value-bind/values chains so both
return values propagate correctly through all fallback stages.
Also remove MY_TERM_COLS/ROWS env-var pre-check — it returned stale
startup dimensions after terminal resize.
2026-05-15 08:51:13 -04:00
bb579be207 fix: remove per-call finish-output from backend-write (flush once per frame via end-sync)
backend-write flushed output after every single draw-text/draw-rect
call, causing hundreds of individual flushes per frame. This caused
visible flicker on slow terminals.

Remove finish-output from backend-write — all critical flush points
(initialize-backend, shutdown-backend, enable-mouse, enable-bracketed-paste,
end-sync) already call finish-output explicitly.

DECICM sync (begin-sync/end-sync) wraps every frame boundary,
making the frame render atomically with a single flush at end-sync.
2026-05-14 19:36:21 -04:00
916f473107 docs: sync .org with implementation for backend-size, read-raw-byte, SIGWINCH
backend-protocol.org / simple.lisp:
- Replace hard-coded 80x24 prose with full 5-step fallback chain
  (MY_TERM env vars → ioctl fd 0 → ioctl stdout → /dev/tty → 80x24)
- Document return-from pattern (or discards secondary values)

modern-backend.org / modern.lisp:
- Replace simple ioctl-only prose with 4-step fallback chain
- Document env-var pre-check and /dev/tty fallback

text-input.org / input.lisp:
- Update read-raw-byte prose: with-pinned-objects/vector-sap
  instead of alien buffer (code was already correct, prose stale)
- Add missing (require :sb-posix) to SIGWINCH handler code block
- Document :sb-posix requirement in prose
2026-05-14 16:25:45 -04:00
b44b4b6aa0 fix: use return-from for env var fallback (or discards secondary values)
or in Common Lisp only preserves the primary value — secondary
values from the truthy branch are lost. return-from preserves
all values, so both cols and rows are returned correctly.
2026-05-14 15:03:00 -04:00
36fbe81441 fix: use MY_TERM_COLS/LINES instead of COLUMNS/LINES
SBCL unconditionally strips COLUMNS and LINES from the
environment. MY_TERM_COLS/MY_TERM_LINES bypass this filter.
2026-05-14 14:55:37 -04:00
8cb269dfee fix: add COLUMNS/LINES env var fallback in backend-size
When all ioctl methods return rows=0 (SBCL process context), try
/ from the shell environment. These are set by bash
and may survive SBCL's env filtering in some configurations.
2026-05-14 14:53:02 -04:00
11a70956a0 fix: use ioctl on fd 0 (stdin) as primary sizing method
The parent's fd 0 IS the real terminal when running from a shell.
This directly queries the terminal size without subprocess or
alien complexity. Added proper when guard on the unix-ioctl result.
2026-05-14 14:48:54 -04:00
9a54b7ade6 fix: check unix-ioctl return value before reading winsize
unix-ioctl returns NIL on failure, but the code still reads the
uninitialized alien winsize buffer, getting garbage values for
cols and rows (often 0 for rows). Now checks 'ok' before reading.
2026-05-14 14:47:43 -04:00
aa73171c30 fix: use :input :interactive for stty size subprocess
:input :interactive opens /dev/tty for the child's stdin, giving
stty access to the real terminal for its ioctl query.
2026-05-14 14:44:15 -04:00
eedf065e6e fix: use :input :inherit for stty size subprocess
:input :inherit preserves the parent's fd 0 (the terminal) in the
child process, so stty can query it via ioctl. Previous approaches
(:input :interactive, /dev/tty) all failed because uiop's process
setup redirects stdin away from the terminal.
2026-05-14 14:41:45 -04:00
21c7b1c2d9 fix: replace stty size with tput cols/lines in backend-size
stty size returns incomplete data when run through uiop:run-program
(the child may not have terminal access). tput is a terminfo utility
that outputs a single number per call, avoiding parsing issues.
Works reliably in any subprocess context.
2026-05-14 14:40:11 -04:00
733ba7c1b8 fix: remove :input :interactive from stty size subprocess
:input :interactive causes uiop to block on /dev/tty in the parent.
stty size queries terminal via ioctl, not stdin — no input
redirection needed.
2026-05-14 14:30:17 -04:00
ce7af16b13 fix: restore ioctl block in simple.lisp (was lost in edit) 2026-05-14 14:25:23 -04:00
31f864471c fix: use :input :interactive for stty size subprocess
SBCL's stdin during --load is the load file, NOT the terminal.
When uiop:run-program creates a subprocess, it inherits this
stdin, so 'stty size' reads from the load file and fails.
:input :interactive opens /dev/tty for the child's stdin,
matching the behavior of 'stty size' from an interactive shell.
2026-05-14 14:24:55 -04:00
4b1ff3ed0f fix: move stty size to first priority in backend-size
stty size via subprocess is the most reliable method — it
returns the correct 59x83 from the user's terminal. Move it
before ioctl to ensure it's tried first.
2026-05-14 14:22:27 -04:00
fe301dc25b fix: run stty size via 'sh -c' with /dev/tty redirection
uiop:run-program may redirect the child's stdin, preventing stty
from querying the terminal. 'stty size < /dev/tty' explicitly
reads from the controlling terminal regardless of stdin setup.
2026-05-14 14:20:22 -04:00
4df3048a13 fix: add stty size subprocess fallback in both backends
stty size via uiop:run-program is the most reliable method —
it works from the shell on every Unix system and bypasses
alien/ioctl quirks. Placed between stdout ioctl and /dev/tty
ioctl in the fallback chain.
2026-05-14 14:15:30 -04:00
41e2b867be fix: use O_RDONLY (0) for /dev/tty open, guard invalid fd 2026-05-14 14:10:58 -04:00
a227a52c48 fix: use raw O_RDWR=2 constant (sb-unix:o-rdwr doesn't exist) 2026-05-14 13:57:03 -04:00
37f83db35e fix: replace stty/tput fallback with direct ioctl on /dev/tty
uiop:run-program can inherit different terminal state than the
interactive shell. Opening /dev/tty directly and calling ioctl
on that fd is equivalent to what the shell's stty does, and
works regardless of SBCL's fd inheritance quirks.
2026-05-14 13:56:16 -04:00
9b472e281f fix: remove env var fallback for COLUMNS/LINES (SBCL strips them)
SBCL unconditionally strips COLUMNS and LINES from the environment,
so posix-getenv always returns nil for those names. stty size is
the reliable cross-platform fallback for terminal dimensions.
2026-05-14 13:46:13 -04:00
4fa7e98b80 fix: set both dimensions from stty, add tput fallback, export shell vars
- stty size returns 'rows cols'. Old code set only one dimension
  when both env vars were missing; new code sets both.
- Added tput cols/lines as final env-var fallback for systems
  where COLUMNS/LINES are not exported and stty is unavailable.
- Added 'export COLUMNS LINES' to the passepartout script so
  SBCL can read them from the environment.
2026-05-14 13:36:54 -04:00
03ffec75c8 fix: add (require :sb-posix) before SIGWINCH handler
sb-posix is a built-in SBCL contrib, available via require.
Without it, sb-posix:sigwinch causes a reader error and the
eval-when for the SIGWINCH handler never executes, making
*terminal-resized-p* always nil and resize detection broken.
2026-05-14 13:30:38 -04:00
5e9a974981 fix: fill missing env dimension from stty size
When only COLUMNS or only LINES is set, run 'stty size' to get the
other dimension. This handles tmux/screen where only one env var
is exported.
2026-05-14 13:14:22 -04:00
4b9482c09a fix: add env var fallback to backend-size in both backends
ioctl on stdout's fd can return 80x24 even when the terminal is
larger. Add COLUMNS/LINES from the shell as a fallback. Also adds
ioctl-based sizing to simple-backend (was hardcoded 80x24).
2026-05-14 13:12:49 -04:00
83a6e87720 fix: simplify backend-size with direct when guard on values
The try-ioctl function returns (values cols rows) only when both
are valid integers > 0. or propagates complete pairs. This avoids
the nil-in-h crash from partial ioctl results.
2026-05-14 13:11:16 -04:00
db07f8c3a7 fix: guard ioctl results with when to avoid partial values
ignore-errors + ioctl can return (values 80 nil) when the fd exists
but isn't a terminal. or propagates partial values, causing nil in
w or h. Wrap with multiple-value-bind + when to filter.
2026-05-14 13:07:55 -04:00
4a86ae3274 fix: ioctl on stdin fd (0) first, then stdout fd, then env vars
The user's terminal reports 186x60 via stty (which uses stdin fd)
and via COLUMNS/LINES, but ioctl on stdout's fd returns 80x24.
Priority: fd 0 → backend output fd → env vars → 80x24 fallback.
2026-05-14 13:07:05 -04:00
7813e27907 fix: revert to simple ioctl-first with env var fallback
The previous logic (check ioctl result, prefer env when 80x24)
added complexity and crashes. Simple or with env vars after ioctl
is safe: ioctl returns 80x24 on stdout fd mismatch, env vars
(COLUMNS/LINES from shell) provide the correct initial size.
2026-05-14 13:05:53 -04:00
abe4edaffc fix: fallback to stty size when LINES env var is missing
Some environments (tmux) export COLUMNS but not LINES. Use
'stty size' as a fallback for the missing dimension.
2026-05-14 13:04:57 -04:00
1ac6ca02ee fix: handle nil env vars in backend-size
parse-integer errors on nil input. Guard with when before parsing.
2026-05-14 13:03:42 -04:00