@david@collantes.us heads up 👋 that verification code never reached you — outbound email was broken on my end (my mail relay was rejecting twtxt.net senders 🤦♂️). Fixed + deployed now 🥳 give the hosted feed another go, it’ll land this time 🤞
Hurray, I can now press gg instead of g to go to the top in tt. Much better! :-) Other multi-key combinations are also easily possible now.
I should probably write a real article about this at some point, but here we go. The only downside with my new key binding system is that it breaks tview’s established pattern. You’ve got an InputHandler(), that is implemented using WrapInputHandler(…). It typically then directly implements the switching logic depending on the key press. Something like this:
func (w *Widget) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
// WrapInputHandler allows for intercepting key events with SetInputCapture(…)
// from the outside for customization. This handles the default key bindings.
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
switch event.Key() {
case tcell.KeyRune:
if event.Modifiers() == tcell.ModNone {
switch event.Rune() {
case 'k':
w.scrollUp()
return // we already handled the event, stop processing
case 'j':
w.scrollDown()
return
}
}
}
// We didn't handle the key event. Maybe the parent
// widget knows what to do with it.
if handler := w.parent.InputHandler(); handler != nil {
handler(event, setFocus)
}
})
}
From the outside, you can intercept and either stop or continue the widget’s original key handling with a potentially rewritten key event using SetInputCapture(…):
w := NewWidget()
// customized or additional key bindings
w.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyUp:
// Rewrite the event, so the "cursor up" key is an alias
// for the vim key binding "k", that is handled by the
// wrapped input handler above. (I know, I know, this is a
// completely unrealistic example, why would anyone use
// cursor keys when there are vim key bindings available?!)
return tcell.NewEventKey(tcell.KeyRune, 'k', tcell.ModNone)
case tcell.KeyRune:
if event.Modifiers() == tcell.ModNone {
switch event.Rune() {
case 'q':
app.Stop()
// we already handled the event, do not pass it
// to the wrapped input handler above
return nil
case 'r':
toggleMessageReadStatus()
return nil
}
}
}
// we didn't handle the event, pass it to the wrapped
// input handler above
return event
}
Since they all expect a single key, I’ve noticed that using multiple dedicated KeyBindings of mine on these different levels kinda breaks multi-key handling with common prefixes. The outer-most KeyBinding captures the prefix, but it can’t transfer it to the inner one if not handled by the outer one. At least not without some more (potentially ugly) changes. So, I now have to work with just a single KeyBindings object for the entire widget chain (if it consists of multiple other widgets or the regular input handler and input capture are in the game). The outside needs to register all its key bind customizations or extensions at the same level that the original widget handles its default ones. Doable by exposing the widget’s KeyBindings instance, but not pretty. You always have to keep this in mind.
With the KeyBindings, it will look like that:
type Widget struct {
parent tview.Primitive
// make it available to children or the outside either by
// direct field access or by providing a getter method
KeyBindings *bind.KeyBindings
}
func NewWidget() *Widget {
w := &Widget{KeyBindings: &bind.KeyBindings{}}
w.KeyBindings. // default key bindings
Bind0(bind.KeySequence('k', w.scrollUp).
Bind0(bind.KeySequence('j', w.scrollDown)
return w
}
func (w *Widget) InputHandler() InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
// also note the missing support for focus transfer at the moment
event = w.KeyBindings.Capture(event)
if event == nil {
return
}
if handler := w.parent.InputHandler(); handler != nil {
handler(event, setFocus)
}
}
}
And then from the outside, or in a child widget:
w := NewWidget()
w.KeyBindings. // additional or customized key bindings
Bind1(bind.KeySequence(tcell.KeyUp), func(*tcell.EventKey) *tcell.EventKey {
return tcell.NewEventKey(tcell.KeyRune, 'k', tcell.ModNone)
}).
Bind0(bind.KeySequence('q'), app.Stop).
Bind0(bind.KeySequence('r'), toggleMessageReadStatus)
When directly working with tview primitives that are not part of custom widget implementations, the following works well so far:
textView := tview.NewTextView().
SetWordWrap(true).
SetText("…")
SetScrollable(true)
textView.SetInputCapture((&bind.KeyBindings{}).
Bind0(bind.KeySequence('q'), app.Stop).
Bind1(bind.KeySequence('g', 'g'), func(*tcell.EventKey) *tcell.EventKey {
return tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone)
}).
Capture)
I need to sleep on this some more.
Also, writing very long messages like this one is really not all that fun in tt’s editor. I should absolutely provide a way to shell out to vim.
(Took me about one and a half hours to compose, holy crap. But not only because of not using vim. Although, that might have saved me a quarter hour or so for sure. Proof-reading this message also uncovered quite a few bugs in my real documentation. So, that’s a big win!) Good night!
@kat@yarn.girlonthemoon.xyz omg i haven’t been here in FOREVER i’m sorry yarn friends!!! i’ve had a lot going on including being extremely depressed :(
@prologic@twtxt.net TBH, I was just going to try and reverse engineer it from the Go source files, but any other docs you can provide would be helpful, for sure! 🙏
+00:00 vs Z should be treated as equivalent UTC 🤦♂️ I'll take a look at the timestamp parsing in Yarnd 🧐
@prologic@twtxt.net For what it’s worth, the twt hash extension is specifically modeled after yarnd’s implementation with all the quirks coming from Go’s stdlib: https://twtxt.dev/exts/twt-hash.html#timestamp-format
“All timezones representing UTC must be formatted using the designated Zulu indicator Z rather than the numeric offsets +00:00 or -00:00. If the timestamp does not explicitly include any timezone information, it must be assumed to be in UTC.”
Go for it! 🙌
@david@daiwei.me good, let’s get this test going. This is simply a reply to iqqsqst5vokf (first twtxt).
There you go: https://movq.de/blog/postings/2026-07-10/0/POSTING-en.html
Friday, my love, we meet again. I am going to take you to lunch, and pamper you. I will led you to believe you are the only one in my life, but then, as the working day sunsets, I shall leave you at the door, like a stood up girl by her prom date.
Weekend babies, here we come! 😂
And I meant “its simplicity”. Autocorrect is going to get me in troubles one of these days. LOL.
And I bet your internets are going off soon. Post interesting, exotic pics!
What in the forkity fork is going on here! 😂
I slept for over 10 hours last night and today. Finally caught up for the many nights going to bed at 02:00, and waking up at 05:00.
Hello everyone ! 👋 Behold I bring you (after many years) the launch of the Twtxt App 😅 – Ye, this is a Desktop and Mobile app built as a Progressive Web App (PWA) using a little framework (Swag) I put together iafter some experiments @xuu@txt.sour.is and I did in Go and HTMX and Service Workers.
The App is offline-first and supports installing to Desktop and Mobile (add to Home screen) and supports a number of publishing backends, including Yarn.social’s yarnd Pod, Github, Codeberg/Gitea, and a little tiny twtd Twtxt server (See: https://git.mills.io/yarnsocial/twtd).
Please try it out, no need for any account(s) or such, works with your existing feed(s) (as long as the publishing backends work well enough for you!). Please give me feedback! 🙏
Also, did you know the Twtxt Search Engine is back? 🎉
AOL’s Owner Bending Spoons Hits Wall Street with $1.7 billion IPO
“The owner of AOL and other tech businesses hit Wall Street with a $1.7 billion initial public offering Wednesday,” reports the Associated Press:
The company is getting $1 billion in proceeds, while the rest is going to shareholders. The stock surged 39.7% in its first day of trading under the symbol “BSP” on the Nasdaq, giving it a market value of $2 … ⌘ Read more
Okay I’m going to bed, g’night folks 👋
@movq@www.uninformativ.de It varies… We tend to prefer bowtie collars for all of our little boys, but they like to wrestle sometimes, and since they’re indoor-only, they can go for months without collars at all.
The mentioned go.{mod,sum} change is already part of tview 0.42.0. After implementing Set/GetDisabled(…) and PasteHandler(), tt starts up fine and seems to work without issues.
Fedora 45 Looks To Finally Offer Install Support For Stratis Storage
Ever since RHEL deprecated their short-lived Btrfs plans, Red Hat engineers over the past decade have been developing Stratis Storage as their storage management solution leveraging XFS, LUKS, DM, and their Rust-based daemon. While Stratis Storage has been available in Fedora Linux going all the way back to Fedora 28, until now there hasn’t been the option of using it for the root file-system on new Fedora installations. Finally with Fedora 45 … ⌘ Read more
@bender@twtxt.net There already is one haha 🤣 I already wrote ed and vi in Go 🤣
Linux 7.2-rc1 Released: “Things Look Reasonably Normal” While Landing AMDGPU HDMI 2.1 FRL, AMD ISP4 & CAS
As expected, Linux 7.2-rc1 was released a brief time ago to cap off the Linux 7.2 merge window. Now it’s off for eight weeks or so of testing before Linux 7.2 stable is released that will in turn go on to power the likes of Fedora 45 and Ubuntu 26.10… ⌘ Read more
The firefly season is ending. I only saw 200 of them or so. There was one female directly on the forest road. If only I brought my camera and tripod, that would have worked out I reckon. I had my torch with me and this looked really cool.
Dusk took forever today. It was really long light out there. Full moon is tomorrow.
On the way back, there was suddenly a load clatter and crashing sound 100 meters away from me. I didn’t see anything, but a tree fell over in the forest out of the blue. Fuck me dead, that was scary as hell. Luckily, I was already on the main road, only meadows around me. It’s the second time I witnessed a tree accidentally coming down. The first one was during the most expensive hail storm in our area so far in 2011 behind me when setting up a summer camp. The weather changed in less than 15 minutes.
Maybe not such a good idea to go out so late alone. :-? Any rustling in the forest immediately reminded me of the boar the other day. Luckily, always false alarm. Still a bit terrified from that event.
We’re at 39.5 °C now. Are we going to hit 40? 
box (command-line container runtime). It works great 👍
@movq@www.uninformativ.de CEF turns out to be pretty easy. I had to write a bit of C and Go to bridge, but once that got going I was able to write it into my pure Go go-wayland wlui library for final rendering. The delegating the entire CEF part was a good idea though because it keeps all the complexity in a container Image, leaving me with just the Go + C stubs/interface and SHM/IPC parts.
So I decided to change tact a bit with GoNIX and instead of trying to build apure Go browser from scratch (which I kinda of half succeeded, in at least it was able to render most static ssr sites), I’ve instead decided to write a new browsered using the Chromium Embedded Framework, otherwise known as CEF. So now I have a fully working browser in GoNIX 🎉 – However since my goal is to keep GoNIX pretty lcean and mostly written in Go, I delegated the cef part(s) to an OCI container image and run that with GoNIX’s box (command-line container runtime). It works great 👍
KDE Plasma 6.7.2 To Fix KWin’s Most Common Crash, Plasma 6.8 To Not Crash When Ejecting CDs
While Plasma 6.7.1 was just released this week following the recent stable debut of Plasma 6.7, there are already a number of fixes piling up for Plasma 6.7.2 due out in July. Plus more feature work and fixes for Plasma 6.8 as the next desktop version going Wayland-only… ⌘ Read more
date := time.Date(2026, time.June, 19, /**/ 17, 0, 0, 0, time.UTC) the most. 🤔 (My only gripe with this is that it isn’t obvious whether the third 0 is milli-, micro- or nanoseconds. These days it’s probably nanoseconds, but you never know.)
@movq@www.uninformativ.de Right. A Go programmer eventually knows that its nanoseconds precision. Keyword arguments like in Python are just sooo superior to unnamed positional arguments. I wish that Go had them, too.
A deer, multiple frogs, several thousand fireflies and something else. It was already very dark when I was silently drifting along on a nice soft mossy path, enjoying the firefly show left and right and in front of me. I then heard some rustling about 30 meters in the distance in the shrubs. I thought that I must have scared up a deer. But it kept on rustling without any worries. And I closed in without seeing anything.
Only when I heard the quick oink from just 10 meters away, I froze. Shit, no deer, but a boar! Suddenly, I was the one who was scared. It probably hadn’t noticed me before. But did it notice me now? Was that grunt a warning or just completely unrelated? The rustling appeared to slowly come closer. What if there were also piglets around? I couldn’t figure out how many boars there were. Maybe just one, possibly more. A wild boar easily rips a hunting dog apart, so I didn’t want to take any chances and decided I will not wait for them to eventually pass me behind the brush in just a hand full of meters, so I can keep on going. While I was just turning around, I heard another oink and was frighened to death. I ran 20 meters, before calming down a little bit. I listened for half a second and nobody was following me. Phew. I then walked back the path.
What an adventure, I tell you. That was my second (or maybe third?) wild boar encounter in the woods ever. A hell lot more scary at night than during daylight when you can actually see something.
Linux 7.2 Protects Against Crafted Perf Data From Going Rogue
With the help of Claude Opus 4.6, the Linux 7.2 kernel added protections to fend off specially crafted or corrupted perf data for the perf tool that could cause a number of issues for the running system… ⌘ Read more
Valve Prices the Steam Machine At $1,049
Valve’s new Steam Machine will launch June 29 starting at $1,049 and go up from there depending on the configuration. Although it costs considerably more than the PS5 ($599.99) and Xbox Series X ($649.99), “the value proposition for the Steam Machine is that it can play your library of Steam games you may have accumulated over years (or even decades), rather than just PlayStation games, and it’ … ⌘ Read more
@movq@www.uninformativ.de Here you go 🤣 https://git.mills.io/prologic/gonix/src/branch/main/cmd/imgview/main.go
@movq@www.uninformativ.de I looked into swagimg. That’s the thing, The latest version pulls in fark’n C++ (geez fuck) and luajit. Anything else I’ve round for Wayland depdns on Rust (wtf?!) – So I built my own in Pure Go. It’s wonderful, so simple, only ~170 lines of Go 🤣
Linux Finally Lands Battery/Charger Driver For 14 Year Old Microsoft Surface RT Tablet
It’s been 14 years already since Microsoft announced the Surface RT hybrid tablet as their first-generation Surface device for going up against the Apple iPad. All these years later, this NVIDIA Tegra 3 powered device is finally seeing a mainline Linux kernel driver for supporting battery and charger status… ⌘ Read more
@movq@www.uninformativ.de It is horribly hot and humid here, and is not even 08:00. AC ran overnight for 3+ hours. It is going to be hellish, not going to lie.
Your birthday, or someone else’s? Either way, happy birthday! 🥳🎂 May many more years come, with good health… and less heat! ☺️
It’s ten thousand million degree celsius outside and I have to go to a birthday party today because wElL iTs My BiRtHdAy ToDaY, I think I’m going to die, send help.
@prologic@twtxt.net 100%. I am never going back to anything else but. Static sites would last much longer than any other too, for sure.
So I’ve been working on GoNIX the last few days… Which is derived from µLinux – At least it’s entire build process. GoNIX however has a 100% Go userland, including the init process, package and service management.
Now… As an experiment, because I was able to make much process on enhancing the build tools and package management, I decided to see if I could build a “Desktop” Gui of sorts…
I still wanted it to be fairly minimal and lightweight. So I went with wayland (of course) and labwc and yambar. So far I’m liking the result 👌 42 packages in the wayland-desktop meta port. Not too bad. Not sure if I can slim that down anymore… But trying to avoid Mesa/GL as that drags in far too much “cruft”.
Linux 7.2 Begins Making Preparations For NVIDIA “Blackwell-Next”
When going through the VFIO subsystem patches for the ongoing Linux 7.2 merge window, there isn’t too much to get excited about for end users with these changes. But there is the first time mentioning “Blackwell-Next” enablement by NVIDIA for the Linux kernel… ⌘ Read more
@movq@www.uninformativ.de We’re already at 29°C now. Five more to go. It’s terrible!
I found my tripod and headed into the woods. There was a ton of glow. \o/ The fireflies were everywhere, super cool. It looked so amazing, especially with all the flying boys. There was one amazing spot in particular, I had 80-100 individuals in my view at once. Absolutely breathtaking. Unfortunately, the mozzies were also delighted about my visit.
I tried my best, but it’s impossible to capture anything on film with my equipment. The fireflies are just way too dim. In the end, I managed to get some very bright girls in the bush. That’s the best I could do, but still really bad. Sorry @bender@twtxt.net. :-(
https://lyse.isobeef.org/gluehwuermchen-2026-06-19/
And no idea what the heck is going on with the CSS there. Anyway. Garbage to trash, seems fitting. ;-)
AMD ACP7.D/7.E/7.F Driver Added In Linux 7.2: “Substantial Design Changes” For AMD Audio
It looks like AMD’s next-gen SoCs not only will be exciting on the CPU side with the much anticipated Zen 6 cores but the AMD Audio Co-Processor “ACP” IP looks to be going through some significant updates… ⌘ Read more
@movq@www.uninformativ.de ahem that dreaded time has come! In the US they are due on 15 April, and wife, the tax doer, waits until the last day to complete them. “If we are going to pay, we may as well delay”, that’s her motto. 😅
Smartphone Market To Shrink 15% This Year Due To Memory Crisis
CCS Insight expects global smartphone shipments to fall 15% this year as AI-driven demand pushes memory manufacturers toward higher-margin server chips. “[S]ome entry-level devices have already seen their sticker prices go up by more than 50 percent since last year,” reports The Register. From the report: The firm found that the primary smartphone … ⌘ Read more
@movq@www.uninformativ.de Gotta make the economy go “around” and keep public services in play 😅 Good luck! 🤞
tt. But then, in the message tree, I spot another missed typo. My process is then to go to my twtxt.txt and fix it by hand. However, I still have to clean up tt's cache. This is rather tidious:
Fuck me! I tried to upgrade tview and the first thing I notice is a shitload of added dependency versions:
go.mod | 18 ++++-----
go.sum | 97 ++++++++++++++++++++++++++++++++++++++-----------
My code does not compile anymore as the view.FormItem interface was extended. Get/SetDisabled(…) are quickly implemented, no worries.
But the tview.Primitive (what makes a widget) interface has now a bunch of PRIVATE methods. For focus handling. Would you believe that!? Thanks, I cannot satisfy this interface in my very custom widgets anymore. Okay then, I just embed *tview.Box. tt now successfully compiles, but does not react anymore on key presses and the message tree is not focused either.
I’m not in the mood to debug this shit. :-( Lunch time.
@prologic@twtxt.net Awesome work!
I’ve been thinking about learning Go for a while, maybe this will be the thing that finally gets me to do it.
tt. But then, in the message tree, I spot another missed typo. My process is then to go to my twtxt.txt and fix it by hand. However, I still have to clean up tt's cache. This is rather tidious:
Getting the vim key bindings to work for focus switching in this modal dialog took me forever. Only cursors and (Shift+)Tab are supported out of the box. I absolutely understand that, it’s fine. I installed an input handler on the dialog, but the focus always stayed the same.
After two wasted hours, I was in despair to copy the tview.Modal into my own code base. Of course, I had to fix all the private tview field accesses first. But even installing the input handler directly on the buttons themselves did not work. Even though, the handler was definitely executed, the focus did not shift. Forcing redraws as a last resort also did not work.
Looking through all the messy chained input handling, I eventually stumbled across another place in the tview.Form, which is internally used by tview.Modal. This messed around with app focus receptions and input handlers. This gave me the idea to make the tview.Application refocus my modal dialog after I told the modal dialog which button to select. And would you look at that, this did the trick! I haven’t completely figured out what is going on exactly, but I could get rid of my Modal clone again.
I always go through hell with focus handling in tview. Each and every time. It just does not feel natural to me. Complete brainfuck to wrap my head around. The Urwid API felt sooo much more refined, it never was an issue. It just works. In fact, I cannot think of any other TUI library that has remotely the same pain level when it comes to focusing widgets as tview.
Now I’m curious how movwin deals with that. ;-)
Every now and then, I think that I have carefully proof-read my message enough times and hit the “Add message” button in tt. But then, in the message tree, I spot another missed typo. My process is then to go to my twtxt.txt and fix it by hand. However, I still have to clean up tt’s cache. This is rather tidious:
- Recall the
sqlitebrowser ~/.local/share/twtxt/tt2.sqlitefrom my shell history.
- Switch to the “Browse data” tab.
- Go to the
messagestable and wait a second or two until it’s loaded.
- Sort by the
created_atcolumn twice, so that I get descending order.
- Select the first message, which is typically the one in question.
- Find the “Remove currently selected row” button in the tool bar.
- Commit the changes.
- Close sqlitebrowser.
So, I finally implemented the removal of messages from the cache in tt. I can now hit d and confirm the removal. Bam! Should have done that ages ago!

Next up is the search, I think.
Belhod! I present Swag – Build offline-first web apps in pure Go and HTML.
@lyse@lyse.isobeef.org Thanks!
On the AI changelog part, though, I’d rather recommend to just not have a changelog at all.
I’m afraid that ship has sailed. You can rest assured that someone who uses AI/LLMs for their code (which is almost everybody at this point) will most certainly also use it for changelogs.
I actually considered not mentioning AI output at all, because this just opens a huge can of worms … 😞
While going through these terrible GitHub release pages, I also found these “New Project Contributors” sections
Yeah, they play on a nerd’s pride.
Now, it’s just the same auto shitshow with MR titles in a rolling date-versioned release scheme. It’s just our team who has to deal with that, though. I think I’m the only one who is not a fan of it.
I’ve found that this whole situation is much worse at work than it is in the Free Software world. At work, it’s literally work and hardly anybody actually cares. We still don’t have all people convinced that writing good commit messages or using good branch names is worth the time. It’s … oh god, no, I’m going to stop here, this is bad for my mental health. 😅
Suffice it to say, all release notes at work are now AI-generated. Nobody gives a fuck.