summaryrefslogtreecommitdiffhomepage
path: root/internal/tui/components/dialog
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-03-27 22:35:48 +0100
committerKujtim Hoxha <[email protected]>2025-04-01 13:38:54 +0200
commitafd9ad0560d76c2a6d161dad52553b10ff428905 (patch)
tree69f78b05ff0d7952cd3e3c9332f001e66abb2faf /internal/tui/components/dialog
parent904061c243f70696bfe781e97bf4e392e6954d07 (diff)
downloadopencode-afd9ad0560d76c2a6d161dad52553b10ff428905.tar.gz
opencode-afd9ad0560d76c2a6d161dad52553b10ff428905.zip
rework llm
Diffstat (limited to 'internal/tui/components/dialog')
-rw-r--r--internal/tui/components/dialog/permission.go185
-rw-r--r--internal/tui/components/dialog/quit.go23
2 files changed, 144 insertions, 64 deletions
diff --git a/internal/tui/components/dialog/permission.go b/internal/tui/components/dialog/permission.go
index f7581330a..7dcca9bae 100644
--- a/internal/tui/components/dialog/permission.go
+++ b/internal/tui/components/dialog/permission.go
@@ -1,9 +1,14 @@
package dialog
import (
+ "fmt"
+
"github.com/charmbracelet/bubbles/key"
+ "github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
+ "github.com/kujtimiihoxha/termai/internal/llm/tools"
"github.com/kujtimiihoxha/termai/internal/permission"
"github.com/kujtimiihoxha/termai/internal/tui/components/core"
"github.com/kujtimiihoxha/termai/internal/tui/layout"
@@ -28,12 +33,6 @@ type PermissionResponseMsg struct {
Action PermissionAction
}
-// Width and height constants for the dialog
-var (
- permissionWidth = 60
- permissionHeight = 10
-)
-
// PermissionDialog interface for permission dialog component
type PermissionDialog interface {
tea.Model
@@ -41,13 +40,28 @@ type PermissionDialog interface {
layout.Bindings
}
+type keyMap struct {
+ ChangeFocus key.Binding
+}
+
+var keyMapValue = keyMap{
+ ChangeFocus: key.NewBinding(
+ key.WithKeys("tab"),
+ key.WithHelp("tab", "change focus"),
+ ),
+}
+
// permissionDialogCmp is the implementation of PermissionDialog
type permissionDialogCmp struct {
- form *huh.Form
- content string
- width int
- height int
- permission permission.PermissionRequest
+ form *huh.Form
+ width int
+ height int
+ permission permission.PermissionRequest
+ windowSize tea.WindowSizeMsg
+ r *glamour.TermRenderer
+ contentViewPort viewport.Model
+ isViewportFocus bool
+ selectOption *huh.Select[string]
}
func (p *permissionDialogCmp) Init() tea.Cmd {
@@ -57,41 +71,101 @@ func (p *permissionDialogCmp) Init() tea.Cmd {
func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
- // Process the form
- form, cmd := p.form.Update(msg)
- if f, ok := form.(*huh.Form); ok {
- p.form = f
- cmds = append(cmds, cmd)
+ switch msg := msg.(type) {
+ case tea.WindowSizeMsg:
+ p.windowSize = msg
+ case tea.KeyMsg:
+ if key.Matches(msg, keyMapValue.ChangeFocus) {
+ p.isViewportFocus = !p.isViewportFocus
+ if p.isViewportFocus {
+ p.selectOption.Blur()
+ } else {
+ p.selectOption.Focus()
+ }
+ return p, nil
+ }
}
- if p.form.State == huh.StateCompleted {
- // Get the selected action
- action := p.form.GetString("action")
-
- // Close the dialog and return the response
- return p, tea.Batch(
- util.CmdHandler(core.DialogCloseMsg{}),
- util.CmdHandler(PermissionResponseMsg{Action: PermissionAction(action), Permission: p.permission}),
- )
+ if p.isViewportFocus {
+ viewPort, cmd := p.contentViewPort.Update(msg)
+ p.contentViewPort = viewPort
+ cmds = append(cmds, cmd)
+ } else {
+ form, cmd := p.form.Update(msg)
+ if f, ok := form.(*huh.Form); ok {
+ p.form = f
+ cmds = append(cmds, cmd)
+ }
+
+ if p.form.State == huh.StateCompleted {
+ // Get the selected action
+ action := p.form.GetString("action")
+
+ // Close the dialog and return the response
+ return p, tea.Batch(
+ util.CmdHandler(core.DialogCloseMsg{}),
+ util.CmdHandler(PermissionResponseMsg{Action: PermissionAction(action), Permission: p.permission}),
+ )
+ }
}
-
return p, tea.Batch(cmds...)
}
-func (p *permissionDialogCmp) View() string {
- contentStyle := lipgloss.NewStyle().
- Width(p.width).
- Padding(1, 0).
- Foreground(styles.Text).
- Align(lipgloss.Center)
+func (p *permissionDialogCmp) render() string {
+ form := p.form.View()
+ keyStyle := lipgloss.NewStyle().Bold(true).Foreground(styles.Rosewater)
+ valueStyle := lipgloss.NewStyle().Foreground(styles.Peach)
+
+ headerParts := []string{
+ lipgloss.JoinHorizontal(lipgloss.Left, keyStyle.Render("Tool:"), " ", valueStyle.Render(p.permission.ToolName)),
+ " ",
+ lipgloss.JoinHorizontal(lipgloss.Left, keyStyle.Render("Path:"), " ", valueStyle.Render(p.permission.Path)),
+ " ",
+ }
+ r, _ := glamour.NewTermRenderer(
+ glamour.WithStyles(styles.CatppuccinMarkdownStyle()),
+ glamour.WithWordWrap(p.width-10),
+ glamour.WithEmoji(),
+ )
+ content := ""
+ switch p.permission.ToolName {
+ case tools.BashToolName:
+ pr := p.permission.Params.(tools.BashPermissionsParams)
+ headerParts = append(headerParts, keyStyle.Render("Command:"))
+ content, _ = r.Render(fmt.Sprintf("```bash\n%s\n```", pr.Command))
+ case tools.EditToolName:
+ pr := p.permission.Params.(tools.EditPermissionsParams)
+ headerParts = append(headerParts, keyStyle.Render("Update:"))
+ content, _ = r.Render(fmt.Sprintf("```diff\n%s\n```", pr.Diff))
+ case tools.WriteToolName:
+ pr := p.permission.Params.(tools.WritePermissionsParams)
+ headerParts = append(headerParts, keyStyle.Render("Content:"))
+ content, _ = r.Render(fmt.Sprintf("```diff\n%s\n```", pr.Content))
+ default:
+ content, _ = r.Render(p.permission.Description)
+ }
+ headerContent := lipgloss.NewStyle().Padding(0, 1).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
+ p.contentViewPort.Width = p.width - 2 - 2
+ p.contentViewPort.Height = p.height - lipgloss.Height(headerContent) - lipgloss.Height(form) - 2 - 2 - 1
+ p.contentViewPort.SetContent(content)
+ contentBorder := lipgloss.RoundedBorder()
+ if p.isViewportFocus {
+ contentBorder = lipgloss.DoubleBorder()
+ }
+ cotentStyle := lipgloss.NewStyle().MarginTop(1).Padding(0, 1).Border(contentBorder).BorderForeground(styles.Flamingo)
return lipgloss.JoinVertical(
- lipgloss.Center,
- contentStyle.Render(p.content),
- p.form.View(),
+ lipgloss.Top,
+ headerContent,
+ cotentStyle.Render(p.contentViewPort.View()),
+ form,
)
}
+func (p *permissionDialogCmp) View() string {
+ return p.render()
+}
+
func (p *permissionDialogCmp) GetSize() (int, int) {
return p.width, p.height
}
@@ -99,13 +173,14 @@ func (p *permissionDialogCmp) GetSize() (int, int) {
func (p *permissionDialogCmp) SetSize(width int, height int) {
p.width = width
p.height = height
+ p.form = p.form.WithWidth(width)
}
func (p *permissionDialogCmp) BindingKeys() []key.Binding {
return p.form.KeyBinds()
}
-func newPermissionDialogCmp(permission permission.PermissionRequest, content string) PermissionDialog {
+func newPermissionDialogCmp(permission permission.PermissionRequest) PermissionDialog {
// Create a note field for displaying the content
// Create select field for the permission options
@@ -116,14 +191,13 @@ func newPermissionDialogCmp(permission permission.PermissionRequest, content str
huh.NewOption("Allow for this session", string(PermissionAllowForSession)),
huh.NewOption("Deny", string(PermissionDeny)),
).
- Title("Permission Request")
+ Title("Select an action")
// Apply theme
theme := styles.HuhTheme()
// Setup form width and height
form := huh.NewForm(huh.NewGroup(selectOption)).
- WithWidth(permissionWidth - 2).
WithShowHelp(false).
WithTheme(theme).
WithShowErrors(false)
@@ -132,25 +206,22 @@ func newPermissionDialogCmp(permission permission.PermissionRequest, content str
selectOption.Focus()
return &permissionDialogCmp{
- permission: permission,
- form: form,
- content: content,
- width: permissionWidth,
- height: permissionHeight,
+ permission: permission,
+ form: form,
+ selectOption: selectOption,
}
}
// NewPermissionDialogCmd creates a new permission dialog command
-func NewPermissionDialogCmd(permission permission.PermissionRequest, content string) tea.Cmd {
- permDialog := newPermissionDialogCmp(permission, content)
+func NewPermissionDialogCmd(permission permission.PermissionRequest) tea.Cmd {
+ permDialog := newPermissionDialogCmp(permission)
// Create the dialog layout
dialogPane := layout.NewSinglePane(
permDialog.(*permissionDialogCmp),
- layout.WithSignlePaneSize(permissionWidth+2, permissionHeight+2),
layout.WithSinglePaneBordered(true),
layout.WithSinglePaneFocusable(true),
- layout.WithSinglePaneActiveColor(styles.Blue),
+ layout.WithSinglePaneActiveColor(styles.Warning),
layout.WithSignlePaneBorderText(map[layout.BorderPosition]string{
layout.TopMiddleBorder: " Permission Required ",
}),
@@ -158,10 +229,24 @@ func NewPermissionDialogCmd(permission permission.PermissionRequest, content str
// Focus the dialog
dialogPane.Focus()
-
+ widthRatio := 0.7
+ heightRatio := 0.6
+ minWidth := 100
+ minHeight := 30
+
+ switch permission.ToolName {
+ case tools.BashToolName:
+ widthRatio = 0.5
+ heightRatio = 0.3
+ minWidth = 80
+ minHeight = 20
+ }
// Return the dialog command
return util.CmdHandler(core.DialogMsg{
- Content: dialogPane,
+ Content: dialogPane,
+ WidthRatio: widthRatio,
+ HeightRatio: heightRatio,
+ MinWidth: minWidth,
+ MinHeight: minHeight,
})
}
-
diff --git a/internal/tui/components/dialog/quit.go b/internal/tui/components/dialog/quit.go
index e73550ab5..60c1fc0d2 100644
--- a/internal/tui/components/dialog/quit.go
+++ b/internal/tui/components/dialog/quit.go
@@ -3,7 +3,6 @@ package dialog
import (
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
"github.com/kujtimiihoxha/termai/internal/tui/components/core"
"github.com/kujtimiihoxha/termai/internal/tui/layout"
"github.com/kujtimiihoxha/termai/internal/tui/styles"
@@ -14,11 +13,6 @@ import (
const question = "Are you sure you want to quit?"
-var (
- width = lipgloss.Width(question) + 6
- height = 3
-)
-
type QuitDialog interface {
tea.Model
layout.Sizeable
@@ -37,8 +31,6 @@ func (q *quitDialogCmp) Init() tea.Cmd {
func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
-
- // Process the form
form, cmd := q.form.Update(msg)
if f, ok := form.(*huh.Form); ok {
q.form = f
@@ -67,6 +59,7 @@ func (q *quitDialogCmp) GetSize() (int, int) {
func (q *quitDialogCmp) SetSize(width int, height int) {
q.width = width
q.height = height
+ q.form = q.form.WithWidth(width).WithHeight(height)
}
func (q *quitDialogCmp) BindingKeys() []key.Binding {
@@ -84,28 +77,30 @@ func newQuitDialogCmp() QuitDialog {
theme.Focused.FocusedButton = theme.Focused.FocusedButton.Background(styles.Warning)
theme.Blurred.FocusedButton = theme.Blurred.FocusedButton.Background(styles.Warning)
form := huh.NewForm(huh.NewGroup(confirm)).
- WithWidth(width).
- WithHeight(height).
WithShowHelp(false).
+ WithWidth(0).
+ WithHeight(0).
WithTheme(theme).
WithShowErrors(false)
confirm.Focus()
return &quitDialogCmp{
- form: form,
- width: width,
+ form: form,
}
}
func NewQuitDialogCmd() tea.Cmd {
content := layout.NewSinglePane(
newQuitDialogCmp().(*quitDialogCmp),
- layout.WithSignlePaneSize(width+2, height+2),
layout.WithSinglePaneBordered(true),
layout.WithSinglePaneFocusable(true),
layout.WithSinglePaneActiveColor(styles.Warning),
)
content.Focus()
return util.CmdHandler(core.DialogMsg{
- Content: content,
+ Content: content,
+ WidthRatio: 0.2,
+ HeightRatio: 0.1,
+ MinWidth: 40,
+ MinHeight: 5,
})
}