(ns examples.timer "Countdown timer example - demonstrates async commands. Mirrors bubbletea's stopwatch/timer examples." (:require [tui.core :as tui])) ;; === Model === (def initial-model {:seconds 10 :running true :done false}) ;; === Update === (defn update-model [model msg] (cond ;; Quit (or (tui/key= msg "q") (tui/key= msg [:ctrl \c])) [model tui/quit] ;; Timer tick - decrement timer (= msg :timer-tick) (if (:running model) (let [new-seconds (dec (:seconds model))] (if (<= new-seconds 0) ;; Timer done [(assoc model :seconds 0 :done true :running false) nil] ;; Continue countdown [(assoc model :seconds new-seconds) (tui/after 1000 :timer-tick)])) [model nil]) ;; Space - pause/resume (tui/key= msg " ") (let [new-running (not (:running model))] [(assoc model :running new-running) (when new-running (tui/after 1000 :timer-tick))]) ;; r - reset (tui/key= msg "r") [(assoc model :seconds 10 :done false :running true) (tui/after 1000 :timer-tick)] :else [model nil])) ;; === View === (defn format-time [seconds] (let [mins (quot seconds 60) secs (mod seconds 60)] (format "%02d:%02d" mins secs))) (defn view [{:keys [seconds running done]} _size] [:col {:gap 1} [:box {:border :rounded :padding [1 2]} [:col [:text {:bold true} "Countdown Timer"] [:text ""] [:text {:fg (cond done :green (< seconds 5) :red :else :cyan) :bold true} (if done "Time's up!" (format-time seconds))] [:text ""] [:text {:fg :gray} (cond done "Press r to restart" running "Running..." :else "Paused")]]] [:text {:fg :gray} "space: pause/resume r: reset q: quit"]]) ;; === Main === (defn -main [& _args] (println "Starting timer...") (let [final-model (tui/run {:init initial-model :update update-model :view view :init-cmd (tui/after 1000 :timer-tick)})] (when (:done final-model) (println "Timer completed!"))))