package main import ( "context" "fmt" "os" "time" "code.humancabbage.net/sam/moonmath/moon" "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var baseStyle = lipgloss.NewStyle(). BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color("240")) type model struct { math moon.Math prices table.Model projections table.Model } func (m model) Init() tea.Cmd { return func() tea.Msg { _ = m.math.Refresh(context.TODO()) return m.math } } func initialModel() model { math := moon.NewMath(nil, nil) tableStyle := table.DefaultStyles() tableStyle.Selected = lipgloss.NewStyle() prices := table.New( table.WithColumns([]table.Column{ {Title: "Price", Width: 9}, }), table.WithHeight(1), table.WithStyles(tableStyle), ) projectionCols := []table.Column{ {Title: "Labels", Width: 8}, } for i := range math.Columns { projectionCols = append(projectionCols, table.Column{ Title: math.Columns[i].Base.Name(), Width: 10, }) } projectionRows := make([]table.Row, len(math.Goals)+2) for i := range projectionRows { projectionRows[i] = make(table.Row, len(projectionCols)) } projectionRows[0][0] = "Starting" projectionRows[1][0] = "CDPR" for i := range math.Goals { projectionRows[i+2][0] = fmt.Sprintf("$%.0f", math.Goals[i]) } projections := table.New( table.WithColumns(projectionCols), table.WithRows(projectionRows), table.WithHeight(len(math.Goals)+2), table.WithStyles(tableStyle), ) return model{ math: math, prices: prices, projections: projections, } } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case moon.Math: m.math = msg refillPrice(&m) refillProjections(&m) return m, tea.Tick(time.Second*30, func(t time.Time) tea.Msg { _ = m.math.Refresh(context.TODO()) return m.math }) case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q", "esc": return m, tea.Quit } } return m, nil } func refillPrice(m *model) { rows := []table.Row{ []string{fmt.Sprintf("$%0.2f", m.math.CurrentPrice)}, } m.prices.SetRows(rows) } func refillProjections(m *model) { rows := m.projections.Rows() for col := range m.math.Columns { _ = col never := false if m.math.Columns[col].CDPR <= 1 { never = true } rows[0][col+1] = fmt.Sprintf("$%.2f", m.math.Columns[col].StartingPrice) rows[1][col+1] = fmt.Sprintf("%.2f%%", (m.math.Columns[col].CDPR-1)*100) for row := 0; row < len(m.math.Goals); row++ { var cell string if never { cell = "NEVER!!!!!" } else { cell = m.math.Columns[col]. Projections.Dates[row]. Format("2006-01-02") } rows[row+2][col+1] = cell } } m.projections.SetRows(rows) } func (m model) View() string { var s string s += lipgloss.JoinHorizontal( lipgloss.Top, baseStyle.Render(m.prices.View()), baseStyle.Render(m.projections.View()), ) return s + "\n" } func main() { p := tea.NewProgram(initialModel()) if _, err := p.Run(); err != nil { fmt.Printf("program error: %v\n", err) os.Exit(1) } }