From 527490e5df819973afbf5568c6d5f32340608c3a Mon Sep 17 00:00:00 2001 From: kami Date: Fri, 3 Jul 2026 01:00:55 +0400 Subject: [PATCH] fix(tui): surface late clarifications and stop bg bleed on input + action rows - clarDismissed was model-global but only reset when the clarification's session was selected on arrival, so a clarification for an unselected session stayed silently hidden. Reset on every arrival. - input bar (in-session + launcher) only truncated lines, never padded to width, so the terminal backdrop bled through the right. Pad with padTo. - action/narration/user rows prefixed plain (unstyled) spacers around their icons, leaving transparent gaps near the symbols. Use bg-styled spacers. --- apps/tui-go/internal/app/server.go | 4 ++++ apps/tui-go/internal/app/view.go | 14 ++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/tui-go/internal/app/server.go b/apps/tui-go/internal/app/server.go index 4625ef5f..d5773541 100644 --- a/apps/tui-go/internal/app/server.go +++ b/apps/tui-go/internal/app/server.go @@ -544,6 +544,10 @@ func (m *Model) onClarificationRequired(msg protocol.ServerMessage) { if s := m.session(msg.SessionID); s != nil { s.Clar = c } + // A newly-arrived clarification must always surface. clarDismissed is model-global, and + // clarInitState (which clears it) only runs for the selected session — so a clarification + // arriving for an unselected session while the flag was left set would stay silently hidden. + m.clarDismissed = false if msg.SessionID == m.selectedID { m.clarInitState(len(qs)) } diff --git a/apps/tui-go/internal/app/view.go b/apps/tui-go/internal/app/view.go index 381399d9..819b36c8 100644 --- a/apps/tui-go/internal/app/view.go +++ b/apps/tui-go/internal/app/view.go @@ -564,7 +564,7 @@ func (m Model) launcherInput(w int) string { rule := lipgloss.NewStyle().Foreground(ruleColor).Background(t.P.Bg).Render(strings.Repeat("─", w)) boxLines := append([]string{rule}, content...) for i, ln := range boxLines { - boxLines[i] = ansi.Truncate(ln, w, "") + boxLines[i] = padTo(ln, w, t.P.Bg) } box := strings.Join(boxLines, "\n") @@ -713,8 +713,10 @@ func (m Model) renderInput() string { rule := lipgloss.NewStyle().Foreground(ruleColor).Background(t.P.Bg).Render(strings.Repeat("─", m.width)) lines := append([]string{rule}, content...) lines = append(lines, sub) + // Pad (not just truncate) every line to full width with the panel bg so the terminal backdrop + // can't bleed through the right of the placeholder / status sub-line (matches the frame fill). for i, ln := range lines { - lines[i] = ansi.Truncate(ln, m.width, "") + lines[i] = padTo(ln, m.width, t.P.Bg) } return strings.Join(lines, "\n") } @@ -797,7 +799,7 @@ func (m Model) buildTranscriptRows(w int) ([]string, []int) { break } tag := lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Bold(true).Render("›") - rows = append(rows, tag+" "+t.span(e.Content, t.P.FgStrong)) + rows = append(rows, tag+t.span(" ", t.P.Bg)+t.span(e.Content, t.P.FgStrong)) case "router": // The router turn is model output: render it as markdown (bold, // lists, headings, code) wrapped to the panel width. glamour applies @@ -818,9 +820,9 @@ func (m Model) buildTranscriptRows(w int) ([]string, []int) { lines := wrap(e.Content, w-2) for i, ln := range lines { if i == 0 { - rows = append(rows, diamond+" "+t.span(ln, t.P.FgStrong)) + rows = append(rows, diamond+t.span(" ", t.P.Bg)+t.span(ln, t.P.FgStrong)) } else { - rows = append(rows, " "+t.span(ln, t.P.FgStrong)) + rows = append(rows, t.span(" ", t.P.Bg)+t.span(ln, t.P.FgStrong)) } } if s := metricsSuffix(e.Metrics); s != "" { @@ -835,7 +837,7 @@ func (m Model) buildTranscriptRows(w int) ([]string, []int) { break } icon := lipgloss.NewStyle().Foreground(t.P.Accent2).Background(t.P.Bg).Render(e.Icon) - rows = append(rows, " "+icon+" "+t.span(e.Content, t.P.Dim)) + rows = append(rows, t.span(" ", t.P.Bg)+icon+t.span(" ", t.P.Bg)+t.span(e.Content, t.P.Dim)) } } return rows, msgStart