Bubble Tea (a Go TUI framework) recently hit 1.0 [1] and the author's tool that is built with it is called pug [2] which is an awesome terminal user interface for terraform which we featured on Terminal Trove [3] a while back.
[1] https://github.com/charmbracelet/bubbletea/releases
- entr is a good livereload tool: https://eradman.com/entrproject/. I prefer to wrap the livereload scripts in a Makefile, under something like `make dev`.
- Managing layout arithmetic by yourself is a real pain. A widget abstraction like Bubbletea's is very helpful. Also, don't forget about weird Unicode characters (eg. emoji) potentially breaking width calculations.
- Since this follows the Elm architecture, consider storing your data the Elm way into one big, flat Model. Elm webapps turn into headaches when you split state among submodels. I think the same happens in TUI apps. I'm glad the author found a solution with a tree of models and message passing. But personally, I'd rather have a Model with 100 fields than the complexity of sumbodels.
Very coincidentally I actually found myself needing a Go TUI library for a small program I wrote just last week with pretty simple needs (some basic dynamic list views including a file picker view) and I spent about half an hour messing with bubbletea before tossing it away and switching to tview
I don't doubt that bubbletea is a far more elegant and powerful library than tview for writing Go-based TUIs but for my relatively simple application I didn't need that power and yet bubbletea still expected me to pay for it in terms of understanding its architecture at a pretty deep level just to make it do anything at all.
It very much does not adhere to the idea of "keep the simple things simple" (which IMO makes it kind of a strange fit for Go, as that's the primary thing I love about Go).
And this is coming from someone who spent a lot of time in the past doing C-based Win32 programming which actually has a lot of similarities to bubbletea's message-based architecture and despite that I still couldn't be assed to deal with learning bubbletea's complexity when my needs didn't feel like they called for it.
1. Draw a circle 2. Draw a lower overlapping circle 3. Draw the rest of the owl.
> go func() { […] m.content = "initialized\n" }() […]
> …but only once a key is pressed several seconds later does it return:
> initialized
> For this reason, making changes to the model is best done in the normal message flow, either in Update() or via a tea.Cmd.
Not just best practice, but absolutely necessary because the code has a proper race condition. It must absolutely be avoided at all cost. Fortunately, it’s easy to do: never mutate the model outside of the event loop (ie a separate goroutine). Fortunately Go’s race detector is generally excellent at detecting this at runtime as well, so it’s always a wise idea to add -race to your testing/QA process. It surprises me Google built such an awesome and easy-to-use tool yet few seem to use it.
Great post btw. I use a homegrown similar architecture myself. I do wish Go had better dispatch architecture than a switch statement but it’s not the end of the world.
That said, I've bookmarked this excellent resource should I decide to pursue that path soon. Thanks for the write-up!
A terminal user interface for terraform power users: https://github.com/leg100/pug
Was hoping for an affiliate program guide for bubble tea advertisers
We're building a lot of SSH apps over at https://pico.sh