Skip to content

Move remaining inlined :render-fn forms to render.cljs #705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 86 additions & 4 deletions src/nextjournal/clerk/render.cljs
Original file line number Diff line number Diff line change
@@ -172,6 +172,14 @@
(defn render-unreadable-edn [edn]
[:span.inspected-value.whitespace-nowrap.cmt-default edn])

(def !read-string-without-tag-table
(delay (eval 'nextjournal.clerk.viewer/read-string-without-tag-table)))

(defn render-read+inspect
[x] (try [inspect @!read-string-without-tag-table]
(catch js/Error _e
(render-unreadable-edn x))))

(defn error-badge [& content]
[:div.bg-red-50.rounded-sm.text-xs.text-red-400.px-2.py-1.items-center.sans-serif.inline-flex
[:svg.h-4.w-4.text-red-400 {:xmlns "http://www.w3.org/2000/svg" :viewBox "0 0 20 20" :fill "currentColor" :aria-hidden "true"}
@@ -333,6 +341,9 @@
(get-in x [:nextjournal/render-opts :id])
(with-meta {:key (str (get-in x [:nextjournal/render-opts :id]) "@" @!eval-counter)})))))

(defn render-children [xs opts]
(into [:<>] (nextjournal.clerk.render/inspect-children opts) xs))

(def expand-style
["cursor-pointer"
"bg-indigo-50"
@@ -435,6 +446,18 @@
[:span.cmt-number.inspected-value
(if (js/Number.isNaN num) "NaN" (str num))])

(defn render-hex-number [num] (render-number (str "0x" (.toString (js/Number. num) 16))))

(defn render-map-entry [xs opts]
(into [:<>] (comp (nextjournal.clerk.render/inspect-children opts) (interpose " ")) xs))

(defn render-symbol [x] [:span.cmt-keyword.inspected-value (str x)])
(defn render-keyword [x] [:span.cmt-atom.inspected-value (str x)])
(defn render-nil [_] [:span.cmt-default.inspected-value "nil"])
(defn render-boolean [x] [:span.cmt-bool.inspected-value (str x)])
(defn render-char [c] [:span.cmt-string.inspected-value "\\" c])
(defn render-var [x] [:span.inspected-value [:span.cmt-meta "#'" (str x)]])

(defn sort! [!sort i k]
(let [{:keys [sort-key sort-order]} @!sort]
(reset! !sort {:sort-index i
@@ -526,12 +549,30 @@
[:div.rounded.border.border-red-200.border-t-0.overflow-hidden
[throwable-view ex opts]]))

(defn render-tagged-value
([tag value] (render-tagged-value {:space? true} tag value))
(defn render-tagged-value*
([tag value] (render-tagged-value* {:space? true} tag value))
([{:keys [space?]} tag value]
[:span.inspected-value.whitespace-nowrap
[:span.cmt-meta tag] (when space? nbsp) value]))


(defn render-tagged-value [{:keys [tag value space?]} opts]
[render-tagged-value
{:space? (:nextjournal/value space?)}
(str "#" (:nextjournal/value tag))
[nextjournal.clerk.render/inspect-presented value]])

(defn render-js-object [v opts]
[render-tagged-value* {:space? true}
"#js"
[nextjournal.clerk.render/render-map v opts]])

(defn render-js-array [v opts]
[render-tagged-value* {:space? true}
"#js"
[nextjournal.clerk.render/render-coll v opts]])


(defn set-viewers! [scope viewers]
#_(js/console.log :set-viewers! {:scope scope :viewers viewers})
(swap! !viewers assoc scope (vec viewers))
@@ -695,12 +736,16 @@
(let [re-eval (fn [{:keys [form]}] (viewer/->viewer-fn form))]
(w/postwalk (fn [x] (cond-> x (viewer/viewer-fn? x) re-eval)) doc)))

(defn replace-viewer-fns [{:as doc :keys [name->viewer]}]
(assoc (w/postwalk-replace name->viewer doc)
:name->viewer name->viewer))

(defn ^:export set-state! [{:as state :keys [doc]}]
(when (contains? state :doc)
(when (exists? js/window)
;; TODO: can we restore the scroll position when navigating back?
(.scrollTo js/window #js {:top 0}))
(reset! !doc doc))
(reset! !doc (replace-viewer-fns doc)))
;; (when (and error (contains? @!doc :status))
;; (swap! !doc dissoc :status))
(when (remount? doc)
@@ -713,7 +758,7 @@

(defn patch-state! [{:keys [patch]}]
(if (remount? patch)
(do (swap! !doc #(re-eval-viewer-fns (apply-patch % patch)))
(do (swap! !doc #(re-eval-viewer-fns (replace-viewer-fns (apply-patch % patch))))
;; TODO: figure out why it doesn't work without `js/setTimeout`
(js/setTimeout #(swap! !eval-counter inc) 10))
(swap! !doc apply-patch patch)))
@@ -967,6 +1012,9 @@
[:span {:dangerouslySetInnerHTML {:__html (.renderToString katex tex-string (j/obj :displayMode (not inline?) :throwOnError false))}}]
default-loading-view)))

(defn render-katex-inline [tex opts]
(nextjournal.clerk.render/render-katex tex (assoc opts :inline? true)))

(defn render-mathjax [value]
(let [mathjax (hooks/use-d3-require "https://run.nextjournalusercontent.com/data/QmQadTUYtF4JjbwhUFzQy9BQiK52ace3KqVHreUqL7ohoZ?filename=es5/tex-svg-full.js&content-type=application/javascript")
ref-fn (react/useCallback (fn [el]
@@ -1027,10 +1075,44 @@
[render-code code-string (assoc opts :language "clojure")]]])))


(defn render-example [{:keys [form val]} opts]
[:div.mb-3.last:mb-0
[:div.bg-slate-100.dark:bg-slate-800.px-4.py-2.border-l-2.border-slate-200.dark:border-slate-700
(inspect-presented opts form)]
[:div.pt-2.px-4.border-l-2.border-transparent
(inspect-presented opts val)]])

(defn render-examples [examples opts]
[:div
[:div.uppercase.tracking-wider.text-xs.font-sans.font-bold.text-slate-500.dark:text-white.mb-2.mt-3 "Examples"]
(into [:div] (inspect-children opts) examples)])

(defn render-row [items opts]
(into [:div {:class "md:flex md:flex-row md:gap-4 not-prose"
:style opts}]
(map (fn [item]
[:div.flex.items-center.justify-center.flex-auto
[inspect-presented opts item]]))
items))

(defn render-col [items opts]
(into [:div {:class "md:flex md:flex-col md:gap-4 clerk-grid not-prose"
:style opts}]
(map (fn [item]
[:div.flex.items-center.justify-center
[inspect-presented opts item]]))
items))

(defn render-empty-fragment [_ _] [:<>])

(defn url-for [{:as src :keys [blob-id]}]
(if (string? src)
src
(str "/_blob/" blob-id (when-let [opts (seq (dissoc src :blob-id))]
(str "?" (opts->query opts))))))

(defn render-image [blob-or-url]
[:div.flex.flex-col.items-center.not-prose
[:img {:src (url-for blob-or-url)}]])

(def consume-view-context view-context/consume)
76 changes: 21 additions & 55 deletions src/nextjournal/clerk/viewer.cljc
Original file line number Diff line number Diff line change
@@ -660,7 +660,7 @@
(->> (mapv (partial with-viewer
(cond-> result-viewer
(hidden-viewer-eval-result? cell)
(assoc :render-fn '(fn [_ _] [:<>]))))))))
(assoc :render-fn 'nextjournal.clerk.render/render-empty-fragment)))))))

(defn transform-cell [cell]
(let [{:keys [code? result?]} (->visibility cell)]
@@ -677,7 +677,7 @@
(def cell-viewer
{:name `cell-viewer
:transform-fn (update-val transform-cell)
:render-fn '(fn [xs opts] (into [:<>] (nextjournal.clerk.render/inspect-children opts) xs))})
:render-fn 'nextjournal.clerk.render/render-children})

(defn lift-block-images
"Lift an image node to top-level when it is the only child of a paragraph."
@@ -750,7 +750,7 @@
(def table-missing-viewer
{:name `table-missing-viewer
:pred #{:nextjournal/missing}
:render-fn '(fn [x] [:<>])})
:render-fn 'nextjournal.clerk.render/render-empty-fragment})

(def table-markup-viewer
{:name `table-markup-viewer
@@ -809,7 +809,7 @@
;; formulas
{:name :nextjournal.markdown/formula
:transform-fn (comp :text ->value)
:render-fn '(fn [tex] (nextjournal.clerk.render/render-katex tex {:inline? true}))}
:render-fn 'nextjournal.clerk.render/render-katex-inline}
{:name :nextjournal.markdown/block-formula
:transform-fn (comp :text ->value)
:render-fn 'nextjournal.clerk.render/render-katex}
@@ -855,7 +855,7 @@
:transform-fn (fn [wrapped-value] (with-viewer `html-viewer [:sup.sidenote-ref (-> wrapped-value ->value :ref inc)]))}])

(def char-viewer
{:name `char-viewer :pred char? :render-fn '(fn [c] [:span.cmt-string.inspected-value "\\" c])})
{:name `char-viewer :pred char? :render-fn 'nextjournal.clerk.render/char-viewer})

(def string-viewer
{:name `string-viewer
@@ -874,27 +874,25 @@
(instance? clojure.lang.BigInt %)) pr-str))])})

(def number-hex-viewer
{:name `number-hex-viewer :render-fn '(fn [num] (nextjournal.clerk.render/render-number (str "0x" (.toString (js/Number. num) 16))))})
{:name `number-hex-viewer :render-fn 'nextjournal.clerk.render/render-hex-number})

(def symbol-viewer
{:name `symbol-viewer :pred symbol? :render-fn '(fn [x] [:span.cmt-keyword.inspected-value (str x)])})
{:name `symbol-viewer :pred symbol? :render-fn 'nextjournal.clerk.render/render-symbol})

(def keyword-viewer
{:name `keyword-viewer :pred keyword? :render-fn '(fn [x] [:span.cmt-atom.inspected-value (str x)])})
{:name `keyword-viewer :pred keyword? :render-fn 'nextjournal.clerk.render/render-keyword})

(def nil-viewer
{:name `nil-viewer :pred nil? :render-fn '(fn [_] [:span.cmt-default.inspected-value "nil"])})
{:name `nil-viewer :pred nil? :render-fn 'nextjournal.clerk.render/render-nil})

(def boolean-viewer
{:name `boolean-viewer :pred boolean? :render-fn '(fn [x] [:span.cmt-bool.inspected-value (str x)])})
{:name `boolean-viewer :pred boolean? :render-fn 'nextjournal.clerk.render/render-boolean})

(def map-entry-viewer
{:name `map-entry-viewer :pred map-entry? :render-fn '(fn [xs opts] (into [:<>] (comp (nextjournal.clerk.render/inspect-children opts) (interpose " ")) xs)) :page-size 2})
{:name `map-entry-viewer :pred map-entry? :render-fn 'nextjournal.clerk.render/render-map-entry :page-size 2})

(def read+inspect-viewer
{:name `read+inspect-viewer :render-fn '(fn [x] (try [nextjournal.clerk.render/inspect (nextjournal.clerk.viewer/read-string-without-tag-table x)]
(catch js/Error _e
(nextjournal.clerk.render/render-unreadable-edn x))))})
{:name `read+inspect-viewer :render-fn 'nextjournal.clerk.render/render-read+inspect})

(def vector-viewer
{:name `vector-viewer :pred vector? :render-fn 'nextjournal.clerk.render/render-coll :opening-paren "[" :closing-paren "]" :page-size 20})
@@ -914,7 +912,7 @@
{:name `var-viewer
:pred var?
:transform-fn (comp #?(:cljs var->symbol :clj symbol) ->value)
:render-fn '(fn [x] [:span.inspected-value [:span.cmt-meta "#'" (str x)]])})
:render-fn 'nextjournal.clerk.render/render-var})

(defn ->opts [wrapped-value]
(select-keys wrapped-value [:nextjournal/budget :nextjournal/css-class :nextjournal/width :nextjournal/render-opts
@@ -961,9 +959,7 @@
:nextjournal/width (image-width image)}
mark-presented))])
:name `image-viewer
:render-fn '(fn [blob-or-url] [:div.flex.flex-col.items-center.not-prose
[:img {:src #?(:clj (nextjournal.clerk.render/url-for blob-or-url)
:cljs blob-or-url)}]])})
:render-fn 'nextjournal.clerk.render/render-image})

(def ideref-viewer
{:name `ideref-viewer
@@ -1038,21 +1034,10 @@
(update-val (fn [v] (if (string? v) v (str/trim (with-out-str (pprint/pprint v)))))))})

(def row-viewer
{:name `row-viewer :render-fn '(fn [items opts]
(let [item-count (count items)]
(into [:div {:class "md:flex md:flex-row md:gap-4 not-prose"
:style opts}]
(map (fn [item]
[:div.flex.items-center.justify-center.flex-auto
(nextjournal.clerk.render/inspect-presented opts item)])) items)))})
{:name `row-viewer :render-fn 'nextjournal.clerk.render/render-row})

(def col-viewer
{:name `col-viewer :render-fn '(fn [items opts]
(into [:div {:class "md:flex md:flex-col md:gap-4 clerk-grid not-prose"
:style opts}]
(map (fn [item]
[:div.flex.items-center.justify-center
(nextjournal.clerk.render/inspect-presented opts item)])) items))})
{:name `col-viewer :render-fn 'nextjournal.clerk.render/render-col})

(def table-viewers
[(-> string-viewer
@@ -1111,14 +1096,9 @@

(def tagged-value-viewer
{:name `tagged-value-viewer
:render-fn '(fn [{:keys [tag value space?]} opts]
(nextjournal.clerk.render/render-tagged-value
{:space? (:nextjournal/value space?)}
(str "#" (:nextjournal/value tag))
[nextjournal.clerk.render/inspect-presented value]))
:render-fn 'nextjournal.clerk.render/render-tagged-value
:transform-fn mark-preserve-keys})


#?(:cljs
(def js-promise-viewer
{:name `js-promise-viewer :pred #(instance? js/Promise %) :render-fn 'nextjournal.clerk.render/render-promise}))
@@ -1129,9 +1109,7 @@
:pred goog/isObject
:page-size 20
:opening-paren "{" :closing-paren "}"
:render-fn '(fn [v opts] (nextjournal.clerk.render/render-tagged-value {:space? true}
"#js"
(nextjournal.clerk.render/render-map v opts)))
:render-fn 'nextjournal.clerk.render/render-js-object
:transform-fn (update-val (fn [^js o]
(into {}
(comp (remove (fn [k] (identical? "function" (goog/typeOf (j/get o k)))))
@@ -1148,10 +1126,7 @@
{:name `js-array-viewer
:pred js-iterable?
:transform-fn (update-val seq)
:render-fn '(fn [v opts]
(nextjournal.clerk.render/render-tagged-value {:space? true}
"#js"
(nextjournal.clerk.render/render-coll v opts)))
:render-fn 'nextjournal.clerk.render/render-js-array
:opening-paren "[" :closing-paren "]"
:page-size 20}))

@@ -1977,21 +1952,12 @@
:transform-fn (fn [wrapped-value]
(-> wrapped-value
mark-preserve-keys
(assoc :nextjournal/viewer {:render-fn '(fn [{:keys [form val]} opts]
[:div.mb-3.last:mb-0
[:div.bg-slate-100.dark:bg-slate-800.px-4.py-2.border-l-2.border-slate-200.dark:border-slate-700
(nextjournal.clerk.render/inspect-presented opts form)]
[:div.pt-2.px-4.border-l-2.border-transparent
(nextjournal.clerk.render/inspect-presented opts val)]])})
(assoc :nextjournal/viewer {:render-fn 'nextjournal.clerk.render/render-example})
(update-in [:nextjournal/value :val] maybe-wrap-var-from-def (get-in wrapped-value [:nextjournal/value :form]))
(update-in [:nextjournal/value :form] code)))})

(def examples-viewer
{:name `examples-viewer
:transform-fn (update-val (fn [examples]
(mapv (partial with-viewer example-viewer) examples)))
:render-fn '(fn [examples opts]
[:div
[:div.uppercase.tracking-wider.text-xs.font-sans.font-bold.text-slate-500.dark:text-white.mb-2.mt-3 "Examples"]
(into [:div]
(nextjournal.clerk.render/inspect-children opts) examples)])})
:render-fn 'nextjournal.clerk.render/render-examples})