|
@@ -56,9 +56,7 @@ package pingo
|
|
|
import (
|
|
import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"slices"
|
|
"slices"
|
|
|
- "time"
|
|
|
|
|
|
|
|
|
|
- "charm.land/bubbles/v2/viewport"
|
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
tea "charm.land/bubbletea/v2"
|
|
|
"charm.land/lipgloss/v2"
|
|
"charm.land/lipgloss/v2"
|
|
|
"github.com/NimbleMarkets/ntcharts/linechart/streamlinechart"
|
|
"github.com/NimbleMarkets/ntcharts/linechart/streamlinechart"
|
|
@@ -66,6 +64,17 @@ import (
|
|
|
|
|
|
|
|
// Style Definitions
|
|
// Style Definitions
|
|
|
var (
|
|
var (
|
|
|
|
|
+ // 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)
|
|
|
// A style for chart headers
|
|
// A style for chart headers
|
|
|
headerStyle = lipgloss.NewStyle().
|
|
headerStyle = lipgloss.NewStyle().
|
|
|
Bold(true).
|
|
Bold(true).
|
|
@@ -92,22 +101,9 @@ var (
|
|
|
// BorderForeground(lipgloss.Color("8")).
|
|
// BorderForeground(lipgloss.Color("8")).
|
|
|
// // Padding(1, 2).
|
|
// // Padding(1, 2).
|
|
|
// BorderStyle(lipgloss.NormalBorder())
|
|
// 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
|
|
|
- tickMsg time.Time
|
|
|
|
|
pollResultMsg struct {
|
|
pollResultMsg struct {
|
|
|
results []float64
|
|
results []float64
|
|
|
index int
|
|
index int
|
|
@@ -117,19 +113,17 @@ type ( // tea.Msg signatures
|
|
|
|
|
|
|
|
// Bubbletea model
|
|
// Bubbletea model
|
|
|
type Model struct {
|
|
type Model struct {
|
|
|
- Addresses []Address // as defined in internal/tui/types.go
|
|
|
|
|
- viewport viewport.Model // mark: opinionated render
|
|
|
|
|
- UpdateSpeed time.Duration
|
|
|
|
|
|
|
+ Addresses []Address // as defined in internal/tui/types.go
|
|
|
ChartHeight int
|
|
ChartHeight int
|
|
|
Height int
|
|
Height int
|
|
|
Width int
|
|
Width int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func InitialModel(addresses []string, speed time.Duration, chartHeight int) Model {
|
|
|
|
|
|
|
+func InitialModel(addresses []string, width, height, chartHeight int) Model {
|
|
|
var model Model
|
|
var model Model
|
|
|
- model.viewport.MouseWheelEnabled = true // mark: opinionated render
|
|
|
|
|
- model.UpdateSpeed = speed
|
|
|
|
|
model.ChartHeight = chartHeight
|
|
model.ChartHeight = chartHeight
|
|
|
|
|
+ model.Width = width
|
|
|
|
|
+ model.Height = height
|
|
|
|
|
|
|
|
for _, address := range addresses {
|
|
for _, address := range addresses {
|
|
|
var addr Address
|
|
var addr Address
|
|
@@ -141,94 +135,46 @@ func InitialModel(addresses []string, speed time.Duration, chartHeight int) Mode
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m Model) Init() tea.Cmd {
|
|
func (m Model) Init() tea.Cmd {
|
|
|
- return m.Tick()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (m Model) Tick() tea.Cmd {
|
|
|
|
|
- return tea.Tick(time.Millisecond*m.UpdateSpeed, func(t time.Time) tea.Msg {
|
|
|
|
|
- return tickMsg(t)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return m.Poll()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // mark: opinionated render
|
|
|
|
|
|
|
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
|
|
var cmd tea.Cmd
|
|
var cmd tea.Cmd
|
|
|
var cmds []tea.Cmd
|
|
var cmds []tea.Cmd
|
|
|
|
|
|
|
|
switch msg := msg.(type) {
|
|
switch msg := msg.(type) {
|
|
|
- // if case is KeyMsg (keypress)
|
|
|
|
|
- case tea.WindowSizeMsg: // mark: opinionated render
|
|
|
|
|
- if m.Width == 0 && m.Height == 0 {
|
|
|
|
|
- m.viewport = viewport.New(
|
|
|
|
|
- viewport.WithHeight(10),
|
|
|
|
|
- viewport.WithWidth(msg.Width),
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- m.Width = msg.Width
|
|
|
|
|
- m.Height = msg.Height
|
|
|
|
|
- for i, address := range m.Addresses {
|
|
|
|
|
- address.MaxResults = m.Width
|
|
|
|
|
- m.Addresses[i] = address
|
|
|
|
|
- }
|
|
|
|
|
- m.viewport.SetHeight(m.Height - m.getVerticalMargin())
|
|
|
|
|
- m.viewport.SetWidth(m.Width)
|
|
|
|
|
- m.viewport.YPosition = 1
|
|
|
|
|
-
|
|
|
|
|
- case tea.KeyPressMsg: // mark: opinionated render
|
|
|
|
|
- if k := msg.String(); k == "j" { // scroll down
|
|
|
|
|
- m.viewport.ScrollDown(1)
|
|
|
|
|
- } else if k == "k" { // scroll up
|
|
|
|
|
- m.viewport.ScrollUp(1)
|
|
|
|
|
- } else {
|
|
|
|
|
- if k == "ctrl+c" {
|
|
|
|
|
- cmds = append(cmds, tea.Quit)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case tickMsg:
|
|
|
|
|
- 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.Render())
|
|
|
|
|
- m.viewport, cmd = m.viewport.Update(msg)
|
|
|
|
|
|
|
+ for i := range m.Addresses {
|
|
|
|
|
+ m.Addresses[i].MaxResults = m.Width
|
|
|
|
|
+ }
|
|
|
cmds = append(cmds, cmd)
|
|
cmds = append(cmds, cmd)
|
|
|
- // cmds = append(cmds, m.Poll)
|
|
|
|
|
|
|
|
|
|
return m, tea.Batch(cmds...)
|
|
return m, tea.Batch(cmds...)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (m Model) View() tea.View { // mark: opinionated render
|
|
|
|
|
- content := fmt.Sprintf("%s%s\n%s", m.header(), m.viewport.View(), m.footer())
|
|
|
|
|
-
|
|
|
|
|
- var v tea.View
|
|
|
|
|
- v.SetContent(content)
|
|
|
|
|
- v.AltScreen = true
|
|
|
|
|
- return v
|
|
|
|
|
-}
|
|
|
|
|
-func (m Model) Render() string { // mark: opinionated render
|
|
|
|
|
- var output string
|
|
|
|
|
|
|
+func (m Model) View() tea.View {
|
|
|
|
|
+ var content string
|
|
|
for _, address := range m.Addresses {
|
|
for _, address := range m.Addresses {
|
|
|
if len(address.Results) == 0 {
|
|
if len(address.Results) == 0 {
|
|
|
- output = output + fmt.Sprintf("\n%s\tloading...", headerStyle.Render(address.Address))
|
|
|
|
|
|
|
+ content = content + fmt.Sprintf("\n%s\tloading...", headerStyle.Render(address.Address))
|
|
|
} else if m.Width != 0 && m.Height != 0 {
|
|
} else if m.Width != 0 && m.Height != 0 {
|
|
|
if slices.Contains(address.Results, -1) {
|
|
if slices.Contains(address.Results, -1) {
|
|
|
- output = output + blockStyle.Width(m.Width).Render(headerStyle.Render(
|
|
|
|
|
- fmt.Sprintf("\n%s\t%s",
|
|
|
|
|
|
|
+ content = content + fmt.Sprintf("\n%s",
|
|
|
|
|
+ blockStyle.Width(m.Width).Render(headerStyle.Render(
|
|
|
secondaryColor.Render(address.Address),
|
|
secondaryColor.Render(address.Address),
|
|
|
infoStyle.Render("(connection unstable)"),
|
|
infoStyle.Render("(connection unstable)"),
|
|
|
),
|
|
),
|
|
|
- ))
|
|
|
|
|
|
|
+ ))
|
|
|
} else {
|
|
} else {
|
|
|
- output = output + fmt.Sprintf("\n%s",
|
|
|
|
|
|
|
+ content = content + fmt.Sprintf("\n%s",
|
|
|
blockStyle.Width(m.Width).Render(headerStyle.Render(address.Address)))
|
|
blockStyle.Width(m.Width).Render(headerStyle.Render(address.Address)))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Linechart
|
|
// Linechart
|
|
|
// set chartHeight - vertical margin
|
|
// set chartHeight - vertical margin
|
|
|
- chartHeight := m.Height - m.getVerticalMargin()
|
|
|
|
|
|
|
+ chartHeight := m.Height - 2 // m.getVerticalMargin()
|
|
|
|
|
|
|
|
var slc streamlinechart.Model
|
|
var slc streamlinechart.Model
|
|
|
|
|
|
|
@@ -247,21 +193,18 @@ func (m Model) Render() string { // mark: opinionated render
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
slc.Draw()
|
|
slc.Draw()
|
|
|
- output = output + fmt.Sprintf("\n%s", slc.View())
|
|
|
|
|
|
|
+ content = content + fmt.Sprintf("\n%s", slc.View())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return output
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (m Model) header() string { return titleStyle.Width(m.Width).Render("pingo v0") } // mark: opinionated render
|
|
|
|
|
|
|
+ content = content + "\n"
|
|
|
|
|
|
|
|
-func (m Model) footer() string { // mark: opinionated render
|
|
|
|
|
- return footerStyle.Width(m.Width).Render("j/k: down/up\t|\tq/ctrl-c/esc: quit")
|
|
|
|
|
|
|
+ var v tea.View
|
|
|
|
|
+ v.SetContent(content)
|
|
|
|
|
+ v.AltScreen = true
|
|
|
|
|
+ return v
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (m Model) getVerticalMargin() int { return lipgloss.Height(m.header() + m.footer()) } // mark: opinionated render
|
|
|
|
|
-
|
|
|
|
|
// 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 {
|
|
|
var cmds []tea.Cmd
|
|
var cmds []tea.Cmd
|