Polish chrome and rework tab-switch repaint
Module renamed github.com/harrybrwn/patterm → github.com/hjbdev/patterm across imports. Chrome: - Palette redrawn with rounded box-drawing borders, accent left-bar for the selected item, dim hints, and a separator-aware footer. - Tab bar grew from 1 row to 3: labels with breathing room, a dim argv subtitle truncated to each tab's width, and an accent thick underline for the focused tab with a faint divider extending across the rest of the host width. Layout, viewport-renderer, and screen- renderer tests updated for the new mainTop. - Sidebar reuses the same palette: accent section headers, `▎` selection marker, `●`/`○` status glyphs, dim previews. - Shared SGR constants moved into internal/app/style.go. Palette input: - Adjacent duplicate arrow events (legacy `\x1b[B` + kitty `\x1b[57353u` for one keypress, or two of the same form) are now collapsed via peekArrowEvent + chunk-level dedupe in processStdin. - On open, push `\x1b[>0u` onto the host's kitty keyboard stack so palette input is in plain legacy mode regardless of what the child pushed (codex/ratatui pushes its own flags which had been leaking to the host). Popped on close. Tab-switch repaint (repaintFocused): - Use the emulator's SerializeVT bytes (with SGR / cursor / DECSTBM / tabstops) instead of plain text, fed through the per-focused viewport renderer so the shifter translates row positions. - Prelude resets host SGR / DECOM / DECSTBM (pinned to viewport) / cursor visibility before the replay, so leftover modes from the previously-focused child don't distort the new snapshot. - Re-emit the saved cursor as a child-space CUP after the serialized bytes so the host cursor lands at the emulator's actual position (overriding DECSTBM's home side-effect and the tabstop-setup CHA sequences) AND the renderer's vr.row/vr.col get re-synced via trackCSI. - cursorShifter now carries childRows and rewrites empty `\x1b[r` to `\x1b[<mainTop>;<mainBottom>r` (host coords) — the default (1,1) shifted to (4,4) was producing a one-row scrolling region that scroll-exploded the replay. - After the snapshot lands, nudge the focused child with a one-row PTY winsize toggle so the kernel emits SIGWINCH and ratatui-style TUIs throw away their diff state and emit a fresh frame. Codex still renders incorrectly after a focus switch; see TODO.md "Switch-back render divergence" for the deep investigation handoff.
This commit is contained in:
@@ -22,10 +22,11 @@ import (
|
||||
// CSI commands.
|
||||
type cursorShifter struct {
|
||||
rowOffset int
|
||||
childRows int // viewport height in child rows; used for DECSTBM resets
|
||||
|
||||
state shifterState
|
||||
buf []byte // bytes accumulated in current escape sequence (incl. introducer)
|
||||
csiPrefix []byte // private prefix bytes (?, >, =) after CSI
|
||||
buf []byte // bytes accumulated in current escape sequence (incl. introducer)
|
||||
csiPrefix []byte // private prefix bytes (?, >, =) after CSI
|
||||
pending strings.Builder
|
||||
}
|
||||
|
||||
@@ -44,12 +45,13 @@ const (
|
||||
stSOSPMAPCEsc
|
||||
)
|
||||
|
||||
func newCursorShifter(rowOffset int) *cursorShifter {
|
||||
return &cursorShifter{rowOffset: rowOffset}
|
||||
func newCursorShifter(rowOffset, childRows int) *cursorShifter {
|
||||
return &cursorShifter{rowOffset: rowOffset, childRows: childRows}
|
||||
}
|
||||
|
||||
func (cs *cursorShifter) SetRowOffset(off int) {
|
||||
cs.rowOffset = off
|
||||
func (cs *cursorShifter) SetGeometry(rowOffset, childRows int) {
|
||||
cs.rowOffset = rowOffset
|
||||
cs.childRows = childRows
|
||||
}
|
||||
|
||||
// Shift consumes a chunk of PTY-master bytes, applies row offsets to
|
||||
@@ -209,8 +211,19 @@ func (cs *cursorShifter) emitCSI() {
|
||||
cs.pending.WriteString(strconv.Itoa(r))
|
||||
cs.pending.WriteByte(final)
|
||||
case 'r':
|
||||
// DECSTBM: top;bot. Empty resets to full region; we still
|
||||
// shift to keep the chrome row reserved.
|
||||
// DECSTBM: top;bot. Empty params (\x1b[r) means "reset to the
|
||||
// full screen" from the child's point of view — for us that's
|
||||
// the viewport, not the host's full screen. Rewriting it as
|
||||
// (1,1)+offset would produce \x1b[4;4r, a one-row region that
|
||||
// causes catastrophic scroll-up of the replayed snapshot.
|
||||
if len(paramsRaw) == 0 && cs.childRows > 0 {
|
||||
cs.pending.WriteString("\x1b[")
|
||||
cs.pending.WriteString(strconv.Itoa(cs.rowOffset + 1))
|
||||
cs.pending.WriteByte(';')
|
||||
cs.pending.WriteString(strconv.Itoa(cs.rowOffset + cs.childRows))
|
||||
cs.pending.WriteByte(final)
|
||||
return
|
||||
}
|
||||
top, bot, ok := parseTwoParams(paramsRaw)
|
||||
if !ok {
|
||||
cs.pending.Write(cs.buf)
|
||||
|
||||
Reference in New Issue
Block a user