clerk

Jeremy 2025-03-12T12:13:05.208329Z

Hi all, after serving a notebook, refreshes don't work (404). How can I get some sort of permalink to the notebook? rather than having to go to index and server manually from the editor

respatialized 2025-03-12T20:50:19.276289Z

The new support for (js/import) in notebooks and viewers greatly simplifies the development of viewers that depend on external libs. I've been working on some new Leaflet-based viewers for geographic data, but am having some trouble importing an extension to Leaflet. Here's the https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7 with the examples: the first works, the second encounters a SCI/JS error. The tl;dr: I can load base leaflet just fine, but I encounter an undefined instance method error when trying to use the leaflet-vega library which https://github.com/nyurik/leaflet-vega/blob/main/src/VegaLayer.js the base Leaflet L class with new layer methods. Does this have anything to do with how either SCI or myself loads these libraries?

đź‘€ 1
mkvlr 2025-03-12T21:03:48.339279Z

where is the leaflet in (hooks/use-promise leaflet) coming from? https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7#file-leaflet-cljs-L7

mkvlr 2025-03-12T21:15:22.857329Z

can you get this working with plain js (without clerk)? Not sure how https://raw.githubusercontent.com/nyurik/leaflet-vega/refs/heads/main/src/VegaLayer.js can work using imports? Would this need an import map wich we don’t support yet in clerk?

borkdude 2025-03-12T21:16:59.437799Z

esm.sh is pretty good with this: https://esm.sh/leaflet@latest

mkvlr 2025-03-12T21:18:36.642059Z

but the second script is being loaded from a gist?

mkvlr 2025-03-12T21:18:47.860639Z

import Vsi from 'vega-spec-injector';
// eslint-disable-next-line import/extensions,import/no-unresolved
import L from 'leaflet';
import { version } from '../package.json';

borkdude 2025-03-12T21:19:34.925349Z

what is "the second script"?

mkvlr 2025-03-12T21:20:06.322459Z

the second one being imported in https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7#file-leaflet-cljs-L27-L31

borkdude 2025-03-12T21:20:34.587589Z

oh he already used esm.sh, I see. yeah don't know about this gist :)

mkvlr 2025-03-12T21:20:52.336049Z

first does (js/import "") and then (js/import "")

mkvlr 2025-03-12T21:21:49.246959Z

the second one doesn’t look it it would run without processing, right?

mkvlr 2025-03-12T21:22:26.612009Z

and wouldn’t they also run in isolation from each other by default?

mkvlr 2025-03-12T21:25:05.077829Z

getting cors errors when I try to import the second script on the squint playground

mkvlr 2025-03-12T21:27:47.248239Z

@afoltzm try (js/import "") I think this brings in vega as well

đź‘€ 1
respatialized 2025-03-12T21:29:18.760959Z

an earlier version of this script attempted to import leaflet-vega from esm.sh but I forgot to change it back, sorry about that

borkdude 2025-03-12T21:44:07.510069Z

if you can make an updated version of this to avoid all confusion, I can take a look at the "instance" problem tomorrow.

respatialized 2025-03-13T15:47:28.810209Z

revised viewer:

(defn vega-lite
  [vl-spec]
  (when-let [leaflet-vega
             (hooks/use-promise
              (js/import
               "https://esm.sh/leaflet-vega@0.9.0/es2022/leaflet-vega.mjs"))
             vl-spec vl-spec]
    (let
      [map-div-id (str (gensym))
       m (atom nil)
       attribution
       "© OpenStreetMap"]
      [:div
       {:id    map-div-id
        :ref   (fn [el]
                 (when el
                   (let [vega-layer (.vega js/L (clj->js vl-spec) (clj->js {}))
                         tile-layer
                         (.tileLayer
                          js/L
                          "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png"
                          (clj->js {:attribution attribution}))]
                     (reset! m (.map js/L
                                     map-div-id
                                     {:renderer (.canvas js/L)}))
                     (.setView @m (clj->js [40.690214 -73.986964]) 13)
                     (.addTo tile-layer @m)
                     #_(.addTo vega-layer @m))))
        :style {:height "500px"}}])))
it works to display a basemap when the call to create the vega layer is commented out, but uncommenting the line creating the layer results in a vega is undefined error. I will try to reproduce outside of Clerk to make sure the issue isn’t with the underlying JS library.

borkdude 2025-03-13T15:50:07.422369Z

> revised viewer: can you please make one consistent repository / update your gist, it's easy to make mistakes when copy/pasting snippets back and forth > I will try to reproduce outside of Clerk ok, I'll wait for this then

respatialized 2025-04-09T17:58:27.802209Z

https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7 Following up on this after a while - I updated the gist with a functional example. I also confirmed that the leaflet-vega https://github.com/nyurik/leaflet-vega/blob/main/demo/demo.html works locally (plain JS, outside of Clerk/SCI). I attempted to try porting it to use import instead of <script>, but I couldn't figure out how to do it because I don't understand how JS imports work well enough.

borkdude 2025-04-09T18:00:34.225589Z

so the leaflet-vega value is the problem or also the L value? if you js/console.log those values you might be able to find out more. sometimes modules have a default export which you need to go through, so something like:

L.default
will get you the thing you're looking for, perhaps

respatialized 2025-04-09T18:03:04.730649Z

I did include a js/console.log statement that lists the instance methods for both L and js/L in the leaflet-vega cljs viewer; the console output seems to indicate that the vega method isn't being added to L by leaflet-vega.

borkdude 2025-04-09T18:03:51.244559Z

oh, that's probably expected, modules are immutable

borkdude 2025-04-09T18:05:19.587549Z

shouldn't you be using it like this?

L.vegaLayer(vegaGrapSpec).addTo(map);

borkdude 2025-04-09T18:06:28.210779Z

oh yes, I see

borkdude 2025-04-09T18:07:02.229019Z

this library mutates L which is the default export from the Leaflet module:

import L from 'leaflet';

✔️ 1
respatialized 2025-04-09T18:07:42.183499Z

the README for leaflet-vega is unfortunately https://github.com/nyurik/leaflet-vega/blob/612683768947e752a40afdb717543dffa96d28e5/src/VegaLayer.js#L6 - I may try to manually construct a`VegaLayer` object rather than using the L instance method

borkdude 2025-04-09T18:08:28.011879Z

with dynamic import this would be (hooks/use-promise [L (-> (js/import "...") (.then (fn [module] (.-default module))))) (paren nesting may be incorrect)

borkdude 2025-04-09T18:09:07.586469Z

and that value should be mutated after loading the vega lib. note that loading is async so there may be a point in time that you see the L object without the vega method (so you should probably be defensive about this)

borkdude 2025-04-09T18:10:25.206409Z

also when-let doesn't support multiple bindings in clojure

borkdude 2025-04-09T18:11:28.104239Z

SCI is a bit too sloppy here:

user=> (when-let [a 1 b 2] [a b])
clojure.lang.ExceptionInfo: Could not resolve symbol: b [at <repl>:2:1]

borkdude 2025-04-09T18:12:13.734369Z

nesting the when-let s with 1 binding should work

borkdude 2025-04-09T18:14:07.470859Z

another issue with the esm.sh approach is that the leaflet import may just load another version of Leaflet so it wouldn't mutate the Leaflet module you loaded

borkdude 2025-04-09T18:15:29.263329Z

you could find out in the browser console if it's doing the right thing (to see if the leaflet module is only loaded once)

borkdude 2025-04-09T18:16:35.920049Z

check here with "Specifying dependencies" https://esm.sh/#docs

borkdude 2025-04-09T18:18:21.621829Z

it might just be better to avoid relying on the mutation that leaflet-vega does indeed

respatialized 2025-04-09T18:24:13.646939Z

I don't think that's possible, unfortunately; even the layer class is defined within the scope of L for leaflet-vega https://github.com/nyurik/leaflet-vega/blob/612683768947e752a40afdb717543dffa96d28e5/src/VegaLayer.js#L10

respatialized 2025-04-09T18:27:17.039399Z

https://leafletjs.com/examples/extending/extending-1-classes.html I don't know enough about JS modules to know whether this is considered a good practice according to modern standards, but Leaflet pretty much expects you to extend it in this way, which may make dynamic loading difficult for the reasons you describe

respatialized 2025-04-09T18:29:11.316769Z

should I do an end-run around the dynamic import problem by including leaflet and leaflet-vega in the head of the Clerk doc in the call to alter-var-root! for #'nextjournal.clerk.view/include-css+js I do to add the Leaflet-specific CSS?

borkdude 2025-04-09T18:30:05.232449Z

ok sure, I don't know enough about Leaflet, but there are at least two problems: • when-let should not contain multiple bindings • esm.sh loads dependencies and adds query parameters to them, which make them effectively different modules every time, they aren't global objects so you should look at how to properly combine dependencies in the link I linked earlier

✔️ 1
borkdude 2025-04-09T18:30:36.859169Z

hacking in the script tags may also work of course

borkdude 2025-04-09T18:37:31.083299Z

another hack may be this: do a js/fetch to the umd build (or whatever they call it in JS when they use globals) and call eval on it

borkdude 2025-04-09T18:37:42.017409Z

for both libs

respatialized 2025-04-09T18:43:07.654339Z

I updated the https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7 with your suggested revisions; using the ?deps syntax gets the instance method working, but I now run into a different error: i is undefined.

borkdude 2025-04-09T18:46:42.755559Z

perhaps you can try to simplify the code first, to see if the bug is not in your own code

borkdude 2025-04-09T18:46:53.572729Z

good news to hear that the instance method works

borkdude 2025-04-09T18:52:18.151569Z

this just uses the globals stuff

borkdude 2025-04-09T18:52:34.864409Z

you can see that L.vega is defined in the console

respatialized 2025-04-09T19:02:53.380289Z

In the squint example, I can see it in the autocomplete suggestions but I still get an error about it being undefined when I attempt to call the methods:

borkdude 2025-04-09T19:03:31.168989Z

weird

borkdude 2025-04-09T19:03:45.687209Z

probably the esm.sh + deps is most promising

2025-03-15T10:11:09.749629Z

( @afoltzm We are trying to solve a similar problem in "Kindly-render". Crude attempts: https://github.com/scicloj/kindly-render/blob/91c501f48febb9590d679e854ef4419d910da469/src/scicloj/kindly_render/note/to_hiccup_inline_js.cljc#L40 More refined attempt: https://github.com/scicloj/kindly-render/pull/46 We would be glad for any "solutions" if workable / adaptable outside of Clerk)