Search
Will McGugan - SE Radio guest

SE Radio 669: Will McGugan on Text-Based User Interfaces

Will McGugan, the CEO and founder of Textualize, speaks with host Gregory M. Kapfhammer about how to use packages such as Rich and Textual to build text-based user interfaces (TUIs) and command-line interfaces (CLIs) in Python. Along with discussing the design idioms that enable developers to create TUIs in Python, they consider practical strategies for efficiently rendering the components of a TUI. They also explore the subtle idiosyncrasies of implementing performant TUI frameworks like Textual and Rich and introduce the steps that developers would take to create their own CLI or TUI.

This episode is sponsored by Fly.io.
Codegate



Show Notes

Related Episodes

Other References


Transcript

Transcript brought to you by IEEE Software magazine.
This transcript was automatically generated. To suggest improvements in the text, please contact [email protected] and include the episode number and URL.

Gregory Kapfhammer 00:00:51 Welcome to Software Engineering Radio. I’m your host, Gregory Kapfhammer. Today’s guest is Will McGugan. He’s the creator of Rich and Textual and the CEO and founder of Textualize. Will welcome to Software Engineering Radio.

Will McGugan 00:01:06 Hi Greg. Good to be here.

Gregory Kapfhammer 00:01:08 Hey, I’m glad that you could be on the podcast today. We’re going to be talking about text-based user interfaces and command line user interfaces, sometimes called 2E and CLI. Are you ready to dive into the conversation?

Will McGugan 00:01:21 Very much so, yes.

Gregory Kapfhammer 00:01:22 Alright, so you’re well known for creating Rich and Textual, which are two libraries for building CLI and 2E in the Python programming language. And before we get started and go into those details, perhaps you could start by telling us a little bit more what is a CLI and what is a 2E and how are they similar to and different from each other?

Will McGugan 00:01:43 Sure. So CLI Command Line Interface is essentially what you use when you’re in terminal for the most part. You type a command, might have some options and switches hit return and you get response and that’s basically Command Line Interface. So that command is an interface to program software. 2E stands for text or user interface and it’s basically a user interface. So there’s buttons and text and scroll bars, et cetera. But the interface is composed of text, and it lives inside your terminal. So when you run a command, it pops up. That’s user interface in your terminal. You can interact with it in much the same way as a desktop application or a web application. You can work in there and then when you’re done hit control Q and then you’re back to command line. It’s like a user interface that stays inside the terminal when you need it.

Gregory Kapfhammer 00:02:34 Okay, that’s helpful. Now there’s many ways that as engineers we can build and ship applications and use applications. So we could also have a web-based application or a graphical user interface. From your perspective, what are the benefits of using a 2E or a CLI, especially if you’re a developer?

Will McGugan 00:02:52 They tend to be very responsive and very quick and very snappy and there’s less contact switching. I mean I used to be a web developer, I kind of use the web a lot like most people do, but there’s quite a lot of contexts switching when you go between web application, browser tab, et cetera. But the benefits of 2E running in the terminal is that it stays in your own flow. So it’s like everything’s in one place and it’s snappy and responsive and it comes up when you need it. It’s very quick to interact with and then it goes away as quickly when you don’t need it.

Gregory Kapfhammer 00:03:25 Okay. We’re going to use Rich and Textual as like a vehicle for exploring 2Es and CLI in greater detail. So to get started with this next phase of our conversation, can you tell us a little bit about what Rich is and what Textual is?

Will McGugan 00:03:40 So Rich is a Library wrote a number of years ago now and its purpose is basically to write better formatted content to the terminal. People have been using terminals for multiple decades and we’ve run quite custom to the output. It tends to be monochrome, it tends to be unformatted so you won’t even have text wrapping, it’ll just be kind of split into the next line and the output can often be quite difficult to visually parse. Rich basically writes easier to visually parse information so it can format it, it can add color and style and it can change the layout. So you’re basically seeing the same data but it’s just much easier to read at a glance and it gives developers the tools to basically write CLIs which are much more friendly to most developers.

Gregory Kapfhammer 00:04:32 So you mentioned words like friendly and easy to use and I’ve actually either built or used a number of different apps that are written with rich and textual. Can you give us a couple concrete examples and explain why they’re friendly and easy to use?

Will McGugan 00:04:46 Sure. So Rich has a number of different I call renderable in which respect it’s a poor name but basically, it’s just one method of transforming some raw data into some visual output. So you can write a table for instance. Tables are remarkably tricky to do. If you ever try to write a table before Rich, you find it’s very difficult to format all the content, especially when the cells, the data in the table can change size, you know, if it’s too large you might have to wrap it and then you have to pick the optimum number of rows to fit inside the terminal. But Rich does all that. So if you’ve got tabular data, you can write it with Rich and then you can easily review it in the terminal. And most of the renderable are like that. They just take data that you’ve calculate in your app, and they present it to the user in a much easier to read way.

Gregory Kapfhammer 00:05:38 The example with tables in rich is a good one. Can you give an example when it comes to using textual, what kinds of applications or features would we find in a textual application?

Will McGugan 00:05:49 Yeah, so textual adds essentially a layer over rich. Textual uses rich under the hood and it can build interactive apps. So often in the command line you write command, you might wait a little bit and then you’ll get response there. But with textual is an application much like a website or desktop application and it’s something you can interact with right data, click buttons and do your work there. And then these apps can be pretty much anything really. It’s quite often used for developer related tools. Things like interacting with databases, forms, APIs, that kind of thing. It’s for work which is a bit more detailed than your average CLI because a CLI, you just get the data out there is you look at it by textual app, you want to interact with it, you want to do little things, you want to remove things, add, move things. So crud type operations and then see it immediately and then various layers of functionality on top of that. So yeah, it’s basically applications running inside the terminal.

Gregory Kapfhammer 00:06:59 Can you give a specific example of maybe one of your favorite applications that has been built with textual? What would it look like on the screen and how would someone interact with it?

Will McGugan 00:07:09 Sure. So we got Harlequin, this has been around for a while. It is one of the most popular types of tools at the time, certainly. And basically, it’s an interface to databases. So you connect to a database, could be my SQL Postgres or even SQL Light. And you see a tree view of all your tables, which you can navigate with the keys or the mouse. You can click on them, and you can also enter SQL into like a dialogue box. It’s nicely formatted, and syntax highlighted. You enter the SQL, you run the query and then you see results in a table, and you can navigate that table again with the mouse or the cursor keys to explore the results of your query. And that’s become really popular actually because previously if you want to do that inside terminal, yes you can do, of course you can edit a SQL command and run it.

Will McGugan 00:08:02 The data that comes out is probably not particularly well formatted so you might have to take the data and do something else to visualize it. But with Harlequin, everything’s in that one window and it’s easy to use and it’s very easy to explore. One of the advantages I think of textual app over CLI is you can go straight into the textual app and explore it with the mouse and the keys. You can go from knowing nothing about it to being moderately proficient in tens of minutes. Yeah, I really like that one. I think Harlequins a very good example, an app which doesn’t have to be in the terminal. This could be a desktop app, it could be a web app, but the virtue of it being the terminal means that it’s right where you want it.

Gregory Kapfhammer 00:08:43 Yeah, you mentioned the idea of exploration and I think that’s a good one. I often find that 2Es are nice from the perspective of discoverability because I can see various things on my screen and if there’s a tree view, I can click on parts of the tree view and expand them or contract them. And I think you’re right, exploration and discoverability are really important. Now when I think of discoverability, I read something really interesting which is that textual is a 2E framework but that somehow it can also ship it to the web. Can you explain that a little bit further?

Will McGugan 00:09:16 So we have a couple of technologies which basically does just that. The textual app running in the terminal is a default setting but you can serve it to the web. So if you run the textual serve command which comes built in, it’ll create a link. You click on that link and then you can interact with the same app and it looks much the same, almost identical actually to the app running in the terminal. But because it’s in the web browser, you can share it on your network. You can also share it on the public internet if you wanted to. And the advantage of that is basically that non-developers can find it more accessible. Text apps are easy to use but not everyone is familiar with the terminal. Terminals are on every desktop computer ever shipped. But it’s pretty much only developers and technical people that know about it.

Will McGugan 00:10:05 Others might struggle with it, but when it’s in the web you can share that link and anyone can use it. And the textual apps are built such that you don’t need to be technical to use it once it’s on your screen. You might think it has a kind of a retro aesthetic because it’s built up from metrics of characters. But apart from that, everything’s there that you’d expect from any application. You just click things and the click responds to your input on the screen. So yeah, so we have textual serve which runs a web application locally. We’ve got textual web which creates the application and runs it on the public internet so you don’t have to worry about firewalls and serving things, et cetera. The application is just there, you write it locally, you want it to text your web and then you’ve got a public link which you can share with people on your team or anyone else.

Gregory Kapfhammer 00:10:59 So that sounds incredible. When I use textual web, my thing is then publicly available on the internet. Do I have to provision a server or create a digital ocean droplet? How does that actually work?

Will McGugan 00:11:11 You would basically, the app runs on the same machine where you’ve installed textual web, it connects to our server via web sockets and then the public viewer of that link connects to our web socket server. So when they connect to the URL, it opens up an instance of the application on your server. This could be your laptop or your digital ocean drop, et cetera. And whilst that browser tab is open, the user can interact with the application that’s running on your server or servers.

Gregory Kapfhammer 00:11:44 So we’ll investigate that in greater detail when we talk later a little bit more about how you built those features. But I wanted to pick up on something you said a moment ago. I remember you said there’s a terminal on every operating system. So that leads me to two questions that I’m hoping you can address. Number one, are there specific terminal emulators that you need to use to run richer textual applications? And then second of all, does textual and rich applications run on Windows, Mac OS and Linux or are there restrictions?

Will McGugan 00:12:14 This is a complicated question. It’ll run just about everywhere on any terminal. But there are some caveats. On MacOS the default terminal is limited to 256 colors and some of the block characters don’t work all that well. It’s still usable but it’s not pretty. So on the Mac we do recommend downloading and installing one of the other terminals, which are better in any other ways. There is iTerm, WezTerm, Kitty, Alacritty, Ghosty, which is a very recent one, which is very, very good. And the textual experience on that is on all those terminals is almost identical. The same goes for other platforms as well. So they’ll work on Linux. Linux has great support for terminals, the default terminal on any Linux distro, will be able to run textual apps very well. On Windows, there’s a different situation. Microsoft released Windows Terminal a few years ago and its very feature complete and it, it runs textual apps very well indeed. Prior to that, the default Windows terminal was pretty poor. Microsoft hadn’t done anything with it in such a long time. The output of text apps was terrible but if you have Windows 10 or Windows 11 then textual apps will run beautifully on Windows terminal there. So it’s remarkably universal textual apps will run on all the major platforms and virtually every terminal out there.

Gregory Kapfhammer 00:13:41 So I noticed you mentioned that it’ll run on a wide variety of terminal emulators and we can pick the one that we prefer as long as it’s a relatively recent and modern one. Are there restrictions when it comes to the shell that you use? Like can I use Bash or ZSH or Fish or do I have to pick a certain shell for my terminal?

Will McGugan 00:14:01 No, you can use any shell you wish and the text labs will run as they always do.

Gregory Kapfhammer 00:14:06 So one of the things I’ve noticed when I’m building Python applications is that sometimes it’s a little bit challenging to make sure it works across all operating systems. Have you ever faced any of those challenges when either you were building textual or rich or building an app using textual or rich?

Will McGugan 00:14:24 Well definitely building textual or rich, yes. Even though the terminals work, they’re fairly compatible, they use the same protocol. But when you do the kind of work I do, you find the little subtle differences and sometimes you do have to compensate for those. The good thing is that’s all in the textual library itself. If you’re an app developer, chances are you won’t encounter any kind of differences from one terminal to another. Frankly, the situation is a little bit better than browsers because I was a web developer for a number of years and even though there are standards in the web world, there are differences with browsers. So users will get a slightly different experience and you don’t have any choice except to work around those differences. And they do happen. I think the biggest challenge with terminals is emoji support. Would you believe it? I mean when terminals came about, no one could have possibly conceived that people would be sending little smiley faces to each other in the terminal.

Will McGugan 00:15:22 But nowadays everybody wants to do that. And the problem is that terminals have varying support for Unicode and emoji. These emoji characters that came out are sort of relatively recent and the terminal will display them but there’ll be inconsistencies in how wide the characters are. Because some characters can be twice as wide as your average Latin alphabet character and there’s no way for textual to know how wide those characters are going to be. And it will try to stick to the widths that are set in the Unicode database. But sometimes the version of the database that the term is using isn’t the same as a version of database that rich and textual using and you can’t know which is which. So sometimes people will expect they’ll write a Unicode emoji and it’ll break the display in some annoying ways. That’s an example of something that we can’t work around unfortunately that is just an issue with terminals in general. But if you restrict yourself to like a subset of emojis in version nine, then everything tends to work as you’d expect.

Gregory Kapfhammer 00:16:26 A moment ago you mentioned character encoding and you talked about UTF-8 when you were discussing emojis. Can you briefly say what is a character encoding and what is UTF-8 and then how do those terms connect to emojis?

Will McGugan 00:16:39 So Unicode is the standard way of identifying characters. Characters is a very broad term. Letter A is a character and Unicode the smiley face is a character. Also all the Asian characters that Korean, Chinese and Japanese, their character and Unicode assigns essentially a number to each of those codes. But then you’ve got to put it into actual data and there are a number of different ways of squeezing all this list of any code points, which is the numbers into actual bits and bytes. So that’s an encoding. The most popular in coding is UTF-8, which is variable length. So some characters will take up one bite, some characters will take up two, three or four bytes. For the large part you don’t have to worry about that because most terminals work with UTF-8 and those that don’t rich and textual will try to convert the output into the encoding which the platform supports. So yeah, you tend not to have to worry about that with rich and textual. It’s something that we have to concern ourselves with.

Gregory Kapfhammer 00:17:46 So I think the good news that you’re telling me is you’ve done the hard work of building rich and textual so that when I’m writing a program using those frameworks, I don’t have to know every detail about how to get an emoji to appear on the screen.

Will McGugan 00:17:59 No, exactly the developer, you don’t have to concern yourself with the frankly messy implementation details. You just want to write text, whatever that happens to be. It gets quite complicated. One of the major complicating factors is the fact that a character can take up one cell or two cell. A cell is the size that you need to fit in a typical Latin alphabet character. But Asian characters will take up two cells and if you’re just writing the text a terminal, that doesn’t matter too much. But if you’re formatting it in any anyway, if you’re putting it inside a table, your code has to know the exact width the characters in order to put in the rows and lines and make all neat and lineup. So yeah, that’s something that the libraries cater for because you don’t want the developer to have to think about that. If they take in some input and it’s in Latin, you put your name in Latin and Latin alphabet and that that works fine. But then if another user from China puts their name in and they’re using Chinese characters which are twice the width, it should just work. It should just work. You shouldn’t have to get respond to issues about hey this doesn’t work in China, right? As a developer we just want to use libraries and just be confident that things are going to work wherever you happen to be using the program.

Gregory Kapfhammer 00:19:15 So what I’d like to do now is built on what you just said and talk further about how you implemented rich and textual. And we’ll start with rich. When I’ve used rich in the past, I was impressed to see that you can have like a table, or you can have text or a spinner or a progress bar. Can you pick one of those and then tell us a little bit about how you actually built that feature?

Will McGugan 00:19:37 Sure, yeah. Progress bars was a fun, an interesting one. So there’s multiple ways of rendering a progress bar. I mean the older versions going back years would use maybe like a hash or pound symbol then maybe like 20 of them and would add another one as each time as you moved along, which is fine, that works fine but you can do a little better graphically. They’re a bunch of characters in Unicode just for drawing lines. So you can combine these together and you can get more granularity. So the progress bars in rich, you have two different characters per cell, so it looks like there’s more steps on the progress bar. It makes it look smoother. We added a bit of color to that so the bar can be magenta in the background gray, so it makes it easier to read. That was kind of a visual thing.

Will McGugan 00:20:26 But what I found interesting about the progress bar is I did them and I made them look pretty and then I got reports that it was slowing down the code in which it was running, which kind of like sort of defeats the purpose of having a progress bar if showing the progress bar makes your application slower. And this was happening because people were running a bit of code that had multiple steps and this the time between the steps was very small. So each step might have taken a fraction of a second even like, you know, hundredth or thousandth of a second and the progress code would work with that is just, it would update that line of text a hundred times a second and that will slow it down, if you take like something that’s very fast, which rendering the progress part was and you multiply it by a hundred or a thousand, it gets slower. So to solve that, we separated the updates from the rendering. So now if you do like a thousand or a hundred thousand updates per second, it’ll only render 10 times per second. So that means that the progress bars no longer slow down the thing that you’re tracking. The progress of that was a very interesting problem to solve.

Gregory Kapfhammer 00:21:37 So those are some interesting experiences when it comes to actually building a performant textual bar or rich bar. And we’re going to talk about performance optimization later. One of the things I noticed about the progress bars that we were discussing is that you can like decorate them with estimates for how long a task is going to take or how many of those tasks have been completed. Am I remembering that correctly? And then how did you actually build in those additions that go along with the progress bar?

Will McGugan 00:22:06 One of the great things about rich is that you can compose different renderable. You can put a progress bar inside the table. So if you want uh, a progress bar with a few other columns for the speed or the estimated time to completion, you can do that by putting the progress bar inside a table and then adding other things to the columns. And you can even display multiple progress bars. So if your task involves several things, you can track them independently. So you can put whatever you want in there. We have a bunch of predefined columns for the speed estimated time of arrival. If it’s data that the speed will be shown in bytes or kilobytes or megabytes per second and you can customize it. So if you want to put something else inside there, you could as well.

Gregory Kapfhammer 00:22:48 Thanks for that insight. Another thing that I noticed is that a rich allows you to do syntax highlighting of the code when you display it in your terminal window and you can even then factor that into your textual app as well and display nicely formatted syntax highlighted code inside of your app. So how does that work Will? It’s really impressive.

Will McGugan 00:23:11 Yeah, so we have, I mentioned progress bar and tables etc. Those are all examples of renderables. We also have a syntax renderable and that takes code, which could be any kind of code, be Python, C even marked down SQL etc. And then it applies syntax highlighting we’re using the library Pigments that parses the code and then applies color to it and the renderable takes that information and assembles it into one view and they can do features like adding line numbers, also indentation guides. So the syntax renderable takes the code, does all that work, spits out the terminal and it looks nice. It looks like the code that you’d have in your editor and in textual, textual can display these renderable as well because it’s textual is built on top of it. So it takes any rich renderable such as the syntax display and then you can put that inside a window and then you can scroll it with the scroll bars or up down the keys. And that actually applies to any of the renderable, which you built something rich, and you want to display it inside a window. Then it’s just super easy. It’s just the same code essentially. You just do the widget to update given the renderable and then it displays it there.

Gregory Kapfhammer 00:24:26 Given what you just said, there must be a way when you’re building a textual application to define the layout, like the tree view goes to the left and the syntax highlighted source code goes to the right. Can you explain that feature of textual in a little bit more detail?

Will McGugan 00:24:41 Sure. So one of the decisions I took quite a while ago was to implement CSS, which is a browser technology inside the terminal. Seems like an odd decision because CSS was designed for the web and the terminal’s quite a different beast, but it solves the same problems. It separates the code from the presentation. So there are a number of like CSS rules which define layout. You can create a horizontal list or a vertical list. You can also create a grid which is kind of like a table rows, columns and you can span rows and span columns, et cetera. And you can combine those in almost an infinite number of ways. So you can create a layout using just that CSS. So it might seem like a complex thing, but it boils down to half a dozen lines of CSS and then you can tweak it very easily.

Will McGugan 00:25:32 Part of the problem I had with the previous generation of two frameworks, but it’s very hard to iterate. Yes you could probably do these layouts yourself and you’d have to write a lock code and if later you decided, no, I don’t like that thing on the left of the screen, I want to move it to the right or I want to put it at the bottom somewhere. Or I want to pop it out the screen, have it floating over the top, that requires a lock code to change that. So the advantage of CSS is that you can write it very quickly and then you can change it just as quickly.

Gregory Kapfhammer 00:26:01 I read that textual has like a reactive programming model and that moreover it supports asynchronous programming. Can you explore those two concepts and slightly greater detail so that we can get a full featured understanding of what it’s like to program using textual?

Will McGugan 00:26:17 So I come from the web world and the great thing about the web is that building user interfaces has been iterated on very rapidly for the last 10, 20 years. People have been trying new ways, new interfaces, refining it and creating new technologies to create these user interfaces. But none of that made it to the terminal. The terminal was considered a bit archaic and people used it for more simplistic outputs. So I took web technologies and essentially ported what I think is the best of them to the terminal. And one of those was reactive variables. So in a web it works a little bit differently than the terminal, but essentially you create attributes on your widget. A widget is like a visual component and then you can assign to that attribute and magic happens not real magic, it’s a computer magic, but you just change the data, and the UI will update accordingly. And it has a bunch of, I call them superpowers. For instance if you assign to a reactive attribute, you can have a watcher which runs some code when it changes. And that allows you to basically create a very kind of elegant interface where you’re just changing the data that describes your update and the UI updates accordingly. And once you get used to this way of working, you can build quite powerful features with them. Very little code.

Gregory Kapfhammer 00:27:44 I like the way that you said you’re basically bringing concepts from the web into the terminal window. I had never thought about it that way, but it really helped me to understand some of the features and programming models that are in textual. Thanks for sharing that idea.

Will McGugan 00:27:59 Thank you. Yes, like I said, I love the web and some of the features and web program were just so missing from the terminal. Not everything, not everything. I mean there’s lots of stuff that we do in web development, which I do not like and I wouldn’t want to have anywhere near the terminal. So I like to think I’ve cherry picked the better technologies and pulled them into the terminal world.

Gregory Kapfhammer 00:28:23 I know you and your team have really done a lot of engineering work when it comes to like getting the terminal applications to be fast. And there’s several blog posts that you’ve written about the strategies you’ve adopted to make things fast for a 2E or even for a CLI. And we’ll link the listeners of our episode to some of those so that they can read the articles. But there were several really thoughtful phrases that were in the articles. And Will, if you don’t mind, I’m going to read you a few of the phrases and then maybe you could explain what you went through and what you learned from the lesson that I’m going to encapsulate in the quick quotation. Does that sound cool?

Will McGugan 00:29:00 Sure, yeah let’s do it.

Gregory Kapfhammer 00:29:02 Okay. So one of the things that you wrote provocatively is that you should overwrite instead of clearing, what does that mean?

Will McGugan 00:29:09 You consider the terminal to be a matrix of characters. That’s essentially all, it’s that composes all the elements that run inside the terminal and if you change it, you can build up a new frame. If you were to clear the screen, which might be obvious and then write your new screen on top of it, you get flicker because the terminal was never really designed. In a sense it was, but it’s not good at displaying user interfaces. That’s not really its primary purpose even though it can, the way around that is instead of clearing the screen, you just write the new content on top of it and do you need to change the bits of the screen which have logically changed. And textual does that by combining all the updates that have happened within the 60th of a frame and displays them all at once. So that eliminates virtually all flicker. A flicker can happen for a number of reasons, but that kind of reduces it to the bare minimum. It looks just like a web app.

Gregory Kapfhammer 00:30:07 Okay. So if you don’t clear you avoid flicker. That makes a lot of sense. Now the next one I’m going to talk about is perhaps a little bit more in the land of Python programming, but in one of your posts you said you should use immutable objects. First of all, what is immutability and good grief, how does that actually connect to performance?

Will McGugan 00:30:26 Immutability is the opposite of mutability. Mutability means the ability to change, mutate your data. If an object isn’t mutable, it’s immutable and it can’t change and it might seem like a restriction, like you’ve got a data, you’ve got an object, you can’t change it. It feels like a restriction but it is massively beneficial. It means that when you change it, you create a new object rather than modifying the existing one. And when you do that, caching works a lot better. So there’s a whole bunch of operations that happen in textual where we take an object and we apply a transformation to it and get new data out of it. And that can be very cacheable but that only works when things are mutable. If the object can change then you can cache it. So a lot of our big speed winds came from caching and it came from making objects immutable, which logically you might think are mutable

Gregory Kapfhammer 00:31:25 In the context of Python programming, I’m familiar with using the decorator that’s called LRU cache. Are you using something like that or did you build your own caching mechanism into textual and rich?

Will McGugan 00:31:37 We use both. Yeah. LRU cache is terrific for certain types of data. It was so easy and important. You write at LRU cache, you give it a max size, that’s very important for most data types. You don’t want it to increase infinitely because eventually your memory will fill. But we have other caching algorithms. There’s a cache called PI inside textual and that includes an LRU cache that’s implemented as an object rather than a decorator. And we can use that in places where the decorator doesn’t quite fit. And there’s also other caching algorithms and we apply those quite liberally whatever. We’ve got a computation which is done repeatedly, but the input and output doesn’t change. I forget what the term is for that. When you have the same input, you expect the same output. Those type of operations can be cached and whatever algorithm we use, whether they use the standard library or our caching classes, it creates big wins as far as runtime goes.

Gregory Kapfhammer 00:32:38 Okay. So, so caching leads to big wins. I noticed that both you and I said LRU. Can you define what LRU is and why that’s important in the context of caching?

Will McGugan 00:32:49 Oh sure. LRU Least Recently Used. Basically when you look something up in a cache, it goes to the top of a list and if you add something to the cache, the Least Recently Used item gets discarded. So you keep a set that’s within max size and that contains the elements which are most commonly used. So that tends to give you the bigger speed win. And that works if you’re doing operations where some items are calculated more than others, you don’t want to keep around result of the calculation, which isn’t likely to be used anytime soon. But you do want to keep the calculations which are likely to be used more frequently. So that’s what the LRU and LRU cache are.

Gregory Kapfhammer 00:33:33 So for the LRU cache, you mentioned there’s a maximum size. Do I specify that when I’m creating my 2E using textual or did you build that into textual?

Will McGugan 00:33:44 That’s built into textual. We’ve come up with just by experimentation what the best size for cache is. You want it to be large enough to keep your most frequently used items too large, you use up too much memory, too small and you don’t get the benefits of caching. So we’ve come up with a size of the cache and they tend to be quite small. The power of two, I think many of them are like 1,024 items and depends on the item, how large a set the cache, if your item is very small, if the output is very small, just a few bites, then you can, your cache just could be larger but if your data is larger you don’t want to keep around too many of them. So yeah, it’s something that we’ve thought about and tweaked.

Gregory Kapfhammer 00:34:26 Yeah, I can imagine there’s a tradeoff between time overhead and space overhead and it sounds like you have experimentally evaluated what’s the best tradeoff for textual?

Will McGugan 00:34:36 Yeah a little bit experimenting a little bit, kind of just best judgment you can watch what’s going on in these caches. The LRU cache decorator, it adds some attributes to the function. So if you’ve got a function with an LRU cache, you can do function, I think it’s cache info and that’ll tell you how many hits and misses and the size of the cache. So if you do that and you see that you’re getting lots of hits, not many misses, then that probably means you’re about right with a maximum size. If you find that you’re getting lots of misses, not many hits, then you probably need to increase the cache or even reconsider using a cache top.

Gregory Kapfhammer 00:35:14 When it comes to performance, I’m sure that one thing that we often care about is the ability to scroll in our terminal user interface or if our CLI produces a lot of data, we want to be able to scroll rapidly there as well. And I know that many recent terminal emulators use the GPU in order to make smooth scrolling, but you’ve had to think about how to do smooth scrolling inside of the framework itself. So can you tell us a little bit about some of your strategies for ensuring that your 2E scroll in a smooth fashion?

Will McGugan 00:35:47 Yeah, sure. So yeah, modern terminals use the GPU, which means they’re very good at spitting characters onto the screen, but most of scrolling happens inside your application. So in inside textual because it has to render the next part of the screen and then send that to the terminal. So most of the work goes on inside textual and there are a number of ways to make it smoother initially just means make it faster if there’s a lot of computation going on to create the screen and then you move a scroll bar, you have to do that again. If that takes half a second, you’re going to get a very unpleasant experience where you’re scrolling. But the actual output is very jittery. So we have to make it fast, we have to like render as quickly as possible. One of the ways we do that is with caching that creates some very big wins.

Will McGugan 00:36:31 For instance, one of the final steps when we render a window is to turn some internal data into escape sequences. The escape sequences are what tells the terminal to change the color background and style. We can cache that for a given line, the output for the escape sequences is not going to change. So we cache that. So when you scroll you might reveal a line that’s never seen before. That calculation will be done there. It’ll calculate the escape sequences and then they’ll reuse it. So when you’re scrolling, it’s not doing much work at all, it’s just spitting out some text it has in memory and that is definitely a big part of making scrolling smooth. But smooth has different definitions to different people. Some terminals, well actually most terminals can report the mouse coordinates in pixels. So you move the mouse and it writes the coordinates in pixels to standard input which textual can then parse and you can use that information to create even smoother scrolling because the scroll bars can actually render within a character.

Will McGugan 00:37:37 They’re block characters which render scroll bars essentially and there’s eight of them. So you can render an eighth of a cell, a character when you’re rendering the scroll bar. So the scroll bar can look smooth and we use that to make the scroll bar look smooth. And we also make the window look smooth by animating it accordingly. So if you move the scroll bar, one click the window might move two or three lines. Now you could go from line zero to line three or line four, but then it would jump and that would be visually obvious. But by animating it from zero four, we go zero, then one, then two, then three. It looks much smoother. It’s a bit of an illusion but it just, to the eye it just looks like the text is much smoother. So there’s lots of things going on to create the illusion of smooth scrolling. It can never be a hundred percent smooth because the characters in the window that you’re moving, they can only be an integer coordinates. They can only go from one cell to the other. You can’t move a character up a pixel, it has to move up a full character. So it’s never going to be perfectly smooth like a web browser. But with all these optimizations, the caching, the pixel movements, you can make it fairly smooth to the eye to most people.

Gregory Kapfhammer 00:38:48 Okay, that makes a lot of sense. So you were talking about mouse coordinates. Another thing I read in one of your articles is that somehow it keeps inside of its memory a spatial map. What’s the spatial map and how do you use that to support smooth scrolling?

Will McGugan 00:39:04 That’s not really a smooth scrolling thing. Well, to some extent. So it’s the spatial map. When everything’s been laid out, textual records where all these things are, it records the offset and the width and the height of every single widget and a widget may contain other widgets. So a widget may contain a view with buttons and check boxes, et cetera. All those have to be laid out at the coordinates needed on the screen. And the spatial map is a way of very quickly figuring out what’s under the mouse. So if you click something rather than go through all the widgets, imagine it going through a list of widgets and saying, is the mouse under this coordinate? Is the mouse under this window? It precalculate a lot of that. It’s kind of an index. So when you click something it does very little computation to figure out exactly which widget or widgets is underneath the mouse.

Gregory Kapfhammer 00:39:54 Thanks for that clarification, I really appreciate it. We won’t have time to go into all of the steps that a developer would take if they’re going to create a CLI or a 2E using frameworks like rich or textual. But one thing I wanted to dwell on for a moment is the testing aspect. So if I’ve built a 2E using textual, what kinds of test cases can I write to establish a confidence in its correctness?

Will McGugan 00:40:19 So testing is obviously very important to making robust applications. And when we’re building textual course we realize this that if you want people to use it for serious things, we have to make it testable, which previously was quite tricky. So with the reactive system, you can write tests quite easily because you’re writing data and then you can check results of the data. But often the hardest thing to test are visual aspects. You might make a change which breaks your eye somewhere, maybe a button’s been scrolled off the screen, et cetera. So we have a snapshot testing system. Basically you write a test which kind of runs the application in a headless mode. You can give it key presses and mouse events, et cetera. And that simulates it as if you’re using a real application. And then you can take a snapshot of the output and the output is rendered into an SVG.

Will McGugan 00:41:11 So we take the application, what it looks like inside the terminal, but rather than write it to terminal, we write an SVG file, scalable vector graphics file, which we could actually load in your browser to visualize the application. And if you like that, if you think that’s working correctly, you save that snapshot later on you decide to make some changes, maybe you’ve moved some things about you change the style, et cetera, and it’ll tell you what’s changed in the output and it’ll put them side by side. So you can say, is that correct or is that not correct? It’ll even and overlay the new screenshot on top of the old screenshot. So you if it’s subtle, maybe a single button’s been moved or a bit text, it’s being moved, you can see and visualize it and then you can make a decision. Does this break it or is it just slightly different? And if you accept it then it stores that screenshot the next time it’ll do the whole thing all over again. And it means that you can write quite robust apps that are well tested.

Gregory Kapfhammer 00:42:05 So when you’re doing the snapshot testing, do you have the ability to maybe try different font sizes? Can you try different resolutions of your terminal window? What works in that fashion?

Will McGugan 00:42:17 So the font size is somewhat irrelevant when it comes to testing because you make the font bigger but the application will scale uniformly. So the font size doesn’t come into the testing very much. Sorry, what was the second part of your question?

Gregory Kapfhammer 00:42:31 The second thing that I was wondering about is like the size of the terminal window.

Will McGugan 00:42:35 Yes. So you can resize the virtual application, so give it like a terminal size of a hundred or 200 and that’ll generate a larger screenshot. So if you do have something and you want to test, it works on a small window versus a large window, you can give it the size of the application and you’ll get a larger SVG. So yeah, you can test for various sizes of window.

Gregory Kapfhammer 00:42:58 Okay. Now I’ve looked on GitHub and there are a whole bunch of different systems that are already using textual. How do I actually go about installing one of those programs and using it in my own terminal?

Will McGugan 00:43:11 It depends on the author of the application, how they distributed it, but generally it’s through PiPi Python package index. So you basically install the application and it adds a command line, you run that command line and it pops up the textual app. Something newer in the Python world is UV. It’s kind of like a very grand package manager. It has a really nice feature in that you don’t even have to consciously install something. You do UVX for instance. We have the textual demo. So you do UVX textual hyphen demo that downloads it, installs it and runs it in one command, which is lovely but it means you don’t have to worry about package managers and finding URLs and downloading everything all happens in one. So I think that that is going to come a very common way where people distribute textual apps because it’s the lowest common denominator to getting something installed, just one command line and it runs and when you want the app, again it’s exactly the same command line.

Gregory Kapfhammer 00:44:05 Yeah, thanks for bringing up UV. In fact I use UVX to run most of my textual apps now as well because it’s just so easy and additionally, so incredibly fast.

Will McGugan 00:44:17 Low effort, easy to remember and yeah, it’s very fast. I’m surprised how fast it is. You would kind of expect, you know when you install most applications you’re sitting there looking at several progress bars and you can go off and have a cup of coffee but because textual apps are quite lean, there’s only distributing a few dozen Python files, maybe a few assets. It’s also fast. It downloads almost instantaneously. I mean they’re much smaller than your typical webpage these days. I mean a 10-megabyte webpages is fairly common. But for textual app, I donít know, it’s going to be in the K, it’s going to be like a hundred K or something.

Gregory Kapfhammer 00:44:50 Okay. So thanks for sharing those details that are related to testing and we’ve learned a little bit more about how to actually install one of these applications. I’m wondering if you think about the current implementations of your frameworks, do they have any limitations or do you have any ideas for what the future might look like and what you want to try to build next?

Will McGugan 00:45:11 Yeah, textual apps, term apps in general. Yeah, they do have restrictions. They can only display text. It kind of like blurs the definition of what text is because there’s some characters to draw lines and corners and rounded edges, et cetera. But essentially it’s just text. Terminals don’t do images terribly well. There are a number of extensions to the terminal protocol where you can work with images in terms of pixels and not characters. Textual doesn’t support that yet. It might do one day. It does support very simple block characters. So some of the unit code characters consist of just squares and you can use them to represent pixels but they’re quite low resolution. So yeah, we don’t have like graphics generally as you would think of in a web application, but that kind of restriction, it’s not really much of an issue because the type of things that you want to work with in the terminal tend to be text-based anyway. It tends to be data if you want rich graphics and images and photographs, that’s what the web for terminal is for text and data and information and numbers.

Gregory Kapfhammer 00:46:20 So when you’ve been talking throughout our conversation today, we’ve been saying the word text quite a bit and it made me think about the way that I often interact with AI systems like Claude or Chat GPT that’s also very text-based in its fashion of interaction. Can you comment briefly what do you see as some of the interactions between CLIs and 2E and various AI based applications?

Will McGugan 00:46:46 There’s a few applications which basically talk to chat bots. The textual app has a text input you write in your prompt and then it contacts the API and in responses displayed in the window. So it’s a great way of doing developer kind of things with AI in the terminal where you want to work with the data once those application is Alaya, which youíll find a link to and that is a Chat GPT client that built with textual. It looks great and it looks just as good as web applications. And also to flip it around a bit, AI is getting better at writing applications in textual. Claude I think is the best one I’ve used so far, but it changes weekly. If you go to Claude and ask it to build a textual app, it will generally get there. You might take a couple of prompts, maybe it’ll hallucinate a method name or something and you just tell it no, that doesn’t exist, and it fixes it. And you can get quite far with very little programming, just basically typing prompts to build applications, which you can then run inside the terminal, and you can continue to edit with AI or you could just open up your editor and then tweak it and code.

Gregory Kapfhammer 00:47:56 Thanks for that response. We’ve talked a lot about CLI and 2E and you’ve given us some insights in terms of how they work with either rich or textual or both combined together. Are there additional topics that we didn’t discuss that you think we should chat about now?

Will McGugan 00:48:12 I’m sure there’s plenty we could discuss kind of drawing a blank here. Greg, did you have any ideas or?

Gregory Kapfhammer 00:48:19 No, I think what we’ve talked about so far has been really good. The only thing that I wanted to dive into further is some advice that you might give to a programmer who wants to get started with building a CLI or a 2E. They may or may not know Python, but if you could just broadly comment, what steps do you think someone should take if they want to create their own CLI or 2E or maybe even build their own 2E framework?

Will McGugan 00:48:45 So there’s lots of options. People are different types of learners. Personally, I read docs when I go into the docs, I devour them and then I start coding if that’s what you like, the docs are available for our projects and other projects, so I would just go read the docs. If that’s not your thing, you might want examples. And there’s plenty of examples on the web. We have lots of examples of our projects, but if you go to GitHub and find an application which you think is quite close to what you would want, have a look at how the other developer did it. If it’s a good project, it’ll be commented, and you should be able to follow it quite easily. There is a landing curve of course, but it’s not insurmountable even from scratch. There’s lots of friendly people who are willing to help. I mean, our Discord server is full of people coming on who have not much experience. They ask a few questions, get them started, we’ll send them in the right direction. Yeah, they can do it that way. You build on your knowledge layer by layer by experimenting, changing, tweaking, and that kind of mindset doesn’t change whether you’re first year in programming or you’ve been doing it for 30 years. It’s a continual process of like experimentation, trying something, refining it and repeat.

Gregory Kapfhammer 00:50:01 Thank you for that call to action and the invitation to join the community. We’ll make sure to link the listeners of our show to some of the resources that are available on like the textual website or the rich website and we’ll include some links in the show notes to some sample applications as well.

Will McGugan 00:50:17 Yeah, sounds good. Yeah, I have lots of information there.

Gregory Kapfhammer 00:50:20 So Will, thank you for taking all of this time to chat with us today about how to build 2Es and CLIs using frameworks like rich and textual. For me, it’s been an informative and fun conversation. Thank you for joining today.

Will McGugan 00:50:34 Thank you, Greg. It’s been a pleasure. Thank you.

Gregory Kapfhammer 00:50:36 Thanks to all of our listeners of Software Engineering Radio. See you next time.

[End of Audio]

Join the discussion

More from this show