genekim 2020-07-13T00:17:58.401700Z

OMG. I love it! re-frame to build terminal apps! so great!!!

phronmophobic 2020-07-13T00:20:17.401900Z

😄 awesome. let me know if you have any questions, issues, or feedback!

genekim 2020-07-13T05:19:56.402200Z

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! 😉

phronmophobic 2020-07-13T05:29:50.402400Z

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.

genekim 2020-07-13T05:35:14.402600Z

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!!!

phronmophobic 2020-07-13T05:35:44.402800Z


phronmophobic 2020-07-13T05:38:55.403Z

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

genekim 2020-07-13T05:44:14.403200Z

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

genekim 2020-07-13T05:45:35.403600Z

(^^^ hmm, no form elements in re-com. I think I already led you astray!)

phronmophobic 2020-07-13T05:46:32.403800Z

you can capture key events with:

(on :key-press
    (fn [s])
    (ui/label "hello"))

genekim 2020-07-13T05:47:10.404Z

Cool! Thank you! (I’ll be right back. 🙂

genekim 2020-07-13T06:10:11.404200Z

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

phronmophobic 2020-07-13T06:11:32.404400Z

on just returns a view that responds to key-presses

phronmophobic 2020-07-13T06:13:49.404600Z

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

genekim 2020-07-13T06:14:26.404800Z

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.

genekim 2020-07-13T06:15:17.405Z

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

phronmophobic 2020-07-13T06:15:58.405200Z

the :key-press event should capture capture key-presses regardless of focus

phronmophobic 2020-07-13T06:18:17.405500Z

i'm just realizing println statements mess with lanterna

genekim 2020-07-13T06:19:40.405700Z

Oh! Sorry, I missed your code example while I was typing — trying it now. (How does one log debug stuff, if not in println?)

phronmophobic 2020-07-13T06:22:16.405900Z

I think the example I gave you isn't quite right. fixing...

genekim 2020-07-13T06:25:09.406100Z

This is exciting — thanks for the help!!

phronmophobic 2020-07-13T06:30:41.406300Z

ok, here's an example that should actually work:

(defn -main [& args]
  (dispatch [:initialize-db])  
  (lanterna/run #(memframe/re-frame-app
                   (fn [s]
                     (spit "test.log" (str "keypress: " s "\n") :append true)
                     [[:keydown s]]
                   (lanterna/label "hello world")))))

phronmophobic 2020-07-13T06:31:08.406500Z

this will log to test.log

genekim 2020-07-13T06:31:25.406700Z

Ha! I see the logging mechanism. 🙂 Trying again…

genekim 2020-07-13T06:40:33.406900Z

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

phronmophobic 2020-07-13T06:42:02.407100Z

you can do something similar

genekim 2020-07-13T06:42:30.407300Z

(Oops. Didn’t mean to hit Enter and send that. Hacking away on it right now. 🙂

phronmophobic 2020-07-13T06:42:45.407500Z

you'll want to use (membrane.ui/label "hello world") instead of (lanterna/label "hello world")

phronmophobic 2020-07-13T06:43:35.407700Z

since font-height doesn't really make sense in a terminal view, but it does make sense in a desktop view

phronmophobic 2020-07-13T06:46:01.407900Z

so, for the desktop view:

(defn -main [& args]
  (dispatch [:initialize-db])  
  (skia/run #(memframe/re-frame-app
               (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

genekim 2020-07-13T06:47:13.408200Z

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!!!

phronmophobic 2020-07-13T06:47:31.408500Z

thanks for giving it a shot!

genekim 2020-07-13T06:59:36.408700Z

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! 🙂

phronmophobic 2020-07-13T07:02:15.409300Z

there area also backends for webgl and the virtual dom

genekim 2020-07-13T07:08:00.413400Z

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!

genekim 2020-07-13T07:11:59.416600Z

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!!!

phronmophobic 2020-07-13T07:14:38.416900Z

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

phronmophobic 2020-07-13T07:25:50.417100Z

looks like github is down, but you can find more docs at

phronmophobic 2020-07-13T15:28:32.417500Z

there's also a #membrane channel if you have more questions or feedback

genekim 2020-07-13T15:30:19.418700Z

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

genekim 2020-07-13T15:54:33.434900Z

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 !

genekim 2020-07-13T15:54:50.435Z

Here was his announcement of the project yesterday — so fun!!!

genekim 2020-07-13T15:56:26.436100Z

And a screenshot of the ridiculous simple app I'm so happy about! :)

genekim 2020-07-13T15:58:15.437100Z

Further discussions in #membrane!

phronmophobic 2020-07-13T16:01:54.437300Z

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

Rob Knight 2020-07-13T16:41:42.446900Z

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?

ggiraldez 2020-07-13T17:05:21.447100Z

I have not used it (yet), but I think you want track!

ggiraldez 2020-07-13T17:08:02.447300Z

removes the need of a react component and is more explicit in intent

Rob Knight 2020-07-13T17:14:15.447500Z

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.

gadfly361 2020-07-13T17:19:45.447700Z

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.

Rob Knight 2020-07-13T17:37:36.447900Z

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.

Rob Knight 2020-07-13T18:54:36.448400Z

Does track! work with re-frame subscriptions? I'm trying it but not seeing the results I would expect.

ggiraldez 2020-07-13T19:22:04.448600Z

re-frame subscriptions are reagent reactions, so yes, it should work

ggiraldez 2020-07-13T19:22:43.448800Z

just make sure to deref the subscription inside the function

Rob Knight 2020-07-13T19:23:07.449Z

Yeah, that's what I'm doing.

Rob Knight 2020-07-13T19:23:36.449200Z

(defn active-tracker []
  (let [active (re-frame/subscribe [::active])]
    (prn @active)))

(defonce tracker (r/track! active-tracker))

Rob Knight 2020-07-13T19:24:25.449400Z

It prints once, but doesn't do anything when the subscription updates.

ggiraldez 2020-07-13T19:25:27.449600Z

hmmm... my guess is it doesn't work because you are creating the reaction inside the tracking function

Rob Knight 2020-07-13T19:26:27.449800Z

Oh, hmm. Where else am I supposed to create it?

ggiraldez 2020-07-13T19:27:40.450Z

(let [active (re-frame/subscribe [::active])
     tracker (fn [] (println @active))]
  (r/track! tracker))

ggiraldez 2020-07-13T19:27:47.450200Z

that should work

Rob Knight 2020-07-13T19:27:58.450400Z

Thanks, I'll try that

ggiraldez 2020-07-13T19:29:22.450600Z

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

Rob Knight 2020-07-13T19:30:54.450800Z

This seems to behave the same way as the original version 😞

ggiraldez 2020-07-13T19:31:52.451Z

maybe something like:

(defonce tracker (let [active (re-frame/subscribe [::active])] (r/track! (fn [] (println @active))))

ggiraldez 2020-07-13T19:33:25.451200Z

although probably an atom would be more appropriate to hold the reference

Rob Knight 2020-07-13T19:37:17.451400Z

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.

Rob Knight 2020-07-13T20:32:48.451600Z

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.

adam 2020-07-13T23:27:30.452900Z

Any good open source re-frame project to get idea about to to properly structure the components, etc?

phronmophobic 2020-07-13T23:34:10.453Z

@genekim suggested to me yesterday and it looks pretty good and has lots of examples:

phronmophobic 2020-07-13T23:34:35.453400Z

for specifically flexible and re-usable components

adam 2020-07-13T23:43:09.453800Z

Thanks @smith.adriane