1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
package main
import (
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"time"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/mattn/go-isatty"
)
var (
helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")).Render
mainStyle = lipgloss.NewStyle().MarginLeft(1)
)
func main() {
var (
daemonMode bool
showHelp bool
opts []tea.ProgramOption
)
flag.BoolVar(&daemonMode, "d", false, "run as a daemon")
flag.BoolVar(&showHelp, "h", false, "show help")
flag.Parse()
if showHelp {
flag.Usage()
os.Exit(0)
}
if daemonMode || !isatty.IsTerminal(os.Stdout.Fd()) {
// If we're in daemon mode don't render the TUI
opts = []tea.ProgramOption{tea.WithoutRenderer()}
} else {
// If we're in TUI mode, discard log output
log.SetOutput(io.Discard)
}
p := tea.NewProgram(newModel(), opts...)
if _, err := p.Run(); err != nil {
fmt.Println("Error starting Bubble Tea program:", err)
os.Exit(1)
}
}
type result struct {
duration time.Duration
emoji string
}
type model struct {
spinner spinner.Model
results []result
quitting bool
}
func newModel() model {
const showLastResults = 5
sp := spinner.New()
sp.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("206"))
return model{
spinner: sp,
results: make([]result, showLastResults),
}
}
func (m model) Init() tea.Cmd {
log.Println("Starting work...")
return tea.Batch(
m.spinner.Tick,
runPretendProcess,
)
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
m.quitting = true
return m, tea.Quit
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
case processFinishedMsg:
d := time.Duration(msg)
res := result{emoji: randomEmoji(), duration: d}
log.Printf("%s Job finished in %s", res.emoji, res.duration)
m.results = append(m.results[1:], res)
return m, runPretendProcess
default:
return m, nil
}
}
func (m model) View() string {
s := "\n" +
m.spinner.View() + " Doing some work...\n\n"
for _, res := range m.results {
if res.duration == 0 {
s += "........................\n"
} else {
s += fmt.Sprintf("%s Job finished in %s\n", res.emoji, res.duration)
}
}
s += helpStyle("\nPress any key to exit\n")
if m.quitting {
s += "\n"
}
return mainStyle.Render(s)
}
// processFinishedMsg is sent when a pretend process completes.
type processFinishedMsg time.Duration
// pretendProcess simulates a long-running process.
func runPretendProcess() tea.Msg {
pause := time.Duration(rand.Int63n(899)+100) * time.Millisecond // nolint:gosec
time.Sleep(pause)
return processFinishedMsg(pause)
}
func randomEmoji() string {
emojis := []rune("🍦🧋🍡🤠👾😭🦊🐯🦆🥨🎏🍔🍒🍥🎮📦🦁🐶🐸🍕🥐🧲🚒🥇🏆🌽")
return string(emojis[rand.Intn(len(emojis))]) // nolint:gosec
}
|