mirror of
https://github.com/aljazceru/opencode.git
synced 2026-01-06 01:14:52 +01:00
feat: add shimmer text rendering (#2027)
This commit is contained in:
@@ -339,6 +339,7 @@ func (m *editorComponent) Content() string {
|
||||
t := theme.CurrentTheme()
|
||||
base := styles.NewStyle().Foreground(t.Text()).Background(t.Background()).Render
|
||||
muted := styles.NewStyle().Foreground(t.TextMuted()).Background(t.Background()).Render
|
||||
|
||||
promptStyle := styles.NewStyle().Foreground(t.Primary()).
|
||||
Padding(0, 0, 0, 1).
|
||||
Bold(true)
|
||||
@@ -381,9 +382,11 @@ func (m *editorComponent) Content() string {
|
||||
status = "waiting for permission"
|
||||
}
|
||||
if m.interruptKeyInDebounce && m.app.CurrentPermission.ID == "" {
|
||||
hint = muted(
|
||||
status,
|
||||
) + m.spinner.View() + muted(
|
||||
bright := t.Accent()
|
||||
if status == "waiting for permission" {
|
||||
bright = t.Warning()
|
||||
}
|
||||
hint = util.Shimmer(status, t.Background(), t.TextMuted(), bright) + m.spinner.View() + muted(
|
||||
" ",
|
||||
) + base(
|
||||
keyText+" again",
|
||||
@@ -391,7 +394,11 @@ func (m *editorComponent) Content() string {
|
||||
" interrupt",
|
||||
)
|
||||
} else {
|
||||
hint = muted(status) + m.spinner.View()
|
||||
bright := t.Accent()
|
||||
if status == "waiting for permission" {
|
||||
bright = t.Warning()
|
||||
}
|
||||
hint = util.Shimmer(status, t.Background(), t.TextMuted(), bright) + m.spinner.View()
|
||||
if m.app.CurrentPermission.ID == "" {
|
||||
hint += muted(" ") + base(keyText) + muted(" interrupt")
|
||||
}
|
||||
|
||||
@@ -234,7 +234,13 @@ func renderText(
|
||||
}
|
||||
content = util.ToMarkdown(text, width, backgroundColor)
|
||||
if isThinking {
|
||||
content = styles.NewStyle().Background(backgroundColor).Foreground(t.TextMuted()).Render("Thinking") + "\n\n" + content
|
||||
label := util.Shimmer("Thinking...", backgroundColor, t.TextMuted(), t.Accent())
|
||||
label = styles.NewStyle().Background(backgroundColor).Width(width - 6).Render(label)
|
||||
content = label + "\n\n" + content
|
||||
} else if strings.TrimSpace(text) == "Generating..." {
|
||||
label := util.Shimmer(text, backgroundColor, t.TextMuted(), t.Text())
|
||||
label = styles.NewStyle().Background(backgroundColor).Width(width - 6).Render(label)
|
||||
content = label
|
||||
}
|
||||
case opencode.UserMessage:
|
||||
ts = time.UnixMilli(int64(casted.Time.Created))
|
||||
@@ -779,7 +785,9 @@ func renderToolTitle(
|
||||
) string {
|
||||
if toolCall.State.Status == opencode.ToolPartStateStatusPending {
|
||||
title := renderToolAction(toolCall.Tool)
|
||||
return styles.NewStyle().Width(width - 6).Render(title)
|
||||
t := theme.CurrentTheme()
|
||||
shiny := util.Shimmer(title, t.BackgroundPanel(), t.TextMuted(), t.Accent())
|
||||
return styles.NewStyle().Width(width - 6).Render(shiny)
|
||||
}
|
||||
|
||||
toolArgs := ""
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
@@ -59,6 +60,7 @@ type messagesComponent struct {
|
||||
lineCount int
|
||||
selection *selection
|
||||
messagePositions map[string]int // map message ID to line position
|
||||
animating bool
|
||||
}
|
||||
|
||||
type selection struct {
|
||||
@@ -99,6 +101,7 @@ func (s selection) coords(offset int) *selection {
|
||||
|
||||
type ToggleToolDetailsMsg struct{}
|
||||
type ToggleThinkingBlocksMsg struct{}
|
||||
type shimmerTickMsg struct{}
|
||||
|
||||
func (m *messagesComponent) Init() tea.Cmd {
|
||||
return tea.Batch(m.viewport.Init())
|
||||
@@ -107,6 +110,15 @@ func (m *messagesComponent) Init() tea.Cmd {
|
||||
func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case shimmerTickMsg:
|
||||
if !m.app.HasAnimatingWork() {
|
||||
m.animating = false
|
||||
return m, nil
|
||||
}
|
||||
return m, tea.Sequence(
|
||||
m.renderView(),
|
||||
tea.Tick(90*time.Millisecond, func(t time.Time) tea.Msg { return shimmerTickMsg{} }),
|
||||
)
|
||||
case tea.MouseClickMsg:
|
||||
slog.Info("mouse", "x", msg.X, "y", msg.Y, "offset", m.viewport.YOffset)
|
||||
y := msg.Y + m.viewport.YOffset
|
||||
@@ -270,6 +282,12 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.dirty {
|
||||
cmds = append(cmds, m.renderView())
|
||||
}
|
||||
|
||||
// Start shimmer ticks if any assistant/tool is in-flight
|
||||
if !m.animating && m.app.HasAnimatingWork() {
|
||||
m.animating = true
|
||||
cmds = append(cmds, tea.Tick(90*time.Millisecond, func(t time.Time) tea.Msg { return shimmerTickMsg{} }))
|
||||
}
|
||||
}
|
||||
|
||||
m.tail = m.viewport.AtBottom()
|
||||
|
||||
Reference in New Issue
Block a user