Wrap toast bodies, slim the dismiss hint, and stop flicker

Toasts now render three content rows with word-wrapped bodies. The
in-toast "Ctrl-N · N more" hint is replaced by a short
"Ctrl-N · dismiss" entry on the status strip that only appears
while a notification is live.

The box stops flickering while the focused child repaints its TUI:
the overlay is stitched onto the per-chunk PTY write under outMu
and bracketed by DECSET 2026 so supporting terminals buffer the
child's redraw and the box paint into a single frame.
This commit is contained in:
2026-05-15 21:24:18 +01:00
parent ef9b8e71c6
commit cf65d5d707
4 changed files with 220 additions and 56 deletions

View File

@@ -969,14 +969,19 @@ func (st *uiState) OnPTYOut(childID string, chunk []byte) {
st.metrics.recordRender(time.Since(rstart))
}
}
// One write covers the autowrap-disable prelude, the chunk, and the
// autowrap-restore postlude — three syscalls collapsed into one
// under outMu. The three sequences were already emitted atomically
// under the lock; coalescing just halves the syscall count.
wrapped := make([]byte, 0, len(out)+10)
// One write covers the autowrap-disable prelude, the chunk, the
// autowrap-restore postlude, and (when a toast is up) the toast
// overlay — four syscalls collapsed into one under outMu. The
// sequences were already emitted atomically under the lock;
// coalescing just halves the syscall count and makes claude's
// continuous redraws + our toast layer land in the same frame so
// the box doesn't flicker as the child paints over its cells.
overlay := st.toastOverlayBytes()
wrapped := make([]byte, 0, len(out)+len(overlay)+10)
wrapped = append(wrapped, "\x1b[?7l"...)
wrapped = append(wrapped, out...)
wrapped = append(wrapped, "\x1b[?7h"...)
wrapped = append(wrapped, overlay...)
var wstart time.Time
if st.metrics != nil {
wstart = time.Now()
@@ -1219,6 +1224,12 @@ func (st *uiState) drawStatusLine() {
hints = append(hints, "Ctrl-R · restart")
}
}
// Surface the toast-dismiss chord only while a notification is on
// screen — the hint is noise otherwise, and Ctrl-N falls through
// to the focused PTY when the stack is empty.
if st.toasts.length() > 0 {
hints = append(hints, "Ctrl-N · dismiss")
}
right := strings.Join(hints, " · ")
for len(hints) > 1 && int(cols)-len(left)-len(right) < 1 {
hints = hints[1:]