1package dialog
2
3import (
4 "github.com/charmbracelet/bubbles/key"
5 "github.com/charmbracelet/bubbles/textinput"
6 tea "github.com/charmbracelet/bubbletea"
7 "github.com/charmbracelet/lipgloss"
8
9 "github.com/opencode-ai/opencode/internal/tui/styles"
10 "github.com/opencode-ai/opencode/internal/tui/theme"
11 "github.com/opencode-ai/opencode/internal/tui/util"
12)
13
14// ArgumentsDialogCmp is a component that asks the user for command arguments.
15type ArgumentsDialogCmp struct {
16 width, height int
17 textInput textinput.Model
18 keys argumentsDialogKeyMap
19 commandID string
20 content string
21}
22
23// NewArgumentsDialogCmp creates a new ArgumentsDialogCmp.
24func NewArgumentsDialogCmp(commandID, content string) ArgumentsDialogCmp {
25 t := theme.CurrentTheme()
26 ti := textinput.New()
27 ti.Placeholder = "Enter arguments..."
28 ti.Focus()
29 ti.Width = 40
30 ti.Prompt = ""
31 ti.PlaceholderStyle = ti.PlaceholderStyle.Background(t.Background())
32 ti.PromptStyle = ti.PromptStyle.Background(t.Background())
33 ti.TextStyle = ti.TextStyle.Background(t.Background())
34
35 return ArgumentsDialogCmp{
36 textInput: ti,
37 keys: argumentsDialogKeyMap{},
38 commandID: commandID,
39 content: content,
40 }
41}
42
43type argumentsDialogKeyMap struct {
44 Enter key.Binding
45 Escape key.Binding
46}
47
48// ShortHelp implements key.Map.
49func (k argumentsDialogKeyMap) ShortHelp() []key.Binding {
50 return []key.Binding{
51 key.NewBinding(
52 key.WithKeys("enter"),
53 key.WithHelp("enter", "confirm"),
54 ),
55 key.NewBinding(
56 key.WithKeys("esc"),
57 key.WithHelp("esc", "cancel"),
58 ),
59 }
60}
61
62// FullHelp implements key.Map.
63func (k argumentsDialogKeyMap) FullHelp() [][]key.Binding {
64 return [][]key.Binding{k.ShortHelp()}
65}
66
67// Init implements tea.Model.
68func (m ArgumentsDialogCmp) Init() tea.Cmd {
69 return tea.Batch(
70 textinput.Blink,
71 m.textInput.Focus(),
72 )
73}
74
75// Update implements tea.Model.
76func (m ArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
77 var cmd tea.Cmd
78 var cmds []tea.Cmd
79
80 switch msg := msg.(type) {
81 case tea.KeyMsg:
82 switch {
83 case key.Matches(msg, key.NewBinding(key.WithKeys("esc"))):
84 return m, util.CmdHandler(CloseArgumentsDialogMsg{})
85 case key.Matches(msg, key.NewBinding(key.WithKeys("enter"))):
86 return m, util.CmdHandler(CloseArgumentsDialogMsg{
87 Submit: true,
88 CommandID: m.commandID,
89 Content: m.content,
90 Arguments: m.textInput.Value(),
91 })
92 }
93 case tea.WindowSizeMsg:
94 m.width = msg.Width
95 m.height = msg.Height
96 }
97
98 m.textInput, cmd = m.textInput.Update(msg)
99 cmds = append(cmds, cmd)
100
101 return m, tea.Batch(cmds...)
102}
103
104// View implements tea.Model.
105func (m ArgumentsDialogCmp) View() string {
106 t := theme.CurrentTheme()
107 baseStyle := styles.BaseStyle()
108
109 // Calculate width needed for content
110 maxWidth := 60 // Width for explanation text
111
112 title := baseStyle.
113 Foreground(t.Primary()).
114 Bold(true).
115 Width(maxWidth).
116 Padding(0, 1).
117 Render("Command Arguments")
118
119 explanation := baseStyle.
120 Foreground(t.Text()).
121 Width(maxWidth).
122 Padding(0, 1).
123 Render("This command requires arguments. Please enter the text to replace $ARGUMENTS with:")
124
125 inputField := baseStyle.
126 Foreground(t.Text()).
127 Width(maxWidth).
128 Padding(1, 1).
129 Render(m.textInput.View())
130
131 maxWidth = min(maxWidth, m.width-10)
132
133 content := lipgloss.JoinVertical(
134 lipgloss.Left,
135 title,
136 explanation,
137 inputField,
138 )
139
140 return baseStyle.Padding(1, 2).
141 Border(lipgloss.RoundedBorder()).
142 BorderBackground(t.Background()).
143 BorderForeground(t.TextMuted()).
144 Background(t.Background()).
145 Width(lipgloss.Width(content) + 4).
146 Render(content)
147}
148
149// SetSize sets the size of the component.
150func (m *ArgumentsDialogCmp) SetSize(width, height int) {
151 m.width = width
152 m.height = height
153}
154
155// Bindings implements layout.Bindings.
156func (m ArgumentsDialogCmp) Bindings() []key.Binding {
157 return m.keys.ShortHelp()
158}
159
160// CloseArgumentsDialogMsg is a message that is sent when the arguments dialog is closed.
161type CloseArgumentsDialogMsg struct {
162 Submit bool
163 CommandID string
164 Content string
165 Arguments string
166}
167
168// ShowArgumentsDialogMsg is a message that is sent to show the arguments dialog.
169type ShowArgumentsDialogMsg struct {
170 CommandID string
171 Content string
172}
173