This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-23
Channels
- # ai (1)
- # aleph (1)
- # announcements (7)
- # babashka (87)
- # beginners (34)
- # biff (9)
- # clerk (4)
- # clojars (37)
- # clojure (144)
- # clojure-art (12)
- # clojure-europe (13)
- # clojure-nl (1)
- # clojure-norway (4)
- # clojure-uk (2)
- # clr (5)
- # conjure (1)
- # data-science (1)
- # datahike (7)
- # datalevin (6)
- # datomic (13)
- # events (1)
- # fulcro (1)
- # graalvm (5)
- # gratitude (1)
- # honeysql (4)
- # hyperfiddle (122)
- # malli (26)
- # nbb (2)
- # off-topic (16)
- # portal (93)
- # practicalli (1)
- # re-frame (1)
- # reitit (15)
- # releases (3)
- # remote-jobs (1)
- # shadow-cljs (5)
- # tools-deps (6)
- # xtdb (4)
Portal Q! I get that I can build a custom viewer by creating a reagent component. But let’s say I want to use some cljs library like Mafs.cljs
when building the components — is this possible with portal?
The sci implementation in portal should be able to load cljs code from the classpath so in theory this should be possible 👍
Oh got it! I might have to make a custom build that adds all the code I need into the sci context in that case
But that’s very comfortable at this point :)
Mafs.cljs builds on the mafs npm package
So you can pull js directly from node_modules, https://github.com/djblue/portal/tree/master/examples/plotly-viewer is an example of dynamically loading plotly
Oh very cool
I don't think people have used this dependency loading code in portal so it might have issues, but I'm down to fix any issues you run into 👍
Okay I’ll take a look!! That’s awesome, I forgot it might be simpler because we’re staying local vs deploying a bundle
https://cljdoc.org/d/djblue/portal/0.40.0/doc/guides/custom-viewer is some docs on setting the viewer within the Portal ui runtime
https://github.com/djblue/portal/blob/0.40.0/examples/portal-present/src/portal_present/viewer.cljs is a more complete example
(do (portal.api/eval-str
"(ns portal-present.viewer
(:require [portal.ui.api :as p]
[\"mafs\" :as m]
[portal.ui.inspector :as ins]
[reagent.core :as r]))
(defn view-scene [_]
[:> m/Mafs {}
[:> (.-Cartesian m/Coordinates) {}]])
(portal.ui.api/register-viewer!
{:name ::mafs
:predicate (constantly true)
:component view-scene})")
(tap>
^{:portal.viewer/default :portal-present.viewer/mafs}
[]))
this is a “hello world”; I am fairly certain I’m going to need to add any new library I want to use to the SCI context
yeah, I think I’ll need to extend
Show: Project-Only All
Hide: Clojure Java REPL Tooling Duplicates (14 frames hidden)
1. Unhandled clojure.lang.ExceptionInfo
Could not resolve symbol: m/Mafs
{:error
#error {:message "Could not resolve symbol: m/Mafs", :data {:type :sci/error, :line 8, :column 2, :file nil, :phase "analysis"}},
:message "Could not resolve symbol: m/Mafs",
:op :portal.rpc/response,
:portal.rpc/id 39}
launcher.clj: 117 portal.runtime.jvm.launcher/eval-str
launcher.clj: 110 portal.runtime.jvm.launcher/eval-str
api.cljc: 138 portal.api$eval_str/invokeStatic
api.cljc: 122 portal.api$eval_str/invoke
api.cljc: 136 portal.api$eval_str/invokeStatic
api.cljc: 122 portal.api$eval_str/invoke
api.cljc: 134 portal.api$eval_str/invokeStatic
api.cljc: 122 portal.api$eval_str/invoke
REPL: 743 emmy.mafs.core/eval67631
REPL: 743 emmy.mafs.core/eval67631
Compiler.java: 7194 clojure.lang.Compiler/eval
Compiler.java: 7183 clojure.lang.Compiler/eval
Compiler.java: 7149 clojure.lang.Compiler/eval
core.clj: 3215 clojure.core/eval
core.clj: 3211 clojure.core/eval
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn
AFn.java: 152 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
core.clj: 667 clojure.core/apply
core.clj: 1990 clojure.core/with-bindings*
core.clj: 1990 clojure.core/with-bindings*
RestFn.java: 425 clojure.lang.RestFn/invoke
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 437 clojure.main/repl/read-eval-print/fn
main.clj: 437 clojure.main/repl/read-eval-print
main.clj: 458 clojure.main/repl/fn
main.clj: 458 clojure.main/repl
main.clj: 368 clojure.main/repl
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 218 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 217 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 833 java.lang.Thread/run
I first tried to require a cljs
file that was on the classpath. If i do this:
(do (portal.api/eval-str
"(ns portal-present.viewer
(:require [portal.ui.api :as p]
[mafs.core :as m]
[mafs.coordinate :as c]
[reagent.core :as r]))
(defn view-scene [_]
[m/Mafs [m/Cartesian]])
(p/register-viewer!
{:name ::mafs
:predicate (constantly true)
:component view-scene})")
(tap>
^{:portal.viewer/default :portal-present.viewer/mafs}
[]))
I get
1. Unhandled clojure.lang.ExceptionInfo
Could not resolve symbol: reagent/with-let
{:error
#error {:message "Could not resolve symbol: reagent/with-let", :data {:type :sci/error, :line 190, :column 1, :file "file:/Users/sritchie/code/clj/mafs.cljs/src/mafs/core.cljs", :phase "analysis"}},
:message "Could not resolve symbol: reagent/with-let",
:op :portal.rpc/response,
:portal.rpc/id 40}
Ah, there is!
this is with 0.40.0
[""
""
""]
these are the 3 css files
I am puzzled that this is working, but if we can get it going this will be an awesome viewer, I’m really excited
to get this going without the ceremony of a Clerk notebook
I figured out today how to do a decent job of getting values together that describe interactive scenes
to get set up, I followed this: https://github.com/djblue/portal#api after adding this dep:
djblue/portal {:mvn/version "0.40.0"}
the actual viewer I will end up writing is something like an eval-viewer
… where I am going to pass the reagent component in as my value, something like this built server-side:
[mafs.core/Mafs
[mafs.coordinates/Cartesian {}]
[mafs.core/Point {:x 1, :y 2}]]
and then the component will… I guess eval
it directly@U1G869VNV so what was the secret??
I may have to run shortly to take my daughter to the pool, but I am very motivated here
Did you npm install mafs to the root of the repl process? That might be the main difference here if we are using the same version but your Portal is unable to resolve the dependency :thinking_face:
I started my repl via CIDER so I wonder if it has a different root
It’s npm installed for sure
https://github.com/djblue/portal/blob/master/src/portal/runtime/npm.cljc#L53C44-L61 is the npm resolution algo Portal uses :thinking_face:
Clearly it was able to find the mafs.cljs dependency and start loading it, so once I get this solved I bet the rest will work
Probably should just use this in Portal https://github.com/babashka/sci.configs/blob/main/src/sci/configs/reagent/reagent.cljs
In trying to load mafs.macros, I did run into "No matching clause: require-macros" which I think is a limitation in sci :thinking_face:
I have a new version without the macros, I’ll send the branch tonight and more info
(ns mafs.macros
#?(:cljs
(:require [reagent.core])))
(defmacro defcomponent
([sym component]
`(defcomponent ~sym "" {} ~component))
([sym docstring component]
`(defcomponent ~sym ~docstring {} ~component))
([sym docstring attr component]
{:pre [(string? docstring)
(map? attr)]}
(let [m (-> (meta sym)
(merge (assoc attr :doc docstring)))]
`(def ~(with-meta sym m)
(reagent.core/adapt-react-class ~component)))))
worked for me locally(do (portal.api/eval-str
"(ns portal-present.viewer
(:require [portal.ui.api :as p]
[mafs.core :as m]
[mafs.coordinates :as c]
[reagent.core :as r]))
(defn inject [href]
(let [link (.createElement js/document \"link\")]
(set! (.-rel link) \"stylesheet\")
(set! (.-href link) href)
(js/document.head.appendChild link)))
(inject \"\")
(inject \"\")
(inject \"\")
(defn view-scene [_]
[m/Mafs [c/Cartesian]])
(p/register-viewer!
{:name ::mafs
:predicate (constantly true)
:component view-scene})")
(tap>
^{:portal.viewer/default :portal-present.viewer/mafs}
[]))
Works for me with https://github.com/djblue/portal/commit/26188aec167e6e617c5869cbecbcbdba6e9e01a6Also, if you want to load the code from a file you can
(portal.api/eval-str "(require 'portal-present.viewer :reload)")
Do you have other hello world examples? I would like to try some stuff out before I cut a release 👌
Also, I think it might be nice to have some helpers to get resources from node_modules that aren't js files. Would that be something useful to you? That way the viewer could work entirely offline.
Yes I’ll send some this evening, I’m in family maelstrom! I know you’re not pressuring I’m just excited so checking in
I think it would be helpful to get the CSS out, as I think you’re anticipating. Assuming we’d be able to load those in portal and wouldn’t see an error from loading a file://
so anything in https://mafs.mentat.org/ inside of a show-sci
form should work. The only change to make is expanding the mafs
alias into mafs.core
and reagent
into reagent.core
this branch removes all of the macro code, so pulling this in via git dep should solve that issue
I’m running into this error when using portal as a git dep:
(base) [sritchie@wintermute ~/code/clj/emmy-viewers (sritchie/mafs)]$ clj -X:deps prep :aliases '[:nextjournal/clerk :dev]'
Prepping io.github.djblue/portal in /Users/sritchie/.gitlibs/libs/io.github.djblue/portal/26188aec167e6e617c5869cbecbcbdba6e9e01a6
Execution error (FileNotFoundException) at tasks.docs/eval461$loading (docs.clj:1).
Could not locate examples/data__init.class, examples/data.clj or examples/data.cljc on classpath.
Full report at:
/var/folders/xw/0lq56zhn4hb4lknppw_k086c0000gn/T/clojure-4188203586225886789.edn
Execution error (ExceptionInfo) at clojure.tools.deps/prep-libs!$fn (deps.clj:711).
Prep function could not be resolved: tasks.build/prep
I’m going to apply your patch to the clj file inside the 0.40.0 jar 🙂I am trying a local build but running into trouble where I can’t get the prep step to run. I will debug more or maybe wait for a release here, but looks like this is all close to working well
ah, very nice, I use those too and should have checked
we have liftoff
If you find the need to tweak any of the portal code, try (portal.api/open {:mode :dev})
I can get an interactive thing up, but it doesn’t look like reagent atoms are doing their thing inside of portal - probably a mistake on my part
let me know if you think this should work:
first, this:
(portal.api/eval-str
"(ns portal-present.viewer
(:require [portal.ui.api :as p]
[mafs.debug]
[mafs.line]
[mafs.core]
[mafs.coordinates]
[mafs.plot]
[reagent.core :as r]))
(defn inject [href]
(let [link (.createElement js/document \"link\")]
(set! (.-rel link) \"stylesheet\")
(set! (.-href link) href)
(js/document.head.appendChild link)))
(inject \"\")
(inject \"\")
(inject \"\")
(p/register-viewer!
{:name ::mafs
:predicate (constantly true)
:component eval})")
notice that I have :component eval
then this works:
(-> '[mafs.core/Mafs [mafs.coordinates/Cartesian]]
(with-meta {:portal.viewer/default :portal-present.viewer/mafs})
(tap>))
a more complex one also looks great! but the atom state doesn’t seem to update, which means that elements can’t respond reactively. this is obviously some sort of bug on my side, since you have the slideshow example where state does work
(-> '(reagent.core/with-let
[G__68160 (reagent.core/atom [0 0])]
[:<>
[:pre (clojure.core/pr-str @G__68160)]
[mafs.core/Mafs
[mafs.coordinates/Cartesian {}]
(reagent.core/with-let
[G__68161
(js/Function.
"[y0001]"
"[p0002]"
" const _0006 = - p0002;\n const _0008 = _0006 + y0001;\n const _0013 = Math.sinh(_0008);\n const _0014 = Math.cosh(_0008);\n const _0015 = Math.pow(_0014, 4.0);\n return (- 6.0 * Math.pow(_0013, 4.0) + 8.0 * Math.pow(_0013, 2.0) * Math.pow(_0014, 2.0) - 2.0 * _0015) / _0015;")]
[mafs.plot/OfX
{:y
(let [G__68162 (mapv @G__68160 [0])] (fn [x] (G__68161 [x] G__68162)))}])
(reagent.core/with-let
[G__68163
(js/Function.
"[y0001]"
"[p0002]"
" const _0006 = - p0002;\n const _0008 = _0006 + y0001;\n const _0013 = Math.sinh(_0008);\n const _0014 = Math.cosh(_0008);\n const _0015 = Math.pow(_0014, 4.0);\n return (- 6.0 * Math.pow(_0013, 4.0) + 8.0 * Math.pow(_0013, 2.0) * Math.pow(_0014, 2.0) - 2.0 * _0015) / _0015;")
G__68165
(js/Function. "y0001" " return Math.cos(y0001);")]
[mafs.plot/Inequality
{:y
{:<=
(let [G__68164 (mapv @G__68160 [0])] (fn [x] (G__68163 [x] G__68164))),
:> G__68165},
:color :blue}])
[mafs.core/MovablePoint {:atom G__68160, :constrain "horizontal"}]]])
(with-meta {:portal.viewer/default :portal-present.viewer/mafs})
(tap>))
(there’s the code for that example, generated of course, not handwritten)
this simpler example seems to fail to update a reagent atom inside of with-let
. So maybe with-let is the problem?
(-> '(reagent.core/with-let [!click (reagent.core/atom 0)]
[:div
"The atom " [:code "!click"] " has value: "
@!click ". "
[:input {:type :button :value "Click me!" :on-click #(swap! !click inc)}]])
(with-meta {:portal.viewer/default :portal-present.viewer/mafs})
(tap>))
I have to duck out for now, but my next debugging steps are
1. see if this works when I don’t use eval
2. try it without with-let
, maybe that has some error
curious if you have thoughts too.
But this is now looking like it will be trivial for me to ship support for Clerk AND portal with my “emmy-viewers” project, so folks can do 2d stuff like above and also 3d:
(-> '(fn []
(reagent.core/with-let [!click (reagent.core/atom 0)]
[:div
"The atom " [:code "!click"] " has value: "
@!click ". "
[:input {:type :button
:value "Click me!"
:on-click
(fn [e]
(js/console.log e)
(.stopPropagation e)
(swap! !click inc))}]]))
(with-meta {:portal.viewer/default :portal-present.viewer/mafs})
(tap>))
Works for meI think this is related to it not being a "react" component so wrapping it in a fn produces a functional component 👌
I would also be down to ship this type of viewer in portal, something like portal.viewer/reagent :thinking_face:
Then the only thing Emmy needs is the default viewer metadata which could be included unconditionally
that sounds great
yeah, the eval-style viewer cracked this project open for me
the reason it is important is that Emmy can compile these fast JS function definitions down from high level code after running a powerful simplifier on it… I actually defined that function body as
(fn [shift]
(((cube D) tanh) (- identity shift)))
where the return value is using
• function algebra! (- identity shift)
is actually a function, (fn [x] (- x shift)
, and the whole form is actually (fn [x] (((cube D) tanh) (- x shift)))
• automatic differentiation… ((cube D) tanh)
is the 3rd derivative of tanhI feel like I can fully appreciate your conj talk now, it's all coming together for me 🙏
thanks for all of your help here, I remain happily surprised at how easy it is to fuse together clojure projects that weren’t explicitly designed to fuse
Also, got git deps working again, I guess for a prep builds you don't automatically get your top level :paths/:deps by default :thinking_face:
I have an example here in Clerk where (plot/of-x f)
returns a reagent fragment, [mafs.plot/OfX {:y <my-compiled-function-body>}]
, which Clerk then evaluates into an actual reagent component using SCI
so it feels like this is something I should be able to do in Portal, to let these plot objects render correctly.