package tui import ( "fmt" "time" "github.com/NimbleMarkets/ntcharts/linechart/streamlinechart" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) // Bubbletea model type Model struct { width int Addresses []Address // as defined in internal/tui/types.go viewport viewport.Model UpdateSpeed time.Duration } func InitialModel(addresses []string, speed time.Duration) Model { var model Model model.viewport = viewport.New(0, 0) model.viewport.MouseWheelEnabled = true model.UpdateSpeed = speed for _, address := range addresses { var addr Address addr.max_results = 80 addr.Address = address model.Addresses = append(model.Addresses, addr) } return model } func (m Model) Init() tea.Cmd { return m.Tick() } type tickMsg time.Time func (m Model) Tick() tea.Cmd { return tea.Tick(time.Millisecond*m.UpdateSpeed, func(t time.Time) tea.Msg { return tickMsg(t) }) } func (m Model) content() string { var headerStyle = lipgloss.NewStyle(). Bold(true). Italic(true) var blockStyle = lipgloss.NewStyle(). Width(m.width). Align(lipgloss.Center) var footerStyle = lipgloss.NewStyle(). Width(m.width). Align(lipgloss.Center). Italic(true). Faint(true) output := "\n" for _, address := range m.Addresses { if len(address.results) == 0 { output = output + fmt.Sprintf("\n%s\tloading...\n\n", headerStyle.Render(address.Address)) } else { output = output + fmt.Sprintf("\n%s\n\n", blockStyle.Render(headerStyle.Render(address.Address))) // Linechart slc := streamlinechart.New(m.width, 7) for _, v := range address.results { slc.Push(v) } slc.Draw() output = output + blockStyle.Render(fmt.Sprintf("\n%s\n", slc.View())) } } output = output + footerStyle.Render("\n--------\npingo v0\n") return output } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd var cmds []tea.Cmd switch msg := msg.(type) { // if case is KeyMsg (keypress) case tea.WindowSizeMsg: m.width = msg.Width for i, address := range m.Addresses { address.max_results = m.width m.Addresses[i] = address } m.viewport.Height = msg.Height m.viewport.Width = msg.Width // m.viewport.YPosition = 0 case tea.KeyMsg: if k := msg.String(); k == "j" { // scroll down m.viewport.HalfViewDown() } else if k == "k" { // scroll up m.viewport.HalfViewUp() } else { return m, tea.Quit } case tickMsg: cmds = append(cmds, m.Tick()) cmds = append(cmds, m.Poll) } m.viewport, cmd = m.viewport.Update(msg) m.viewport.SetContent(m.content()) cmds = append(cmds, cmd) // cmds = append(cmds, m.Poll) return m, tea.Batch(cmds...) } func (m Model) View() string { return fmt.Sprintf("\n%s\n", m.viewport.View()) } // A wrapper for the underlying [tui.Address.Poll] function. For each address in // [tui.Model.Addresses], run its respective Poll function and update [tui.Model] // // NOTE(async): this function fully blocks execution of the current thread. func (m Model) Poll() tea.Msg { for i, element := range m.Addresses { element.Poll() // element.results = append(element.results, -1) m.Addresses[i] = element } return nil }