fix(tui): keep the steering note visible in the approval band
The steer note replaced the action row while editing and vanished once you esc'd out — so you couldn't see the note or the approve/reject keys, and it was unclear the note still applied (it did: decide() reads the buffer). Now the note is a persistent line above the action row (editable with a caret while typing, a dim reminder once set), and the action row stays visible. Editing hints clarify enter=approve+send, esc=keep note.
This commit is contained in:
@@ -84,6 +84,22 @@ func PreviewFrame(kind string, w, h int) string {
|
||||
}
|
||||
}
|
||||
|
||||
case "approval-steer":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
m.steerBuffer = "also print the current distro and kernel version"
|
||||
if s := m.session("04a546aa"); s != nil {
|
||||
s.CurrentStage = "write_script"
|
||||
s.Pending = &Approval{
|
||||
RequestID: "req-3", SessionID: "04a546aa", Tier: "T3", Risk: "MEDIUM",
|
||||
ToolName: "file_write",
|
||||
Preview: "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -0,0 +1,2 @@\n+#!/usr/bin/env bash\n+echo ok\n",
|
||||
}
|
||||
}
|
||||
|
||||
case "approval-shell":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
|
||||
@@ -63,7 +63,11 @@ func (m Model) approvalBandHeight() int {
|
||||
rat = n + 1 // rationale lines + trailing blank
|
||||
}
|
||||
}
|
||||
h := 2 + 1 + 1 + rat + rows + 1 + 1
|
||||
steer := 0
|
||||
if m.steering || m.steerBuffer != "" {
|
||||
steer = 1
|
||||
}
|
||||
h := 2 + 1 + 1 + rat + rows + 1 + steer + 1
|
||||
if maxH := m.height * 55 / 100; h > maxH {
|
||||
h = maxH
|
||||
}
|
||||
@@ -110,7 +114,11 @@ func (m Model) renderApprovalBand(h int) string {
|
||||
if len(a.Rationale) > 0 {
|
||||
ratBlock = len(a.Rationale) + 1 // rationale lines + trailing blank
|
||||
}
|
||||
diffH := innerH - 4 - ratBlock // header, blank, blank, action row, + rationale block
|
||||
steerBlock := 0
|
||||
if m.steering || m.steerBuffer != "" {
|
||||
steerBlock = 1
|
||||
}
|
||||
diffH := innerH - 4 - ratBlock - steerBlock // header, blank, blank, action row, + rationale/steer
|
||||
if diffH < 1 {
|
||||
diffH = 1
|
||||
}
|
||||
@@ -134,26 +142,45 @@ func (m Model) renderApprovalBand(h int) string {
|
||||
for len(body) < diffStart+diffH {
|
||||
body = append(body, "")
|
||||
}
|
||||
body = append(body, "", m.approvalActions())
|
||||
body = append(body, "")
|
||||
if steerBlock == 1 {
|
||||
body = append(body, m.steerLine())
|
||||
}
|
||||
body = append(body, m.approvalActions())
|
||||
return t.box("permission required", body, m.width, h, true)
|
||||
}
|
||||
|
||||
// approvalActions renders the action row of the approval band, or the live steer
|
||||
// note when the operator is composing one.
|
||||
// steerLine renders the steering note inside the band: editable (with a caret)
|
||||
// while the operator is typing it, or as a persistent reminder once set — so it
|
||||
// stays visible after editing and it's clear it rides along with approve/reject.
|
||||
func (m Model) steerLine() string {
|
||||
t := m.theme
|
||||
label := t.span("steer ", t.P.Faint)
|
||||
caret := lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Render("▏")
|
||||
if m.steering {
|
||||
if m.steerBuffer == "" {
|
||||
return label + caret + t.span(" type a note…", t.P.Faint)
|
||||
}
|
||||
return label + t.span(m.steerBuffer, t.P.FgStrong) + caret
|
||||
}
|
||||
return label + t.span(m.steerBuffer, t.P.Accent2) +
|
||||
t.span(" (sent with your decision · s to edit)", t.P.Faint)
|
||||
}
|
||||
|
||||
// approvalActions renders the action row of the approval band. While the operator
|
||||
// is composing a steer note the keys are different (typing is captured), so the
|
||||
// hints switch to the editing controls.
|
||||
func (m Model) approvalActions() string {
|
||||
t := m.theme
|
||||
if m.steering {
|
||||
note, fg := m.steerBuffer, t.P.FgStrong
|
||||
if note == "" {
|
||||
note, fg = "(steer note — enter to send, esc to cancel)", t.P.Faint
|
||||
}
|
||||
return t.span("steer ", t.P.Faint) + t.span(note, fg) +
|
||||
lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Render("▏")
|
||||
}
|
||||
hint := func(k, l string) string {
|
||||
return lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Bold(true).Render(k) +
|
||||
t.span(" "+l, t.P.Faint)
|
||||
}
|
||||
if m.steering {
|
||||
return strings.Join([]string{
|
||||
hint("enter", "approve + send note"), hint("esc", "keep note, decide below"),
|
||||
}, t.span(" ", t.P.Faint))
|
||||
}
|
||||
parts := []string{hint("a", "approve"), hint("A", "auto"), hint("s", "steer"), hint("r", "reject")}
|
||||
if s := m.session(m.selectedID); s != nil && s.Pending != nil && isUnifiedDiff(s.Pending.Preview) {
|
||||
parts = append(parts, hint("^x", "fullscreen"))
|
||||
|
||||
Reference in New Issue
Block a user