chore: use new search component in find dialog

This commit is contained in:
adamdotdevin 2025-07-14 11:56:25 -05:00
parent f1e7e7c138
commit a1c8e5af45
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75

View file

@ -3,14 +3,10 @@ package dialog
import (
"log/slog"
"github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/v2/textinput"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/components/list"
"github.com/sst/opencode/internal/components/modal"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util"
)
@ -30,120 +26,61 @@ type FindDialog interface {
}
type findDialogComponent struct {
query string
completionProvider CompletionProvider
width, height int
modal *modal.Modal
textInput textinput.Model
list list.List[CompletionItemI]
}
type findDialogKeyMap struct {
Select key.Binding
Cancel key.Binding
}
var findDialogKeys = findDialogKeyMap{
Select: key.NewBinding(
key.WithKeys("enter"),
),
Cancel: key.NewBinding(
key.WithKeys("esc"),
),
searchDialog *SearchDialog
}
func (f *findDialogComponent) Init() tea.Cmd {
return textinput.Blink
return f.searchDialog.Init()
}
func (f *findDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
switch msg := msg.(type) {
case []CompletionItemI:
f.list.SetItems(msg)
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
if f.textInput.Value() == "" {
return f, nil
}
f.textInput.SetValue("")
return f.update(msg)
// Convert CompletionItemI to list.ListItem
items := make([]list.ListItem, len(msg))
for i, item := range msg {
items[i] = item
}
f.searchDialog.SetItems(items)
return f, nil
switch {
case key.Matches(msg, findDialogKeys.Select):
item, i := f.list.GetSelectedItem()
if i == -1 {
return f, nil
}
case SearchSelectionMsg:
// Handle selection from search dialog
if item, ok := msg.Item.(CompletionItemI); ok {
return f, f.selectFile(item)
case key.Matches(msg, findDialogKeys.Cancel):
return f, f.Close()
default:
f.textInput, cmd = f.textInput.Update(msg)
cmds = append(cmds, cmd)
f, cmd = f.update(msg)
cmds = append(cmds, cmd)
}
}
return f, nil
return f, tea.Batch(cmds...)
}
case SearchCancelledMsg:
return f, f.Close()
func (f *findDialogComponent) update(msg tea.Msg) (*findDialogComponent, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
query := f.textInput.Value()
if query != f.query {
f.query = query
cmd = func() tea.Msg {
items, err := f.completionProvider.GetChildEntries(query)
case SearchQueryChangedMsg:
// Update completion items based on search query
return f, func() tea.Msg {
items, err := f.completionProvider.GetChildEntries(msg.Query)
if err != nil {
slog.Error("Failed to get completion items", "error", err)
}
return items
}
cmds = append(cmds, cmd)
}
u, cmd := f.list.Update(msg)
f.list = u.(list.List[CompletionItemI])
cmds = append(cmds, cmd)
return f, tea.Batch(cmds...)
// Forward all other messages to the search dialog
updatedDialog, cmd := f.searchDialog.Update(msg)
f.searchDialog = updatedDialog.(*SearchDialog)
return f, cmd
}
func (f *findDialogComponent) View() string {
t := theme.CurrentTheme()
f.textInput.SetWidth(f.width - 8)
f.list.SetMaxWidth(f.width - 4)
inputView := f.textInput.View()
inputView = styles.NewStyle().
Background(t.BackgroundElement()).
Height(1).
Width(f.width-4).
Padding(0, 0).
Render(inputView)
listView := f.list.View()
return styles.NewStyle().
Height(12).
Background(t.BackgroundPanel()).
Width(f.width - 4).
Render(inputView + "\n" + listView)
return f.searchDialog.View()
}
func (f *findDialogComponent) SetWidth(width int) {
f.width = width
if width > 4 {
f.textInput.SetWidth(width - 4)
f.list.SetMaxWidth(width - 4)
}
f.searchDialog.SetWidth(width - 4)
}
func (f *findDialogComponent) SetHeight(height int) {
@ -151,7 +88,7 @@ func (f *findDialogComponent) SetHeight(height int) {
}
func (f *findDialogComponent) IsEmpty() bool {
return f.list.IsEmpty()
return f.searchDialog.GetQuery() == ""
}
func (f *findDialogComponent) selectFile(item CompletionItemI) tea.Cmd {
@ -168,67 +105,32 @@ func (f *findDialogComponent) Render(background string) string {
}
func (f *findDialogComponent) Close() tea.Cmd {
f.textInput.Reset()
f.textInput.Blur()
f.searchDialog.SetQuery("")
f.searchDialog.Blur()
return util.CmdHandler(modal.CloseModalMsg{})
}
func createTextInput(existing *textinput.Model) textinput.Model {
t := theme.CurrentTheme()
bgColor := t.BackgroundElement()
textColor := t.Text()
textMutedColor := t.TextMuted()
ti := textinput.New()
ti.Styles.Blurred.Placeholder = styles.NewStyle().
Foreground(textMutedColor).
Background(bgColor).
Lipgloss()
ti.Styles.Blurred.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
ti.Styles.Focused.Placeholder = styles.NewStyle().
Foreground(textMutedColor).
Background(bgColor).
Lipgloss()
ti.Styles.Focused.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
ti.Styles.Cursor.Color = t.Primary()
ti.VirtualCursor = true
ti.Prompt = " "
ti.CharLimit = -1
ti.Focus()
if existing != nil {
ti.SetValue(existing.Value())
ti.SetWidth(existing.Width())
}
return ti
}
func NewFindDialog(completionProvider CompletionProvider) FindDialog {
ti := createTextInput(nil)
li := list.NewListComponent(
[]CompletionItemI{},
10, // max visible items
completionProvider.GetEmptyMessage(),
false,
)
searchDialog := NewSearchDialog("Search files...", 10)
// Initialize with empty query to get initial items
go func() {
items, err := completionProvider.GetChildEntries("")
if err != nil {
slog.Error("Failed to get completion items", "error", err)
return
}
li.SetItems(items)
// Convert CompletionItemI to list.ListItem
listItems := make([]list.ListItem, len(items))
for i, item := range items {
listItems[i] = item
}
searchDialog.SetItems(listItems)
}()
return &findDialogComponent{
query: "",
completionProvider: completionProvider,
textInput: ti,
list: li,
searchDialog: searchDialog,
modal: modal.New(
modal.WithTitle("Find Files"),
modal.WithMaxWidth(80),