Bundles the in-flight work into the first tagged release. See CHANGELOG.md `[0.0.1] - 2026-05-14` for the full per-change list. Highlights: - Sidebar / chrome stability: clamp absolute cursor positioning and printable bytes to the viewport so long-running TUIs (claude, codex) can't spray into the right rail; bound tab bar's row clear to the viewport width so the rail isn't wiped on every tab redraw; flag scroll escapes (RI/IND/NEL/SU/SD/IL/DL) and clamp `CSI 0/1/2 J`/`K` to viewport columns. - Palette: "Spawn process…" form, macros (`sw `, `k `, `sp `), kill entries mark the focused tab, dead agents drop out of the switch list. - Sidebar: split into Processes (session-wide) + Agent Tree (per-active-agent) sections; relaunch indicator; Ctrl+W/S walks the combined list, Ctrl+A/D steps tabs. - MCP: protocol handshake (`initialize`, `tools/list`, `tools/call`, `ping`), `mcp_injection.kind = cli_override / config_env` so codex and opencode pick up the server with no file writes, `lifecycle` help topic and tool-description cleanup-duty pointers. - Lifecycle: orchestrator-spawned children cascade-killed when the parent dies; orchestrator-injected prompts end with CR + delayed Enter so claude submits cleanly.
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
type cursorShifter struct {
|
||||
rowOffset int
|
||||
childRows int // viewport height in child rows; used for DECSTBM resets
|
||||
childCols int // viewport width in child cols; used to clamp CUP/HVP/CHA/HPA columns
|
||||
|
||||
state shifterState
|
||||
buf []byte // bytes accumulated in current escape sequence (incl. introducer)
|
||||
@@ -45,13 +46,25 @@ const (
|
||||
stSOSPMAPCEsc
|
||||
)
|
||||
|
||||
func newCursorShifter(rowOffset, childRows int) *cursorShifter {
|
||||
return &cursorShifter{rowOffset: rowOffset, childRows: childRows}
|
||||
func newCursorShifter(rowOffset, childRows, childCols int) *cursorShifter {
|
||||
return &cursorShifter{rowOffset: rowOffset, childRows: childRows, childCols: childCols}
|
||||
}
|
||||
|
||||
func (cs *cursorShifter) SetGeometry(rowOffset, childRows int) {
|
||||
func (cs *cursorShifter) SetGeometry(rowOffset, childRows, childCols int) {
|
||||
cs.rowOffset = rowOffset
|
||||
cs.childRows = childRows
|
||||
cs.childCols = childCols
|
||||
}
|
||||
|
||||
// clampCol returns col clamped to the viewport's rightmost column, so a
|
||||
// child that drifted into believing it has more horizontal space than
|
||||
// patterm assigned it can't reach into the sidebar. childCols == 0 (an
|
||||
// uninitialised shifter, only seen in tests) disables clamping.
|
||||
func (cs *cursorShifter) clampCol(col int) int {
|
||||
if cs.childCols > 0 && col > cs.childCols {
|
||||
return cs.childCols
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
// Shift consumes a chunk of PTY-master bytes, applies row offsets to
|
||||
@@ -194,11 +207,24 @@ func (cs *cursorShifter) emitCSI() {
|
||||
return
|
||||
}
|
||||
r += cs.rowOffset
|
||||
c = cs.clampCol(c)
|
||||
cs.pending.WriteString("\x1b[")
|
||||
cs.pending.WriteString(strconv.Itoa(r))
|
||||
cs.pending.WriteByte(';')
|
||||
cs.pending.WriteString(strconv.Itoa(c))
|
||||
cs.pending.WriteByte(final)
|
||||
case 'G', '`':
|
||||
// CHA / HPA: absolute column. Clamp to the viewport so a stale
|
||||
// child width can't reach into the sidebar.
|
||||
c, ok := parseOneParam(paramsRaw, 1)
|
||||
if !ok {
|
||||
cs.pending.Write(cs.buf)
|
||||
return
|
||||
}
|
||||
c = cs.clampCol(c)
|
||||
cs.pending.WriteString("\x1b[")
|
||||
cs.pending.WriteString(strconv.Itoa(c))
|
||||
cs.pending.WriteByte(final)
|
||||
case 'd':
|
||||
// VPA: row.
|
||||
r, ok := parseOneParam(paramsRaw, 1)
|
||||
|
||||
Reference in New Issue
Block a user