portal 2024-11-15

Hi \o Let's say I have some atom in clj that I would like to keep in sync with a portal (e.g. for a real-time audio data chart similar to https://clojurians.slack.com/archives/C053Q1QSG/p1731004686632579), does Portal have some out-of-box functionality for this? I could use websockets or even even a REST API by creating some custom viewers with the nice nREPL integration Portal provides (thanks for that!!), but maybe something is already in place \o And thanks for this project, it's wonderful =D

✅ 1

But this re-renders the whole list, right? Let's say I need to do it 60 or 30 times a second, I'd like to know the fastest way Portal provides (maybe via websocket) to send data from the server to the client in a raw way and then, in cljs, I could listen to that and do whatever I need to do

Ah. You want to update a single atom, not the whole Portal atom. Not sure about that.

If you tap an atom and then reset! or swap! it, that change will show in Portal.

☝️ 1

But no. I don't know what might be faster than the async update of the tapped value to its targets.

Ok ok, yeah, I will use some custom solution then, thanks \o

Something like below takes from 2s to 9s, Portal is doing lots of work to render things correctly, will update this thread if I have anything working on my side \o

(time
   (doseq [_ (range 60)]
     (swap! portal inc)))

One thing you might be running up against here is that portal will only notify the UI when an atom has changed if its value has been stable for at least 100ms, this is to avoid spamming the UI with updates that won't matter for too long: https://github.com/djblue/portal/blob/master/src/portal/runtime.cljc#L147-L151

I could see a world where this is configurable per session and than any atoms coupled to that instance are free to spam the UI at a higher frequency 👌

You could try this locally with

(in-ns 'portal.runtime)
(defn- invalidate [session-id a old new]
  (when-not (= (value->key old) (value->key new))
    (set-timeout
     #(when (= @a new) (notify session-id a))
     0)))

Thanks, man! Let me try it now

I've tried it in eval-str and it's saying that value->key does not exist, also tried requiring portal.runtime and using namespaced functions, but same error

Ohh, you need to run this code on the jvm

Oh ok, I see heueahuaehu nice, let me see

Yeah, it's not helping, I've put a print inside the new invalidate, but it's not showing up. But don't worry, my use case is not common (game framework that would use Portal as one of the tools), I will check another way to do it without having to disrupt the Portal runtime eahaehuauhe o/

Thanks, man

Not sure where you are expecting the print to show up, but I do see values updating more quickly locally.

Ok, so if you run

(do (in-ns 'portal.runtime)
      (defn- invalidate [session-id a old new]
        (println :aa)
        (when-not (= (value->key old) (value->key new))
          (set-timeout
           #(when (= @a new) (notify session-id a))
           0))))
, do you see :aa in the console log?

If you don't mind, how much time does the code below takes to you?

(time
   (doseq [_ (range 60)]
     (swap! portal inc)))

After you do (reset! portal 0) ofc o/

I've see :aa once in the console log after I have Portal restarted. Maybe the bottleneck is somewhere else?

I tested it with the following

(do
  (def a (atom 0))
  (tap> a)
  (doseq [n (range 10)]
    (reset! a n)))

Ahhh ok, I was doing reset! on a portal following Porter's link and thought he was referring to the Portal's atom, awesome! I haven't seen this info in the manual yet (I've read it some time ago TBH), will check this there \o Thank you, Chris, thank you Porter, I'm good then =D

It's working really fast now \o/ Appreciate it

Another QQ, to get a reference to the view and changeset components of a vega-lite obj (e.g. as in https://clojurians.slack.com/archives/C035GRLJEP8/p1664893250276169?thread_ts=1664723778.282809&cid=C035GRLJEP8), I would have to create my own viewer or maybe https://github.com/djblue/portal/blob/04bf786a909fe881753c00403adaca231fc4d536/src/portal/ui/viewer/vega.cljs already exposes that somehow?

✅ 1

I think for this you might want to register your own viewer that re-used the vega reagent component 🤔

Yeah, I've tried to eval https://github.com/djblue/portal/blob/04bf786a909fe881753c00403adaca231fc4d536/src/portal/ui/viewer/vega.cljs locally (removed clojure.spec.alpha as it was not finding it), but I'm having some cyclic dependency issues, but I will try to make my own viewer \o Thanks

I wouldn't eval the file, instead I would simply require the namespace as it's already bundled in the sci runtime https://github.com/djblue/portal/blob/master/src/portal/ui/repl/sci/libs.cljs#L100-L101

Yeah, I'm doing it to use this code as a base, but don't worry, I am able now to download scripts à la carte by using the code below and can modify the viewer \o

(defn add-script! [src]
  (let [script (js/document.createElement "script")]
    (.setAttribute script "src" src)
    (js/document.head.appendChild script)))

(run! add-script!
      [""
       ""
       ""])

If you want references the underlying js libs, you can also require those as well: https://github.com/djblue/portal/blob/master/src/portal/ui/repl/sci/libs.cljs#L53-L59

(portal.api/eval-str "(require '[\"vega\" :as vega]) (js->clj vega)")

Thanks \o We can consider this resolved then, I guess I have a clear path now =D Appreciate it, Chris o/

1