summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 21:24:04 +0300
committerPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 21:24:04 +0300
commit235ff7f34e46553386b2abb9104dd467c2724d70 (patch)
tree9ee5e7074f8b2f8c428e4151b8fc1acd44e11377
parent1989b6a3d4339321797a442d2fa2bbee21e23cd1 (diff)
Show fireworks on exit
-rw-r--r--cmd/tasksamurai/main.go2
-rw-r--r--internal/ui/fireworks.go120
2 files changed, 122 insertions, 0 deletions
diff --git a/cmd/tasksamurai/main.go b/cmd/tasksamurai/main.go
index 52263de..a9d1837 100644
--- a/cmd/tasksamurai/main.go
+++ b/cmd/tasksamurai/main.go
@@ -35,4 +35,6 @@ func main() {
fmt.Fprintln(os.Stderr, "error running ui:", err)
os.Exit(1)
}
+
+ ui.Fireworks()
}
diff --git a/internal/ui/fireworks.go b/internal/ui/fireworks.go
new file mode 100644
index 0000000..c902a67
--- /dev/null
+++ b/internal/ui/fireworks.go
@@ -0,0 +1,120 @@
+package ui
+
+import (
+ "math/rand"
+ "strings"
+ "time"
+
+ tea "github.com/charmbracelet/bubbletea"
+)
+
+type fwModel struct {
+ width int
+ height int
+ start time.Time
+ fws []firework
+}
+
+type firework struct {
+ x, y int
+ frame int
+}
+
+type tickMsg struct{}
+
+func tick() tea.Cmd {
+ return tea.Tick(150*time.Millisecond, func(time.Time) tea.Msg { return tickMsg{} })
+}
+
+func (m fwModel) Init() tea.Cmd {
+ m.start = time.Now()
+ return tick()
+}
+
+var frames = [][]string{
+ {"*"},
+ {" * ", "* *", " * "},
+ {" * ", " * * ", "* *", " * * ", " * "},
+}
+
+func (m fwModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+ switch msg := msg.(type) {
+ case tea.WindowSizeMsg:
+ m.width = msg.Width
+ m.height = msg.Height
+ return m, nil
+ case tickMsg:
+ if time.Since(m.start) > 5*time.Second {
+ return m, tea.Quit
+ }
+ // advance frames
+ for i := 0; i < len(m.fws); {
+ m.fws[i].frame++
+ if m.fws[i].frame >= len(frames) {
+ m.fws = append(m.fws[:i], m.fws[i+1:]...)
+ } else {
+ i++
+ }
+ }
+ // maybe create new firework
+ if m.width > 0 && m.height > 0 {
+ if rand.Float64() < 0.4 {
+ x := rand.Intn(m.width)
+ y := rand.Intn(m.height)
+ m.fws = append(m.fws, firework{x: x, y: y})
+ }
+ }
+ return m, tick()
+ case tea.KeyMsg:
+ return m, tea.Quit
+ }
+ return m, nil
+}
+
+func (m fwModel) View() string {
+ if m.width == 0 || m.height == 0 {
+ return ""
+ }
+ grid := make([][]rune, m.height)
+ for i := range grid {
+ row := make([]rune, m.width)
+ for j := range row {
+ row[j] = ' '
+ }
+ grid[i] = row
+ }
+ for _, fw := range m.fws {
+ fr := frames[fw.frame]
+ oy := fw.y - len(fr)/2
+ for dy, line := range fr {
+ y := oy + dy
+ if y < 0 || y >= m.height {
+ continue
+ }
+ ox := fw.x - len([]rune(line))/2
+ for dx, r := range line {
+ x := ox + dx
+ if x < 0 || x >= m.width {
+ continue
+ }
+ if r != ' ' {
+ grid[y][x] = r
+ }
+ }
+ }
+ }
+ var b strings.Builder
+ for _, row := range grid {
+ b.WriteString(string(row))
+ b.WriteByte('\n')
+ }
+ return b.String()
+}
+
+// Fireworks runs a short fireworks animation. It stops after five seconds or
+// when a key is pressed.
+func Fireworks() {
+ rand.Seed(time.Now().UnixNano())
+ p := tea.NewProgram(fwModel{}, tea.WithAltScreen())
+ p.Run()
+}