You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit cf55a58
Browse filesBrowse the repository at this point in the historyBrowse files
@@ -425,14 +425,29 @@ every Enter, because the reader erases exactly one line (hold Enter, get a colum
425
425
leaves one rule per turn behind, travelling up the scrollback. One rule, below the input, is the shape
426
426
that has neither problem.
427
427
428
-
**Wiping the screen.**`/cls` clears the window and leaves the input and the block at the bottom —
429
-
Ctrl-L does the same, bound by the line reader itself rather than by this project (a test pins that, so
430
-
a keymap change cannot quietly take it away). `/clear` wipes the screen *and* drops the history: what is
431
-
still on screen after a `/clear` is a conversation the model no longer has, which reads as if it were
432
-
still in play. Neither touches the terminal emulator's own scrollback — what was written stays where
433
-
the scrollbar can reach it. The block at the bottom is redrawn from a kept copy afterwards: JLine draws
434
-
the pinned region only when its *content* changes, and a wipe does not change the content, it only takes
435
-
it off the screen — so asking it to redraw does nothing and the bottom of the window stays empty.
428
+
**Wiping the screen.**`/cls` clears the window and leaves the input and the block at the bottom, and
429
+
Ctrl-L does exactly the same — the key is bound to this command rather than to the line reader's own
430
+
clear, because the reader's own leaves the input somewhere else. `/clear` wipes the screen *and* drops the
431
+
history: what is still on screen after a `/clear` is a conversation the model no longer has, which reads
432
+
as if it were still in play. Neither touches the terminal emulator's own scrollback — what was written
433
+
stays where the scrollbar can reach it.
434
+
435
+
**It clears by scrolling, not by erasing**, and that is the whole implementation: a window's worth of
436
+
blank lines, printed above the prompt. Erasing was tried in three shapes and each was reported as a
437
+
defect. Erase then print blank rows to push the input back down: erasing clears the *visible* area and
438
+
leaves those lines in the scrollback, so scrolling pulls them straight back into view — text above the
439
+
prompt. Erase then move the cursor: the reader owns the cursor while it prints above the prompt, so a
440
+
cursor move smuggled in there leaves its bookkeeping wrong and stranded a character above the prompt.
441
+
Erase and stop: the input then sat at the top left, and worse — the reader redraws its prompt as a
442
+
*difference* against what it believes is on screen, an erase invalidates that belief, and the measured
443
+
result was no prompt on screen at all. Scrolling has none of those problems, because it is nothing but
444
+
output: the screen goes blank, the scrollback keeps everything, the pinned block is never touched, and
445
+
the cursor ends on its row because printing is what puts it there.
446
+
447
+
**A useful side effect**: a screen that has lost rows on its own (see the resize note below) is repaired
448
+
by `/cls` or Ctrl-L, because printing walks the cursor back down one row per line. That is the same
449
+
recovery as pressing Enter a dozen times, in one keystroke.
450
+
436
451
437
452
**Why the screen is scrolled once at startup.** The line reader draws its prompt where the cursor is,
438
453
which is directly after the last thing printed; only the block below it is pinned to the window. On a
@@ -448,15 +463,94 @@ network. Each is an icon, a space, its value. Rows are cut by **screen columns**
448
463
because an icon is one character and two columns — counting characters lets a row come out wider than
449
464
the window, wrap, and push the pinned block out of place.
450
465
451
-
**Resizing the window** is left to JLine, which resizes the pinned region and re-cuts its rows itself.
452
-
A handler of our own was tried for a reported row of `> > > > >` after dragging the window smaller and
453
-
made it worse: the line reader installs its own handler for as long as it is reading, so ours only
454
-
added a second writer on the terminal while the reader was redrawing. A test drives the real path — a
455
-
size change plus the resize signal, with the reader reading as it does all session — across shrinking,
456
-
growing and a changed row count, and every one draws exactly one prompt. The leftover `> ` row that is
457
-
still reported is therefore not produced there; the remaining suspect is the console reflowing its own
458
-
screen buffer on a resize, which moves lines the program never wrote again and which nothing on this
459
-
side can reproduce. `/cls` or Ctrl-L cleans it up.
466
+
**Output is folded by the agent, not by the console**, to one column less than the window. That is what
467
+
makes dragging the window survivable, and the reason is worth knowing: a line the console wrapped is *one*
468
+
logical line over two screen rows, and Windows joins such lines again when the window is widened. The text
469
+
above then needs fewer rows and everything below moves **up** — including the pinned block's last-drawn rows,
470
+
which end up above the block where nothing writes again. One leftover per drag, which is where the rules
471
+
climbing up the screen came from; narrowing does it in reverse and walks the input upwards. A line that was
472
+
never soft-wrapped has nothing to join. The price: text keeps the line breaks it was printed with, so widening
473
+
the window does not re-flow the conversation — the same trade an append-only console already makes.
474
+
475
+
**Making the window narrower no longer erases the conversation.** The status bar's own housekeeping used to
476
+
clear a band of rows on a geometry change and pulled the start of that band upwards "to account for wrapped
477
+
status lines" — six rows above a three-row bar in a halved window, straight through the answers on screen.
478
+
Measured: three answers, halve the width, two gone. Since a bar row is padded one column short it cannot wrap at
479
+
all, so there was nothing to account for. That was the report "after making it narrower I cannot see it any
480
+
more": the output was never scrolled away, it was erased.
481
+
482
+
**The pinned block never writes the last column of a row**, which is what stopped the stacks of bars. A row
483
+
padded to the full reported width wraps when that width is not the screen's real one — a dragged window reports
484
+
a width before it has applied it — and a wrapped row takes two screen rows where the bar reserves one, so the
485
+
bar's last row wraps past the bottom and scrolls it: the bar moves up and leaves a copy above, one per bad
486
+
render. Leaving the last column alone costs nothing, because each row is addressed rather than reached by
487
+
wrapping, and it also stops the terminal from marking the row as wrapped at all.
488
+
489
+
**Changing the window's WIDTH clears the screen once you let go**, in either direction, and that is deliberate. A console reflows when
490
+
the width changes: lines it had wrapped are joined again, the text above needs fewer rows, and everything below
491
+
moves up — measured on a real console, a 32-row window widened from 86 to 111 columns left the cursor three rows
492
+
higher, exactly the rows that joining freed. The pinned block's rows move up with everything else, the next
493
+
redraw puts a fresh block at the bottom, and the carried-up copy stays above it: one leftover rule per size
494
+
event, and a drag reports one every ~125 ms. It cannot be prevented — the console reflows before the program is
495
+
told the size — and nothing can find the copy afterwards, because a program cannot read the screen back.
496
+
Scrolling removes it, which is what `/cls` does and why that was always the repair. So the agent does it itself
497
+
once the size settles. Narrowing needs the same treatment for the mirror-image reason: there the lines are split
498
+
instead of joined, and the block's own rows — built for the old width — no longer fit and are re-wrapped across
499
+
several screen rows. And then the conversation is **printed again**, folded for the width the window now has: the console remembers
500
+
the lines it printed (the last few hundred, before folding), so after the wipe the recent ones come back and a
501
+
paragraph that needed two rows in a narrow window takes one in a wide one. Dragging only the height changes
502
+
nothing here, because without a width change nothing re-wraps.
503
+
504
+
**After a height change the prompt is printed back down to its row.** JLine draws the prompt wherever the
505
+
cursor happens to be, and a window whose height changes moves the screen's content by however many rows the
506
+
console's buffer gives it — measured: growing a 20-row window to 26 left the input three rows too high, and
507
+
shrinking it to 14 left it three rows too *low*, which means inside the pinned block, where it is drawn over
508
+
and vanishes. The amount cannot be computed, but it is bounded by the height change, and printing moves the
509
+
cursor down one row per line until it reaches its row and then simply scrolls — so the agent prints that many
510
+
blank lines once the size settles. A width change costs nothing, because folding means a width change moves no
511
+
rows at all. Shrinking is not repaired this way (the prompt would have to move *up*, which nothing can do
512
+
without breaking the reader's cursor bookkeeping); `/cls` puts it back.
513
+
514
+
**Resizing the window.** The pinned region is JLine's, but the rows in it are this project's: a rule built
515
+
for a 113-column window stays 113 columns wide until somebody re-makes it, so the block is rebuilt whenever
516
+
the window changes size, from a poll. A resize handler of our own was tried and made things worse — the line
517
+
reader installs its own for as long as it is reading, so ours only added a second writer while the reader was
518
+
redrawing.
519
+
520
+
**And the redraw after the size settles is a repaint rather than a redraw**, which is what fixed "smaller was
521
+
fine, bigger still breaks". The mechanism is worth knowing because it explains a report that survived every
522
+
earlier fix. JLine pads each region row to the width the terminal *reports* and writes the rows one after
523
+
another, relying on the terminal wrapping at the right margin to start the next one — so a screen that is
524
+
**wider** than the reported width never wraps and the next row continues on the same screen line: a rule with
525
+
the activity row cut short beside it, and the last row still looking perfectly correct. Windows gets there by
526
+
a second route that does not correct itself: it **reflows** its screen buffer when the window is widened,
527
+
joining rows it had marked as wrapped, which is every region row. JLine is never told, so its model still
528
+
matches what it wrote, every later update computes an empty difference and writes **nothing**, and the joined
529
+
rows stay for the rest of the session — which is exactly why holding Enter repaired it and no redraw did.
530
+
Once the size stops changing, the block is therefore repainted unconditionally instead of compared.
531
+
532
+
**The deeper half of that was in the library.** One measurement: a status update sends a single cursor
533
+
address and then writes its rows back to back, each padded to the reported width — the second row begins on
534
+
a new screen row only because writing the last column of the first one made the terminal *wrap*. So a screen
535
+
wider than the reported width does not merely truncate: **every row of the block lands on one screen row,
536
+
side by side**. And since the block is reserved from the bottom, what that pushes past the window ends up in
537
+
the output area *above* it, where nothing writes again — one fragment per drag, which is why a dozen drags
538
+
left a screen full of rule fragments and why `/cls` was the only thing that cleared them. The pinned region
539
+
now addresses each of its rows instead of trusting the wrap.
540
+
That needs a way to ask for a repaint, which the library did not have — `redraw()` is the same diff under another name, and `reset()` forgets the scroll region as well, so the next update grows the region by scrolling and leaves the old rows *above* the block. So a fifth fix is carried against JLine: a `Status.repaint()` that clears the model and nothing else. It is the only one of the five that adds a method, so it is called reflectively and skipped when absent: the agent must stay buildable with whatever JLine a copy of it finds, and the pom names the released version on purpose.
541
+
542
+
What that cannot reach: the prompt is drawn by a display of its own with the same difference logic and no way
543
+
in from outside, so a reflow that damages the prompt's row is still beyond repair from here. `/cls` or Ctrl-L
544
+
cleans that up, because they only print.
545
+
546
+
547
+
**A known JLine defect on Windows.** With a pinned block, typing without pressing Enter and then
548
+
widening the window shows the prompt and the typed text once per keystroke, side by side. The repeats
549
+
are written *while typing* — clipped past the right edge, so widening merely reveals them. It is
550
+
reproducible with plain JLine and a three-row status block and **no** code from this project, is not
551
+
fixed in JLine 4.4.6, and is not caused by resize signals; the write-up is in
0 commit comments