Welcome to the blog of Janek Fischer.
On this page you can see the latest blog updates. For further articles, please use the search bar or navigate through the blue tags.
I recommend any decent RSS/Atom aggregator to get notified on blog updates.
In my task manager, I wanted priorities to be simple and accessible, but flexible for power use. So instead of nailing you down to a given set, I decided on simply using a number in the backend. The frontend could still display that number in any desired format.
Such scales ideally expand upwards - not like the EU energy label which has now been reset because they built a scale of few items with A being the best, instead of the other way around where you could at least extend it to the following letters instead of introducing a confusing reset.
That is why low priorities are given low numbers (and there is no technical reason not to allow negative numbers either, to allow expansion in both directions) so that old low-priority tasks do not pollute the list in a need to be adjusted downwards. Because if low number meant high priority, similar to the energy label, a new priority in your repertoire is easier to add to the bottom, requiring you to adjust. Adding it to the top means minimal adjustment needed, bceause there are typically fewer high-priority tasks.
Additionally, I introduced a mechanism that allows for interspersing as desired: A single-digit priority is always multiplied times 10, allowing one to easily get started incrementally with 10 priorities without caring about that mechanism, but if even more granularity is desired, the transition is seamless to one hundred or even more priority levels (not that I could envision myself properly using that, but I want to keep everything open for all kinds of individual workflows).
I like simplicity, but I also like convenience. The Linux command line offers a lot of that, though the latter sometimes needs some searching. So also in the case of image viewers.
Over the course of the last months, I have developed two primary use-cases: Previewing a bunch of images directly in the terminal, first with generic iterm, then within kitty - images was one of the reasons prompting me to switch to it. The second one is a quick popup window, as KDE's showfoto is only marginally faster than the full-blown digikam which takes multiple seconds to start, with a lot of bells and whistles and pop-ups.
In the terminal, I quickly sorted out pxl and catimg because of inferior quality. cacaview converts images into colorful ASCII-Art, but is very coarse and pops up if you are in a graphical session and I found no way to turn that off, making it unsuitable for my uses. I took timg, viu and tiv into a detailed comparison using a little helper script to vary the width as seen below:
Both timg and viu supported kitty at the time of the test, and while viu creates a smooth image in normal terminals, the jaggedness of tiv preserves a lot more details. timg hits a very good middle ground on these, and also allows previewing pdfs directly without conversion and can show images in grids, making it perfect for my previews. tiv supports a directory mode while viu only shows one image at a time, keeping timg as a clear winner. ueberzug also looked interesting, but more appropriate for integration with other applications. lsix is also an interesting alternative to kitty with its sixel protocol because it natively lists images in a grid with their names, but so far kitty served me well enough to not give this a second thought, though recent developments highlight sixel as a more established standard.
For the popup version for use from xdg-open, I wanted quick opening, ability to easily navigate back and forth in the folder of the image and focus capture with quick quitting. w3m is interesting cause it is also a browser, but seemed a bit clunky for this usecase. qiv fell out cause I tested it on KDE Plasma, where the popup window did not capture focus. feh also did not cut it, leaving me with the unmaintained sxiv and its various forks.
Today I experimented with nsxiv, pqiv and vimiv, with the latter as clear winner because of its ability to easily navigate between and within images with familiar vim shortcuts.
As I am polishing the interface for my task manager, I am looking into ways of reusing code across different display environments - specifically I right now want to expand from the CLI to the web and Messenger Bots, with potential native interfaces in mind for later as well. One very specific component prompted a bit of a search: A table with colspan, styled to not have cell delimiters.
I found some hope in https://github.com/Canop/termimad - it uses Markdown which can easily be converted to HTML and is frequently supported within messengers. But my table requirements are not supported, and would not be portable either way because such specificities are outside the scope of Markdown. For now I might just settle with it though, finding a way to move forward without colspan.
For the future, another aspect that needs to be reconsidered is interactivity: The CLI is a REPL, so there are no interactive elements, similarly for messengers. But on the web and in an app, clickable elements are expected and useful. By then, a cross-platform library like flutter or its aspiring Rust alternative https://github.com/emilk/egui shall be considered.
I am building a Task Manager called mostr where I am testing a lot of surprisingly novel concepts. Fundamentally, all tasks are organized in a tree rather than a pretermined project/subtasks structure. This allows tracking time by simply moving like through a directory tree. The currently selected task is the one being time-tracked, but also heavily influences the creation of new tasks, which are automatically subtasks of the current task.
Furthermore, you can attach tags to a task. Adding a context to your session then both filters for visible tasks with specific properties but also adds these properties to any newly created task.
Now I am figuring out how the selected task should affect the current task, because there should be some sort of tag inheritance: Usually subtasks would be annotated with similar tags as their parent tasks. So initially I thought I could simply match all subtasks of a matching parent task (virtual tag inheritance) or apply the tags of the parent task to the subtask (physical tag inheritance) But while I do want straightforward and intuitive defaults, I also want to maintain maximum flexibility so that mostr can accomodate different power users. But cases like this would not be handled well by the above options of inheritance:
Mostr #pc #dev
|- Think about Feature #think
|- Write about Feature #pc #write
|- Implement Feature #pc #dev
When I work on mostr, I am generally on the computer, which is why it has the pc tag. But there are also tasks in that project that do not necessarily happen on the computer. And how do the task tags interact with an active context?
When entering a task, we have three cases to handle:
no current context
current context subset of task tags (common case, when navigating
through the tree)
mismatch of current context and task tags (when jumping to a
specific task)
With that in mind, I came up with the idea of virtual tags. When you enter a task and there is no existing context, the virtual tags behave like a transient context, both applying to filtering and creation. If the context is a subset of the current task tags, the extra tags only apply to creation of subtasks. If there is a mismatch, the current context is apparently obsolete, so it is removed before proceeding as if there was no context.
So the remaining question is how to remove virtual tags if you want to find or create a subtask which only has a subset of those. This touches the issue of storage: The context is a list of tags and excluded tags (tasks with these tags are hidden from view, not explicitly relevant here) and I presumed I could derive the virtual tags from the selected task without storing them separately. But to allow removing a virtual tag, I either need to track removed virtual tags or current virtual tags. This becomes even more tricky when moving up and down with such tasks. So when I enter mostr, I have the virtual tags #pc #dev - I remove them to enter the thinking task - but then what happens when I move up again? Per default, the virtual tags would be active again, unless the removal of virtual tags is also tracked in the context, which might just be the most effective way.
After being on my list of languages to explore for years following my contribution to Mixxx in the Google Summer of Code 2020 where I got to know C++ professionals who love Rust, I finally start learning Rust last month by implementing a long-anticipated nostr-based project.
First of all, I wanted to get an introduction to the language, so I read a bit in "The Book" - The Rust Programming Language. In a quest for a more interactive experience I found Rustlings and then the Jetbrains adaptation. I have been an Intellij IDEA user for years, displacing Eclipse which I used back in school, especially once I migrated to Kotlin - Jetbrains also made Kotlin so their IDE is of course ahead while also being the de-facto best Java IDE (even though I would love to use fully open-source software like Eclipse, I sometimes need to be pragmatic). But I soon found those courses too slow and basic for somebody of my experience level - instead I opted to get my hands dirty straight away in a project, producing a usable tool within days. In between I read up on some select chapters of The Book and of course did some research.
I started using Duolingo about three months ago on my third visit to Kenya, after seeing a friend use it to practice German. I realized that in as much as I knew some bits of Swahili, I was missing a proper foundation of vocabulary to practice with. And I did not want to go back to pesky word lists from school, that worked decently well for me to learn Latin but were very simplistic and unflexible.
First of all, I can report sucess in that: I am now able to express myself decently! Duolingo nicely fit itself into pockets of time throughout my day, teaching me incrementally.
The main struggle right now might be a feature of the language: Swahili is very compact and often spoken quickly (unlik almost anything else Africans do ^^) so comprehension in a conversation is still low, but also got better - rather than understanding only individual words like a few months ago, I can now comprehend phrases and sometimes a whole sentence. Still, a conversation requires the other person to consciously accomodate me so far, which especially kids are not really aware of unfortunately - and those are the ones I like to connect with most. Then again, not always words are needed.
Another challenge is the fact that Kenyans commonly intertwine Swahili and English, to the point that many are unable to speak either language purely. This means that I tend to commonly only get to hear a certain subset of words, with words such as colors entirely missing in everyday conversation. Additionally, I pick up Sheng, colloquial words typically used for example in money amounts - Nusu, Mbao, Chwani, Soo, Punch. Tanzania purportedly can help.
Many people nowadays struggle to keep in touch with friends. Let me share my system that works for me and many of my closest contacts.
Rather than struggling to allocate time specifically, I maintain a list of people I call regularly. When an opportunity comes up, like a longer drive or a walk, I see who fits the mood and is available. I keep being surprised how glad people are for me to call them. Since I do it at my convenience, everybody is happy and I almost always have somebody to talk my thoughts over with.
This works most easily when I am with my family with the obligation to walk the dog every day. Because then I usually get outside at least twice a day, with ample opprtunity for a run, listening to music and podcasts, talking to people and enjoying nature.
This is my way of having regular walks with friends, bridging any distance without painful scheduling.
Habari, niaje, sasa, mambo, jambo - a diversity of greetings point at the rich history of the common east african language. Typically African, none of these can really be equated to "Hello" - somehow they all more or less mean "how are you?" - even though that phrase also has literal translations like "Ukosalama?" or "Ukofiti?" I gave up on trying to make a proper distinction and just went along. What I can say is that "habari" is more formal (literally meaning "news") while "jambo" is the greeting of the coast. In my experience it is the only of these greetings used like "hello" - for all other words, you are not expected to respond with the greeting but a response indicating your state, almost always positive - here a selection in descending formality: Mzuri (good), Salama (peaceful), Nikosawa (I am fine), Fiti (nice), Poa (cool)
Similar to German, it is spoken across a few countries but differently - Tanzanians are generally regarded as speaking it properly, while people in Kenya and Uganda commonly mix it with English or tribal languages in a single sentence.
One and a half years ago, I ordered one of the first Framework laptops and never regretted the move from my old bulky HP Pavilion, despite initially thinking the display would be too small for me. The aesthetics, the portability, the flexibility are on a whole new level. To this day I have not added a single sticker to my framework because I felt it would ruin it despite having had prepared a dedicated bag of stickers for my new laptop. I also want it to be recognizable - just like the Fairphone, it affirms my identity as somebody valueing sustainability and multiple times made people ask me whether I would recommend it - My answer is a resounding YES, and that is why I also finally wrote this post now.
The ability to have an HDMI jack as part of the laptop on the go or as many USB-Ports as I want, exchanging memory and disks as I was always used to and in the future upgrade from an Intel to AMD CPU (only Intel was available at the time of purchase) without abandoning all the other components has proven handy already.
The small size became a big plus for me. Unlike my previous laptop with double the weight, I hardly ever think twice about whether to carry my Framework. It slides into every little backpack and swiftly back out even when I only have a few minutes on commute weighing little more than a water bottle. When the screen estate does not suffice (such as when gaming, or doing advanced programming) external monitors are quickly attached via USB-C, and by now there are even plenty of affordable portable displays. This way I can now decide whether to pack the whole workstation or the minimalistic variant - either because the usecase does not need more (such as writing a blog entry) or because I know I can easily hook up at my destination.
I do have a few complaints, but they do little to hamper my enthusiasm:
the hinge between display and keyboard is too flexible, this is a
known issue with the first models and can easily be solved with a spare
part
speaker and microphone quality are not the best, but that is why I
have a decent headset
the battery drains faster than it should, but this could also be
related to my Manjaro Linux setup
speakers and ventilation face downwards, so both are easily impeded
when using the laptop on the lap or - god forbid - on a pillow.
But with Framework 16 released, with flexible keyboard/speaker modules and the potential to have an inbuilt spare battery, all those can easily fade.
I love writing, even technical. And I am a perfectionist. This leads to me regularly hitting edit while reading through a README file or the like because adjustments jump at me. In this post, I want to share some of my lessons and recommendations from doing this in the last years.
Now that I am in Kenya the second time, it is time to recap what I have learned so far. First of all, like with all explorative journeys, I recommend to use https://wikivoyage.org , the collaborative encyclopedia (like Wikipedia) for travel guidance which has detailed articles on many topics touched in this article, some of which with contributions by me. Also, learn some basic Swahili words to amuse the locals and make a less touristic and more genuinely explorative impression :)
Im Februar 2022 war ich auf Lanzarote, mein erster Flug in vielen Jahren, und dann im März 2023, vor nunmehr einem Jahr, auf Teneriffa - beide den spanischen kanarischen Inseln zugehörig. Daran schloss sich letzten November noch ein Besuch der separaten portugiesischen Insel Madeira - immer jeweils ein bis zwei Wochen. Dazwischen war ich noch in Israel, Irland, und Kenya, doch hier möchte ich mich auf die europäischen Inseln fokussieren, da diese unkompliziert und kostengünstig Sonnenbaden und Erkundungstouren versprechen. Dazu kommen noch die etwas weiter Richtung Amerika gelegenen ebenfalls portugiesischen Azoren, auf denen ich noch nicht war, aber aufgrund eines guten Reiseführers schon einen Eindruck gewinnen konnte.
Für Unentschlossene empfehle ich gleich vorweg Teneriffa: Es hat grünes Hochland, schöne Strände, einen Regenwald, den Vulkan El Teide und generell viel Diversität. Lanzarote, und von dem was ich gehört habe, auch das benachbarte Fuerteventura, sind auch sonnig und haben ein paar interessante Tunnelsysteme, sind aber ansonsten eher karg. Ein Argument für Madeira sind die ganzjährig milderen Temperaturen - diese schwanken auf den anderen Inseln stärker, sodass man im Winter eine Jacke braucht und es im Sommer wohl wirklich heiß wird.
Ein paar interessante Fakten zu den Inseln:
die Azoren sind ein guter Ort für einen Zwischenstopp zwischen
Europa und Nordamerika - und zwischen den Inseln nutzt man kleine
Propellerflugzeuge wie bei uns Züge zum täglichen Transport
Billigflüge zu den Kanaren fliegen über Marokko, aber jegliche Flüge
auf die ebensoweit entfernten afrikanischen Länder kosten ob bis zum
zehnfachen
trotz seiner guten Lage und des ganzjährig milden Klimas hat es
Madeira scheinbar nötig, seine Wirtschaft anzukurbeln, und lockt mit
staatlich finanzierten Förderprogrammen gerade im IT-Sektor, wodurch es
sich zu einem Stützpunkt für digitale Nomaden entwickelt hat
wer Sandstrandurlaube sucht, ist mit den Kanaren generell gut
beraten - Madeira und die Azoren sind da eher dünn besetzt
bei einem Besuch in Lanzarote empfehle ich definitiv einen Abstecher
zur Nebeninsel La Graciosa mit reichlich Zeit zum Wandern, die Insel ist
klein und dünn besiedelt und hat dadurch eine ganz eigene
Atmosphäre
Today in talking with my mentor I once again realized the insufficiency of our digital tools. Even as a proper nerd using Linux and experimenting with all kinds of Software, in the end my tasks end up in notebooks, scraps of paper, on post-its or buried in messengers.
So I had some more or less novel ideas that would make use of idle technological space.
I created a child dataset in each of our datasets ([f]ast=SDD for
root filesystem, [b]igdata=HDD for storage) specifically for VMs, this
makes administration much easier
I went with debian, which has a slim genericcloud image, which
worked on Harvester but it does not work here because the cloud-init
config is mounted as CD-ROM - so after some tinkering I found the
generic image to work well
I added the password, which is now unfortunately mandatory
The section "BOTH" contains a few variable definitions that should be provided before every of the other commands. The "LOCAL" section exists because the cloud-image-utils package is needed and TrueNAS Scale does not allow you to easily install packages. It will presume you provided a suitable cloud-init config in <VM-Name>-seed.qcow.yaml and then convert and copy it over to the TrueNAS Scale server (which I shorthanded as nas in my ssh config). All other variables should be self-explanatory. I did not extract the size of the disks into variables because we usually just go with lavish defaults due to our setup having plenty of space (>100TB).
Here are the commands in a compacted fashion, for easy copying out:
# LOCAL
${EDITOR:-nano} $SEEDFILE.yaml
cloud-localds --verbose $SEEDFILE $SEEDFILE.yaml
scp $SEEDFILE nas:${IMAGE_PATH}
# BOTH
VM_NAME=nostr
IMAGE_PATH=/mnt/b/media/iso/servers/
SEEDFILE=${VM_NAME}-seed.qcow2
# REMOTE
VM_PATH=eagle/vm/${VM_NAME}
# do not use genericcloud here as it is missing CDROM drivers
VM_IMAGE=http://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
# extra data volume, comment out to omit
VM_DATA=b/vm/${VM_NAME}-data
VM_MEMORY=$(expr 8 \* 1024)
VM_PASSWORD=password
sudo zfs create -V 40G "${VM_PATH}"
test -n "${VM_DATA}" && sudo zfs create -V 150G "${VM_DATA}"
cd "${IMAGE_PATH}"
test -e "$(basename ${VM_IMAGE})" || wget "${VM_IMAGE}"
case "${VM_IMAGE}" in
(*.raw) sudo dd if=$(basename ${VM_IMAGE}) of=/dev/zvol/${VM_PATH} status=progress bs=1M;;
(*) sudo qemu-img convert -O raw $(basename ${VM_IMAGE}) /dev/zvol/${VM_PATH};;
esac
# Create the VM
RESULT=`midclt call vm.create '{"name": "'${VM_NAME}'", "cpu_mode": "HOST-MODEL", "bootloader": "UEFI_CSM", "cores": 2, "threads": 2, "memory": '${VM_MEMORY}'}'`
VM_ID=`echo ${RESULT} | jq '.id'`
# Add the display
midclt call vm.device.create '{"vm": "'${VM_ID}'", "dtype": "DISPLAY", "order": 1004, "attributes": {"web": true, "type": "VNC", "bind": "0.0.0.0", "password": "'${VM_PASSWORD}'", "wait": false}}'
# Obtain a random MAC address
MAC_ADDRESS=`midclt call vm.random_mac`
# Add the NIC
midclt call vm.device.create '{"vm": "'${VM_ID}'", "dtype": "NIC", "order": 1010, "attributes": {"type": "VIRTIO", "nic_attach": "br0", "mac": "'${MAC_ADDRESS}'"}}'
# Add the root disk
midclt call vm.device.create '{"vm": "'${VM_ID}'", "dtype": "DISK", "order": 1001, "attributes": {"path": "/dev/zvol/'${VM_PATH}'","type": "VIRTIO"}}'
# Add a data disk
midclt call vm.device.create '{"vm": "'${VM_ID}'", "dtype": "DISK", "order": 1002, "attributes": {"path": "/dev/zvol/'${VM_DATA}'","type": "VIRTIO"}}'
# Add the CDROM
midclt call vm.device.create '{"vm": "'${VM_ID}'", "dtype": "CDROM", "order": 1005, "attributes": {"path":"'${IMAGE_PATH}${SEEDFILE}'"}}'