fix(tui): approval nav lockups, shell non-diff preview, hint dedup

Three QA-found issues in the approval gate:

- displayState gated the in-session/approval surfaces on hasApproval
  before sessionEntered, so moving the list cursor onto a session with a
  pending gate auto-opened it, and `l` back-to-list couldn't escape
  (the gate re-popped). Require sessionEntered first.
- The band ran every preview through the two-column diff renderer, so a
  shell tool's argv JSON rendered as an identical-column "diff". Detect
  real unified diffs (isUnifiedDiff); render other previews as plain
  text, and drop the ^x fullscreen hint when there's nothing to expand.
- The footer duplicated the band's approve/reject/steer/diff keys. It now
  shows navigation (l back / e events / q quit) instead.
This commit is contained in:
2026-06-03 01:16:04 +04:00
parent 3600ec6897
commit b56f0e88ca
5 changed files with 100 additions and 26 deletions
+42
View File
@@ -78,6 +78,48 @@ func parseUnifiedDiff(diff string) []diffRow {
return rows
}
// isUnifiedDiff reports whether s looks like a unified diff (file/hunk headers)
// rather than arbitrary preview text such as a shell argv JSON. Non-diff previews
// must not go through the two-column renderer (it would show identical columns).
func isUnifiedDiff(s string) bool {
return strings.Contains(s, "@@ -") || strings.HasPrefix(s, "--- ") || strings.Contains(s, "\n--- ")
}
// previewPlainLines splits non-diff preview text into display lines.
func previewPlainLines(s string) []string {
return strings.Split(strings.TrimRight(s, "\n"), "\n")
}
// previewRowCount is the number of content rows a preview occupies — diff rows
// for a real diff, plain lines otherwise. Drives band sizing and scroll bounds.
func previewRowCount(s string) int {
if isUnifiedDiff(s) {
return len(parseUnifiedDiff(s))
}
return len(previewPlainLines(s))
}
// renderPreviewLines renders non-diff preview text as dim plain lines (single
// column), up to maxRows starting at off, each truncated to width.
func (m Model) renderPreviewLines(content string, width, maxRows, off int) []string {
lines := previewPlainLines(content)
if off < 0 {
off = 0
}
if off > len(lines) {
off = len(lines)
}
end := off + maxRows
if end > len(lines) {
end = len(lines)
}
out := make([]string, 0, end-off)
for _, ln := range lines[off:end] {
out = append(out, m.theme.span(truncate(ln, width), m.theme.P.Dim))
}
return out
}
// hunkStarts reads the starting old/new line numbers from an @@ -a,b +c,d @@ marker.
func hunkStarts(h string) (int, int) {
old, newer := 1, 1