Posts tagged with "Python"

Rogallo v0.3.0

3 min read; 10 GFI

Rogallo

I've released Rogallo v0.3.0, which mostly concentrates on adding command line support and sorting support for browsing files in the local filesystem. There are also a couple of cosmetic configuration options thrown in.

Configurable cosmetics

Starting with the cosmetic configuration options: I got to feeling that the URI-containing tooltips that show on mouse-hover over a link might be a bit much for some people, so I've added show_link_tooltips to the configuration file1. Set it to false to make the tooltips go away.

Similar to this I've also added disable_animations. Out of the box Textual loves its animations. This is arguably most noticeable if you have a long body of text in a scrolling widget (such as the document viewer in Rogallo), as you use PgUp, PgDn, Home, End, etc., it'll scroll in a fancy animated way. It looks cool for a moment but I can imagine plenty of people getting sick of it, or feeling sick because of it (I sense this is an a11y issue too). With this in mind if disable_animations is set to true they'll all be turned off.

Command line options

Rogallo now also has a number of command line options that can come in useful. In part borrowing from a number of my other TUI applications, and also adding some specific to Rogallo itself. They can be easily found with the --help switch:

usage: rogallo [-h] [-v] [-t THEME] {directories,dirs,d,license,licence,bindings,themes,open} ...

A terminal-based client for the Gemini Protocol.

positional arguments:
  {directories,dirs,d,license,licence,bindings,themes,open}
                        Available commands
    directories (dirs, d)
                        Show the directories created and used by Rogallo
    license (licence)   Show license information
    bindings            List commands that can have their bindings changed
    themes              List the available themes that can be used with --theme
    open                Open a location

options:
  -h, --help            show this help message and exit
  -v, --version         Show version information
  -t, --theme THEME     Set the theme for the application (see `themes` command for available themes)

v0.3.0

directories

Because I feel it's important that people know where applications drop things in your filesystem, there is the directories command, which shows you which directories are used by Rogallo.

$ rogallo directories
/Users/davep/.config/rogallo
/Users/davep/.local/share/rogallo

licence

On the off chance you really need to know the licence of Rogallo (it's GPL3+), you can ask to see the key details:

$ rogallo licence
Rogallo - A client for the Gemini protocol for the terminal.
Copyright (C) 2026 Dave Pearson

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.

bindings

If you wish to know what commands are available for binding, and what the default bindings are, you can use this command:

$ rogallo bindings
Backward - Move backward through history
    Default: ctrl+left_square_bracket
ChangeCommandLineLocation - Swap the position of the command line between top and bottom
    Default: ctrl+up, ctrl+down
ChangeTheme - Change the application's theme
    Default: f9
Forward - Move forward through history
    Default: ctrl+right_square_bracket
Help - Show help for and information about the application
    Default: f1, ?
JumpToCommandLine - Jump to the command line
    Default: /, ctrl+1
JumpToDocument - Jump to the document
    Default: ctrl+slash, ctrl+g, ctrl+2
Quit - Quit the application
    Default: f10, ctrl+q
ToggleHistory - Toggle the display of the history viewer
    Default: f2, ctrl+3

The how of changing bindings still needs to be documented, but it's the same as with most of my other TUI applications, so if you look at how it's done in OldNews, for example, you should get the idea.

themes

This lists the available themes that can be used with the --theme switch.

$ rogallo themes
atom-one-dark
atom-one-light
catppuccin-frappe
catppuccin-latte
catppuccin-macchiato
catppuccin-mocha
dracula
flexoki
gruvbox
monokai
nord
rose-pine
rose-pine-dawn
rose-pine-moon
solarized-dark
solarized-light
textual-dark
textual-light
tokyo-night

open

This command can be used to open a location from the command line. You can pass it either a URI for a Gemini capsule, or the path to a file in the local filesystem.

Viewing local files

Talking of viewing files in the local filesystem... that's now supported too. This is something I wanted to build in from the start, as I feel it could be handy to anyone writing gemtext files prior to deployment. I sense there might be a couple of edge cases relating to this that I might still need to iron out, but mostly it seems to be working well.

At some point I'll probably also pull in textual-fspicker so that the user can browse for files to view, making it just a little easier to open a file in some cases.

Not requiring the gemini:// scheme prefix

So far, to connect to a Gemini capsule, it's been necessary to provide the full URI. That's kind of annoying. It had been deliberately left like this until I sorted the work to allow specifying local files, as I wasn't quite sure how it would all interact. Now that I've got that in place I could address this too. So whereas before you had to type gemini://davep.gemcities.com/ to get to my test capsule, now it's enough to enter davep.gemcities.com.

There is some guesswork going on in the background, with the resolution rules looking something like this:

  • Have I been given a URI that is obviously a Gemini URI?
  • If not, if it has no scheme, and it matches the name of a file in the filesystem, let's assume the user meant that.
  • If it's not a file in the filesystem, and it doesn't have a scheme, let's slap gemini:// on the front and see how we get on with that.
  • None of the above applied, yet it has a scheme: throw it at the operating system's URI resolution system.

In casual testing so far this is working out well.

More to come

I'm still having a blast working on this, and there's still a lot more to do. The TODO list is staying pretty constant in size at the moment because, as I knock an item off, I seem to keep finding new things I want to add or improve. I see this as a good thing.

I have a very busy AFK life for the next week or so, so I don't imagine too many updates during that period. Once things have settled again I want to try and tackle the two big issues of user input and client certificates. I'll be happy that Rogallo is getting close to generally usable when I know I can log in and water my plant in Astrobotany.


  1. ~/.config/rogallo/configuration.json on most systems. This and all the options within will eventually be documented, when I get round to creating the site to document Rogallo. 

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.

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.

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.

Gemini client basic framework

1 min read; 10 GFI

Following on from the initial release of Wasat yesterday, I've got a very basic proof-of-concept client up and running. So far, so good. Here it is loading up my own capsule on GemCities:

Loading my capsule

There's a lot to do yet, it'll be some time before this turns into a public project, but having it at least load up a document from a capsule means there's enough there for me to want to keep going and developing this.

Also, as I said yesterday, this is all home-grown and hand-crafted code. BlogMore has been tons of fun, but it's nice to kick off a significant bit of Python code where I'm writing it all myself. It's nice to be in that flow state once again.