diff options
| author | Jay V <[email protected]> | 2025-05-21 15:01:25 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-05-21 15:01:25 -0400 |
| commit | 9049295cc961b250be6144585dde322e778534d7 (patch) | |
| tree | c8a2f09ed6cea54eb9587243eb7dbe298fef1b20 /internal/tui/components/chat | |
| parent | 4526b14b17dc49f3ef4f3b1a1d02eff5c6b6b59f (diff) | |
| parent | dff8e77eb6d1709fa1ddeb52d0d9c19afd13d385 (diff) | |
| download | opencode-9049295cc961b250be6144585dde322e778534d7.tar.gz opencode-9049295cc961b250be6144585dde322e778534d7.zip | |
Merge branch 'dev' into docs
Diffstat (limited to 'internal/tui/components/chat')
| -rw-r--r-- | internal/tui/components/chat/editor.go | 112 | ||||
| -rw-r--r-- | internal/tui/components/chat/messages.go (renamed from internal/tui/components/chat/list.go) | 2 | ||||
| -rw-r--r-- | internal/tui/components/chat/sidebar.go | 10 |
3 files changed, 106 insertions, 18 deletions
diff --git a/internal/tui/components/chat/editor.go b/internal/tui/components/chat/editor.go index 0b2c9abb8..212ad5529 100644 --- a/internal/tui/components/chat/editor.go +++ b/internal/tui/components/chat/editor.go @@ -2,6 +2,7 @@ package chat import ( "fmt" + "log/slog" "os" "os/exec" "slices" @@ -16,6 +17,7 @@ import ( "github.com/sst/opencode/internal/message" "github.com/sst/opencode/internal/status" "github.com/sst/opencode/internal/tui/components/dialog" + "github.com/sst/opencode/internal/tui/image" "github.com/sst/opencode/internal/tui/layout" "github.com/sst/opencode/internal/tui/styles" "github.com/sst/opencode/internal/tui/theme" @@ -23,17 +25,23 @@ import ( ) type editorCmp struct { - width int - height int - app *app.App - textarea textarea.Model - attachments []message.Attachment - deleteMode bool + width int + height int + app *app.App + textarea textarea.Model + attachments []message.Attachment + deleteMode bool + history []string + historyIndex int + currentMessage string } type EditorKeyMaps struct { Send key.Binding OpenEditor key.Binding + Paste key.Binding + HistoryUp key.Binding + HistoryDown key.Binding } type bluredEditorKeyMaps struct { @@ -56,6 +64,18 @@ var editorMaps = EditorKeyMaps{ key.WithKeys("ctrl+e"), key.WithHelp("ctrl+e", "open editor"), ), + Paste: key.NewBinding( + key.WithKeys("ctrl+v"), + key.WithHelp("ctrl+v", "paste content"), + ), + HistoryUp: key.NewBinding( + key.WithKeys("up"), + key.WithHelp("up", "previous message"), + ), + HistoryDown: key.NewBinding( + key.WithKeys("down"), + key.WithHelp("down", "next message"), + ), } var DeleteKeyMaps = DeleteAttachmentKeyMaps{ @@ -69,7 +89,7 @@ var DeleteKeyMaps = DeleteAttachmentKeyMaps{ ), DeleteAllAttachments: key.NewBinding( key.WithKeys("r"), - key.WithHelp("ctrl+r+r", "delete all attchments"), + key.WithHelp("ctrl+r+r", "delete all attachments"), ), } @@ -132,6 +152,15 @@ func (m *editorCmp) send() tea.Cmd { m.textarea.Reset() attachments := m.attachments + // Save to history if not empty and not a duplicate of the last entry + if value != "" { + if len(m.history) == 0 || m.history[len(m.history)-1] != value { + m.history = append(m.history, value) + } + m.historyIndex = len(m.history) + m.currentMessage = "" + } + m.attachments = nil if value == "" { return nil @@ -200,6 +229,67 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.deleteMode = false return m, nil } + + if key.Matches(msg, editorMaps.Paste) { + imageBytes, text, err := image.GetImageFromClipboard() + if err != nil { + slog.Error(err.Error()) + return m, cmd + } + if len(imageBytes) != 0 { + attachmentName := fmt.Sprintf("clipboard-image-%d", len(m.attachments)) + attachment := message.Attachment{FilePath: attachmentName, FileName: attachmentName, Content: imageBytes, MimeType: "image/png"} + m.attachments = append(m.attachments, attachment) + } else { + m.textarea.SetValue(m.textarea.Value() + text) + } + return m, cmd + } + + // Handle history navigation with up/down arrow keys + // Only handle history navigation if the filepicker is not open + if m.textarea.Focused() && key.Matches(msg, editorMaps.HistoryUp) && !m.app.IsFilepickerOpen() { + // Get the current line number + currentLine := m.textarea.Line() + + // Only navigate history if we're at the first line + if currentLine == 0 && len(m.history) > 0 { + // Save current message if we're just starting to navigate + if m.historyIndex == len(m.history) { + m.currentMessage = m.textarea.Value() + } + + // Go to previous message in history + if m.historyIndex > 0 { + m.historyIndex-- + m.textarea.SetValue(m.history[m.historyIndex]) + } + return m, nil + } + } + + if m.textarea.Focused() && key.Matches(msg, editorMaps.HistoryDown) && !m.app.IsFilepickerOpen() { + // Get the current line number and total lines + currentLine := m.textarea.Line() + value := m.textarea.Value() + lines := strings.Split(value, "\n") + totalLines := len(lines) + + // Only navigate history if we're at the last line + if currentLine == totalLines-1 { + if m.historyIndex < len(m.history)-1 { + // Go to next message in history + m.historyIndex++ + m.textarea.SetValue(m.history[m.historyIndex]) + } else if m.historyIndex == len(m.history)-1 { + // Return to the current message being composed + m.historyIndex = len(m.history) + m.textarea.SetValue(m.currentMessage) + } + return m, nil + } + } + // Handle Enter key if m.textarea.Focused() && key.Matches(msg, editorMaps.Send) { value := m.textarea.Value() @@ -243,7 +333,6 @@ func (m *editorCmp) SetSize(width, height int) tea.Cmd { m.height = height m.textarea.SetWidth(width - 3) // account for the prompt and padding right m.textarea.SetHeight(height) - m.textarea.SetWidth(width) return nil } @@ -314,7 +403,10 @@ func CreateTextArea(existing *textarea.Model) textarea.Model { func NewEditorCmp(app *app.App) tea.Model { ta := CreateTextArea(nil) return &editorCmp{ - app: app, - textarea: ta, + app: app, + textarea: ta, + history: []string{}, + historyIndex: 0, + currentMessage: "", } } diff --git a/internal/tui/components/chat/list.go b/internal/tui/components/chat/messages.go index baa7c7e6d..d6f252aad 100644 --- a/internal/tui/components/chat/list.go +++ b/internal/tui/components/chat/messages.go @@ -386,6 +386,8 @@ func (m *messagesCmp) help() string { baseStyle.Foreground(t.TextMuted()).Bold(true).Render("+"), baseStyle.Foreground(t.Text()).Bold(true).Render("enter"), baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" for newline,"), + baseStyle.Foreground(t.Text()).Bold(true).Render(" ↑↓"), + baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" for history,"), baseStyle.Foreground(t.Text()).Bold(true).Render(" ctrl+h"), baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" to toggle tool messages"), ) diff --git a/internal/tui/components/chat/sidebar.go b/internal/tui/components/chat/sidebar.go index f2dec7878..973b03ef1 100644 --- a/internal/tui/components/chat/sidebar.go +++ b/internal/tui/components/chat/sidebar.go @@ -71,8 +71,7 @@ func (m *sidebarCmp) View() string { return baseStyle. Width(m.width). PaddingLeft(4). - PaddingRight(2). - Height(m.height - 1). + PaddingRight(1). Render( lipgloss.JoinVertical( lipgloss.Top, @@ -98,14 +97,9 @@ func (m *sidebarCmp) sessionSection() string { sessionValue := baseStyle. Foreground(t.Text()). - Width(m.width - lipgloss.Width(sessionKey)). Render(fmt.Sprintf(": %s", m.app.CurrentSession.Title)) - return lipgloss.JoinHorizontal( - lipgloss.Left, - sessionKey, - sessionValue, - ) + return sessionKey + sessionValue } func (m *sidebarCmp) modifiedFile(filePath string, additions, removals int) string { |
