Fix sidebar repaint and command restart navigation
This commit is contained in:
@@ -298,6 +298,35 @@ func (st *uiState) focusProcess(processID string) {
|
||||
st.drawStatusLine()
|
||||
}
|
||||
|
||||
func (st *uiState) restartFocusedCommand(processID string) {
|
||||
c := st.sess.FindChild(processID)
|
||||
if c == nil || c.Kind != KindCommand {
|
||||
return
|
||||
}
|
||||
layout := st.layoutSnapshot()
|
||||
renderer := newViewportRenderer(layout)
|
||||
st.mu.Lock()
|
||||
st.focusedID = c.ID
|
||||
st.focusedName = c.DisplayName()
|
||||
st.renderer = renderer
|
||||
st.repaintNextPTY = c.ID
|
||||
st.repaintNextPTYBudget = 8
|
||||
st.mu.Unlock()
|
||||
|
||||
st.outMu.Lock()
|
||||
_, _ = os.Stdout.Write(renderer.ClearViewport())
|
||||
st.outMu.Unlock()
|
||||
|
||||
if err := st.sess.Restart(c.ID, syscall.SIGTERM, layout.childCols(), layout.childRows()); err != nil {
|
||||
st.flashError(fmt.Sprintf("restart %s: %v", c.DisplayName(), err))
|
||||
return
|
||||
}
|
||||
st.moveToViewportOrigin()
|
||||
st.drawTabBar()
|
||||
st.drawSidebar()
|
||||
st.drawStatusLine()
|
||||
}
|
||||
|
||||
// updateActiveAgentLocked records the active agent root for the agent
|
||||
// tree section whenever focus lands on an agent or one of its
|
||||
// sub-agents. Focusing a top-level command process leaves the previous
|
||||
@@ -513,14 +542,13 @@ func (st *uiState) OnPTYOut(childID string, chunk []byte) {
|
||||
_, _ = os.Stdout.Write(out)
|
||||
_, _ = os.Stdout.Write([]byte("\x1b[?7h"))
|
||||
st.outMu.Unlock()
|
||||
// RI / IND / NEL / SU / SD / IL / DL scroll content within the host's
|
||||
// scroll region, which spans every column — so any of them drags the
|
||||
// right-hand sidebar's session-tree entries downward along with the
|
||||
// main pane. (Codex emits an 8× RI burst on startup, which produced
|
||||
// the original report.) The viewport renderer flags any chunk that
|
||||
// contained one of those escapes; when set, drop the sidebar cache
|
||||
// so the next drawSidebar repaints over the clobber instead of
|
||||
// hitting the cache and leaving the gap visible.
|
||||
// RI / IND / NEL / SU / SD / IL / DL and bottom-margin LF / VT / FF
|
||||
// scroll content within the host's scroll region, which spans every
|
||||
// column — so any of them drags the right-hand sidebar's session-tree
|
||||
// entries along with the main pane. The viewport renderer flags any
|
||||
// chunk that scrolls; when set, drop the sidebar cache so the next
|
||||
// drawSidebar repaints over the clobber instead of hitting the cache
|
||||
// and leaving the gap visible.
|
||||
scrolled := renderer.TookScrollAction()
|
||||
if scrolled {
|
||||
st.chromeCacheMu.Lock()
|
||||
@@ -639,13 +667,17 @@ func (st *uiState) drawStatusLine() {
|
||||
if trustMsg != "" {
|
||||
left = "[trust] " + trustMsg
|
||||
}
|
||||
// Hints decay shortest-first when the host is narrow so the focused
|
||||
// Hints decay left-to-right when the host is narrow so the focused
|
||||
// child name + ownership note on the left side never get clipped.
|
||||
// Context-specific hints are appended so they survive longest.
|
||||
hints := []string{
|
||||
"Ctrl-A/D · tabs",
|
||||
"Ctrl-W/S · tree",
|
||||
"Ctrl-K · palette",
|
||||
}
|
||||
if c := st.sess.FindChild(focusID); c != nil && c.Kind == KindCommand {
|
||||
hints = append(hints, "Ctrl-R · restart")
|
||||
}
|
||||
right := strings.Join(hints, " · ")
|
||||
for len(hints) > 1 && int(cols)-len(left)-len(right) < 1 {
|
||||
hints = hints[1:]
|
||||
@@ -833,6 +865,7 @@ func (st *uiState) processStdin(chunk []byte) {
|
||||
|
||||
var pendingAction *paletteAction
|
||||
var pendingNavID string
|
||||
var pendingRestartID string
|
||||
|
||||
// Tracks the last arrow direction and the byte offset immediately
|
||||
// after its CSI sequence. Some terminals emit a duplicate adjacent
|
||||
@@ -928,6 +961,14 @@ func (st *uiState) processStdin(chunk []byte) {
|
||||
i += adv
|
||||
break
|
||||
}
|
||||
if hit, adv := matchCtrlChar(chunk, i, 'r'); hit {
|
||||
if c := st.sess.FindChild(st.focusedID); c != nil && c.Kind == KindCommand {
|
||||
flushForward()
|
||||
pendingRestartID = c.ID
|
||||
i += adv
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
forward = append(forward, b)
|
||||
i++
|
||||
@@ -941,6 +982,9 @@ func (st *uiState) processStdin(chunk []byte) {
|
||||
if pendingNavID != "" {
|
||||
st.focusProcess(pendingNavID)
|
||||
}
|
||||
if pendingRestartID != "" {
|
||||
st.restartFocusedCommand(pendingRestartID)
|
||||
}
|
||||
}
|
||||
|
||||
func (st *uiState) openPaletteLocked() {
|
||||
@@ -1035,7 +1079,7 @@ func (st *uiState) closePalette(action paletteAction) {
|
||||
|
||||
case "switch":
|
||||
c := st.sess.FindChild(action.childID)
|
||||
if c == nil || c.Status() != StatusRunning {
|
||||
if c == nil || (c.Kind == KindAgent && c.Status() != StatusRunning) {
|
||||
st.repaintFocused()
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user