package widget import ( "context" "fmt" "io" "git.ohea.xyz/cursorius/tui/queries" "github.com/Khan/genqlient/graphql" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" ) type pipelineWidgetViewPage int const ( topLevel pipelineWidgetViewPage = 0 viewRun = 1 viewRunBuildOutput = 2 viewRunStdout = 3 viewRunStderr = 4 ) type pipelineWidgetView struct { client graphql.Client page pipelineWidgetViewPage pipeline queries.GetPipelinePipeline runsList list.Model viewRunSelector list.Model viewRunBuildOutputPager viewport.Model viewRunStdoutPager viewport.Model viewRunStderrPager viewport.Model } func (m pipelineWidgetView) Init() tea.Cmd { return nil } func (m pipelineWidgetView) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.runsList.SetWidth(msg.Width - 20) m.runsList.SetHeight(msg.Height - 20) case tea.KeyMsg: switch msg.String() { case "r": return m, func() tea.Msg { view, err := createPipelineWidgetView(m.client, m.pipeline.Id) if err != nil { return err } return pipelineWidgetScreenSwitch(view) } case "backspace": switch m.page { case topLevel: return m, func() tea.Msg { view, err := createPipelineWidgetList(m.client) if err != nil { return err } return pipelineWidgetScreenSwitch(view) } case viewRun: m.page = topLevel case viewRunBuildOutput: m.page = viewRun case viewRunStderr: m.page = viewRun case viewRunStdout: m.page = viewRun } case "enter": switch m.page { case topLevel: run := m.pipeline.Runs[m.runsList.Index()] content := []list.Item{ pipelineWidgetViewRunsSelectorItem(fmt.Sprintf("ID: %v", run.Id)), } if run.InProgress != nil { content = append(content, pipelineWidgetViewRunsSelectorItem(fmt.Sprintf("In Progress: %v", *run.InProgress))) } else { content = append(content, pipelineWidgetViewRunsSelectorItem("In Progress: null")) } if run.Result != nil { content = append(content, pipelineWidgetViewRunsSelectorItem(fmt.Sprintf("Result: %v", *run.Result))) } else { content = append(content, pipelineWidgetViewRunsSelectorItem("Result: null")) } content = append(content, pipelineWidgetViewRunsSelectorItem("View Build Output")) content = append(content, pipelineWidgetViewRunsSelectorItem("View Stdout")) content = append(content, pipelineWidgetViewRunsSelectorItem("View Stderr")) runOutputList := list.New(content, pipelineWigetViewRunsSelectorItemDelegate{}, m.runsList.Width(), m.runsList.Height()) runOutputList.SetShowStatusBar(false) runOutputList.Title = "View Run" runOutputList.DisableQuitKeybindings() m.viewRunSelector = runOutputList m.page = viewRun case viewRun: run := m.pipeline.Runs[m.runsList.Index()] switch m.viewRunSelector.Index() { case 3: if run.BuildOutput == nil { break } m.viewRunBuildOutputPager = viewport.New(m.runsList.Width(), m.runsList.Height()) m.viewRunBuildOutputPager.SetContent(*run.BuildOutput) m.page = viewRunBuildOutput case 4: if run.Stdout == nil { break } m.viewRunStdoutPager = viewport.New(m.runsList.Width(), m.runsList.Height()) m.viewRunStdoutPager.SetContent(*run.Stdout) m.page = viewRunStdout case 5: if run.Stderr == nil { break } m.viewRunStderrPager = viewport.New(m.runsList.Width(), m.runsList.Height()) m.viewRunStderrPager.SetContent(*run.Stderr) m.page = viewRunStderr } } } } var cmd tea.Cmd switch m.page { case topLevel: m.runsList, cmd = m.runsList.Update(msg) case viewRun: m.viewRunSelector, cmd = m.viewRunSelector.Update(msg) case viewRunBuildOutput: m.viewRunBuildOutputPager, cmd = m.viewRunBuildOutputPager.Update(msg) case viewRunStdout: m.viewRunStdoutPager, cmd = m.viewRunStdoutPager.Update(msg) case viewRunStderr: m.viewRunStderrPager, cmd = m.viewRunStderrPager.Update(msg) } return m, cmd } func (m pipelineWidgetView) View() string { s := "" switch m.page { case topLevel: s += fmt.Sprintf("Pipeline: %v\n\n", m.pipeline.Name) s += fmt.Sprintf("Id: %v\n", m.pipeline.Id) s += fmt.Sprintf("Url: %v\n", m.pipeline.Url) s += fmt.Sprintf("Poll Interval: %v\n", m.pipeline.PollInterval) if m.pipeline.CloneCredential != nil { s += fmt.Sprintf("Clone Credential: %v (%v)\n\n", m.pipeline.CloneCredential.Name, m.pipeline.CloneCredential.Type) } else { s += fmt.Sprintf("Clone Credential: None\n\n") } s += m.runsList.View() case viewRun: s += m.viewRunSelector.View() case viewRunBuildOutput: s += m.viewRunBuildOutputPager.View() case viewRunStdout: s += m.viewRunStdoutPager.View() case viewRunStderr: s += m.viewRunStderrPager.View() } return s } func createPipelineWidgetView(client graphql.Client, id string) (tea.Model, error) { getPipelineResp, err := queries.GetPipeline(context.Background(), client, id) if err != nil { return nil, err } if getPipelineResp.Pipeline == nil { return nil, fmt.Errorf("Server did not give us a pipeline") } var content []list.Item for _, run := range getPipelineResp.Pipeline.Runs { content = append(content, pipelineWidgetViewRunsItem{ id: run.Id, }) } runsList := list.New(content, pipelineWigetViewRunsItemDelegate{}, 0, 0) runsList.SetShowStatusBar(false) runsList.Title = "Runs" return pipelineWidgetView{ client: client, page: topLevel, pipeline: *getPipelineResp.Pipeline, runsList: runsList, }, nil } type pipelineWidgetViewRunsItem struct { id string } func (i pipelineWidgetViewRunsItem) FilterValue() string { return "" } type pipelineWigetViewRunsItemDelegate struct{} func (d pipelineWigetViewRunsItemDelegate) Height() int { return 1 } func (d pipelineWigetViewRunsItemDelegate) Spacing() int { return 0 } func (d pipelineWigetViewRunsItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } func (d pipelineWigetViewRunsItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { switch i := listItem.(type) { case pipelineWidgetViewRunsItem: fn := itemStyle.Render if index == m.Index() { fn = func(s string) string { return selectedItemStyle.Render("> " + s) } } str := fn(i.id) fmt.Fprint(w, str) } } type pipelineWidgetViewRunsSelectorItem string func (i pipelineWidgetViewRunsSelectorItem) FilterValue() string { return "" } type pipelineWigetViewRunsSelectorItemDelegate struct{} func (d pipelineWigetViewRunsSelectorItemDelegate) Height() int { return 1 } func (d pipelineWigetViewRunsSelectorItemDelegate) Spacing() int { return 0 } func (d pipelineWigetViewRunsSelectorItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } func (d pipelineWigetViewRunsSelectorItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { switch i := listItem.(type) { case pipelineWidgetViewRunsSelectorItem: fn := itemStyle.Render if index == m.Index() { fn = func(s string) string { return selectedItemStyle.Render("> " + s) } } str := fn(string(i)) fmt.Fprint(w, str) } }