libify v0.0.0

This commit is contained in:
Adam Jeniski 2025-07-18 18:22:12 -09:00
parent 941f668eff
commit bd6bd11519
2 changed files with 157 additions and 167 deletions

View File

@ -1,78 +1,19 @@
(ns example.core
(:require
[clojure.java.io :as io]
[clojure.string :as string]
[dev.onionpancakes.chassis.compiler :as hc]
[dev.onionpancakes.chassis.core :as h]
[example.utils :as u]
[reitit.ring.middleware.parameters :as rmparams]
[example.utils :refer [html->str defpage defaction defaction-async] :as u]
[reitit.ring :as rr]
[ring.util.response :as ruresp]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr]
[clojure.data.json :as json]))
[ring.util.response :as ruresp]))
(declare conns add-elements! sse-navigate!)
;; global utils
(defn tap! [x]
(println "tap:" x)
x)
;; html utils
(defn html->str [hiccup-forms]
(h/html
(hc/compile
hiccup-forms)))
(defn html-template [content]
(-> (io/resource "public/index.html")
slurp
(string/split-lines)
(->> (drop 3)
(apply str))
(str "<div data-on-load=\"@get('connect')\"></div>")
(string/replace "%%content%%" content)))
;; fix. use replaceState api? or push some real state? back button doesn't work yet
(defn update-url-frag [url]
(html->str [:script (format "history.pushState({page:1}, 'Title', '%s')"
url)]))
(defn sse-page [request respond view url]
(respond
(->sse-response request
{on-open
(fn [sse]
(d*/with-open-sse sse
(sse-navigate! sse (view {}))
(add-elements! sse (update-url-frag url))))})))
(defn text-html-page [request respond view]
(-> (html-template (view {}))
ruresp/response
(ruresp/content-type "text/html")
respond))
(defn page [request respond url view]
(if (-> request
:headers
(get "datastar-request")
(= "true"))
(sse-page request respond view url)
(text-html-page request respond view)
))
;; views
(def home-view
(fn [_]
(defn home-view [_]
(html->str
[:main
[:div
[:input {:data-bind "msg"}]
[:div {:data-text "$msg"}]
[:button {:data-on-click "@get('/hello-world')"} "click for text animation"]]
[:button {:data-on-click "@get('/page2')"} "go to page 2"]])))
[:button {:data-on-click "@get('/page2')"} "go to page 2"]]))
(defn page2-view [_]
@ -81,72 +22,6 @@
[:p "this is page2"]
[:button {:data-on-click "@get('/')"} "go back to home"]]))
;; d* api utils
(defn patch-signals-edn! [sse edn]
(d*/patch-signals! sse (json/write-str edn)))
(defn add-elements! [sse elems]
(d*/patch-elements! sse elems #:d*.elements{:patch-mode "append"
:selector "body"}))
(defn sse-navigate! [sse elems]
(d*/patch-elements! sse elems #:d*.elements{:patch-mode "replace"
:selector "main"}))
(defn try!
[d*-f! sse & args]
(try (apply d*-f! sse args)
(catch Exception e
(println "exception occured. dropping connection. error:" e)
(d*/close-sse! sse)
(swap! conns disj sse))))
;; broadcast utils
(def conns (atom #{}))
(defn broadcast!
([f]
(doseq [conn @conns]
(try! f conn)))
([f arg & args]
(doseq [conn @conns]
(apply try! f conn arg args))))
(defn connect-sse! [sse]
(swap! conns conj sse)
(println "adding connection"))
(defn disconnect-sse! [sse]
(swap! conns disj sse)
(println "dropping connection"))
(defn broadcast-signals! [data]
(broadcast! patch-signals-edn! data))
(defn broadcast-js! [js-src]
(broadcast! add-elements! (html->str [:script js-src])))
(defn broadcast-reload! []
(broadcast-js! "location.reload()"))
(defn kill-broadcast! []
(doseq [conn @conns]
(try! d*/close-sse! conn)
(swap! conns disj conn)))
(defn defaction-async [url on-open-f on-close-f]
(let [sse-handler
(fn [request respond raise_]
(respond
(->sse-response
request
{on-open on-open-f
on-close on-close-f})))]
[[url sse-handler]
[(str "/sse" url) sse-handler]]))
;; hello world animation
(def message "Hello, world!")
@ -154,34 +29,15 @@
(defn hello-world [sse]
(dotimes [i msg-count]
(patch-signals-edn! sse {:msg (subs message 0 (inc i))})
(u/patch-signals-edn! sse {:msg (subs message 0 (inc i))})
(Thread/sleep 500)))
;; http stuffs
(defn defaction [url f]
(let [sse-handler
(fn [request respond raise_]
(respond
(->sse-response
request
{on-open #(d*/with-open-sse % (f %))})))]
[[url sse-handler]
[(str "/sse" url) sse-handler]]))
(defn defpage [endpoint view]
[[endpoint
{:handler (fn [request respond _]
(page request respond endpoint view))}]
[(str "/sse" endpoint)
{:handler (fn [request respond _]
(page request respond endpoint view))}]])
(def routes
(concat
(defpage "/" home-view)
(defpage "/page2" page2-view)
(defaction "/hello-world" hello-world)
(defaction-async "/connect" connect-sse! disconnect-sse!)))
(defaction-async "/connect" u/connect-sse! u/disconnect-sse!)))
(def router (rr/router routes))
@ -190,11 +46,11 @@
;; repl it up ;P
(comment
(broadcast-signals! {:msg "hi franz"})
(broadcast-signals! {:msg "hi ty"})
(broadcast! d*/console-log! "hi franz")
(broadcast-js! "console.log('test')")
(broadcast-reload!)
(u/broadcast-signals! {:msg "hi franz"})
(u/broadcast-signals! {:msg "hi ty"})
(u/broadcast! d*/console-log! "hi franz")
(u/broadcast-js! "console.log('test')")
(u/broadcast-reload!)
(kill-broadcast!)

View File

@ -1,13 +1,147 @@
(ns example.utils
(:require
[charred.api :as charred]
[starfederation.datastar.clojure.api :as d*]))
[clojure.java.io :as io]
[clojure.string :as string]
[dev.onionpancakes.chassis.compiler :as hc]
[dev.onionpancakes.chassis.core :as h]
[reitit.ring.middleware.parameters :as rmparams]
[reitit.ring :as rr]
[ring.util.response :as ruresp]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr]
[clojure.data.json :as json]))
(declare conns add-elements! sse-navigate!)
;; global utils
(defn tap! [x]
(println "tap:" x)
x)
;; html utils
(defn html->str [hiccup-forms]
(h/html
(hc/compile
hiccup-forms)))
(defn html-template [content]
(-> (io/resource "public/index.html")
slurp
(string/split-lines)
(->> (drop 3)
(apply str))
(str "<div data-on-load=\"@get('connect')\"></div>")
(string/replace "%%content%%" content)))
;; fix. use replaceState api? or push some real state? back button doesn't work yet
(defn update-url-frag [url]
(html->str [:script (format "history.pushState({page:1}, 'Title', '%s')"
url)]))
(defn sse-page [request respond view url]
(respond
(->sse-response request
{on-open
(fn [sse]
(d*/with-open-sse sse
(sse-navigate! sse (view {}))
(add-elements! sse (update-url-frag url))))})))
(defn text-html-page [request respond view]
(-> (html-template (view {}))
ruresp/response
(ruresp/content-type "text/html")
respond))
(defn page [request respond url view]
(if (-> request
:headers
(get "datastar-request")
(= "true"))
(sse-page request respond view url)
(text-html-page request respond view)))
(def ^:private bufSize 1024)
(def read-json (charred/parse-json-fn {:async? false :bufsize bufSize}))
;; d* api utils
(defn patch-signals-edn! [sse edn]
(d*/patch-signals! sse (json/write-str edn)))
(defn get-signals [req]
(-> req d*/get-signals read-json))
(defn add-elements! [sse elems]
(d*/patch-elements! sse elems #:d*.elements{:patch-mode "append"
:selector "body"}))
(defn sse-navigate! [sse elems]
(d*/patch-elements! sse elems #:d*.elements{:patch-mode "replace"
:selector "main"}))
(defn defaction-async [url on-open-f on-close-f]
(let [sse-handler
(fn [request respond raise_]
(respond
(->sse-response
request
{on-open on-open-f
on-close on-close-f})))]
[[url sse-handler]
[(str "/sse" url) sse-handler]]))
;; broadcast utils
(defn try!
[d*-f! sse & args]
(try (apply d*-f! sse args)
(catch Exception e
(println "exception occured. dropping connection. error:" e)
(d*/close-sse! sse)
(swap! conns disj sse))))
(defonce conns (atom #{}))
(defn broadcast!
([f]
(doseq [conn @conns]
(try! f conn)))
([f arg & args]
(doseq [conn @conns]
(apply try! f conn arg args))))
(defn connect-sse! [sse]
(swap! conns conj sse)
(println "adding connection"))
(defn disconnect-sse! [sse]
(swap! conns disj sse)
(println "dropping connection"))
(defn broadcast-signals! [data]
(broadcast! patch-signals-edn! data))
(defn broadcast-js! [js-src]
(broadcast! add-elements! (html->str [:script js-src])))
(defn broadcast-reload! []
(broadcast-js! "location.reload()"))
(defn kill-broadcast! []
(doseq [conn @conns]
(try! d*/close-sse! conn)
(swap! conns disj conn)))
;; http stuffs
(defn defaction [url f]
(let [sse-handler
(fn [request respond raise_]
(respond
(->sse-response
request
{on-open #(d*/with-open-sse % (f %))})))]
[[url sse-handler]
[(str "/sse" url) sse-handler]]))
(defn defpage [endpoint view]
[[endpoint
{:handler (fn [request respond _]
(page request respond endpoint view))}]
[(str "/sse" endpoint)
{:handler (fn [request respond _]
(page request respond endpoint view))}]])