on main help sign in
feat: Support named arguments in custom commands (#158)

* Allow multiple named args * fix: Fix styling in multi-arguments dialog * Remove old unused modal * Focus on only one input at a time

commit36faca4b4d80b30f3d90483a422b4f20cb0f03f2 authorEd Zynda <ezynda3@users.noreply.github.com> dateThu 15 May 2025, 16:52 (1 year ago) parent1617685
6 files changed+344−106
README.md+13−3 internal/tui/components/dialog/arguments.go+156−72 internal/tui/components/dialog/custom_commands.go+24−4 internal/tui/components/dialog/custom_commands_test.go+106 internal/tui/page/chat.go+13−1 internal/tui/tui.go+32−26
README.md+13−3
@@ -19,6 +19,7 @@ OpenCode is a Go-based CLI application that brings AI assistance to your termina
1919- **LSP Integration**: Language Server Protocol support for code intelligence
2020- **File Change Tracking**: Track and visualize file changes during sessions
2121- **External Editor Support**: Open your preferred editor for composing messages
22+- **Named Arguments for Custom Commands**: Create powerful custom commands with multiple named placeholders
2223
2324## Installation
2425
@@ -375,13 +376,22 @@ This creates a command called `user:prime-context`.
375376
376377### Command Arguments
377378
378−You can create commands that accept arguments by including the `$ARGUMENTS` placeholder in your command file:
379+OpenCode supports named arguments in custom commands using placeholders in the format `$NAME` (where NAME consists of uppercase letters, numbers, and underscores, and must start with a letter).
380+
381+For example:
379382
380383```markdown
381−RUN git show $ARGUMENTS
384+# Fetch Context for Issue $ISSUE_NUMBER
385+
386+RUN gh issue view $ISSUE_NUMBER --json title,body,comments
387+RUN git grep --author="$AUTHOR_NAME" -n .
388+RUN grep -R "$SEARCH_PATTERN" $DIRECTORY
382389```
383390
384−When you run this command, OpenCode will prompt you to enter the text that should replace `$ARGUMENTS`.
391+When you run a command with arguments, OpenCode will prompt you to enter values for each unique placeholder. Named arguments provide several benefits:
392+- Clear identification of what each argument represents
393+- Ability to use the same argument multiple times
394+- Better organization for commands with multiple inputs
385395
386396### Organizing Commands
387397
internal/tui/components/dialog/arguments.go+156−72
@@ -1,6 +1,7 @@
11package dialog
22
33import (
4+ "fmt"
45 "github.com/charmbracelet/bubbles/key"
56 "github.com/charmbracelet/bubbles/textinput"
67 tea "github.com/charmbracelet/bubbletea"
@@ -11,35 +12,6 @@ import (
1112 "github.com/opencode-ai/opencode/internal/tui/util"
1213)
1314
14−// ArgumentsDialogCmp is a component that asks the user for command arguments.
15−type 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.
24−func 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−
4315type argumentsDialogKeyMap struct {
4416 Enter key.Binding
4517 Escape key.Binding
@@ -64,77 +36,204 @@ func (k argumentsDialogKeyMap) FullHelp() [][]key.Binding {
6436 return [][]key.Binding{k.ShortHelp()}
6537}
6638
39+// ShowMultiArgumentsDialogMsg is a message that is sent to show the multi-arguments dialog.
40+type ShowMultiArgumentsDialogMsg struct {
41+ CommandID string
42+ Content string
43+ ArgNames []string
44+}
45+
46+// CloseMultiArgumentsDialogMsg is a message that is sent when the multi-arguments dialog is closed.
47+type CloseMultiArgumentsDialogMsg struct {
48+ Submit bool
49+ CommandID string
50+ Content string
51+ Args map[string]string
52+}
53+
54+// MultiArgumentsDialogCmp is a component that asks the user for multiple command arguments.
55+type MultiArgumentsDialogCmp struct {
56+ width, height int
57+ inputs []textinput.Model
58+ focusIndex int
59+ keys argumentsDialogKeyMap
60+ commandID string
61+ content string
62+ argNames []string
63+}
64+
65+// NewMultiArgumentsDialogCmp creates a new MultiArgumentsDialogCmp.
66+func NewMultiArgumentsDialogCmp(commandID, content string, argNames []string) MultiArgumentsDialogCmp {
67+ t := theme.CurrentTheme()
68+ inputs := make([]textinput.Model, len(argNames))
69+
70+ for i, name := range argNames {
71+ ti := textinput.New()
72+ ti.Placeholder = fmt.Sprintf("Enter value for %s...", name)
73+ ti.Width = 40
74+ ti.Prompt = ""
75+ ti.PlaceholderStyle = ti.PlaceholderStyle.Background(t.Background())
76+ ti.PromptStyle = ti.PromptStyle.Background(t.Background())
77+ ti.TextStyle = ti.TextStyle.Background(t.Background())
78+
79+ // Only focus the first input initially
80+ if i == 0 {
81+ ti.Focus()
82+ ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
83+ ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
84+ } else {
85+ ti.Blur()
86+ }
87+
88+ inputs[i] = ti
89+ }
90+
91+ return MultiArgumentsDialogCmp{
92+ inputs: inputs,
93+ keys: argumentsDialogKeyMap{},
94+ commandID: commandID,
95+ content: content,
96+ argNames: argNames,
97+ focusIndex: 0,
98+ }
99+}
100+
67101// Init implements tea.Model.
68−func (m ArgumentsDialogCmp) Init() tea.Cmd {
69− return tea.Batch(
70− textinput.Blink,
71− m.textInput.Focus(),
72− )
102+func (m MultiArgumentsDialogCmp) Init() tea.Cmd {
103+ // Make sure only the first input is focused
104+ for i := range m.inputs {
105+ if i == 0 {
106+ m.inputs[i].Focus()
107+ } else {
108+ m.inputs[i].Blur()
109+ }
110+ }
111+
112+ return textinput.Blink
73113}
74114
75115// Update implements tea.Model.
76−func (m ArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
77− var cmd tea.Cmd
116+func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
78117 var cmds []tea.Cmd
118+ t := theme.CurrentTheme()
79119
80120 switch msg := msg.(type) {
81121 case tea.KeyMsg:
82122 switch {
83123 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,
124+ return m, util.CmdHandler(CloseMultiArgumentsDialogMsg{
125+ Submit: false,
88126 CommandID: m.commandID,
89127 Content: m.content,
90− Arguments: m.textInput.Value(),
128+ Args: nil,
91129 })
130+ case key.Matches(msg, key.NewBinding(key.WithKeys("enter"))):
131+ // If we're on the last input, submit the form
132+ if m.focusIndex == len(m.inputs)-1 {
133+ args := make(map[string]string)
134+ for i, name := range m.argNames {
135+ args[name] = m.inputs[i].Value()
136+ }
137+ return m, util.CmdHandler(CloseMultiArgumentsDialogMsg{
138+ Submit: true,
139+ CommandID: m.commandID,
140+ Content: m.content,
141+ Args: args,
142+ })
143+ }
144+ // Otherwise, move to the next input
145+ m.inputs[m.focusIndex].Blur()
146+ m.focusIndex++
147+ m.inputs[m.focusIndex].Focus()
148+ m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
149+ m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
150+ case key.Matches(msg, key.NewBinding(key.WithKeys("tab"))):
151+ // Move to the next input
152+ m.inputs[m.focusIndex].Blur()
153+ m.focusIndex = (m.focusIndex + 1) % len(m.inputs)
154+ m.inputs[m.focusIndex].Focus()
155+ m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
156+ m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
157+ case key.Matches(msg, key.NewBinding(key.WithKeys("shift+tab"))):
158+ // Move to the previous input
159+ m.inputs[m.focusIndex].Blur()
160+ m.focusIndex = (m.focusIndex - 1 + len(m.inputs)) % len(m.inputs)
161+ m.inputs[m.focusIndex].Focus()
162+ m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
163+ m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
92164 }
93165 case tea.WindowSizeMsg:
94166 m.width = msg.Width
95167 m.height = msg.Height
96168 }
97169
98− m.textInput, cmd = m.textInput.Update(msg)
170+ // Update the focused input
171+ var cmd tea.Cmd
172+ m.inputs[m.focusIndex], cmd = m.inputs[m.focusIndex].Update(msg)
99173 cmds = append(cmds, cmd)
100174
101175 return m, tea.Batch(cmds...)
102176}
103177
104178// View implements tea.Model.
105−func (m ArgumentsDialogCmp) View() string {
179+func (m MultiArgumentsDialogCmp) View() string {
106180 t := theme.CurrentTheme()
107181 baseStyle := styles.BaseStyle()
108182
109183 // Calculate width needed for content
110184 maxWidth := 60 // Width for explanation text
111185
112− title := baseStyle.
186+ title := lipgloss.NewStyle().
113187 Foreground(t.Primary()).
114188 Bold(true).
115189 Width(maxWidth).
116190 Padding(0, 1).
191+ Background(t.Background()).
117192 Render("Command Arguments")
118193
119− explanation := baseStyle.
194+ explanation := lipgloss.NewStyle().
120195 Foreground(t.Text()).
121196 Width(maxWidth).
122197 Padding(0, 1).
123− Render("This command requires arguments. Please enter the text to replace $ARGUMENTS with:")
198+ Background(t.Background()).
199+ Render("This command requires multiple arguments. Please enter values for each:")
124200
125− inputField := baseStyle.
126− Foreground(t.Text()).
127− Width(maxWidth).
128− Padding(1, 1).
129− Render(m.textInput.View())
201+ // Create input fields for each argument
202+ inputFields := make([]string, len(m.inputs))
203+ for i, input := range m.inputs {
204+ // Highlight the label of the focused input
205+ labelStyle := lipgloss.NewStyle().
206+ Width(maxWidth).
207+ Padding(1, 1, 0, 1).
208+ Background(t.Background())
209+
210+ if i == m.focusIndex {
211+ labelStyle = labelStyle.Foreground(t.Primary()).Bold(true)
212+ } else {
213+ labelStyle = labelStyle.Foreground(t.TextMuted())
214+ }
215+
216+ label := labelStyle.Render(m.argNames[i] + ":")
217+
218+ field := lipgloss.NewStyle().
219+ Foreground(t.Text()).
220+ Width(maxWidth).
221+ Padding(0, 1).
222+ Background(t.Background()).
223+ Render(input.View())
224+
225+ inputFields[i] = lipgloss.JoinVertical(lipgloss.Left, label, field)
226+ }
130227
131228 maxWidth = min(maxWidth, m.width-10)
132229
230+ // Join all elements vertically
231+ elements := []string{title, explanation}
232+ elements = append(elements, inputFields...)
233+
133234 content := lipgloss.JoinVertical(
134235 lipgloss.Left,
135− title,
136− explanation,
137− inputField,
236+ elements...,
138237 )
139238
140239 return baseStyle.Padding(1, 2).
@@ -147,27 +246,12 @@ func (m ArgumentsDialogCmp) View() string {
147246}
148247
149248// SetSize sets the size of the component.
150−func (m *ArgumentsDialogCmp) SetSize(width, height int) {
249+func (m *MultiArgumentsDialogCmp) SetSize(width, height int) {
151250 m.width = width
152251 m.height = height
153252}
154253
155254// Bindings implements layout.Bindings.
156−func (m ArgumentsDialogCmp) Bindings() []key.Binding {
255+func (m MultiArgumentsDialogCmp) Bindings() []key.Binding {
157256 return m.keys.ShortHelp()
158−}
159−
160−// CloseArgumentsDialogMsg is a message that is sent when the arguments dialog is closed.
161−type 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.
169−type ShowArgumentsDialogMsg struct {
170− CommandID string
171− Content string
172−}
173−
257+}
internal/tui/components/dialog/custom_commands.go+24−4
@@ -4,6 +4,7 @@ import (
44 "fmt"
55 "os"
66 "path/filepath"
7+ "regexp"
78 "strings"
89
910 tea "github.com/charmbracelet/bubbletea"
@@ -17,6 +18,9 @@ const (
1718 ProjectCommandPrefix = "project:"
1819)
1920
21+// namedArgPattern is a regex pattern to find named arguments in the format $NAME
22+var namedArgPattern = regexp.MustCompile(`\$([A-Z][A-Z0-9_]*)`)
23+
2024// LoadCustomCommands loads custom commands from both XDG_CONFIG_HOME and project data directory
2125func LoadCustomCommands() ([]Command, error) {
2226 cfg := config.Get()
@@ -133,18 +137,33 @@ func loadCommandsFromDir(commandsDir string, prefix string) ([]Command, error) {
133137 Handler: func(cmd Command) tea.Cmd {
134138 commandContent := string(content)
135139
136− // Check if the command contains $ARGUMENTS placeholder
137− if strings.Contains(commandContent, "$ARGUMENTS") {
138− // Show arguments dialog
139− return util.CmdHandler(ShowArgumentsDialogMsg{
140+ // Check for named arguments
141+ matches := namedArgPattern.FindAllStringSubmatch(commandContent, -1)
142+ if len(matches) > 0 {
143+ // Extract unique argument names
144+ argNames := make([]string, 0)
145+ argMap := make(map[string]bool)
146+
147+ for _, match := range matches {
148+ argName := match[1] // Group 1 is the name without $
149+ if !argMap[argName] {
150+ argMap[argName] = true
151+ argNames = append(argNames, argName)
152+ }
153+ }
154+
155+ // Show multi-arguments dialog for all named arguments
156+ return util.CmdHandler(ShowMultiArgumentsDialogMsg{
140157 CommandID: cmd.ID,
141158 Content: commandContent,
159+ ArgNames: argNames,
142160 })
143161 }
144162
145163 // No arguments needed, run command directly
146164 return util.CmdHandler(CommandRunCustomMsg{
147165 Content: commandContent,
166+ Args: nil, // No arguments
148167 })
149168 },
150169 }
@@ -163,4 +182,5 @@ func loadCommandsFromDir(commandsDir string, prefix string) ([]Command, error) {
163182// CommandRunCustomMsg is sent when a custom command is executed
164183type CommandRunCustomMsg struct {
165184 Content string
185+ Args map[string]string // Map of argument names to values
166186}
internal/tui/components/dialog/custom_commands_test.gonew file+106
@@ -0,0 +1,106 @@
1+package dialog
2+
3+import (
4+ "testing"
5+ "regexp"
6+)
7+
8+func TestNamedArgPattern(t *testing.T) {
9+ testCases := []struct {
10+ input string
11+ expected []string
12+ }{
13+ {
14+ input: "This is a test with $ARGUMENTS placeholder",
15+ expected: []string{"ARGUMENTS"},
16+ },
17+ {
18+ input: "This is a test with $FOO and $BAR placeholders",
19+ expected: []string{"FOO", "BAR"},
20+ },
21+ {
22+ input: "This is a test with $FOO_BAR and $BAZ123 placeholders",
23+ expected: []string{"FOO_BAR", "BAZ123"},
24+ },
25+ {
26+ input: "This is a test with no placeholders",
27+ expected: []string{},
28+ },
29+ {
30+ input: "This is a test with $FOO appearing twice: $FOO",
31+ expected: []string{"FOO"},
32+ },
33+ {
34+ input: "This is a test with $1INVALID placeholder",
35+ expected: []string{},
36+ },
37+ }
38+
39+ for _, tc := range testCases {
40+ matches := namedArgPattern.FindAllStringSubmatch(tc.input, -1)
41+
42+ // Extract unique argument names
43+ argNames := make([]string, 0)
44+ argMap := make(map[string]bool)
45+
46+ for _, match := range matches {
47+ argName := match[1] // Group 1 is the name without $
48+ if !argMap[argName] {
49+ argMap[argName] = true
50+ argNames = append(argNames, argName)
51+ }
52+ }
53+
54+ // Check if we got the expected number of arguments
55+ if len(argNames) != len(tc.expected) {
56+ t.Errorf("Expected %d arguments, got %d for input: %s", len(tc.expected), len(argNames), tc.input)
57+ continue
58+ }
59+
60+ // Check if we got the expected argument names
61+ for _, expectedArg := range tc.expected {
62+ found := false
63+ for _, actualArg := range argNames {
64+ if actualArg == expectedArg {
65+ found = true
66+ break
67+ }
68+ }
69+ if !found {
70+ t.Errorf("Expected argument %s not found in %v for input: %s", expectedArg, argNames, tc.input)
71+ }
72+ }
73+ }
74+}
75+
76+func TestRegexPattern(t *testing.T) {
77+ pattern := regexp.MustCompile(`\$([A-Z][A-Z0-9_]*)`)
78+
79+ validMatches := []string{
80+ "$FOO",
81+ "$BAR",
82+ "$FOO_BAR",
83+ "$BAZ123",
84+ "$ARGUMENTS",
85+ }
86+
87+ invalidMatches := []string{
88+ "$foo",
89+ "$1BAR",
90+ "$_FOO",
91+ "FOO",
92+ "$",
93+ }
94+
95+ for _, valid := range validMatches {
96+ if !pattern.MatchString(valid) {
97+ t.Errorf("Expected %s to match, but it didn't", valid)
98+ }
99+ }
100+
101+ for _, invalid := range invalidMatches {
102+ if pattern.MatchString(invalid) {
103+ t.Errorf("Expected %s not to match, but it did", invalid)
104+ }
105+ }
106+}
internal/tui/page/chat.go+13−1
@@ -2,6 +2,7 @@ package page
22
33import (
44 "context"
5+ "strings"
56
67 "github.com/charmbracelet/bubbles/key"
78 tea "github.com/charmbracelet/bubbletea"
@@ -63,8 +64,19 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6364 if p.app.CoderAgent.IsBusy() {
6465 return p, util.ReportWarn("Agent is busy, please wait before executing a command...")
6566 }
67+
68+ // Process the command content with arguments if any
69+ content := msg.Content
70+ if msg.Args != nil {
71+ // Replace all named arguments with their values
72+ for name, value := range msg.Args {
73+ placeholder := "$" + name
74+ content = strings.ReplaceAll(content, placeholder, value)
75+ }
76+ }
77+
6678 // Handle custom command execution
67− cmd := p.sendMessage(msg.Content, nil)
79+ cmd := p.sendMessage(content, nil)
6880 if cmd != nil {
6981 return p, cmd
7082 }
internal/tui/tui.go+32−26
@@ -133,8 +133,8 @@ type appModel struct {
133133 showThemeDialog bool
134134 themeDialog dialog.ThemeDialog
135135
136− showArgumentsDialog bool
137− argumentsDialog dialog.ArgumentsDialogCmp
136+ showMultiArgumentsDialog bool
137+ multiArgumentsDialog dialog.MultiArgumentsDialogCmp
138138
139139 isCompacting bool
140140 compactingMessage string
@@ -214,11 +214,11 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
214214
215215 a.initDialog.SetSize(msg.Width, msg.Height)
216216
217− if a.showArgumentsDialog {
218− a.argumentsDialog.SetSize(msg.Width, msg.Height)
219− args, argsCmd := a.argumentsDialog.Update(msg)
220− a.argumentsDialog = args.(dialog.ArgumentsDialogCmp)
221− cmds = append(cmds, argsCmd, a.argumentsDialog.Init())
217+ if a.showMultiArgumentsDialog {
218+ a.multiArgumentsDialog.SetSize(msg.Width, msg.Height)
219+ args, argsCmd := a.multiArgumentsDialog.Update(msg)
220+ a.multiArgumentsDialog = args.(dialog.MultiArgumentsDialogCmp)
221+ cmds = append(cmds, argsCmd, a.multiArgumentsDialog.Init())
222222 }
223223
224224 return a, tea.Batch(cmds...)
@@ -438,33 +438,39 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
438438 }
439439 return a, util.ReportInfo("Command selected: " + msg.Command.Title)
440440
441− case dialog.ShowArgumentsDialogMsg:
442− // Show arguments dialog
443− a.argumentsDialog = dialog.NewArgumentsDialogCmp(msg.CommandID, msg.Content)
444− a.showArgumentsDialog = true
445− return a, a.argumentsDialog.Init()
441+ case dialog.ShowMultiArgumentsDialogMsg:
442+ // Show multi-arguments dialog
443+ a.multiArgumentsDialog = dialog.NewMultiArgumentsDialogCmp(msg.CommandID, msg.Content, msg.ArgNames)
444+ a.showMultiArgumentsDialog = true
445+ return a, a.multiArgumentsDialog.Init()
446446
447− case dialog.CloseArgumentsDialogMsg:
448− // Close arguments dialog
449− a.showArgumentsDialog = false
447+ case dialog.CloseMultiArgumentsDialogMsg:
448+ // Close multi-arguments dialog
449+ a.showMultiArgumentsDialog = false
450450
451− // If submitted, replace $ARGUMENTS and run the command
451+ // If submitted, replace all named arguments and run the command
452452 if msg.Submit {
453− // Replace $ARGUMENTS with the provided arguments
454− content := strings.ReplaceAll(msg.Content, "$ARGUMENTS", msg.Arguments)
453+ content := msg.Content
454+
455+ // Replace each named argument with its value
456+ for name, value := range msg.Args {
457+ placeholder := "$" + name
458+ content = strings.ReplaceAll(content, placeholder, value)
459+ }
455460
456461 // Execute the command with arguments
457462 return a, util.CmdHandler(dialog.CommandRunCustomMsg{
458463 Content: content,
464+ Args: msg.Args,
459465 })
460466 }
461467 return a, nil
462468
463469 case tea.KeyMsg:
464− // If arguments dialog is open, let it handle the key press first
465− if a.showArgumentsDialog {
466− args, cmd := a.argumentsDialog.Update(msg)
467− a.argumentsDialog = args.(dialog.ArgumentsDialogCmp)
470+ // If multi-arguments dialog is open, let it handle the key press first
471+ if a.showMultiArgumentsDialog {
472+ args, cmd := a.multiArgumentsDialog.Update(msg)
473+ a.multiArgumentsDialog = args.(dialog.MultiArgumentsDialogCmp)
468474 return a, cmd
469475 }
470476
@@ -488,8 +494,8 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
488494 if a.showModelDialog {
489495 a.showModelDialog = false
490496 }
491− if a.showArgumentsDialog {
492− a.showArgumentsDialog = false
497+ if a.showMultiArgumentsDialog {
498+ a.showMultiArgumentsDialog = false
493499 }
494500 return a, nil
495501 case key.Matches(msg, keys.SwitchSession):
@@ -898,8 +904,8 @@ func (a appModel) View() string {
898904 )
899905 }
900906
901− if a.showArgumentsDialog {
902− overlay := a.argumentsDialog.View()
907+ if a.showMultiArgumentsDialog {
908+ overlay := a.multiArgumentsDialog.View()
903909 row := lipgloss.Height(appView) / 2
904910 row -= lipgloss.Height(overlay) / 2
905911 col := lipgloss.Width(appView) / 2