diff options
| author | Kujtim Hoxha <[email protected]> | 2025-03-23 22:25:31 +0100 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-03-23 22:25:31 +0100 |
| commit | e7258e38aeb46281fda474b8b7fcc3eee35edd9f (patch) | |
| tree | 0ae4a7558b3942519ff137aed7c3cd6a9b473bf5 /internal/tui | |
| parent | 8daa6e774a6e02698c90392e7b2008542f789594 (diff) | |
| download | opencode-e7258e38aeb46281fda474b8b7fcc3eee35edd9f.tar.gz opencode-e7258e38aeb46281fda474b8b7fcc3eee35edd9f.zip | |
initial agent setup
Diffstat (limited to 'internal/tui')
| -rw-r--r-- | internal/tui/components/repl/editor.go | 52 | ||||
| -rw-r--r-- | internal/tui/components/repl/messages.go | 32 | ||||
| -rw-r--r-- | internal/tui/components/repl/sessions.go | 45 | ||||
| -rw-r--r-- | internal/tui/page/repl.go | 1 | ||||
| -rw-r--r-- | internal/tui/tui.go | 7 |
5 files changed, 113 insertions, 24 deletions
diff --git a/internal/tui/components/repl/editor.go b/internal/tui/components/repl/editor.go index 1de216bd0..8d795eb14 100644 --- a/internal/tui/components/repl/editor.go +++ b/internal/tui/components/repl/editor.go @@ -1,8 +1,11 @@ package repl import ( + "strings" + "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" + "github.com/cloudwego/eino/schema" "github.com/kujtimiihoxha/termai/internal/app" "github.com/kujtimiihoxha/termai/internal/tui/layout" "github.com/kujtimiihoxha/vimtea" @@ -13,6 +16,7 @@ type EditorCmp interface { layout.Focusable layout.Sizeable layout.Bordered + layout.Bindings } type editorCmp struct { @@ -25,12 +29,16 @@ type editorCmp struct { height int } -type localKeyMap struct { - SendMessage key.Binding - SendMessageI key.Binding +type editorKeyMap struct { + SendMessage key.Binding + SendMessageI key.Binding + InsertMode key.Binding + NormaMode key.Binding + VisualMode key.Binding + VisualLineMode key.Binding } -var keyMap = localKeyMap{ +var editorKeyMapValue = editorKeyMap{ SendMessage: key.NewBinding( key.WithKeys("enter"), key.WithHelp("enter", "send message normal mode"), @@ -39,6 +47,22 @@ var keyMap = localKeyMap{ key.WithKeys("ctrl+s"), key.WithHelp("ctrl+s", "send message insert mode"), ), + InsertMode: key.NewBinding( + key.WithKeys("i"), + key.WithHelp("i", "insert mode"), + ), + NormaMode: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "normal mode"), + ), + VisualMode: key.NewBinding( + key.WithKeys("v"), + key.WithHelp("v", "visual mode"), + ), + VisualLineMode: key.NewBinding( + key.WithKeys("V"), + key.WithHelp("V", "visual line mode"), + ), } func (m *editorCmp) Init() tea.Cmd { @@ -58,11 +82,11 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch { - case key.Matches(msg, keyMap.SendMessage): + case key.Matches(msg, editorKeyMapValue.SendMessage): if m.editorMode == vimtea.ModeNormal { return m, m.Send() } - case key.Matches(msg, keyMap.SendMessageI): + case key.Matches(msg, editorKeyMapValue.SendMessageI): if m.editorMode == vimtea.ModeInsert { return m, m.Send() } @@ -75,36 +99,30 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } -// Blur implements EditorCmp. func (m *editorCmp) Blur() tea.Cmd { m.focused = false return nil } -// BorderText implements EditorCmp. func (m *editorCmp) BorderText() map[layout.BorderPosition]string { return map[layout.BorderPosition]string{ layout.TopLeftBorder: "New Message", } } -// Focus implements EditorCmp. func (m *editorCmp) Focus() tea.Cmd { m.focused = true return m.editor.Tick() } -// GetSize implements EditorCmp. func (m *editorCmp) GetSize() (int, int) { return m.width, m.height } -// IsFocused implements EditorCmp. func (m *editorCmp) IsFocused() bool { return m.focused } -// SetSize implements EditorCmp. func (m *editorCmp) SetSize(width int, height int) { m.width = width m.height = height @@ -113,8 +131,10 @@ func (m *editorCmp) SetSize(width int, height int) { func (m *editorCmp) Send() tea.Cmd { return func() tea.Msg { - // TODO: Send message - return nil + content := strings.Join(m.editor.GetBuffer().Lines(), "\n") + m.app.Messages.Create(m.sessionID, *schema.UserMessage(content)) + m.app.LLM.SendRequest(m.sessionID, content) + return m.editor.Reset() } } @@ -122,6 +142,10 @@ func (m *editorCmp) View() string { return m.editor.View() } +func (m *editorCmp) BindingKeys() []key.Binding { + return layout.KeyMapToSlice(editorKeyMapValue) +} + func NewEditorCmp(app *app.App) EditorCmp { return &editorCmp{ app: app, diff --git a/internal/tui/components/repl/messages.go b/internal/tui/components/repl/messages.go index cd3ddac71..9d95d98f5 100644 --- a/internal/tui/components/repl/messages.go +++ b/internal/tui/components/repl/messages.go @@ -2,27 +2,47 @@ package repl import ( tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" "github.com/kujtimiihoxha/termai/internal/app" + "github.com/kujtimiihoxha/termai/internal/message" + "github.com/kujtimiihoxha/termai/internal/pubsub" + "github.com/kujtimiihoxha/termai/internal/session" ) type messagesCmp struct { - app *app.App + app *app.App + messages []message.Message + session session.Session } -func (i *messagesCmp) Init() tea.Cmd { +func (m *messagesCmp) Init() tea.Cmd { return nil } -func (i *messagesCmp) Update(_ tea.Msg) (tea.Model, tea.Cmd) { - return i, nil +func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case pubsub.Event[message.Message]: + if msg.Type == pubsub.CreatedEvent { + m.messages = append(m.messages, msg.Payload) + } + case SelectedSessionMsg: + m.session, _ = m.app.Sessions.Get(msg.SessionID) + m.messages, _ = m.app.Messages.List(m.session.ID) + } + return m, nil } func (i *messagesCmp) View() string { - return "Messages" + stringMessages := make([]string, len(i.messages)) + for idx, msg := range i.messages { + stringMessages[idx] = msg.MessageData.Content + } + return lipgloss.JoinVertical(lipgloss.Top, stringMessages...) } func NewMessagesCmp(app *app.App) tea.Model { return &messagesCmp{ - app, + app: app, + messages: []message.Message{}, } } diff --git a/internal/tui/components/repl/sessions.go b/internal/tui/components/repl/sessions.go index aef9b993e..44c870021 100644 --- a/internal/tui/components/repl/sessions.go +++ b/internal/tui/components/repl/sessions.go @@ -7,6 +7,7 @@ import ( "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/kujtimiihoxha/termai/internal/app" + "github.com/kujtimiihoxha/termai/internal/pubsub" "github.com/kujtimiihoxha/termai/internal/session" "github.com/kujtimiihoxha/termai/internal/tui/layout" "github.com/kujtimiihoxha/termai/internal/tui/styles" @@ -42,19 +43,30 @@ type SelectedSessionMsg struct { SessionID string } +type sessionsKeyMap struct { + Select key.Binding +} + +var sessionKeyMapValue = sessionsKeyMap{ + Select: key.NewBinding( + key.WithKeys("enter", " "), + key.WithHelp("enter/space", "select session"), + ), +} + func (i *sessionsCmp) Init() tea.Cmd { existing, err := i.app.Sessions.List() if err != nil { return util.ReportError(err) } if len(existing) == 0 || existing[0].MessageCount > 0 { - session, err := i.app.Sessions.Create( + newSession, err := i.app.Sessions.Create( "New Session", ) if err != nil { return util.ReportError(err) } - existing = append(existing, session) + existing = append([]session.Session{newSession}, existing...) } return tea.Batch( util.CmdHandler(InsertSessionsMsg{existing}), @@ -70,10 +82,35 @@ func (i *sessionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { items[i] = listItem{ id: s.ID, title: s.Title, - desc: fmt.Sprintf("Tokens: %d, Cost: %.2f", s.Tokens, s.Cost), + desc: fmt.Sprintf("Tokens: %d, Cost: %.2f", s.PromptTokens+s.CompletionTokens, s.Cost), } } return i, i.list.SetItems(items) + case pubsub.Event[session.Session]: + if msg.Type == pubsub.UpdatedEvent { + // update the session in the list + items := i.list.Items() + for idx, item := range items { + s := item.(listItem) + if s.id == msg.Payload.ID { + s.title = msg.Payload.Title + s.desc = fmt.Sprintf("Tokens: %d, Cost: %.2f", msg.Payload.PromptTokens+msg.Payload.CompletionTokens, msg.Payload.Cost) + items[idx] = s + break + } + } + return i, i.list.SetItems(items) + } + + case tea.KeyMsg: + switch { + case key.Matches(msg, sessionKeyMapValue.Select): + selected := i.list.SelectedItem() + if selected == nil { + return i, nil + } + return i, util.CmdHandler(SelectedSessionMsg{selected.(listItem).id}) + } } if i.focused { u, cmd := i.list.Update(msg) @@ -129,7 +166,7 @@ func (i *sessionsCmp) BorderText() map[layout.BorderPosition]string { } func (i *sessionsCmp) BindingKeys() []key.Binding { - return layout.KeyMapToSlice(i.list.KeyMap) + return append(layout.KeyMapToSlice(i.list.KeyMap), sessionKeyMapValue.Select) } func NewSessionsCmp(app *app.App) SessionsCmp { diff --git a/internal/tui/page/repl.go b/internal/tui/page/repl.go index dcb1ebcf7..47a924b7b 100644 --- a/internal/tui/page/repl.go +++ b/internal/tui/page/repl.go @@ -16,5 +16,6 @@ func NewReplPage(app *app.App) tea.Model { layout.BentoRightTopPane: repl.NewMessagesCmp(app), layout.BentoRightBottomPane: repl.NewEditorCmp(app), }, + layout.WithBentoLayoutCurrentPane(layout.BentoRightBottomPane), ) } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 72e9174fa..fcc03657c 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -1,10 +1,14 @@ package tui import ( + "log" + "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/kujtimiihoxha/termai/internal/app" + "github.com/kujtimiihoxha/termai/internal/llm" + "github.com/kujtimiihoxha/termai/internal/pubsub" "github.com/kujtimiihoxha/termai/internal/tui/components/core" "github.com/kujtimiihoxha/termai/internal/tui/components/dialog" "github.com/kujtimiihoxha/termai/internal/tui/layout" @@ -66,6 +70,9 @@ func (a appModel) Init() tea.Cmd { func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { + case pubsub.Event[llm.AgentEvent]: + log.Println("Event received") + log.Println(msg) case vimtea.EditorModeMsg: a.editorMode = msg.Mode case tea.WindowSizeMsg: |
