Skip to content

Add TUI interactive mode#37

Open
pteich wants to merge 25 commits intomasterfrom
tui-mode
Open

Add TUI interactive mode#37
pteich wants to merge 25 commits intomasterfrom
tui-mode

Conversation

@pteich
Copy link
Copy Markdown
Owner

@pteich pteich commented Jan 11, 2026

Add interactive mode using a Bubbletea based TUI.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 elasticClient type to an exported Client type to enable reuse in the TUI
  • Added GetIndices methods 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.

pteich and others added 8 commits January 11, 2026 22:09
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Comment on lines +208 to +248
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...)
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
tui/model.go Outdated
Comment on lines +567 to +581
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,
)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
pteich and others added 9 commits January 12, 2026 12:57
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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Comment on lines +280 to +281
m.conf.Query = m.inputs[0].Value()
m.conf.RAWQuery = m.inputs[1].Value()
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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
}

Copilot uses AI. Check for mistakes.
pteich and others added 2 commits January 12, 2026 18:58
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants