74 points cat-whisperer 2 days ago 15 comments
But how? How does it all work? I proposed we just do it the Ratatui way, but my friend was like, nah! We shall make it possible to build TUIs in Lua. (Weirdo!) But it paid off, and we got ourselves a shiny new Lua library, ito!!!
He didn't get the gravity of the situation. We had an awesome terminal UI library in Lua! I wanted everyone to know how cool that is, so I got cooking, wrote myself a custom canvas implementation of this library instead of the usual terminal adaptor, exported it as wasm, and made this super cool website where you can experiment building awesome UIs in Lua. For vibe-coders, we got an llms.txt too. Have at it, and see if you can build something really cool with it. The library's at github.com/uji-labs/ito, and the awesome sauce harness is at github.com/uji-labs/uji.
I hope you like it!
hollowturtle 2 days ago | parent
cat-whisperer 2 days ago | parent
Yannik_Sc 2 days ago | parent
ui.Button "Button Text" {
on_click = function()
print("Hello world")
end,
disabled = true
}
or ui.VStack {
padding = 31, -- Named key => option
wrap = true,
ui.Button "..." {}, -- Indexed by number => children
}
feels more Lua and slightly cleaner imootikik 2 days ago | parent
To me it is more about expressiveness and usage of basic language features. For example here's how a text field is set up currently.
ito.TextField(draft)
:label("Name")
:focused()
:on_submit(function(value)
if value:match("%S") then
phase.value = "review"
end
end)
:on_cancel(function()
phase.value = "choose"
end),
The field is created on the first line, and then altered further with subsequent method calls that alter it. This reads like someone very used to Rust (and its Builder pattern) and then translating it verbatim to Lua. A more native Lua interface for me would look like this: ito.TextField(draft, {
label = "Name",
focused = true,
on_submit = function(value)
if value:match("%S") then
phase.value = "review"
end
end,
on_cancel = function()
phase.value = "choose"
end),
})
As in: just pass a table to the builder. And if they ned to modify it later, they can just do: some_textfield.on_cancel = <some other function>
No need to "guard" the internal table with those extra methods.cat-whisperer 2 days ago | parent
maybe it’s a bad idea :X
cat-whisperer 2 days ago | parent
buffer_overlord 2 days ago | parent
jrop 2 days ago | parent
Since you mentioned you're Neovim nerds: you may enjoy this: I build a declarative rendering engine on top of Neovim itself: morph.nvim [1]. Every Neovim install already has a grid, extmarks, floating windows, and a Lua runtime. On top of morph, I've dog-fooded 14+ interactive TUI applets -- docker, k8s, systemd, AWS, GitHub, HN reader [2].
Aside from declarative rendering, morph does a lot of other stuff: region-specific keymaps, read-only regions, extmark creation/managment/etc.
bluerooibos 2 days ago | parent
hnlmorg 2 days ago | parent
All TUI means is that it runs in character mode.
farmeroy 1 day ago | parent
aa-jv 1 day ago | parent
Its also very flexible and allows a great deal of coding styles. I like the declarative API!
cat-whisperer 18 hours ago | parent