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
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?
where is the leaflet in (hooks/use-promise leaflet) coming from? https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7#file-leaflet-cljs-L7
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?
esm.sh is pretty good with this: https://esm.sh/leaflet@latest
but the second script is being loaded from a gist?
import Vsi from 'vega-spec-injector';
// eslint-disable-next-line import/extensions,import/no-unresolved
import L from 'leaflet';
import { version } from '../package.json';what is "the second script"?
the second one being imported in https://gist.github.com/respatialized/e2e791e6dc5f1addb98c5b121e955ab7#file-leaflet-cljs-L27-L31
oh he already used esm.sh, I see. yeah don't know about this gist :)
first does (js/import " and then (js/import
"
the second one doesn’t look it it would run without processing, right?
and wouldn’t they also run in isolation from each other by default?
getting cors errors when I try to import the second script on the squint playground
@afoltzm try (js/import " I think this brings in vega as well
an earlier version of this script attempted to import leaflet-vega from esm.sh but I forgot to change it back, sorry about that
if you can make an updated version of this to avoid all confusion, I can take a look at the "instance" problem tomorrow.
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.> 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
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.
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, perhapsI 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.
oh, that's probably expected, modules are immutable
shouldn't you be using it like this?
L.vegaLayer(vegaGrapSpec).addTo(map);oh yes, I see
this library mutates L which is the default export from the Leaflet module:
import L from 'leaflet';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
with dynamic import this would be (hooks/use-promise [L (-> (js/import "...") (.then (fn [module] (.-default module))))) (paren nesting may be incorrect)
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)
also when-let doesn't support multiple bindings in clojure
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]nesting the when-let s with 1 binding should work
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
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)
check here with "Specifying dependencies" https://esm.sh/#docs
it might just be better to avoid relying on the mutation that leaflet-vega does indeed
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
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
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?
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
hacking in the script tags may also work of course
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
for both libs
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.
perhaps you can try to simplify the code first, to see if the bug is not in your own code
good news to hear that the instance method works
I also got this hacky thing working but didn't manage to also load leaflet-vega: https://squint-cljs.github.io/squint/?src=KGRlZm4gXjphc3luYyBmb28gW10KICAobGV0IFtyZXNwIChqcy1hd2FpdCAoanMvZmV0Y2ggImh0dHBzOi8vdW5wa2cuY29tL2xlYWZsZXRAMS45LjQvZGlzdC9sZWFmbGV0LmpzIikpCiAgICAgICAgdHh0IChqcy1hd2FpdCAoLnRleHQgcmVzcCkpCiAgICAgICAgbGliIChqcy9ldmFsIHR4dCkKICAgICAgICA7OyByZXNwIChqcy1hd2FpdCAoanMvZmV0Y2ggImh0dHBzOi8vdW5wa2cuY29tL2xlYWZsZXQtdmVnYUAwLjkuMC9kaXN0L2J1bmRsZS5qcyIpKQogICAgICAgIDs7IHR4dCAoanMtYXdhaXQgKC50ZXh0IHJlc3ApKQogICAgICAgIDs7IGxpYiAoanMvZXZhbCB0eHQpCiAgICAgICAgXQogICAgKGpzL2NvbnNvbGUubG9nIGxpYikKICAgIGxpYikpCgooZm9vKQ%3D%3D
this just uses the globals stuff
you can see that L.vega is defined in the console
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:
weird
probably the esm.sh + deps is most promising
( @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)