tui.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package tui
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/NimbleMarkets/ntcharts/linechart/streamlinechart"
  6. "github.com/charmbracelet/bubbles/viewport"
  7. tea "github.com/charmbracelet/bubbletea"
  8. "github.com/charmbracelet/lipgloss"
  9. )
  10. // Bubbletea model
  11. type Model struct {
  12. width int
  13. Addresses []Address // as defined in internal/tui/types.go
  14. viewport viewport.Model
  15. UpdateSpeed time.Duration
  16. }
  17. func InitialModel(addresses []string, speed time.Duration) Model {
  18. var model Model
  19. model.viewport = viewport.New(0, 0)
  20. model.viewport.MouseWheelEnabled = true
  21. model.UpdateSpeed = speed
  22. for _, address := range addresses {
  23. var addr Address
  24. addr.max_results = 80
  25. addr.Address = address
  26. model.Addresses = append(model.Addresses, addr)
  27. }
  28. return model
  29. }
  30. func (m Model) Init() tea.Cmd {
  31. return m.Tick()
  32. }
  33. type tickMsg time.Time
  34. func (m Model) Tick() tea.Cmd {
  35. return tea.Tick(time.Millisecond*m.UpdateSpeed, func(t time.Time) tea.Msg {
  36. return tickMsg(t)
  37. })
  38. }
  39. func (m Model) content() string {
  40. var headerStyle = lipgloss.NewStyle().
  41. Bold(true).
  42. Italic(true)
  43. var blockStyle = lipgloss.NewStyle().
  44. Width(m.width).
  45. Align(lipgloss.Center)
  46. var footerStyle = lipgloss.NewStyle().
  47. Width(m.width).
  48. Align(lipgloss.Center).
  49. Italic(true).
  50. Faint(true)
  51. output := "\n"
  52. for _, address := range m.Addresses {
  53. if len(address.results) == 0 {
  54. output = output + fmt.Sprintf("\n%s\tloading...\n\n", headerStyle.Render(address.Address))
  55. } else {
  56. output = output + fmt.Sprintf("\n%s\n\n", blockStyle.Render(headerStyle.Render(address.Address)))
  57. // Linechart
  58. slc := streamlinechart.New(m.width, 7)
  59. for _, v := range address.results {
  60. slc.Push(v)
  61. }
  62. slc.Draw()
  63. output = output + blockStyle.Render(fmt.Sprintf("\n%s\n", slc.View()))
  64. }
  65. }
  66. output = output + footerStyle.Render("\n--------\npingo v0\n")
  67. return output
  68. }
  69. func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  70. var cmd tea.Cmd
  71. var cmds []tea.Cmd
  72. switch msg := msg.(type) {
  73. // if case is KeyMsg (keypress)
  74. case tea.WindowSizeMsg:
  75. m.width = msg.Width
  76. for i, address := range m.Addresses {
  77. address.max_results = m.width
  78. m.Addresses[i] = address
  79. }
  80. m.viewport.Height = msg.Height
  81. m.viewport.Width = msg.Width
  82. // m.viewport.YPosition = 0
  83. case tea.KeyMsg:
  84. if k := msg.String(); k == "j" { // scroll down
  85. m.viewport.HalfViewDown()
  86. } else if k == "k" { // scroll up
  87. m.viewport.HalfViewUp()
  88. } else {
  89. return m, tea.Quit
  90. }
  91. case tickMsg:
  92. cmds = append(cmds, m.Tick())
  93. cmds = append(cmds, m.Poll)
  94. }
  95. m.viewport, cmd = m.viewport.Update(msg)
  96. m.viewport.SetContent(m.content())
  97. cmds = append(cmds, cmd)
  98. // cmds = append(cmds, m.Poll)
  99. return m, tea.Batch(cmds...)
  100. }
  101. func (m Model) View() string {
  102. return fmt.Sprintf("\n%s\n", m.viewport.View())
  103. }
  104. // A wrapper for the underlying [tui.Address.Poll] function. For each address in
  105. // [tui.Model.Addresses], run its respective Poll function and update [tui.Model]
  106. //
  107. // NOTE(async): this function fully blocks execution of the current thread.
  108. func (m Model) Poll() tea.Msg {
  109. for i, element := range m.Addresses {
  110. element.Poll()
  111. // element.results = append(element.results, -1)
  112. m.Addresses[i] = element
  113. }
  114. return nil
  115. }