site.clj (8382B)
1 (ns site 2 (:require [hiccup2.core :as h] 3 [clojure.java.io :as io] 4 [clojure.string :as cstr] 5 [core])) 6 7 (def domain "massi.world") 8 (def pages {}) 9 10 (defn getspec [key-or-spec] 11 (if (map? key-or-spec) 12 key-or-spec 13 (if (nil? (pages key-or-spec)) 14 (throw (Exception. (str "ensure check failed! page " key-or-spec " not found."))) 15 (pages key-or-spec)))) 16 17 (def p (core/mk-path-fns domain getspec)) 18 19 (defn test-path [pgkey] 20 (println) 21 (println (str "paths for " pgkey ":\n")) 22 23 (letfn [(mypp [obj] 24 (doseq [[k v] obj] 25 (println (str " " k)) 26 (println (str " " v))))] 27 (println "dev:") 28 (mypp {:p (p "/is/a/path.txt" true) 29 :network-path (p :network-path pgkey true) 30 :url (p :url pgkey true) 31 :path (p :path pgkey true)}) 32 33 (println "prod:") 34 (mypp {:p (p "/is/a/path.txt") 35 :network-path (p :network-path pgkey) 36 :url (p :url pgkey) 37 :path (p :path pgkey)}) 38 39 (println "other:") 40 (mypp 41 {:outfile (p :out pgkey)}))) 42 43 (defn site-header [spec] 44 (concat 45 (list [:link {:rel "preload" :as "style" :href (p "/style.css")}] 46 [:link {:rel "stylesheet" :href (p "/style.css")}] 47 [:script {:src (p "/shared/js/the-world.js")}]) 48 (core/massi-world-domain-stuff (p :url spec) p spec) 49 (list [:link {:rel "manifest" :href (p "/manifest.json")}]))) 50 51 (defn with-head [pg-key-or-spec & body] 52 (h/html (h/raw "<!DOCTYPE html>") 53 [:html.no-js 54 [:head 55 (site-header 56 (getspec pg-key-or-spec))]] 57 [:body (seq body)])) 58 59 (defmacro defpage [key spec & render] 60 `(def pages 61 (merge pages 62 {~key (merge {:render 63 (fn [] 64 (with-head ~spec 65 ~@render))} ~spec)}))) 66 67 (defn page-identifier? [thing] 68 (or (map? thing) (keyword? thing))) 69 70 (def svg-arrow 71 [:svg {:height "1em" :width "1em" :viewBox "0 0 16 16" :id "svg-arrow"} 72 [:path {:style "fill:var(--fg)" :d "M 9.3370298,4.2576328 8.9740248,4.8872198 13.221,7.329 H 0.72416487 V 8.032 H 13.221 l -4.2469782,2.442097 0.363005,0.629587 5.9300332,-3.4230256 z"}]]) 73 74 (defn mw-anchor 75 ([pgkey-or-spec] (mw-anchor pgkey-or-spec identity)) 76 ([pgkey-or-spec transform-title] 77 [:a {:href (p :path pgkey-or-spec)} (-> pgkey-or-spec getspec :title transform-title)])) 78 79 (def the-world 80 [:span {:id "contains-the-world"} [:div {:id "the-world"}]]) 81 82 (defn header [& crumbs] 83 [:header 84 (concat 85 (list [:span {:class "header-home"} 86 (mw-anchor 87 :home 88 (fn [_] (list the-world [:span {:class "home-anchor-text"} "home"]))) 89 (when crumbs [:span {:class "crumb-interposer"} svg-arrow])]) 90 (map (fn [x] [:span x [:span {:class "crumb-interposer"} svg-arrow]]) 91 (butlast crumbs)) 92 (when (last crumbs) 93 (list [:span {:class "flex-break"}] [:span (last crumbs)])))]) 94 95 (defn footer [] 96 [:footer [:small 97 "generated " 98 (.format (java.text.SimpleDateFormat. "MM/dd/yyyy hh:mm:ssa z") 99 (new java.util.Date))]]) 100 101 (defn build-manifest [] 102 (core/prnt 1 "🤖" "writing build/massi.world/manifest.json") 103 (core/process-templ "site/manifest.templ.json" 104 "build/massi.world/manifest.json" 105 {:icon-192 (p "/shared/assets/icon-192-192.png") 106 :index-url (p :url :home)})) 107 108 (defn build-css [] 109 (core/prnt 1 "🖼" "writing build/massi.world/style.css") 110 (core/process-templ "site/style.templ.css" 111 "build/massi.world/style.css" 112 {:web-root (p "")})) 113 114 (defn render-page [pgkey] 115 (core/prnt 1 "👉" pgkey) 116 (let [out-file (p :out pgkey) 117 out-str (do (core/prnt 2 "🌀" "rendering " pgkey) 118 (str ((-> pgkey getspec :render)))) 119 out-str (if (and (core/dev?) (cstr/ends-with? out-file ".html")) 120 (do 121 (core/prnt 2 "✨" (str "tidying html...")) 122 (core/prettify-html out-str)) 123 out-str)] 124 125 (io/make-parents out-file) 126 (core/prnt 2 "✍" "writing " out-file) 127 (spit out-file out-str) 128 (core/prnt 2 "✅" "done!"))) 129 130 131 (defpage :home {:path ["index.html"] 132 :title "massi world" 133 :desc "a website"} 134 [:header {:class "home-header"} 135 (mw-anchor :home (fn [_] (list (h/raw "massi ") the-world (h/raw " world"))))] 136 [:section 137 (core/md "site/md/blurb.templ.md")] 138 [:section 139 [:ul 140 [:li [:a {:href "https://git.massi.world"} "git"]] 141 [:li (mw-anchor :projects)]]] 142 (footer)) 143 144 (defpage :projects {:path ["projects" "index.html"] 145 :title "projects" 146 :desc "a variety of programs made by massi"} 147 (header (mw-anchor :projects)) 148 [:ul 149 [:li [:p (mw-anchor :fretfret cstr/capitalize) 150 " is an android app for learning the notes on a guitar fretboard."]] 151 [:li [:p (mw-anchor :color-synth cstr/capitalize) 152 " is a modular, digital video synthesizer that runs on the web."]] 153 [:li (core/md "site/md/swatchbuckler.templ.md" 154 {:swatchbuckler-app-path (core/if-dev "http://localhost:8080/swatchbuckler.massi.world" 155 "https://swatchbuckler.massi.world")})]] 156 (footer)) 157 158 (defpage :color-synth {:path ["projects" "color-synth.html"] 159 :title "color-synth" 160 :desc "a modular digital video synthesizer on the web"} 161 (header (mw-anchor :projects) (mw-anchor :color-synth)) 162 [:main.color-synth 163 [:div.md (core/md "site/md/color-synth.templ.md" 164 {:samples [:div.samples 165 [:img {:src (p "/assets/color-synth-sample-1.webp") 166 :alt "a pixelated wobbling smiley face"}] 167 [:img {:src (p "/assets/color-synth-sample-2.webp") 168 :alt "jagged vibrant waves"}] 169 [:img {:src (p "/assets/color-synth-sample-3.webp") 170 :alt "a complicated animation. fuzzy, sharp, and checkered"}] 171 [:img {:src (p "/assets/color-synth-sample-4.webp") 172 :alt "mellow swampy waves"}]]})]] 173 (footer)) 174 175 (defpage :swatchbuckler {:path ["projects" "swatchbuckler.html"] 176 :title "swatchbuckler" 177 :desc "generate fg/bg pairs from a midtone"} 178 (header (mw-anchor :projects) (mw-anchor :swatchbuckler)) 179 [:div.md 180 (core/md "site/md/swatchbuckler.templ.md" 181 {:swatchbuckler-app-path (core/if-dev "http://localhost:8080/swatchbuckler.massi.world" 182 "https://swatchbuckler.massi.world")} 183 ;; {:rgb-gradient [:div {:id "gradient-sample-1" :class "sb-gradient"}] 184 ;; :hsl-gradient [:div {:id "gradient-sample-2" :class "sb-gradient"}] 185 ;; :hcl-gradient [:div {:id "gradient-sample-3" :class "sb-gradient"}]} 186 )] 187 [:script {:type "module" :src (p "/shared/js/swatchbuckler-project.js")}] 188 (footer)) 189 190 (defpage :fretfret {:path ["projects" "fretfret.html"] 191 :title "fretfret" 192 :desc "an Android app for learning the notes on a guitar fretboard"} 193 (header (mw-anchor :projects) 194 (list (mw-anchor :fretfret) 195 [:img {:src (p "/assets/fretfret-app-icon.webp")}])) 196 [:picture 197 [:source {:srcset (p "/assets/fretfret-scrot-dark.png") :media "(prefers-color-scheme:dark)"}] 198 [:img {:src (p "/assets/fretfret-scrot-light.png")}]] 199 [:ul 200 [:li [:a {:href "https://git.massi.world/fretfret"} "source code"]] 201 [:li [:a {:href "https://releases.massi.world/fretfret/fretfret-2024-08-05.apk"} "download apk"] [:small " 2024.08.05"]]] 202 [:div.md 203 (core/md "site/md/fretfret.templ.md")] 204 (footer)) 205 206 ;; _ arg is so we can call this with clj -X massi-world/build 207 ;; _ is expected to be a map, and clojure complains if we don't accept it 208 (defn build [_] 209 (println) 210 (core/prnt 0 "🌎" domain) 211 (time 212 (do 213 (doseq [pgkey (keys pages)] 214 (render-page pgkey)) 215 (build-manifest) 216 (build-css) 217 (print "\t 🕜\t"))) 218 (println))