2022-08-24 17:57:32 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/charmbracelet/bubbles/spinner"
|
|
|
|
"github.com/charmbracelet/bubbles/timer"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
This example assumes an existing understanding of commands and messages. If you
|
|
|
|
haven't already read our tutorials on the basics of Bubble Tea and working
|
|
|
|
with commands, we recommend reading those first.
|
|
|
|
|
|
|
|
Find them at:
|
|
|
|
https://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands
|
|
|
|
https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics
|
|
|
|
*/
|
|
|
|
|
|
|
|
// sessionState is used to track which model is focused
|
|
|
|
type sessionState uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultTime = time.Minute
|
|
|
|
timerView sessionState = iota
|
|
|
|
spinnerView
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Available spinners
|
|
|
|
spinners = []spinner.Spinner{
|
|
|
|
spinner.Line,
|
|
|
|
spinner.Dot,
|
|
|
|
spinner.MiniDot,
|
|
|
|
spinner.Jump,
|
|
|
|
spinner.Pulse,
|
|
|
|
spinner.Points,
|
|
|
|
spinner.Globe,
|
|
|
|
spinner.Moon,
|
|
|
|
spinner.Monkey,
|
|
|
|
}
|
|
|
|
modelStyle = lipgloss.NewStyle().
|
2022-10-03 12:06:18 -04:00
|
|
|
Width(15).
|
|
|
|
Height(5).
|
|
|
|
Align(lipgloss.Center, lipgloss.Center).
|
2022-08-24 17:57:32 -04:00
|
|
|
BorderStyle(lipgloss.HiddenBorder())
|
|
|
|
focusedModelStyle = lipgloss.NewStyle().
|
2022-10-03 12:06:18 -04:00
|
|
|
Width(15).
|
|
|
|
Height(5).
|
|
|
|
Align(lipgloss.Center, lipgloss.Center).
|
2022-08-24 17:57:32 -04:00
|
|
|
BorderStyle(lipgloss.NormalBorder()).
|
|
|
|
BorderForeground(lipgloss.Color("69"))
|
|
|
|
spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69"))
|
|
|
|
helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241"))
|
|
|
|
)
|
|
|
|
|
|
|
|
type mainModel struct {
|
|
|
|
state sessionState
|
|
|
|
timer timer.Model
|
|
|
|
spinner spinner.Model
|
|
|
|
index int
|
|
|
|
}
|
|
|
|
|
|
|
|
func newModel(timeout time.Duration) mainModel {
|
|
|
|
m := mainModel{state: timerView}
|
|
|
|
m.timer = timer.New(timeout)
|
|
|
|
m.spinner = spinner.New()
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mainModel) Init() tea.Cmd {
|
|
|
|
// start the timer and spinner on program start
|
|
|
|
return tea.Batch(m.timer.Init(), m.spinner.Tick)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
|
var cmd tea.Cmd
|
|
|
|
var cmds []tea.Cmd
|
|
|
|
switch msg := msg.(type) {
|
|
|
|
case tea.KeyMsg:
|
|
|
|
switch msg.String() {
|
|
|
|
case "ctrl+c", "q":
|
|
|
|
return m, tea.Quit
|
|
|
|
case "tab":
|
|
|
|
if m.state == timerView {
|
|
|
|
m.state = spinnerView
|
|
|
|
} else {
|
|
|
|
m.state = timerView
|
|
|
|
}
|
|
|
|
case "n":
|
|
|
|
if m.state == timerView {
|
|
|
|
m.timer = timer.New(defaultTime)
|
|
|
|
cmds = append(cmds, m.timer.Init())
|
|
|
|
} else {
|
|
|
|
m.Next()
|
|
|
|
m.resetSpinner()
|
|
|
|
cmds = append(cmds, spinner.Tick)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch m.state {
|
|
|
|
// update whichever model is focused
|
|
|
|
case spinnerView:
|
|
|
|
m.spinner, cmd = m.spinner.Update(msg)
|
|
|
|
cmds = append(cmds, cmd)
|
|
|
|
default:
|
|
|
|
m.timer, cmd = m.timer.Update(msg)
|
|
|
|
cmds = append(cmds, cmd)
|
|
|
|
}
|
|
|
|
case spinner.TickMsg:
|
|
|
|
m.spinner, cmd = m.spinner.Update(msg)
|
|
|
|
cmds = append(cmds, cmd)
|
|
|
|
case timer.TickMsg:
|
|
|
|
m.timer, cmd = m.timer.Update(msg)
|
|
|
|
cmds = append(cmds, cmd)
|
|
|
|
}
|
|
|
|
return m, tea.Batch(cmds...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mainModel) View() string {
|
|
|
|
var s string
|
|
|
|
model := m.currentFocusedModel()
|
|
|
|
if m.state == timerView {
|
2022-09-14 19:08:36 -04:00
|
|
|
s += lipgloss.JoinHorizontal(lipgloss.Top, focusedModelStyle.Render(fmt.Sprintf("%4s", m.timer.View())), modelStyle.Render(m.spinner.View()))
|
2022-08-24 17:57:32 -04:00
|
|
|
} else {
|
|
|
|
s += lipgloss.JoinHorizontal(lipgloss.Top, modelStyle.Render(fmt.Sprintf("%4s", m.timer.View())), focusedModelStyle.Render(m.spinner.View()))
|
|
|
|
}
|
2022-10-03 12:06:18 -04:00
|
|
|
s += helpStyle.Render(fmt.Sprintf("\ntab: focus next • n: new %s • q: exit\n", model))
|
2022-08-24 17:57:32 -04:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mainModel) currentFocusedModel() string {
|
|
|
|
if m.state == timerView {
|
|
|
|
return "timer"
|
|
|
|
}
|
|
|
|
return "spinner"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mainModel) Next() {
|
|
|
|
if m.index == len(spinners)-1 {
|
|
|
|
m.index = 0
|
|
|
|
} else {
|
|
|
|
m.index++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mainModel) resetSpinner() {
|
|
|
|
m.spinner = spinner.New()
|
|
|
|
m.spinner.Style = spinnerStyle
|
|
|
|
m.spinner.Spinner = spinners[m.index]
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
p := tea.NewProgram(newModel(defaultTime))
|
|
|
|
|
|
|
|
if err := p.Start(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|