portal

Sam Ritchie 2023-06-01T13:11:03.187639Z

@djblue question — where does the code live that makes the magic possible, for SCI to go find NPM code or cljs code like mafs?

djblue 2023-06-01T15:30:59.650299Z

Are you running into issues with the code?

Sam Ritchie 2023-06-01T15:35:00.397339Z

I was interested for @mkvlr and Clerk

Sam Ritchie 2023-06-01T15:35:18.719439Z

Since this is such a nice way to extend the system with no custom build

djblue 2023-06-01T15:43:21.953669Z

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 2023-06-01T15:47:07.338209Z

Then eval :cljs/quit to drop the repl back into clj mode

mkvlr 2023-06-01T16:13:42.260659Z

@djblue very cool 👏

🙏 1
Sam Ritchie 2023-06-01T19:51:39.399119Z

@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.

Sam Ritchie 2023-06-01T19:53:55.100859Z

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 renderer

djblue 2023-06-01T19:59:38.857989Z

So, 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)

djblue 2023-06-01T20:02:14.085179Z

This will return a promise and deal with serialization both ways

djblue 2023-06-01T20:04:14.884179Z

If you dont want to deal with the rpc stuff, adding the pregenerated latex string as metadata on the list might be easier

Sam Ritchie 2023-06-01T20:04:36.042289Z

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) %)})

Sam Ritchie 2023-06-01T20:05:12.291299Z

I stick a transform on as metadata and then apply it before serializing the value out to Clerk if that metadata entry exists

Sam Ritchie 2023-06-01T20:05:39.163169Z

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)

djblue 2023-06-01T20:06:02.614019Z

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

djblue 2023-06-01T20:06:48.894009Z

(portal.ui.rpc/call (:transform (meta value)) value) might work 🤔

Sam Ritchie 2023-06-01T20:08:10.028289Z

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/

Sam Ritchie 2023-06-01T20:08:10.821719Z

?

djblue 2023-06-01T20:09:07.890699Z

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 👌

djblue 2023-06-01T20:09:49.206839Z

So when the host runtime goes to call apply, the fn should show back up for the party 🎉

Sam Ritchie 2023-06-01T20:10:36.536539Z

wild if that does the trick

djblue 2023-06-01T20:11:15.928909Z

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

Sam Ritchie 2023-06-01T20:14:23.067609Z

okay very cool. I probably need to just go try this now!

Sam Ritchie 2023-06-01T20:14:51.633629Z

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

💯 1
Sam Ritchie 2023-06-02T03:50:33.699139Z

@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 see

Sam Ritchie 2023-06-02T03:51:03.192599Z

I 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):

Sam Ritchie 2023-06-02T03:55:50.420669Z

(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)})

Sam Ritchie 2023-06-02T03:56:41.080699Z

I’m not seeing anything… I am sure I’m making some simple mistake

Sam Ritchie 2023-06-02T03:56:59.036679Z

tapping my submit vs p/submit is not a good solution of course

djblue 2023-06-02T03:58:00.011669Z

The rpc call is going to yield a promise which is probably making reagent/react unhappy

djblue 2023-06-02T03:58:49.398009Z

I think you might need to use a react/useEffect to make the rpc call and .then the promise

Sam Ritchie 2023-06-02T04:00:49.654819Z

interesting, and then eval the result of that call, and presumably I can pass that out as a component…

👍 1
Sam Ritchie 2023-06-02T04:01:11.219039Z

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

Sam Ritchie 2023-06-02T04:03:46.136479Z

I’ll give that a try in the AM, thank you!

👍 1
djblue 2023-06-02T04:05:27.751789Z

If you have a branch, I'd also be happy to help 👌

Sam Ritchie 2023-06-02T04:08:39.882049Z

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)

Sam Ritchie 2023-06-02T04:09:22.129979Z

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!

👍 1
Sam Ritchie 2023-06-02T16:24:57.551479Z

@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-L56

djblue 2023-06-02T17:30:00.448559Z

I 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

💯 1
djblue 2023-06-02T18:18:27.169269Z

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])

Sam Ritchie 2023-06-02T18:34:59.183099Z

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

djblue 2023-06-02T19:01:28.719569Z

I think https://github.com/mentat-collective/emmy-viewers/pull/27 should be able to get you going 👌

Sam Ritchie 2023-06-02T19:20:23.819039Z

Amazing!!! I will try this shortly!