diff options
| author | adamdottv <[email protected]> | 2025-05-08 12:03:59 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-05-08 12:03:59 -0500 |
| commit | f41b7bbd0a0cc731fd7c471b7ee8b26f14a21755 (patch) | |
| tree | bd34e77a07516735a220c210d4930fbe2132a63b /internal/tui/components | |
| parent | e35ea2d448d1a3c9cf0a6fba1318e522fc61d1eb (diff) | |
| download | opencode-f41b7bbd0a0cc731fd7c471b7ee8b26f14a21755.tar.gz opencode-f41b7bbd0a0cc731fd7c471b7ee8b26f14a21755.zip | |
chore: refactoring status updates
Diffstat (limited to 'internal/tui/components')
| -rw-r--r-- | internal/tui/components/chat/editor.go | 22 | ||||
| -rw-r--r-- | internal/tui/components/chat/list.go | 37 | ||||
| -rw-r--r-- | internal/tui/components/core/status.go | 90 | ||||
| -rw-r--r-- | internal/tui/components/dialog/filepicker.go | 21 | ||||
| -rw-r--r-- | internal/tui/components/dialog/models.go | 3 | ||||
| -rw-r--r-- | internal/tui/components/dialog/theme.go | 5 |
6 files changed, 110 insertions, 68 deletions
diff --git a/internal/tui/components/chat/editor.go b/internal/tui/components/chat/editor.go index 982415182..99991cdf7 100644 --- a/internal/tui/components/chat/editor.go +++ b/internal/tui/components/chat/editor.go @@ -12,9 +12,9 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/opencode-ai/opencode/internal/app" - "github.com/opencode-ai/opencode/internal/logging" "github.com/opencode-ai/opencode/internal/message" "github.com/opencode-ai/opencode/internal/session" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/components/dialog" "github.com/opencode-ai/opencode/internal/tui/layout" "github.com/opencode-ai/opencode/internal/tui/styles" @@ -87,7 +87,8 @@ func (m *editorCmp) openEditor(value string) tea.Cmd { tmpfile, err := os.CreateTemp("", "msg_*.md") tmpfile.WriteString(value) if err != nil { - return util.ReportError(err) + status.Error(err.Error()) + return nil } tmpfile.Close() c := exec.Command(editor, tmpfile.Name()) //nolint:gosec @@ -96,14 +97,17 @@ func (m *editorCmp) openEditor(value string) tea.Cmd { c.Stderr = os.Stderr return tea.ExecProcess(c, func(err error) tea.Msg { if err != nil { - return util.ReportError(err) + status.Error(err.Error()) + return nil } content, err := os.ReadFile(tmpfile.Name()) if err != nil { - return util.ReportError(err) + status.Error(err.Error()) + return nil } if len(content) == 0 { - return util.ReportWarn("Message is empty") + status.Warn("Message is empty") + return nil } os.Remove(tmpfile.Name()) attachments := m.attachments @@ -121,7 +125,8 @@ func (m *editorCmp) Init() tea.Cmd { func (m *editorCmp) send() tea.Cmd { if m.app.CoderAgent.IsSessionBusy(m.session.ID) { - return util.ReportWarn("Agent is working, please wait...") + status.Warn("Agent is working, please wait...") + return nil } value := m.textarea.Value() @@ -153,7 +158,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case dialog.AttachmentAddedMsg: if len(m.attachments) >= maxAttachments { - logging.ErrorPersist(fmt.Sprintf("cannot add more than %d images", maxAttachments)) + status.Error(fmt.Sprintf("cannot add more than %d images", maxAttachments)) return m, cmd } m.attachments = append(m.attachments, msg.Attachment) @@ -185,7 +190,8 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } if key.Matches(msg, editorMaps.OpenEditor) { if m.app.CoderAgent.IsSessionBusy(m.session.ID) { - return m, util.ReportWarn("Agent is working, please wait...") + status.Warn("Agent is working, please wait...") + return m, nil } value := m.textarea.Value() m.textarea.Reset() diff --git a/internal/tui/components/chat/list.go b/internal/tui/components/chat/list.go index 83538eb3b..55f76c342 100644 --- a/internal/tui/components/chat/list.go +++ b/internal/tui/components/chat/list.go @@ -15,10 +15,10 @@ import ( "github.com/opencode-ai/opencode/internal/message" "github.com/opencode-ai/opencode/internal/pubsub" "github.com/opencode-ai/opencode/internal/session" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/components/dialog" "github.com/opencode-ai/opencode/internal/tui/styles" "github.com/opencode-ai/opencode/internal/tui/theme" - "github.com/opencode-ai/opencode/internal/tui/util" ) type cacheItem struct { @@ -26,17 +26,17 @@ type cacheItem struct { content []uiMessage } type messagesCmp struct { - app *app.App - width, height int - viewport viewport.Model - session session.Session - messages []message.Message - uiMessages []uiMessage - currentMsgID string - cachedContent map[string]cacheItem - spinner spinner.Model - rendering bool - attachments viewport.Model + app *app.App + width, height int + viewport viewport.Model + session session.Session + messages []message.Message + uiMessages []uiMessage + currentMsgID string + cachedContent map[string]cacheItem + spinner spinner.Model + rendering bool + attachments viewport.Model showToolMessages bool } type renderFinishedMsg struct{} @@ -447,7 +447,8 @@ func (m *messagesCmp) SetSession(session session.Session) tea.Cmd { m.session = session messages, err := m.app.Messages.List(context.Background(), session.ID) if err != nil { - return util.ReportError(err) + status.Error(err.Error()) + return nil } m.messages = messages if len(m.messages) > 0 { @@ -483,11 +484,11 @@ func NewMessagesCmp(app *app.App) tea.Model { vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp vp.KeyMap.HalfPageDown = messageKeys.HalfPageDown return &messagesCmp{ - app: app, - cachedContent: make(map[string]cacheItem), - viewport: vp, - spinner: s, - attachments: attachmets, + app: app, + cachedContent: make(map[string]cacheItem), + viewport: vp, + spinner: s, + attachments: attachmets, showToolMessages: true, } } diff --git a/internal/tui/components/core/status.go b/internal/tui/components/core/status.go index 9460e7f98..84e0be42b 100644 --- a/internal/tui/components/core/status.go +++ b/internal/tui/components/core/status.go @@ -13,10 +13,10 @@ import ( "github.com/opencode-ai/opencode/internal/lsp/protocol" "github.com/opencode-ai/opencode/internal/pubsub" "github.com/opencode-ai/opencode/internal/session" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/components/chat" "github.com/opencode-ai/opencode/internal/tui/styles" "github.com/opencode-ai/opencode/internal/tui/theme" - "github.com/opencode-ai/opencode/internal/tui/util" ) type StatusCmp interface { @@ -25,22 +25,34 @@ type StatusCmp interface { } type statusCmp struct { - info util.InfoMsg - width int - messageTTL time.Duration - lspClients map[string]*lsp.Client - session session.Session + statusMessages []statusMessage + width int + messageTTL time.Duration + lspClients map[string]*lsp.Client + session session.Session +} + +type statusMessage struct { + Level status.Level + Message string + Timestamp time.Time + ExpiresAt time.Time } // clearMessageCmd is a command that clears status messages after a timeout -func (m statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd { - return tea.Tick(ttl, func(time.Time) tea.Msg { - return util.ClearStatusMsg{} +func (m statusCmp) clearMessageCmd() tea.Cmd { + return tea.Tick(time.Second, func(t time.Time) tea.Msg { + return statusCleanupMsg{time: t} }) } +// statusCleanupMsg is a message that triggers cleanup of expired status messages +type statusCleanupMsg struct { + time time.Time +} + func (m statusCmp) Init() tea.Cmd { - return nil + return m.clearMessageCmd() } func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -58,15 +70,26 @@ func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.session = msg.Payload } } - case util.InfoMsg: - m.info = msg - ttl := msg.TTL - if ttl == 0 { - ttl = m.messageTTL + case pubsub.Event[status.StatusMessage]: + if msg.Type == pubsub.CreatedEvent { + statusMsg := statusMessage{ + Level: msg.Payload.Level, + Message: msg.Payload.Message, + Timestamp: msg.Payload.Timestamp, + ExpiresAt: msg.Payload.Timestamp.Add(m.messageTTL), + } + m.statusMessages = append(m.statusMessages, statusMsg) } - return m, m.clearMessageCmd(ttl) - case util.ClearStatusMsg: - m.info = util.InfoMsg{} + case statusCleanupMsg: + // Remove expired messages + var activeMessages []statusMessage + for _, sm := range m.statusMessages { + if sm.ExpiresAt.After(msg.time) { + activeMessages = append(activeMessages, sm) + } + } + m.statusMessages = activeMessages + return m, m.clearMessageCmd() } return m, nil } @@ -128,8 +151,7 @@ func (m statusCmp) View() string { status += tokensStyle } - diagnostics := - styles.Padded().Background(t.BackgroundDarker()).Render(m.projectDiagnostics()) + diagnostics := styles.Padded().Background(t.BackgroundDarker()).Render(m.projectDiagnostics()) model := m.model() @@ -141,25 +163,31 @@ func (m statusCmp) View() string { lipgloss.Width(diagnostics), ) - if m.info.Msg != "" { + // Display the first status message if available + if len(m.statusMessages) > 0 { + sm := m.statusMessages[0] infoStyle := styles.Padded(). Foreground(t.Background()). Width(statusWidth) - switch m.info.Type { - case util.InfoTypeInfo: + + switch sm.Level { + case "info": infoStyle = infoStyle.Background(t.Info()) - case util.InfoTypeWarn: + case "warn": infoStyle = infoStyle.Background(t.Warning()) - case util.InfoTypeError: + case "error": infoStyle = infoStyle.Background(t.Error()) + case "debug": + infoStyle = infoStyle.Background(t.TextMuted()) } // Truncate message if it's longer than available width - msg := m.info.Msg + msg := sm.Message availWidth := statusWidth - 10 if len(msg) > availWidth && availWidth > 0 { msg = msg[:availWidth] + "..." } + status += infoStyle.Render(msg) } else { status += styles.Padded(). @@ -272,8 +300,12 @@ func NewStatusCmp(lspClients map[string]*lsp.Client) StatusCmp { // Initialize the help widget with default text helpWidget = getHelpWidget("") - return &statusCmp{ - messageTTL: 10 * time.Second, - lspClients: lspClients, + statusComponent := &statusCmp{ + statusMessages: []statusMessage{}, + messageTTL: 4 * time.Second, + lspClients: lspClients, } + + return statusComponent } + diff --git a/internal/tui/components/dialog/filepicker.go b/internal/tui/components/dialog/filepicker.go index a61c8ef54..f747dc9af 100644 --- a/internal/tui/components/dialog/filepicker.go +++ b/internal/tui/components/dialog/filepicker.go @@ -18,6 +18,7 @@ import ( "github.com/opencode-ai/opencode/internal/config" "github.com/opencode-ai/opencode/internal/logging" "github.com/opencode-ai/opencode/internal/message" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/image" "github.com/opencode-ai/opencode/internal/tui/styles" "github.com/opencode-ai/opencode/internal/tui/theme" @@ -156,7 +157,7 @@ func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { path = f.cwd.Value() fileInfo, err := os.Stat(path) if err != nil { - logging.ErrorPersist("Invalid path") + status.Error("Invalid path") return f, cmd } isPathDir = fileInfo.IsDir() @@ -225,7 +226,7 @@ func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) { modeInfo := GetSelectedModel(config.Get()) if !modeInfo.SupportsAttachments { - logging.ErrorPersist(fmt.Sprintf("Model %s doesn't support attachments", modeInfo.Name)) + status.Error(fmt.Sprintf("Model %s doesn't support attachments", modeInfo.Name)) return f, nil } if isExtSupported(f.dirs[f.cursor].Name()) { @@ -233,17 +234,17 @@ func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) { selectedFilePath := filepath.Join(f.cwdDetails.directory, "/", f.selectedFile) isFileLarge, err := image.ValidateFileSize(selectedFilePath, maxAttachmentSize) if err != nil { - logging.ErrorPersist("unable to read the image") + status.Error("unable to read the image") return f, nil } if isFileLarge { - logging.ErrorPersist("file too large, max 5MB") + status.Error("file too large, max 5MB") return f, nil } content, err := os.ReadFile(selectedFilePath) if err != nil { - logging.ErrorPersist("Unable read selected file") + status.Error("Unable read selected file") return f, nil } @@ -255,7 +256,7 @@ func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) { return f, util.CmdHandler(AttachmentAddedMsg{attachment}) } if !isExtSupported(f.selectedFile) { - logging.ErrorPersist("Unsupported file") + status.Error("Unsupported file") return f, nil } return f, nil @@ -425,7 +426,7 @@ func readDir(path string, showHidden bool) []os.DirEntry { go func() { dirEntries, err := os.ReadDir(path) if err != nil { - logging.ErrorPersist(err.Error()) + status.Error(err.Error()) errChan <- err return } @@ -457,12 +458,12 @@ func readDir(path string, showHidden bool) []os.DirEntry { return sanitizedDirEntries - case err := <-errChan: - logging.ErrorPersist(fmt.Sprintf("Error reading directory %s", path), err) + case <-errChan: + status.Error(fmt.Sprintf("Error reading directory %s", path)) return []os.DirEntry{} case <-time.After(5 * time.Second): - logging.ErrorPersist(fmt.Sprintf("Timeout reading directory %s", path), nil) + status.Error(fmt.Sprintf("Timeout reading directory %s", path)) return []os.DirEntry{} } } diff --git a/internal/tui/components/dialog/models.go b/internal/tui/components/dialog/models.go index 77c2a02ac..9dc2fdffe 100644 --- a/internal/tui/components/dialog/models.go +++ b/internal/tui/components/dialog/models.go @@ -10,6 +10,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/opencode-ai/opencode/internal/config" "github.com/opencode-ai/opencode/internal/llm/models" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/layout" "github.com/opencode-ai/opencode/internal/tui/styles" "github.com/opencode-ai/opencode/internal/tui/theme" @@ -126,7 +127,7 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.switchProvider(1) } case key.Matches(msg, modelKeys.Enter): - util.ReportInfo(fmt.Sprintf("selected model: %s", m.models[m.selectedIdx].Name)) + status.Info(fmt.Sprintf("selected model: %s", m.models[m.selectedIdx].Name)) return m, util.CmdHandler(ModelSelectedMsg{Model: m.models[m.selectedIdx]}) case key.Matches(msg, modelKeys.Escape): return m, util.CmdHandler(CloseModelDialogMsg{}) diff --git a/internal/tui/components/dialog/theme.go b/internal/tui/components/dialog/theme.go index d35d3e2b6..1794bd631 100644 --- a/internal/tui/components/dialog/theme.go +++ b/internal/tui/components/dialog/theme.go @@ -4,6 +4,7 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/opencode-ai/opencode/internal/status" "github.com/opencode-ai/opencode/internal/tui/layout" "github.com/opencode-ai/opencode/internal/tui/styles" "github.com/opencode-ai/opencode/internal/tui/theme" @@ -106,7 +107,8 @@ func (t *themeDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return t, util.CmdHandler(CloseThemeDialogMsg{}) } if err := theme.SetTheme(selectedTheme); err != nil { - return t, util.ReportError(err) + status.Error(err.Error()) + return t, nil } return t, util.CmdHandler(ThemeChangedMsg{ ThemeName: selectedTheme, @@ -195,4 +197,3 @@ func NewThemeDialogCmp() ThemeDialog { currentTheme: "", } } - |
