Skip to content

Commit ac409d1

Browse files
philippamarkovicsmkzampino
authored
Home page & index improvements (#458)
This improves the homepage including dark mode support. We also unify the index and make it usable in interactive mode so it's no longer needed to perform a `build!` to see it. Add a header to the interactive mode that allows to navigate to both the index and home and allow to customize it. Co-authored-by: Martin Kavalar <martin@nextjournal.com> Co-authored-by: Andrea Amantini <lo.zampino@gmail.com>
1 parent 8075a64 commit ac409d1

File tree

11 files changed

+233
-142
lines changed

11 files changed

+233
-142
lines changed

resources/stylesheets/viewer.css

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
}
138138
@media (min-width: 960px){
139139
.notebook-viewer .code-viewer .cm-content {
140-
@apply py-4 pl-12;
140+
@apply pb-2 pl-12;
141141
}
142142
.notebook-viewer .code-listing {
143143
width: 48rem !important;
@@ -300,8 +300,6 @@
300300
/* Notebook Spacing */
301301
/* --------------------------------------------------------------- */
302302

303-
.notebook-viewer { @apply py-16; }
304-
#clerk-static-app .notebook-viewer { @apply pt-[0.8rem] pb-16; }
305303
.markdown-viewer *:first-child:not(.code-viewer):not(li):not(h2):not(.sidenote) { @apply mt-0; }
306304
/*.viewer + .viewer { @apply mt-6; }*/
307305
.viewer + .result-viewer { @apply mt-0; }

src/nextjournal/clerk.clj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
[nextjournal.clerk.config :as config]
1212
[nextjournal.clerk.eval :as eval]
1313
[nextjournal.clerk.parser :as parser]
14-
[nextjournal.clerk.view :as view]
1514
[nextjournal.clerk.viewer :as v]
1615
[nextjournal.clerk.webserver :as webserver]))
1716

@@ -393,13 +392,6 @@
393392
`(nextjournal.clerk/with-viewer v/examples-viewer
394393
(mapv (fn [form# val#] {:form form# :val val#}) ~(mapv (fn [x#] `'~x#) body) ~(vec body)))))
395394

396-
(defn file->viewer
397-
"Evaluates the given `file` and returns it's viewer representation."
398-
([file] (file->viewer {:inline-results? true} file))
399-
([opts file] (view/doc->viewer opts (eval/eval-file file))))
400-
401-
#_(file->viewer "notebooks/rule_30.clj")
402-
403395
(defn halt-watcher!
404396
"Halts the filesystem watcher when active."
405397
[]

src/nextjournal/clerk/builder.clj

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"Clerk's Static App Builder."
33
(:require [babashka.fs :as fs]
44
[babashka.process :refer [sh]]
5+
[clojure.edn :as edn]
56
[clojure.java.browse :as browse]
67
[clojure.java.io :as io]
78
[clojure.string :as str]
@@ -12,7 +13,8 @@
1213
[nextjournal.clerk.view :as view]
1314
[nextjournal.clerk.viewer :as viewer]
1415
[nextjournal.clerk.webserver :as webserver]
15-
[nextjournal.clerk.config :as config]))
16+
[nextjournal.clerk.config :as config])
17+
(:import (java.net URL)))
1618

1719
(def clerk-docs
1820
(into ["CHANGELOG.md"
@@ -124,9 +126,11 @@
124126
(throw (ex-info "nothing to build" (merge {:expanded-paths expanded-paths} (select-keys build-opts [:paths :paths-fn :index]))))
125127
expanded-paths))
126128

127-
(defn ^:private maybe-add-index [{:as build-opts :keys [paths paths-fn index]} resolved-paths]
128-
(when (and index (or (not (string? index)) (not (fs/exists? index))))
129-
(throw (ex-info "`:index` must be string and point to existing file" {:index index})))
129+
(defn ^:private maybe-add-index [{:as opts :keys [index]} resolved-paths]
130+
(when (contains? opts :index)
131+
(or (instance? URL index)
132+
(and (string? index) (fs/exists? index))
133+
(throw (ex-info "`:index` must be either an instance of java.net.URL or a string and point to an existing file" {:index index}))))
130134
(cond-> resolved-paths
131135
(and index (not (contains? (set resolved-paths) index)))
132136
(conj index)))
@@ -185,22 +189,30 @@
185189
(-> opts'
186190
(update :resource->url #(merge {} %2 %1) @config/!resource->url)
187191
(cond-> #_opts'
188-
expand-paths? (dissoc :expand-paths?)
189-
(and (not index) (= 1 (count expanded-paths))) (assoc :index (first expanded-paths)))))))
192+
expand-paths?
193+
(dissoc :expand-paths?)
194+
(and (not index) (= 1 (count expanded-paths)))
195+
(assoc :index (first expanded-paths))
196+
(and (not index) (< 1 (count expanded-paths)) (every? (complement #{"index.clj"}) expanded-paths))
197+
(as-> opts
198+
(let [index (io/resource "nextjournal/clerk/index.clj")]
199+
(-> opts (assoc :index index) (update :expanded-paths conj index)))))))))
190200

191201
#_(process-build-opts {:index 'book.clj :expand-paths? true})
192202
#_(process-build-opts {:paths ["notebooks/rule_30.clj"] :expand-paths? true})
203+
#_(process-build-opts {:paths ["notebooks/rule_30.clj"
204+
"notebooks/markdown.md"] :expand-paths? true})
193205

194206
(defn build-path->url [{:as opts :keys [bundle?]} docs]
195207
(into {}
196208
(map (comp (juxt identity #(cond-> (->> % (viewer/map-index opts) strip-index) (not bundle?) ->html-extension))
197-
:file))
209+
str :file))
198210
docs))
199211
#_(build-path->url {:bundle? false} [{:file "notebooks/foo.clj"} {:file "index.clj"}])
200212
#_(build-path->url {:bundle? true} [{:file "notebooks/foo.clj"} {:file "index.clj"}])
201213

202214
(defn build-static-app-opts [{:as opts :keys [bundle? out-path browse? index]} docs]
203-
(let [path->doc (into {} (map (juxt :file :viewer)) docs)]
215+
(let [path->doc (into {} (map (juxt (comp str :file) :viewer)) docs)]
204216
(assoc opts
205217
:bundle? bundle?
206218
:path->doc path->doc
@@ -230,23 +242,23 @@
230242
[opts docs]
231243
(let [{:as opts :keys [bundle? out-path browse? ssr?]} (process-build-opts opts)
232244
index-html (str out-path fs/file-separator "index.html")
233-
{:as static-app-opts :keys [path->url path->doc]} (build-static-app-opts opts docs)]
245+
{:as static-app-opts :keys [path->url path->doc]} (build-static-app-opts (viewer/update-if opts :index str) docs)]
246+
(when-not (contains? (-> path->url vals set) "")
247+
(throw (ex-info "Index must have been processed at this point" {:opts opts :docs docs})))
234248
(when-not (fs/exists? (fs/parent index-html))
235249
(fs/create-dirs (fs/parent index-html)))
236250
(if bundle?
237251
(spit index-html (view/->static-app static-app-opts))
238-
(do (when-not (contains? (-> path->url vals set) "") ;; no user-defined index page
239-
(spit index-html (view/->static-app (dissoc static-app-opts :path->doc))))
240-
(doseq [[path doc] path->doc]
241-
(let [out-html (str out-path fs/file-separator (->> path (viewer/map-index opts) ->html-extension))]
242-
(fs/create-dirs (fs/parent out-html))
243-
(spit out-html (view/->static-app (cond-> (assoc static-app-opts :path->doc (hash-map path doc) :current-path path)
244-
ssr? ssr!)))))))
252+
(doseq [[path doc] path->doc]
253+
(let [out-html (str out-path fs/file-separator (->> path (viewer/map-index opts) ->html-extension))]
254+
(fs/create-dirs (fs/parent out-html))
255+
(spit out-html (view/->static-app (cond-> (assoc static-app-opts :path->doc (hash-map path doc) :current-path path)
256+
ssr? ssr!))))))
245257
(when browse?
246258
(browse/browse-url (-> index-html fs/absolutize .toString path-to-url-canonicalize)))
247259
{:docs docs
248260
:index-html index-html
249-
:build-href (if (and @webserver/!server (= out-path default-out-path)) "/build" index-html)}))
261+
:build-href (if (and @webserver/!server (= out-path default-out-path)) "/build/" index-html)}))
250262

251263

252264
(defn compile-css!
@@ -287,12 +299,29 @@
287299
(defn doc-url
288300
([opts doc file path] (doc-url opts doc file path nil))
289301
([{:as opts :keys [bundle?]} docs file path fragment]
290-
(let [url (get (build-path->url opts docs) path)]
302+
(let [url (get (build-path->url (viewer/update-if opts :index str) docs) path)]
291303
(if bundle?
292304
(str "#/" url)
293305
(str (viewer/relative-root-prefix-from (viewer/map-index opts file))
294306
url (when fragment (str "#" fragment)))))))
295307

308+
(defn read-opts-from-deps-edn! []
309+
(if (fs/exists? "deps.edn")
310+
(let [deps-edn (edn/read-string (slurp "deps.edn"))]
311+
(if-some [clerk-alias (get-in deps-edn [:aliases :nextjournal/clerk])]
312+
(get clerk-alias :exec-args
313+
{:error "No `:exec-args` found in `:nextjournal/clerk` alias."})
314+
{:error "No `:nextjournal/clerk` alias found in `deps.edn`."}))
315+
{:error "No `deps.edn` found in project."}))
316+
317+
(def ^:dynamic ^:private *build-opts* nil)
318+
(defn index-paths []
319+
(let [{:as opts :keys [index error]} (or *build-opts* (read-opts-from-deps-edn!))]
320+
(if error opts {:paths (remove #{index "index.clj"} (expand-paths opts))})))
321+
322+
#_(index-paths)
323+
#_(nextjournal.clerk/show! 'nextjournal.clerk.index)
324+
296325
(defn build-static-app! [{:as opts :keys [bundle?]}]
297326
(let [{:as opts :keys [download-cache-fn upload-cache-fn report-fn compile-css? expanded-paths error]}
298327
(try (process-build-opts (assoc opts :expand-paths? true))
@@ -324,9 +353,11 @@
324353
(report-fn {:stage :building :doc doc :idx idx})
325354
(let [{result :result duration :time-ms} (eval/time-ms
326355
(try
327-
(let [doc (binding [viewer/doc-url (partial doc-url opts state file)]
328-
(eval/eval-analyzed-doc doc))]
329-
(assoc doc :viewer (view/doc->viewer (assoc opts :inline-results? true) doc)))
356+
(binding [*build-opts* opts
357+
viewer/doc-url (partial doc-url opts state file)]
358+
(let [doc (eval/eval-analyzed-doc doc)]
359+
(assoc doc :viewer (view/doc->viewer (assoc opts :static-build? true
360+
:nav-path (str file)) doc))))
330361
(catch Exception e
331362
{:error e})))]
332363
(report-fn (merge {:stage :built :duration duration :idx idx}
@@ -369,3 +400,9 @@
369400
:resource->url {"/js/viewer.js" "/viewer.js"}
370401
:paths ["notebooks/cherry.clj"]
371402
:out-path "build"})
403+
#_(build-static-app! {:paths ["CHANGELOG.md"
404+
"notebooks/markdown.md"
405+
"notebooks/viewers/image.clj"
406+
"notebooks/viewers/html.cj"]
407+
:git/sha "d60f5417"
408+
:git/url "https://github.com/nextjournal/clerk"})

src/nextjournal/clerk/home.clj

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,24 @@
1919
^{::clerk/css-class ["w-full" "m-0"]}
2020
(clerk/html
2121
[:div.max-w-prose.px-8.mx-auto
22-
[:div.md:flex.md:justify-between.px-8.text-center.md:text-left
22+
[:div.md:flex.md:justify-between.text-center.md:text-left
2323
[:h1 "👋 Welcome to Clerk!"]
2424
#_[:div.text-sm.md:text-xs.font-sans.md:text-right.mt-1
2525
[:span.font-bold.block
2626
"You’re running Clerk " [:a {:href "#"} "v0.12.707"] "."]
2727
[:span "The newest version is " [:a {:href "#"} "v0.12.707"] ". " [:a {:href "#"} "What’s changed?"]]]]
28-
[:div.rounded-lg.border-2.border-amber-100.bg-amber-50.px-8.pt-3.pb-4.mx-auto.text-center.font-sans.mt-6.md:mt-4
28+
[:div.rounded-lg.border-2.border-amber-100.bg-amber-50.dark:border-slate-600.dark:bg-slate-800.dark:text-slate-100.px-8.py-4.mx-auto.text-center.font-sans.mt-6.md:mt-4
2929
[:div.font-medium
30-
"Call " [:span.font-mono.text-sm.bg-white.bg-opacity-80.mx-1.font-bold "nextjournal.clerk/show!"]
30+
"Call "
31+
[:span.font-mono.text-sm.bg-white.bg-amber-100.border.border-amber-300.relative.dark:bg-slate-900.dark:border-slate-600.rounded.font-bold
32+
{:class "px-[4px] py-[1px] -top-[1px] mx-[2px]"}
33+
"nextjournal.clerk/show!"]
3134
" from your REPL to make a notebook appear!"]
3235
[:div.mt-2.text-sm "⚡️ This works best when you " [:a {:href "https://book.clerk.vision/#editor-integration"} "set up your editor to use a key binding for this!"]]]
33-
[:div.rounded-lg.border-2.border-indigo-100.bg-indigo-50.px-8.pt-3.pb-4.mt-6.text-center.font-sans
34-
[:div.font-medium "📖 New to Clerk? Learn all about it in " [:a {:href "https://book.clerk.vision"} "The Book of Clerk"] "."]
36+
[:div.rounded-lg.border-2.border-indigo-100.bg-indigo-50.dark:border-slate-600.dark:bg-slate-800.dark:text-slate-100.px-8.py-4.mt-6.text-center.font-sans
37+
[:div.font-medium.md:flex.items-center.justify-center
38+
[:span.text-xl.relative {:class "top-[2px] mr-2"} "📖"]
39+
[:span "New to Clerk? Learn all about it in " [:a {:href "https://book.clerk.vision"} [:span.block.md:inline "The Book of Clerk."]]]]
3540
#_
3641
[:div.mt-2.text-sm
3742
"Here are some handy links:"

src/nextjournal/clerk/index.clj

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
(ns nextjournal.clerk.index
2+
{:nextjournal.clerk/visibility {:code :hide :result :hide}
3+
:nextjournal.clerk/no-cache true}
4+
(:require [nextjournal.clerk :as clerk]
5+
[nextjournal.clerk.viewer :as v]
6+
[nextjournal.clerk.builder :as builder]))
7+
8+
(def !paths (delay (builder/index-paths)))
9+
10+
(def index-item-viewer
11+
{:pred string?
12+
:transform-fn (clerk/update-val (fn [path]
13+
(clerk/html
14+
[:li.border-t.first:border-t-0.dark:border-gray-800.odd:bg-slate-50.dark:odd:bg-white
15+
{:class "dark:odd:bg-opacity-[0.03]"}
16+
[:a.pl-4.pr-4.py-2.flex.w-full.items-center.justify-between.hover:bg-indigo-50.dark:hover:bg-gray-700
17+
{:href (clerk/doc-url path)}
18+
[:span.text-sm.md:text-md.monospace.flex-auto.block.truncate path]
19+
[:svg.h-4.w-4.flex-shrink-0 {:xmlns "http://www.w3.org/2000/svg" :fill "none" :viewBox "0 0 24 24" :stroke "currentColor"}
20+
[:path {:stroke-linecap "round" :stroke-linejoin "round" :stroke-width "2" :d "M9 5l7 7-7 7"}]]]])))})
21+
22+
(def index-viewer
23+
{:render-fn '(fn [xs opts]
24+
[:div.not-prose
25+
[:h1.mb-4 "Clerk"]
26+
(into [:ul.border.dark:border-slate-800.rounded-md.overflow-hidden]
27+
(nextjournal.clerk.render/inspect-children opts)
28+
xs)])
29+
:transform-fn (fn [wrapped-value]
30+
(update wrapped-value :nextjournal/viewers v/add-viewers [index-item-viewer]))})
31+
32+
{::clerk/visibility {:result :show}}
33+
(let [{:keys [paths error]} @!paths]
34+
(cond
35+
error (clerk/md error)
36+
paths (clerk/with-viewer index-viewer paths)))
37+
38+
#_[:div.bg-gray-100.dark:bg-gray-900.flex.justify-center.overflow-y-auto.w-screen.h-screen.p-4.md:p-0
39+
{:ref ref-fn}
40+
[:div.fixed.top-2.left-2.md:left-auto.md:right-2.z-10
41+
[render/dark-mode-toggle !state]]
42+
[:div.md:my-12.w-full.md:max-w-lg
43+
[:div.bg-white.dark:bg-gray-800.shadow-lg.rounded-lg.border.dark:border-gray-800.dark:text-white
44+
[:div.px-4.md:px-8.py-3
45+
[:h1.text-xl "Clerk"]]
46+
(into [:ul]
47+
(map (fn [path]
48+
[:li.border-t.dark:border-gray-900
49+
[:a.pl-4.md:pl-8.pr-4.py-2.flex.w-full.items-center.justify-between.hover:bg-indigo-50.dark:hover:bg-gray-700
50+
{:href (doc-url view-data path)}
51+
[:span.text-sm.md:text-md.monospace.flex-auto.block.truncate path]
52+
[:svg.h-4.w-4.flex-shrink-0 {:xmlns "http://www.w3.org/2000/svg" :fill "none" :viewBox "0 0 24 24" :stroke "currentColor"}
53+
[:path {:stroke-linecap "round" :stroke-linejoin "round" :stroke-width "2" :d "M9 5l7 7-7 7"}]]]]))
54+
(sort paths))]
55+
[:div.my-4.md:mb-0.text-xs.text-gray-400.sans-serif.px-4.md:px-8
56+
[:a.hover:text-indigo-600.dark:hover:text-white
57+
{:href "https://github.com/nextjournal/clerk"}
58+
"Generated with Clerk."]]]]

0 commit comments

Comments
 (0)