PUROMA is an incremental horror game about isolation and it looks great
An incremental horror seems like quite an unusual mashup but PUROMA looks really good.
Read the full article on GamingOnLinux. ā Read more
Robot Rhapsody is a cyberpunk odyssey of techno-cults, rebels, & robotic bards
Robot Rhapsody looks like a good one if youāre into pixel-styled JRPGs, a cyberpunk odyssey of techno-cults, rebels, & robotic bards.
Read the full article on [GamingOnLinux](https://www.gamingonlinux.com/2026/07/robot-rhapsody-is-a-cyberpunk-odyssey-of-techno-cults-rebels-robotic-bar ⦠ā Read more
Retro vehicle combat game FUMES adds co-op support for as many friends as you can handle
FUMES is a vehicle combat game made like some of the classics, and now it has full online co-op support for a whole lot of extra action.
Read the full article on [GamingOnLinux](https://www.gamingonlinux.com/2026/07/retro-vehicle-combat-game-fumes-adds-co-op-su ⦠ā Read more
Okay, so, my website also includes my code / git repos, and those are made browsable by stagit. What I donāt like about this (these days) is that this includes all the diffs of my commits. In other words: All my code.
This makes it super easy for malicious crawlers to slurp up valuable data. I donāt like that.
Iām thinking about switching to this instead:
It still shows project infos and there are Atom feeds, but to get the code, you have to actually clone the repos.
š¤
(If you spot any bugs, let me know.)
@movq@www.uninformativ.de Hahaha, I didnāt expect anything like that. But yeah, makes perfect sense. :-D
tt. I run into this bug almost daily for weeks now.
@prologic@twtxt.net A screenshot wonāt help in this case, as you donāt see anything. :-D It starts off just fine with a conversation tree like that:
Unknown conversation root
āā“Read reply
āā“Read subreply
Everything works. After reloading the feeds, a new message becomes part of the conversation, so the conversation e.g. looks:
Unknown conversation root
āā“Read reply
āā“Read subreply
āā“Unread subreply
However, the bug is that the whole conversation is not shown at all. None of the three (or four with the root) messages appear in the message tree view. My recursive SQL determining the messages to display is clearly broken.
twtd instnace or via Github/Gitea or any other publishing backend (doesn't amtter). Please read.
IMPORTANT: Treat the re oery code like a password.
Posting the review and plan here for posterity as it is related to this thread: https://canvas.mills.io/a/6QVGjzRW
The PR(s) as-is will likely not go ahead Iām afraid. More work to be done, but this is basically all about the āRecoveryā story and how to anchor and notion of an āaccountā without well umm an account š¤£
Bethesda roadmap: more Fallout is coming, Starfield support continues, but Elder Scrolls VI is the main priority
Bethesda Game Studios have given a rare update into their roadmap of what theyāre working on, and it sounds like quite exciting news.
Read the full article on [GamingOnLinux](https://www.gamingonlinux.com/2026/07/beth ⦠ā Read more
tt. I run into this bug almost daily for weeks now.
@lyse@lyse.isobeef.org Can you explain this like iām five? 𤣠Scgeenshot?
The first thing that came out of my mouth this morning was: āI donāt feel like I want to go to the beach. Sue me!ā
Well, yes! It IS one of those days.
@dce@hashnix.club So let me get this straight⦠You want to store your feed on a Codeberg repo right? But you want to clone that repo down somewhere else to serve it on a different location. Right? And youād like to use the Twtxt App (https://twtxt.app) to front all of this? Right?
Unread messages are yellow, while read messages are white in tt. Focusing them just alternates the fore- and background colors. With the old color scheme, I disliked that inline code and code blocks were basically just the opposite of normal text. Hence, unread code was white and read code yellow. I found this often confusing, especially with larger code blocks. Sure, there are the timestamp and author columns that still show the usual white (read) and yellow (unread) background for selected messages, but still.
This is how it was before with unread messages:

Before with read messages:

So, I just reworked the code styles. Not sure if I like that or if it is actually an improvement. Unread code is teal on gray when not in focus and becomes blue on orange when focused. I thought the dark gray code background on a black regular background is still nice and subtle. The same similarity in colors for focused messages meant to go with an orange code background on a yellow regular background. The teal was too light, so went with a blue foreground color:

When read and unfocused, the new color scheme calls for the same code style teal on dark gray. However, with white as the main background for selected messages, I went with a light gray code background and a blue code foreground. Again, the contrast with white and teal wasnāt good enough. Vice versa, blue on dark gray is also not all that readable:

It looks like a parrot. Letās see if I begin to like it.
@movq@www.uninformativ.de The nice thing about properties is that you can compute and cache things on the fly at first attempt and also ensure validation for writing. But like you said, since itās not obvious that reading or writing might do some more things, itās strongly advised to avoid doing expensive stuff disguised as properties.
I reckon the vast majority of property use cases is to provide read-only access. At least that was my impression when I was doing a lot more in Python.
Personally, I think that this just reads a lot nicer:
oink.my_property
oink.my_property = 42
Than:
oink.get_my_property()
oink.set_my_property(42)
Btw, any field access is implemented using method calls. I might be wrong, but I believe thereās always __getattr__ and __setattr__ involved. 8-)
I trip over this in our code at work all the time.
Python has this concept of āpropertiesā:
class Oink:
def __init__(self):
self._foo = 3
@property
def my_property(self):
return self._foo
a = Oink()
print(a.my_property)
my_property() is a method but it can be used as if it were a field.
This can also be used to define a setter:
class Oink:
def __init__(self):
self._foo = 3
@property
def my_property(self):
return self._foo
@my_property.setter
def my_property(self, value):
self._foo = 123 * value
Because, for some reason, Python people donāt like getters and setters. Instead, they hide it behind a property.
The result is, when you read this:
a.my_property = 5
print(a.my_property)
You have no idea that this actually calls a method.
@david@daiwei.me Oh, really? I thought Iāve posted compose view screenshots before. Anyway. Glad you like it as much as I do. :-)
The update interval has always been one second. I just didnāt remember and thus tried to time it by watching the preview update while typing. It felt like roughly under two seconds, but apparently my inner clock was off. After taking the screenshot and then examining it more closely, I noticed that the interval is stated right in the UI. :-D So, I just amended my message and didnāt bother taking a new screenshot. I figured I just leave it alone and see who spots the change, if at all. And, of course, you found the easter egg. Congrats, mate! 8-)
@movq@www.uninformativ.de Looks like subject parsing is broken.
tt has a "draft" mode right? You didn't publish, then edit over and over did you? š
@prologic@twtxt.net Not sure if this really counts as a draft mode or this is what you had in mind. I just was in the editor for ages and didnāt close it. tt provides an integrated preview for the rendered message in there. It automatically updates every second.
Hereās a screenshot of the compose view with the conversation context on the top to which to reply to, the editor in the middle and the almost-live preview at the bottom, I hope itās big enough: 
But itās not like I hit the āAdd messageā button in the compose view (the one currently selected on the screenshot), see the message in the conversation tree and then come back into the compose view to continue editing. Thereās no edit functionality in tt. Once the message is appended to my twtxt.txt file on disk, all I can do is edit it with vim. The U+2028 line breaks are really annoying to deal with (Iām sure I could do something about that if I spent the time), so I try to avoid that at all costs.
Once new messages have been added to my local file, I then manually upload the file to my server in a separate terminal. Thereās no upload command integrated into tt. Right from my very first message in the beginning, Iāve always done it exactly like that. Iām used to this and it really doesnāt bother me. But I can see that others might not be fans of that at all. I might add an upload mechanism to tt at some point in the future.
@dce@hashnix.club I like the teal colors in the file manager.
I really think I should go back to Java.
Writing programs in Python is so exhausting. I want a compiler and I want static typing. No, linters and type checkers and IDEs are not good enough. Compilers catch way more errors in advance.
Rust is also exhausting. Theyāre constantly adding language features and, at the same time, the runtime library remains tiny and you need 3rd party libraries for everything. Many of those are still at version 0.x (SemVer!) and you canāt rely on anything. Often times, you need the latest Rust nightly compiler.
Go is ⦠I donāt like it. And huge binaries.
I like C as a language, but itās too fragile. I want to have a proper HashMap every now and then.
None of the above have good GUI libraries, at least not on Linux.
And then thereās Java. This is my fractal renderer that I wrote over 17 years ago:
https://movq.de/v/fcd3c4e557/vid-1784121825.mp4
Itās fast. It has a GUI with custom widgets and those werenāt even hard to make. It still works without changing a single line of code. The source code files have timestamps from 2009 and I just noticed that the JAR file Iām using in the video was compiled in 2010.
Java as a language is relatively easy to learn and to master. There are few surprises. The source code organization with packages is good. Java API docs are clear and well written.
The JVM ramp-up times have improved considerably:
https://movq.de/v/e7314e521e/vid-1784121998.mp4
This isnāt like the Dark Ages anymore. Might even be usable for some CLI tools.
The only thing where Java really sucks is anything close-ish to the kernel. Try issuing an ioctl() ⦠I couldnāt have made my TUI framework in Java, but then again, I wouldnāt have needed to because Swing already exists and it just works.
@david@daiwei.me Thatās a good thing. I still use it heavily, but I also realize that it is addictive. This whole idea of getting likes and boosts is horrible. Seeing ānumber goes upā is inherently addictive design, if you ask me. This should never have been added to a Free Platform like Mastodon, and Iām glad that twtxt doesnāt have anything like it.
@david@daiwei.me @prologic@twtxt.net No worries, I was just trying it on for size.
I played it again today (10/12 on first try), but I canāt say that I love the format of the āshare linkā (the unnecessary yellow and green circles).
I think if I did want to post daily scores or something (for posterity), Iād likely setup a separate feed just for my āachievementsā. š
I also set this to local years ago:
$ go env | grep TELEM
GOTELEMETRY='local'
When this came out I was also outraged. But it doesnāt go anywhere, there are no network connections. It is effectively āoffā like this.
letās just see if something like this crops up again.
I will very likely add a way to delete your feed(s) from the search engine, because I do thing thatās important. But as Art 17 points out, we canāt really guaranteed deletion in everyoneās caches around the planet haha š
@david@daiwei.me You mean, you mean⦠like mowing down a whole rain forest in a thunderstormās brutal heat? :-?
Show us todayās rain. :-)
Wow, 79 new messages over night, similar numbers in the past days. Looks like weāre surfing a high-traffic wave again. :-)
as upset repeatedly in the past and many debates and discussions, I donāt think thereās any other viable way to do, threatening in a purely decentralized way because I think youāre just create another set of problems that are probably likely far worse
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!
<tab> (white space) between the timestamps and posts look a bit shorter than the ones from jenny? just noticed that and thought maybe it's someting you'd want to know.
@aelaraji@aelaraji.com @david@daiwei.me @prologic@twtxt.net It looks like tab stops to me, every 8 characters, and the tab character jumps to the next stop (hence different tab widths based on where the tab occurs in relation to the next tab stop).
@prologic@twtxt.net I really like how these two apps pair-up. Seeing as Iām still tweaking TwtKpr, Iām considering adding support for the same APIs as twtd (so maybe it can be used as another backend for Twtxt.App). š¤
@balloonfu-sen@yarn.girlonthemoon.xyz Unfortunately I tried to support SFTP but ripped this out as Browsers (which the Swag framework uses under the hood as a framework to build PWA(s)) doesnāt support raw TCP connections. So FTP / SFTP is not possible without hacks like a proxy. Which I donāt really want to support. So only things that have some kind of HTTP API are possible viable publihsing backends right now. That is Github/Gitea, twtd, Yarn, etc.
Looks like twtxt.app on mobile emits +00:00 UTC timestamps instead of Z ā Yarnd should handle both, but doesnāt š¤¦āāļø On the list š¤
@bender@twtxt.net @david@daiwei.me Good debugging session š Sounds like the root cause is twtxt.app on mobile ā +00:00 timestamps and quoted mentions. Iāll dig into Yarndās side of that š§
@david@daiwei.me Ta, I continued my fun with studying the tcell and cbind code bases for key bindings. My plan is to eventually not only support custom key bindings in the tt configuration file, but also to enable multi-key sequences, such as gg to jump to the top of a list/tree. Or use other vim-like navigation movements like 7j or 25gg etc.
And it turns out there are only a hand full oft tcell/cbind version combinations that work together. Only if all stars align, thereās chance of success. I will probably end up pulling cbind in to simplify my life. There are situations where tcell.EventKeyās triple of key, modifiers and rune are not all that intuitive to me. Letās see.
@prologic@twtxt.net Well, 15 shows the site. On the left, I had a roll mat on a tarp. I borrowed some āNVA tarpsā from the scouts for this trip. The scouts got them from the National Peopleās Army, the German Democratic Republicās armed forces after Germany was reunited. Theyāre 1.75m x 1.75m in size and weigh 1.3kg, quite heavy, but super awesome. One tarp on the bottom, another one to cover up the clothes, shoes and sleeping bag in order to protect against the thaw. Finally, a mosquito net over all that, hung from a rope between two trees.
My mate just used a hammock with a mozzie net on the right hand side. The third tarp served as the luxurious bedside carpet. :-)
We sat on my second tarp to chill and enjoy the sunset and surroundings. It was nice to notice birds etc. die down. It took a really long time for the last light to fade away. Since we have a very high risk of forest fires, we of course couldnāt have a camp fire. But after all the exhaustion, I didnāt even miss it for one second.
Since we had dinner at home before leaving, all we brought were two lye rolls, two grain rolls, two brezels, some sausage and chocolate biscuits for breakfast. From the 2.5l of water, I ended up using 2l. Itās always good to have a little extra, despite the unnecessary weight. We had brekkie a few kilometers further on a bench in the shade. The first bench was already in direct sun.
Our camp site was maybe 30m to the side and a few meters down of a summit path hidden behind some trees and bushes. We were quite lucky, the other side of the hill got quite a bit of a breeze at night. We could hear the leaved treetops making much more noise behind us.
@movq@www.uninformativ.de Yes, these kind of dogs should really be strictly forbidden!
Itās not illegal if you own the forest or ask the owner. :-)
@david@daiwei.me Yeah, no clue. But my mate said the dog is disqualified from such adventures in the future. :-)
The temps were supposed to hit 14°C just before sunrise. Since we didnāt bring a thermometer, I canāt tell for sure. I was rather hot in my sleeping bag, so I had to pull out my arms every now and then. My mateās sleeping bag was a little lighter and, unfortunately, the zipper jammed up. Since it didnāt close all the way, it felt quite a bit cold I was told in the morning. When we got up at 6ish (we said, we donāt care about time at all), it was probably already 16°C if not more. I brought a jumper, but a t-shirt was already nice enough to wear. The jumper just served as my pillow. The mercury raised by the minute then.
Yeah, I circled the spot with a biro to keep an eye on it. Until now, thereās absolutely nothing to see. Looks like I got lucky.
@klaxzy@klaxzy.net oh wow, looks like a big crowd, for sure! What was the protest (I am assuming it was a protest) about?
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! š
@GabesArcade@gabesarcade.com Glad you like what weāve nuilt up here over many years š„°
@kiwu@twtxt.net awesome, thatās what I like to hear! One more day, and it gets even better: weekend!
@prologic@twtxt.net Iām glad you like it. :-)
@GabesArcade@gabesarcade.com hey, thanks Gabe! One of the things I like the most about twtxt is the community size. We are relatively small. Twtxt users tend to āknowā each other better than on the other, much bigger communities. I believe thatās good. Glad to have you around!
Of course, itās simplicity is part of its strength too!
2027-07-07T18:05:37+00:00 but is always showing up as having been just posted now on twtd š¤
@aelaraji@aelaraji.com Greetings!
It looks like that post is dated for 2027:
I can see it in your twtxt.txt file that way as well:
Host header is sent by the client. And donāt even think about serving UTF-8 or even just putting utf-8 in the content type. š
And for the JPEG thumbnails I pass a special flag to ImageMagick so that IBM Web Explorer from OS/2 wonāt trip. 𤣠And always use link rel="stylesheet" for CSS, because some browsers render inlined CSS as literal text. And ⦠probably more that I forgot by now. š
@movq@www.uninformativ.de see anything you like here? š¤

@lyse@lyse.isobeef.org 07, right there, is my favourite. Thatās not to say I donāt like the rest. I love them all! I donāt think I would ever get tired of enjoying that view, each and every day. š
@movq@www.uninformativ.de There is nothing like that on Linux, because you donāt need it. Everything just works!
Windows NT 4 didnāt have a Device Manager. You know, this thing right here that got introduced with Windows 95:

And thatās super awkward in NT4.
You know what doesnāt have a Device Manager, either? Linux. Why? š¤ Isnāt this one of the most useful system tools? It gives you an overview of the devices in your system and tells you which driver is used for them. Linux could really use such a tool, I think? š¤
(There are programs like āhardinfoā and I remember ancient KDE providing such a tool, but theyāre all an afterthought. Hardly integrated into the overall system.)
@lyse@lyse.isobeef.org I canāt take credit for that word, that was a @bender@twtxt.net original. However, I did hashtag it on Masto in hopes of possibly inspiring someone to learn about what truly decentralized social media looks like (and maybe even come join us). š
Hobbit-like Humans May Have Scavenged Komodo Dragonsā Leftovers to Survive
CNN reports:
Prehistoric human relatives, nicknamed āhobbitsā due to their short stature, may have been scavengers, rather than skilled hunters capable of taking down big game or building cooking fires, according to new research. The study adds to growing evidence that Homo floresiensis, which had a brain only slightl ⦠ā Read more
@itsericwoodward@itsericwoodward.com Haha! Iām glad you like it! 𤣠I ummed and arrred over the set of publishing backends it should support, and in the end decided to support Yarn, Github/Gitea and twtd. I hope thatās enough and flexible enough for most folks š¤