package widget import ( "context" "fmt" "io" "github.com/Khan/genqlient/graphql" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "git.ohea.xyz/cursorius/tui/queries" ) type pipelineWidgetScreenSwitch tea.Model type pipelineWidget struct { currentView tea.Model width int height int } func (m pipelineWidget) Init() tea.Cmd { return nil } func (m pipelineWidget) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height case pipelineWidgetScreenSwitch: m.currentView = msg } var cmd tea.Cmd m.currentView, cmd = m.currentView.Update(msg) return m, cmd } func (m pipelineWidget) View() string { return m.currentView.View() } type pipelineWidgetList struct { list list.Model client graphql.Client } func (m pipelineWidgetList) Init() tea.Cmd { return nil } func (m pipelineWidgetList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.list.SetWidth(msg.Width - 10) m.list.SetHeight(msg.Height - 10) case tea.KeyMsg: switch msg.String() { case "down": if m.list.Index() < len(m.list.Items()) { m.list.Select(m.list.Index() + 1) } case "up": if m.list.Index() > 0 { m.list.Select(m.list.Index() - 1) } case "enter": if m.list.Index() < len(m.list.Items())-1 { //item := m.list.SelectedItem() //if item, ok := item.(pipelineListItem); ok { // return m, func() tea.Cmd { // view, err := createPipelineEditForm(client, item.id) // if err != nil { // return err // } // return pipelineWidgetScreenSwitch(view) // } //} } else { return m, func() tea.Msg { view, err := createPipelineCreateForm(m.client, m.list.Width(), m.list.Height()) if err != nil { return err } return pipelineWidgetScreenSwitch(view) } } } } return m, nil } func (m pipelineWidgetList) View() string { return m.list.View() } type pipelineListItem struct { name string id string } func createPipelineWidgetList(client graphql.Client) (tea.Model, error) { getPipelineResp, err := queries.GetPipelines(context.Background(), client) if err != nil { return pipelineWidget{}, fmt.Errorf("Could not connect to server: %w", err) } var content []list.Item if getPipelineResp.Pipelines != nil { for _, pipeline := range getPipelineResp.Pipelines { if pipeline != nil { content = append(content, pipelineListItem{ name: pipeline.Name, id: pipeline.Id, }) } } } content = append(content, pipelineCreateListItem("[Create Pipeline]")) pipelineList := list.New(content, pipelineListItemDelegate{}, 0, 0) pipelineList.SetShowStatusBar(false) pipelineList.Title = "Pipelines" return pipelineWidgetList{ list: pipelineList, client: client, }, nil } type pipelineCreateForm struct { list list.Model } func (m pipelineCreateForm) Init() tea.Cmd { return nil } func (m pipelineCreateForm) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "enter": // TODO: enable submitting } } var cmd tea.Cmd m.list, cmd = m.list.Update(msg) items := m.list.Items() for i := 0; i < len(items); i++ { if e, ok := items[i].(createPipelineItem); ok { if i == m.list.Index() { e.field.Focus() e.field, _ = e.field.Update(msg) } else { e.field.Blur() } m.list.SetItem(i, e) } } return m, cmd } func (m pipelineCreateForm) View() string { return m.list.View() } type pipelineCreateListItem string func (i pipelineListItem) FilterValue() string { return "" } func (i pipelineCreateListItem) FilterValue() string { return "" } type pipelineListItemDelegate struct{} func (d pipelineListItemDelegate) Height() int { return 1 } func (d pipelineListItemDelegate) Spacing() int { return 0 } func (d pipelineListItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } func (d pipelineListItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { switch i := listItem.(type) { case pipelineListItem: fn := itemStyle.Render faintFn := faintItemStyle.Render if index == m.Index() { fn = func(s string) string { return selectedItemStyle.Render("> " + s) } faintFn = faintSelectedItemStyle.Render } str := fn(i.name) + faintFn(" ("+i.id+")") fmt.Fprint(w, str) case pipelineCreateListItem: fn := itemStyle.Render if index == m.Index() { fn = func(s string) string { return selectedItemStyle.Render("> " + s) } } str := string(i) fmt.Fprint(w, fn(str)) } } type createPipelineItem struct { field textinput.Model } type createPipelineSubmitItem string func (i createPipelineItem) FilterValue() string { return "" } func (i createPipelineSubmitItem) FilterValue() string { return "" } type createPipelineDelegate struct{} func (d createPipelineDelegate) Height() int { return 1 } func (d createPipelineDelegate) Spacing() int { return 0 } func (d createPipelineDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } func (d createPipelineDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { var str string switch i := listItem.(type) { case createPipelineItem: str = i.field.View() case createPipelineSubmitItem: str = string(i) } fn := itemStyle.Render if index == m.Index() { fn = func(s string) string { return selectedItemStyle.Render("> " + s) } } fmt.Fprint(w, fn(str)) } func createPipelineCreateForm(client graphql.Client, width int, height int) (tea.Model, error) { nameField := textinput.New() nameField.Focus() nameField.Width = 20 nameField.Prompt = "Name: " nameField.Placeholder = "pipeline0" urlField := textinput.New() urlField.Prompt = "Url: " urlField.Placeholder = "https://git.cursorius.ohea/group/repo" pollIntervalField := textinput.New() pollIntervalField.Width = 20 pollIntervalField.Prompt = "Poll Interval: " pollIntervalField.Placeholder = "0 (disable polling)" items := []list.Item{ createPipelineItem{field: nameField}, createPipelineItem{field: urlField}, createPipelineItem{field: pollIntervalField}, createPipelineSubmitItem("> Submit <"), } l := list.New(items, createPipelineDelegate{}, width, height-10) l.Title = "Create a new pipeline." l.SetShowStatusBar(false) l.KeyMap.GoToStart.SetEnabled(false) l.KeyMap.GoToEnd.SetEnabled(false) l.KeyMap.Filter.SetEnabled(false) l.KeyMap.Quit.SetEnabled(false) return pipelineCreateForm{ list: l, }, nil } func CreatePipelineWidget(client graphql.Client) (tea.Model, error) { view, err := createPipelineWidgetList(client) if err != nil { return pipelineWidget{}, err } return pipelineWidget{ currentView: view, }, nil }