This is how games were written back in the day before DirectX was a thing. You'd write directly to the frame buffer and instead of clearing and redrawing, you'd redraw what changed and what was around and under it (because there was no time to refresh the entire view in time in addition to everything else you need to do)
Alright, so we're using some bastardization of CSS as well? That might be going a little bit too far. The react model already breaks the idea of CSS in a lot of ways, preferring standardized components. Sure, developers still use CSS to customize components, but I view that more as a side effect of how react evolved rather than as a justifiable architectural choice. But as long as you don't have to use CSS I suppose it's fine.
Last I tried it, you do have to use CSS. There are no good standard components, so you will be making your own, and instead of having components be one nice self encapsulated Python class the standard docs use things like list components and then style them with an external style sheet.
For those reasons textual just isn't for me yet. In python there should be one, and preferably only one, obvious way to do something. By mirroring react so closely they're also mirroring what I see as the JavaScript communities biggest vice.
This includes a lot of what you'd expect from HTML, classes, CSS, etc.
They have reactive attributes https://textual.textualize.io/guide/reactivity/
It has HTML (or at least a DoM), css, and you design widgets the same way.
I am likely just dense and uncreative, but the truth is, when I switched from DOS to Linux in the 90s, I was never again as productive as I happened to be with B800. Granted, it likely took me a long time to understand the need for double buffering and the difference between a local/direct text mode vs a terminal, let alone escape sequences. But still. Whenever I tried to do something directly in ncurses, I pretty much gave up due to a distinct feeling of being unhappy. Completely different to what I was able to do with the simple ideal of B800.
It's not a virtual DOM. It's not technically even a DOM, because there is no Document. The name has stuck, which is why we went with that. Technically, its a tree. One of the most common data structures used to represent a UI, and predates React by decades.
> This includes a lot of what you'd expect from HTML, classes, CSS, etc.
It has CSS in common with HTML. classes are pretty much required for CSS. That's not "a lot". But why shouldn't a UI framework borrow concepts that work for, you know, User Interfaces?
> They have reactive attributes https://textual.textualize.io/guide/reactivity/
Reactives attributes are very useful concept to manage UI complexity. And again, not exclusive to React.
You don't have to use CSS (actually you never did). Every style can be set in code, and the docs have CSS + Python equivalent for every style.
> There are no good standard components
I guess its been a while since you checked https://textual.textualize.io/widget_gallery/
If you care about accessibility even one bit, for the love of god, please, don't use any of the features this post mentions.
Things like animation or unicode diagrams break screen readers in horrible ways.
The most promising way forward for accessibility is the web support for Textual. It would be possible for the app to work with the browser to make highly accessible TUIs.
Turbo Vision, curses and dialog were cool back in the 1990's.
Having started with computers in 1986, I really don't get the TUI fetisch, not even remote access is an issue, given X Windows, VNC, RDP, Citrix,... exist for decades.
For example, YouTube link in the article showed a possibility to display table with highlighting cells. Why would I need that as TUI? Probably if I want to navigate through table with highlighting active cell I would also need a bunch of other stuff and eventually I would need a proper GUI.
There are libraries like Unidecode[0py] [0go] [0js] which convert from unicode to ASCII text that might be easiest to include in a TUI. All the ones I looked at will convert emoji to `[?]` but many other characters are converted to that, too, including unknowns.
On the other end you can keep a running list of what you mean by emoji[1] and pattern match on those characters, then substitute for a representative emoji. But it will still pose some difficulty around what to choose for the representative symbol and how to make it fit nicely within a TUI. An example of a library for pattern-matching on emoji is emoji-test-regex-pattern[2] but you can see it is based on a txt file that needs to be updated to correspond with additions to Unicode.
[0py]: https://github.com/avian2/unidecode
[0go]: (actually there are a few of these) https://pkg.go.dev/github.com/gosimple/unidecode
[0js]: https://github.com/xen0n/jsunidecode
[1]: these aren't really contiguous ranges, and opinions vary, see https://en.m.wikipedia.org/wiki/Emoji#Unicode_blocks
[2]: https://github.com/mathiasbynens/emoji-test-regex-pattern
However, I'm not sure what a "proper" GUI is. Terminals have a widely used open standard. These protocols are more standard and interoperable than any GUI framework, and they are supported on every system. Once you add video in there, close a few more gaps, I think competing with the browser, or bringing the full computer experience to the terminal is reasonable.
Obviously if terminals add something like video, it's "copying" rendering pipelines from "proper" GUIs, but making the terminal experience better, which could also be viewed as bringing UNIX zen to GUIs, if it makes it big, you'll love it too! <3
hmm, well, i guess traverseda was sort of saying that react was bad. i doubt that's a widely shared opinion tho
Isn't it possible to expose this content via a publicly accessible API that screen readers could simply hook into? BTW, thank you so very much for Rich and Textual. Wonderful tools. Love 'em.
Personally I found Textual a little weird to use, but better than ncurses. Though it didn't really yield what I wanted. I like the old mainframe style TUI application, those already struck me as being wildly efficient.
> Isn't it possible to expose this content via a publicly accessible API that screen readers could simply hook into?
Definitely possible from a technical standpoint, but I don't know of anyone who has done that. Maybe one day, Textual could provide that solution.
I maintain two TUI libraries which use this technique and emoji support has been (nearly) great. (One of which uses your uniseg library!)
https://mitchellh.com/writing/grapheme-clusters-in-terminals
jesus, i sure have built shitty user interfaces on a lot of platforms
So, imagine a normal GUI window, but one of the components in it is a terminal window. Is there something like that?
Or should I just use mono font text view?
https://yoavmoshe.com/blog/learning-swedish-with-sway-and-an...
I have complained about this not existing many times and at length, and have actually been thinking about how such a think could work.
It's a great idea, but we'd need support both from screen readers, TUI libraries and some terminal emulators, and I don't know if we could get that to happen.
The employees work by keyboard shortcuts and are extremely efficient, and every time someone tries to replace the AS400 with a modern web app, their productivity drops 100x.
It’s the same scenario as a vim/emacs wizard vs a slick looking GUI that doesn’t have keyboard shortcuts.
Additionally, it has a hacker aesthetic. The styling is aggressively not separated from the content, and the styling knobs are pretty limited, so tui apps kind of converge on a single style. That style reminds us of hacker movies and cool sci-fi shit :)
It's not loved by corporate designers. Companies don't make sales based on their TUIs (to either businesses or consumers). So without those commercial pressures, tuis are designed by developers for developers.
Because of this, the meme of tuis self-reinforces. Developers see and use TUIs, notice that they are usually tools built with developers in mind, and then want to go on to make their own TUIs.
But once a fixed-width text grid stops being the right tool for the job, it's likely better to have a web UI.
In graphics mode it was, IIRC, 0xa000. I once wrote a pacman-type easter egg inside the point-of-sale system[1], because doing direct graphics straight to an address is so easy and simple.
[1] Was removed after pilot and before actual release.
The first is to write to another buffer (possibly in normal RAM, not video RAM), then when the frame is done copy the whole buffer at once, so every pixel gets changed only once.
The second is to write to another buffer that must be in video RAM too, then change the registers of the graphics hardware to use that buffer to generate pixels for the monitor to show.
They had different tradeoffs. Copying the whole buffer when done was expensive, changing an address register was cheap. But the details of the register were possibly hardware-dependent, and there was no real graphics driver framework in place. Also, to just "flip buffers" (as changing the address register was called), rendering to the off-screen buffer meant sending pixels to video RAM, which was (IIRC) slower to access than normal RAM (basically a NUMA architecture), so depending on how often a pixel gets overdrawn, rendering in normal RAM could be faster overall even with the final copy taken into account.
Did this change with 3dfx's Glide (or subsequently Direct3D once Windows got a foothold into the gaming industry)?
Yes and no, it would be possible, but only if the terminal emulator, the operating system's accessibility API, the screen reader and the library supported it. The library needs to output the right information via escape codes, the terminal emulator needs to expose it via the OS API, the OS API needs to have support for it, and the screen reader needs to know what to do with the information. I believe you could wrangle the existing a11y APIs and make it work, but you'd still need to implement support for it in emulators, libraries and screen readers. Not to mention that you'd actually have to design the standard and write a spec for it.
This would be slightly easier to do for terminal screen readers, which run directly inside a terminal emulator (kind of like Tmux) and pass most commands and keys unmodified, generating speech in the meantime.
(I work at a company automating medical billing.)
Rather than using this as a heuristic, couldn't you simply use this whenever you need to determine the width of a string? Write it to the terminal, if it ends up going farther than you expected, then you need to wrap it somewhere. I used a trick like this at one point after I got annoyed with wcwidth.
For layout concerns, fractions are probably a more natural fit (things like 1/3), but Decimal is a great floating point default for a lot of problems. They aren't exact, but a lot of the "normal" floating point weirdness goes away and your results look a lot more human. Highly recommend if you don't have a perf reason not to.
I'm adding that to my CLI project. https://youtu.be/NxsaHxON350?si=319RyQPsb5AVDQj9
their slow as hell and incredibly inefficient to build graphics on top of
> but many are powered by the same graphics technologies used in video games
what does that mean? obviously they have to render text to graphics at some point
> The first trick is "overwrite, don't clear". If you clear the "screen" and then add new content, you risk seeing a blank or partially blank frame for a brief moment.
> The second trick would be to write new content in a single write to standard output.
its just luck when any of these work
> The third trick would be to use the Synchronized Output protocol;
this could work...
> With these three tricks in place you can create very smooth animation as long as you can deliver updates at regular intervals. Textual uses 60fps as a baseline. Any more than that probably isnt going to be noticeable.
no, anything above 60hz will be noticeable more smooth and less laggy. also this isnt a question about bigger=better. youll notice jank even if you run 75fps on 60hz or 60fps on 75hz, and of course with unsynchronized rendering there will be jank and other artifacts even with Xfps on Xhz.
> Now that you can have smooth animation in the terminal, the question becomes should you?
nothing you demonstrated was smooth, just better than current broken (terminal) shit. and no, because the thing terminals miss out on is being able to scroll at just a constant pixel/time rate and actually follow the mouse precisely and without lag. as you can see in the video you cant do this because the terminal cant do this period because they can only move one font width/height per step. then if we actually explore this issue well soon find out a 125hz mouse polling rate adds tons of jank, and well discover 60hz is too slow to be smooth even with perfect sync, then that you need a CRT to have legible text at such a slow framerate. your video looks like a slightly faster windows 3.0. which is good for a terminal, but bad compared to what graphics could do even 20 years ago (at 60hz), no your not scrolling at 60hz, because thats impossible in the terminal.
none of this means i want what Windows / GTK / KDE / android / ... came up with where you press a button and the program then starts slowly animating something like its (ironically) a type writer instead of just doing what i said. you lose your position in scrolling because its done wrong, the "smooth scrolling" shit in firefox or whatever is just a poor (oblivious) attempt at the solution
Second, and this is mostly because my personal use cases are very humble, a much, much simple to implement workaround, for everyone involved, would be a couple of OSC sequences which would mark a part of output text as the prompt (when terminal is in canonical/cooked mode), so that a huge chunk of readline could be simply thrown away.
So your program could just print a prompt, and then simply read the cooked line. In the meanwhile, the terminal emulator would handle line editing, line-wrapping and asynchronous output: if you keep outputing text to the terminal while a prompt is active, the terminal would clear the prompt and the unfinished line, print the text, then re-display the prompt and the line; basically what all "async readline" libraries do already with rl_clear/rl_redisplay — but doing it in the terminal would take care of this properly, because the terminal definitely knows how wide all the symbols it itself thinks are. And the tab completion could be supported by returning a <TAB>-terminated line to the program, instead of an <LF>-terminated line.
Unfortunately, I don't think something like this can actually become even moderately widely adopted.
Edit: Or, you know, maybe we could extend terminfo? Like, introduce twcswidth() function that would take your string, and the somehow encoded Unicode grapheme clustering data that the current terminal is actually using which you can query from terminfo, and return the number of screen cells it would take on this terminal.
It’s much better to hyperlink and open image and video in the browser from the terminal. In my opinion. Playing it would just block your terminal session, so it’s pretty annoying to have to open another tab with an inline video playing. I am happy with the features with textualize and haven’t even thought about needing things that play over a duration beyond a few seconds.
Textualize then becomes the browser api that I build my terminal browser and render only the components I care about.
None of that background or js libraries, ads. It’s well worth it.
How about displaying data on cli w/ textualize vs on an admin web interface.
I find it much easier + direct to use some py-orm w/ textualize.
On an admin interface you’d have to worry about auth + some js ui framework that prints your custom html directives to tables… that eventually display text.
Sometimes that works ok (ie forwarding X on a high bandwidth connection), but other times the proper GUI acts like a complete pig. :(
A text based GUI sounds like it might be the best of both worlds.
Out of curiosity, have you looked at it's sibling project "rich"?
https://github.com/Textualize/rich
Seems like it provides a TUI toolkit as well, and it looks a bit less weird than the approach Textual uses.
Was thinking of trying it out with a side project recently, but got pulled onto some other stuff instead so haven't yet started. Nor made the choice between them. ;)
https://github.com/ArthurSonzogni/FTXUI
It's a nice tool to build interactive dashboards with both keyboard and mouse support.
I just want to, e.g. write a simple Python program that has
for line in streaming_response.lines():
print(line)
in one thread, and while True:
cmd = input('> ').strip()
if cmd == 'q':
break
if cmd == 'stop':
requests.post(...)
...
in another, and be able to input my commands without the echo of my input being teared up by the output. Erlang's shell can do that. Readline can be used to do that, but Python's bindings don't export the needed functions. Swapping out the sys.stdout/sys.stdin with my custom interceptors to do this manually... barely works, slow, ugly as hell and complicated.[0] https://github.com/thejoshwolfe/consoline
[1] https://github.com/erlang/otp/blob/90a48ae2bff26d5df67ceaa7e...
https://gitlab.gnome.org/GNOME/vte
Basically its usage starts with vte_terminal_new().
Unicode? Emojis? https://gitlab.gnome.org/GNOME/vte/-/blob/master/doc/ambiguo...
What hurts me? The only thing which hurts me is that the maintainer doesn’t like background transparency in the official terminal application. The library supports it and many terminals use it. But this is another topic. With an embedded terminal you will likely not use that feature ;)
PS: KDE likely has a smiliar solution within Qt. As you named portability either Gtk or Qt are you tools. The biggest hurdle is shipping the libraries on macOS. On Linux it is done automatically. Windows is rather easy. Regarding Gtk, ship the gdk-pixbuf loaders alongside, they are loaded at runtime via dlopen(). The small differences hurt during porting.
Specifically when dealing with remote environments where you connect to through ssh anyway it is really awesome when some things can be done through a nice TUI. In that context a GUI would actually be more resource heavy considering that it likely will be web based with much more client side processing happening for the GUI.
[0]: https://xdsl.dev/ [1]: https://marimo.io/
Sketching out this idea, what I'd want is that ssh would set some standardized env var pointing to unix socket (analogous to $DISPLAY), and applications when starting up should pick that up. That should trigger applications to start listening on another unix socket (instead of tcp port), and notify ssh through the socket pointed by the env var. Upon that notification, ssh should set up new tunnel and open new browser window pointing to the tunnel.
Nothing about that is technically particularly difficult (I'd say its almost trivial), but it'd need standardization to be truly useful.
Of course that would lead to not recognizing weird color codes and so on, but perhaps there could be a plugin system, where one could add a plugin that transforms color codes and such in the output into XML tree representation.
And then one could perhaps log the whole thing in various formats: Only visible text with colors, without colors, whole XML tree, or as JSON, or whatever other format it translates well to. Also could be extended via plugin.
But bare bones it merely treats everything as text.
Yeah but those are all awful. I mean, okay, they work almost okay with a wired network connection and on the same network.
But as soon as you hit the WAN and you throw wifi in there it's painful. Having noticeable latency and graphical artifacts does, in my opinion, hurt productivity. For those pieces of software where completing tasks as fast and accurately as possible is the most important goal, TUIs are great. Especially when you have comprehensive keyboard shortcuts. If you've ever seen an office worker rip through a TUI underwriting a loan, you'll know what I mean.
That would be a browser. Forgive me if you were being sarcastic and I didn't pick up on it.
What are the shortcomings? It always works very well when I use it.
Not just emojis. Recently I've had some fun trying weird Unicode characters in different terminals (e.g. 𒐫):
- QTerminal/Konsole: Tofu
- Xfce terminal: Results in overlaps with characters that comes after it.
- Alacritty: Similar to Xfce terminal, but glitches when the cursor/glyph moves.
- COSMIC term: No overlapping glyphs, except that the line then wraps only after it grows out of screen.
- Kitty/WezTerm: Scales the glyph to fit it into a single column. (Barely legible.)
I don't even known what to expect. It is indeed a mess over there.
Smaller problem is that because the applications all appear as localhost you lose some of the compartmentalization that browsers have, so different applications might end up seeing each others cookies/localstorage/cache/etc.