(ns examples.timer "Countdown timer example - demonstrates async commands. Mirrors bubbletea's stopwatch/timer examples." (:require [tui.core :as tui] [tui.events :as ev])) ;; === Model === (def initial-model {:seconds 10 :running true :done false}) ;; === Update === (defn update-fn [{:keys [model event]}] (cond ;; Quit (or (ev/key= event \q) (ev/key= event \c #{:ctrl})) {:model model :events [(ev/quit)]} ;; Timer tick - decrement timer (= (:type event) :timer-tick) (if (:running model) (let [new-seconds (dec (:seconds model))] (if (<= new-seconds 0) ;; Timer done {:model (assoc model :seconds 0 :done true :running false)} ;; Continue countdown {:model (assoc model :seconds new-seconds) :events [(ev/delayed-event 1000 {:type :timer-tick})]})) {:model model}) ;; Space - pause/resume (ev/key= event \space) (let [new-running (not (:running model))] {:model (assoc model :running new-running) :events (when new-running [(ev/delayed-event 1000 {:type :timer-tick})])}) ;; r - reset (ev/key= event \r) {:model (assoc model :seconds 10 :done false :running true) :events [(ev/delayed-event 1000 {:type :timer-tick})]} :else {:model model})) ;; === 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]}] [: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-fn :view view :init-events [(ev/delayed-event 1000 {:type :timer-tick})]})] (when (:done final-model) (println "Timer completed!"))))