@borkdude another little bug due to not having proper width calculation in prstack. JLine will probably solve this. I'm also thinking what if instead of complicated stripping logic, the library keeps around both versions, styled and unstyled, it's not that much memory and would make it a lot simpler.
yes, implementing this with jline will fix it. you can try it as of today with the dev build which will help me get things into place as well for the full release :)
Maybe I can make a test branch but I'd like to keep main stable. Aside of that, it's also my first open source CLI tool and I'm still figuring out how to release it to package managers etc..
I'll see about a test branch later so we can try it out though π
Oh mb I realise you probably weren't asking for me to use the dev build in production π
the new bb release is due in 2 days - 2 weeks max
so it's not too early to start rebuilding
Cool. I'll open source bb-tui after that
@borkdude I've made a branch which uses JLine (requires bb dev). See https://github.com/yannvanhalewyn/prstack/tree/test-swap-jline-needs-bb-dev
You can run it from any repo, ideally where you have some branches going, and use <bb-dev> <path-to-bin/prstack>
nice :)
I guess you could also migrate away from gum so you don't need external stuff?
Thanks π
I'll leave it in for now, I think their UIs and spinners look great! Likely when working on the bb-tui library I'll start porting over some components
sure
Oh yeah forgot to mention, you need gum and gh to be available on the path
yes, that's the problem I want to get rid off by adding Jline
that's the whole point really ;)
Did everything work on your machine?
the charm.clj library is supposed to fix this problem (eventually?)
> Did everything work on your machine?
I think so yes
I already had those installed
> the charm.clj library is supposed to fix this problem (eventually?) Yeah, or any library that can provide some kind of UI. I like what they did with https://github.com/charmbracelet/lipgloss for example
I'd make a similar library with a bunch of useful UI components that other libraries and applications can rely on
yeah
is that wat bb-tui would become?
Not sure, I'm focusing on the Reagent like API of the UI, focus on re-rendering. I'll probably start by adding some UI components there, I need floats, prompts, layouts, scrollviews for PRStack. Then start extracting a library from that for apps or TUI frameworks
Or keeping it in bb-tui in case the implementation get too tied up. Will need to mess around a bit
https://github.com/TimoKramer/charm.clj/blob/main/docs/guides/styling-patterns.md A few things should be possible with charm.clj already. @yannvahalewyn
I hit a problem with JLine. The FFM Terminal doesn't seem to work on linux static (musl). It falls back to an ExecTerminal (which is probably a thing where it shells out all the time to accomplish ANSI stuff). That's a bit unfortunate since this is probably less performant? The debug script I ran to discover this in π§΅ (cc @timok @yannvahalewyn)
echo '(import (quote [org.jline.terminal TerminalBuilder])
(quote [org.jline.terminal.spi TerminalProvider SystemStream])
(quote [org.jline.terminal.impl AbstractPosixTerminal])
(quote [org.jline.terminal.impl.exec ExecTerminalProvider])
(quote [org.jline.terminal.impl.ffm FfmTerminalProvider]))
(println "--- FFM ---")
(try (let [p (FfmTerminalProvider.)] (println :ffm (.isSystemStream p SystemStream/Output))) (catch Throwable e (println :ffm-failed (class e))))
(println "--- Exec ---")
(try (let [p (ExecTerminalProvider.)] (println :exec (.isSystemStream p SystemStream/Output))) (catch Throwable e (println :exec-failed (class e))))
(println "--- TerminalBuilder ---")
(try (let [t (-> (TerminalBuilder/builder) (.system true) (.build))] (println :terminal (.getClass t)) (println :pty (.getClass (.getPty ^AbstractPosixTerminal t))) (.close t)) (catch Throwable e (println :terminal-failed (class
e))))' > /tmp/r.bb
./bb /tmp/r.bb
--- FFM ---
:ffm-failed java.lang.ExceptionInInitializerError
--- Exec ---
:exec true
--- TerminalBuilder ---
:terminal org.jline.terminal.impl.PosixSysTerminal
:pty org.jline.terminal.impl.exec.ExecPty
I added these classes to bb just for this "research" problem:
org.jline.terminal.spi.TerminalProvider
org.jline.terminal.spi.Pty
org.jline.terminal.spi.SystemStream
org.jline.terminal.impl.AbstractPosixTerminal
org.jline.terminal.impl.exec.ExecTerminalProvider
org.jline.terminal.impl.exec.ExecPty
org.jline.terminal.impl.ffm.FfmTerminalProvider
org.jline.terminal.impl.ffm.FfmNativePty
The issue is that when I force to create a FFM Terminal, I get this in the static binary
Caused by: java.lang.IllegalArgumentException: Can't load library: libc.so.6
at org.graalvm.nativeimage.foreign/com.oracle.svm.core.foreign.Util_java_lang_foreign_SymbolLookup.createNativeLibraries(Target_java_lang_foreign_SymbolLookup.java:155)
but isn't the idea of musl that the libc is part of the binary itself?@rahul080327 re: shelling out: this means that JLine falls back on another provider which just shells out to stty to replace things on the screen, which is less efficient
It's not part of the binary, just that all the syscalls would work. This seems to be looking for the so file specifically so it fails
Where are you running this? This may work in an env where libc is installed or with gccompat alpine
This is running in bb CI
but also tested on other linux machine
I think a musl compiled static binary just doesn't dlopen stuff
You would be able to load shared libs from any binary given that it can make the loading syscall, does this happen during the compile or runtime?
There may be another way! https://github.com/jline/jline3/blob/c13de76f16eb632b55b23749130627ff1653b693/jansi-core/src/main/java/org/jline/jansi/AnsiConsole.java#L136
Looks promising
Has anyone tried creating a "Ralph Wiggum" AI orchestration loop using Babashka? I wonder if Babashka (i.e., Clojure) presents opportunities that are absent/difficult in other "shells".
At work, I am trying to make a case for using Babashka instead of "make". Does it sound like a good idea to have "bb" allow multiple tasks to execute in sequence (e.g., bb task1 task2 task2)? Also, does it sound like a good idea to have "bb" do command-line completion on available tasks like "make" does?
Completion: https://book.babashka.org/#_terminal_tab_completion
Multiple tasks: you can either use (run 'task) or :depends to declare one task that runs multiple other tasks, or just write: bb task1 && bb task2
I'm not in favor of bb task1 task2 since tasks can take command line arguments and this conflicts with it
Thank you so much for Babashka, and the great docs that I should have consulted before bothering you π
no problem, it's not always easy to find even if there are docs!
As someone relatively new to Babashka, I'll just note that :depends was the key for me, in making chains of tasks.
One suggestion. In Babashka Book you might want to explain that one can remove the "-f" parameter from the "complete" command in the ".bashrc" code to ONLY see tasks
Thank you for the response Sean. I didn't know about :depends until this thread
Issues and PRs are welcome to the book project on Github :)
one caveat when using task dependencies is that it can be easy to lose yourself in these chains. So documentation is valuable. In fact, it should be possible to visualize the dependency tree and create easy to consume documentation from the bb.edn
PR submitted
merged and published!
π€
@borkdude A task dependency visualizer is actually a really useful idea. I might add something like this because I already have experienced this pain in this use case. https://github.com/nextdoc/salesforce-babashka-sample/blob/master/bb.edn Although this is a simple example, my real world dependency tree is much more complex. If I do this, I can do it as a separate task and library. But I wonder if this might be something youβd prefer to have in the core CLI Library?
From some AI assisted thinking, I realized that this visualizer will not render a tree, but a DAG. So itβs non-trivial, but doable. I also realised that your new J-Line support might help, but an initial version can just be static. ASCII rendering of the DAG without any interactivity. But the new J-Line feature set could enhance it a lot once the initial version starts producing feedback from those of us that use it.
How do we debug problems with babashka.deps/add-deps?
It seems that add-deps downloads the provided dependency and adds it to the classpath, but when :importing a java class from it fails with Unable to resolve classname: org.freedesktop.dbus.connections.impl.DBusConnection :
#!/usr/bin/env bb
(require '[babashka.deps :as deps])
(deps/add-deps
'{:deps {com.github.hypfvieh/dbus-java-core {:mvn/version "5.2.0"}
com.github.hypfvieh/dbus-java-transport-native-unixsocket {:mvn/version "5.2.0"}}})
#_{:clj-kondo/ignore [:namespace-name-mismatch]}
(ns midi-ble-adv
(:require
[clojure.string :as str])
(:import
[org.freedesktop.dbus.connections.impl DBusConnection]))
(defn -main [& args]
(println "Hello there"))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))
; vim: set ft=clojure:
obviously, the class exists in the dependency: https://github.com/hypfvieh/dbus-java/blob/dbus-java-parent-5.2.0/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.javababashka cannot import Java classes that are not pre-compiled before hand with it, it only looks at clojure sources
Ah yes it makes sense! I've used that feature before with a clojure library and I somehow thought it applied to all jvm dependencies
I'll open an issue to maybe add a sentence here to indicate the above in bb book: https://book.babashka.org/#babashkadeps
@borkdude about v-dom maybe being overkill for TUI apps, this video just happened to come out π https://youtu.be/LvW1HTSLPEk?si=eCfduIQiy1v8P8OK
what's "rizz"?
nvm, I'll just watch the 15 minute video :)
What Does 'Rizz' Mean? Is It Just Flirting or a Red Flag?
"Rizz" is slang for charisma, referring to someone's skill in charming, flirting, or attracting a romantic partner. Popularized by streamer Kai Cenat and often used on TikTok, it is short for "charizzma" and indicates one's ability to "get game" or "rizz up" (seduce) someone.
Key Details About "Rizz":
Definition: Romantic, verbal, or flirtatious appeal.
Usage: It acts as a noun ("He has so much rizz") or a verb ("Rizz up").
Origin: Coined by YouTuber and Twitch streamer Kai Cenat around 2021.
Recognition: Named the 2023 Word of the Year by the
Oxford University Press
.
Variations:
"Rizz up": To flirt with or charm someone.
"W Rizz": Having high-level, successful charm.
"L Rizz": Lacking charm or failing to flirt.
"Unspoken Rizz": The ability to attract someone without saying anything. ok, I thought it had to do with TUIs ;)
Haha, just gen z stuff
I watched most of the video. Great points. The snake game I did with reagami also didn't need reagami, you could just do immediate mode and it would work just fine. Which is why the port to Jline directly works also just fine without any diffing. Immediate mode FTW! https://gist.githubusercontent.com/borkdude/21f8e8996d5d20044f3f1c265ab7cc48/raw/81ff36c0f3730536c4bfc35d2a5963c398adc727/snake.clj
I wonder why there is a ansi parser namespace that only has tests and isn't used anywhere else in charm.clj
I asked claude to see if there is code in charm.render.screen that could be done with jline instead too. Here's what it said:
Duplicated functionality
charm.render.screen has ANSI string constants:
- cursor-hide, cursor-show, clear-screen, cursor-to, etc.
charm.terminal has the same via capabilities:
- hide-cursor, show-cursor, clear-screen, move-cursor
These could potentially be consolidated.
Screen buffer replacement with Display
JLine's Display could replace the manual screen buffer. Here's roughly what it would look like:
(:import [org.jline.utils Display AttributedString])
(defn create-display
"Create a JLine Display for efficient screen rendering."
[^Terminal terminal]
(doto (Display. terminal false)
(.resize (-> terminal .getSize .getRows)
(-> terminal .getSize .getColumns))))
(defn render-lines!
"Render lines to display with diff-based updates."
[^Display display lines cursor-col cursor-row]
(let [attributed (mapv #(AttributedString/fromAnsi %) lines)]
(.update display attributed (+ (* cursor-row (.getColumns display)) cursor-col))))
JLine's Display handles:
- Differential updates (only redraws changed cells)
- Proper cursor positioning
- Width calculation for wide chars
- Resize handling
Would you like me to:
1. Just consolidate the duplicate ANSI/cursor functions?
2. Replace the screen buffer with Display?
3. Both?ok, did just that. Replaced the screen buffer/diff things with JLine's display. It's all already in JLine. Also added this to bb.
pushed it
please do
i am back at the keyboard now
pushed the JLine Display class to bb master now too
JLine also has stuff for styling, AttributedStyle etc. Did you design this library initially without JLine in mind?
no, I did not have a lot of things in mind tbh... just wanted to know if this works.
β― What would be other JLine classes that are not yet in bb, that are commonly used?
βΊ Common JLine classes that are frequently used together:
Styling (often used with AttributedString):
- org.jline.utils.AttributedStyle - define colors, bold, underline, etc.
- org.jline.utils.AttributedStringBuilder - build styled strings incrementally
Line reading:
- org.jline.reader.LineReader - the main interface for reading input with editing
- org.jline.reader.History - command history
- org.jline.reader.ParsedLine - parsed input result
- org.jline.reader.EndOfFileException - thrown on Ctrl-D
- org.jline.reader.UserInterruptException - thrown on Ctrl-C
Tab completion:
- org.jline.reader.Completer - interface for completers
- org.jline.reader.Candidate - completion candidate
- org.jline.builtins.Completers$FileNameCompleter - file completion
Terminal utilities:
- org.jline.terminal.Cursor - cursor position
- org.jline.terminal.Attributes - terminal attributes (raw mode, echo, etc.)
The most impactful additions would likely be:
1. AttributedStyle + AttributedStringBuilder - essential for colored output
2. LineReader - for building REPLs or interactive CLIs
3. EndOfFileException + UserInterruptException - for proper signal handling in interactive apps
Would you like me to check which of these are already included, or help add any of them?haha
yeah, i can go through that
ok, let's finish the charm.clj library first with JLine in mind and let's add classes to bb that are necessary for that
good idea?
after we're done, we can do a bb release
sure
ok, just let me know which classes I should add after the current PR
I pushed your commits (with changed message) to main branch
you wanna go on with the AttributedStyle?
sure
tldr; basically the primeagen makes a full video about how it makes not sense to do v-diffing in TUI apps, you have the lines and can just render every time, it's not that many 'pixels'.
yeah, and apart from that he also teaches how to rizz, what a man! I am using Arch BTW