babashka

timo 2026-01-28T12:33:57.073919Z

here is a pomodoro timer: https://codeberg.org/timokramer/charm.clj/raw/branch/master/docs/examples/src/examples/pomodoro.clj

šŸ”„ 2
4
šŸ… 1
borkdude 2026-01-28T12:37:26.464289Z

excellent. I downloaded the file, prepended it with:

#_:clj-kondo/ignore
(babashka.deps/add-deps '{:deps {org.codeberg.timokramer/charm.clj {:git/sha "0db03d34885267620d51c1c1f68644106f278acd"}}})
and added (-main) at the end and then I could run it even without any other files :)

maleghast 2026-01-28T12:47:50.383029Z

I tried doing this^^ and got this:

Error building classpath. Failed to infer git url for: org.codeberg.timokramer/charm.clj
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:
Data:     {:proc #object[java.lang.ProcessImpl 0x550675cf "Process[pid=46934, exitValue=1]"], :exit 1, :in #object[java.lang.ProcessBuilder$NullOutputStream 0x6e386f72 "java.lang.ProcessBuilder$NullOutputStream@6e386f72"], :out #object[java.lang.ProcessImpl$ProcessPipeInputStream 0x4e5d3945 "java.lang.ProcessImpl$ProcessPipeInputStream@4e5d3945"], :err #object[java.lang.ProcessBuilder$NullInputStream 0x2b7fc968 "java.lang.ProcessBuilder$NullInputStream@2b7fc968"], :prev nil, :cmd ["/usr/bin/java" "-XX:-OmitStackTraceInFastThrow" "-classpath" "/Users/oliver/.deps.clj/1.12.4.1582/ClojureTools/clojure-tools-1.12.4.1582.jar" "clojure.main" "-m" "clojure.tools.deps.script.make-classpath2" "--config-user" "" "--config-project" "__babashka_no_deps_file__.edn" "--basis-file" "/Users/oliver/.clojure/.cpcache/71A0AB41F908CB45811FF9A516C5A6F2.basis" "--cp-file" "/Users/oliver/.clojure/.cpcache/71A0AB41F908CB45811FF9A516C5A6F2.cp" "--jvm-file" "/Users/oliver/.clojure/.cpcache/71A0AB41F908CB45811FF9A516C5A6F2.jvm" "--main-file" "/Users/oliver/.clojure/.cpcache/71A0AB41F908CB45811FF9A516C5A6F2.main" "--manifest-file" "/Users/oliver/.clojure/.cpcache/71A0AB41F908CB45811FF9A516C5A6F2.manifest" "--config-data" "{:deps {org.codeberg.timokramer/charm.clj {:git/sha \"0db03d34885267620d51c1c1f68644106f278acd\"}}, :deps-root \"\", :aliases {:org.babashka/defaults {:replace-paths [], :classpath-overrides {org.clojure/clojure \"\", org.clojure/spec.alpha \"\", org.clojure/core.specs.alpha \"\"}}}}" "-A::org.babashka/defaults"], :type :babashka.process/error}
Location: /Users/oliver/Desktop/./pomodoro.bb:2:1

----- Context ------------------------------------------------------------------
1: #_:clj-kondo/ignore
2: (babashka.deps/add-deps '{:deps {org.codeberg.timokramer/charm.clj {:git/sha "0db03d34885267620d51c1c1f68644106f278acd"}}})
   ^---
3:
4: (ns examples.pomodoro
5:   "Pomodoro CLI timer with work/break cycles.
6:
7:    Usage:

----- Stack trace --------------------------------------------------------------
babashka.process/check                          - <built-in>
babashka.process/shell                          - <built-in>
babashka.impl.deps/add-deps/fn--29015           - <built-in>
borkdude.deps/-main                             - <built-in>
clojure.core/apply                              - <built-in>
... (run with --debug to see elided elements)
clojure.core/apply                              - <built-in>
clojure.core/with-bindings*                     - <built-in>
babashka.impl.deps/add-deps/fn--29022           - <built-in>
babashka.impl.deps/add-deps                     - <built-in>
user                                            - /Users/oliver/Desktop/./pomodoro.bb:2:1

maleghast 2026-01-28T12:48:23.295319Z

Have I got the wrong version of babashka, or am I doing something else dumb?

timo 2026-01-28T12:49:27.031789Z

https://clojurians.slack.com/archives/CLX41ASCS/p1769601100306739?thread_ts=1769521188.320639&amp;cid=CLX41ASCS Sorry for not replying in the proper thread

maleghast 2026-01-28T12:50:22.869349Z

Aha! I need the dev version of bb, that makes sense! Thanks @timok

maleghast 2026-01-28T12:54:09.768919Z

That is so cool! Thx again! (I am going to watch out for a stable release for bb with jline3 so that I can use this day2day)

šŸ™ 1
borkdude 2026-01-28T12:57:40.975609Z

@maleghast please test the dev version if you have time. you can install it alongside the stable one: https://clojurians.slack.com/archives/CLX41ASCS/p1769601100306739?thread_ts=1769521188.320639&amp;cid=CLX41ASCS

maleghast 2026-01-28T13:02:57.590059Z

I have built a dev-build of bb in a folder as per those instructions and then run the pomodoro timer with the additions that you mentioned and it works beautifully! šŸ™‚

šŸŽ‰ 1
maleghast 2026-01-28T13:04:04.329549Z

I am not going to have time to but the dev build through its paces more widely right now, I am afraid, but I am happy to wait for a stable release with jline3 as and when you are completely ready / satisfied that the update is stable and ready to be rolled out šŸ™‚

maleghast 2026-01-28T13:04:10.495279Z

Thx again šŸŽ‰

borkdude 2026-01-28T13:04:41.269729Z

thanks for testing it, this was already helpful

šŸ‘ 1
🫔 1
yannvahalewyn 2026-01-28T13:56:10.373039Z

Awesome @timok šŸ™‚. I've been myself working on a TUI app using Babashka, polishing up for release somewhere this week probably: http://prstack.dev I've made a small incomplete TUI library with a react like API, this is how the code looks: https://github.com/yannvanhalewyn/prstack/blob/main/src/prstack/tui/app.clj#L57 I was actually planning on making a TUI library myself from the code in src/bb_tui. But maybe Charm would be sufficient / we could join forces on something.

šŸ’Æ 1
borkdude 2026-01-28T13:57:17.223829Z

awesome. I hear a bb conf proposal coming

yannvahalewyn 2026-01-28T13:58:46.008419Z

@borkdude FYI I was planning on making a proposal about how to leverage Babashka to make great dev tools and showing off these things, bit of inspiration and open discussion. Will make a submission after releasing PRStack

borkdude 2026-01-28T14:09:39.613789Z

you still have a few weeks :)

yannvahalewyn 2026-01-28T14:14:19.123919Z

I know, just FYI šŸ™‚

yannvahalewyn 2026-01-28T14:21:29.448529Z

@timok cool I've tested charm. I couldn't get it to run with Babashka because of JLine dependency. On my Intel Mac machine using native-image as in the examples README complained, I'm not often using native-image and might not have things set up properly.

Caused by: com.oracle.svm.core.util.UserError$UserException: Class initialization of clojure.pprint.dispatch__init failed. Use the option

    '--initialize-at-run-time=clojure.pprint.dispatch__init'

 to explicitly request initialization of this class at run time. Exception thrown by the class initializer:

java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.pprint/set-pprint-dispatch
	at clojure.lang.Var$Unbound.throwArity(Var.java:45)
	at clojure.lang.AFn.invoke(AFn.java:32)
	at clojure.pprint.dispatch__init.load(Unknown Source)
	at clojure.pprint.dispatch__init.(Unknown Source)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1169)
	(internal stack frames of the image generator are omitted)
clj -M commands work well! Some examples don't return to the old terminal state correctly. The Pomodoro example exits properly.

borkdude 2026-01-28T14:26:21.461149Z

what I mean with that link: it points you to the dev build which should now work with charm.clj

borkdude 2026-01-28T14:26:31.368229Z

(in case it wasn't clear)

yannvahalewyn 2026-01-28T14:30:01.097669Z

I've installed a local dev build of bb and it gets through the JLine error. However the examples turn up empty on my machine:

borkdude 2026-01-28T14:30:55.958029Z

it's because you need to invoke the main function

yannvahalewyn 2026-01-28T14:31:17.133239Z

Ah yes you mentioned that earlier silly me

yannvahalewyn 2026-01-28T14:32:14.367389Z

Yep, works like a charm šŸŖ„

borkdude 2026-01-28T14:33:14.027569Z

woohoo :)

yannvahalewyn 2026-01-28T14:36:17.905699Z

Should a babshka library for TUIs be implemented without JLine? It seemed to grow the bb binary. I did have an implementation before to prompt without it and now prompts in PRStack have been replaced with https://github.com/lispyclouds/bblgum which works great. Is JLine something useful to add to babashka in general?

borkdude 2026-01-28T14:37:36.316849Z

it's already merged. adds 3.5mb to the binary. TUI stuff has been asked for for long time and shelling out to binaries is ok-ish but this is much more convenient

yannvahalewyn 2026-01-28T14:40:03.645869Z

Ok cool. Nice job šŸ™‚. Also RIP the time I spent working around it šŸ˜„

borkdude 2026-01-28T14:43:37.764859Z

every time I've seen people try or ask for TUI stuff contributed to pulling the trigger on this. You've been doing your thing, lately someone else was asking about it and ended up using the lanterna pod, other people shelled out to gum, yet other people shelled out to some other tool which I don't remember the name of (but it's documented in some blog somewhere). I was reluctant to use jline3 because of the '3" which to me signals: 4 is coming which breaks everything, but the maintainer ensured me that not much is breaking at all. Since GraalVM 25 native-image supports FFM and jline now also has an FFM terminal, so there's nothing workaround-ish happening anymore with JNI and copying .so libraries at startup etc. So jline3 is fine in bb now. I'm surprised it only adds 3.5mb since I had to activate the FFM stuff in GraalVM now which is probably 2mb or so

yannvahalewyn 2026-01-28T14:46:45.641809Z

Thanks for the insight šŸ‘. Yeah I get it. Btw my comment above was a little joke, I also learned a lot messing around and such.

borkdude 2026-01-28T14:46:48.066139Z

(here's an example of a recent question: https://clojurians.slack.com/archives/CLX41ASCS/p1768583461449979)

borkdude 2026-01-28T14:47:08.118609Z

oh sure :)

yannvahalewyn 2026-01-28T14:51:29.473759Z

It makes sense to want to use Babashka for TUI apps. Great to see you've pulled the trigger on JLine. TUI apps are making a bit of a comeback it seems and who wouldn't want to make such programs using Clojure - seriously! So I'd be very excited to see some kind of well maintained library for making TUI apps using BB. It's likely still some work to get the right design and to get it working correctly everywhere. I'd like to either build or help maintain such a thing.

yannvahalewyn 2026-01-28T14:53:06.317099Z

I've been using my thing for a bit under a year now and seems stable on Mac, yet feature incomplete and 0 time spent on performance, it just re-renders everything every state change.

borkdude 2026-01-28T14:54:24.019689Z

have you seen my snake game? https://gist.github.com/borkdude/21f8e8996d5d20044f3f1c265ab7cc48 I ported it from reagami to TUI (I let an LLM do it) This is the same game in reagami: https://squint-cljs.github.io/squint/?src=gzip%3AH4sIAAAAAAAAE41WW4%2BjNhR%2Bz684y6iS2YoJTLsvZGenlbqqql2pUi9PiFYOPiSeGJu1HRI6yn%2BvbJNAZpnVECngw%2BfP534g0kBDuVwAkFzjlz3XCEW0tbY1%2BXKJprk126VGuqENjyCnBoZFGceLBWFYg%2BH%2FIdylMaxWsNGcBQG5S493aRwgFQoBWYAgrbZBcA9Z2h4Di5IVgrHUolOFWtXAU24k3SEUxTvI0hKKH8PtB3crYQVccsupgABbK9Yv4HzljGsoMkgdsFEdlxvQfLO1E0itFIMiC%2BwjnRNPUFTwDh%2FA6j2ezjZL0FQy1SSBolwAFMSJEi6ttz%2BGZ%2BvysrVRHSaDaW4nMQfavrkYD0BqCcVTvsPeQBGAzhp%2FVtCm9JEw9lQOihJeA5HKDu%2Fji%2F5mNJgItFBskTIgHVZQkO%2BBSLsFUnNtbHBjDGkcpO7INB6Z5q95jmzCkcXO9pmt1OIDkHvwGjnjZlESD4OzvNKkUtKELc5mzzG8Xu%2BtoBcd4nnVj0ExT5DOIvoJIptFbLlNDlSIByBKA3kPR%2B%2B099C%2FQHnlsA%2F3cDynyId76MPziwcZFPUDEKMahJuzt75z6YXGjt6J4%2FI5g3MV%2B5p2oj01RlVg7CXJayoMzqgyUeTVe0J4R3iI0hjOUH5kUkhzTshRGPwGTRyuxWK1gr94gxqEUu2CPJqlQfubtKg7KqY1d5emAf4J%2B7WimkGlpNVKmAW5pYx97FDaz9xYlKjh0SwPXDJ1gGiHPVMHGS2GCsXg8VBWO%2ByB3CbuhpdQTCsb9i1zN9eYXs4RT8y4%2FiqY14GlBmGH%2FbdzLfpZa3X4u41Cd7j31VikkJXx%2BTHJ5kvzOckvzu5nNMmE55U0n7G21zSuQQ8siX9%2BDc0fro9f8yQTolfwMK7PeRN6sptHicbKQlEcoS%2BhUkL5KBS5Fz%2FlRyBv4eiRMeS9W%2FVhNTkrP3Bmh%2FGWb9Ep6hdTSM2FCPyncShsaINJSMcwFHxWXQ%2BB6QA4wU8%2BrUKiFDnjXTijyE23gadBEfI2TONB60GjK%2BmVp3Jje4HwlK%2BVZqghytojGCU4g7Wg1S6a9Wu%2BptVuo9VeMohuEOvodBqAq1Xox%2BeKqJWGoqV6aNNjnv%2FjbQVirAb3Pj5BMUbF74g2GlFGl%2BCuVtNZPUF7R0UaWVSOUOdhUB1q%2FyfouXrIYYtyfngWucVjiP271MU8S1PXt6RNho%2BeIZpRcM4Jol%2FdKb93qN9EZVyOH0laKTeJ%2FcB4NEumqn2D0t5%2B2aPu%2F0SBlVUaohvatlFQgDBl1TW40kgtfhToVhAx3kXxpdWgfQMJZxCNFHC93X0h3bYaW5RsmvwapYt1yLvh8245CJ3eUIzZGSyijCUHaqvt0NvyPMDPrfFf8L%2FS0Tl5OGx4%2Fh8hQm1YdAoAAA%3D%3D Reagami is a simple library that prevents re-renders when possible (similar to React, but less sophisticated).

borkdude 2026-01-28T14:54:35.154799Z

the TUI snake game re-renders everything

yannvahalewyn 2026-01-28T14:54:41.972239Z

No will check it out!

borkdude 2026-01-28T14:54:44.050109Z

but it could work more like reagami

borkdude 2026-01-28T14:54:58.245659Z

I've seen flickering on Windows with this game, but not on mac

borkdude 2026-01-28T14:55:09.855629Z

I mean, the current TUI version

yannvahalewyn 2026-01-28T14:56:03.411579Z

Cool! Note to self, always talk to you before building something. I've been re-inventing some wheeels /s

borkdude 2026-01-28T14:56:56.370839Z

well, reagami just compares virtual nodes to the real dom nodes. perhaps the same idea would work with terminals? I don't know :)

yannvahalewyn 2026-01-28T14:57:59.988429Z

Yes it probably would! I was pushing back implementing this but super cool to see this already exists. I'll play around with it sometime

yannvahalewyn 2026-01-28T14:58:14.417559Z

The bb snake game works great on my mac machine indeed

yannvahalewyn 2026-01-28T15:00:09.050739Z

With reagami, can we get the diff and execute rendering ourselves somehow?

borkdude 2026-01-28T15:00:54.257759Z

no, reagami is hardcoded agains to the DOM to yield ultra small JS. you can compile this snake game to 5kb gzipped for example

yannvahalewyn 2026-01-28T15:01:15.199519Z

Ok. Maybe extract some kind of diffing library then or what would you suggest?

borkdude 2026-01-28T15:01:16.959559Z

I don't recommend to use reagami's code, just the simple idea of a vdom probably

yannvahalewyn 2026-01-28T15:01:39.096069Z

I'll use it as inspiration then

borkdude 2026-01-28T15:01:44.614229Z

forget about reagami, I just mentioned it because I ported the snake game.

yannvahalewyn 2026-01-28T15:02:02.462739Z

yeah I misread the intention, I heard a re-usable virtual dom diffing library haha

borkdude 2026-01-28T15:02:05.626299Z

and you could use a vdom for performance in TUI, maybe. perhaps charm.clj does that

borkdude 2026-01-28T15:02:32.899269Z

gotta run now

yannvahalewyn 2026-01-28T15:03:12.132639Z

The way I understand charm is it's pretty imperative, write this to the UI now. I would try to go a bit more declarative route

yannvahalewyn 2026-01-28T15:03:16.602039Z

Cya and thanks!

borkdude 2026-01-28T15:04:45.926039Z

we should write reagent or reagami for TUI, that's basically the idea I wanted to convey (but I did poorly :P)

borkdude 2026-01-28T15:05:20.113259Z

for just CRUD TUIs just re-rendering the whole screen is fine probably

borkdude 2026-01-28T15:05:42.336279Z

for something like claude code, something more optimal is probably nice (to prevent flickering)

yannvahalewyn 2026-01-28T15:14:16.634989Z

Np I got it. Maybe I’m poorly communicating that that is exactly what I’d like to see and have a working prototype for in PRStack

yannvahalewyn 2026-01-28T15:29:10.522219Z

You are right about it being enough to re-render everything for CRUD TUIs. I have been using my tool for a while now and haven't run into any flickering issues, computers are fine rendering a few lines. But once you start having more dynamic elements like spinners I'd assume it's worth not rendering everything for every animation frame

šŸ‘ 1
borkdude 2026-01-28T15:34:18.349259Z

Spinners are quite easy to control in a more performant manner

yannvahalewyn 2026-01-28T15:36:15.626579Z

Considering these points maybe v-dom is a bit overkill for this environment. Maybe declaring components that each take responsibility of their own little screen space, a bit like Flutter, would also be an option. Along with some way to subscribe to changes in global state.

šŸ‘ 1
yannvahalewyn 2026-01-28T15:36:53.248009Z

Would need to do something smart about layout changes though

šŸ‘ 1
borkdude 2026-01-28T15:37:49.019139Z

I guess you could just te render on layout change

yannvahalewyn 2026-01-28T15:38:01.661009Z

Something like that yes

borkdude 2026-01-28T15:39:34.982499Z

I think just creating enough bb TUIs with the bare minimum helper functions will tell over time which patterns are useful

šŸ‘ 1
yannvahalewyn 2026-01-28T15:45:33.466969Z

I'll probably just release bb-tui as an option with the current reagent-like API with full re-rendering. And put on the engineering hat if people run into issues

šŸ‘ 1
yannvahalewyn 2026-01-28T15:49:13.568599Z

Talking about performance, @timok on my machine the file_browser example is rather slow. j/k takes about 1-2 seconds. How is it for others?

borkdude 2026-01-28T17:12:58.519869Z

is it the same with clojure -M -m example.file-browser?

borkdude 2026-01-28T17:35:29.692199Z

I think I identified some slowness in the bb version compared to clj. here bb re-renders the file thing in 166ms vs 6ms. I'll have to inspect that code to see if something can be done.

;; Regular message
               :else
               (let [[new-state cmd] (update @state m)]
                 (reset! state new-state)
                 (execute-cmd! cmd msg-chan)
                 (time (render/render! renderer (view new-state))))

borkdude 2026-01-28T17:45:14.654569Z

render-diff! seems to be a pretty expensive function

borkdude 2026-01-28T17:45:36.742799Z

perhaps re-rendering the whole thing is more performant in bb? worth a shot

yannvahalewyn 2026-01-28T17:58:07.040169Z

Indeed, the Clojure version is smooth. BB version feels laggy

yannvahalewyn 2026-01-28T18:02:26.904789Z

For me every render seems slow, 1.5s on first render as well

yannvahalewyn 2026-01-28T18:08:01.714969Z

This is the snippet that's taking 1.5s in BB:

(if (pos? width)
    (mapv #(scr/truncate-line % width) new-lines)
    new-lines)
Found at render/core.clj:201

borkdude 2026-01-28T18:31:55.484219Z

you are on intel mac right?

yannvahalewyn 2026-01-28T18:33:56.926219Z

Yes 2,3Ghz 8-core Intel i9

yannvahalewyn 2026-01-28T18:34:16.492819Z

let's solve it by using some more of those cores /s

borkdude 2026-01-28T18:37:57.075359Z

GraalVM will support this architecture one more year, FWIW

yannvahalewyn 2026-01-28T18:44:43.265839Z

Thanks good to know. Wanting to upgrade to apple silicon machines once my bank account supports this feature

borkdude 2026-01-28T18:59:08.235959Z

I'll support it with bb as long as GraalVM supports it

borkdude 2026-01-28T18:59:25.544149Z

or I'll have to compile with older versions after that, might work for a while

borkdude 2026-01-28T18:59:36.593969Z

ok, let's see the slow bit

borkdude 2026-01-28T19:00:55.520199Z

that part takes 100ms on my machine and it also seems laggy since 100ms isn't instant. but 1.5s is quite a difference

yannvahalewyn 2026-01-28T19:01:06.472109Z

Are they dropping support as in it won't work anymore, or just not maintaining it further? If it's the latter it could still work for a little bit longer

yannvahalewyn 2026-01-28T19:01:41.995739Z

Yeah big difference

yannvahalewyn 2026-01-28T19:02:20.722309Z

Probably not worth digging deeper if it's only on my old machine with decaying support

borkdude 2026-01-28T19:02:22.366809Z

ok, now I found string-width to be the slow bit

šŸ‘ 1
borkdude 2026-01-28T19:02:30.895129Z

no I think preventing lag is good

šŸ‘ 1
yannvahalewyn 2026-01-28T19:02:48.145429Z

Enjoy the hunt! šŸ¦†

borkdude 2026-01-28T19:06:07.606179Z

I think I know what's going on, it's doing interop on character by character and this can get very slow in bb

borkdude 2026-01-28T19:06:21.420289Z

graphemes is the "guilty" one

yannvahalewyn 2026-01-28T19:12:23.242169Z

Nice find! šŸ™‚

borkdude 2026-01-28T19:14:14.315169Z

yeah when I change:

(defn truncate-line
  "Truncate a line to fit within terminal width."
  [line width]
  line #_(if (or (<= width 0) (<= (w/string-width line) width))
    line
    (w/truncate line width :tail "")))
(see commented out, I just return line) then it becomes instant

yannvahalewyn 2026-01-28T19:15:44.371339Z

Yup, instant on my machine as well

yannvahalewyn 2026-01-28T19:17:03.912379Z

I'm jealous of @timok's AI, his string width calculation code is so much more sophisticated than what my AI did šŸ˜„

yannvahalewyn 2026-01-28T19:18:59.859829Z

On one hand mine added simple regex based ansi stripping, and on another it just added :no-color? true to UI rendering functions and calculates strings twice, once with colors for rendering and once without to calculate widths haha.

borkdude 2026-01-28T19:19:03.601029Z

We could optimize this for when no character is a special char

yannvahalewyn 2026-01-28T19:19:12.262339Z

The kind of stuff I need to clean up now for release

yannvahalewyn 2026-01-28T19:20:06.659879Z

> We could optimize this for when no character is a special char Sounds good. I'll steal whatever code comes out of this šŸ‘

borkdude 2026-01-28T19:20:09.554329Z

ah one quick win is calling this only once:

(Character/getType cp)

šŸ‘ 1
borkdude 2026-01-28T19:20:15.625359Z

yeah I'll throw some claude at it too

yannvahalewyn 2026-01-28T19:20:19.350119Z

I gotta run now cya šŸ‘‹

borkdude 2026-01-28T19:26:12.282719Z

nice, I have a faster version now without sacrificing functionality

borkdude 2026-01-28T19:32:36.293529Z

@timok I'll send a PR :)

borkdude 2026-01-28T19:32:55.002299Z

or maybe I should first release bb so we can also use it in CI. how does codeberg use CI... does it provide anything?

borkdude 2026-01-28T19:33:38.223829Z

anyway, I'll send the PR

šŸ‘ 1
timo 2026-01-28T19:34:32.597069Z

codeberg has a ci feature that i have to look into... if it is not compelling i'll switch to github

borkdude 2026-01-28T19:46:11.541279Z

I get a 500 when creating a PR :-s https://codeberg.org/timokramer/charm.clj/compare/master...borkdude/charm.clj:feat/no-spi-with-ffm

šŸ˜• 1
borkdude 2026-01-28T19:47:04.616799Z

Maybe I can e-mail you a patch?

timo 2026-01-28T19:47:19.854789Z

of course

borkdude 2026-01-28T19:53:35.605099Z

fucking hell, I'm in the middle of some rebase to create a single patch, I don't even remember how this work. can I just copy/paste the changes here?

borkdude 2026-01-28T19:54:15.288659Z

bb.edn:

{:paths ["src" "test"]
 :tasks {test:bb
         {:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
                  :task cognitect.test-runner/-main}}}

borkdude 2026-01-28T19:55:02.587169Z

ansi/width.clj:

borkdude 2026-01-28T19:55:19.574409Z

after these changes, smooth file browser

borkdude 2026-01-28T19:56:47.115389Z

I had the branch merged with master, but I didn't remember how to create a patch file from this situation:

abcd merge commit
defg my actual commit

borkdude 2026-01-28T19:56:56.575739Z

so I think just copy paste the code and apply it yourself :)

timo 2026-01-28T19:57:36.913189Z

sure lms...

timo 2026-01-28T20:01:26.939109Z

microsoft style engineering haha

borkdude 2026-01-28T20:02:57.922529Z

github in 2010 :)

timo 2026-01-28T20:05:35.616779Z

can you just quickly tell me what these changes are supposed to do? tried to understand what you talked about above but it's not entirely clear

timo 2026-01-28T20:07:49.426659Z

ah i see the comment for fastpath for babashka

borkdude 2026-01-28T20:08:09.119539Z

Yes. Iterating over each character and doing interop on each character is slow in bb. This can be avoided by checking if all the characters are normal ASCII

borkdude 2026-01-28T20:08:27.683269Z

The or is replaced with a set operation which is also faster in bb

borkdude 2026-01-28T20:08:48.008079Z

And some interop expressions were done multiple times, it is now one let binding

timo 2026-01-28T20:08:57.508799Z

yes, great thanks

yannvahalewyn 2026-01-28T20:41:25.139849Z

Haha the verbal pull request

borkdude 2026-01-28T22:14:00.957999Z

@timok would you consider adding the bb.edn too?

borkdude 2026-01-28T22:14:25.275189Z

I'm trying to change some stuff here to optimize string-width with a function from jline which would be much faster if I added it to bb

borkdude 2026-01-28T22:16:08.367869Z

This is the new implementation:

(if (or (nil? s) (empty? s))
    0
    (.columnLength (AttributedString/fromAnsi s)))

borkdude 2026-01-28T22:16:13.130269Z

all the other crap can be gone

timo 2026-01-28T22:16:46.728349Z

yes, of course. I pushed to github: https://github.com/TimoKramer/charm.clj

timo 2026-01-28T22:16:51.658909Z

you can open prs there

borkdude 2026-01-28T22:16:57.005979Z

ah ok

borkdude 2026-01-28T22:33:07.154279Z

BOOM. Got rid of dozens of lines of code by using 1 Jline class which I just added to bb. https://github.com/TimoKramer/charm.clj/pull/1/changes

borkdude 2026-01-28T22:33:19.151699Z

This should make TUI in bb with charm.clj even more smooth

borkdude 2026-01-28T22:34:03.706859Z

can confirm, smooth as hell now that file browser

timo 2026-01-28T22:34:18.120649Z

crazy good. tell me... was this now human intelligence?

timo 2026-01-28T22:34:24.575939Z

šŸ™‚

borkdude 2026-01-28T22:34:41.080429Z

I suspected that Jline would have something for this

borkdude 2026-01-28T22:34:50.361179Z

and claude confirmed my suspicion

timo 2026-01-28T22:35:00.324509Z

that's experience. thank you!

yannvahalewyn 2026-01-28T22:41:39.162819Z

Awesome @borkdude!! PRStack's width code is also not perfect and I was unaware of AttributedString/fromAnsi. When BB gets a release I can also use this to improve PRStack. Thanks for the great work! šŸ™

yannvahalewyn 2026-01-28T14:42:10.604349Z

@timok about Charm, the next challenge I was facing with my TUI library are scroll views and layouts. Did you have any ideas brewing on this topic?

yannvahalewyn 2026-01-28T14:44:18.296569Z

I'm currently considering having some kind of light flexboxy implementation where you can set up either fixed size components or letting things grow to fill the remainder of the screen. But I haven't designed anything specific yet.

timo 2026-01-28T17:55:49.600929Z

Hey, i will have to look into that. Did not yet think about it any further but that's a valid feature.

šŸ‘ 1
yannvahalewyn 2026-01-28T18:15:06.421949Z

Allright. The app I'm making needs this so I'll be thinking about this some more.

yannvahalewyn 2026-01-28T18:17:03.113369Z

I'm wondering if you had an intention to the architecture of Charm? I'm making a Reagent-like API. IMO, if you are heading the same route it may be a good idea to put our work together, just let me know. If you want to go another route we'd both make our own thing and offer options to the community.

timo 2026-01-28T19:37:22.312049Z

so i was basically advising claude to copy over williamagh/tui4j or in that regard charmbracelet/bubbletea to clojure. that's in simple terms what i told it to do. in bubbletea it says that it is based on the elm architecture, so probably not exactly reagent-like then.

yannvahalewyn 2026-01-28T20:52:47.301319Z

Ah ok, I’m not super familiar with Elm but looking at Bubbletea it does seem to head in a similar direction

yannvahalewyn 2026-01-28T20:57:50.992899Z

At first glance both ideas seem similar when it comes to declaring the UI as a function of the state. Where a Reagent-like API would not really care how you handle your state as long as it can react to something (r/atom), Bubbletea / Elm seem to also be batteries included for a redux like dispatching system

timo 2026-01-28T20:58:34.162019Z

was my initial impression as well

yannvahalewyn 2026-01-28T21:03:10.811149Z

My personal preference is to follow the Reagent model and focus on a reactive UI only. Offer a simple example to dispatch yourself using multimethods or allow for custom state management libraries a-la re-frame for more complicated apps

šŸ‘ 1
yannvahalewyn 2026-01-28T22:53:49.175099Z

For the layout feature, I think we can get some inspiration from https://github.com/charmbracelet/lipgloss

BuddhiLW 2026-01-28T22:54:24.798829Z

Are there frameworks or libraries to write CLI applications in bb? For example, in Go there are https://github.com/rwxrob/bonzai and https://github.com/spf13/cobra etc

borkdude 2026-01-28T22:56:23.192319Z

Do you mean command line parsing?

borkdude 2026-01-28T22:56:30.755089Z

clojure.tools.cli and babashka.cli are built-in

yannvahalewyn 2026-01-28T22:59:50.251889Z

For Bonzai, I have this little personal babaska program that does something similar. You can copy it and make it your own: https://github.com/yannvanhalewyn/cmd/blob/main/src/cmd/commands.clj#L82

yannvahalewyn 2026-01-28T23:01:58.254719Z

It does this

šŸ‘€ 1
BuddhiLW 2026-01-28T23:06:17.261349Z

Hmm... But there are not meta-frameworks? It's not only for parsing. But, thinking of it, that's a big part. For example,

var HelpCmd = &bonzai.Cmd{
  Name:  `help`,
  Alias: `h|?`,
  Short: `display help information`,
  Do: func(x *bonzai.Cmd, args ...string) error {
    caller := x.Caller()
    if caller != nil && caller != x {
      return showHelp(caller)
    }
    // Fallback: show help for self
    return showHelp(x)
  },
}
This makes such you call yourscript help will call this "Do" function. What bonzai role would be to organize, what is metadata, help info, man related info etc. in Long tag you could entire sections to compose a man page. And it also will support automatically the bash-native auto-completion tool (see in screenshot). And some other things too. Recently, I PR'ed a support for native MCP on Bonzai. You add metadata to these bonzai.Cmd; build binary; and automatically they will be picked up by LLMs as a tool in the mcp server.

BuddhiLW 2026-01-28T23:07:12.180009Z

Wow, supper cool

borkdude 2026-01-28T23:07:26.546429Z

babashka.cli has support for subcommands

borkdude 2026-01-28T23:07:36.071789Z

you can hook up a function to one subcommand

borkdude 2026-01-28T23:07:54.516049Z

(https://github.com/babashka/cli)

yannvahalewyn 2026-01-28T23:09:03.946329Z

I don't think there is a framework that does all the things, might be mistaken. Typically it's pretty easy to make all those things happen by using a few tools together and writing a bit of code yourself, like clojure.tools.cli and babashka.cli. It's a bit more work but it does keep you flexible. I just copy over boilerplate code from project to project, let me see if I can make a gist for you

BuddhiLW 2026-01-28T23:10:06.028529Z

Thank you! But, it's just curiosity. I often create CLIs. Currently, do Go Bonzai, mostly. Just brainstorming if I should turn more to babashka

borkdude 2026-01-28T23:10:09.994499Z

there is an open issue for bash completions in babashka.cli, but I didn't even get around to help output for subcommands since I haven't reached a one-size fits all vision on how to do it

BuddhiLW 2026-01-28T23:10:45.925309Z

Don't lose time with me haha @yannvahalewyn (regarding the gist)

yannvahalewyn 2026-01-28T23:11:53.259489Z

ok well I've found a project where most of these things are implemented, but I'd need to clean it up a bit for a gist. Should you ever want to have a closer look let me know I'll share it

šŸ™ 1
BuddhiLW 2026-01-28T23:12:48.203409Z

Claude created a mutiplexed-designed bb-mcp for my project. So, I mean. It's already powerful enough, anyways! I have been using it daily. Spawn a father JVM. And next bb-mcp connections pass commands to the heavyweight. It works as a charm. But, nice to hear from the horse's mouth about the tool itself.

BuddhiLW 2026-01-28T23:12:53.105249Z

For sure!

BuddhiLW 2026-01-28T23:13:15.527539Z

https://github.com/hive-agi/bb-mcp for reference

yannvahalewyn 2026-01-28T23:14:42.385399Z

there is an open issue for bash completions in babashka.cli, but I didn't even get around to help output for subcommands since I haven't reached a one-size fits all vision on how to do it@borkdude I've been doing it using datastructures to declare commands, subcommands, and the flags etc, have some glue code find the correct command, parse the attrs according to those flags and either run the command or generate help from it. Something like:

;; commands/app.clj
(def test
  {:command/key "test"
   :command/description "Compiles and runs the test builds"
   :command/options [[nil "--watch" "Runs a CLJS test watcher in the browser"]]
   :command/handler
   (fn [options]
     (if (:watch options)
       (p/check (build/browser-test))
       (do (p/check (build/test))
           (p/check (p/process! ["node" "target/test/index.js"]
                                {:inherit true :dir (project/dir :app)})))))})

(def command
  {:command/key "app"
   :command/description "Build or manage the App"
   :command/sub-commands [install dev run debug test build]})

;; commands.clj
(def root-command
  {:command/description "A single CLI to run any task for all Pilloxa projects"
   :command/options [["-h" "--help" "Show this help message"]]
   :command/sub-commands [app/command
                          ci/command
                          code/command
                          console/command]})

āœ… 1
borkdude 2026-01-28T23:15:24.545459Z

yeah, babashka.cli has a similar structure, but you can just nest subcommands and the corresponding options

yannvahalewyn 2026-01-28T23:15:43.751469Z

Would run like command app test --watch

BuddhiLW 2026-01-28T23:16:47.961569Z

This is already close to Bonzai šŸ˜‚. Clojure and it's magic

borkdude 2026-01-28T23:17:07.070829Z

this is what it looks like in neil which has nested subcommands: https://github.com/babashka/neil/blob/8d5ccdbd8185fb2e03bd7de04b3d190e90331709/src/babashka/neil.clj#L886-L946

šŸ‘Œ 1
yannvahalewyn 2026-01-28T23:17:56.107219Z

> This is already close to Bonzai šŸ˜‚. Clojure and it's magic The glue code is really 10 lines of code, generating help a bit more but still. This is why I love Clojure, most things you need are just a few expressive lines of code away.

yannvahalewyn 2026-01-28T23:18:17.128109Z

And hence some libraries just, like not existing šŸ˜„

yannvahalewyn 2026-01-28T23:27:35.727149Z

The nice thing about keeping a nested tree is that you can generate help at any layer. command app -h would show the description of the app command, the list of sub commands and their descriptions. Makes it more discoverable, better dev-ux.

yannvahalewyn 2026-01-28T23:29:37.130909Z

šŸš€ 1