1package dialog
2
3import (
4 "github.com/charmbracelet/bubbles/key"
5 tea "github.com/charmbracelet/bubbletea"
6 "github.com/charmbracelet/lipgloss"
7 "github.com/kujtimiihoxha/termai/internal/permission"
8 "github.com/kujtimiihoxha/termai/internal/tui/components/core"
9 "github.com/kujtimiihoxha/termai/internal/tui/layout"
10 "github.com/kujtimiihoxha/termai/internal/tui/styles"
11 "github.com/kujtimiihoxha/termai/internal/tui/util"
12
13 "github.com/charmbracelet/huh"
14)
15
16type PermissionAction string
17
18// Permission responses
19const (
20 PermissionAllow PermissionAction = "allow"
21 PermissionAllowForSession PermissionAction = "allow_session"
22 PermissionDeny PermissionAction = "deny"
23)
24
25// PermissionResponseMsg represents the user's response to a permission request
26type PermissionResponseMsg struct {
27 Permission permission.PermissionRequest
28 Action PermissionAction
29}
30
31// Width and height constants for the dialog
32var (
33 permissionWidth = 60
34 permissionHeight = 10
35)
36
37// PermissionDialog interface for permission dialog component
38type PermissionDialog interface {
39 tea.Model
40 layout.Sizeable
41 layout.Bindings
42}
43
44// permissionDialogCmp is the implementation of PermissionDialog
45type permissionDialogCmp struct {
46 form *huh.Form
47 content string
48 width int
49 height int
50 permission permission.PermissionRequest
51}
52
53func (p *permissionDialogCmp) Init() tea.Cmd {
54 return nil
55}
56
57func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
58 var cmds []tea.Cmd
59
60 // Process the form
61 form, cmd := p.form.Update(msg)
62 if f, ok := form.(*huh.Form); ok {
63 p.form = f
64 cmds = append(cmds, cmd)
65 }
66
67 if p.form.State == huh.StateCompleted {
68 // Get the selected action
69 action := p.form.GetString("action")
70
71 // Close the dialog and return the response
72 return p, tea.Batch(
73 util.CmdHandler(core.DialogCloseMsg{}),
74 util.CmdHandler(PermissionResponseMsg{Action: PermissionAction(action), Permission: p.permission}),
75 )
76 }
77
78 return p, tea.Batch(cmds...)
79}
80
81func (p *permissionDialogCmp) View() string {
82 contentStyle := lipgloss.NewStyle().
83 Width(p.width).
84 Padding(1, 0).
85 Foreground(styles.Text).
86 Align(lipgloss.Center)
87
88 return lipgloss.JoinVertical(
89 lipgloss.Center,
90 contentStyle.Render(p.content),
91 p.form.View(),
92 )
93}
94
95func (p *permissionDialogCmp) GetSize() (int, int) {
96 return p.width, p.height
97}
98
99func (p *permissionDialogCmp) SetSize(width int, height int) {
100 p.width = width
101 p.height = height
102}
103
104func (p *permissionDialogCmp) BindingKeys() []key.Binding {
105 return p.form.KeyBinds()
106}
107
108func newPermissionDialogCmp(permission permission.PermissionRequest, content string) PermissionDialog {
109 // Create a note field for displaying the content
110
111 // Create select field for the permission options
112 selectOption := huh.NewSelect[string]().
113 Key("action").
114 Options(
115 huh.NewOption("Allow", string(PermissionAllow)),
116 huh.NewOption("Allow for this session", string(PermissionAllowForSession)),
117 huh.NewOption("Deny", string(PermissionDeny)),
118 ).
119 Title("Permission Request")
120
121 // Apply theme
122 theme := styles.HuhTheme()
123
124 // Setup form width and height
125 form := huh.NewForm(huh.NewGroup(selectOption)).
126 WithWidth(permissionWidth - 2).
127 WithShowHelp(false).
128 WithTheme(theme).
129 WithShowErrors(false)
130
131 // Focus the form for immediate interaction
132 selectOption.Focus()
133
134 return &permissionDialogCmp{
135 permission: permission,
136 form: form,
137 content: content,
138 width: permissionWidth,
139 height: permissionHeight,
140 }
141}
142
143// NewPermissionDialogCmd creates a new permission dialog command
144func NewPermissionDialogCmd(permission permission.PermissionRequest, content string) tea.Cmd {
145 permDialog := newPermissionDialogCmp(permission, content)
146
147 // Create the dialog layout
148 dialogPane := layout.NewSinglePane(
149 permDialog.(*permissionDialogCmp),
150 layout.WithSignlePaneSize(permissionWidth+2, permissionHeight+2),
151 layout.WithSinglePaneBordered(true),
152 layout.WithSinglePaneFocusable(true),
153 layout.WithSinglePaneActiveColor(styles.Blue),
154 layout.WithSignlePaneBorderText(map[layout.BorderPosition]string{
155 layout.TopMiddleBorder: " Permission Required ",
156 }),
157 )
158
159 // Focus the dialog
160 dialogPane.Focus()
161
162 // Return the dialog command
163 return util.CmdHandler(core.DialogMsg{
164 Content: dialogPane,
165 })
166}
167