diff options
| author | adamdotdevin <[email protected]> | 2025-08-15 07:43:30 -0500 |
|---|---|---|
| committer | adamdotdevin <[email protected]> | 2025-08-15 07:43:30 -0500 |
| commit | 6e0e87fb2ad5cc440cc5455c4e43cafc9d07aff0 (patch) | |
| tree | 163bce201bfaac03955bda338ce4f87ce95da7c9 /packages/tui | |
| parent | c875d11959d568f9fca7fea29ebf6479b29391e3 (diff) | |
| download | opencode-6e0e87fb2ad5cc440cc5455c4e43cafc9d07aff0.tar.gz opencode-6e0e87fb2ad5cc440cc5455c4e43cafc9d07aff0.zip | |
fix: more commands cleanup
Diffstat (limited to 'packages/tui')
| -rw-r--r-- | packages/tui/internal/app/state.go | 2 | ||||
| -rw-r--r-- | packages/tui/internal/commands/command.go | 89 | ||||
| -rw-r--r-- | packages/tui/internal/components/dialog/find.go | 236 | ||||
| -rw-r--r-- | packages/tui/internal/components/dialog/init.go | 184 | ||||
| -rw-r--r-- | packages/tui/internal/components/fileviewer/fileviewer.go | 278 | ||||
| -rw-r--r-- | packages/tui/internal/components/status/status.go | 2 | ||||
| -rw-r--r-- | packages/tui/internal/tui/tui.go | 107 |
7 files changed, 46 insertions, 852 deletions
diff --git a/packages/tui/internal/app/state.go b/packages/tui/internal/app/state.go index 0e4010bec..2876eb3a9 100644 --- a/packages/tui/internal/app/state.go +++ b/packages/tui/internal/app/state.go @@ -35,8 +35,6 @@ type State struct { Agent string `toml:"agent"` RecentlyUsedModels []ModelUsage `toml:"recently_used_models"` RecentlyUsedAgents []AgentUsage `toml:"recently_used_agents"` - MessagesRight bool `toml:"messages_right"` - SplitDiff bool `toml:"split_diff"` MessageHistory []Prompt `toml:"message_history"` ShowToolDetails *bool `toml:"show_tool_details"` ShowThinkingBlocks *bool `toml:"show_thinking_blocks"` diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go index 19f8586dc..64a78acf1 100644 --- a/packages/tui/internal/commands/command.go +++ b/packages/tui/internal/commands/command.go @@ -108,9 +108,12 @@ func (r CommandRegistry) Matches(msg tea.KeyPressMsg, leader bool) []Command { const ( AppHelpCommand CommandName = "app_help" - SwitchAgentCommand CommandName = "switch_agent" - SwitchAgentReverseCommand CommandName = "switch_agent_reverse" + AppExitCommand CommandName = "app_exit" + ThemeListCommand CommandName = "theme_list" + ProjectInitCommand CommandName = "project_init" EditorOpenCommand CommandName = "editor_open" + ToolDetailsCommand CommandName = "tool_details" + ThinkingBlocksCommand CommandName = "thinking_blocks" SessionNewCommand CommandName = "session_new" SessionListCommand CommandName = "session_list" SessionShareCommand CommandName = "session_share" @@ -118,34 +121,25 @@ const ( SessionInterruptCommand CommandName = "session_interrupt" SessionCompactCommand CommandName = "session_compact" SessionExportCommand CommandName = "session_export" - ToolDetailsCommand CommandName = "tool_details" - ThinkingBlocksCommand CommandName = "thinking_blocks" + MessagesPageUpCommand CommandName = "messages_page_up" + MessagesPageDownCommand CommandName = "messages_page_down" + MessagesHalfPageUpCommand CommandName = "messages_half_page_up" + MessagesHalfPageDownCommand CommandName = "messages_half_page_down" + MessagesFirstCommand CommandName = "messages_first" + MessagesLastCommand CommandName = "messages_last" + MessagesCopyCommand CommandName = "messages_copy" + MessagesUndoCommand CommandName = "messages_undo" + MessagesRedoCommand CommandName = "messages_redo" ModelListCommand CommandName = "model_list" - AgentListCommand CommandName = "agent_list" ModelCycleRecentCommand CommandName = "model_cycle_recent" ModelCycleRecentReverseCommand CommandName = "model_cycle_recent_reverse" - ThemeListCommand CommandName = "theme_list" - FileListCommand CommandName = "file_list" - FileCloseCommand CommandName = "file_close" - FileSearchCommand CommandName = "file_search" - FileDiffToggleCommand CommandName = "file_diff_toggle" - ProjectInitCommand CommandName = "project_init" + AgentListCommand CommandName = "agent_list" + AgentCycleCommand CommandName = "agent_cycle" + AgentCycleReverseCommand CommandName = "agent_cycle_reverse" InputClearCommand CommandName = "input_clear" InputPasteCommand CommandName = "input_paste" InputSubmitCommand CommandName = "input_submit" InputNewlineCommand CommandName = "input_newline" - MessagesPageUpCommand CommandName = "messages_page_up" - MessagesPageDownCommand CommandName = "messages_page_down" - MessagesHalfPageUpCommand CommandName = "messages_half_page_up" - MessagesHalfPageDownCommand CommandName = "messages_half_page_down" - - MessagesFirstCommand CommandName = "messages_first" - MessagesLastCommand CommandName = "messages_last" - - MessagesCopyCommand CommandName = "messages_copy" - MessagesUndoCommand CommandName = "messages_undo" - MessagesRedoCommand CommandName = "messages_redo" - AppExitCommand CommandName = "app_exit" ) func (k Command) Matches(msg tea.KeyPressMsg, leader bool) bool { @@ -185,16 +179,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Trigger: []string{"help"}, }, { - Name: SwitchAgentCommand, - Description: "next agent", - Keybindings: parseBindings("tab"), - }, - { - Name: SwitchAgentReverseCommand, - Description: "previous agent", - Keybindings: parseBindings("shift+tab"), - }, - { Name: EditorOpenCommand, Description: "open editor", Keybindings: parseBindings("<leader>e"), @@ -259,12 +243,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Trigger: []string{"models"}, }, { - Name: AgentListCommand, - Description: "list agents", - Keybindings: parseBindings("<leader>a"), - Trigger: []string{"agents"}, - }, - { Name: ModelCycleRecentCommand, Description: "next recent model", Keybindings: parseBindings("f2"), @@ -275,31 +253,26 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Keybindings: parseBindings("shift+f2"), }, { - Name: ThemeListCommand, - Description: "list themes", - Keybindings: parseBindings("<leader>t"), - Trigger: []string{"themes"}, + Name: AgentListCommand, + Description: "list agents", + Keybindings: parseBindings("<leader>a"), + Trigger: []string{"agents"}, }, - // { - // Name: FileListCommand, - // Description: "list files", - // Keybindings: parseBindings("<leader>f"), - // Trigger: []string{"files"}, - // }, { - Name: FileCloseCommand, - Description: "close file", - Keybindings: parseBindings("esc"), + Name: AgentCycleCommand, + Description: "next agent", + Keybindings: parseBindings("tab"), }, { - Name: FileSearchCommand, - Description: "search file", - Keybindings: parseBindings("<leader>/"), + Name: AgentCycleReverseCommand, + Description: "previous agent", + Keybindings: parseBindings("shift+tab"), }, { - Name: FileDiffToggleCommand, - Description: "split/unified diff", - Keybindings: parseBindings("<leader>v"), + Name: ThemeListCommand, + Description: "list themes", + Keybindings: parseBindings("<leader>t"), + Trigger: []string{"themes"}, }, { Name: ProjectInitCommand, diff --git a/packages/tui/internal/components/dialog/find.go b/packages/tui/internal/components/dialog/find.go deleted file mode 100644 index 40be600c5..000000000 --- a/packages/tui/internal/components/dialog/find.go +++ /dev/null @@ -1,236 +0,0 @@ -package dialog - -import ( - "log/slog" - - tea "github.com/charmbracelet/bubbletea/v2" - "github.com/sst/opencode/internal/completions" - "github.com/sst/opencode/internal/components/list" - "github.com/sst/opencode/internal/components/modal" - "github.com/sst/opencode/internal/layout" - "github.com/sst/opencode/internal/styles" - "github.com/sst/opencode/internal/theme" - "github.com/sst/opencode/internal/util" -) - -const ( - findDialogWidth = 76 -) - -type FindSelectedMsg struct { - FilePath string -} - -type FindDialogCloseMsg struct{} - -type findInitialSuggestionsMsg struct { - suggestions []completions.CompletionSuggestion -} - -type FindDialog interface { - layout.Modal - tea.Model - tea.ViewModel - SetWidth(width int) - SetHeight(height int) - IsEmpty() bool -} - -// findItem is a custom list item for file suggestions -type findItem struct { - suggestion completions.CompletionSuggestion -} - -func (f findItem) Render( - selected bool, - width int, - baseStyle styles.Style, -) string { - t := theme.CurrentTheme() - - itemStyle := baseStyle. - Background(t.BackgroundPanel()). - Foreground(t.TextMuted()) - - if selected { - itemStyle = itemStyle.Foreground(t.Primary()) - } - - return itemStyle.PaddingLeft(1).Render(f.suggestion.Display(itemStyle)) -} - -func (f findItem) Selectable() bool { - return true -} - -type findDialogComponent struct { - completionProvider completions.CompletionProvider - allSuggestions []completions.CompletionSuggestion - width, height int - modal *modal.Modal - searchDialog *SearchDialog - dialogWidth int -} - -func (f *findDialogComponent) Init() tea.Cmd { - return tea.Batch( - f.loadInitialSuggestions(), - f.searchDialog.Init(), - ) -} - -func (f *findDialogComponent) loadInitialSuggestions() tea.Cmd { - return func() tea.Msg { - items, err := f.completionProvider.GetChildEntries("") - if err != nil { - slog.Error("Failed to get initial completion items", "error", err) - return findInitialSuggestionsMsg{suggestions: []completions.CompletionSuggestion{}} - } - return findInitialSuggestionsMsg{suggestions: items} - } -} - -func (f *findDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case findInitialSuggestionsMsg: - // Handle initial suggestions setup - f.allSuggestions = msg.suggestions - - // Calculate dialog width - f.dialogWidth = f.calculateDialogWidth() - - // Initialize search dialog with calculated width - f.searchDialog = NewSearchDialog("Search files...", 10) - f.searchDialog.SetWidth(f.dialogWidth) - - // Convert to list items - items := make([]list.Item, len(f.allSuggestions)) - for i, suggestion := range f.allSuggestions { - items[i] = findItem{suggestion: suggestion} - } - f.searchDialog.SetItems(items) - - // Update modal with calculated width - f.modal = modal.New( - modal.WithTitle("Find Files"), - modal.WithMaxWidth(f.dialogWidth+4), - ) - - return f, f.searchDialog.Init() - - case []completions.CompletionSuggestion: - // Store suggestions and convert to findItem for the search dialog - f.allSuggestions = msg - items := make([]list.Item, len(msg)) - for i, suggestion := range msg { - items[i] = findItem{suggestion: suggestion} - } - f.searchDialog.SetItems(items) - return f, nil - - case SearchSelectionMsg: - // Handle selection from search dialog - now we can directly access the suggestion - if item, ok := msg.Item.(findItem); ok { - return f, f.selectFile(item.suggestion) - } - return f, nil - - case SearchCancelledMsg: - return f, f.Close() - - case SearchQueryChangedMsg: - // Update completion items based on search query - return f, func() tea.Msg { - items, err := f.completionProvider.GetChildEntries(msg.Query) - if err != nil { - slog.Error("Failed to get completion items", "error", err) - return []completions.CompletionSuggestion{} - } - return items - } - - case tea.WindowSizeMsg: - f.width = msg.Width - f.height = msg.Height - // Recalculate width based on new viewport size - oldWidth := f.dialogWidth - f.dialogWidth = f.calculateDialogWidth() - if oldWidth != f.dialogWidth { - f.searchDialog.SetWidth(f.dialogWidth) - // Update modal max width too - f.modal = modal.New( - modal.WithTitle("Find Files"), - modal.WithMaxWidth(f.dialogWidth+4), - ) - } - f.searchDialog.SetHeight(msg.Height) - } - - // Forward all other messages to the search dialog - updatedDialog, cmd := f.searchDialog.Update(msg) - f.searchDialog = updatedDialog.(*SearchDialog) - return f, cmd -} - -func (f *findDialogComponent) View() string { - return f.searchDialog.View() -} - -func (f *findDialogComponent) calculateDialogWidth() int { - // Use fixed width unless viewport is smaller - if f.width > 0 && f.width < findDialogWidth+10 { - return f.width - 10 - } - return findDialogWidth -} - -func (f *findDialogComponent) SetWidth(width int) { - f.width = width - f.searchDialog.SetWidth(f.dialogWidth) -} - -func (f *findDialogComponent) SetHeight(height int) { - f.height = height -} - -func (f *findDialogComponent) IsEmpty() bool { - return f.searchDialog.GetQuery() == "" -} - -func (f *findDialogComponent) selectFile(item completions.CompletionSuggestion) tea.Cmd { - return tea.Sequence( - f.Close(), - util.CmdHandler(FindSelectedMsg{ - FilePath: item.Value, - }), - ) -} - -func (f *findDialogComponent) Render(background string) string { - return f.modal.Render(f.View(), background) -} - -func (f *findDialogComponent) Close() tea.Cmd { - f.searchDialog.SetQuery("") - f.searchDialog.Blur() - return util.CmdHandler(modal.CloseModalMsg{}) -} - -func NewFindDialog(completionProvider completions.CompletionProvider) FindDialog { - component := &findDialogComponent{ - completionProvider: completionProvider, - dialogWidth: findDialogWidth, - allSuggestions: []completions.CompletionSuggestion{}, - } - - // Create search dialog and modal with fixed width - component.searchDialog = NewSearchDialog("Search files...", 10) - component.searchDialog.SetWidth(findDialogWidth) - - component.modal = modal.New( - modal.WithTitle("Find Files"), - modal.WithMaxWidth(findDialogWidth+4), - ) - - return component -} diff --git a/packages/tui/internal/components/dialog/init.go b/packages/tui/internal/components/dialog/init.go deleted file mode 100644 index cf81e5a07..000000000 --- a/packages/tui/internal/components/dialog/init.go +++ /dev/null @@ -1,184 +0,0 @@ -package dialog - -import ( - "github.com/charmbracelet/bubbles/v2/key" - tea "github.com/charmbracelet/bubbletea/v2" - "github.com/charmbracelet/lipgloss/v2" - - "github.com/sst/opencode/internal/styles" - "github.com/sst/opencode/internal/theme" - "github.com/sst/opencode/internal/util" -) - -// InitDialogCmp is a component that asks the user if they want to initialize the project. -type InitDialogCmp struct { - width, height int - selected int - keys initDialogKeyMap -} - -// NewInitDialogCmp creates a new InitDialogCmp. -func NewInitDialogCmp() InitDialogCmp { - return InitDialogCmp{ - selected: 0, - keys: initDialogKeyMap{}, - } -} - -type initDialogKeyMap struct { - Tab key.Binding - Left key.Binding - Right key.Binding - Enter key.Binding - Escape key.Binding - Y key.Binding - N key.Binding -} - -// ShortHelp implements key.Map. -func (k initDialogKeyMap) ShortHelp() []key.Binding { - return []key.Binding{ - key.NewBinding( - key.WithKeys("tab", "left", "right"), - key.WithHelp("tab/←/→", "toggle selection"), - ), - key.NewBinding( - key.WithKeys("enter"), - key.WithHelp("enter", "confirm"), - ), - key.NewBinding( - key.WithKeys("esc", "q"), - key.WithHelp("esc/q", "cancel"), - ), - key.NewBinding( - key.WithKeys("y", "n"), - key.WithHelp("y/n", "yes/no"), - ), - } -} - -// FullHelp implements key.Map. -func (k initDialogKeyMap) FullHelp() [][]key.Binding { - return [][]key.Binding{k.ShortHelp()} -} - -// Init implements tea.Model. -func (m InitDialogCmp) Init() tea.Cmd { - return nil -} - -// Update implements tea.Model. -func (m InitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch { - case key.Matches(msg, key.NewBinding(key.WithKeys("esc"))): - return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false}) - case key.Matches(msg, key.NewBinding(key.WithKeys("tab", "left", "right", "h", "l"))): - m.selected = (m.selected + 1) % 2 - return m, nil - case key.Matches(msg, key.NewBinding(key.WithKeys("enter"))): - return m, util.CmdHandler(CloseInitDialogMsg{Initialize: m.selected == 0}) - case key.Matches(msg, key.NewBinding(key.WithKeys("y"))): - return m, util.CmdHandler(CloseInitDialogMsg{Initialize: true}) - case key.Matches(msg, key.NewBinding(key.WithKeys("n"))): - return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false}) - } - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - } - return m, nil -} - -// View implements tea.Model. -func (m InitDialogCmp) View() string { - t := theme.CurrentTheme() - baseStyle := styles.NewStyle().Foreground(t.Text()) - - // Calculate width needed for content - maxWidth := 60 // Width for explanation text - - title := baseStyle. - Foreground(t.Primary()). - Bold(true). - Width(maxWidth). - Padding(0, 1). - Render("Initialize Project") - - explanation := baseStyle. - Foreground(t.Text()). - Width(maxWidth). - Padding(0, 1). - Render("Initialization generates a new AGENTS.md file that contains information about your codebase, this file serves as memory for each project, you can freely add to it to help the agents be better at their job.") - - question := baseStyle. - Foreground(t.Text()). - Width(maxWidth). - Padding(1, 1). - Render("Would you like to initialize this project?") - - maxWidth = min(maxWidth, m.width-10) - yesStyle := baseStyle - noStyle := baseStyle - - if m.selected == 0 { - yesStyle = yesStyle. - Background(t.Primary()). - Foreground(t.Background()). - Bold(true) - noStyle = noStyle. - Background(t.Background()). - Foreground(t.Primary()) - } else { - noStyle = noStyle. - Background(t.Primary()). - Foreground(t.Background()). - Bold(true) - yesStyle = yesStyle. - Background(t.Background()). - Foreground(t.Primary()) - } - - yes := yesStyle.Padding(0, 3).Render("Yes") - no := noStyle.Padding(0, 3).Render("No") - - buttons := lipgloss.JoinHorizontal(lipgloss.Center, yes, baseStyle.Render(" "), no) - buttons = baseStyle. - Width(maxWidth). - Padding(1, 0). - Render(buttons) - - content := lipgloss.JoinVertical( - lipgloss.Left, - title, - baseStyle.Width(maxWidth).Render(""), - explanation, - question, - buttons, - baseStyle.Width(maxWidth).Render(""), - ) - - return baseStyle.Padding(1, 2). - Border(lipgloss.RoundedBorder()). - BorderBackground(t.Background()). - BorderForeground(t.TextMuted()). - Width(lipgloss.Width(content) + 4). - Render(content) -} - -// SetSize sets the size of the component. -func (m *InitDialogCmp) SetSize(width, height int) { - m.width = width - m.height = height -} - -// CloseInitDialogMsg is a message that is sent when the init dialog is closed. -type CloseInitDialogMsg struct { - Initialize bool -} - -// ShowInitDialogMsg is a message that is sent to show the init dialog. -type ShowInitDialogMsg struct { - Show bool -} diff --git a/packages/tui/internal/components/fileviewer/fileviewer.go b/packages/tui/internal/components/fileviewer/fileviewer.go deleted file mode 100644 index 3df369ab9..000000000 --- a/packages/tui/internal/components/fileviewer/fileviewer.go +++ /dev/null @@ -1,278 +0,0 @@ -package fileviewer - -import ( - "fmt" - "strings" - - tea "github.com/charmbracelet/bubbletea/v2" - - "github.com/sst/opencode/internal/app" - "github.com/sst/opencode/internal/commands" - "github.com/sst/opencode/internal/components/dialog" - "github.com/sst/opencode/internal/components/diff" - "github.com/sst/opencode/internal/layout" - "github.com/sst/opencode/internal/styles" - "github.com/sst/opencode/internal/theme" - "github.com/sst/opencode/internal/util" - "github.com/sst/opencode/internal/viewport" -) - -type DiffStyle int - -const ( - DiffStyleSplit DiffStyle = iota - DiffStyleUnified -) - -type Model struct { - app *app.App - width, height int - viewport viewport.Model - filename *string - content *string - isDiff *bool - diffStyle DiffStyle -} - -type fileRenderedMsg struct { - content string -} - -func New(app *app.App) Model { - vp := viewport.New() - m := Model{ - app: app, - viewport: vp, - diffStyle: DiffStyleUnified, - } - if app.State.SplitDiff { - m.diffStyle = DiffStyleSplit - } - return m -} - -func (m Model) Init() tea.Cmd { - return m.viewport.Init() -} - -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - var cmds []tea.Cmd - - switch msg := msg.(type) { - case fileRenderedMsg: - m.viewport.SetContent(msg.content) - return m, util.CmdHandler(app.FileRenderedMsg{ - FilePath: *m.filename, - }) - case dialog.ThemeSelectedMsg: - return m, m.render() - case tea.KeyMsg: - switch msg.String() { - // TODO - } - } - - vp, cmd := m.viewport.Update(msg) - m.viewport = vp - cmds = append(cmds, cmd) - - return m, tea.Batch(cmds...) -} - -func (m Model) View() string { - if !m.HasFile() { - return "" - } - - header := *m.filename - header = styles.NewStyle(). - Padding(1, 2). - Width(m.width). - Background(theme.CurrentTheme().BackgroundElement()). - Foreground(theme.CurrentTheme().Text()). - Render(header) - - t := theme.CurrentTheme() - - close := m.app.Key(commands.FileCloseCommand) - diffToggle := m.app.Key(commands.FileDiffToggleCommand) - if m.isDiff == nil || *m.isDiff == false { - diffToggle = "" - } - - background := t.Background() - footer := layout.Render( - layout.FlexOptions{ - Background: &background, - Direction: layout.Row, - Justify: layout.JustifyCenter, - Align: layout.AlignStretch, - Width: m.width - 2, - Gap: 5, - }, - layout.FlexItem{ - View: close, - }, - - layout.FlexItem{ - View: diffToggle, - }, - ) - footer = styles.NewStyle().Background(t.Background()).Padding(0, 1).Render(footer) - - return header + "\n" + m.viewport.View() + "\n" + footer -} - -func (m *Model) Clear() (Model, tea.Cmd) { - m.filename = nil - m.content = nil - m.isDiff = nil - return *m, m.render() -} - -func (m *Model) ToggleDiff() (Model, tea.Cmd) { - switch m.diffStyle { - case DiffStyleSplit: - m.diffStyle = DiffStyleUnified - default: - m.diffStyle = DiffStyleSplit - } - return *m, m.render() -} - -func (m *Model) DiffStyle() DiffStyle { - return m.diffStyle -} - -func (m Model) HasFile() bool { - return m.filename != nil && m.content != nil -} - -func (m Model) Filename() string { - if m.filename == nil { - return "" - } - return *m.filename -} - -func (m *Model) SetSize(width, height int) (Model, tea.Cmd) { - if m.width != width || m.height != height { - m.width = width - m.height = height - m.viewport.SetWidth(width) - m.viewport.SetHeight(height - 4) - return *m, m.render() - } - return *m, nil -} - -func (m *Model) SetFile(filename string, content string, isDiff bool) (Model, tea.Cmd) { - m.filename = &filename - m.content = &content - m.isDiff = &isDiff - return *m, m.render() -} - -func (m *Model) render() tea.Cmd { - if m.filename == nil || m.content == nil { - m.viewport.SetContent("") - return nil - } - - return func() tea.Msg { - t := theme.CurrentTheme() - var rendered string - - if m.isDiff != nil && *m.isDiff { - diffResult := "" - var err error - if m.diffStyle == DiffStyleSplit { - diffResult, err = diff.FormatDiff( - *m.filename, - *m.content, - diff.WithWidth(m.width), - ) - } else if m.diffStyle == DiffStyleUnified { - diffResult, err = diff.FormatUnifiedDiff( - *m.filename, - *m.content, - diff.WithWidth(m.width), - ) - } - if err != nil { - rendered = styles.NewStyle(). - Foreground(t.Error()). - Render(fmt.Sprintf("Error rendering diff: %v", err)) - } else { - rendered = strings.TrimRight(diffResult, "\n") - } - } else { - rendered = util.RenderFile( - *m.filename, - *m.content, - m.width, - ) - } - - rendered = styles.NewStyle(). - Width(m.width). - Background(t.BackgroundPanel()). - Render(rendered) - - return fileRenderedMsg{ - content: rendered, - } - } -} - -func (m *Model) ScrollTo(line int) { - m.viewport.SetYOffset(line) -} - -func (m *Model) ScrollToBottom() { - m.viewport.GotoBottom() -} - -func (m *Model) ScrollToTop() { - m.viewport.GotoTop() -} - -func (m *Model) PageUp() (Model, tea.Cmd) { - m.viewport.ViewUp() - return *m, nil -} - -func (m *Model) PageDown() (Model, tea.Cmd) { - m.viewport.ViewDown() - return *m, nil -} - -func (m *Model) HalfPageUp() (Model, tea.Cmd) { - m.viewport.HalfViewUp() - return *m, nil -} - -func (m *Model) HalfPageDown() (Model, tea.Cmd) { - m.viewport.HalfViewDown() - return *m, nil -} - -func (m Model) AtTop() bool { - return m.viewport.AtTop() -} - -func (m Model) AtBottom() bool { - return m.viewport.AtBottom() -} - -func (m Model) ScrollPercent() float64 { - return m.viewport.ScrollPercent() -} - -func (m Model) TotalLineCount() int { - return m.viewport.TotalLineCount() -} - -func (m Model) VisibleLineCount() int { - return m.viewport.VisibleLineCount() -} diff --git a/packages/tui/internal/components/status/status.go b/packages/tui/internal/components/status/status.go index 1634bd6c5..792637825 100644 --- a/packages/tui/internal/components/status/status.go +++ b/packages/tui/internal/components/status/status.go @@ -132,7 +132,7 @@ func (m *statusComponent) View() string { modeForeground = t.BackgroundPanel() } - command := m.app.Commands[commands.SwitchAgentCommand] + command := m.app.Commands[commands.AgentCycleCommand] kb := command.Keybindings[0] key := kb.Key if kb.RequiresLeader { diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 63243dc3d..7b5fc72c5 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -23,7 +23,6 @@ import ( "github.com/sst/opencode/internal/components/chat" cmdcomp "github.com/sst/opencode/internal/components/commands" "github.com/sst/opencode/internal/components/dialog" - "github.com/sst/opencode/internal/components/fileviewer" "github.com/sst/opencode/internal/components/modal" "github.com/sst/opencode/internal/components/status" "github.com/sst/opencode/internal/components/toast" @@ -78,7 +77,6 @@ type Model struct { interruptKeyState InterruptKeyState exitKeyState ExitKeyState messagesRight bool - fileViewer fileviewer.Model } func (a Model) Init() tea.Cmd { @@ -94,13 +92,6 @@ func (a Model) Init() tea.Cmd { cmds = append(cmds, a.status.Init()) cmds = append(cmds, a.completions.Init()) cmds = append(cmds, a.toastManager.Init()) - cmds = append(cmds, a.fileViewer.Init()) - - // Check if we should show the init dialog - cmds = append(cmds, func() tea.Msg { - shouldShow := a.app.Info.Git && a.app.Info.Time.Initialized > 0 - return dialog.ShowInitDialogMsg{Show: shouldShow} - }) return tea.Batch(cmds...) } @@ -586,12 +577,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { slog.Error("Server error", "name", err.Name, "message", err.Data.Message) return a, toast.NewErrorToast(err.Data.Message, toast.WithTitle(string(err.Name))) } - case opencode.EventListResponseEventFileWatcherUpdated: - if a.fileViewer.HasFile() { - if a.fileViewer.Filename() == msg.Properties.File { - return a.openFile(msg.Properties.File) - } - } case tea.WindowSizeMsg: msg.Height -= 2 // Make space for the status bar a.width, a.height = msg.Width, msg.Height @@ -653,8 +638,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Reset exit key state after timeout a.exitKeyState = ExitKeyIdle a.editor.SetExitKeyInDebounce(false) - case dialog.FindSelectedMsg: - return a.openFile(msg.FilePath) case tea.PasteMsg, tea.ClipboardMsg: // Paste events: prioritize modal if active, otherwise editor if a.modal != nil { @@ -753,10 +736,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, cmd) } - fv, cmd := a.fileViewer.Update(msg) - a.fileViewer = fv - cmds = append(cmds, cmd) - return a, tea.Batch(cmds...) } @@ -806,26 +785,6 @@ func (a Model) Cleanup() { a.status.Cleanup() } -func (a Model) openFile(filepath string) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - response, err := a.app.Client.File.Read( - context.Background(), - opencode.FileReadParams{ - Path: opencode.F(filepath), - }, - ) - if err != nil { - slog.Error("Failed to read file", "error", err) - return a, toast.NewErrorToast("Failed to read file") - } - a.fileViewer, cmd = a.fileViewer.SetFile( - filepath, - response.Content, - response.Type == "patch", - ) - return a, cmd -} - func (a Model) home() (string, int, int) { t := theme.CurrentTheme() effectiveWidth := a.width - 4 @@ -1014,11 +973,11 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { case commands.AppHelpCommand: helpDialog := dialog.NewHelpDialog(a.app) a.modal = helpDialog - case commands.SwitchAgentCommand: + case commands.AgentCycleCommand: updated, cmd := a.app.SwitchAgent() a.app = updated cmds = append(cmds, cmd) - case commands.SwitchAgentReverseCommand: + case commands.AgentCycleReverseCommand: updated, cmd := a.app.SwitchAgentReverse() a.app = updated cmds = append(cmds, cmd) @@ -1197,21 +1156,6 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { case commands.ThemeListCommand: themeDialog := dialog.NewThemeDialog() a.modal = themeDialog - // case commands.FileListCommand: - // a.editor.Blur() - // findDialog := dialog.NewFindDialog(a.fileProvider) - // cmds = append(cmds, findDialog.Init()) - // a.modal = findDialog - case commands.FileCloseCommand: - a.fileViewer, cmd = a.fileViewer.Clear() - cmds = append(cmds, cmd) - case commands.FileDiffToggleCommand: - a.fileViewer, cmd = a.fileViewer.ToggleDiff() - cmds = append(cmds, cmd) - a.app.State.SplitDiff = a.fileViewer.DiffStyle() == fileviewer.DiffStyleSplit - cmds = append(cmds, a.app.SaveState()) - case commands.FileSearchCommand: - return a, nil case commands.ProjectInitCommand: cmds = append(cmds, a.app.InitializeProject(context.Background())) case commands.InputClearCommand: @@ -1242,42 +1186,21 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { a.messages = updated.(chat.MessagesComponent) cmds = append(cmds, cmd) case commands.MessagesPageUpCommand: - if a.fileViewer.HasFile() { - a.fileViewer, cmd = a.fileViewer.PageUp() - cmds = append(cmds, cmd) - } else { - updated, cmd := a.messages.PageUp() - a.messages = updated.(chat.MessagesComponent) - cmds = append(cmds, cmd) - } + updated, cmd := a.messages.PageUp() + a.messages = updated.(chat.MessagesComponent) + cmds = append(cmds, cmd) case commands.MessagesPageDownCommand: - if a.fileViewer.HasFile() { - a.fileViewer, cmd = a.fileViewer.PageDown() - cmds = append(cmds, cmd) - } else { - updated, cmd := a.messages.PageDown() - a.messages = updated.(chat.MessagesComponent) - cmds = append(cmds, cmd) - } + updated, cmd := a.messages.PageDown() + a.messages = updated.(chat.MessagesComponent) + cmds = append(cmds, cmd) case commands.MessagesHalfPageUpCommand: - if a.fileViewer.HasFile() { - a.fileViewer, cmd = a.fileViewer.HalfPageUp() - cmds = append(cmds, cmd) - } else { - updated, cmd := a.messages.HalfPageUp() - a.messages = updated.(chat.MessagesComponent) - cmds = append(cmds, cmd) - } + updated, cmd := a.messages.HalfPageUp() + a.messages = updated.(chat.MessagesComponent) + cmds = append(cmds, cmd) case commands.MessagesHalfPageDownCommand: - if a.fileViewer.HasFile() { - a.fileViewer, cmd = a.fileViewer.HalfPageDown() - cmds = append(cmds, cmd) - } else { - updated, cmd := a.messages.HalfPageDown() - a.messages = updated.(chat.MessagesComponent) - cmds = append(cmds, cmd) - } - + updated, cmd := a.messages.HalfPageDown() + a.messages = updated.(chat.MessagesComponent) + cmds = append(cmds, cmd) case commands.MessagesCopyCommand: updated, cmd := a.messages.CopyLastMessage() a.messages = updated.(chat.MessagesComponent) @@ -1327,8 +1250,6 @@ func NewModel(app *app.App) tea.Model { toastManager: toast.NewToastManager(), interruptKeyState: InterruptKeyIdle, exitKeyState: ExitKeyIdle, - fileViewer: fileviewer.New(app), - messagesRight: app.State.MessagesRight, } return model |
