|
@@ -1,5 +1,9 @@
|
|
|
-// TODO(doc): document TUI lifecycle
|
|
|
|
|
-// TODO(test): write unittests for types.go
|
|
|
|
|
|
|
+// TODO document TUI lifecycle
|
|
|
|
|
+// TODO viewport separation
|
|
|
|
|
+//
|
|
|
|
|
+// BLOCKERS: header/footer height func
|
|
|
|
|
+//
|
|
|
|
|
+// TODO header/footer height func
|
|
|
package pingo
|
|
package pingo
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
@@ -14,7 +18,6 @@ import (
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
// Style Defintions
|
|
// Style Defintions
|
|
|
-
|
|
|
|
|
var (
|
|
var (
|
|
|
// A style for chart headers
|
|
// A style for chart headers
|
|
|
headerStyle = lipgloss.NewStyle().
|
|
headerStyle = lipgloss.NewStyle().
|
|
@@ -33,6 +36,27 @@ var (
|
|
|
// A style for the primary colour
|
|
// A style for the primary colour
|
|
|
// primaryColor = lipgloss.NewStyle().
|
|
// primaryColor = lipgloss.NewStyle().
|
|
|
// Foreground(lipgloss.Color("#f0f3f5"))
|
|
// Foreground(lipgloss.Color("#f0f3f5"))
|
|
|
|
|
+
|
|
|
|
|
+ // A style for handling center-aligning
|
|
|
|
|
+ blockStyle = lipgloss.NewStyle().
|
|
|
|
|
+ Align(lipgloss.Center)
|
|
|
|
|
+
|
|
|
|
|
+ // borderStyle = lipgloss.NewStyle().
|
|
|
|
|
+ // BorderForeground(lipgloss.Color("8")).
|
|
|
|
|
+ // // Padding(1, 2).
|
|
|
|
|
+ // BorderStyle(lipgloss.NormalBorder())
|
|
|
|
|
+
|
|
|
|
|
+ // footer styles
|
|
|
|
|
+ titleStyle = lipgloss.NewStyle().
|
|
|
|
|
+ Align(lipgloss.Center). // implies consumer functions will apply a width
|
|
|
|
|
+ Italic(true).
|
|
|
|
|
+ Faint(true)
|
|
|
|
|
+
|
|
|
|
|
+ // footer style
|
|
|
|
|
+ footerStyle = lipgloss.NewStyle().
|
|
|
|
|
+ Align(lipgloss.Center). // implies consumer functions will apply a width
|
|
|
|
|
+ Italic(true).
|
|
|
|
|
+ Faint(true)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
type ( // tea.Msg signatures
|
|
type ( // tea.Msg signatures
|
|
@@ -51,6 +75,8 @@ type Model struct {
|
|
|
viewport viewport.Model
|
|
viewport viewport.Model
|
|
|
UpdateSpeed time.Duration
|
|
UpdateSpeed time.Duration
|
|
|
ChartHeight int
|
|
ChartHeight int
|
|
|
|
|
+ ModelHeight int
|
|
|
|
|
+ ModelWidth int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func InitialModel(addresses []string, speed time.Duration, chartHeight int) Model {
|
|
func InitialModel(addresses []string, speed time.Duration, chartHeight int) Model {
|
|
@@ -78,51 +104,6 @@ func (m Model) Tick() tea.Cmd {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (m Model) content() string {
|
|
|
|
|
- var blockStyle = lipgloss.NewStyle().
|
|
|
|
|
- Width(m.width).
|
|
|
|
|
- Align(lipgloss.Center)
|
|
|
|
|
-
|
|
|
|
|
- output := "\n"
|
|
|
|
|
- for _, address := range m.Addresses {
|
|
|
|
|
- if len(address.results) == 0 {
|
|
|
|
|
- output = output + fmt.Sprintf("%s\tloading...", headerStyle.Render(address.Address))
|
|
|
|
|
-
|
|
|
|
|
- } else if m.viewport.Width() != 0 && m.viewport.Height() != 0 {
|
|
|
|
|
- if slices.Contains(address.results, -1) {
|
|
|
|
|
- output = output + blockStyle.Render(headerStyle.Render(
|
|
|
|
|
- fmt.Sprintf("\n%s\t%s",
|
|
|
|
|
- secondaryColor.Render(address.Address),
|
|
|
|
|
- infoStyle.Render("(connection unstable)"),
|
|
|
|
|
- ),
|
|
|
|
|
- ))
|
|
|
|
|
- } else {
|
|
|
|
|
- output = output + fmt.Sprintf("\n%s",
|
|
|
|
|
- blockStyle.Render(headerStyle.Render(address.Address)))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Linechart
|
|
|
|
|
- viewportHeight := m.viewport.Height() - 4
|
|
|
|
|
- var slc streamlinechart.Model
|
|
|
|
|
- if m.ChartHeight == 0 && len(m.Addresses) == 1 {
|
|
|
|
|
- slc = streamlinechart.New(m.width, viewportHeight)
|
|
|
|
|
- } else if m.ChartHeight == 0 && len(m.Addresses) > 1 {
|
|
|
|
|
- slc = streamlinechart.New(m.width, viewportHeight-5)
|
|
|
|
|
- } else {
|
|
|
|
|
- slc = streamlinechart.New(m.width, m.ChartHeight)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- for _, v := range address.results {
|
|
|
|
|
- slc.Push(v)
|
|
|
|
|
- }
|
|
|
|
|
- slc.Draw()
|
|
|
|
|
- output = output + blockStyle.Render(fmt.Sprintf("\n%s\n", slc.View()))
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return output
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
var cmd tea.Cmd
|
|
var cmd tea.Cmd
|
|
|
var cmds []tea.Cmd
|
|
var cmds []tea.Cmd
|
|
@@ -130,13 +111,21 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
switch msg := msg.(type) {
|
|
switch msg := msg.(type) {
|
|
|
// if case is KeyMsg (keypress)
|
|
// if case is KeyMsg (keypress)
|
|
|
case tea.WindowSizeMsg:
|
|
case tea.WindowSizeMsg:
|
|
|
- m.width = msg.Width
|
|
|
|
|
|
|
+ if m.ModelWidth == 0 && m.ModelHeight == 0 {
|
|
|
|
|
+ m.viewport = viewport.New(
|
|
|
|
|
+ viewport.WithHeight(10),
|
|
|
|
|
+ viewport.WithWidth(msg.Width),
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ m.ModelWidth = msg.Width
|
|
|
|
|
+ m.ModelHeight = msg.Height
|
|
|
for i, address := range m.Addresses {
|
|
for i, address := range m.Addresses {
|
|
|
- address.max_results = m.width
|
|
|
|
|
|
|
+ address.max_results = m.ModelWidth
|
|
|
m.Addresses[i] = address
|
|
m.Addresses[i] = address
|
|
|
}
|
|
}
|
|
|
- m.viewport.SetHeight(msg.Height - 4)
|
|
|
|
|
- m.viewport.SetWidth(msg.Width)
|
|
|
|
|
|
|
+ m.viewport.SetHeight(m.ModelHeight - m.getVerticalMargin())
|
|
|
|
|
+ m.viewport.SetWidth(m.ModelWidth)
|
|
|
m.viewport.YPosition = 1
|
|
m.viewport.YPosition = 1
|
|
|
|
|
|
|
|
case tea.KeyPressMsg:
|
|
case tea.KeyPressMsg:
|
|
@@ -151,14 +140,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case tickMsg:
|
|
case tickMsg:
|
|
|
- cmds = append(cmds, m.Tick())
|
|
|
|
|
- cmds = append(cmds, m.Poll())
|
|
|
|
|
|
|
+ cmds = append(cmds, m.Tick(), m.Poll())
|
|
|
|
|
|
|
|
case pollResultMsg:
|
|
case pollResultMsg:
|
|
|
m.Addresses[msg.index].results = msg.results
|
|
m.Addresses[msg.index].results = msg.results
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- m.viewport.SetContent(m.content())
|
|
|
|
|
|
|
+ m.viewport.SetContent(m.Render())
|
|
|
m.viewport, cmd = m.viewport.Update(msg)
|
|
m.viewport, cmd = m.viewport.Update(msg)
|
|
|
cmds = append(cmds, cmd)
|
|
cmds = append(cmds, cmd)
|
|
|
// cmds = append(cmds, m.Poll)
|
|
// cmds = append(cmds, m.Poll)
|
|
@@ -167,27 +155,66 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m Model) View() tea.View {
|
|
func (m Model) View() tea.View {
|
|
|
- var headerStyle = lipgloss.NewStyle().
|
|
|
|
|
- Width(m.width).
|
|
|
|
|
- Align(lipgloss.Center).
|
|
|
|
|
- Italic(true).
|
|
|
|
|
- Faint(true)
|
|
|
|
|
-
|
|
|
|
|
- var footerStyle = lipgloss.NewStyle().
|
|
|
|
|
- Width(m.width).
|
|
|
|
|
- Align(lipgloss.Center).
|
|
|
|
|
- Italic(true).
|
|
|
|
|
- Faint(true)
|
|
|
|
|
-
|
|
|
|
|
- header := headerStyle.Render("pingo v0")
|
|
|
|
|
- footer := footerStyle.Render("\nj/k: down/up\t|\tq/ctrl-c/esc: quit\n")
|
|
|
|
|
- content := fmt.Sprintf("\n%s\n%s\n%s", header, m.viewport.View(), footer)
|
|
|
|
|
|
|
+ content := fmt.Sprintf("%s%s\n%s", m.header(), m.viewport.View(), m.footer())
|
|
|
|
|
|
|
|
var v tea.View
|
|
var v tea.View
|
|
|
v.SetContent(content)
|
|
v.SetContent(content)
|
|
|
v.AltScreen = true
|
|
v.AltScreen = true
|
|
|
return v
|
|
return v
|
|
|
}
|
|
}
|
|
|
|
|
+func (m Model) Render() string {
|
|
|
|
|
+ var output string
|
|
|
|
|
+ for _, address := range m.Addresses {
|
|
|
|
|
+ if len(address.results) == 0 {
|
|
|
|
|
+ output = output + fmt.Sprintf("\n%s\tloading...", headerStyle.Render(address.Address))
|
|
|
|
|
+ } else if m.ModelWidth != 0 && m.ModelHeight != 0 {
|
|
|
|
|
+ if slices.Contains(address.results, -1) {
|
|
|
|
|
+ output = output + blockStyle.Width(m.ModelWidth).Render(headerStyle.Render(
|
|
|
|
|
+ fmt.Sprintf("\n%s\t%s",
|
|
|
|
|
+ secondaryColor.Render(address.Address),
|
|
|
|
|
+ infoStyle.Render("(connection unstable)"),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ output = output + fmt.Sprintf("\n%s",
|
|
|
|
|
+ blockStyle.Width(m.ModelWidth).Render(headerStyle.Render(address.Address)))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Linechart
|
|
|
|
|
+ // set chartHeight - vertical margin
|
|
|
|
|
+ chartHeight := m.ModelHeight - m.getVerticalMargin()
|
|
|
|
|
+
|
|
|
|
|
+ var slc streamlinechart.Model
|
|
|
|
|
+
|
|
|
|
|
+ if m.ChartHeight == 0 && len(m.Addresses) == 1 { // catch user specified fullscreen
|
|
|
|
|
+ // render chart at fullscreen
|
|
|
|
|
+ slc = streamlinechart.New(80, chartHeight)
|
|
|
|
|
+ } else if m.ChartHeight == 0 && len(m.Addresses) > 1 { // catch user specified fullscreen
|
|
|
|
|
+ // render chart at fullscreen minus a few lines to hint at scrolling
|
|
|
|
|
+ slc = streamlinechart.New(m.ModelWidth, chartHeight-5)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ slc = streamlinechart.New(m.ModelWidth, m.ChartHeight)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for _, v := range address.results {
|
|
|
|
|
+ slc.Push(v)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ slc.Draw()
|
|
|
|
|
+ output = output + fmt.Sprintf("\n%s", slc.View())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return output
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (m Model) header() string { return titleStyle.Width(m.ModelWidth).Render("pingo v0") }
|
|
|
|
|
+
|
|
|
|
|
+func (m Model) footer() string {
|
|
|
|
|
+ return footerStyle.Width(m.ModelWidth).Render("j/k: down/up\t|\tq/ctrl-c/esc: quit")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (m Model) getVerticalMargin() int { return lipgloss.Height(m.header() + m.footer()) }
|
|
|
|
|
|
|
|
// Returns a batched set of tea.Cmd functions for each address.
|
|
// Returns a batched set of tea.Cmd functions for each address.
|
|
|
func (m Model) Poll() tea.Cmd {
|
|
func (m Model) Poll() tea.Cmd {
|