OMG. I love it! re-frame to build terminal apps! so great!!!
😄 awesome. let me know if you have any questions, issues, or feedback!
Holy cow, I just checked out your examples — so freaking cool! To set some context: I work on a bunch of programs where I’m the only user, and they mostly are SPAs that run in Heroku. But, holy cow, the effort to learn about the DOM and JavaScript is often so much more than I what I thought I was signing up for. Then throw in CORS and other madness on the backend, and the fun factor plummets even more. I was casually looking at cljfx, to see if that would be any easier, and holy smokes, that just feels too alien — I haven’t learned either Swing or JavaFX yet… This seems more my speed — I just wrote my first lanterna program, and I love the idea that I could have my re-frame programs target the terminal! It almost makes me want to weep with happiness!!! I’m going to write a toy app in the next day or two! Maybe even tonight! Keep up the amazing work!!! Now I’m dying of curiosity: what is your motivation to write this library, which seems like such a throwback to one that died decades ago! 😉
It's still a work in progress, so don't hesitate to ping me if you run into any issues. The back story is pretty long winded, but the short version is that I was trying to build an app and the available options for building UIs for a jvm based clj app all seemed full of incidental complexity.
Okay, I just wrote my first lanterna program that handled a couple of keystrokes, and already, I see why you might want something at a much higher level, like re-frame. Starting my first attempt at doing this in membrane! Woot! (I love where you’re going with it! Yes, so much complexity that I just don’t have any appetite to take on. I bought a book on Swing, but yowza, it’s hundreds of pages long! But, there are days I’d rather learn that than deal with client/server, etc…) Okay, I’ll hopefully have something to have a status update in 30m or so! 🙂 So fun!!!
😄
i'm actually pretty new to re-frame, so I'm sure the integration there can be improved. specifically, I'd like to have a text input component that has its state idiomatically handled by re-frame, but I don't know enough about re-frame to know what that should look like. my first attempt was just to try and include a text input that worked similarly to the text input available in the browser
Okay, I’ve got the app running in the REPL — what’s the easiest way to capture a keydown event, when there’s no focus in a textbox, etc? (Gosh, I don’t even know the language for this — a global keypress event?) PS: to my knowledge, there isn’t a textbox element like that. I think things like that are in re-com, which is a set of general purpose UI elements, inspired by Adobe Flash, etc.: https://github.com/day8/re-com
(^^^ hmm, no form elements in re-com. I think I already led you astray!)
you can capture key events with:
(on :key-press
(fn [s])
(ui/label "hello"))
Cool! Thank you! (I’ll be right back. 🙂
Can I put the (on :key-press)
in the -main
function?
I’m trying to convert a keypress event into a re-frame event…
(defn -main [& args]
(println "main starting")
(dispatch [:initialize-db])
(println "hello")
(on :key-press
(fn [s]
(println "main: keypress event: " s)
(ui/label "Hello from key")
(dispatch [:keydown s])))
on just returns a view that responds to key-presses
you'll probably want something like:
(defn -main [& args]
(dispatch [:initialize-db])
(lanterna/run #(memframe/re-frame-app
(fn []
(on :key-press
(fn [s]
(println "main: keypress event: " s)
[[:keydown s]])
(ui/label "Hello from key"))))))
Ah, I understand your examples better now. What is easiest way to capture keypress events when there is no focus anywhere? In my head, I was thinking about making a vi like editor, accumulating text, capturing arrow keys to move cursor around. Without using any widgets. Sorry for my denseness.
PS: regarding your question: I’d ask Mike Thompson directly in the channel — seriously, this is the friendly channel in Clojurians, which is already super friendly. 🙂
the :key-press
event should capture capture key-presses regardless of focus
i'm just realizing println statements mess with lanterna
Oh! Sorry, I missed your code example while I was typing — trying it now. (How does one log debug stuff, if not in println?)
I think the example I gave you isn't quite right. fixing...
This is exciting — thanks for the help!!
ok, here's an example that should actually work:
(defn -main [& args]
(dispatch [:initialize-db])
(lanterna/run #(memframe/re-frame-app
(on
:key-press
(fn [s]
(spit "test.log" (str "keypress: " s "\n") :append true)
[[:keydown s]]
)
(lanterna/label "hello world")))))
this will log to test.log
Ha! I see the logging mechanism. 🙂 Trying again…
Got it! And it’s working in the terminal version, inside of term-view namespace. That’s super cool. I’m assuming I can do the exact same thing in views namespace, that uses a Swing window? (This is what I tried first.).
you can do something similar
(Oops. Didn’t mean to hit Enter and send that. Hacking away on it right now. 🙂
you'll want to use (membrane.ui/label "hello world")
instead of (lanterna/label "hello world")
since font-height doesn't really make sense in a terminal view, but it does make sense in a desktop view
so, for the desktop view:
(defn -main [& args]
(dispatch [:initialize-db])
(skia/run #(memframe/re-frame-app
(on
:key-press
(fn [s]
(spit "test.log" (str "keypress: " s "\n") :append true)
[[:keydown s]]
)
(ui/label "hello world")))))
the differences being:
1. ui/label
instead of lanterna/label
2. skia/run
instead of lanterna/run
Got it!!! And println works when in a Swing window! 🙂 Gimme 10m, and I think I’ll have my first stateful program running! 🙂 Thank you!!!
1🦜thanks for giving it a shot!
Well, wow, that is super cool! Thank you so much for writing this! I will be thinking for days about how I can use this — brilliant work!!! Here’s my caveman output that I’m so delighted by! 🙂
1🎉1there area also backends for webgl and the virtual dom
Whoa. I don't even know what those mean, but I can't wait to research it! Things I'm now dying to take a stab at: a simple vi modal editor, a snake game, a Trello card browser (based on an existing re-frame app)... I'd love to submit them as sample apps, because I'm so excited by what you've built. It seems like the easiest way to write simple programs. THANK YOU!
1👍I'm even imagining taking an existing re-frame SPA apps (CLJS client, CLJ backend), and adding one more target for a membrane CLJ app (without the ring server, CLJS client)... (but it might be tough to live without all the awesome hot code reloading — you sure can get spoiled by that! :) Have a great night!!!
hot code reloading works really well for the desktop backend. I haven't thought too much about how hot code reloading should work for terminal apps, but it's definitely possible
looks like github is down, but you can find more docs at https://github.com/phronmophobic/membrane/
there's also a #membrane channel if you have more questions or feedback
Good morning — thanks for the help last night! I had trouble going to sleep last night, as I was excitedly thinking about things I could build! :)
1😄Drats — accidentally deleted my message. Reposting. Ever want to write a simple application, but writing a CLJS front end and a CLJ backend just seem like overkill? And the idea of having to learn Java Swing or JavaFX seems too daunting, but so too does the idea of writing a curses/terminal app (which doesn't even have an event loop)? @smith.adriane wrote something so cool, the membrane library — it lets you write a curses/terminal app that runs in your terminal or in a Swing application, but on top of re-frame. He helped me write a simple app yesterday that captures and displays keystrokes (hacked into the membrane sample app, which is the canonical TODO MVC app.) I'm so excited by this, because there's a whole class of simple applications that this seems perfect for! Thank you, @smith.adriane and @mikethompson !
Here was his announcement of the project yesterday — so fun!!! https://clojurians.slack.com/archives/C073DKH9P/p1594229800342600
And a screenshot of the ridiculous simple app I'm so happy about! :) https://clojurians.slack.com/archives/C073DKH9P/p1594623576408700?thread_ts=1594229800.342600&channel=C073DKH9P&message_ts=1594623576.408700
Further discussions in #membrane!
Thanks for the kind words @genekim and also thanks to @mikethompson for re-frame. If this is something people are interested in, I could use some help designing re-frame compatible versions of basic reusable components like textboxes
I'm struggling with a design question.
In my app I have a concept of "actions" that the user can take, which are determined dynamically based on things like selections made in the UI and data fetched from remote services. I have a network of subscriptions which aggregates this nicely into a single list of actions, and a UI which can show the user a searchable list of these actions. Subscriptions mean that the list updates dynamically, as it should.
I also want to provide keyboard shortcuts for actions, and I've been looking at re-pressed as a library for this. Problem is that re-pressed requires me to "declare" the active keyboard shortcuts via an event (`set-keydown-rules`). So instead of having a data-driven reactive workflow as I do with the action list UI, I have to trigger events when the relevant data changes, if the change means that a new keyboard shortcut is now valid (or an old one is not).
My ideal solution is simply to be able to trigger an event when a subscription changes. It seems like the easiest way to do this is to have a Reagent form-3 component which subscribes to the relevant subscription, and triggers an event in the :component-did-update
handler, while returning nil
from the render function. Is there a better design pattern for this?
I have not used it (yet), but I think you want track!
https://reagent-project.github.io/docs/master/reagent.core.html#var-track.21
removes the need of a react component and is more explicit in intent
Oh, that makes sense. Thanks! I was starting from the JS React-ish concept of renderless components, which felt like an un-Reagent/Re-frameish way of doing things.
Regarding re-pressed, yeah, the recommended approach is to dispatch the set-keydown-rules
event anytime the rules change. There is no limit to how frequent you can dispatch that even, so I think you can achieve the dynamic effect you want.
Yes, absolutely! My problem was just that I wanted to re-use the work I had already done in generating the action list, which is bundled up in a subscription, so I needed to figure out the right way of triggering an event on a subscription change.
1👍Does track!
work with re-frame subscriptions? I'm trying it but not seeing the results I would expect.
re-frame subscriptions are reagent reactions, so yes, it should work
just make sure to deref the subscription inside the function
Yeah, that's what I'm doing.
(defn active-tracker []
(let [active (re-frame/subscribe [::active])]
(prn @active)))
(defonce tracker (r/track! active-tracker))
It prints once, but doesn't do anything when the subscription updates.
hmmm... my guess is it doesn't work because you are creating the reaction inside the tracking function
Oh, hmm. Where else am I supposed to create it?
(let [active (re-frame/subscribe [::active])
tracker (fn [] (println @active))]
(r/track! tracker))
that should work
Thanks, I'll try that
you will need to keep a reference of the result of track!
for later disposal though... the example I gave you will leak the reference
This seems to behave the same way as the original version 😞
maybe something like:
(defonce tracker (let [active (re-frame/subscribe [::active])] (r/track! (fn [] (println @active))))
although probably an atom would be more appropriate to hold the reference
I'm doing that, but the basic functionality just doesn't seem to work. I must be missing something obvious but I can't work out what.
Ah, during re-frame setup/auto-reload I have a call to re-frame/clear-subscription-cache!
and this breaks the subscription. As it's not part of a view/component, it doesn't get recreated. That was the problem.
Any good open source re-frame project to get idea about to to properly structure the components, etc?
@genekim suggested https://github.com/day8/re-com to me yesterday and it looks pretty good and has lots of examples: https://github.com/day8/re-com/blob/master/src/re_com/misc.cljs
for specifically flexible and re-usable components
Thanks @smith.adriane