summaryrefslogtreecommitdiffhomepage
path: root/internal/tui
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-03-23 22:25:31 +0100
committerKujtim Hoxha <[email protected]>2025-03-23 22:25:31 +0100
commite7258e38aeb46281fda474b8b7fcc3eee35edd9f (patch)
tree0ae4a7558b3942519ff137aed7c3cd6a9b473bf5 /internal/tui
parent8daa6e774a6e02698c90392e7b2008542f789594 (diff)
downloadopencode-e7258e38aeb46281fda474b8b7fcc3eee35edd9f.tar.gz
opencode-e7258e38aeb46281fda474b8b7fcc3eee35edd9f.zip
initial agent setup
Diffstat (limited to 'internal/tui')
-rw-r--r--internal/tui/components/repl/editor.go52
-rw-r--r--internal/tui/components/repl/messages.go32
-rw-r--r--internal/tui/components/repl/sessions.go45
-rw-r--r--internal/tui/page/repl.go1
-rw-r--r--internal/tui/tui.go7
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: