Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an interactive Terminal User Interface (TUI) mode using the Bubbletea framework, allowing users to configure and execute ElasticSearch/OpenSearch exports through a guided wizard interface. The PR also refactors the export client code to support the new interactive functionality.
Changes:
- Added a new TUI package with an interactive connection wizard for ElasticSearch configuration
- Refactored the internal
elasticClienttype to an exportedClienttype to enable reuse in the TUI - Added
GetIndicesmethods across all ElasticSearch client versions (v7, v8, v9) to support index selection in the TUI - Updated main.go to launch interactive mode when no command-line arguments are provided
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tui/model.go | New TUI model implementing the Bubbletea interface with connection form and step-based workflow |
| main.go | Added conditional logic to launch interactive TUI mode when no arguments are provided |
| export/export.go | Refactored client type to be exported, extracted query building logic, added GetIndices wrapper method |
| elastic/v7/client.go | Added GetIndices method to retrieve index list from ElasticSearch v7 |
| elastic/v8/client.go | Added GetIndices method to retrieve index list from ElasticSearch v8 |
| elastic/v9/client.go | Added GetIndices method to retrieve index list from ElasticSearch v9 |
| go.mod | Added Bubbletea and related TUI dependencies |
| go.sum | Dependency checksums for new TUI libraries |
| README.md | Added documentation for interactive mode and Scoop installation method |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 9 changed files in this pull request and generated 19 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tui/model.go
Outdated
| case "tab", "shift+tab", "enter", "up", "down": | ||
| s := msg.String() | ||
|
|
||
| if s == "enter" && m.focus == len(m.inputs)-2 { | ||
| m.conf.ElasticURL = m.inputs[0].Value() | ||
| m.conf.ElasticUser = m.inputs[1].Value() | ||
| m.conf.ElasticPass = m.inputs[2].Value() | ||
| m.conf.ElasticClientCrt = m.inputs[3].Value() | ||
| m.conf.ElasticClientKey = m.inputs[4].Value() | ||
| m.conf.ElasticVerifySSL = m.toggle | ||
| m.conf.Index = m.inputs[5].Value() | ||
|
|
||
| m.step = stepQueryType | ||
| m.initQueryTypeList() | ||
| return m, nil | ||
| } | ||
|
|
||
| if s == "up" || s == "shift+tab" { | ||
| m.focus-- | ||
| } else { | ||
| m.focus++ | ||
| } | ||
|
|
||
| if m.focus > len(m.inputs)-2 { | ||
| m.focus = 0 | ||
| } else if m.focus < 0 { | ||
| m.focus = len(m.inputs) - 2 | ||
| } | ||
|
|
||
| cmds := make([]tea.Cmd, len(m.inputs)) | ||
| for i := 0; i <= len(m.inputs)-2; i++ { | ||
| if i == m.focus { | ||
| cmds[i] = m.inputs[i].Focus() | ||
| m.inputs[i].TextStyle = focusedStyle | ||
| continue | ||
| } | ||
| m.inputs[i].Blur() | ||
| m.inputs[i].TextStyle = noStyle | ||
| } | ||
| return m, tea.Batch(cmds...) | ||
| } |
There was a problem hiding this comment.
The stepConnection navigation handling includes "tab" and "shift+tab" in the case statement but then also checks for "enter" separately to handle field navigation. This means pressing "enter" on any field except the last one will move to the next field instead of progressing when on the designated "submit" field. The pattern is inconsistent with stepQuery and stepExportManual where tab/shift+tab navigation is separate from enter key handling.
tui/model.go
Outdated
| case stepFields: | ||
| var listContent string | ||
| items := m.list.Items() | ||
| for _, it := range items { | ||
| field := it.(item) | ||
| prefix := " " | ||
| if m.selectedFields[string(field)] { | ||
| prefix = "[X]" | ||
| } | ||
| listContent += fmt.Sprintf("%s %s\n", prefix, string(field)) | ||
| } | ||
| return fmt.Sprintf( | ||
| "Select Fields (or 'm' for manual)\n\n%s\n\n[Space] Select/Deselect [Enter] Continue [m] Manual input", | ||
| listContent, | ||
| ) |
There was a problem hiding this comment.
The stepFields view displays a custom field list with checkboxes, but it doesn't integrate with the built-in list.Model navigation features. Users cannot use arrow keys to navigate through fields since the list is rendered as plain text. This creates an inconsistent user experience compared to other steps that use the interactive list component. Consider either using the list component's built-in rendering or implementing keyboard navigation for the custom display.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
# Conflicts: # tui/model.go
| m.conf.Query = m.inputs[0].Value() | ||
| m.conf.RAWQuery = m.inputs[1].Value() |
There was a problem hiding this comment.
The query type selection logic has a bug. When a user selects a query type like "Match All", it sets m.conf.Query to "*" on line 266. However, initQueryInputs doesn't pre-populate the query input field with this value, and when the user submits on line 280, it unconditionally overwrites m.conf.Query with the input value (which would be empty if the user didn't type anything). This means the query type selection is effectively ignored. The query and RAW query input fields should be pre-populated based on the selected query type, or the inputs should only overwrite the config if they are non-empty.
| m.conf.Query = m.inputs[0].Value() | |
| m.conf.RAWQuery = m.inputs[1].Value() | |
| if v := strings.TrimSpace(m.inputs[0].Value()); v != "" { | |
| m.conf.Query = v | |
| } | |
| if v := strings.TrimSpace(m.inputs[1].Value()); v != "" { | |
| m.conf.RAWQuery = v | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Add interactive mode using a Bubbletea based TUI.