Building TUIs: Gotchas and Good Info

If you're used to building applications that render in the browser, you have to re-frame your thinking when building out text-based user interfaces. Here are some tips and tricks.

Joe Tannenbaum

This is part of a series of posts I'm writing on building TUIs using Laravel Prompts.

If you're used to building applications that render in the browser, you're about to realize how many things the browser takes care of for you when displaying your app.

In the terminal, you are responsible for every pixel on the screen. So you have to re-frame your mentality a bit when laying things out.

As we work our way through this blog series, here are some important things to keep in mind:

Measuring String Lengths

We are about to measure a lot of string lengths. Fun! (No but really, it's kind of fun.)

There are lots of ways to do this, so we have to consider the kinds of strings we're measuring:

strlen

From the docs: Returns the length of the given string.

mb_strlen

From the docs: Gets the length of a string taking the string's encoding into consideration. The encoding parameter is the character encoding. If it is omitted or null, the internal character encoding value will be used.

mb_strwidth

From the docs: Returns the width of string string, where halfwidth characters count as 1, and fullwidth characters count as 2

1$str = '🟥 this is a string!';
2 
3strlen($str); //? 22
4mb_strlen($str); //? 19
5mb_strwidth($str); //? 20

ANSI Escape Sequences

ANSI escape sequences are used to format text in the terminal (in addition to cursor control and other functionality). They look like this when printed raw:

\e[31mThis text is red and this word is also bold: \e[1mSO BOLD\e[22m\e[39m

Importantly (and perhaps obviously) these escape codes are not visible to the user, but they are factored in when measuring string lengths. We'll often have to strip them out just to measure whatever will be displayed to the user but preserve them for the actual display.

Fortunately, Prompts has some helpers for this, which makes our job a bit easier.

Terminal Height and Width

Measuring the terminal height and width on each render is critical, if our TUI overflows in either direction the output becomes jumbled and inconsistent.

You only have the space available to you in the visible terminal window, any "scrolling" that happens is just a rendering technique that we have to control specifically. More on this later.

WIP

This post is ongoing! As I discover/remember important tidbits of information I'll add them here.

(Excellent) syntax highlighting provided by Torchlight