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":
|
case "approval-shell":
|
||||||
m.connected = true
|
m.connected = true
|
||||||
m.currentModel = "llama-cpp:default"
|
m.currentModel = "llama-cpp:default"
|
||||||
|
|||||||
@@ -63,7 +63,11 @@ func (m Model) approvalBandHeight() int {
|
|||||||
rat = n + 1 // rationale lines + trailing blank
|
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 {
|
if maxH := m.height * 55 / 100; h > maxH {
|
||||||
h = maxH
|
h = maxH
|
||||||
}
|
}
|
||||||
@@ -110,7 +114,11 @@ func (m Model) renderApprovalBand(h int) string {
|
|||||||
if len(a.Rationale) > 0 {
|
if len(a.Rationale) > 0 {
|
||||||
ratBlock = len(a.Rationale) + 1 // rationale lines + trailing blank
|
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 {
|
if diffH < 1 {
|
||||||
diffH = 1
|
diffH = 1
|
||||||
}
|
}
|
||||||
@@ -134,26 +142,45 @@ func (m Model) renderApprovalBand(h int) string {
|
|||||||
for len(body) < diffStart+diffH {
|
for len(body) < diffStart+diffH {
|
||||||
body = append(body, "")
|
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)
|
return t.box("permission required", body, m.width, h, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// approvalActions renders the action row of the approval band, or the live steer
|
// steerLine renders the steering note inside the band: editable (with a caret)
|
||||||
// note when the operator is composing one.
|
// 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 {
|
func (m Model) approvalActions() string {
|
||||||
t := m.theme
|
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 {
|
hint := func(k, l string) string {
|
||||||
return lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Bold(true).Render(k) +
|
return lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Bold(true).Render(k) +
|
||||||
t.span(" "+l, t.P.Faint)
|
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")}
|
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) {
|
if s := m.session(m.selectedID); s != nil && s.Pending != nil && isUnifiedDiff(s.Pending.Preview) {
|
||||||
parts = append(parts, hint("^x", "fullscreen"))
|
parts = append(parts, hint("^x", "fullscreen"))
|
||||||
|
|||||||
Reference in New Issue
Block a user