|
| 1 | +^:kindly/hide-code |
| 2 | +^{:clay {:title "The Sandwich Approach to ClojureScript Development" |
| 3 | + :quarto {:author :timothypratley |
| 4 | + :description "A lightweight way to cook with Clay and Scittle" |
| 5 | + :type :post |
| 6 | + :draft true |
| 7 | + :date "2025-08-13" |
| 8 | + :category :clay |
| 9 | + :tags [:clay :workflow :scittle :reagent]}}} |
| 10 | +(ns scicloj.clay.uncompiled-clojurescript |
| 11 | + (:require [scicloj.kindly.v4.kind :as kind])) |
| 12 | + |
| 13 | +;; Imagine you're making breakfast for your significant other one morning, |
| 14 | +;; you just cracked the eggs into a frypan and they call out "poached please". |
| 15 | +;; What do you do? |
| 16 | +;; No need to start over, just add some water. |
| 17 | +;; That's the spirit of hot reload; keep your state and adjust the functions. |
| 18 | + |
| 19 | +;; This kind of fluid adaptation is exactly what we want in our development workflow. |
| 20 | +;; Let's see how to cook up that experience with two key ingredients. |
| 21 | + |
| 22 | +;; ## The Main Ingredient: Scittle |
| 23 | + |
| 24 | +;; [Scittle](https://github.com/babashka/scittle) by the amazing [borkdude](https://github.com/borkdude) is a ClojureScript interpreter that runs in the browser. |
| 25 | +;; No build step, no config, just static files. That makes it perfect to include in Clay notebooks like this one. |
| 26 | + |
| 27 | +;; When you're hungry, do you always cook a gourmet meal? |
| 28 | +;; Sometimes you just want to make a quick sandwich—grab what you need, |
| 29 | +;; slap it together, and start eating. No prep work, no cleanup. |
| 30 | +;; That's Scittle: quick, simple, satisfying ClojureScript without the ceremony. |
| 31 | +;; Sometimes you don't need the full kitchen setup with build tools and configurations. |
| 32 | + |
| 33 | +;; Clay lets us interleave plain Hiccup with Reagent/Scittle components: |
| 34 | + |
| 35 | +(kind/hiccup |
| 36 | + [:div [:strong "Hello from Hiccup"] |
| 37 | + ['(fn [] |
| 38 | + [:em "Hello from Scittle/Reagent"])]]) |
| 39 | + |
| 40 | +;; But because Clay serves HTML, re-evaluating this namespace normally reloads the whole page. |
| 41 | +;; Wouldn’t it be great to get a Figwheel/shadow-cljs style experience where code updates in place? |
| 42 | + |
| 43 | +;; ::: {.callout-note} |
| 44 | +;; Hot Reloading injects only the modified code into the running application without restarting it entirely. |
| 45 | +;; This allows you to preserve state and treat the browser like a REPL. |
| 46 | +;; ::: |
| 47 | + |
| 48 | +;; ## The Inspiration: Hot Reload Scittle |
| 49 | + |
| 50 | +;; [Chris McCormick](https://github.com/chr15m) showed us something clever |
| 51 | +;; with [cljs-josh](https://github.com/chr15m/cljs-josh): Scittle code can be hot-reloaded. Here's his demo: |
| 52 | + |
| 53 | +^:kind/video ^:kindly/hide-code |
| 54 | +{:youtube-id "4tbjE0_W-58" |
| 55 | + :iframe-width "100%"} |
| 56 | + |
| 57 | +;; We'll take that idea but do it the Clay way, |
| 58 | +;; using Clay's live reload to manage our pages. |
| 59 | + |
| 60 | +;; ## Clay Can Hot Reload Scittle |
| 61 | + |
| 62 | +;; Here’s the code in our ClojureScript file: |
| 63 | + |
| 64 | +;; **uncompiled.cljs**: |
| 65 | +^:kindly/hide-code |
| 66 | +(kind/code (slurp "src/scicloj/clay/uncompiled.cljs")) |
| 67 | + |
| 68 | +;; We need a target div for `uncompiled.cljs` to render into, and we load it via Scittle: |
| 69 | + |
| 70 | +(kind/hiccup |
| 71 | + [:div#app |
| 72 | + [:script {:type "application/x-scittle" |
| 73 | + :src "uncompiled.cljs"}]]) |
| 74 | + |
| 75 | +;; ## Turn Up the Heat |
| 76 | + |
| 77 | +;; Time to get cooking! Clay needs `:live-reload true` to simmer. |
| 78 | +;; You can fire it up from the command line, via [editor integration](https://scicloj.github.io/clay/#setup), |
| 79 | +;; or drop this in your REPL: |
| 80 | + |
| 81 | +;; ```clojure |
| 82 | +;; (require '[scicloj.clay.v2.snippets :as snippets]) |
| 83 | +;; (snippets/watch! {}) |
| 84 | +;; ``` |
| 85 | + |
| 86 | +;; Now whenever you save a `.cljs` file, the change is injected into the current Clay page without a full reload. |
| 87 | + |
| 88 | +;; If you prefer editor integration, bind a key to call |
| 89 | +;; `scicloj.clay.v2.server/scittle-eval-string!` on the current form. |
| 90 | + |
| 91 | +;; ::: {.callout-tip} |
| 92 | +;; To take full advantage of hot reload with Reagent, use `defonce` for app state. |
| 93 | +;; This preserves atoms/ratoms across code swaps. |
| 94 | +;; ::: |
| 95 | + |
| 96 | +;; ## A Taste Test |
| 97 | + |
| 98 | +;; 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. |
| 100 | +;; First, we'll set up our ingredients (app state): |
| 101 | + |
| 102 | +;; ```clojure |
| 103 | +;; (ns my-app.core |
| 104 | +;; (:require [reagent.core :as r])) |
| 105 | +;; |
| 106 | +;; (defonce app-state ; <- defonce preserves state during reload |
| 107 | +;; (r/atom {:style "scrambled" |
| 108 | +;; :eggs 2})) |
| 109 | +;; ``` |
| 110 | + |
| 111 | +;; Next, we'll create a simple component to display our breakfast order: |
| 112 | + |
| 113 | +;; ```clojure |
| 114 | +;; (defn breakfast-order [] |
| 115 | +;; [:div |
| 116 | +;; [:h3 "Breakfast Order"] |
| 117 | +;; [:p "Style: " (:style @app-state)] |
| 118 | +;; [:p "Eggs: " (:eggs @app-state)]]) |
| 119 | +;; ``` |
| 120 | + |
| 121 | +;; We can mount our new component on the `app` div. |
| 122 | + |
| 123 | +;; ```clojure |
| 124 | +;; (rdom/render |
| 125 | +;; [breakfast-order] |
| 126 | +;; (js/document.getElementById "app")) |
| 127 | +;; ``` |
| 128 | + |
| 129 | +;; Just like our opening story, imagine the order changes mid-cook. |
| 130 | +;; We can update our component without losing the current state: |
| 131 | + |
| 132 | +;; ```clojure |
| 133 | +;; (defn breakfast-order [] |
| 134 | +;; [:div |
| 135 | +;; [: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 |
| 139 | +;; ``` |
| 140 | + |
| 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. |
| 144 | + |
| 145 | +;; ## Why Go Light? |
| 146 | + |
| 147 | +;; A Michelin-star kitchen is a marvel of efficiency. |
| 148 | +;; Sous chefs prepping ingredients, |
| 149 | +;; 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. |
| 152 | +;; |
| 153 | +;; 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. |
| 155 | +;; No waiting for the kitchen to warm up, no cleanup afterward. |
| 156 | +;; Just write some code and see it work. |
| 157 | + |
| 158 | +;; ## Chef's Notes |
| 159 | + |
| 160 | +;; Clay blends the traditional with the experimental. |
| 161 | +;; Write a namespace, get a webpage. |
| 162 | +;; Add some Scittle, get interactive components. |
| 163 | + |
| 164 | +;; The magic happens in how Clay handles changes: |
| 165 | + |
| 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 |
| 168 | + |
| 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 | +;; Remember how we started with those poached eggs? |
| 174 | +;; 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, |
| 178 | +;; and adjust the ingredients. |
| 179 | +;; That's the kind of flow that keeps creative coding delicious. |
0 commit comments