From 4913ee6afdb29d9ffbc27fd417aabbf35aaf1749 Mon Sep 17 00:00:00 2001 From: Timo Clasen Date: Tue, 19 Aug 2025 22:30:54 +0200 Subject: [PATCH] fix(TUI): make it less shimmer (#2076) --- .../tui/internal/components/chat/editor.go | 14 +++-------- .../tui/internal/components/chat/message.go | 8 +++++- .../tui/internal/components/chat/messages.go | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go index 72daf288..c5ecdc21 100644 --- a/packages/tui/internal/components/chat/editor.go +++ b/packages/tui/internal/components/chat/editor.go @@ -382,11 +382,9 @@ func (m *editorComponent) Content() string { status = "waiting for permission" } if m.interruptKeyInDebounce && m.app.CurrentPermission.ID == "" { - bright := t.Accent() - if status == "waiting for permission" { - bright = t.Warning() - } - hint = util.Shimmer(status, t.Background(), t.TextMuted(), bright) + m.spinner.View() + muted( + hint = muted( + status, + ) + m.spinner.View() + muted( " ", ) + base( keyText+" again", @@ -394,11 +392,7 @@ func (m *editorComponent) Content() string { " interrupt", ) } else { - bright := t.Accent() - if status == "waiting for permission" { - bright = t.Warning() - } - hint = util.Shimmer(status, t.Background(), t.TextMuted(), bright) + m.spinner.View() + hint = muted(status) + m.spinner.View() if m.app.CurrentPermission.ID == "" { hint += muted(" ") + base(keyText) + muted(" interrupt") } diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 65c476e1..9a1531fb 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -213,6 +213,7 @@ func renderText( extra string, isThinking bool, isQueued bool, + shimmer bool, fileParts []opencode.FilePart, agentParts []opencode.AgentPart, toolCalls ...opencode.ToolPart, @@ -234,7 +235,12 @@ func renderText( } content = util.ToMarkdown(text, width, backgroundColor) if isThinking { - label := util.Shimmer("Thinking...", backgroundColor, t.TextMuted(), t.Accent()) + var label string + if shimmer { + label = util.Shimmer("Thinking...", backgroundColor, t.TextMuted(), t.Accent()) + } else { + label = styles.NewStyle().Background(backgroundColor).Foreground(t.TextMuted()).Render("Thinking...") + } label = styles.NewStyle().Background(backgroundColor).Width(width - 6).Render(label) content = label + "\n\n" + content } else if strings.TrimSpace(text) == "Generating..." { diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 83e3e33b..97c52972 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -336,6 +336,25 @@ func (m *messagesComponent) renderView() tea.Cmd { width := m.width // always use full width + // Find the last streaming ReasoningPart to only shimmer that one + lastStreamingReasoningID := "" + if m.showThinkingBlocks { + for mi := len(m.app.Messages) - 1; mi >= 0 && lastStreamingReasoningID == ""; mi-- { + if _, ok := m.app.Messages[mi].Info.(opencode.AssistantMessage); !ok { + continue + } + parts := m.app.Messages[mi].Parts + for pi := len(parts) - 1; pi >= 0; pi-- { + if rp, ok := parts[pi].(opencode.ReasoningPart); ok { + if strings.TrimSpace(rp.Text) != "" && rp.Time.End == 0 { + lastStreamingReasoningID = rp.ID + break + } + } + } + } + } + reverted := false revertedMessageCount := 0 revertedToolCount := 0 @@ -437,6 +456,7 @@ func (m *messagesComponent) renderView() tea.Cmd { files, false, isQueued, + false, fileParts, agentParts, ) @@ -513,6 +533,7 @@ func (m *messagesComponent) renderView() tea.Cmd { "", false, false, + false, []opencode.FilePart{}, []opencode.AgentPart{}, toolCallParts..., @@ -530,6 +551,7 @@ func (m *messagesComponent) renderView() tea.Cmd { "", false, false, + false, []opencode.FilePart{}, []opencode.AgentPart{}, toolCallParts..., @@ -600,6 +622,7 @@ func (m *messagesComponent) renderView() tea.Cmd { } if part.Text != "" { text := part.Text + shimmer := part.Time.End == 0 && part.ID == lastStreamingReasoningID content = renderText( m.app, message.Info, @@ -610,6 +633,7 @@ func (m *messagesComponent) renderView() tea.Cmd { "", true, false, + shimmer, []opencode.FilePart{}, []opencode.AgentPart{}, ) @@ -644,6 +668,7 @@ func (m *messagesComponent) renderView() tea.Cmd { "", false, false, + false, []opencode.FilePart{}, []opencode.AgentPart{}, )