This batches the in-flight [Unreleased] block from CHANGELOG.md into a single commit. Highlights: - Real MCP protocol layer (initialize / tools/list / tools/call) so vendor MCP clients can complete the handshake against the per-PID socket. Legacy direct-dispatch preserved for the harness. - New mcp_injection kinds — cli_override for codex, config_env for opencode — joining the existing env-var and config_file paths so patterm can slot into more agents without touching their real config or auth. - Ctrl+A/D and Ctrl+W/S focus navigation across tabs and intra-tab process lists, recognised in legacy / kitty CSI u / xterm modifyOtherKeys encodings. - Palette macros (sw / k / sp ) and reordering so open sessions surface above spawn-new entries. - Two-row tab bar, sidebar/tabbar/status chrome cache, viewport-wipe on agent spawn, CR-terminated orchestrator injections, and split- Enter PTY writes so paste-detecting TUIs see Enter as a key event. Also fixes the bug logged in TODO: claude's Ctrl+O tool-call expansion emits CSI 0 J, which the viewport renderer was forwarding verbatim — wiping the sidebar to the right of the cursor and leaving the chrome cache convinced nothing had changed. CSI 0 J and CSI 1 J are now translated into per-row ECH sequences clamped to the viewport, same as CSI 2 J and CSI K already were. Agent guides (CLAUDE.md / AGENTS.md) now spell out the TODO->CHANGELOG workflow so completed items land in the changelog rather than as ticked entries left behind in TODO.
78 lines
1.5 KiB
Go
78 lines
1.5 KiB
Go
package app
|
|
|
|
// terminalLayout is the single source of truth for host chrome and the
|
|
// child PTY viewport.
|
|
type terminalLayout struct {
|
|
hostCols uint16
|
|
hostRows uint16
|
|
|
|
mainLeft uint16
|
|
mainTop uint16
|
|
mainCols uint16
|
|
mainRows uint16
|
|
|
|
sidebarVisible bool
|
|
sidebarLeft uint16
|
|
sidebarWidth uint16
|
|
|
|
statusRow uint16
|
|
}
|
|
|
|
func newTerminalLayout(cols, rows uint16) terminalLayout {
|
|
if cols == 0 {
|
|
cols = 1
|
|
}
|
|
if rows == 0 {
|
|
rows = 1
|
|
}
|
|
|
|
l := terminalLayout{
|
|
hostCols: cols,
|
|
hostRows: rows,
|
|
mainLeft: 1,
|
|
mainTop: tabBarRows + 1,
|
|
mainCols: cols,
|
|
mainRows: 1,
|
|
statusRow: rows,
|
|
}
|
|
|
|
if int(cols) > sidebarCols+10 {
|
|
l.sidebarVisible = true
|
|
l.sidebarWidth = sidebarCols
|
|
l.sidebarLeft = cols - sidebarCols + 1
|
|
// The sidebar's left border lives one column to the left of
|
|
// sidebarLeft. The viewport must stop one column short of that
|
|
// border or child output (and clearViewport ECH) would erase
|
|
// it whenever the cursor reached the right margin.
|
|
l.mainCols = cols - sidebarCols - 1
|
|
}
|
|
|
|
reservedRows := tabBarRows + statusRows
|
|
if int(rows) > reservedRows {
|
|
l.mainRows = rows - uint16(reservedRows)
|
|
}
|
|
return l
|
|
}
|
|
|
|
func (l terminalLayout) childCols() uint16 {
|
|
if l.mainCols == 0 {
|
|
return 1
|
|
}
|
|
return l.mainCols
|
|
}
|
|
|
|
func (l terminalLayout) childRows() uint16 {
|
|
if l.mainRows == 0 {
|
|
return 1
|
|
}
|
|
return l.mainRows
|
|
}
|
|
|
|
func ptyRows(hostRows uint16) uint16 {
|
|
return newTerminalLayout(1, hostRows).childRows()
|
|
}
|
|
|
|
func ptyCols(hostCols uint16) uint16 {
|
|
return newTerminalLayout(hostCols, 1).childCols()
|
|
}
|