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"}}} 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" diff --git a/src/nextjournal/clerk/render.cljs b/src/nextjournal/clerk/render.cljs index a5cead563..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)) @@ -622,6 +630,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 +661,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 +783,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 950850fc9..c19c0c975 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])) @@ -186,12 +187,15 @@ sci.configs.reagent/namespaces)}) (defn ^:export onmessage [ws-msg] - (render/dispatch (read-string (.-data ws-msg)))) + (-> (render/await-render-fns (read-string (.-data ws-msg))) + (.then #(render/dispatch %)))) (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) +(defn ^:export init [state] + (-> (render/await-render-fns state) + (.then #(render/init %)))) (defn ^:export ssr [state-str] (init (read-string state-str))