arguments.go

  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