Recent Posts

gemtext - A Gemtext parsing library for Python

1 min read; 11 GFI

I've just made an initial release of a new library related to my ongoing project to build my own Gemini protocol browser. Initially, the code to parse the hypertext format used for Gemini sites, lived in the Rogallo codebase. But despite it being a pretty simple bit of code, I felt it could be useful for other things too. So rather than have it be buried inside a package that has a lot of other dependencies, I've decided to spin it out into its own little package.

So gemtext v0.1.0 is now available. The library provides a single parsing class, which takes raw markup as a string and turns it into a sequence of objects, each typed for the type of line found. A very simple parsing tool might look like this:

import fileinput
from gemtext import Gemtext

def parse_input() -> None:
    for gem_line in Gemtext("".join(fileinput.input())).content:
        print(f"{gem_line!r}")

If fed with the following input:

# This is a heading

## This is a sub-heading

### This is a sub-sub-heading

=> gemini://davep.gemcities.com/ Dave's test capsule

> This is a deep and meaningful quote

```
Here is some pre-formatted text.

Here's some more of that text.
```

* One
* Two
* Three

the output would be this:

Heading(content='This is a heading', level=1)
Paragraph(content='')
Heading(content='This is a sub-heading', level=2)
Paragraph(content='')
Heading(content='This is a sub-sub-heading', level=3)
Paragraph(content='')
Link(content="Dave's test capsule", uri='gemini://davep.gemcities.com/')
Paragraph(content='')
Quote(content='This is a deep and meaningful quote')
Paragraph(content='')
PreFormatted(content="Here is some pre-formatted text.\n\nHere's some more of that text.")
Paragraph(content='')
ListItem(content='One')
ListItem(content='Two')
ListItem(content='Three')

That's the extent of the library for the moment. I don't see it growing too much, given how straightforward the markup language is. Perhaps one addition I might make at some point is a method of going the other way: allow collecting together each of the individual line-oriented objects and getting a text document back, so providing an object-oriented interface for producing Gemtext documents.

For now though this is enough to support what Rogallo needs.

Rogallo v0.2.0

1 min read; 10 GFI

Rogallo v0.2.0 is now available. This version fixes some issues with links, makes gemtext parsing better conform with the specification, and also makes it easier to see where a link will take you.

The first issue is a fix to how page-relative links were resolved if the page you were viewing was the result of a redirection. What was happening was the links were being resolved relative to the initial URI, rather than the final URI of the redirection. This was most noticeably a problem when following links in the geminispace equivalent of webrings. The main change took place in Wasat, with Rogallo making use of the new Response.uri property.

The second fix was to how I parse gemtext. The initial parser was close enough, but I noticed there were some finer points relating to whitespace that I hadn't paid attention to (mainly due to skim-reading the specification). For example, I expected links to always start with => followed by a space, when in fact a link can simply start with a => and then be followed by the URI with no space.

link-line = "=>" *WSP URI-reference [1*WSP 1*(SP / VCHAR)] *WSP CRLF

Similar improvements to the detection of headings and quotes have also been added.

Finally, I've added a couple of features which make it easy to know where a link will take you. The first is that I've added a tooltip to each link, so that when you hover the mouse cursor over it the URI will be displayed. But, because not everyone is mouse-oriented in the terminal1, I've also added a status bar to the main viewer panel that shows the URI of the focused link.

The Rogallo status bar

As you tab through the links it will update, of course.

Status bar in action

This should ensure that links are less likely to be surprising.

If any of this sounds interesting and you want to have a play, Rogallo is licensed GPL-3.0 and available via GitHub and also via PyPI. If you have an environment that has pipx installed, you should be able to get up and running with:

pipx install rogallo

It can also be installed using uv:

uv tool install rogallo

If you don't have uv installed, you can use uvx.sh to perform the installation. For GNU/Linux, macOS, or similar:

curl -LsSf uvx.sh/rogallo/install.sh | sh

or on Windows:

powershell -ExecutionPolicy ByPass -c "irm https://uvx.sh/rogallo/install.ps1 | iex"

Once installed, run the rogallo command.


  1. Quite right too. 

Wasat v0.1.0

1 min read; 11 GFI

I've just released v0.1.0 of Wasat, my async Gemini Protocol client library for Python.

Changes in this release include:

  • Support for generating and storing client certificates to help when handling 6x responses. This is still experimental.
  • Updated the CLI to handle requests for input (handling 1x responses).
  • Added a uri property to the Response class, to expose the target URI reached from a request.
  • Added a history property to the Response class, to expose the redirect history if redirection took place.
  • Added a requested_uri property to the Response class, to expose the originally requested URI.
  • Updated the CLI so that, when in verbose mode, it prints all of the available redirection information.

Most of the changes here are in support of resolving an issue I found with Rogallo yesterday. With v0.1.0 available I should be able to update Rogallo with an easy fix.

So far, building this library, and the client application, is proving to be really interesting and educational. There's something fun about building a "web browser" of sorts, from the ground up. It really hits this point:

{Gemini might be of interest to you if you} Are a hobbyist programmer with a "do it yourself" attitude who enjoys building their own tools and getting real use out of them every day

from the Gemini protocol FAQ. For me, in "hobbyist programmer" mode, this is all kinds of fun.

Rogallo v0.1.0

1 min read; 12 GFI

I've just made public, and published to PyPI, v0.1.0 of Rogallo. As mentioned a couple of times recently, it's a terminal-based client for the Gemini Protocol.

Rogallo displaying the Gemini home page

This is a very early release, with lots of things still to be added. Right now it's at a place where it interacts with the basics of the protocol, handles the document format, and has the basic navigation facilities you'd expect from a document browser. Things I aim to add over the next few weeks include, in no specific order:

  • A bookmark facility
  • Support for the protocol's user input facility (status 1x)
  • Support for the protocol's client authentication facility (status 6x)
  • Support for local browsing (I want to be able to view and navigate local gmi files)
  • A settable home page
  • The ability to clear down the location history
  • A cache system

I'll also be updating the TODO list as other ideas come to mind.

Another thing fairly high on the list is documentation. For now, Rogallo should be pretty easy to grasp, and there is a help screen that describes all the keyboard bindings in the current context.

The help screen

Commands that can be run can also be discovered via the command palette.

The command palette

For those who care about different looks, there is, of course, support for loading up different themes.

The theme options

This means that even folk who, for some reason, like light themes, can go light too.

Rose pine theme Latte theme

Not really my thing, but I'm told some people like this.

If any of this sounds interesting and you want to have a play, Rogallo is licensed GPL-3.0 and available via GitHub and also via PyPI. If you have an environment that has pipx installed, you should be able to get up and running with:

pipx install rogallo

It can also be installed using uv:

uv tool install rogallo

If you don't have uv installed, you can use uvx.sh to perform the installation. For GNU/Linux, macOS, or similar:

curl -LsSf uvx.sh/rogallo/install.sh | sh

or on Windows:

powershell -ExecutionPolicy ByPass -c "irm https://uvx.sh/rogallo/install.ps1 | iex"

Once installed, run the rogallo command.

As I say above, this is very early days for the project, so don't be surprised if you find something missing, or even run into a bug or two. If you need help, have any ideas, or find any problems, please feel free to raise an issue or start a discussion.

BagOfStuff v1.0.0

1 min read; 12 GFI

BagOfStuff started as a very small side project when I was working on OldNews during my winter break, and then into the new year. It began as some support code in OldNews, which was living in a sort of tools section of the codebase (you know: the dreaded and much-maligned utils section of the project). Sensing that something in it could end up being of utility elsewhere, I moved it out into its own library.

Having written some history-management type of code for the Gemini protocol client I'm working on, I got to thinking that it too should move into this library; a benefit here being that I'll eventually migrate Hike's histories to this. With that done, I've decided to promote BagOfStuff to v1.0.0 and call it stable.

I don't see this library growing too much; the old days of languages either having a spartan standard library, or a third-party ecosystem that lacks comprehensive coverage, are pretty far behind us, and Python suffers from neither issue. On the other hand, there are some general-but-niche things that I'm going to want for my own projects, which probably don't deserve a library in their own right, which can live in here.

Also, sometimes, it's fun to just jam on a little problem and try to make the personally-ideal approach to solving it. BagOfStuff serves that purpose too.

Gemini client history (redux)

1 min read; 12 GFI

A couple of days ago I thought I had history working well in the Gemini client I'm working on; I was wrong. It worked, but the more I used it the more I felt it wasn't quite right. The main problem was I had a single history structure that served both the navigation history (moving backwards and forwards) as well as the general location visit history (the list of places I've visited). It made sense initially, but in use it just wasn't cutting it.

So now I've redone it, using two different history lists that also grow in rather different ways. For the navigation history, when "adding" a new location, everything after the current location gets removed, making the new location the latest in the list. The visit history, on the other hand, grows more like an ordered set: if I add a location that is already in the list, the older version is removed and it is added to the end of the list. In doing this, it's also allowed me to track the date and time I've visited a location, which means I can now have a useful history panel (which can be popped in and out of view) that shows me where I've been, and when.

Client, showing the history panel

Having done this, I now want to take some of these approaches and apply them to Hike. It has a pretty simple location and navigation history system that suffers from the same issues that I wasn't liking in this project. It'll benefit quite a bit from this improvement. I sense a v2.0.0 of Hike in the next few months.

As for when I'll let this project see the light of day: I think I'm close to opening the repository and publishing the first version to PyPI. I had wanted to do it sometime yesterday, but the above kicked in and I've been tinkering more to try and make a stable foundation for the initial release. There's still a lot of work needed for it to be feature-complete, but there's enough here to play and experiment.

Recently I found - 2

1 min read; 12 GFI

Introduction

An occasional collection of things I've recently found on the Internet and added to my bookmarks.

Gemini hypertext format specification

The specification for the hypertext format used by the Gemini protocol, also known as "gemtext". Currently I'm developing my own terminal-based Gemini client so this comes in very handy.

UK government's under-16s social-media ban

The press release from the UK government, covering the intended ban on social media for under-16s. Even if we were to assume this is a good thing (and I'm far from convinced it is), the proposal as it looks right now is hilariously bad and appears to be as ill-informed as any attempt to control the Internet that has gone before.

How to be mean to IIS-based servers

A funny, and kind of informative, post about how you can be mean to IIS servers. Not that I'd ever do that (no, really, I wouldn't, that's not my sort of thing), but it's fun to know that some folk actually do that.

NetNewsWire Status

I was very late coming to NetNewsWire, only discovering it in the last year; it's been one of my go-to RSS tools (along with OldNews, of course) on macOS and iOS ever since. Here the author talks about the progress made in the last year.

The LLMentalist Effect

An article in which the author talks about the con played by psychics, and how large language models pretty much do exactly the same thing.

Dialog attendance list

Does what it says on the tin: shows the list of people who were "leaked" on the Dialog website.

The Community is the Achievement; the Achievement is the Community

An article on how community is one of the important properties of where tech has taken us, and the threat that AI poses to this. As of the time of writing, this is still on my to-read list, but I strongly suspect it's going to be a good and useful read.

Gemini client history

1 min read; 12 GFI

Work continues on my terminal-based Gemini client. So far today I've done a bunch of work on tweaking the presentation of a site, improving some of the widgets that go into the on-screen document. I've also added the start of a history system. So now it's easy to navigate backwards and forwards between pages.

History navigation in action

While there's still a lot of work that I want to do on it, I feel that with the addition of this feature, it might be close to time for me to make it public. It's going well and heading in the right direction, so it feels like the moment when I accept it's not going to be something I abandon, but now have to see to completion.

There are also some design choices I've made this time around that have made me want to revisit Hike at some point in the future. Not for a while yet, but I want to go back and clean up some of the code. After AI-based coding I've been doing lately, it's really nice to be hands-on again and designing every single line. It's a good reminder that working this way ensures a much bigger-picture view of how an application hangs together, and how it can benefit other applications you're maintaining.

Copyright Nvidia

1 min read; 7 GFI

I've been trying, on and off, copilot.el. I'm kind of 50/50 on it most of the time. Sometimes it seems to really understand the code around what I'm working on and does a good job of helping complete the thing I'm about to type; other times it makes wild predictions that just annoy me and get in the way.

It's interesting to experiment with though.

But, just now, I saw something new1. I'd just started a fresh file and run funhead. That means that what had been typed into my buffer was:

##############################################################################
# _

and my cursor was sat where you see the _ above. Then Copilot made a suggestion:

Copilot doing its thing

Like, sure, we all know that this is what can happen, but damn. Still, of all the companies involved with or adjacent to AI, I guess they're the ones to care the least.


  1. To me, I know others have seen this sort of thing before. 

Gemini client rendering

1 min read; 11 GFI

Yesterday I got the basic framework of my Gemini client up and going, and this evening I've been working on getting rendering of the gemtext in place. It's not the most tricky format to handle, so the parser and Textual widget to display the result have come together pretty quickly.

Browsing with the client

With this in place I've got the core of a working client now. Next up is going to be adding history with forward and backward navigation, then after that things like bookmarks and so on.