Content-Type: text/plain

It's week 4, and no one's more surprised than I am. This one is only late if you subscribe to a traditional interpretation of time.

#3d

This week I built a reproducible workflow for 3D modelling.

You can read all about that here.

#web

My websites, web services, and all related infrastructure (as code).

Last week's post had some side-by-side code samples and SVGs of labels, including a barcode and QR code. I could have rendered them all as a one-off process and embedded the result, but where's the fun in that? Especially since it was the moment I realised I didn't have a proper implementation of dynamic page components yet.

I can now keep the template as a tidy {{ component "price" }} as long as I register a Component with ui.Register:

const priceEAN = "5602024006102"

ui.Register(pageSlug, "price", components.Label{
    Source: hereduck.Df(`
        Col(
            "NAVIGATOR A4 80GSM",
            Row(
                Large("£6.49"),
                DynamicSpacer(),
                Col(
                    Small("1.3p/sheet"),
                    Small("500 sheets"),
                ),
            ),
            BarCode(%q),
        )`, priceEAN),
    Preview: art.PriceLabel(priceEAN),
})

A Component is defined as:

type Component interface {
    Render() string
}

A simple interface like this provides maximum flexibility while keeping the template minimal.

I reconsidered the use of a large HTTP library dependency (github.com/imroc/req/v3) when maintaining a custom wrapper for stdlib's net/http is just as easy these days, and arguably preferable (opinions vary, don't @ me).

In this case, though, its dependencies are useful for utls's Chrome fingerprint impersonation feature, which is needed for my site-to-RSS feed proxy. Some sites started returning 403s, so it was time to back out of replacing req. If it ain't broke, don't fix it.

A small security update made it in this week: the response cache LRU key included query strings, potentially filling up the cache slots with rubbish. Requests with query strings are now considered non-cacheable.

#system

Everything related to my local personal systems and their configuration (as code).

I plan to take advantage of the Kitty Graphics Protocol in more of my tools. This week it's usagebar, which by default used ANSI codes to create the illusion of a progress bar (with a pace marker):

$ usagebar --ansi
5-hour 60% ▼ 21% 56 m weekly 9% ▼ 22% 4 d 19 h

While this is functional, the lack of a gap between rows kind of detracts from it, as it's limited by the character-based nature of terminals.

Pixels are not limited:

$ usagebar --graphics
5-hour 60% ▼ 21% 56 m weekly 9% ▼ 22% 4 d 19 h

It's not the kind of change worth explaining in detail to your children over dinner, but it is a nice QoL improvement. The breathing room between the bars really makes a difference.

Other changes involved enabling USB passthrough into the pi container for raw access to the label printer, and expanding my agent helper tool agt to sniff the type of archive passed to the archive list|extract subcommand. This was needed to transparently handle .3mf files.

#label

My CLI for the Brother PT-P710BT label printer.

Previously in this series.

I used KGP to add a preview subcommand that outputs a pixel-perfect rendering:

$ label preview -m 24 covfefe

Terminal image rendering is very useful. I might also add a TUI REPL that lets you write out expressions and see the rendered label side by side.

#diffr

A subset of a full-featured visual git diff tool, written in Rust using Tauri.

I did some research into what a rewrite in a cross-platform native GUI framework might look like (Fyne, Gio), and decided against it. Fyne is all about cross-platform compatibility. Gio is entirely GPU-rendered, so it's more like building a game than a GUI. The controls don't look native and feel wrong. No good. Tauri remains, for now.