massi-world

this website of mine
Log | Files | Refs

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&nbsp;") the-world (h/raw "&nbsp;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))