diff --git a/notebooks/viewers/context.clj b/notebooks/viewers/context.clj new file mode 100644 index 000000000..f80831db7 --- /dev/null +++ b/notebooks/viewers/context.clj @@ -0,0 +1,44 @@ +;; # 🪬 Viewer Context + +(ns viewers.context + (:require [nextjournal.clerk :as clerk] + [nextjournal.clerk.viewer :as viewer])) + +;; A viewer's `:pred` function can perform viewer selection based on a value. + +;; It would sometimes be useful to have more context available. Examples of this are: +;; +;; * Selecting a viewer based on the originating form +;; * Selecting a different viewer based on additional context like `:path` +;; * Bring Clerk's handling of out-of-band metadata like `::clerk/visibility` and `::clerk/width` into userspace + +;; To make this a backwards-compatible change, we can opt into these +;; richer predicate functions using a map with a key: + +(def cljc-viewer + {:pred {:wrapped (fn [{:keys [form]}] + (contains? (meta form) :cljc))} + :transform-fn (fn [{:keys [form]}] + (clerk/eval-cljs form))}) + +;; We should probably use a namespaced keyword to disambiguate it. + +;; Also considered letting the pred function opt in using +;; metadata. Rejected this because it's invisible and doesn't work for +;; e.g. keywords. + +^::clerk/no-cache +(clerk/add-viewers! [cljc-viewer]) + + +;; Now what can we use this for? We can now, for example, create a +;; viewer in userspace that evalautes a given form in Clojure and sci, +;; allowing us to use it on both sides. + +^:cljc +(defn my-greet-fn [x] + (str "Greetings from " x)) + +(my-greet-fn "Clojure") + +(clerk/eval-cljs '(my-greet-fn "ClojureScript")) diff --git a/src/nextjournal/clerk/viewer.cljc b/src/nextjournal/clerk/viewer.cljc index b0375ea7e..2e830ca20 100644 --- a/src/nextjournal/clerk/viewer.cljc +++ b/src/nextjournal/clerk/viewer.cljc @@ -510,7 +510,7 @@ (defn transform-result [{:as wrapped-value :keys [path]}] - (let [{:as _cell :keys [form id settings] ::keys [result doc]} (:nextjournal/value wrapped-value) + (let [{:as cell :keys [form id settings] ::keys [result doc]} (:nextjournal/value wrapped-value) {:keys [static-build? bundle?]} doc {:nextjournal/keys [value blob-id viewers]} result blob-mode (cond @@ -522,6 +522,7 @@ (select-keys (keys viewer-opts-normalization)) (set/rename-keys viewer-opts-normalization)) {:as to-present :nextjournal/keys [auto-expand-results?]} (merge (dissoc (->opts wrapped-value) :!budget :nextjournal/budget) + (dissoc cell :result) opts-from-block (ensure-wrapped-with-viewers (or viewers (get-viewers (get-*ns*))) value)) presented-result (-> (present to-present) @@ -1358,9 +1359,12 @@ (throw (ex-info (str "cannot find viewer named " selected-viewer) {:selected-viewer selected-viewer :viewers viewers}))) selected-viewer)) - (find-viewer viewers (let [v (->value x)] - (fn [{:keys [pred]}] - (and (ifn? pred) (pred v))))) + (find-viewer viewers (fn [{:keys [pred]}] + (and (ifn? pred) (if-let [wrapped-pred (and (map? pred) + (ifn? (:wrapped pred)) + (:wrapped pred))] + (wrapped-pred x) + (pred (->value x)))))) (throw (ex-info (str "cannot find matching viewer for value") {:value (->value x) :viewers viewers :x x}))))