summaryrefslogtreecommitdiffhomepage
path: root/internal/tui/tui.go
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-04-16 20:06:23 +0200
committerKujtim Hoxha <[email protected]>2025-04-21 13:42:00 +0200
commitbbfa60c787f2ec459f1689b9a650ddbec9693ed9 (patch)
treef7f2aa31c460c8cc22ec40cc299c386277152241 /internal/tui/tui.go
parent76b4065f17b87a63092acfd98c997bab53700b35 (diff)
downloadopencode-bbfa60c787f2ec459f1689b9a650ddbec9693ed9.tar.gz
opencode-bbfa60c787f2ec459f1689b9a650ddbec9693ed9.zip
reimplement agent,provider and add file history
Diffstat (limited to 'internal/tui/tui.go')
-rw-r--r--internal/tui/tui.go277
1 files changed, 141 insertions, 136 deletions
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index 1b1a1ed50..dff7ad63d 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -1,8 +1,6 @@
package tui
import (
- "context"
-
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@@ -12,47 +10,41 @@ import (
"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/components/repl"
"github.com/kujtimiihoxha/termai/internal/tui/layout"
"github.com/kujtimiihoxha/termai/internal/tui/page"
"github.com/kujtimiihoxha/termai/internal/tui/util"
- "github.com/kujtimiihoxha/vimtea"
)
type keyMap struct {
- Logs key.Binding
- Return key.Binding
- Back key.Binding
- Quit key.Binding
- Help key.Binding
+ Logs key.Binding
+ Quit key.Binding
+ Help key.Binding
}
var keys = keyMap{
Logs: key.NewBinding(
- key.WithKeys("L"),
- key.WithHelp("L", "logs"),
- ),
- Return: key.NewBinding(
- key.WithKeys("esc"),
- key.WithHelp("esc", "close"),
- ),
- Back: key.NewBinding(
- key.WithKeys("backspace"),
- key.WithHelp("backspace", "back"),
+ key.WithKeys("ctrl+l"),
+ key.WithHelp("ctrl+L", "logs"),
),
+
Quit: key.NewBinding(
- key.WithKeys("ctrl+c", "q"),
- key.WithHelp("ctrl+c/q", "quit"),
+ key.WithKeys("ctrl+c"),
+ key.WithHelp("ctrl+c", "quit"),
),
Help: key.NewBinding(
- key.WithKeys("?"),
- key.WithHelp("?", "toggle help"),
+ key.WithKeys("ctrl+_"),
+ key.WithHelp("ctrl+?", "toggle help"),
),
}
-var replKeyMap = key.NewBinding(
- key.WithKeys("N"),
- key.WithHelp("N", "new session"),
+var returnKey = key.NewBinding(
+ key.WithKeys("esc"),
+ key.WithHelp("esc", "close"),
+)
+
+var logsKeyReturnKey = key.NewBinding(
+ key.WithKeys("backspace"),
+ key.WithHelp("backspace", "go back"),
)
type appModel struct {
@@ -62,18 +54,30 @@ type appModel struct {
pages map[page.PageID]tea.Model
loadedPages map[page.PageID]bool
status tea.Model
- help core.HelpCmp
- dialog core.DialogCmp
app *app.App
- dialogVisible bool
- editorMode vimtea.EditorMode
- showHelp bool
+
+ showPermissions bool
+ permissions dialog.PermissionDialogCmp
+
+ showHelp bool
+ help dialog.HelpCmp
+
+ showQuit bool
+ quit dialog.QuitDialog
}
func (a appModel) Init() tea.Cmd {
+ var cmds []tea.Cmd
cmd := a.pages[a.currentPage].Init()
a.loadedPages[a.currentPage] = true
- return cmd
+ cmds = append(cmds, cmd)
+ cmd = a.status.Init()
+ cmds = append(cmds, cmd)
+ cmd = a.quit.Init()
+ cmds = append(cmds, cmd)
+ cmd = a.help.Init()
+ cmds = append(cmds, cmd)
+ return tea.Batch(cmds...)
}
func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -81,22 +85,20 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
- var cmds []tea.Cmd
msg.Height -= 1 // Make space for the status bar
a.width, a.height = msg.Width, msg.Height
a.status, _ = a.status.Update(msg)
-
- uh, _ := a.help.Update(msg)
- a.help = uh.(core.HelpCmp)
-
- p, cmd := a.pages[a.currentPage].Update(msg)
+ a.pages[a.currentPage], cmd = a.pages[a.currentPage].Update(msg)
cmds = append(cmds, cmd)
- a.pages[a.currentPage] = p
- d, cmd := a.dialog.Update(msg)
- cmds = append(cmds, cmd)
- a.dialog = d.(core.DialogCmp)
+ prm, permCmd := a.permissions.Update(msg)
+ a.permissions = prm.(dialog.PermissionDialogCmp)
+ cmds = append(cmds, permCmd)
+
+ help, helpCmd := a.help.Update(msg)
+ a.help = help.(dialog.HelpCmp)
+ cmds = append(cmds, helpCmd)
return a, tea.Batch(cmds...)
@@ -141,7 +143,9 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Permission
case pubsub.Event[permission.PermissionRequest]:
- return a, dialog.NewPermissionDialogCmd(msg.Payload)
+ a.showPermissions = true
+ a.permissions.SetPermissions(msg.Payload)
+ return a, nil
case dialog.PermissionResponseMsg:
switch msg.Action {
case dialog.PermissionAllow:
@@ -151,91 +155,71 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case dialog.PermissionDeny:
a.app.Permissions.Deny(msg.Permission)
}
-
- // Dialog
- case core.DialogMsg:
- d, cmd := a.dialog.Update(msg)
- a.dialog = d.(core.DialogCmp)
- a.dialogVisible = true
- return a, cmd
- case core.DialogCloseMsg:
- d, cmd := a.dialog.Update(msg)
- a.dialog = d.(core.DialogCmp)
- a.dialogVisible = false
- return a, cmd
-
- // Editor
- case vimtea.EditorModeMsg:
- a.editorMode = msg.Mode
+ a.showPermissions = false
+ return a, nil
case page.PageChangeMsg:
return a, a.moveToPage(msg.ID)
+
+ case dialog.CloseQuitMsg:
+ a.showQuit = false
+ return a, nil
+
case tea.KeyMsg:
- if a.editorMode == vimtea.ModeNormal {
- switch {
- case key.Matches(msg, keys.Quit):
- return a, dialog.NewQuitDialogCmd()
- case key.Matches(msg, keys.Back):
- if a.previousPage != "" {
- return a, a.moveToPage(a.previousPage)
- }
- case key.Matches(msg, keys.Return):
- if a.showHelp {
- a.ToggleHelp()
- return a, nil
- }
- case key.Matches(msg, replKeyMap):
- if a.currentPage == page.ReplPage {
- sessions, err := a.app.Sessions.List(context.Background())
- if err != nil {
- return a, util.CmdHandler(util.ReportError(err))
- }
- lastSession := sessions[0]
- if lastSession.MessageCount == 0 {
- return a, util.CmdHandler(repl.SelectedSessionMsg{SessionID: lastSession.ID})
- }
- s, err := a.app.Sessions.Create(context.Background(), "New Session")
- if err != nil {
- return a, util.CmdHandler(util.ReportError(err))
- }
- return a, util.CmdHandler(repl.SelectedSessionMsg{SessionID: s.ID})
- }
- // case key.Matches(msg, keys.Logs):
- // return a, a.moveToPage(page.LogsPage)
- case msg.String() == "O":
- return a, a.moveToPage(page.ReplPage)
- case key.Matches(msg, keys.Help):
- a.ToggleHelp()
+ switch {
+ case key.Matches(msg, keys.Quit):
+ a.showQuit = !a.showQuit
+ if a.showHelp {
+ a.showHelp = false
+ }
+ return a, nil
+ case key.Matches(msg, logsKeyReturnKey):
+ if a.currentPage == page.LogsPage {
+ return a, a.moveToPage(page.ChatPage)
+ }
+ case key.Matches(msg, returnKey):
+ if a.showQuit {
+ a.showQuit = !a.showQuit
+ return a, nil
+ }
+ if a.showHelp {
+ a.showHelp = !a.showHelp
+ return a, nil
+ }
+ case key.Matches(msg, keys.Logs):
+ return a, a.moveToPage(page.LogsPage)
+ case key.Matches(msg, keys.Help):
+ if a.showQuit {
return a, nil
}
+ a.showHelp = !a.showHelp
+ return a, nil
}
}
- if a.dialogVisible {
- d, cmd := a.dialog.Update(msg)
- a.dialog = d.(core.DialogCmp)
- cmds = append(cmds, cmd)
- return a, tea.Batch(cmds...)
+ if a.showQuit {
+ q, quitCmd := a.quit.Update(msg)
+ a.quit = q.(dialog.QuitDialog)
+ cmds = append(cmds, quitCmd)
+ // Only block key messages send all other messages down
+ if _, ok := msg.(tea.KeyMsg); ok {
+ return a, tea.Batch(cmds...)
+ }
+ }
+ if a.showPermissions {
+ d, permissionsCmd := a.permissions.Update(msg)
+ a.permissions = d.(dialog.PermissionDialogCmp)
+ cmds = append(cmds, permissionsCmd)
+ // Only block key messages send all other messages down
+ if _, ok := msg.(tea.KeyMsg); ok {
+ return a, tea.Batch(cmds...)
+ }
}
a.pages[a.currentPage], cmd = a.pages[a.currentPage].Update(msg)
cmds = append(cmds, cmd)
return a, tea.Batch(cmds...)
}
-func (a *appModel) ToggleHelp() {
- if a.showHelp {
- a.showHelp = false
- a.height += a.help.Height()
- } else {
- a.showHelp = true
- a.height -= a.help.Height()
- }
-
- if sizable, ok := a.pages[a.currentPage].(layout.Sizeable); ok {
- sizable.SetSize(a.width, a.height)
- }
-}
-
func (a *appModel) moveToPage(pageID page.PageID) tea.Cmd {
var cmd tea.Cmd
if _, ok := a.loadedPages[pageID]; !ok {
@@ -256,27 +240,55 @@ func (a appModel) View() string {
a.pages[a.currentPage].View(),
}
+ components = append(components, a.status.View())
+
+ appView := lipgloss.JoinVertical(lipgloss.Top, components...)
+
+ if a.showPermissions {
+ overlay := a.permissions.View()
+ row := lipgloss.Height(appView) / 2
+ row -= lipgloss.Height(overlay) / 2
+ col := lipgloss.Width(appView) / 2
+ col -= lipgloss.Width(overlay) / 2
+ appView = layout.PlaceOverlay(
+ col,
+ row,
+ overlay,
+ appView,
+ true,
+ )
+ }
+
if a.showHelp {
bindings := layout.KeyMapToSlice(keys)
if p, ok := a.pages[a.currentPage].(layout.Bindings); ok {
bindings = append(bindings, p.BindingKeys()...)
}
- if a.dialogVisible {
- bindings = append(bindings, a.dialog.BindingKeys()...)
+ if a.showPermissions {
+ bindings = append(bindings, a.permissions.BindingKeys()...)
}
- if a.currentPage == page.ReplPage {
- bindings = append(bindings, replKeyMap)
+ if a.currentPage == page.LogsPage {
+ bindings = append(bindings, logsKeyReturnKey)
}
- a.help.SetBindings(bindings)
- components = append(components, a.help.View())
- }
- components = append(components, a.status.View())
+ a.help.SetBindings(bindings)
- appView := lipgloss.JoinVertical(lipgloss.Top, components...)
+ overlay := a.help.View()
+ row := lipgloss.Height(appView) / 2
+ row -= lipgloss.Height(overlay) / 2
+ col := lipgloss.Width(appView) / 2
+ col -= lipgloss.Width(overlay) / 2
+ appView = layout.PlaceOverlay(
+ col,
+ row,
+ overlay,
+ appView,
+ true,
+ )
+ }
- if a.dialogVisible {
- overlay := a.dialog.View()
+ if a.showQuit {
+ overlay := a.quit.View()
row := lipgloss.Height(appView) / 2
row -= lipgloss.Height(overlay) / 2
col := lipgloss.Width(appView) / 2
@@ -289,30 +301,23 @@ func (a appModel) View() string {
true,
)
}
+
return appView
}
func New(app *app.App) tea.Model {
- // homedir, _ := os.UserHomeDir()
- // configPath := filepath.Join(homedir, ".termai.yaml")
- //
startPage := page.ChatPage
- // if _, err := os.Stat(configPath); os.IsNotExist(err) {
- // startPage = page.InitPage
- // }
-
return &appModel{
currentPage: startPage,
loadedPages: make(map[page.PageID]bool),
- status: core.NewStatusCmp(),
- help: core.NewHelpCmp(),
- dialog: core.NewDialogCmp(),
+ status: core.NewStatusCmp(app.LSPClients),
+ help: dialog.NewHelpCmp(),
+ quit: dialog.NewQuitCmp(),
+ permissions: dialog.NewPermissionDialogCmp(),
app: app,
pages: map[page.PageID]tea.Model{
page.ChatPage: page.NewChatPage(app),
page.LogsPage: page.NewLogsPage(),
- page.InitPage: page.NewInitPage(),
- page.ReplPage: page.NewReplPage(app),
},
}
}