Files
clojure-tui/examples/spinner.clj
2026-01-22 10:50:26 -05:00

91 lines
2.5 KiB
Clojure

(ns examples.spinner
"Spinner example - demonstrates animated loading states.
Mirrors bubbletea's spinner example."
(:require [tui.core :as tui]))
;; === Spinner Frames ===
(def spinner-styles
{:dots ["⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏"]
:line ["|" "/" "-" "\\"]
:circle ["◐" "◓" "◑" "◒"]
:square ["◰" "◳" "◲" "◱"]
:triangle ["◢" "◣" "◤" "◥"]
:bounce ["⠁" "⠂" "⠄" "⠂"]
:dots2 ["⣾" "⣽" "⣻" "⢿" "⡿" "⣟" "⣯" "⣷"]
:arc ["◜" "◠" "◝" "◞" "◡" "◟"]
:moon ["🌑" "🌒" "🌓" "🌔" "🌕" "🌖" "🌗" "🌘"]})
;; === Model ===
(def initial-model
{:frame 0
:style :dots
:loading true
:message "Loading..."
:styles (keys spinner-styles)
:style-idx 0})
;; === Update ===
(defn update-model [{:keys [styles style-idx] :as model} msg]
(cond
;; Quit
(or (tui/key= msg "q")
(tui/key= msg [:ctrl \c]))
[model tui/quit]
;; Tick - advance frame
(= (first msg) :tick)
(if (:loading model)
[(update model :frame inc) (tui/tick 80)]
[model nil])
;; Space - simulate completion
(tui/key= msg " ")
[(assoc model :loading false :message "Done!") nil]
;; Tab - change spinner style
(tui/key= msg :tab)
(let [new-idx (mod (inc style-idx) (count styles))]
[(assoc model
:style-idx new-idx
:style (nth styles new-idx))
nil])
;; r - restart
(tui/key= msg "r")
[(assoc model :loading true :frame 0 :message "Loading...")
(tui/tick 80)]
:else
[model nil]))
;; === View ===
(defn spinner-view [{:keys [frame style]}]
(let [frames (get spinner-styles style)
idx (mod frame (count frames))]
(nth frames idx)))
(defn view [{:keys [loading message style] :as model} _size]
[:col {:gap 1}
[:box {:border :rounded :padding [1 2]}
[:col {:gap 1}
[:text {:bold true} "Spinner Demo"]
[:text ""]
[:row {:gap 1}
(if loading
[:text {:fg :cyan} (spinner-view model)]
[:text {:fg :green} "✓"])
[:text message]]
[:text ""]
[:text {:fg :gray} (str "Style: " (name style))]]]
[:col
[:text {:fg :gray} "tab: change style space: complete r: restart q: quit"]]])
;; === Main ===
(defn -main [& _args]
(println "Starting spinner...")
(tui/run {:init initial-model
:update update-model
:view view
:init-cmd (tui/tick 80)})
(println "Spinner demo finished."))