Giter Club home page Giter Club logo

clerk's Introduction

Clerk: Local-First Notebooks for Clojure

Clerk

Clojars Project

Moldable Live Programming for Clojure

🎪 View Demos📖 Book of Clerk 👩‍🎨 Using Clerk🪚 Development

Clerk takes a Clojure namespace and turns it into a notebook:

Clerk Screenshot

🎪 Demos

Clerk comes with a demo repo full of interesting use cases. Check them out and feel free to add your own via PRs.

⚖️ Rationale

Computational notebooks allow arguing from evidence by mixing prose with executable code. For a good overview of problems users encounter in traditional notebooks like Jupyter, see I don't like notebooks and What’s Wrong with Computational Notebooks? Pain Points, Needs, and Design Opportunities.

Specifically Clerk wants to address the following problems:

  • Less helpful than my editor
  • Notebook code being hard to reuse
  • Reproduction problems coming from out-of-order execution
  • Problems with archival and putting notebooks in source control

Clerk is a notebook library for Clojure that aims to address these problems by doing less, namely:

  • no editing environment, folks can keep using the editors they know and love
  • no new format: Clerk notebooks are either regular Clojure namespaces (interspersed with markdown comments) or regular markdown files (interspersed with Clojure code fences). This also means Clerk notebooks are meant to be stored in source control.
  • no out-of-order execution: Clerk notebooks always evaluate from top to bottom. Clerk builds a dependency graph of Clojure vars and only recomputes the needed changes to keep the feedback loop fast.
  • no external process: Clerk runs inside your Clojure process, giving Clerk access to all code on the classpath.

🚦 Status

ALPHA, expect breaking changes.

👩‍🎨 Using Clerk

To use Clerk in your project, you'll need Java 11+ and clojure. Add the following dependency to your deps.edn:

{:deps {io.github.nextjournal/clerk {:mvn/version "0.15.957"}}}

Require and start Clerk as part of your system start, e.g. in user.clj:

(require '[nextjournal.clerk :as clerk])

;; start Clerk's built-in webserver on the default port 7777, opening the browser when done
(clerk/serve! {:browse true})

;; either call `clerk/show!` explicitly
(clerk/show! "notebooks/rule_30.clj")

;; or let Clerk watch the given `:paths` for changes
(clerk/serve! {:watch-paths ["notebooks" "src"]})

;; start with watcher and show filter function to enable notebook pinning
(clerk/serve! {:watch-paths ["notebooks" "src"] :show-filter-fn #(clojure.string/starts-with? % "notebooks")})

;; Build a html file from the given notebook notebooks.
;; See the docstring for more options.
(clerk/build! {:paths ["notebooks/rule_30.clj"]})

You can then access Clerk at http://localhost:7777.

See the /notebooks folder in the Clerk repository for a number of sample notebooks.

Editor Workflow

For even better flow states, we recommend you bind clerk/show! to a shortcut in your favorite editor:

Emacs

In Emacs, add the following to your config:

(defun clerk-show ()
  (interactive)
  (when-let
      ((filename
        (buffer-file-name)))
    (save-buffer)
    (cider-interactive-eval
     (concat "(nextjournal.clerk/show! \"" filename "\")"))))

(define-key clojure-mode-map (kbd "<M-return>") 'clerk-show)

IntelliJ/Cursive

In IntelliJ/Cursive, you can set up REPL commands via:

  • going to Tools→REPL→Add New REPL Command, then
  • add the following command: (show! "~file-path");
  • make sure the command is executed in the nextjournal.clerk namespace;
  • lastly assign a shortcut of your choice via Settings→Keymap

Neovim + Conjure

With neovim + conjure one can use the following vimscript function to save the file and show it with Clerk:

function! ClerkShow()
  exe "w"
  exe "ConjureEval (nextjournal.clerk/show! \"" . expand("%:p") . "\")"
endfunction

nmap <silent> <localleader>cs :execute ClerkShow()<CR>

🪚 Developing Clerk

Make sure you have Babashka installed, and run:

bb dev :browse true

The will start everything needed to develop Clerk and open your default browser. You can connect your favorite editor to it using nREPL.

Any trailing arguments to bb dev will be forwarded to clojure -X and clerk/serve!. So if you prefer to not open your browser, leave out the :browse true arguments.

🐞 Known Issues

See notebooks/onwards.md.

Citing Clerk

If you are a researcher and use Clerk in your work, we encourage you to cite our work. You can use the following BibTeX citation:

@misc{clerk-github,
  author =  {Martin Kavalar and
             Jack Rusher},
  title = {{Clerk Source Code}},
  howpublished = {\url{https://github.com/nextjournal/clerk}},
  month        = feb,
  year         = 2023
}

clerk's People

Contributors

behrica avatar borkdude avatar craig-latacora avatar dackerman avatar daveliepmann avatar ezmiller avatar filipesilva avatar heliosmaster avatar holyjak avatar ikappaki avatar iku000888 avatar jackrusher avatar kommen avatar leahneukirchen avatar mathisto avatar mhuebert avatar mk avatar pfeodrippe avatar philippamarkovics avatar philomates avatar russmatney avatar sheluchin avatar sneakypeet avatar sohalt avatar sritchie avatar teodorlu avatar vedang avatar zampino avatar zoren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clerk's Issues

Local deps?

I just painfully found out on a flight that clerk's frontend deps (in my case vega, vega lite, vega embed and katex) are not included in the source and therefore failed fetching in-browser. This is an edge case for sure, but since clerk advertises itself being local first, I think it would be nice for clerk to be able to work offline.

Windows path issue when starting clerk (non WSL)

I love clerk and wanted to see if I can get a Windows setup working too (non WSL) but I keep running into the file path issue below. I'll debug it later and provide a patch for it (if the issue can even be fixed and is not a dependency issue) but I wonder if anyone already has a hunch of what it could be - pointers very welcome.

Using io.github.nextjournal/clerk {:mvn/version "0.4.305"}

nREPL server started on port 52852 on host 127.0.0.1 - nrepl://127.0.0.1:52852
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.3
OpenJDK 64-Bit Server VM 1.8.0_302-b08
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (ns user
  #_=>   (:require [nextjournal.clerk :as clerk]))
Execution error (PolyglotException) at sun.nio.fs.WindowsPathParser/normalize (WindowsPathParser.java:182).
java.nio.file.InvalidPathException: Illegal char <:> at index 4: file:/C:/Users/PhilippK%c3%bcng/.m2/repository/io/github/nextjournal/markdown/0.1.38/markdown-0.1.38.jar!/js/markdown.mjs

Seems to hang on infinite sequences

Replication steps

user.clj:

(ns user
  (:require [nextjournal.clerk :as clerk]))

(clerk/serve! {:watch-paths ["notebooks"]})

notebooks/tester.clj:

(range)

This seems to hang, rather than handling the infinite sequence as expected and called out in the example here:

Clerk server launched with Calva's jack-in, with no other dependencies. The Clerk server seems to just stop responding to updates.

On the other hand (range 100) seems to work fine.

CSS size

Hi. Bundled clerk currently contains 3.2 Mb CSS file. As I see the project used to purge instruction for Tailwindcss, but it was disabled with a comment "Move to non-jit compiled tailwind stylesheet, purging doesn't work nicely with custom viewers".

Could you please elaborate this decision, how exactly it doesn't play nicely with custom viewers?

Thank you

fastmath vec2/vec3/vec4 dosn't render and causes exception

Hi, with the latest nextjournal (0.5.346) following code results in exception and renders as nil value. Maybe something is wrong with fastmath.vector implementation however I can't understand what.

(ns sometest
  (:require [fastmath.vector :as v]))

(v/vec2 3 4)
;; => #vec2 [3.0, 4.0]

(comment
  (require '[nextjournal.clerk :as clerk])
  (clerk/build-static-app! {:out-path "docs" :paths ["src/sometest.clj"]}))

The html is generated and prints nil as a vec2 value:

image

And exception is like:

#error {
 :cause class clojure.lang.Keyword cannot be cast to class java.lang.Character (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Character is in module java.base of loader 'bootstrap')
 :via
 [{:type java.lang.ClassCastException
   :message class clojure.lang.Keyword cannot be cast to class java.lang.Character (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Character is in module java.base of loader 'bootstrap')
   :at [clojure.lang.RT uncheckedIntCast RT.java 1476]}]
 :trace
 [[clojure.lang.RT uncheckedIntCast RT.java 1476]
  [fastmath.vector.Vec2 invoke vector.clj 365]
  [fastmath.vector.Vec2 valAt vector.clj 352]
  [clojure.lang.RT get RT.java 760]
  [nextjournal.clerk.viewer$var_from_def_QMARK_ invokeStatic viewer.cljc 163]
  [nextjournal.clerk.viewer$var_from_def_QMARK_ invoke viewer.cljc 162]
  [nextjournal.clerk.viewer$wrapped_with_viewer invokeStatic viewer.cljc 293]
  [nextjournal.clerk.viewer$wrapped_with_viewer invoke viewer.cljc 280]
  [nextjournal.clerk.viewer$describe$fn__18862 invoke viewer.cljc 375]
  [nextjournal.clerk.viewer$describe invokeStatic viewer.cljc 375]
  [nextjournal.clerk.viewer$describe invoke viewer.cljc 366]
  [nextjournal.clerk.viewer$describe invokeStatic viewer.cljc 372]
  [nextjournal.clerk.viewer$describe invoke viewer.cljc 366]
  [nextjournal.clerk.view$__GT_result invokeStatic view.clj 110]
  [nextjournal.clerk.view$__GT_result invoke view.clj 109]
  [nextjournal.clerk.view$describe_block invokeStatic view.clj 164]
  [nextjournal.clerk.view$describe_block invoke view.clj 151]
  [clojure.core$partial$fn__5859 invoke core.clj 2635]
  [clojure.core$map$fn__5880$fn__5881 invoke core.clj 2746]
  [clojure.lang.PersistentVector reduce PersistentVector.java 343]
  [clojure.core$transduce invokeStatic core.clj 6885]
  [clojure.core$into invokeStatic core.clj 6901]
  [clojure.core$into invoke core.clj 6889]
  [nextjournal.clerk.view$doc__GT_viewer$fn__22748 invoke view.clj 171]
  [clojure.core$update invokeStatic core.clj 6185]
  [clojure.core$update invoke core.clj 6177]
  [nextjournal.clerk.view$doc__GT_viewer invokeStatic view.clj 171]
  [nextjournal.clerk.view$doc__GT_viewer invoke view.clj 167]
  [nextjournal.clerk$file__GT_viewer invokeStatic clerk.clj 274]
  [nextjournal.clerk$file__GT_viewer invoke clerk.clj 271]
  [nextjournal.clerk$file__GT_viewer invokeStatic clerk.clj 273]
  [nextjournal.clerk$file__GT_viewer invoke clerk.clj 271]
  [clojure.core$juxt$fn__5840 invoke core.clj 2598]
  [clojure.core$map$fn__5880$fn__5881 invoke core.clj 2746]
  [clojure.lang.PersistentVector reduce PersistentVector.java 343]
  [clojure.core$transduce invokeStatic core.clj 6885]
  [clojure.core$into invokeStatic core.clj 6901]
  [clojure.core$into invoke core.clj 6889]
  [nextjournal.clerk$build_static_app_BANG_ invokeStatic clerk.clj 380]
  [nextjournal.clerk$build_static_app_BANG_ invoke clerk.clj 366]
  [somtest$eval32629 invokeStatic form-init3528201830096940289.clj 8]
  [sometest$eval32629 invoke form-init3528201830096940289.clj 8]
  [clojure.lang.Compiler eval Compiler.java 7181]
  [clojure.lang.Compiler eval Compiler.java 7136]
  [clojure.core$eval invokeStatic core.clj 3202]
  [clojure.core$eval invoke core.clj 3198]
  [nrepl.middleware.interruptible_eval$evaluate$fn__1231$fn__1232 invoke interruptible_eval.clj 87]
  [clojure.lang.AFn applyToHelper AFn.java 152]
  [clojure.lang.AFn applyTo AFn.java 144]
  [clojure.core$apply invokeStatic core.clj 667]
  [clojure.core$with_bindings_STAR_ invokeStatic core.clj 1977]
  [clojure.core$with_bindings_STAR_ doInvoke core.clj 1977]
  [clojure.lang.RestFn invoke RestFn.java 425]
  [nrepl.middleware.interruptible_eval$evaluate$fn__1231 invoke interruptible_eval.clj 87]
  [clojure.main$repl$read_eval_print__9110$fn__9113 invoke main.clj 437]
  [clojure.main$repl$read_eval_print__9110 invoke main.clj 437]
  [clojure.main$repl$fn__9119 invoke main.clj 458]
  [clojure.main$repl invokeStatic main.clj 458]
  [clojure.main$repl doInvoke main.clj 368]
  [clojure.lang.RestFn invoke RestFn.java 1523]
  [nrepl.middleware.interruptible_eval$evaluate invokeStatic interruptible_eval.clj 84]
  [nrepl.middleware.interruptible_eval$evaluate invoke interruptible_eval.clj 56]
  [nrepl.middleware.interruptible_eval$interruptible_eval$fn__1264$fn__1268 invoke interruptible_eval.clj 152]
  [clojure.lang.AFn run AFn.java 22]
  [nrepl.middleware.session$session_exec$main_loop__1334$fn__1338 invoke session.clj 218]
  [nrepl.middleware.session$session_exec$main_loop__1334 invoke session.clj 217]
  [clojure.lang.AFn run AFn.java 22]
  [java.lang.Thread run Thread.java 833]]}

embedding local image

How to display locally saved image from given folder? I tried (clerk/html [:img {:src path}]) with various paths (relative, absolute, etc.) without any success. I'm getting "rendering error".
I would be happy to see the same behaviour as in nextjournal. When I save the file to defined folder, clerk could infer type and embed this into a notebook.
Additionally build-static-app! should be aware of such files and copy them to the out-path.

Is it somehow possible or planned in the near future?

bb dev fails with `unable to clone [email protected]:nextjournal/clerk.git` error

$ bb dev
yarn install v1.22.10
[1/4] Resolving packages...
success Already up-to-date.
Done in 0.38s.
Cloning: [email protected]:nextjournal/clerk.git
Error building classpath. Unable to clone C:\Users\chaos\.gitlibs\_repos\ssh\github.com\nextjournal\clerk
FATAL ERROR: No supported authentication methods available (server sent: publickey)
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

To reproduce.

  1. (Make sure you deprive yourself of git access to github.com:nextjournal/clerk.git if you happen to be a maintainer)
  2. From a shell, run bb dev

You get the above error.

This is because the deps.edn :sci alias is being called, which has a io.github.nextjournal/viewers dependency on the 1b1374db20b56d14a0f32835c1ce5d9254c23527 git tree

:aliases {:sci {:extra-deps  {applied-science/js-interop  {:mvn/version "0.3.0"}
                               borkdude/sci {:mvn/version "0.2.6"}
                               reagent/reagent {:mvn/version "1.1.0"}
                               io.github.nextjournal/viewers {:git/sha "1b1374db20b56d14a0f32835c1ce5d9254c23527"}
                               metosin/reitit-frontend     {:mvn/version "0.5.15"}}}

whose modules/devdocs/deps.edn is setup with a private git repo https://github.com/nextjournal/viewers/blob/914824bce0240ee57261cfc4011ef7d2aba058a5/modules/devdocs/deps.edn#L9:

{:path ["src"]
 :deps {re-frame/re-frame {:git/url "https://github.com/nextjournal/freerange"
                           :git/sha "8cf68c30722a4c6f8f948a134c900d7a656ecad4"}
        io.github.nextjournal/devcards {:local/root "../devcards"}
        io.github.nextjournal/viewer {:local/root "../viewer"}
        io.github.nextjournal/cljs-extensions {:local/root "../cljs-extensions"}
        io.github.nextjournal/markdown {:local/root "../markdown"}

        com.nextjournal/clerk {:git/url "[email protected]:nextjournal/clerk.git"
                               :git/sha "d2cacf6d3d27768f34b9092c100be9083c6dd1fc"}

        ;; we use shadow-resource directly
        thheller/shadow-cljs {:mvn/version "2.15.12"}
        metosin/reitit-frontend   {:mvn/version "0.5.15"}
        com.lambdaisland/deja-fu {:mvn/version "0.3.33"}
        com.lambdaisland/shellutils {:mvn/version "0.0.10"}
        org.flatland/ordered {:mvn/version "1.15.10"}}}

This has been upgraded since with nextjournal/viewers#31, so any viewers update in clerk's deps.edn from that commit onwards will pick up the updated coords from modules/devdocs/deps.edn:

        io.github.nextjournal/clerk {:git/sha "ebc8f998c761f1b0e2c9fc4750ae87d8f1766faa"}

(i.e. setting the io.github.nextjournal to the latest viewerssha indeps.edn` will fix it:

:sci {:extra-deps  {;;...
                           io.github.nextjournal/viewers {:git/sha "4590d1cf992781924b25f57a3fd54f41abbb9f1e"}
                           ;;...}}

)

I haven't filed a PR because I think is up to the maintainers to choose which viewers version to update clerk to.

Thanks,

nippy doesn't trust Java object passed as argument to function

I'm not sure whether this is a nippy issue or a clerk issue. I decided to start here, but please feel free to let me know that should submit the issue at the nippy repo instead.

I'm using an Apache Commons Java class to generate data for a hanami/vega-lite plot. It works fine if I construct the data with a series of defs. But if I wrap up the process in a function, and pass the Java object as an argument, nippy doesn't like that and turns the Java object into an error map.

The example below should work as given, but if you comment out the expression in the the hanami :DATA line and uncomment the expression after that, you should see the error, with the nippy error map printed to stdout. The issue doesn't have to do with hanami--I can get the same effect by trying to print the map that the function is supposed to return--but I'm providing the hanami example for context.

I went through the little bit of docs I found at nippy repo looking for info on how to tell it to trust what I'm giving it. I didn't see a way to configure that. (Is there more documentation somewhere?) I don't know about the dangers that nippy is trying to protect against, but I can easily imagine that they exist. However, in this case nippy is interfering with a very normal Clojure practice.

Thanks!

Add to project.clj (or translate it as you wish for deps.edn, though I haven't tried that):

[org.apache.commons/commons-math3 "3.6.1"]
[aerial.hanami "0.17.0"]

Here's the MWE:

(ns mwe
    (:require 
      [nextjournal.clerk :as clerk]
      [aerial.hanami.common :as hc]
      [aerial.hanami.templates :as ht])
    (:import
      [org.apache.commons.math3.distribution LevyDistribution]))

(defn add-xy-labels
  [xs ys label]
  (map (fn [x y] {"x" x, "y" y, "label" label})
    xs ys))

(def scale 50)
(def dist (LevyDistribution. 0 scale))
(def label (str "scale=" scale))
(def xs (range 1 101))
(def ys (map (fn [x] (.density dist x)) xs)) ; y's are probabilities of x

;; I want to do the above a lot, so why not automate it?
(defn dist-plot-data
  [dist scale num-points]
  ; clerk is passing a nippy error map instead of LevyDistribution:
  (println "\ndist is a" (class dist) "\n" dist)
  (let [xs (range 1 (inc num-points))]
    (add-xy-labels xs (map (fn [x] (.density dist x)) xs)
                   (str "scale=" scale))))

;; No error unless I use the result:
(dist-plot-data dist scale 100)

(clerk/vl 
  (hc/xform ht/line-chart
            :DATA (add-xy-labels xs ys (str "scale=" scale)) ; works
                  ;(dist-plot-data dist scale 100) ; fails
            :COLOR "label"
            :YTITLE "p(x)"
            :TITLE "Lévy distribution"
            :HEIGHT 400))

dependency analysis misses a var

https://github.com/behrica/ds-notebooks/blob/23f80d0a426755bca7eb1be5ed8f93e5b014035f/notebooks/kaggle.clj
text block 22 gets analysed incomplete.

(clerk/vl

 {:$schema "https://vega.github.io/schema/vega-lite/v5.json"
  :config {:axis {:grid true :tickBand "extent"}}
  :width 600
  :height 600
  :data {:values (vec pps-scores)}
  :encoding {:x {:field "x" :type "ordinal"}
             :y {:field "y" :type "ordinal"}}
  :layer [{:encoding {:color {:field "pps"
                              :legend {:orient "top"
                                       :direction "horizontal"
                                       :gradientLength 120}
                              :title "PPS"
                              :type "quantitative"}}
           :mark "rect"}
          {:encoding {
                      :text {:field "pps" :type "quantitative"}}
           :mark "text"}]})

The dependency to pps-scores is noit detected, which gives the issues in the hashing.

This code reveals it:

(->
 (clerk/parse-file "notebooks/kaggle.clj")
 :blocks
 (nth 22)
 :text
 read-string
 h/analyze)

as it only detects dependency:

 {:form
     (clerk/vl
      {:$schema "https://vega.github.io/schema/vega-lite/v5.json",
       :config {:axis {:grid true, :tickBand "extent"}},
       :width 600,
       :height 600,
      :data {:values (vec pps-scores)},
       :encoding
       {:x {:field "x", :type "ordinal"}, :y {:field "y", :type "ordinal"}},
       :layer
       [{:encoding
         {:color
          {:field "pps",
           :legend {:orient "top", :direction "horizontal", :gradientLength 120},
           :title "PPS",
           :type "quantitative"}},
         :mark "rect"}
        {:encoding {:text {:field "pps", :type "quantitative"}}, :mark "text"}]}),
     :ns-effect? false,
     :deps #{nextjournal.clerk/vl}}

Error loading more table items when using viewer metadata

Hi,

First of all, thanks for Clerk, its awesome ;)

I've encountered bug, that loading more items in table does not work, when using viewer metadata:

;; ## Given
(def numbers (into [] (for [a (range 100)]
                        [a])))

;; ## Click 'more' for this table works...
(clerk/table numbers)

;; ## .. but clicking more here causes "Error: No protocol method IStack.-peek defined for type object"
^{::clerk/viewer clerk/table}
numbers

I'm using io.github.nextjournal/clerk {:mvn/version "0.5.346"}

Crashes when `:watch-paths` contains a non-existent path.

I'm gonna look at this later and see if I can make this fail more gracefully.

(clerk/serve! {:watch-paths ["src" "notebooks"]
               :browse? true})
               ;; "notebooks" doesn't exist.

Stacktrace

Clerk webserver started on 7777...
Starting new watcher for paths ["src" "notebooks"]
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" Syntax error compiling at (user.clj:4:1).
	at clojure.lang.Compiler.load(Compiler.java:7652)
	at clojure.lang.RT.loadResourceScript(RT.java:381)
	at clojure.lang.RT.loadResourceScript(RT.java:368)
	at clojure.lang.RT.maybeLoadResourceScript(RT.java:364)
	at clojure.lang.RT.doInit(RT.java:486)
	at clojure.lang.RT.init(RT.java:467)
	at clojure.main.main(main.java:38)
Caused by: java.nio.file.NoSuchFileException: notebooks
	at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
	at java.base/sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
	at java.base/sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:149)
	at java.base/java.nio.file.Files.readAttributes(Files.java:1764)
	at java.base/java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219)
	at java.base/java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276)
	at java.base/java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:322)
	at java.base/java.nio.file.Files.walkFileTree(Files.java:2717)
	at java.base/java.nio.file.Files.walkFileTree(Files.java:2797)
	at io.methvin.watcher.PathUtils.recursiveVisitFiles(PathUtils.java:108)
	at io.methvin.watcher.PathUtils.initWatcherState(PathUtils.java:78)
	at io.methvin.watcher.DirectoryWatcher.<init>(DirectoryWatcher.java:190)
	at io.methvin.watcher.DirectoryWatcher$Builder.build(DirectoryWatcher.java:122)
	at nextjournal.beholder$create.invokeStatic(beholder.clj:29)
	at nextjournal.beholder$create.invoke(beholder.clj:20)
	at nextjournal.beholder$watch.invokeStatic(beholder.clj:37)
	at nextjournal.beholder$watch.doInvoke(beholder.clj:31)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$apply.invoke(core.clj:662)
	at nextjournal.clerk$serve_BANG_.invokeStatic(clerk.clj:266)
	at nextjournal.clerk$serve_BANG_.invoke(clerk.clj:243)
	at user$eval12465.invokeStatic(user.clj:11)
	at user$eval12465.invoke(user.clj:11)
	at clojure.lang.Compiler.eval(Compiler.java:7181)
	at clojure.lang.Compiler.load(Compiler.java:7640)
	... 6 more

Error when trying to build-static-app on MS-Windows

Execution error (URISyntaxException) at java.net.URI$Parser/fail (URI.java:2974).
Illegal character in path at index 12: public/build\index.html

How to reproduce

  1. Open a REPL
  2. (require '[nextjournal.clerk :as clerk])
  3. (clerk/build-static-app! {:paths ["notebooks/hello.clj"]})

This is due to the fn trying to coerce a windows path into a URL.

PR to follow.

Rendering numbers and strings

Two minor issues.

  • floating point number with fractional part = 0 is rendered as an integer
  • strings are double quoted (ok) but in tables are not
[1.0 2.0 3.0 4.5 5.0]
;; [1 2 3 4.5 5]
;; should be [1.0 2.0 3.0 4.5 5.0]

(map str [1.0 2.0 3.0 4.5 5.0])
;; ("1.0" "2.0" "3.0" "4.5" "5.0")

(clerk/table
 [[[1.0 2.0 3.0 4.5 5.0]]
  [(map str [1.0 2.0 3.0 4.5 5.0])]])
;; [1 2 3 4.5 5]
;; (1.0 2.0 3.0 4.5 5.0) ;; should be ("1.0" "2.0" "3.0" "4.5" "5.0")

Does not work when no internet

What do you think about embedding all the JS/CSS deps? The main reason why I ask about that is that at the moment it is not possible to load the clerk page when there is no internet connection 😬

Screen Shot 2022-03-14 at 16 05 37

how to hide result from hidden form?

Having ^{:nextjournal.clerk/visibility #{:hide}} or ^{:nextjournal.clerk/visibility #{:fold}} the form is hidden/folded but still result of evaluating it is rendered (which is confusing). Is there any way to hide result as well?

hashing inconsistent when form contains regexp

(require '[nextjournal.clerk.hashing :as h])
(frequencies (map (comp second vals h/hash h/build-graph h/parse-clojure-string)
                  (repeat 100 "(fn [x] (clojure.string/split x #\"/\"))")))

;;=>  {"5drKysDWADNZJWwWfgWYs6TETrz7zx" 98, 
;;     "5dt92a8LBSfEScpUk3X7Y1L3vBDKJm" 2}

cc @behrica and thanks for bringing this up!

Can't get visible measure units for X axis with clerk/plotly

Hello,

I'm playing with clerk/plotly

(clerk/plotly
   {:data [{:y [1 245 3 564 56]
            :x [1 2 3 4 5]
            :type "scatter"
            :line {:shape "spline"}}]
    :layout {:title "title"
             :xaxis {:title "x axis" :showline true}
             :yaxis {:title "y axis" :showline true}
             }})

and I don't see measure units for X Axis (for Y Axis they are visible as you see):
image

Am I missing some configuration or this is a bug? Please help.

Clerk Dash?

The notebook format is nice and pairs well with the linear model of code files but large notebooks also lead to a lot of scrolling. When the end-user is non technical code viewers are a distraction and the narrow width of the notebook also doesn't make use of available screen real estate. So I'm wondering: what if clerk had a viewers-only (all but code and comment based markdown) dashboard layout?

The dashboard layout could be specified on a code file as namespace metadata, including the format, e.g. number of grid columns or flex model. Using metadata individual viewers can then specify their placement.

Files could potentially be viewed either as a dashboard or regular notebook.

Remote clerk

In order to collect insights from production services I'd like to have a local clerk process (or server) running that eval's on a remote machine. This is analogous to a remote REPL where I send code to eval to a remote machine and receive the results locally in my IDE.

Is this possible today with clerk? If not what needs to be done to enable this?

changing deftype during session produces caching errors

When starting with simple type definition and progressively adding some interfaces casues caching errors and some exceptions.

Step 1 - create notebook with the following

(deftype MyType [a b])
(MyType. 1 2)
;; => #object[sometest.MyType 0x57c6a598 "sometest.MyType@57c6a598"]

Step 2 - add toString

(deftype MyType [a b]
  Object
  (toString [_] (str "#MyType " [a b])))

(MyType. 1 2)
;; => #object[sometest.MyType 0x1987e9b3 "#MyType [1 2]"]

Which causes nippy problems

image

Step 3 - add Seqable interface

(deftype MyType [a b]
  Object
  (toString [_] (str "#MyType " [a b]))
  clojure.lang.Sequential
  clojure.lang.Seqable
  (seq [_] (seq [a b])))

(MyType. 1 2)
;; => #object[sometest.MyType 0x6fd2252 "#MyType [1 2]"]

An exception is thrown:

1. Unhandled java.lang.AbstractMethodError
   Receiver class sometest.MyType does not define or inherit an
   implementation of the resolved method 'abstract clojure.lang.ISeq
   seq()' of interface clojure.lang.Seqable.

                   RT.java:  543  clojure.lang.RT/seqFrom
                   RT.java:  537  clojure.lang.RT/seq
                  core.clj:  139  clojure.core/seq
                  core.clj: 7392  clojure.core/bounded-count
                  core.clj: 7386  clojure.core/bounded-count
               viewer.cljc:  320  nextjournal.clerk.viewer$bounded_count_opts$fn__18923/invoke
               viewer.cljc:  320  nextjournal.clerk.viewer$bounded_count_opts/invokeStatic
               viewer.cljc:  317  nextjournal.clerk.viewer$bounded_count_opts/invoke
               viewer.cljc:  418  nextjournal.clerk.viewer$describe/invokeStatic
               viewer.cljc:  366  nextjournal.clerk.viewer$describe/invoke
               viewer.cljc:  372  nextjournal.clerk.viewer$describe/invokeStatic
               viewer.cljc:  366  nextjournal.clerk.viewer$describe/invoke
                  view.clj:  110  nextjournal.clerk.view/->result
                  view.clj:  109  nextjournal.clerk.view/->result
                  view.clj:  164  nextjournal.clerk.view/describe-block
                  view.clj:  151  nextjournal.clerk.view/describe-block
                  core.clj: 2635  clojure.core/partial/fn
                  core.clj: 2746  clojure.core/map/fn/fn
     PersistentVector.java:  343  clojure.lang.PersistentVector/reduce
                  core.clj: 6885  clojure.core/transduce
                  core.clj: 6901  clojure.core/into
                  core.clj: 6889  clojure.core/into
                  view.clj:  171  nextjournal.clerk.view/doc->viewer/fn
                  core.clj: 6185  clojure.core/update
                  core.clj: 6177  clojure.core/update
                  view.clj:  171  nextjournal.clerk.view/doc->viewer
                  view.clj:  167  nextjournal.clerk.view/doc->viewer
                 clerk.clj:  274  nextjournal.clerk/file->viewer
                 clerk.clj:  271  nextjournal.clerk/file->viewer
                 clerk.clj:  273  nextjournal.clerk/file->viewer
                 clerk.clj:  271  nextjournal.clerk/file->viewer
                  core.clj: 2598  clojure.core/juxt/fn
                  core.clj: 2746  clojure.core/map/fn/fn
     PersistentVector.java:  343  clojure.lang.PersistentVector/reduce
                  core.clj: 6885  clojure.core/transduce
                  core.clj: 6901  clojure.core/into
                  core.clj: 6889  clojure.core/into
                 clerk.clj:  380  nextjournal.clerk/build-static-app!
                 clerk.clj:  366  nextjournal.clerk/build-static-app!
                      REPL:   15  sometest/eval25870

Link to non-root git repo in static build

The link generated in the static build (clerk/build-static-app!) of a notebook assumes the deps.edn file is located at the root location of the git repo. Please add support for an optional attribute to specify a non-root location.

I've created a pull request that implements a simple fix #98 using the attribute :deps/root, to mirror the deps.edn dependency coordinate attribute for a non-root file location.

rendering keywords with spaces fails

rendering this:

[(hash-map (keyword "N hello") [1 2 3])]

in clerk fails with

{
    "message": "The map literal starting with :path contains 7 form(s). Map literals must contain an even number of forms.",
    "data": {
        "meta": null,
        "O": 3,
        "K": [
            {
                "Lb": null,
                "name": "type",
                "X": "type",
                "$d": 1174270348,
                "I": 2153775105,
                "S": 4096
            },
            {
                "Lb": "edamame",
                "name": "error",
                "X": "edamame/error",
                "$d": 882529634,
                "I": 2153775105,
                "S": 4096
            },
            {
                "Lb": null,
                "name": "line",
                "X": "line",
                "$d": 212345235,
                "I": 2153775105,
                "S": 4096
            },
            1,
            {
                "Lb": null,
                "name": "column",
                "X": "column",
                "$d": 2078222095,
                "I": 2153775105,
                "S": 4096
            },
            98
        ],
        "N": null,
        "I": 16647951,
        "S": 139268
    },
    "Ae": null,
    "name": "Error",
    "stack": "Error: The map literal starting with :path contains 7 form(s). Map literals must contain an even number of forms.\n    at new bq (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2202:26)\n    at Function.cq.j (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2204:71)\n    at Function.cq.h (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2204:30)\n    at gS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2493:353)\n    at Pca (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2507:68)\n    at oS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2514:395)\n    at pS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2521:363)\n    at jS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2520:243)\n    at iS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2494:249)\n    at oS (https://storage.googleapis.com/nextjournal-cas-eu/data/8VwU4isthVmn9MopRQfnkSALhbgqYxLqKo5kcDegZMKgH38QUQUt7keu6sqPqCKbv8RgjwnVBvLu96uaQ8xhGQ1sSH:2514:362)"
}

Images do not nest in html

The following example:

(ns images
  (:require [nextjournal.clerk :as clerk]))

(import javax.imageio.ImageIO
        java.net.URL)

(clerk/html
 [:div
  [:h1 "image1"]
  (ImageIO/read (URL. "https://images.unsplash.com/photo-1532879311112-62b7188d28ce?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8"))
  [:h1 "image2"]
  (ImageIO/read (URL. "https://images.unsplash.com/photo-1532879311112-62b7188d28ce?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8"))])

results in the following:
clerk-example

Thanks!

qualified symbol renders without a namespace

Quoted symbol (or java static variable) is rendered without a namespace. Syntax quote is not rendered at all.

(import 'java.awt.Color)

Color/PINK ;; #object[java.awt.Color 0x363b171d "java.awt.Color[r=255,g=175,b=175]"]
'Color/PINK ;; Color/PINK
`Color/PINK ;; java.awt.Color/PINK
clerk/vl ;; #'nextjournal.clerk.viewer/vl
'clerk/vl ;; clerk/vl
`clerk/vl  ;; nextjournal.clerk/vl

Can't define a record named `Node`

(defrecord Node [v l r])
(->Node 1 nil nil)

will fail with the following error:

Unhandled clojure.lang.ArityException
Wrong number of args (3) passed to: clojure.lang.PersistentArrayMap

AFn.java: | 429 | clojure.lang.AFn
AFn.java: | 40 | clojure.lang.AFn
NO_SOURCE_FILE: | 0 | day-0003/eval16646 // <-- my journal
NO_SOURCE_FILE: | -1 | day-0003/eval16646
Compiler.java: | 7181 | clojure.lang.Compiler
Compiler.java: | 7136 | clojure.lang.Compiler
core.clj: | 3202 | clojure.core/eval
core.clj: | 3198 | clojure.core/eval
clerk.clj: | 104 | nextjournal.clerk/eval+cache!/fn--15074
clerk.clj: | 104 | nextjournal.clerk/eval+cache!
clerk.clj: | 103 | nextjournal.clerk/eval+cache!
clerk.clj: | 155 | nextjournal.clerk/read+eval-cached
clerk.clj: | 129 | nextjournal.clerk/read+eval-cached
clerk.clj: | 177 | nextjournal.clerk/eval-analyzed-doc/fn--15108
PersistentVector.java: | 343 | clojure.lang.PersistentVector
core.clj: | 6829 | clojure.core/reduce
core.clj: | 6812 | clojure.core/reduce

Totally fine with

(defrecord Nod [v l r])
(->Nod 1 nil nil)

And works great in a REPL

;;Loading daily_coding_problem/day_0003.clj... done
(in-ns 'day-0003)
=> #object[clojure.lang.Namespace 0x7dfb9dd "day-0003"]
(->Node 1 2 3)
=> #day_0003.Node{:val 1, :left 2, :right 3}

Further in my session I got this error

Multiple definitions found in form, using first one:
:form (defrecord Nod [val left right]) :used-var day-0003/->Nod

So I suppose this something weird about records in general.

Question: Width and Position of VL Images

I saw this closed issue and have tried

(merge
  {:nextjournal/width :full}
(clerk/vl .....

which does set the full width correctly for the image but the image is not centred.

Is there a way to control the width and keep the vl image in the centre ?

Cheers

nextjournal.clerk/build-static-app! tries to open browser

running nextjournal.clerk/build-static-app! always tries to open a browser at http://localhost:7778/build

As I am not developing clerk itself, I never have something running on that port.
I think the "build-static-app" should not assume that there is a server running, and therefore not try to open localhost:7778

Include file in repo that contains CAS-hash to JS asset

This solves the problem of not having to change the source in CI when building the JS.

  • This hash can be calculated fast using dejavu + bb and will be populated on every commit using a git pre-commit hook
  • The JS asset itself will be built on CI and uploaded using this hash
  • For local development, the hash will be ignored and the locally built JS will be used.

This change is necessary to reliably test changes in the UI, see #97

/cc @mk

TODO:

  • upload static asset via dejavu using 512 hash
  • update lookup link to point to 512 hash

Better linting for render-fns

As discussed on Slack, but posting here to keep better track of this issue:

The :render-fn option is a quoted expression that is passed from the backend to the front-end and evaluated there. Currently when writing render fns, one does not get any linting feedback from clj-kondo since clj-kondo treats a quoted expression just as a piece of data without any special meaning.

Using a clj-kondo hook, it is possible to restore linting feedback. But clj-kondo hooks need something to hook on to. One solution could be to introduce a "marker" macro or even just a function, clerk/render-fn which just returns what you pass to it:

:render-fn (clerk/render-fn '(fn [_] ...))

so clj-kondo is able to "hook" on to this expression and can transform it to an unquoted expression, so you get proper linting. Preserving the quoted expression as it is within the call to render-fn takes care of "false positives" in Cursive or other editors that have their own way of looking at the code.

To resolve the usage of the alias v users can introduce a synthetic alias using [nextjournal.viewer :as-alias v] which will not require that dependency to be available at runtime but will help clj-kondo understand what the user means with v/....

Both things (render-fn + :as-alias) are completely optional but do allow for better linting for users that want to have that.

Screen Shot 2022-02-28 at 15 50 11

Screen Shot 2022-02-28 at 15 56 45

The above screenshots were made with the branch clj-kondo-lint-fn.

No headers in `clerk/table`

I'm currently using Clerk 0.2.214.

If I execute the following contrived example, I get the following results.

(clerk/table
 [{:shape "round"
   :color "blue"}
  {:shape "square"
   :color "blue"}
  {:shape "square"
   :color "red"}])

image

I would expect to see "shape" and "color" as the bolded headers of this table. This issue was not present when I was using version 0.1.164 a few weeks ago.

Could not find option with name engine.WarnInterpreterOnly.

$ clj
Clojure 1.10.3
user=> (require '[nextjournal.clerk :as clerk])
user=> Execution error (IllegalArgumentException) at com.oracle.truffle.polyglot.PolyglotEngineException/illegalArgument (PolyglotEngineException.java:128).
Could not find option with name engine.WarnInterpreterOnly.

This is discussed here but I'm not able to follow the details.

https://clojurians-log.clojureverse.org/graalvm/2021-12-08

I did sort it out by upgrading everything (though I still get the problem with Lein). I'd suggest adding clarification about versions for dependencies to the docs.

Custom CSS replaces existing one instead of enhancing it.

Calling below:

(swap! config/!resource->url assoc "/css/viewer.css" "notebooks/custom.css")

works as (probably) intended. However I don't think anyone wishes to build complete CSS for Clerk from the scratch. I think typical use case would be just to add/replace some (small) CSS subset. A couple of classes/ids.

What do you think?

Configure width of output?

It seems as if clerk sets the width of the output in the browser to about 800 pixels for text, and about 700 pixels for Vega-Lite images. The images scroll, which is good, but I'd like to be able to make the displayed image width wider. I wouldn't mind being able to do the same with text, too, but I'm more concerned about Vega-Lite width.

i.e. it would be useful to be able to configure the output widths in the browser. Maybe there's a variable or function to do that already. I've looked for it in the docs and source, but haven't found it.

After working on this for a bit, I don't think the width constraint is coming from Vega-Lite or Hanami (which is what I'm using to generate Vega-Lite). I think the width constraint is either in some CSS used by clerk, or some of other code used to generate the output--either in clerk or a library it uses. I looked at the generated HTML in the browser, but it's not very informative. I think it comes from this function. There's a "clerk" div--maybe that's what the width constraint is attached to--but I don't know how to figure out how that's happening. I looked at the CSS files that that function uses, but nothing in them that I could recognize was the source of the width constraint. But I don't know CSS well, even for formatted code.

Thanks. Not urgent, but it would be nice to be able to configure this.

Feature request: add MS-Windows platform to CI test runs

Hi,

there have been a few cross platform compatibility issues that prevented running clerk on MS-Windows.

Since we have a couple of such fixed submitted so far with cross platforms tests, it will be nice to add MS-Windows as a platform in CI tests to cover them.

PR to follow.

Thanks

clerk can fail to visualize file when editing with Emacs on MS-Windows

Hi,

Clerk can fails to properly watch on file changes when using Emacs on MS-Windows, either with Emacs complaining that it can't unlock the file it is trying to save or with the following error message on the web browser:

Unhandled clojure.lang.ExceptionInfo
Could not resolve var: xxx
{:var chaos}

validate.clj: | 28 | clojure.tools.analyzer.passes.jvm.validate/eval10305/fn--10307
-- | -- | --
MultiFn.java: | 229 | clojure.lang.MultiFn
validate.clj: | 265 | clojure.tools.analyzer.passes.jvm.validate/validate
validate.clj: | 240 | clojure.tools.analyzer.passes.jvm.validate/validate
Var.java: | 384 | clojure.lang.Var
passes.clj: | 166 | clojure.tools.analyzer.passes/compile-passes/fn--8604/fn--8609
passes.clj: | 168 | clojure.tools.analyzer.passes/compile-passes/fn--8604/fn--8611
passes.clj: | 168 | clojure.tools.analyzer.passes/compile-passes/fn--8604/fn--8611
passes.clj: | 168 | clojure.tools.analyzer.passes/compile-passes/fn--8604/fn--8611
core.clj: | 2635 | clojure.core/partial/fn--5859
...

To reproduce using Emacs and Cider

  1. git clone https://github.com/nextjournal/clerk.git
  2. open clerk/notebooks/dice.clj in Emacs
  3. M-x cider-clj-jack-in RET clojure-cli
  4. insert the following somewhere into dice.clj but do not save the file
(comment
  (clerk/serve! {:browse? true})
  (clerk/serve! {:watch-paths ["notebooks"]})
)
  1. Eval the first form, a browser should open at localhost:7777
  2. Eval the second form, it should start watching for changes in the notebooks dir where dice.clj lives
  3. Try to save dice.clj, you either get the above error in the web browser or Emacs will complain it can't unlock the dice.clj file and nothing happens on the web page (the file stays unsaved).

The root cause for this is that Emacs creates a complimentary lock file named .#dice.clj alongside dice.clj to check for multiprocess access to the main file. It so happens that the lock file on MS-Widnows is a real file (rather than a symbolic link) with contents the name of the user and the process id that holds the lock. clerk might try to display the lock file (since it ends in .clj) and thus the error described earlier.

PR to follow.
Thanks,

Watcher does not refresh file on change event on OS X

Hi,

there appears to be a recent issue on OS X that watcher fails to load file after it has been modified.

to reproduce

  1. Bring up the REPL and watch the notebooks folder
(require '[nextjournal.clerk :as clerk])
(clerk/serve! {:watch-paths ["notebooks"]})
  1. Open http://localhost:7777. and back at the REPL do
(clerk/show! "notebooks/hello.clj")
  1. The hello.clj should be shown at the browser.
  2. Add something to the hello.clj file and save it.
  3. The page should refresh with the addition, but nothing happens.

PR to follow.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.