Recent Posts

Terminal bean soup

3 min read; 10 GFI

I'm sure most people reading this will be familiar with the concept of "bean soup theory". In case anyone isn't, here's how Wikipedia describes it:

A specific phenomenon described as a 'What about me' effect. An individual watches a video that doesn't pertain to them, but finds a way to make it about them anyway. Stems from a 2023 TikTok recipe for bean soup, with commenters saying "What if I don't like beans?"

I feel there's a very specific set of folk who love to "bean soup" anything relating to terminals, and it's common to have them turn up in your mentions if you happen to do anything in the terminal, or for the terminal, that isn't about their purist opinions and how everything must be a Vim clone, or worse.

I first encountered this when I started working at Textualize. Unsurprisingly, given the point, purpose and focus of Textual, some folk would turn up in the Discord server, in the issues, in the discussions, in the mentions of posts in various places, and post their very important opinions about how TUI software must work. Not... discuss their preferences, or offer suggestions (there were plenty of people who were pleasant like that, they were lovely to converse with), no: their engagement was written as if every word that was mashed into their keyboard was a well-crafted, handed-from-on-high RFC-like holy document that MUST and SHOULD be followed1.

If I was being very charitable I could cut them some slack. If you're building a framework for building applications for the terminal, there's a good reason to make it at least possible for the application authors to follow recognised best practice and to stick with hard-won conventions. Indeed, Textual has some design choices that I think are deeply questionable and misguided and I objected to them on more than one occasion. So, yeah, if I was being charitable, I could cut them some slack.

I could... if they weren't such arseholes about it.

This gets worse though if you're building an application for the terminal, especially if it's built as Free Software and the motivation is to build something for yourself and be kind enough to share. It irks the hell out of me when I build some application for myself, as a TUI, and then someone turns up and starts complaining about my choice of default key bindings, that work in my choice of terminal emulator and my configuration for that emulator.

It's even worse when they do it in a way where they have to show their very deep knowledge of terminals, and how they're very proud of just how lacking in features and progress their own choice of terminal emulator is.

Dude...2 I know. I've used physical terminals. I've written COBOL on a minicomputer using a line editor on some honking great dumb all-green all-in-one box. I've even written code on a terminal using fan-fold paper. I've used all sorts of terminal emulators. I spent well over a decade doing shit inside rxvt which was running on my GNU/Linux server while displaying on my Windows desktop machine.

I've installed and tested plenty of terminal emulators.

I'm well aware of the conventions and expectations. If Ctrl+C doesn't quit... that is my choice. It's not ignorance, it's not a lack of history with this stuff, it's a choice. For my software.

So when you turn up and feel the need to tell me that some function keys might not work in some terminal on some GNU/Linux box... I get it, you don't like beans. If that's the case, how about you either don't make my hobby all about your tastes and how I should build my application to suit you, or how about you RTFM and configure my application so it works in your choice of terminal?

I mean... we know you won't. We know you don't even care about the application I'm having tons of fun building, that I'm spending time in a joyous flow-state creating and tinkering with. You just care about making the post all about you and your purity. We know you just care about showing how smart you are.

We know you just can't resist the urge to reply-guy3.

We get it: you don't like bean soup. Nobody cares. Nobody asked.


  1. That reminds me: one day I should write a rant about the folk who obsess about the "Zen of Python"

  2. Let's be honest: it's always a dude. 

  3. Let's be honest: it's always a guy. 

Rogallo v0.9.0

1 min read; 9 GFI

I've released v0.9.0 of Rogallo. The first big change in this release is the addition of syntax highlighting to pre-formatted text.

Much like in Markdown, Gemtext also supports pre-formatted text bounded by code fences. Gemtext supports the concept of "alt-text" for such text, like this:

```python
def hello(name: str = "World") -> str:
    return f"Hello, {name}!"
```

Until now Rogallo would simply show the text as-is (with some styling to make it stand out from normal paragraphs). From this release, if there is alt-text for the pre-formatted block, and if it's the name of a supported language, the content will have syntax highlighting applied.

Pre-text with syntax highlighting

If you're curious which languages are supported, it should be anything that Pygments supports.

Another significant change is to the user input dialog. As mentioned in an earlier post, the Gemini protocol imposes limits on how much text can be submitted to a capsule. As documented, the limit is for a resulting URI of 1024 bytes1. To save someone typing out a long entry, only to have it lost2, I've added a count of how much input is left available (calculated from the size of the URI that would result):

The input count

To help ensure that the user doesn't submit too much, I've also added an error display if you go over the limit, which also refuses to submit the input.

Too much input

With these in place it should be a lot harder to run into an unexpected loss of input.

One final tweak is a small change to the optional link stripe system I recently added. While playing around with some other themes, I noticed that the styling didn't show up well, or at all, in many of them. So I've changed it a little so it now shows in all available themes. I'm still not convinced it's quite right -- I'll play some more. But at least now it's visible in all cases.


  1. Although I've already encountered one capsule that limits to 1022, from what I can tell

  2. Right away I'm now thinking it might be a good idea to re-populate the input dialog in such a situation. I'll have to remember to make that a TODO issue. 

Wasat v0.7.0

1 min read; 6 GFI

The Gemini Protocol makes it clear that the maximum length of a URI, when making a request, is 1024 bytes. This has implications for how large a user's input can be when responding to a 1x response. Because of this, I'd like to update Rogallo so that it lets the user know how much space they have left as they type in their input.

With this in mind I've released v0.7.0 of Wasat. This adds the following to GeminiURI:

  • GeminiURI.MAXIMUM_LENGTH -- a constant for the maximum length of a Gemini URI (as mentioned above: 1024).
  • len() support -- if you ask for the len of an instance of GeminiURI it will return the length of the full URI.
  • GeminiURI.bytes_left is a property that tells you how many bytes are left until the limit, given the current URI.
  • GeminiURI.too_long is a boolean property that flags if the current URI is too long to send to a Gemini capsule.

This should give me all I need to add some guardrails to the user input dialog in Rogallo.

Gemtext v1.0.0

1 min read; 7 GFI

I've bumped Gemtext to v1.0.0. I've gone from "this is test code" to "this is stable code" mostly because the library is so simple and I'm unlikely to add any radically new features to it1.

This bump also has one small addition: I've added an alt_text property to the PreFormatted class. This exists to capture and make available any text that comes after the pre-formatted text marker. For example, this text:

```python
print("Hello, World!")
```

will now result in:

PreFormatted(content='print("Hello, World!")', alt_text='python')

If/when I add syntax highlighting of pre-formatted text blocks to Rogallo, this new property will make that possible.


  1. I was thinking about adding a Gemtext builder class at some point, but I can't see a use for it any time soon. Perhaps v2.0.0. 

Rogallo v0.8.0

3 min read; 10 GFI

Rogallo v0.8.0 is now available. This release includes a small bug fix, a big change to how external links and unhandled MIME types are dealt with, and a further improvement to link navigation.

First the big change: hand-off of unhandled content and trust. Until now, if Rogallo was met with a URI it didn't know how to handle, or met with a MIME type it isn't designed to deal with, it would simply hand it off to the browser in your environment. This is generally handy as it means that, when you're browsing gemtext content, if you meet a link that takes you to a conventional website, the link will (normally) be opened in your browser.

There are, of course, any number of schemes that form part of a URI. While Rogallo might commonly be handing off http and https URIs, there's no reason why it can't be handing off a finger or a mailto or whatever. Or, potentially, something a wee bit more dodgy like a javascript URI. While some web browsers don't allow that sort of thing by default (Safari, for example, blocks it unless configured otherwise), others might, and this could result in a nasty surprise.

So now Rogallo has a confirmation step that lets you allow a scheme this once, this and every subsequent time, or just cancel out.

Confirm a scheme

By default the trusted schemes (the ones you say "always" on) are saved in a trusted_schemes file in the data directory.

Similar to the above: if, via a URI that Rogallo can handle, it meets a MIME type it can't handle, it will also pass it off to your operating system. I've added similar protection there too.

Confirm MIME type

In this case any MIME types marked as trusted are dropped into a trusted_mime_types file in the data directory.

ℹ️ Note

If such a MIME type is allowed, it is passed off to your operating system to handle. If you'd like to tell Rogallo to try and internally handle a MIME type, you can set that by configuring the displayable content types.

I feel these changes help remove any possible footguns previously caused by unquestionably handing off URIs to the wider environment.

Another change I've made is to add even more link navigation. This sort of follows on from yesterday's release where I added labelled jumps to links. Again, the usual method of moving between links is to use Tab and Shift+Tab. The problem with those keys is they navigate you through all widgets in the UI that can receive focus. So, if you keep tapping Tab, you'll navigate out of the viewer and into the next available widget, once you reach the last link.

With this release I've added some extra keys which can be used to constantly cycle around the links while you're inside the viewer. These are:

  • Left, Shift+Up, L to go backwards through the links.
  • Right, Shift+Down, l to go forwards through the links.

While these might not be to everyone's taste (I'm already getting very used to using Left and Right), they do add another set of options for link navigation.

The final change is a simple bug fix, where the command line history simply wasn't being saved and loaded correctly. From now on it should save correctly and allow navigation of the history using the cursor keys.

With these changes in place, I feel I'm actually getting close to bumping up to a v1.0.0 release. While there are still things I want to add (there are currently a handful of TODO items in the issues), I see these as nice-to-have items that I want to do later on, rather than as must-have items that preclude considering Rogallo a viable daily-driver.

I'll see how the next day or so goes.

Rogallo v0.7.0

2 min read; 10 GFI

Rogallo v0.7.0 is now available. The main changes in this release revolve around the display and navigation of links.

The aim of Rogallo, as a "TUI" application, is that it is usable purely with the keyboard, and also very usable with the mouse too. So far I think I've managed to hit that mark, but I felt that things weren't quite as smooth as they could be when it came to navigating links.

Links, like any other UI element in the application that can receive "focus", can be navigated to using Tab and Shift+Tab. This is fine if there's just a handful of links in a page, but more than that and having to repeatedly press tab gets a bit boring. Meanwhile, if you have a mouse or other pointing device to hand, it's easy to scroll and click. I wanted to try and have a similar keyboard-based speedup available.

So now, by default, all links on a page get a numeric label:

Links with numeric labels

To jump to a link, while the viewer has focus, simply type the number. When you do so, the corresponding link will be focused, then you just need to hit Enter to follow the link.

The input system is written to "wrap around", as it were. So if you type 1 the link with a label of 1 will be focused. If you then type 2, and if there's a link with a label of 12, it will be focused. If there are fewer than 12 links, the input chain resets, and 2 will be focused. The idea being: as you keep typing numbers, they concatenate until there's no matching link and then it starts fresh.

I was quite keen on having the labels over to the right, so they didn't disturb the layout of the main body of text, but there's a small downside with this: if you have a lot of links next to each other it's not always obvious which link and which label go together. While getting close to your desired link should be good enough (you can then still hit Tab to get to your chosen link), I felt something else could be done to give some help. So I added an option to turn on alternating background stripes for links.

Links with stripes

If all of this seems a bit too cluttered, especially if you're the sort of person who is mouse-first or mouse-heavy with the terminal, there's still the option to turn off both the labels and the stripes and have Rogallo look like it did before.

Links how they used to look

I think this covers pretty much all the possible link navigation needs I can imagine anyone having.

As well as the above, other small changes in v0.7.0 include:

  • Fixed a cosmetic issue with the truncation of the current location shown in the viewer panel, if the location is longer than the available space is wide.
  • The suggestions provided in the command line now include hosts found in the "known hosts" trust store. So if you are trying to remember a location you visited long ago, and you never bookmarked it and it's fallen out of the location history, it's still there as a reminder/hint/completion.
  • Also added the same to the history search palette.

I've also done more work on the website. The configuration section now covers pretty much everything you can modify that changes how Rogallo works. I still want to add more to the site -- mostly concentrating on explaining some key features -- but I felt that anyone using Rogallo right now would be most interested in how they can tailor it to their own taste.

Wasat v0.6.0

1 min read; 8 GFI

Wasat v0.6.0 is now available. This is another quick update that fixes a small typing issue and also adds a handy new method I've been meaning to add to GeminiURI.

The typing issue is a simple enough one. The __init__ method for GeminiURI can take either a string or another GeminiURI as its argument. However, the type was actually specified as str | Self. In Rogallo, I want to have a sub-class of GeminiURI for one particular purpose, which will be passed an instance of GeminiURI. Something like this:

class KnownHost(GeminiURI):
    """A known host."""

From this, I want to be able to do:

[KnownHost(host) for host in self.known_hosts]

where known_hosts is typed as list[GeminiURI]. At this point, the type checker complains:

Argument 1 to "KnownHost" has incompatible type "GeminiURI"; expected "str | KnownHost"  [arg-type]

The error is correct, because the use of Self is saying "this needs to be an instance of my class". There's no reason why it needs to work this way, so I've relaxed the type to str | GeminiURI. In my example above, KnownHost is a subclass of GeminiURI, so the type checker will be happy again.

The new method I've added is GeminiURI.with_default_scheme. This is a class method that acts as a more relaxed "constructor" for a GeminiURI. Again, in Rogallo, there are a few places where I'm taking some input, assuming it's going to be a gemini:// URI, checking if it's missing the scheme prefix, and then prefixing the string with gemini:// before creating a GeminiURI1. This means that Rogallo contains a few instances of this sort of code:

if ...: # ...we should treat some text as a URI but it isn't prefixed with "gemini://"
    uri = GeminiURI(f"{GEMINI_PREFIX}{text}")

It's a small difference, but from now on I'll be able to:

uri = GeminiURI.with_default_scheme(text)

This removes the need to check if there's a scheme already and it saves me having to import GEMINI_PREFIX, etc.


  1. GeminiURI will deliberately raise an exception if the scheme isn't gemini

Wasat v0.5.0

1 min read; 12 GFI

Another quick update to Wasat, my Gemini Protocol client library for Python.

With v0.5.0 I've added a method for getting the list of currently-trusted hosts, and also added a public property to the client class for getting access to the trust store object.

Generally these shouldn't be required, shouldn't be something you'd normally want to work with; I've added them because I thought it might be another useful way of populating the suggested completions facility in the command line inside Rogallo. The idea being: if you're trying to remember the name of a capsule you've visited before, and it might have fallen out of the location history, its trust status might still be recorded so it can complete from there.

Let down by Openreach (again)

2 min read; 8 GFI

I knew it was too good to be true. While a little bit of me hoped it was going to happen this time, mostly I was expecting it to not happen.

It didn't happen.

Today was the day when Openreach were supposed to turn up again and deliver me a full fibre connection. The engineer was booked to turn up some time between 08:00 and 13:00 (as before). I was ready at 08:00. I was ready before 08:00 if I'm honest.

By 12:00 there was no sign of them. Despite there being an hour left to go, I'd written them off as a no-show again. I was wrong this time though. There was a knock on the door about 10 minutes later.

The engineer introduced himself, said he needed to go for a walk around first to get a feel for how and where the work needed to happen, and to figure out where the fibre was to come from (I kind of feel like this would be the sort of thing they'd know up front, but it's not my job and I trust them to know it).

Another 5 minutes and he's back... to tell me they can't do the job because the pole the fibre needs to come from is too far away. O_o

Now, I'm not going to get annoyed at this chap; presumably someone should have done some sort of site survey beforehand. Presumably this was the sort of thing that should have happened before the previous attempt to deliver the product?

So now I'm still without fibre, but with part of the job done. He installed the wee box that goes inside the house, and also the wee box that goes on the outside of the house. Of course, he drilled a hole and hooked them both up. So now I'm down an electrical socket, but have gained a box with a little red light that tells me I don't have full fibre.

He left assuring me that, having done this, it ensures that the actual delivery of the cable is turned into a priority. I can't say I'm that convinced, having sort of been here before. Some time later I got this text message:

We're sorry we couldn't get you connected today. The job is a bit more complex than usual.

We are going to get it sorted though and promise Full Fibre's worth the wait. We'll be back in touch in the next few days.

Again, I'm not convinced, having sort of been here before.

If I was a little more cynical and a little more prone to seeing a conspiracy where there was just a cock-up, I'd almost be tempted to entertain the idea that they turned up and fitted the box knowing full well that there was no fibre to be hooked up. Doing half a job is starting the job, after all. Presumably on some sheet somewhere that looks better than failing to do the job a second time.

I'm going to give them a couple of days to get back to me, as per the message, and then I think it's time to chase up EE and ask why, once again, they've failed to deliver me a product they promised me was available.