Skip to content

Commit c0b56ff

Browse files
authored
Experiment to render viewers to html string (#778)
This is an experiment to find out what's required to support rendering to HTML strings via hiccup, using an alternative viewer stack. If this is feasible, it could be the basis for a much smaller client-side bundle in combination with a morphing lib while moving some of the viewers implementation to custom elements.
1 parent dd752d5 commit c0b56ff

File tree

4 files changed

+105
-7
lines changed

4 files changed

+105
-7
lines changed

notebooks/hello.clj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
(:require [nextjournal.clerk :as clerk]))
77

88
;; Here's a visualization of unemployment in the US.
9-
(clerk/vl {:width 700 :height 400 :data {:url "https://vega.github.io/vega-datasets/data/us-10m.json"
10-
:format {:type "topojson" :feature "counties"}}
11-
:transform [{:lookup "id" :from {:data {:url "https://vega.github.io/vega-datasets/data/unemployment.tsv"}
12-
:key "id" :fields ["rate"]}}]
13-
:projection {:type "albersUsa"} :mark "geoshape" :encoding {:color {:field "rate" :type "quantitative"}}})
9+
#_(clerk/vl {:width 700 :height 400 :data {:url "https://vega.github.io/vega-datasets/data/us-10m.json"
10+
:format {:type "topojson" :feature "counties"}}
11+
:transform [{:lookup "id" :from {:data {:url "https://vega.github.io/vega-datasets/data/unemployment.tsv"}
12+
:key "id" :fields ["rate"]}}]
13+
:projection {:type "albersUsa"} :mark "geoshape" :encoding {:color {:field "rate" :type "quantitative"}}})
14+

notebooks/html_string.clj

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
;; # HTML String Rendering
2+
3+
;; This is an experiment to find out what's required to support
4+
;; rendering to HTML strings via hiccup, using an alternative viewer
5+
;; stack. If this is feasible, it could be the basis for a much
6+
;; smaller client-side bundle in combination with a morphing lib while
7+
;; moving some of the viewers implementation to custom elements.
8+
9+
(ns html-string
10+
(:require [nextjournal.clerk :as clerk]
11+
[nextjournal.clerk.eval :as eval]
12+
[nextjournal.clerk.view :as view]
13+
[nextjournal.clerk.viewer :as viewer]
14+
[clojure.repl.deps :as deps]))
15+
16+
(defn render-cell [xs]
17+
(apply list xs))
18+
19+
(defn render-notebook [{xs :blocks :keys [package doc-css-class sidenotes? toc toc-visibility header footer]}
20+
#_{:as render-opts :keys [!expanded-at]}]
21+
[:div.flex
22+
[:div.flex-auto.w-screen.scroll-container
23+
(into
24+
[:div
25+
(merge
26+
{#_#_:key "notebook-viewer"
27+
:class (cond-> (or doc-css-class (mapv name [:flex :flex-col :items-center :notebook-viewer :flex-auto]))
28+
sidenotes? (conj :sidenotes-layout))})]
29+
(concat (when header [header]) xs (when footer [footer])))]])
30+
31+
(defn render-html [html]
32+
html)
33+
34+
(defn render-code-block [code-string #_{:as opts :keys [id]}]
35+
[:div.viewer.code-viewer.w-full.max-w-wide #_{:data-block-id id}
36+
code-string
37+
#_
38+
[code/render-code code-string (assoc opts :language "clojure")]])
39+
40+
(defn render-toc [_])
41+
42+
(def modified-viewers
43+
[(assoc viewer/cell-viewer :render-fn `render-cell)
44+
(assoc viewer/notebook-viewer :render-fn `render-notebook)
45+
(assoc viewer/code-block-viewer :render-fn `render-code-block)
46+
(assoc viewer/html-viewer :render-fn `render-html)
47+
(assoc viewer/toc-viewer :render-fn `render-toc)])
48+
49+
(defn doc->viewer [doc]
50+
(viewer/present
51+
(viewer/with-viewers (viewer/add-viewers modified-viewers)
52+
(viewer/notebook doc))))
53+
54+
(def viewer-doc
55+
(doc->viewer (eval/eval-file "notebooks/hello.clj")))
56+
57+
(defn extract-viewers [viewer-doc]
58+
(into []
59+
(keep :nextjournal/viewer)
60+
(tree-seq coll? seq viewer-doc)))
61+
62+
(sort (keys (frequencies (mapv (comp :form :render-fn) (extract-viewers viewer-doc)))))
63+
64+
(defn ->fn [sym]
65+
(cond
66+
(simple-symbol? sym) (ns-resolve 'clojure.core sym)
67+
(qualified-symbol? sym) (requiring-resolve sym)
68+
:else (throw (ex-info "->fn expected symbol, got" {:sym sym}))))
69+
70+
(defn eval-viewers [viewer-doc]
71+
(clojure.walk/postwalk (fn [x]
72+
(if (and (map? x)
73+
(contains? x :nextjournal/viewer)
74+
(contains? x :nextjournal/value))
75+
((->fn (get-in x [:nextjournal/viewer :render-fn :form]))
76+
(:nextjournal/value x))
77+
x))
78+
viewer-doc))
79+
#_
80+
(clerk/present! (viewer/html
81+
(eval-viewers viewer-doc)))
82+
83+
(defonce html
84+
(do
85+
(deps/add-lib 'io.github.escherize/huff)
86+
@(requiring-resolve 'huff2.core/html)))
87+
88+
(defn ->html [viewer-doc]
89+
(nextjournal.clerk.view/->html {:exclude-js? true
90+
:html (html (eval-viewers viewer-doc))}))
91+
92+
(doto "test.html"
93+
(spit (->html viewer-doc))
94+
(clojure.java.browse/browse-url))
95+
96+
97+

src/nextjournal/clerk/viewer.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@
793793
{:name :nextjournal.markdown/heading
794794
:transform-fn (into-markup
795795
(fn [{:keys [attrs heading-level]}]
796-
[(str "h" heading-level) attrs]))}
796+
[(keyword (str "h" heading-level)) attrs]))}
797797
{:name :nextjournal.markdown/image
798798
:transform-fn (fn [{node :nextjournal/value}]
799799
(with-viewer `html-viewer

test/nextjournal/clerk/viewer_test.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@
241241
(deftest present
242242
(testing "only transform-fn can select viewer"
243243
(is (match? {:nextjournal/value [:div.viewer.markdown-viewer.w-full.max-w-prose.px-8 {}
244-
["h1" {:id "hello-markdown!"} [:<> "👋 Hello "] [:em [:<> "markdown"]] [:<> "!"]]]
244+
[:h1 {:id "hello-markdown!"} [:<> "👋 Hello "] [:em [:<> "markdown"]] [:<> "!"]]]
245245
:nextjournal/viewer {:name `v/markdown-node-viewer}}
246246
(v/present (v/with-viewer {:transform-fn (comp v/md v/->value)}
247247
"# 👋 Hello _markdown_!")))))

0 commit comments

Comments
 (0)