This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-06-01
Channels
- # announcements (20)
- # babashka (3)
- # beginners (30)
- # calva (28)
- # cider (3)
- # circleci (4)
- # clerk (27)
- # clj-kondo (72)
- # cljdoc (15)
- # cljs-dev (1)
- # clojure (85)
- # clojure-europe (37)
- # clojure-nl (1)
- # clojure-norway (13)
- # clojure-spec (7)
- # clojurescript (19)
- # clr (1)
- # conjure (11)
- # datahike (2)
- # datomic (11)
- # emacs (26)
- # events (4)
- # hoplon (35)
- # hyperfiddle (41)
- # jobs (7)
- # lsp (10)
- # nrepl (3)
- # off-topic (57)
- # portal (47)
- # practicalli (1)
- # rdf (3)
- # reitit (21)
- # releases (1)
- # testing (6)
- # tools-build (16)
- # wasm (1)
- # xtdb (16)
@djblue question — where does the code live that makes the magic possible, for SCI to go find NPM code or cljs code like mafs?
The code lives in two files: • https://github.com/djblue/portal/blob/master/src/portal/ui/load.cljs • https://github.com/djblue/portal/blob/master/src/portal/runtime/npm.cljc
I was interested for @U5H74UNSF and Clerk
Since this is such a nice way to extend the system with no custom build
Gotcha, btw there is also portal.api/repl which will convert your nrepl repl eval to use portal.api/eval-str when evaluating code 👌
@djblue one more for you. is there some way that I can have portal perform some transform server-side before I pass the object to the browser?
For example, if I have (+ 'x 'y)
in Emmy, that evaluates to an Expression
that wraps the list '(+ x y)
.
If I want to make a LaTeX viewer, I have to first call emmy.env/->TeX
to get a string on the server side, and then my viewer can handle rendering the string using katex or mathjax or something.
or for mafs, one trick I’ve been using in Clerk is the following: this form
(mafs/of-x sin)
evaluates to
'[mafs.plot/OfX {:y Math/sin}]
but that will NOT actually work as a reagent component. So I first pass to a function which returns
'[mafs.core/Mafs {}
[mafs.coordinates/Cartesian {}]
[mafs.plot/OfX {:y Math/sin}]]
and THEN passes that along to the reagent / eval rendererSo, since not all runtimes support eval in an easy way, I've opted to expose most functionality via rpc. The good news is that it's equivalent to (apply f & args). In the runtime you can register functions via (portal.api/register! #'my.ns/f) and then in the ui runtime, you can invoke the function via (portal.ui.rpc/call 'my.ns/f & args)
If you dont want to deal with the rpc stuff, adding the pregenerated latex string as metadata on the list might be easier
got it - so in that case, I’d need to find a new approach that doesn’t involve adding a function value as metadata. here’s a better example: I want
(mafs/of-x sin)
to wrap itself up in [mafs [cartesian] …]
like above. but if you nest it, like
(mafs/mafs (mafs/of-x sin) (mafs/of-x cos))
then I don’t want any wrapping.
I’ve been implementing this like
(mafs/of-x sin)
;;=> (with-meta '[mafs.plot/OfX {:y Math/sin}] {:transform #(mafs/mafs (mafs/cartesian) %)})
I stick a transform on as metadata and then apply it before serializing the value out to Clerk if that metadata entry exists
but then if you nest that value I ignore the metadata (actually I go explicitly strip it out so it doesn’t mess with serialization)
I don't do this normally, but that function should be callable via rpc as well since it gets stuffed into the portal cache during serialization
nice, even if it’s a function value under :transform
? I wouldn’t expect that to make it across the wire, but you’re saying something gets across that I can use to make an RPC call/
The value you get in the ui is like a box, which when given back to the host runtime will get the original captured value 👌
So when the host runtime goes to call apply, the fn should show back up for the party 🎉
wild if that does the trick
Also, just as a cool note, when said box gets gc'd in the ui, it will automatically be evicted from the portal cache in the host runtime
okay very cool. I probably need to just go try this now!
I’m trying to tease apart my clerk-specific viewer code from the actual reagent fragment assembly so I can enable portal and clerk separately with most of the same stuff
@djblue okay, I am getting closer… Given something like this (simplified so it doesn’t involve any mafs)
(tap> ^{:transform
(fn [v]
^{:portal.viewer/default :portal-present.viewer/reagent}
[:div "The value is " v])}
[:pre "cake"])
the goal is to seeI have it working with
(defn submit [value]
(if-let [xform (:transform (meta value))]
(submit (xform (vary-meta value dissoc :transform)))
(p/submit value)))
but when I try to do an RPC call from the viewer (snippet and result coming):(p/register-viewer!
{:name ::reagent
:predicate (constantly true)
:component (fn [form]
(eval (list 'fn []
(if-let [xform (:transform (meta form))]
(if (fn? xform)
(portal.ui.rpc/call xform form)))))
form)})
I’m not seeing anything… I am sure I’m making some simple mistake
tapping my submit vs p/submit
is not a good solution of course
The rpc call is going to yield a promise which is probably making reagent/react unhappy
I think you might need to use a react/useEffect to make the rpc call and .then the promise
interesting, and then eval the result of that call, and presumably I can pass that out as a component…
for v1 of this I can also just tell the user that they can’t use this transform feature, so (mafs/of-x sin)
wouldn’t work standalone
https://github.com/djblue/portal/blob/master/src/portal/ui/options.cljs#L24 is an example
I’ll get a repro set up with some simple test cases for us in the AM 🙂
I’ve massaged the clerk support so that I don’t need to ship a clerk dependency anymore in https://github.com/mentat-collective/emmy-viewers; the user can use either emmy.clerk
or emmy.portal
to activate support, and the rest of the library is now ONLY about building these quoted forms that can be ingested by clerk or portal (or other viewer libraries, if those exist now/later)
I would just say “use this branch I’m on” but I’ve got a local/root
dep that I need to back out so this can run easily for you. THANK YOU for the offer, I’ll push something in the AM!
@djblue okay, I’ve got a repro up!
The code is in https://github.com/mentat-collective/emmy-viewers, under the sritchie/mafs
branch.
You should be able to start a REPL with either the :portal
or the :nextjournal/clerk
profile enabled, and then go to dev/examples/portal.clj
for my repro notes:
https://github.com/mentat-collective/emmy-viewers/blob/b77b9a98bf695c1085178af342ec4f6490f09ca2/dev/examples/portal.clj
Things feel pretty good for the code that does NOT need expansion. For the code that does need expansion, I am hitting
;; main.js:3403 Uncaught (in promise) Error: Doesn't support
;; namespace: [object Object] at uh (main.js:3403:404) at
;; main.js:4704:202
Here is my attempt:
https://github.com/mentat-collective/emmy-viewers/blob/b77b9a98bf695c1085178af342ec4f6490f09ca2/src/emmy/portal/viewer.cljs#L38-L56I see, https://github.com/djblue/portal/blob/master/src/portal/ui/state.cljs#L232 is probably the issue, should be an easy fix in portal to support this
Until I get this fixed, you could try the following to get unstuck:
(portal.api/register! #'clojure.core/apply)
(portal.ui.rpc/call 'clojure.core/apply xform [form])
I could also I guess write an “expand” function and rpc call that, basically that’s what you’re suggesting but implementing it outside the viewer code might make things more reusable
I think https://github.com/mentat-collective/emmy-viewers/pull/27 should be able to get you going 👌
Amazing!!! I will try this shortly!