109 comments

[ 548 ms ] story [ 220 ms ] thread
I appreciate the innovation happening with TUIs in these modern times. But, aesthetically, I have to say, I like my borders to have some breathing room, which gets lost in this particular treatment.
My thoughts exactly. Borders costing a cell means you are forced to be frugal with them, lest you run out of space or have to do ugly and clumsy "pwease make ur window biggr :3" messages

One of the top 5 "smells" of LLM generated websites that I can't stand is everything has to have its own round-rect. Everything in a box. Every button, label, header, subheader, so much fucking noise everywhere. You shouldn't need that many.

Another, of course, is the phrase "The wire format", included in this post. Oh yeah, its the format on the wire? What wire? The wire between my psuedo terminal and my kernel?

Would be cool to get borders and lines out of copy/paste
I love this work and everything else Ratatui does but I have to ask: if you're changing the terminal protocol and the terminal itself to do this, why not do something more ambitious; a full monospaced information-dense UI framework, a terminal designed for user interfaces, rather than one designed to replicate the limitations of dumb terminals?
Text terminal with every keystroke round-tripped through the server is not a design that makes sense for any reason other than backwards compatibility. It's kind of annoying to design for and atrocious for performance.
Teletypes and dumb terminals managed to implement local echo. You don't need to round trip everything.
I actually am exploring exactly this. Ironically from the perspective of someone that was trying to leave the Terminal.

How i got here i find kinda interesting. Originally i was trying to leave the terminal. Using GPUI/etc frameworks. I kept running into the terminal though, because no matter how nice the UI is - the terminal still needs to work excellently.

Eventually i decided to try an abomination, to use a custom terminal which, not unlike Kitty images, allows generic apps to pass escape codes to control the UI components. Where the UI components have no restriction on "dumb terminals". So my text editor is a two part app, an editor and a tty to render the actual custom UI APC codes.

This paved the way for true smooth scrolling from within a terminal editor, as the editor effectively paginates but the tty scrolls within the buffered pages. It allows for different UI components to mix fonts and font sizes, eg popup docs being a different size than the other text, and .. well, anything i suppose.

I have to say it's working excellently and is slowly becoming my daily driver. I won't link it though, it's a fully vibe coded and even claude designed horror.

I'm surprised i've not seen more of this in the past to be honest.

I am running ewm (not exwm) which makes emacs into a wayland compositor and it's fantastic, most of the primitives dissolve and you script it with elisp, wayland windows are just a special buffer and where applicable normal windowing and buffering rules can be applied.

The developer seems to have used AI heavily in the elisp code and it works very well.

> To use a custom terminal which, not unlike Kitty images, allows generic apps to pass escape codes to control the UI components.

> I'm surprised i've not seen more of this in the past to be honest.

This is what emacs is. It's a text buffer, but text can have properties like how to render, and custom values. then a bunch of commands to manipulate the buffer and it's contents.

> a full monospaced information-dense UI framework, a terminal designed for user interfaces, rather than one designed to replicate the limitations of dumb terminals?

I can only speak for myself but I use the CLI and TUIs specifically because of the limitations those modalities impose on software that uses them as the primary method for interfacing with the end user. If you want a full featured UI and all that comes with it why not just use QT or GTK or modern HTML or etc?

I think the only "improvements" I'd like to see are thing such as metadata regarding what the different streams are for, an official way to communicate using structured data as opposed to raw bytes, that sort of thing. Spiritually in the same ballpark as the difference between HTML before and after CSS.

Yep,and I want to do that too :)

I've written about it before, but in brief the mountain of standards is worth ripping out wholesale and doing write as a semantic protocol. It's needed to solve several things that work really poorly in terminals because their unit of compatibility is cells rather than a higher primitive. It's hard to bake something on top that works well.

The need is most strong in Accessibility, Text wrapping, Scrollback handling (i.e. making the scrollback editable and not dead text), and various other bits that make writing highly functional TUIs. Coding agents tend to find all those edge cases.

This often gets push back that it's easy, to which I can point nay sayers at the various many months long issues on things in Codex, Gemini, Claude etc. on flickering, scrolling, key handline, IME, ... doing anything moderately complex involves modeling a terminal in a terminal app effectively.

I think I mostly just want to see someone do it so I can see what it looks like; it just feels like something that should exist. You're taking baby steps towards it! :)
I kinda keep talking about it in the hopes that someone will be nerd sniped enough to throw tokens at it as an idea. Sort of what you're probably doing too I guess. ;)
... Write up a more complete spec of the problem and I could be tempted to throw some tokens at it. But before I do, can you write up a more complete description of the problem with existing terminal protocols? I don't think I understand the issues well enough to make a good solution. Eg, can you give some examples of what apps are hard to build now? What prior art is worth looking at? Etc.
> Coding agents tend to find all those edge cases.

Do they? Last time I checked you still couldn't select text via the keyboard in the Claude Code TUI.

It's amusing that the flagship products of the post-2023 AI era can't fully handle 1972s teletype interfaces :-)
You can save all that effort and instead come up with a CSS framework that enforces monospaced fonts, uniform font sizes, encourage information-dense layouts, have borders that don’t take up a cell (border: solid 0.1rem black; padding: 0.5rem;). Pair it up with a small JavaScript framework that listens to keypresses and buffers them.
Because it will turn into the kitchen-sink mess that the Web is.

Terminals work so well because there's a huge frictional cost to adding to the protocol. That's what keeps the protocol simple, which is what keeps multiple implementations feasible. Unlike the web, where unless you have GoogleBux you can't afford to implement it properly.

Or even more ambitious - ditch the monospace
For a while in the 2000s I tried programming with Verdana in my IDE at the time. It wasn't all that bad. It would be in a terminal however - fun idea regardless.
Have you tried this? It’s awful
everyday we get closer to building emacs
Whenever I use dumb terminals, I have the same feeling. I am also actively looking for a way to get a good developer experience while keeping more ownership of what gets generated with the new tools. My specific bottleneck is working in remote environments and not being able to have it , now that we have tools like Claude, Codex, and Gemini. (I find them safer to run them in virtual environments and working on them remotely is better than doing it locally.)

I also don't like the fact that each TUI application (btop, for instance) comes with its own color palette. So, a friend of mine developed a terminal (https://bateri.dev, macOS only for now, but the plan is to port it to Linux later) and yet another protocol where terminal SPAs can communicate over standard SSH (inspired by X over SSH, but we didn't like its drawbacks). The SPA itself is a set of Lua scripts that runs in a sandbox inside the terminal. The application on the host that communicates with the SPA can be implemented in any language (preferably Lua to keep it simple). With this approach, all SPAs (plugins) adhere to the user's color palette preferences. (There are more details that can share if you are interested.)

So far, to stress test it and help with devex, he has built a text editor (https://github.com/bateri/textpen) and a Docker/task monitor (https://github.com/bateri/dockerpen). I am also working on a plan document viewer that works within the terminal. We'd love some feedback if you are interested.

In other words you want a library that wraps X11 and displays a character grid in a monospaced font.
What if, dare I say it, we just served HTML over ssh, and let the browser render a proper UI, instead of layering hacks on top of hacks?
Honestly why? It is not designed at all for that.

In fact it just makes it less secure.

You mean serving html over SSH?

It can already port forward so you can basically already do this, I don’t think that’s insecure per se.

Doing it through port forwarding is kinda bad though because generally access to network ports is not very well access controlled. Doing it through unix sockets (like x) would be much better but annoyingly browsers do not support http over uds

https://bugzilla.mozilla.org/show_bug.cgi?id=1688774 etc

Ssh lets you forward arbitrary ports, and forward X11 directly. Why is HTML more of a problem?
UI in a browser is a massive hack in itself, a clean design would be better. For example a compact semantic remote access protocol to use with UIs, something like SPICE but better optimized for networks, fully presentation-independent, and less janky. This would let everyone use a GUI framework of their choice with an adapter. But people building terminal GUIs are culturally not ready for this, they'd rather continue reinventing the wheel and piling up hacks in terminals. I wonder when they realize that proportional fonts are more readable when vertical alignment is handled separtely, and introduce terminals with variable-width fonts. And the presentation layer (I suggest calling it CSS).
I think the other side of things is why have the graphic program run over ssh, just have raw text cli tools to manipulate what ever then have a program driving it. This for me is where emacs excels and some programs already fit well with this model and the fantastic (albeit hacky) tramp.
No, i really don't want to have electron level bloat as a terminal
A much better alternative to this would be to for more terminal emulators to implement overstrike mode (DECSET 20, as available on the VK100). That way you could just draw border characters over existing text cells to achieve the same thing.

Unfortunately mintty is the only terminal emulator I'm aware of which supports overstrike [0], as this would open up a world of new possibilities for TUI apps if more widely supported.

[0] https://github.com/mintty/mintty/wiki/CtrlSeqs#overstrike

I’m not against overstrike, but I can think of one drawback of that solution: it becomes significantly harder to have rounded corners with a filled interior.

But I’m not trying to argue for nor against either solution.

How is it harder? If I understand correctly overstrike is a superset of the regluar 1-char-per-cell mode, so nothing is lost by going overstrike?
Because the background colour isn’t bounded by the border colour using overstrike, but it can be when using borders.

This isn’t an issue if you want square borders. But the screenshots in the article showed fancy rounded corners.

There's some downsides to this - I can't recall where I called this out off the top of my head, but this also adds a bunch more things that that wouldn't do that are covered in the spec.
If you mean your proposal adds more things, that sounds like it might be over-complicated. To solve the problem of tightly-fitting boxes around terminal text -- if you consider that to be a real problem -- overstrike sounds like a really elegant solution.
Which things do you consider in your experience of writing tuis that are mentioned as being wanted in the blog post / rationale doc linked to be unnecessary?

If you want those things then overstrike is insufficient.

cell borders sound even more elegant imo, they are a targeted, precise fix for a useful feature. overstrike sounds messy to me though I can see where it has a lot of uses.
Expecting Terminal Emulators to support overstrike is a high bar even many text editors don't even support strikethrough. Would love to see it though
Overstrike is underrated!

I've really enjoyed using it on paper: https://hughpyle.com/ASR33/

Hey, this was super cool!

Ages ago I had found a software that would convert an image to ascii-art, and then used another tool called posteriza to split the large image into several A4s, which I then taped together and hung on my wall. Would love to do something similar but with better tools.

Overstriking support might be nice, but it wouldn’t help much for this use–case.
If it's not obvious how this could be achieved with overstrike, basically the idea is that you could draw text on a cell then overstrike the cell with 1/8 block characters for the 4 edges characters (U+2581 & friends) to create a border around the cell.

Edit: HN stripped the characters in question from my comment

What makes it much better?
Terminal emulators are called emulators because they mostly emulate DEC's terminals, which gives them a shared spec to follow, and this escape sequence was already in DEC's terminals from the 80s. It's not a new extension we need to agree on like TFA's, although some extensions have been successful too.
Huh, I'm not sure whether it is the same protocol, I know of this: https://vtdn.dev/docs/sgr/sgr-overline

The syntax is:

CSI 53 m SGR 53 — Overlined CSI 55 m SGR 55 — Not overlined

which is much more different than mitty's Control Sequence, but supports much more emulators

The better alternative to most of this is to use a different display protocol, one that gives you pixel-level control, if you want pixel-level control.
This protocol would still be used for user interfaces, but it would use a completely different approach to rendering them, I guess we could call this approach "graphical"?
Why wouldn’t this work over tmux? The authors claims don’t make a whole lot of sense because if this were a cell property, like any other CSI, then it’ll get rendered in the terminal emulator exactly as intended. And we know this because none of the other CSI sequences get garbled over multiplextors.

But for there to be a dedicated section on how multiplexors wouldn’t work suggests to me that the authors already tried this via tmux.

So I’m left wondering what they’ve done with the implementation of this to make that a problem.

I spotted they are using DCS sequences. But even that shouldn’t be an issue itself.

Tmux drops DCS that it doesn't know like this. That aside, this is extra state per cell that needs to be tracked, handled, understood from a rendering perspective etc. It can work, but would require maybe a thousand lines of code or so to support storing and handling state, redraw etc. and all the various edge cases.
Really? That surprises me. Not saying it’s untrue, just that I’m surprised. I’ve found tmux doesn’t drop CSI, OSC nor APC.

I wouldn’t recommend APC because Apple Terminal doesn’t follow spec with APC parsing (one of the reasons I switched some of my custom sequences from APC to OSC).

Regarding the extra state to be tracked, tmux wouldn’t handle that cell state. That would be down to the terminal emulator.

Since these are cell properties, it does feel like CSI would be the right place (like Kittys undercurl).

This is gorgeous and I need it in trunk for both kitty and ghostty
How much RAM does this use?
Nothing really - Ghostty has a bit of padding that's unused per cell - 16 bits. The implementation stores an ID into a lookup table with border combinations. If all of those were used and never cleaned up, 65535 at around 100 for each combination used, order of magnitude 25MB absolute worst case assuming no real optimization in place for this.
There's a nice thread about this on lobsters, but the author is getting piled on.
I upvote naysayers comments there as I don't think votes should be used to indicate (dis)agreement at least not without commenting why, more an indication that this is a useful thing to discuss. So if you see my comments not getting votes while every other one has a bump, that's my vote on them :)
Same here. Alas, it's the zeitgeist that everything runs along tribal or political lines these days, but it doesn't have to be that way.

I like seeing different points of view, and maybe controversial topics should be discussed () more than others, not less.

and discussed means fact-based, civil discourse, not piling on

The big thing to note on this is one last caveat. I don't care about the actual spec on this - happy for that to be whatever works. I care more about the capability than how it's done / implemented.

There's some bits that feel like they probably make sense to consider doing other ways, like how border placement works (inside, outside, on the border, etc.), and how the filling works - to make borders incorporate external or internal padding.

This is pretty cool but this does feel like we are just reinventing another GUI display at this point.

I don't generally like TUI's and think one gets more flexibility with raw text interfaces and being able to glue things together is more important then them looking nice. I think less fancy out put is better to be honest.

I completely agree.

If we must embed 2D interfaces in terminals, I feel like we'd be better off putting in iframes or canvases that occupy a dedicated rectangle of cells.

Everyone has different priorities. For me, the main benefit of the TUI is the brain-dead ease in which I have windowed applications running over SSH.
ssh -Y worked well for me. That usecase should be revived in a bandwidth efficient manner (eg. RDP does bearable over crappy links, eg. over the ocean) instead.
I have vibe coded a remote X/Wayland solution that actually works really well for this, at least as long as you are mainly interested in one machine (so good for people with remote work machines, not so good for network/infra engineers).

You just run `mycommand connect host` and it will connect via SSH, start a Wayland server with X support, set up the appropriate environment variables, and also automatically reconnect, so you only have to do this once.

After that you can just run GUI commands via SSH and appear locally like you always wished they would. It uses a VNC-like image data protocol so the client works on any OS. And windows are seamless - no windows within windows nonsense. I haven't put much effort into the video protocol tbh but it's already better than Xpra and very usable with 100ms latency (London to NY) so that's good enough for me.

I'll try to open source it.

I think a better solution would be if SSH itself supported some GUI channel, but the only thing is the Kitty graphics protocol which kind of sucks tbh. I did try writing some tools using that for the GUI a while back but it's awkward because you have to use PNG, you're still stuck with the character grid for text, etc.

100ms sounds good, but how about the consistency of the GUI (remote vs local) and HiDPI issues when it comes to this approach? (I assume latency would be higher when there are many applications running in many windows?)
Love this. And the people who like more space can opt for that (this doesn't look things in).
> If that bothers you, feel free to close the tab.

I probably missed on something, but I stopped there

Normally I would too, but after reading the article, I really don't have any complaints. Disclosure is appreciated too.

Whatever the author is doing works reasonably well to be readable and inoffensive.

Having markdown tables render with zero-cell spacing (use underlines for horizontal and different background shades for vertical) is just one of the billion reasons I will never, ever, ever go back to closed-source coding agents.
A pet peeve of mine is that most people / WM get "useless gaps" wrong. You do want a gap between two terminals because otherwise there are times where things can get really confusing (where you don't know what belong to which terminal / window).

So the "borderless" (0 pixels) / zero "useless gaps" simply are confusing and... Wrong.

But then people who do use useless gap, as in TFA, get it wrong too: they typically add gaps near the monitor's edges.

Yeah. But no. Sorry but my monitor's edge is a perfectly fine gap. I don't risk mistaking it for anything.

So I modified my WM (the Awesome WM) to do proper "useless gaps" computation and remove the ones that really are useless: and that'd be those near the monitor's physical border.

I mean: nearly everybody gets it wrong, but it's not complicated to get it right, is it?

P.S: even the terminology is wrong... People calling those "useless gaps" haven't got a clue: they are useful. The only useless ones are the one on the monitor's edge (if your monitor has physical edges, which it probably does).

Man, I’d really like a monitor with no edges. One that just keeps going and going, past my window and the tree outside until it’s completely out of sight. 100% useful area.
This was intentional to highlight the borders more obviously.

The test app has a key to turn that off see https://github.com/joshka/ratatui/blob/joshka/cell-borders/C...

The side gaps were for for making it feel balanced in the screenshots and visualizations to be able to show where the borders line up with cells easily etc. It wouldn't be what I'd use in a real app.

One thing to note though is that some things which are "wrong" can be aesthetically pleasing to look at and may be the actual best UX in certain situations. There's a lot of new design principle work (especially in macOS / iOS) that is about things going past the edge, or being overlaid with floating things. Some of this is "wrong" some of it is wrong, and some of it sitting in in the useless gap beteen the two. ;)

“That don’t cost a cell.” Can’t stand AI bro speak.
“….that don’t cost a cell.” How much do I hate AI hustle bro passive riddle writing.
Why do you people have to ruin terminals? If you wanted a web browser, we already have those, and they already have borders.
I think that's a fair question. It seems that people want more keyboard navigation and (sometimes) monospace UIs, but that should be perfectly feasible in a web UI.
I am in general against the big push to fit GUI interfaces into the tty, but I have to say that this feature looks genuinely good. Vim will look great with this.
Thanks for that slop warning, on a general point: wondering whether there might be enough momentum to finally ditch the whole terminal garbage and use proper GUIs in spite of what the card-punching devils intended?
Sure - I use GUIs most of the time despite maintaining Ratatui. I much prefer them for coding agents as the amount of interactions and various things I want out of the interface are not handled by TUI well. GUIs are a vastly superior interface (I worked on the Codex TUI at OpenAI btw).
This funny to read! I drive Codex TUI daily. In the past, I paired it with VSCode for better code navigation and file reading; seems similar to what you are saying or do you also mean Source Level Debuggers, etc.? But, I eventually worked on Kitty terminal typesetting instead and now reading the files with my gloss tool [1]; combined with Rex blocks it's really nice, easier on my eyes.

Thanks for Ratatui, which certainly has a top tier name, and also has had tons of great software written with it! I've been inspired by much of that ecosystem, especially the charting libs. The weird GUI stuff I'm doing with Kitty+Bubbletea, can certainly be done on Ratatui, in the spirit of Makepad. IMO, Golang needs this more than Rust.

[1] Viewing the gloss README in the Gloss web viewer (Golang WASM in a Ghostty frame), press 't' to typeset it. Alpha release quality. https://nimbleterminal.github.io/gloss/app.html?src=https%3A...

As awesome as this is, but I can't shake the feeling terminals are becoming lightweight browsers...
What I'd really love to see instead of all of this is an OS that has composable areas that can interact and have event loops of their own. E.g. ditch the app boundary and compose. We're getting there sorta with AI Canvas type things (e.g. github copilot app, some of the stuff in chatgpt visualizations), but all of this sort of work tends to devolve to browser type things instead of simpler components. This is mostly unrelated to the terminal side of things though from my perspective on this. But it's a fun thought experiment.
That was X11.
Exactly. Any GUI element is a "window" and composed of other sub-areas, also windows. You could create Windows in the "root window", then the WM would manage then and draw window decorations, but in theory you could also just render a window in a certain area within another window (e.g. a full application, or even just a single view or editor of an application).

I'm not too familiar with higher level frameworks like Qt or gtk, but I suspect since they just draw the whole thing, that they only use single X windows and don't compose them, so you wouldn't be able to render a subview of them inside a different context.

Compatibility will always be the hard part with new standards like this. Given that most terminals already have some special handling of box drawing characters, I don't know if it's completely dead on arrival for implementors, but I don't love as an application author with ratatui, not knowing how the padding on my app will look. I might honestly rather disable this and know it'll be more consistent, than enable it and fragment my UI.
The ratatui change was a pure PoC, isolated branch, my own fork, not the ratatui repo even. This would likely never ship as a default approach as it would be way too breaking. I haven't really spent any time on making it hook into the current block border approaches - it's just at the cell level where this is opted in. That will never merge without much much more though into things.

I used ratatui there mostly so that I could prove that it would work in real looking apps that do panels and similar stuff that benefits from borders.

But yeah, compat is key. It would probably go out to ghostty, rio, kitty pretty easily, terminal.app never, other terminals much slower. IDK about the windows / linux side of things.

I used to be an active maintainer for Alacritty. I expect uphill resistance to this there, though that won't surprise anyone who uses it.

BTW, love ratatui. I've been building ratmark lately, for markdown rendering. But I really need link support ;)

I kinda like the examples without the native borders…

The charm of the terminal is that you can do whatever you want, as long as it fits in one cell.