(ns examples.views "Multiple views example - demonstrates state machine pattern. Mirrors bubbletea's views example." (:require [tui.core :as tui])) ;; === Model === (def initial-model {:view :menu ; :menu, :detail, :confirm :cursor 0 :items [{:name "Profile" :desc "View and edit your profile settings"} {:name "Settings" :desc "Configure application preferences"} {:name "Help" :desc "Get help and documentation"} {:name "About" :desc "About this application"}] :selected nil}) ;; === Update === (defn update-model [{:keys [view cursor items] :as model} msg] (case view ;; Menu view :menu (cond (or (tui/key= msg "q") (tui/key= msg [:ctrl \c])) [model tui/quit] (or (tui/key= msg :up) (tui/key= msg "k")) [(update model :cursor #(max 0 (dec %))) nil] (or (tui/key= msg :down) (tui/key= msg "j")) [(update model :cursor #(min (dec (count items)) (inc %))) nil] (tui/key= msg :enter) [(assoc model :view :detail :selected (nth items cursor)) nil] :else [model nil]) ;; Detail view :detail (cond (or (tui/key= msg "q") (tui/key= msg [:ctrl \c])) [(assoc model :view :confirm) nil] (or (tui/key= msg :escape) (tui/key= msg "b")) [(assoc model :view :menu :selected nil) nil] :else [model nil]) ;; Confirm quit dialog :confirm (cond (tui/key= msg "y") [model tui/quit] (or (tui/key= msg "n") (tui/key= msg :escape)) [(assoc model :view :detail) nil] :else [model nil]))) ;; === Views === (defn menu-view [{:keys [cursor items]}] [:col {:gap 1} [:box {:border :rounded :padding [0 1] :title "Main Menu"} [:col (for [[idx item] (map-indexed vector items)] (let [is-cursor (= idx cursor)] [:row {:gap 1} [:text {:fg (when is-cursor :cyan)} (if is-cursor ">" " ")] [:text {:bold is-cursor :fg (when is-cursor :cyan)} (:name item)]]))]] [:text {:fg :gray} "j/k: navigate enter: select q: quit"]]) (defn detail-view [{:keys [selected]}] [:col {:gap 1} [:box {:border :double :padding [1 2]} [:col {:gap 1} [:text {:bold true :fg :cyan} (:name selected)] [:text ""] [:text (:desc selected)]]] [:text {:fg :gray} "b/esc: back q: quit"]]) (defn confirm-view [_model] [:col {:gap 1} [:box {:border :rounded :padding [1 2]} [:col [:text {:bold true :fg :yellow} "Quit?"] [:text ""] [:text "Are you sure you want to quit?"] [:text ""] [:row {:gap 2} [:text {:fg :green} "[y] Yes"] [:text {:fg :red} "[n] No"]]]]]) (defn view [{:keys [view] :as model}] (case view :menu (menu-view model) :detail (detail-view model) :confirm (confirm-view model))) ;; === Main === (defn -main [& _args] (println "Starting views demo...") (tui/run {:init initial-model :update update-model :view view}) (println "Views demo finished."))