|
3 | 3 | :quarto {:author :timothypratley |
4 | 4 | :description "A lightweight way to cook with Clay and Scittle" |
5 | 5 | :type :post |
6 | | - :draft true |
7 | 6 | :date "2025-08-13" |
8 | 7 | :category :clay |
9 | 8 | :tags [:clay :workflow :scittle :reagent]}}} |
|
33 | 32 | ;; Clay lets us interleave plain Hiccup with Reagent/Scittle components: |
34 | 33 |
|
35 | 34 | (kind/hiccup |
36 | | - [:div [:strong "Hello from Hiccup"] |
| 35 | + [:div [:strong "Hello from Hiccup (Clojure)"] |
37 | 36 | ['(fn [] |
38 | | - [:em "Hello from Scittle/Reagent"])]]) |
| 37 | + [:em "Hello from Scittle/Reagent (ClojureScript)"])]]) |
39 | 38 |
|
40 | 39 | ;; But because Clay serves HTML, re-evaluating this namespace normally reloads the whole page. |
41 | 40 | ;; Wouldn’t it be great to get a Figwheel/shadow-cljs style experience where code updates in place? |
|
96 | 95 | ;; ## A Taste Test |
97 | 96 |
|
98 | 97 | ;; Let's walk through a small example to see hot reload in action. |
99 | | -;; I recommend copying these snippets into the `uncompiled.cljs` and saving as you go. |
| 98 | +;; I recommend copying these snippets into `uncompiled2.cljs` and saving as you go. |
100 | 99 | ;; First, we'll set up our ingredients (app state): |
101 | 100 |
|
102 | 101 | ;; ```clojure |
103 | | -;; (ns my-app.core |
104 | | -;; (:require [reagent.core :as r])) |
| 102 | +;; (ns scicloj.clay.uncompiled2 |
| 103 | +;; (:require [reagent.core :as r] |
| 104 | +;; [reagent.dom :as rdom])) |
105 | 105 | ;; |
106 | 106 | ;; (defonce app-state ; <- defonce preserves state during reload |
107 | 107 | ;; (r/atom {:style "scrambled" |
|
116 | 116 | ;; [:h3 "Breakfast Order"] |
117 | 117 | ;; [:p "Style: " (:style @app-state)] |
118 | 118 | ;; [:p "Eggs: " (:eggs @app-state)]]) |
| 119 | +;; |
| 120 | +;; (rdom/render |
| 121 | +;; [breakfast-order] |
| 122 | +;; (js/document.getElementById "app2")) |
119 | 123 | ;; ``` |
120 | 124 |
|
121 | | -;; We can mount our new component on the `app` div. |
| 125 | +;; We want to be able to change cooking styles. |
122 | 126 |
|
123 | 127 | ;; ```clojure |
124 | | -;; (rdom/render |
125 | | -;; [breakfast-order] |
126 | | -;; (js/document.getElementById "app")) |
| 128 | +;; (defn cycle-style [] |
| 129 | +;; (swap! app-state update :style |
| 130 | +;; {"scrambled" "fried" |
| 131 | +;; "fried" "poached" |
| 132 | +;; "poached" "scrambled"})) |
| 133 | +;; ``` |
| 134 | + |
| 135 | +;; And while we're at it, let's add controls for the number of eggs: |
| 136 | + |
| 137 | +;; ```clojure |
| 138 | +;; (defn add-egg [] |
| 139 | +;; (swap! app-state update :eggs inc)) |
| 140 | + |
| 141 | +;; (defn remove-egg [] |
| 142 | +;; (swap! app-state update :eggs #(max 1 (dec %)))) |
127 | 143 | ;; ``` |
128 | 144 |
|
129 | | -;; Just like our opening story, imagine the order changes mid-cook. |
130 | | -;; We can update our component without losing the current state: |
| 145 | +;; Now we can update our component to use these functions. |
131 | 146 |
|
132 | 147 | ;; ```clojure |
133 | 148 | ;; (defn breakfast-order [] |
134 | 149 | ;; [:div |
135 | 150 | ;; [:h3 "Breakfast Order"] |
136 | | -;; [:p "Style: " [:em (:style @app-state)]] ; <- added emphasis |
137 | | -;; [:p "Eggs: " (:eggs @app-state)] |
138 | | -;; [:small "Water added for poaching..."]]) ; <- new status line |
| 151 | +;; [:div |
| 152 | +;; [:p "Style: " [:strong (:style @app-state)]] |
| 153 | +;; [:button {:onClick cycle-style} "Change Style"]] |
| 154 | +;; [:div |
| 155 | +;; [:p "Eggs: " (:eggs @app-state)] |
| 156 | +;; [:button {:onClick remove-egg |
| 157 | +;; :disabled (= 1 (:eggs @app-state))} "−"] |
| 158 | +;; [:button {:onClick add-egg} "+"]] |
| 159 | +;; [:div |
| 160 | +;; (case (:style @app-state) |
| 161 | +;; "poached" "Water added for poaching..." |
| 162 | +;; "fried" "Turn n burn!" |
| 163 | +;; "scrambled" "Whisking away...")]]) |
139 | 164 | ;; ``` |
140 | 165 |
|
141 | | -;; Save the file, and voilà! The display updates while preserving the |
142 | | -;; existing order details in app-state. No need to start over or lose |
143 | | -;; your place in the cooking process. |
| 166 | +;; If you are running this locally, |
| 167 | +;; notice how we keep the same state while adding new features. |
| 168 | +;; Each time we save the ClojureScript file, |
| 169 | +;; our app updates but keeps its current state. |
| 170 | + |
| 171 | +;; Here's the final result of our cooking: |
| 172 | + |
| 173 | +(kind/hiccup |
| 174 | + [:div#app2 |
| 175 | + [:script {:type "application/x-scittle" |
| 176 | + :src "uncompiled2.cljs"}]]) |
| 177 | + |
| 178 | +;; We replaced functions on the fly while our state lives on. |
| 179 | +;; The new code creates a fresh experience without losing where we were. |
| 180 | +;; Just like adding water to the pan. |
144 | 181 |
|
145 | 182 | ;; ## Why Go Light? |
146 | 183 |
|
147 | 184 | ;; A Michelin-star kitchen is a marvel of efficiency. |
148 | 185 | ;; Sous chefs prepping ingredients, |
149 | 186 | ;; line cooks at their stations, everything precisely mise en place. |
150 | | -;; That's your typical ClojureScript setup: build tools, asset compilation, |
151 | | -;; development servers, and careful configuration. |
| 187 | +;; That's your typical ClojureScript setup. |
| 188 | +;; Build tools, asset compilation, development servers, and careful configuration. |
152 | 189 | ;; |
153 | 190 | ;; But sometimes you just want to slap some cold cuts and cheese in a sandwich and start munching. |
154 | | -;; That's Scittle: quick, simple, and satisfying. |
| 191 | +;; That's Scittle. Quick, simple, and satisfying. |
155 | 192 | ;; No waiting for the kitchen to warm up, no cleanup afterward. |
156 | | -;; Just write some code and see it work. |
| 193 | +;; Write some code and see it work. |
| 194 | + |
| 195 | +;;  |
157 | 196 |
|
158 | 197 | ;; ## Chef's Notes |
159 | 198 |
|
160 | 199 | ;; Clay blends the traditional with the experimental. |
161 | 200 | ;; Write a namespace, get a webpage. |
162 | 201 | ;; Add some Scittle, get interactive components. |
163 | 202 |
|
164 | | -;; The magic happens in how Clay handles changes: |
| 203 | +;; Magic happens when Clay handles changes: |
165 | 204 |
|
166 | | -;; - For your narrative and page structure in Clojure live reload refreshes the whole view |
167 | | -;; - For your Scittle components hot reload updates just the code, keeping state alive |
| 205 | +;; - For your narrative and page structure in Clojure, live reload refreshes the whole page |
| 206 | +;; - For your Scittle components, hot reload updates only the code, keeping state alive |
| 207 | + |
| 208 | +;; This is especially sweet when you're cooking up mini-games with MatterJS. |
| 209 | +;; Tweak physics parameters or game logic, and watch them take effect as soon as you save. |
168 | 210 |
|
169 | | -;; This is especially sweet when you're cooking up interactive elements like mini-games |
170 | | -;; with matter.js. Tweak physics parameters or game logic, and watch them take effect |
171 | | -;; instantly. Your game state, scores, and position all preserved. |
172 | | -;; |
173 | 211 | ;; Remember how we started with those poached eggs? |
174 | 212 | ;; Maybe they were destined for a sandwich all along. |
175 | | -;; That's the beauty of this approach: start simple, |
176 | | -;; stay flexible, and build exactly what you need. |
177 | | -;; Clay lets you shape your story with markdown, spice it up with interactive widgets, |
| 213 | +;; That's the beauty of this approach. |
| 214 | +;; Start simple, stay flexible, and build what you need. |
| 215 | +;; Clay lets you shape your story with markdown, |
| 216 | +;; spice it up with interactive widgets, |
178 | 217 | ;; and adjust the ingredients. |
179 | 218 | ;; That's the kind of flow that keeps creative coding delicious. |
0 commit comments