[idea:26w28-rheo]2026-07-12

Rheo

In Rheo dev news, I have started preparing a 0.5.0 release, despite just last week releasing 0.4.0. This is another major version bump because it changes (in a breaking way) how spines work and how they are exposed to source Typst files.

This set of changes stemmed from my first serious attempt to build a zettelkasten-style linking system with Rheo, a project I’ve had planned since I started writing weeknotes. I thus set out to extend Rheo’s spine abstraction so that it included some notion of ‘blocks’, i.e. sections of text within a file. A spine in Rheo currently is essentially just a list of vertebrae, which have a one-to-one mapping with the files in a Rheo content directory. Typst documents, of course, are often broken up by headings, such as the one under which this text sits. The text that follows from each heading can therefore serve as a block, where a block essentially captures a select portion of continuous content in a Typst source file.

Following the pattern I have been using in these weeknotes, my first attempt was to get Rheo to track all labelled headings and to synthesize unique block IDs by concatenating the heading label with the spine vertebra handle. If the label <a> was attached to a heading in an ‘intro.typ’ file, Rheo would thus create the block ID <intro:a>, essentially extending the relative linking pattern to include blocks within files in the spine.

There isn’t a hard mechanical reason this couldn’t work, I don’t believe. But as I built out the implementation, the abstraction began to feel clunky and leaky. In order to synthesize such labels in Typst files in particular, Rheo would essentially have to edit the Typst source provided by the user, either replacing existing labels or inserting extra ones. Though there might be future features where it makes sense to do this, I realized that tracking blocks in this way is a feature that is somewhat specific to this zettelkasten use case, rather than a functionality that is likely to be used by other formats at this stage.

I therefore changed tack and worked out what would be necessary in order to do block synthesis downstream in a Rheo package, rather than in the core Rheo compiler. The required feature is one I’ve been meaning to add for some time, which is exposing spine information in Typst at compile-time. The way I decided to do this is to inject a rheo-context variable into every spine vertebra, where rheo-context is a dictionary that contains both a handle indicating the current file’s location in the spine, as well as a map of the whole spine. This latter map will enable an improved sidebar package, which will now be able to infer what to include rather than needing an explicit data structure, as well as for other code and packages that need a sitemap.

I also decided to incorporate the Rheo target info as part of rheo-context to limit the namespace that Rheo effectively reserves to just one variable. This means deprecating rheo-context, which is a breaking change, which is technically what is triggering the major version bump to 0.5.0. I don’t think Rheo has very many serious users other than me currently, so I’m not getting hung up about consistently staging releases or keeping backwards compatibility at this stage of development. I also started working on a more structured way to specify spines, which will amount to another breaking change, so jumping straight to 0.5.0 on the next release (sometime next week) will certainly be warranted.

Lastly, I added a justify package which operationalizes a demo Andy Matuschak sent us showing how you can get better Knuth-Plass spacing variance in the browser using pretext.