From 55a0b03546a2396b60fcf70bb2460222b7253d12 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Tue, 26 Sep 2023 13:28:55 +0200 Subject: [PATCH 1/7] Bump sci --- deps.edn | 2 +- render/deps.edn | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deps.edn b/deps.edn index f3de62143..b8c3bb698 100644 --- a/deps.edn +++ b/deps.edn @@ -68,7 +68,7 @@ org.xerial/sqlite-jdbc {:mvn/version "3.34.0"} org.clojure/data.csv {:mvn/version "1.0.0"} hickory/hickory {:mvn/version "0.7.1"} - sicmutils/sicmutils {:mvn/version "0.20.0"} + sicmutils/sicmutils {:mvn/version "0.20.0" :exclusions [org.babashka/sci]} io.github.mentat-collective/emmy {:git/sha "b98fef51d80d3edcff3100562b929f9980ff34d7" :exclusions [org.babashka/sci]} io.github.nextjournal/clerk-slideshow {:git/sha "11a83fea564da04b9d17734f2031a4921d917893"}}} diff --git a/render/deps.edn b/render/deps.edn index 2b8f456b5..58a8dc84b 100644 --- a/render/deps.edn +++ b/render/deps.edn @@ -2,10 +2,12 @@ :deps {applied-science/js-interop {:mvn/version "0.3.3"} binaryage/devtools {:mvn/version "1.0.3"} cider/cider-nrepl {:mvn/version "0.28.3"} - org.babashka/sci {:mvn/version "0.7.39"} + org.babashka/sci {:git/url "https://github.com/babashka/sci" + :git/sha "6d8f440610f934bf4dee3502d52f997540c08c9f"} reagent/reagent {:mvn/version "1.2.0"} io.github.babashka/sci.configs {:git/sha "0702ea5a21ad92e6d7cca6d36de84271083ea68f"} io.github.nextjournal/clojure-mode {:git/sha "1f55406087814a0dda6806396aa596dbe13ea302"} thheller/shadow-cljs {:mvn/version "2.23.1"} io.github.squint-cljs/cherry {;; :local/root "/Users/borkdude/dev/cherry" + :exclusions [org.babashka/sci] :git/sha "ac89d93f136ee8fab91f62949de5b5822ba08b3c"}}} From deff518998fda065b3b3b295fc60b5b907baf852 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Wed, 27 Sep 2023 08:58:40 +0200 Subject: [PATCH 2/7] Use sci.async/eval-form --- src/nextjournal/clerk/render.cljs | 10 +++++++--- src/nextjournal/clerk/sci_env.cljs | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/nextjournal/clerk/render.cljs b/src/nextjournal/clerk/render.cljs index a5cead563..6abb0cf42 100644 --- a/src/nextjournal/clerk/render.cljs +++ b/src/nextjournal/clerk/render.cljs @@ -520,6 +520,10 @@ (defn valid-react-element? [x] (react/isValidElement x)) +(defn render-p [render-fn-promise value opts] + (when-let [render-fn (hooks/use-promise render-fn-promise)] + [render-fn value opts])) + (defn inspect-presented ([x] (r/with-let [!expanded-at (r/atom (:nextjournal/expanded-at x))] @@ -531,9 +535,9 @@ #_(prn :inspect-presented value :valid-element? (react/isValidElement value) :viewer viewer) ;; each view function must be called in its own 'functional component' so that it gets its own hook state. ^{:key (str (:hash viewer) "@" (peek (:path opts)))} - [(:render-fn viewer) value (merge opts - (:nextjournal/render-opts x) - {:viewer viewer :path path})])))) + [render-p (:f (:render-fn viewer)) value (merge opts + (:nextjournal/render-opts x) + {:viewer viewer :path path})])))) (defn inspect [value] (r/with-let [!state (r/atom nil)] diff --git a/src/nextjournal/clerk/sci_env.cljs b/src/nextjournal/clerk/sci_env.cljs index 950850fc9..99a77862c 100644 --- a/src/nextjournal/clerk/sci_env.cljs +++ b/src/nextjournal/clerk/sci_env.cljs @@ -32,6 +32,7 @@ [sci.configs.applied-science.js-interop :as sci.configs.js-interop] [sci.configs.reagent.reagent :as sci.configs.reagent] [sci.core :as sci] + [sci.async :as sci-async] [sci.ctx-store] [shadow.esm])) @@ -189,7 +190,7 @@ (render/dispatch (read-string (.-data ws-msg)))) (defn ^:export eval-form [f] - (sci/eval-form (sci.ctx-store/get-ctx) f)) + (sci-async/eval-form (sci.ctx-store/get-ctx) f)) (def ^:export init render/init) From 5651213dc8c7ee7b1c8971aec77e09b26da5a2ad Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Wed, 27 Sep 2023 09:56:45 +0200 Subject: [PATCH 3/7] Await render-fns initially to avoid flickering --- src/nextjournal/clerk/render.cljs | 10 +++------- src/nextjournal/clerk/sci_env.cljs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/nextjournal/clerk/render.cljs b/src/nextjournal/clerk/render.cljs index 6abb0cf42..a5cead563 100644 --- a/src/nextjournal/clerk/render.cljs +++ b/src/nextjournal/clerk/render.cljs @@ -520,10 +520,6 @@ (defn valid-react-element? [x] (react/isValidElement x)) -(defn render-p [render-fn-promise value opts] - (when-let [render-fn (hooks/use-promise render-fn-promise)] - [render-fn value opts])) - (defn inspect-presented ([x] (r/with-let [!expanded-at (r/atom (:nextjournal/expanded-at x))] @@ -535,9 +531,9 @@ #_(prn :inspect-presented value :valid-element? (react/isValidElement value) :viewer viewer) ;; each view function must be called in its own 'functional component' so that it gets its own hook state. ^{:key (str (:hash viewer) "@" (peek (:path opts)))} - [render-p (:f (:render-fn viewer)) value (merge opts - (:nextjournal/render-opts x) - {:viewer viewer :path path})])))) + [(:render-fn viewer) value (merge opts + (:nextjournal/render-opts x) + {:viewer viewer :path path})])))) (defn inspect [value] (r/with-let [!state (r/atom nil)] diff --git a/src/nextjournal/clerk/sci_env.cljs b/src/nextjournal/clerk/sci_env.cljs index 99a77862c..6f019e89e 100644 --- a/src/nextjournal/clerk/sci_env.cljs +++ b/src/nextjournal/clerk/sci_env.cljs @@ -186,13 +186,24 @@ sci.configs.js-interop/namespaces sci.configs.reagent/namespaces)}) +(defn await-render-fns [x] + (let [viewer-fns (set (filter viewer/viewer-fn? (tree-seq coll? seq x))) + !viewer-fns->resolved (atom {})] + (doseq [viewer-fn viewer-fns] + (.then (:f viewer-fn) + #(swap! !viewer-fns->resolved assoc viewer-fn (assoc viewer-fn :f %)))) + (-> (js/Promise.allSettled (into-array (map :f viewer-fns))) + (.then #(clojure.walk/postwalk-replace @!viewer-fns->resolved x))))) + (defn ^:export onmessage [ws-msg] (render/dispatch (read-string (.-data ws-msg)))) (defn ^:export eval-form [f] (sci-async/eval-form (sci.ctx-store/get-ctx) f)) -(def ^:export init render/init) +(defn ^:export init [state] + (-> (await-render-fns state) + (.then #(render/init %)))) (defn ^:export ssr [state-str] (init (read-string state-str)) From a537e038c9e8d46824a0aef39d826bdfc0a8f642 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Wed, 27 Sep 2023 10:40:37 +0200 Subject: [PATCH 4/7] avail-render-fns in onmessage --- src/nextjournal/clerk/sci_env.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nextjournal/clerk/sci_env.cljs b/src/nextjournal/clerk/sci_env.cljs index 6f019e89e..3f4792a4d 100644 --- a/src/nextjournal/clerk/sci_env.cljs +++ b/src/nextjournal/clerk/sci_env.cljs @@ -196,7 +196,8 @@ (.then #(clojure.walk/postwalk-replace @!viewer-fns->resolved x))))) (defn ^:export onmessage [ws-msg] - (render/dispatch (read-string (.-data ws-msg)))) + (-> (await-render-fns (read-string (.-data ws-msg))) + (.then #(render/dispatch %)))) (defn ^:export eval-form [f] (sci-async/eval-form (sci.ctx-store/get-ctx) f)) From b2174a0124f5c4f2e884bb69015915336a272c57 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Wed, 27 Sep 2023 11:22:29 +0200 Subject: [PATCH 5/7] Fix re-mount --- src/nextjournal/clerk/render.cljs | 22 +++++++++++++++++----- src/nextjournal/clerk/sci_env.cljs | 13 ++----------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/nextjournal/clerk/render.cljs b/src/nextjournal/clerk/render.cljs index a5cead563..ec5091f29 100644 --- a/src/nextjournal/clerk/render.cljs +++ b/src/nextjournal/clerk/render.cljs @@ -622,6 +622,15 @@ (defn remount? [doc-or-patch] (true? (some #(= % :nextjournal.clerk/remount) (tree-seq coll? seq doc-or-patch)))) +(defn await-render-fns [x] + (let [viewer-fns (set (filter viewer/viewer-fn? (tree-seq coll? seq x))) + !viewer-fns->resolved (atom {})] + (doseq [viewer-fn viewer-fns] + (.then (js/Promise.resolve (:f viewer-fn)) + #(swap! !viewer-fns->resolved assoc viewer-fn (assoc viewer-fn :f %)))) + (-> (js/Promise.allSettled (into-array (map #(js/Promise.resolve (:f %)) viewer-fns))) + (.then #(clojure.walk/postwalk-replace @!viewer-fns->resolved x))))) + (defn re-eval-viewer-fns [doc] (let [re-eval (fn [{:keys [form]}] (viewer/->viewer-fn form))] (w/postwalk (fn [x] (cond-> x (viewer/viewer-fn? x) re-eval)) doc))) @@ -644,9 +653,11 @@ (defn patch-state! [{:keys [patch]}] (if (remount? patch) - (do (swap! !doc #(re-eval-viewer-fns (apply-patch % patch))) - ;; TODO: figure out why it doesn't work without `js/setTimeout` - (js/setTimeout #(swap! !eval-counter inc) 10)) + (-> (await-render-fns (re-eval-viewer-fns (apply-patch @!doc patch))) + (.then #(do (reset! !doc %) + ;; TODO: figure out why it doesn't work without `js/setTimeout` + (js/setTimeout (fn [] (swap! !eval-counter inc)) 10) + %))) (swap! !doc apply-patch patch))) (defonce !pending-clerk-eval-replies @@ -764,8 +775,9 @@ (.render react-root (r/as-element [root])))) (defn ^:dev/after-load ^:after-load re-render [] - (swap! !doc re-eval-viewer-fns) - (mount)) + (-> (await-render-fns (re-eval-viewer-fns @!doc)) + (.then #(do (reset! !doc %) + (mount))))) (defn ^:export init [{:as state :keys [bundle? path->doc path->url current-path]}] (setup-router! state) diff --git a/src/nextjournal/clerk/sci_env.cljs b/src/nextjournal/clerk/sci_env.cljs index 3f4792a4d..c19c0c975 100644 --- a/src/nextjournal/clerk/sci_env.cljs +++ b/src/nextjournal/clerk/sci_env.cljs @@ -186,24 +186,15 @@ sci.configs.js-interop/namespaces sci.configs.reagent/namespaces)}) -(defn await-render-fns [x] - (let [viewer-fns (set (filter viewer/viewer-fn? (tree-seq coll? seq x))) - !viewer-fns->resolved (atom {})] - (doseq [viewer-fn viewer-fns] - (.then (:f viewer-fn) - #(swap! !viewer-fns->resolved assoc viewer-fn (assoc viewer-fn :f %)))) - (-> (js/Promise.allSettled (into-array (map :f viewer-fns))) - (.then #(clojure.walk/postwalk-replace @!viewer-fns->resolved x))))) - (defn ^:export onmessage [ws-msg] - (-> (await-render-fns (read-string (.-data ws-msg))) + (-> (render/await-render-fns (read-string (.-data ws-msg))) (.then #(render/dispatch %)))) (defn ^:export eval-form [f] (sci-async/eval-form (sci.ctx-store/get-ctx) f)) (defn ^:export init [state] - (-> (await-render-fns state) + (-> (render/await-render-fns state) (.then #(render/init %)))) (defn ^:export ssr [state-str] From 1320e8bcc3436d7b7dfb53f1b645b1af9929e689 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Wed, 27 Sep 2023 14:42:00 +0200 Subject: [PATCH 6/7] Fix inspect --- src/nextjournal/clerk/render.cljs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nextjournal/clerk/render.cljs b/src/nextjournal/clerk/render.cljs index ec5091f29..2bbfd99fc 100644 --- a/src/nextjournal/clerk/render.cljs +++ b/src/nextjournal/clerk/render.cljs @@ -535,6 +535,14 @@ (:nextjournal/render-opts x) {:viewer viewer :path path})])))) + +;; TODO: figure out if we can make `inspect` work synchronously by +;; evaluating the `:render-fn`s on init to fix the flicker +(declare await-render-fns) +(defn await+inspect-presented [x] + (when-let [desc (hooks/use-promise (await-render-fns x))] + [inspect-presented desc])) + (defn inspect [value] (r/with-let [!state (r/atom nil)] (when (not= (:value @!state ::not-found) value) @@ -546,7 +554,7 @@ (.resolve js/Promise (present-elision-fn fetch-opts))) (fn [more] (swap! !state update :desc viewer/merge-presentations more fetch-opts))))} - [inspect-presented (:desc @!state)]])) + [await+inspect-presented (:desc @!state)]])) (defn show-panel [panel-id panel] (swap! !panels assoc panel-id panel)) From 2871aa1a01be6f84843a963173d8aa89c721cd4a Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Tue, 14 Nov 2023 14:21:03 +0100 Subject: [PATCH 7/7] Try fixing ssr Co-authored-by: Michiel Borkent --- src/nextjournal/clerk/builder.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nextjournal/clerk/builder.clj b/src/nextjournal/clerk/builder.clj index 8523b5aa9..f39b5c451 100644 --- a/src/nextjournal/clerk/builder.clj +++ b/src/nextjournal/clerk/builder.clj @@ -246,7 +246,7 @@ (report-fn {:stage :ssr}) (let [{duration :time-ms :keys [result]} (eval/time-ms (sh {:in (str "import '" (resource->url "/js/viewer.js") "';" - "console.log(nextjournal.clerk.sci_env.ssr(" (pr-str (pr-str static-app-opts)) "))")} + "nextjournal.clerk.sci_env.ssr(" (pr-str (pr-str static-app-opts)) ").then(console.log)")} "node" "--abort-on-uncaught-exception" "--experimental-network-imports"