{"version":"https://jsonfeed.org/version/1.1","title":"Deniz Akşimşek","home_page_url":"https://deniz.aksimsek.tr/","feed_url":"https://deniz.aksimsek.tr/feed.json","description":"A personal blogh","language":"en","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"items":[{"id":"https://deniz.aksimsek.tr/2026/psatina/","url":"https://deniz.aksimsek.tr/2026/psatina/","title":"Psatina: a new tiny JS framework","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<p>While I was building <a href=\"https://codeberg.org/dz4k/femtocrm\">FemtoCRM</a>,\nI needed a dynamic form for entering sale line items.\nI could build some basic JS to add and remove rows from a table,\nbut each row's inputs needed to have the row index in its name\n(i.e. <code>line_items[2].quantity</code>) which seemed like a pain to manage[^1].</p>\n<p>I reached for one of those \"reactive\" frameworks\nwhere you define a template and it updates itself in response to data changes.\nThe obvious choice was <a href=\"https://alpinejs.dev/\">Alpine.js</a>, so that I could keep the sale form in the HTML template.\nI don't like having separate JS files for extremely page-specific stuff.</p>\n<p><img src=\"https://deniz.aksimsek.tr/media/alpine-claude.png\" alt=\"GitHub screenshot: alpine (public). A user you’ve blocked has previously contributed to this repository. claude.\"></p>\n<p>Great, man. Love that for you.</p>\n<p>Well, it's not <em>just</em> that they slopped my blorbo.\nI'd been trying to build a JS framework for years.\nBut so far, my efforts consisted of building\n<a href=\"https://codeberg.org/dz4k/js4k\">a collection of tiny libraries</a>\nand hoping a framework would arise out of that somehow.</p>\n<p>Well, it didn't, and it turns out separate tiny libraries\nget bigger when you put them together.\nOver the last week or two, built <a href=\"https://psatina.dz4k.com/\">Psatina</a>,\na framework inspired by Vue and Alpine in 1.6 KiB (.min.br).</p>\n<pre><code class=\"language-html\">&lt;template p:data=\"{ count: 1 }\"&gt;\n  &lt;output&gt;[| count |]&lt;/output&gt;\n  &lt;button type=\"button\" p:on:click=\"update(() =&gt; count++)\"&gt;Increase&lt;/button&gt;\n&lt;/template&gt;\n</code></pre>\n<p>Psatina sacrifices a lot to get its small size,\nmostly in performance -- I haven't tried building a\nDBMonster with it.\nI consider it the 20% of Alpine I need for 80% of usecases[^2].\nI'm thinking about reviving my htmx clone <a href=\"https://codeberg.org/dz4k/whet\">whet</a> to pair with it.</p>\n<p>[^1]: OK, it probably wouldn't be <em>that</em> hard.\nBut as mentioned, I like my JS components to be general-purpose.\nFor this kind of use case, the generally-applicable tool is\na reactive framework.\n[^2]: Not actual numbers.</p>\n","tags":["entry","computer/projects"],"date_published":"2026-04-16T22:06:38.000Z"},{"id":"https://deniz.aksimsek.tr/2026/app-grid-folders/","url":"https://deniz.aksimsek.tr/2026/app-grid-folders/","title":"Pin files & folders to your app grid on GNOME","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<p>This script generates a desktop entry that opens a folder,\nwith the same icon and all.</p>\n<pre><code class=\"language-python\">#!/usr/bin/env python3\n\nimport sys, textwrap\nfrom pathlib import Path\n\nimport gi\ngi.require_version('Gtk', '4.0')\nfrom gi.repository import Gio, GLib, Gtk\n\ndef get_icon_name(path: Path):\n    \"\"\"Get the icon associated with a file.\"\"\"\n    file = Gio.File.new_for_path(path.as_posix())\n    info = file.query_info('standard::icon', 0, Gio.Cancellable())\n    return info.get_icon().get_names()[0]\n\ndef install_desktop_file(path: Path):\n    \"\"\"Generate and install desktop entry for a file or folder.\"\"\"\n    content = textwrap.dedent(f'''\n      [Desktop Entry]\n      Type=Application\n      Version=1.0\n      Name={path.name}\n      Exec=xdg-open {path}\n      Icon={get_icon_name(path)}\n    ''')\n    print(content)\n\n    destpath = Path(GLib.get_user_data_dir()) / 'applications' / (path.stem + '.desktop')\n    destpath.write_text(content)\n\nif __name__ == '__main__':\n    install_desktop_file(Path(sys.argv[1]))\n</code></pre>\n","tags":["entry","computers/til"],"date_published":"2026-04-16T19:00:07.000Z"},{"id":"https://deniz.aksimsek.tr/2026/training-data/","url":"https://deniz.aksimsek.tr/2026/training-data/","title":"Generative AI is not trained on \"data\"","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<blockquote>\n<dl>\n<dt>data</dt>\n<dd>\n<ol>\n<li><mark>factual information</mark> (such as measurements or statistics) used as a basis for reasoning, discussion, or calculation</li>\n</ol>\n</dd>\n</dl>\n</blockquote>\n<p>--- <a href=\"https://www.merriam-webster.com/dictionary/data\">Merriam Webster, \"data\"</a></p>\n<p>Is a poem factual information?</p>\n<blockquote>\n<p>Do not go gentle into that good night,<br>\nOld age should burn and rave at close of day;<br>\nRage, rage against the dying of the light.</p>\n</blockquote>\n<p>--- Dylan Thomas, <cite>Do Not Go Gentle into That Good Night</cite>. TODO: replace with a more topical poem.</p>\n<p>Doesn't look like it. But what about this?</p>\n<blockquote>\n<p>The first verse of \"Do Not Go Gentle into That Good Night\" by Dylan Thomas goes like this: \"Do not go gentle into that good night, / Old age should burn and rave at close of day; / Rage, rage against the dying of the light.\"</p>\n</blockquote>\n<p>Oh dear, that's looking pretty factual to me! See how the magical transformation is completed:</p>\n<blockquote>\n<ol start=\"2\">\n<li>information in digital form that can be transmitted or processed</li>\n</ol>\n</blockquote>\n<p>--- M-W</p>\n<p>Behold, a .txt file:</p>\n<table style=\"width: 100%;\">\n  <colgroup><col style=\"width: 6ch;\"><col>\n  </colgroup><caption>Dylan Thomas, \"Do Not Go Gentle into That Good Night\"</caption>\n  <tbody><tr><td>0000</td><td>D\n  </td></tr><tr><td>0001</td><td>o\n  </td></tr><tr><td>0002</td><td> \n  </td></tr><tr><td>0003</td><td>n\n  </td></tr><tr><td>0004</td><td>o\n  </td></tr><tr><td>0005</td><td>t\n  </td></tr><tr><td>0006</td><td> \n  </td></tr><tr><td>0007</td><td>g\n  </td></tr><tr><td>0008</td><td>o\n  </td></tr><tr><td colspan=\"2\">...\n</td></tr></tbody></table>\n<p>Aha! Merely by saving it in a file, I have transformed <cite>Do Not Go Gentle...</cite> from poetry to data! It's presented as a table, it must be so.</p>\n<p>And there's more! The advent of the <a href=\"https://en.wikipedia.org/wiki/Multimedia_computer\">multimedia computer</a> means I can perform the same trick on paintings, songs, speeches, movies and anything else your heart desires!</p>\n<h2>Maybe we shouldn't treat everything that can be saved on a computer like soil density</h2>\n<p>Even though an amateur rhetorician (that's a portmanteau of \"rhetoric\" and \"magician\", I'm still working on it) like me can trivially transmute <em>works</em> into <em>data</em>, the two have very different moral frameworks.</p>\n<p>Depending on where you live and what your ideology is, data is either unownable, or owned by the person who <em>collects</em> it. Even if it's unownable, you can still charge for the service of collecting it, you just can't stop others from doing the same. Either way, the <em>producer</em> of data has no rights over it[^gdpr] --- you don't pay the wind to measure its speed.</p>\n<p>[^gdpr]: Unless that producer is a human in a country with privacy regulations like the GDPR, but even then, the \"data subject\" in GDPR terminology is not a \"data owner\".</p>\n<p>But with creative works, the producer has all the rights. Copyright, for one, but even if you don't believe in that, most people still think plagiarism is bad, and artists should be able to sell their art as long as money exists.</p>\n<p>These are sensible if you assume that <em>data</em> is produced involuntarily, mostly by inanimate beings, and collecting it takes effort, while <em>works</em> are the result of intentional human labor, and are easy to \"collect\" since most creatives want their works seen.</p>\n<p>They're also sensible if you assume that <em>data</em> is collected by <em>researchers</em> to advance science or provide reference material, and <em>works</em> are collected by <em>publishers</em> to sell for entertainment and edification.</p>\n<p>So, when a research non-profit uses well-established resources like Common Crawl to advance the field of artificial intelligence, nobody bats an eye. But when that firm pivots to selling its research artifact as a product, it becomes the political issue of the decade.</p>\n<h2>Wait... aren't AI models data too?</h2>\n<p>The spirit known as OpenAI and its thralls have turned all web-published works into data by saving them into a database and doing research to them (this is the same spell we just performed earlier),</p>\n<p>But these powerful entities, through rituals like <em>word vectorization</em>, <em>reinforcement learning with human feedback</em>, and <em>bribing politicians</em>, are now trying to transmute the database into an ownable, sellable work without turning the data inside back into works as well.</p>\n<blockquote>\n<p>They will, of course, be granted challenges to business models—as new technologies always are—especially for those who make their money off of gating up and charging access to data. But such practices simply aren’t tenable in the long term, legally or practically (let alone morally). Under US law, <data>facts aren’t copyrightable</data> (thanks to the landmark Supreme Court decision in Feist v. Rural Telephone Service) and <mark>databases are just collections of facts.</mark></p>\n</blockquote>\n<p>--- <a href=\"https://upload.wikimedia.org/wikipedia/commons/3/3f/Aaron_Swartz_s_A_Programmable_Web_An_Unfinished_Work.pdf\">Aaron Swartz, A Programmable Web</a></p>\n<p>(Another \"data is factual information\", nice! Maybe I'll take all the works that say this and put them in a CSV so they're data.)</p>\n<p>I like that facts aren't copyrightable.[^data-trade] But \"gating up and charging access to\" something is sadly the only reliable way to make money off of it. And now that all digital works have been turned into data, creatives are losing their ability to do exactly that.</p>\n<p>[^data-trade]: I wish they weren't tradable either --- it is absurd that information about me that I give to a business so they can provide services is an asset that can be valuated and sold. How did this ever become normal?</p>\n<p>The <s>megacorps</s> spirits continue to make money, because they can perform the bribery rituals.</p>\n<p>I cannot. So, instead, I'm trying to cast a counterspell upon the initial data-fication spell.</p>\n<h2>Training materials</h2>\n<p>I'm dropping the magic shtick now.</p>\n<p>This post is not an appeal for you to stop saying \"training data\". After years of wrestling with \"free as in beer\", I remain unconvinced using a different word to describe the same thing makes people want to hear you out.</p>\n<p>But the data-works distinction exists, and it's a distinction between concepts not words. (I don't think \"works\" is the best word for it anyway, anyone have a better one?) When you're building software, everything is \"data\",[^cms] but we need to step back at some point and realize that this is only an illusory state that works must inhabit to fit inside the computer --- they get turned into data temporarily, after the moment of creation, until being restored at the point of retrieval. For anyone who doesn't live in the computer, a poem is not data.</p>\n<p>So, I will be calling the inputs to an AI training process \"training materials\", unless they are truly data. You can do it too. It can be our thing. It won't make the general public interested, nobody will be begging us to explain one more time that you can pay for free software, but at least we'll know when we've chanced upon one another at the slop trough.</p>\n<p>[^cms]: or \"content\", as in the information-free phrase \"Content Management System\". Hey, why are we still building Thing Doers? Hasn't anyone designed a flexible enough Thing Doer that can Do any Thing?</p>\n","tags":["entry","takes"],"date_published":"2026-01-23T19:37:42.000Z"},{"id":"https://deniz.aksimsek.tr/2025/tailscale/","url":"https://deniz.aksimsek.tr/2025/tailscale/","title":"how I run Tailscale on an immutable Linux system","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<p>GNOME OS, to be specific. There is no package manager here, and I don't understand how to build sysexts, so this is my solution.</p>\n<p>You can install Tailscale without touching any files outside your home directory. This is how I did it, and it's been serving me well for a few months.</p>\n<p>(Instructions for systemd.)</p>\n<h2>Create systemd unit</h2>\n<p>We're going to run <code>tailscaled</code> as a <em>user service</em>, so create a file called <code>tailscaled.service</code> in <code>~/.config/systemd/user/</code> with the following contents:</p>\n<pre><code class=\"language-ini\">[Unit]\nDescription=Tailscale node agent -- modified to run as user service\nDocumentation=https://tailscale.com/kb/\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=notify\nRestart=on-failure\n\n# TODO: don't hardcode .local/bin, use $XDG_BIN_HOME\n# problem: that's not set on most systems, and systemd env var expansion\n# doesn't support default values\nExecStart=%h/.local/bin/tailscaled \\\n  --state=${STATE_DIRECTORY}/tailscaled.state \\\n  --socket=${RUNTIME_DIRECTORY}/tailscaled.sock \\\n  --tun=userspace-networking \\\n  --port=41641\nExecStopPost=%h/.local/bin/tailscaled --cleanup\n\nRuntimeDirectory=tailscale\nRuntimeDirectoryMode=0755\nStateDirectory=tailscale\nStateDirectoryMode=0700\nCacheDirectory=tailscale\nCacheDirectoryMode=0750\n\n[Install]\nWantedBy=default.target\n</code></pre>\n<p>I wrote this based on the unit file Tailscale ships combined with some other unit files I saw online.</p>\n<h2>Install Tailscale binaries</h2>\n<p>Go to https://pkgs.tailscale.com/stable/#static to download the latest static binary builds of Tailscale. Extract and place the <code>tailscale</code> and <code>tailscaled</code> files in <code>~/.local/bin/</code>.</p>\n<p>I wrote a script I use that does this, and starts up the service (needs systemd unit file to be created first):</p>\n<pre><code class=\"language-sh\">#!/usr/bin/env bash\nset -Eeuo pipefail\n\ntmp=$(mktemp -d)\n\ntarball=$(curl -sL https://pkgs.tailscale.com/stable/?mode=json | jq -r .Tarballs.amd64)\n\necho \"Downloading Tailscale into $tmp\"\ncurl \"https://pkgs.tailscale.com/stable/$tarball\" | tar -xzC \"$tmp\"\n\nsystemctl --user stop tailscaled.service\n\ntsdir=$(find \"$tmp\" -name 'tailscale_*' -type d | head -n1)\ncp \"$tsdir\"/{tailscale,tailscaled} ~/.local/bin/\n\nsystemctl --user enable tailscaled.service\nsystemctl --user start  tailscaled.service\n</code></pre>\n","tags":["entry","computer/til"],"date_published":"2025-11-23T23:05:53.000Z"},{"id":"https://deniz.aksimsek.tr/2025/webdev/","url":"https://deniz.aksimsek.tr/2025/webdev/","title":"A web developer's feed reader","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<ul>\n<li><strong>Introducing the <code>&lt;zoop&gt;</code> element</strong> --- Chrozilla Dev Blog</li>\n<li><strong>How to magically improve every aspect of your website using <code>&lt;zoop&gt;</code></strong> --- Warm Color Site</li>\n<li><strong>Is <code>&lt;zoop&gt;</code> the next big thing in web development?</strong> --- XSLT-Tricks</li>\n<li><strong>How to integrate <code>&lt;zoop&gt;</code> with Nẅxt.js</strong> --- Vorcel</li>\n<li><strong><code>&lt;zoop&gt;</code> still not supported in iOS Safari</strong> --- May I Use</li>\n<li><strong>doop.js: a <code>&lt;zoop&gt;</code> polyfill</strong> --- GitCub</li>\n<li><strong>New <code>&lt;zoop&gt;</code> element causes some screen readers to shout slurs at user</strong> --- Aeleveny Review</li>\n</ul>\n","tags":["entry","takes"],"date_published":"2025-07-29T11:26:40.000Z"},{"id":"https://deniz.aksimsek.tr/2025/retrofuture/","url":"https://deniz.aksimsek.tr/2025/retrofuture/","title":"Retrofuture: a blackletter-inspired pixel font","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<style>\n@font-face { font-family: \"Retrofuture\"; src: url(/media/Retrofuture.ttf) format(truetype) }\n</style>\n<figure style=\"margin: 2em 0;\">\n<div style=\"\n  font-family: Retrofuture; hyphens: auto; font-size: 2em; color: black;\n  background: lightgoldenrodyellow; padding: 0 1em;\n  border: 1px solid currentcolor;\">\n<p>IN CASE OF CONFLICT, consider users over authors over implementors over specifiers over theoretical purity.</p>\n</div>\n<figcaption><cite>Retrofuture</cite> sample. Quote: W3C, HTML Design Principles\n  § 3.2 Priority of Constituencies</figcaption>\n</figure>\n<p><strong><a href=\"https://deniz.aksimsek.tr/media/Retrofuture.ttf\" download=\"\">Download Retrofuture</a></strong></p>\n<p>Another pixel font I made. This was supposed to be a vector font. The idea was\nto interpret blackletter forms with no stroke variation and a limited number of\nangles. Sadly, I still can't make vector fonts.</p>\n<p>Retrofuture replaces the wonderful <a href=\"https://www.soft-type.com/jacquarda-bastarda\">Jacquarda Bastarda</a>\non my website.</p>\n","tags":["entry","projects","design/projects"],"date_published":"2025-07-14T22:45:05.000Z"},{"id":"https://deniz.aksimsek.tr/2025/hypersystem/","url":"https://deniz.aksimsek.tr/2025/hypersystem/","title":"Hypersystem: a pixel display font for Hypermedia Systems","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<style>\n@font-face { font-family: \"Hypersystem\"; src: url(/media/Hypersystem.ttf) format(truetype) }\n</style>\n<figure style=\"\n  font-family: Hypersystem; hyphens: auto; color: #bbf7ff; background: black;\n  margin: 2em 0; padding: 0 1em; border: 1px solid currentcolor;\">\n<p>Hypertexts: new forms of writing, appearing on computer screens, that will\nbranch or perform at the reader’s command. A hypertext is a non-sequential\npiece of writing; only the computer display makes it practical.</p>\n</figure>\n<p><strong><a href=\"https://deniz.aksimsek.tr/media/Hypersystem.ttf\" download=\"\">Download Hypersystem</a></strong></p>\n<p>Hypersystem is a new font I designed for the web version of Hypermedia Systems.</p>\n<p>Recently, I reworked the web page of our book Hypermedia Systems\n(<a href=\"https://hypermedia.systems/\">https://hypermedia.systems</a>). I was happy with the layout, but unhappy with\nhow the book title looked. It was set in Jaro, a great free display font we also\nused for the print release, but I didn't think it worked to communicate the tone\nof our book on the home page.</p>\n<p>After trying out a few alternatives, Carson suggested that I adapt the lettering\nfrom the cover of the <a href=\"https://www.lulu.com/shop/deniz-ak%C5%9Fim%C5%9Fek-and-adam-stepinski-and-carson-gross-and-mike-amundsen/hypermedia-systems/paperback/product-jen2vm2.html\">paperback edition</a>. The pixel artist we hired did an\nabsolutely fantastic job, but we decided to roll our own for the lettering.</p>\n<figure>\n  <div style=\"display: flex; flex-flow: row nowrap; gap: 1em;\">\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=\"https://deniz.aksimsek.tr/media/hypersys-lettering-1.png\"><img src=\"https://deniz.aksimsek.tr/media/hypersys-lettering-1.png\"></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=\"https://deniz.aksimsek.tr/media/hypersys-lettering-2.png\"><img src=\"https://deniz.aksimsek.tr/media/hypersys-lettering-2.png\"></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=\"https://deniz.aksimsek.tr/media/hypersys-lettering-3.png\"><img src=\"https://deniz.aksimsek.tr/media/hypersys-lettering-3.png\"></a></figure>\n  </div>\n<figcaption>My early attempts at Hypermedia Systems cover lettering.</figcaption>\n</figure>\n<p>After trying to make off-the-shelf fonts work for a while, we eventually asked\nthe artist for the original PSD and I lettered in a custom title. Making it go\nbehind the car was Carson's idea.</p>\n<figure>\n<a href=\"https://deniz.aksimsek.tr/media/hypersys-lettering-final.png\"><img src=\"https://deniz.aksimsek.tr/media/hypersys-lettering-final.png\"></a>\n<figcaption>The published cover.</figcaption>\n</figure>\n<p>The initial plan was to make an unslanted version of the lettering and put it\non the landing page as an image, but I'd recently heard about Panic's <a href=\"https://play.date/caps\">Caps</a>\nfont design tool for the Playdate console, so I decided to give a making a whole\nfont a go.</p>\n<p>Caps is great, but it can only save fonts in a Playdate-specific format --- a\nfact I realized far too late. After much searching, I found <a href=\"https://github.com/kreativekorp/bitsnpicas\">Bits'n'Picas</a>, a\nbitmap font tool that could both import the Playdate format and export to .ttf.</p>\n<p>The font is live on <a href=\"https://hypermedia.systems/\">https://hypermedia.systems</a>, both on the landing page and\nin the content for chapter and section headings.</p>\n<p>Right now, Hypersystem supports ASCII, rudimentary Turkish, and a few extra\npunctuation characters.</p>\n<p><strong><a href=\"https://deniz.aksimsek.tr/media/Hypersystem.ttf\" download=\"\">Download Hypersystem</a></strong></p>\n","tags":["entry","projects","design/projects"],"date_published":"2025-05-23T00:40:57.000Z","image":"https://deniz.aksimsek.tr/hypersystem-preview.png"},{"id":"https://deniz.aksimsek.tr/2025/version/","url":"https://deniz.aksimsek.tr/2025/version/","title":"A versioning scheme for end-user software","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<p>The title is wrong. It should be more like \"versioning scheme for software that suffers from the <a href=\"https://xkcd.com/1172/\">spacebar heating</a> problem, i.e. software for which a \"breaking change\" cannot reasonably be distinguished from a non-breaking one.</p>\n<h2>Background (feel free to skip)</h2>\n<p>I was planning on publishing the first ever alpha release of Denizen. I typed <code>git tag</code> into my terminal and paused.</p>\n<p>I get these long pauses a lot. It usually happens when I'm starting a new project and want a good name, always a challenge having already used up Denizen.</p>\n<p>I <em>was</em> going to type <code>v0.0.0</code> and push. Then I thought, \"hm, should it be 0.0.0 or 0.0.1?\" Then I asked myself the real question, \"how and why am I planning to use semver for a project with no public API beyond its user interface?\" Denizen does interact with nonhuman systems via APIs, but these are things like Webmentions that are governed by external standards -- and besides, the target sites who these APIs target can't choose which version of Denizen is contacting them, so a semantic version is no use.</p>\n<p>I started looking around online for alternative versioning systems. My conclusion was that SemVer was the worst system, except for all the others.</p>\n<p>I was thinking of using a single increasing version number, but it didn't sit right with me. This post came from my attempt to figure out why.</p>\n<h2>Concepts</h2>\n<p>A software package under CoolVer has three kinds of published version: release, pre-release and hotfix.</p>\n<ul>\n<li>\n<p>Releases add, remove or alter features. They may or may not break your workflow based on your use case, environment, personality and vibes.</p>\n</li>\n<li>\n<p>Hotfixes fix bugs in previous regular releases (and do nothing else). The existence of a hotfix declares that the preceding version is broken and this one should be used instead. Hotfixes \"shouldn't\" break your workflow, if they do, it's because they had to.</p>\n</li>\n<li>\n<p>Prereleases are unstable testing releases in preparation for a regular release. Each prerelease may fix bugs or add/remove/alter features.</p>\n</li>\n</ul>\n<h2>Scheme</h2>\n<figure>\n<div style=\"text-align:center;padding-block:1em\">\n<p>REGULAR.HOTFIX[-PRERELEASE]</p>\n<p>where REGULAR = VANITY.RELEASE</p>\n<p>and PRERELEASE = CHANNEL.NUMBER</p>\n</div>\n<figcaption>The CoolVer versioning scheme.</figcaption>\n</figure>\n<p>REGULAR is the regular-release identifier, either of this release (if it is a regular release) or the associated release (if it is a hotfix or prerelease).</p>\n<p>VANITY is exactly what it sounds like. Increment it when you've made changes you're excited about, or you want other people to be excited about. Incrementing VANITY resets RELEASE, which otherwise increments with each regular release.</p>\n<p>HOTFIX is the hotfix identifier. If the release is not a hotfix, it is 0. Otherwise, it is a number that monotonically increases <strong>across the whole release history</strong>. If a hotfix H to a release R has been backported to an older release B, the hotfix ID can be reused: V.B.H is the backporting of V.R.H to V.B.0.</p>\n<p>PRERELEASE is for prereleases -- it consists of a CHANNEL like \"alpha\", \"beta\" or \"rc\", and a numerical identifier.</p>\n<p>METADATA is build metadata as defined in SemVer.</p>\n<h2>Example</h2>\n<table>\n<thead>\n<tr>\n<th>Version</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>0.0.0-alpha.0</code></td>\n<td>starting a new project</td>\n</tr>\n<tr>\n<td><code>0.0.0-alpha.1</code></td>\n<td>still working on it</td>\n</tr>\n<tr>\n<td><code>0.0.0-beta.0</code></td>\n<td>anyone want to try it out?</td>\n</tr>\n<tr>\n<td><code>0.1.0</code></td>\n<td>it's stable</td>\n</tr>\n<tr>\n<td><code>1.0.0-rc.1</code></td>\n<td>i think i'm ready to start promoting this, let me know if you spot any bugs</td>\n</tr>\n<tr>\n<td><code>1.0.0</code></td>\n<td>hey everyone look at my production-ready, blazing-fast, webscale project!</td>\n</tr>\n<tr>\n<td><code>1.0.1</code></td>\n<td>oops, i broke the build</td>\n</tr>\n<tr>\n<td><code>1.1.0</code></td>\n<td>added frobinator</td>\n</tr>\n<tr>\n<td><code>1.1.2</code></td>\n<td>the frobinator had a bug</td>\n</tr>\n<tr>\n<td><code>1.2.0-rc.1</code></td>\n<td>sneak preview of barbinator</td>\n</tr>\n<tr>\n<td><code>1.2.0</code></td>\n<td>add barbinator</td>\n</tr>\n<tr>\n<td><code>1.2.3</code></td>\n<td>another frobinator bug we didn't catch until now</td>\n</tr>\n<tr>\n<td><code>1.1.3</code></td>\n<td>backport the frobinator fix for the people who hate the barbinator</td>\n</tr>\n</tbody>\n</table>\n<h2>Rationale</h2>\n<p>CoolVer is syntactically compatible with SemVer and matches its sorting order. This means it can be used with most tools that expect SemVer.</p>\n<p>Using the SemVer patch field for hotfixes, but tags for prereleases ensures that releases are sorted sensibly, and is arguably more SemVer than the <code>-hotfix.1</code> convention many projects use.</p>\n","tags":["entry"],"date_published":"2025-01-17T13:42:50.000Z"},{"id":"https://deniz.aksimsek.tr/2025/intl/","url":"https://deniz.aksimsek.tr/2025/intl/","title":"How do I internationalize my CMS?","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<h2>Background</h2>\n<p>Denizen is a CMS for personal websites.\nI want to work on multi-language support --\npeople should be able to make websites and posts in any language they want.\n(My own blog is in English, Turkish, and Toki Pona --\nso this is a blocker for eating my dog food).</p>\n<p>Every aspect of this feels like something other people must have already solved,\nso I wanted to publicly ask if anyone has the secret right answers.</p>\n<h2>Language picker</h2>\n<p>Requirements: Users should be able to set their site to \"any language\".\nI know that's impossible to define, of course.\nI'm happy to settle for \"every language with an ISO-639 code\"\nincluding conlangs like Toki Pona (<code>tok</code>), which I want for my own site.\nHowever, I can't find a good list anywhere.\nWikipedia has one that only lists two-letter codes,\nand another that's split across 26 pages with quite a bit of missing data.\nIdeally, I could just download a big JSON file of every assigned code,\nwith each language's native and English names,\nas well as labels for the macrolanguages and non-language codes like 'zxx'.</p>\n<h2>Fallback</h2>\n<p>In addition to marking up the website and the posts in it,\nthe language setting would be used for Denizen's UI\nincluding phrases in the public blog like \"reply to\" or \"last updated\".\nSince Denizen can't possibly be translated to every ISO-639 language,\nit needs a fallback mechanism.</p>\n<p>There's three tiers of sophistication for language matching, all of which I'd ideally like to support:</p>\n<ul>\n<li>\n<p>Simple BCP 47 matching, e.g. <code>en-US</code> to <code>en</code>.</p>\n</li>\n<li>\n<p>Sister language matching, e.g. Dutch <code>nl</code> and Afrikaans <code>af</code>, or Turkish <code>tr</code> and Azerbaijani <code>az</code>.</p>\n</li>\n<li>\n<p>Unrelated language matching. I could swear I saw Wikipedia using Turkish as a fallback for Laz <code>lzz</code>,\nbut I can't reproduce it. Nevertheless, it would be a desirable feature.\nFor context, Laz and Turkish are not related linguistically (beyond loanwords),\nbut most Laz speakers are bilingual in Turkish.</p>\n</li>\n</ul>\n<p>Here, <a href=\"https://cldr.unicode.org/\">CLDR</a> seems to promise a fix, but I couldn't figure out how.</p>\n<h2>Selecting language for individual posts</h2>\n<p>The big social medias use heuristics to detect language.\nIt's expensive and goes wrong sometimes.</p>\n<p>Is there an alternative?\nIf someone uses denizen to post mixed language articles (e.g. language learning material),\nwill they annotate it all correctly? Can I somehow do it for them?\nCan I help them? Can I encourage them?</p>\n","tags":["entry","questions","computer"],"date_published":"2025-01-16T14:02:39.000Z"},{"id":"https://deniz.aksimsek.tr/2024/fruit-credits/","url":"https://deniz.aksimsek.tr/2024/fruit-credits/","title":"Fruit Credits: a personal accounting app based on hledger","authors":[{"name":"Deniz Akşimşek","url":"https://deniz.aksimsek.tr/","avatar":"https://deniz.aksimsek.tr/assets/me.jpeg"}],"content_html":"<p>I recently published the first pre-release version of <a href=\"https://fruitcredits.dz4k.com/\">Fruit Credits</a> on Flathub.\n(I then immediately published two more because the reviewer discovered a bug).</p>\n<h2>why: boring life stuff</h2>\n<p>After quitting my job and realizing it would be a while before I found a new one[^1],\nI realized I might need to be a bit more responsible with money.\nNaturally, I downloaded <a href=\"https://hledger.org/\">hledger</a>.</p>\n<p>I'd tried to use hledger before but couldn't make a habit of it.\nI was concerned that my new attempt could be a procrastination mechanism,\nbut to my surprise, it was actually massively helpful.\nEven after one month, I felt more in control of my life than ever before,\nand it's kept me in the black since.</p>\n<p>It was hard for me at first to see the value of keeping your own books\nwhen banks already record and present a history of transactions.\nRight now, the benefits I experience are:</p>\n<ul>\n<li>\n<p><strong>Everything is in one place.</strong>\nMost people keep multiple accounts at multiple banks.\nBanks are somehow all shit at application engineering,\nor even just providing usable data export.\nBefore picking up hledger, I straight-up had no idea <em>how much money I had</em>.</p>\n</li>\n<li>\n<p><strong>The data is queryable.</strong>\nI'm talking super basic stuff --\nI've not even scratched the surface of what one can do with plain text accounting,\nyet it's still massively valuable to ask the computer\n\"how much did I spend on A since B?\"</p>\n</li>\n<li>\n<p><strong>The usual reasons.</strong>\nIt's free software operating on free file formats.\nI can see my account balances without popups offering me loans I can't pay back.</p>\n</li>\n</ul>\n<p>[^1]: I'm fine -- I live with my parents, have no dependents\nand still have some income via <a href=\"https://hypermedia.systems/\">Hypermedia Systems</a>.\nI would still very much like a job though, which is why\nI invested in highly demanded skills as Vala programming and Haskell Flatpak packaging.</p>\n<p>I started by typing transactions in the ledger format directly in a text editor.\nThen, I started using <code>hledger add</code>, which has autocompletion capabilities.\nUnfortunately, both of these were too clunky for me --\nmaybe it takes getting used to, but I kept making typos in <code>hledger add</code>\nand fumbling as I tried to undo them.</p>\n<p>I imagined a GUI for adding transactions quickly that wouldn't require me to enter things in order.\nThis vision would eventually feature-creep itself into a GUI version of hledger.</p>\n<h2>how: exciting computer stuff</h2>\n<p>I'd heard <a href=\"https://tauri.app/\">Tauri</a> was Electron but good,\nwhich was an attractive proposition to a web developer.\nIt took me about a week to give up --\nit turns out making a web app look good as a desktop app is harder than Discord makes it look.</p>\n<p>Being a GNOME user,\nI was inspired by the many <a href=\"https://circle.gnome.org/\">GNOME Circle</a> apps to build something with <a href=\"https://gtk.org/\">GTK4</a> and <a href=\"https://gnome.pages.gitlab.gnome.org/libadwaita/\">libadwaita</a>.\nI fired up GNOME Builder and spent about 2 days paralyzed by the language selector.\nAfter looking at code examples online, I decided <a href=\"https://vala.dev/\">Vala</a> was the simplest option.</p>\n<p>I used GNOME Builder to scaffold the app, and used it to develop for a while.\nEventually, I figured out how to build the app without it, and went back to Zed.</p>\n<p>It took me a while to get everything configured --\nfor example, the default Builder template has a directory for translations with <a href=\"https://en.wikipedia.org/wiki/Gettext\">gettext</a>,\nbut there's extra setup required to actually build and use them.</p>\n<p>However, between the build headaches, the programming inner loop was quite enjoyable,\nespecially with the new-ish <a href=\"https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/\">Blueprint</a> UI language.</p>\n<h3>vala is pretty cool, imo</h3>\n<p>It seems that Rust is the recommendation for new GTK apps.\nUnfortunately, I never got into Rust, and\nI don't think adding the GObject memory management model into the mix would make it grow on me.</p>\n<p>For the uninitiated, GObject is a subsystem that was originally part of GTK that\nprovides object-oriented programming and reference-counted memory management features in C.\nThough GObject has some fun features like signals,\nthere's little fun to be had in writing classes in C --\neven with all the macro magic provided, class definitions are full of boilerplate code.\nIn addition, the reference counting mechanism requires you to <code>g_object_ref ()</code> and unref your objects manually.</p>\n<p>One feature of GObject is the ability to generate bindings to other languages,\nincluding ones with actual OOP features,\nwhich allows GTK apps to be implemented in a variety of languages.\nHowever, these bindings often suffer from various impedance mismatches\nbetween GObject and the languages' own object models.</p>\n<p>Vala, however, is implemented with GObject in mind from the start.\nIt has built-in syntax for features (like signals)</p>\n<p>Though I've fallen in love with Vala, I can see why other people might not enjoy it.\nIt has unfixed compiler bugs, and a standard library that was written for C.\nMost frustratingly though, refcounting is not as ergonomic as full tracing garbage collection,\nand when using C libraries (including GObject based ones!)\nthat expect you to be comfortable with creative usage of memory,\nyou can run into hard-to-debug segfaults.</p>\n<p>The de facto debugger for Vala is <a href=\"https://en.wikipedia.org/wiki/GNU_Debugger\">gdb</a>, and it has no Vala support.\nYou have to debug through the generated C.\nI had no gdb experience.\nThankfully, GLib has a built in logging framework that makes printf-style debugging quite comfortable.</p>\n<p>What draws me to Vala is the feeling of consideration that it exudes from every orifice.\nI would constantly discover that whatever I was trying to do,\nthe Vala designers knew I'd need to do it, and had implemented a feature to make it easier.</p>\n<h3>flatpacking haskell</h3>\n<p>A GTK app written in Vala and compiled with Meson is the happiest of happy paths for Flatpak.\nUnfortunately, hledger, which Fruit Credits necessarily depends on, is written in Haskell.</p>\n<p>Flathub requires not only that everything is built from source,\nbut also that all necessary sources can be downloaded before the build process (which is sandboxed).\nThis is antithetical to the model used by Haskell (and npm, and cargo, and many others)\nwhere a build tool downloads dependencies and discovers transitive dependencies at the same time.</p>\n<p>After a few days of digging through outdated resources, I understood that\na tool called <a href=\"https://hackage.haskell.org/package/cabal-flatpak\">cabal-flatpak</a> was the best way to generate Flatpak manifests that\nfetch and compile Haskell libraries in the right order.\nUnfortunately, despite seemingly being under active maintenance, the tool had bugs,\nand no discernible way to report them.\nSince I don't know Haskell, I couldn't maintain a fork,\nso I wrote <a href=\"https://github.com/flathub/com.dz4k.FruitCredits/tree/master/scripts\">some jq and shell to work around the bugs</a>.</p>\n<p>Upon submitting my app to Flathub, I found out I was something of a pioneer:</p>\n<blockquote>\n<ul role=\"list\">\n<li><strong>bbhtt:</strong> why are these needed and why do you need to do this manually?</li>\n<li><strong>dz4k:</strong> The haskell modules in the manifest are based on the output of\n<a href=\"https://hackage.haskell.org/package/cabal-flatpak-0.0\">cabal-flatpak</a>\nand the article <a href=\"https://medium.com/@lettier/how-to-flatpak-a-haskell-app-into-flathub-86ef6d69e94d\">How to Flatpak a Haskell App into Flathub</a>.\nAre there more up-to-date resources for bundling Haskell programs?</li>\n<li><strong>bbhtt:</strong> <mark>No, we don't have haskell apps. This is probably the second or third one ever.</mark></li>\n</ul>\n</blockquote>\n<p class=\"quote-attribution\">-- <a href=\"https://github.com/flathub/flathub/pull/5731#discussion_r1805683874\">Add com.dz4k.FruitCredits by dz4k · Pull Request #5731 · flathub/flathub</a></p>\n<p>fun.</p>\n<h2>what: thermonuclear war stuff</h2>\n<p>Though Fruit Credits is pretty barebones compared to what hledger can do,\nit's crossed from \"dogfooding\" to actually usable software for my use cases.\nQueries and transaction input work well.</p>\n<p>I'm currently working on a setup interface to create a new ledger file from scratch,\nas well as making the app more beginner-friendly in general.\nI'd like to add some way of editing and deleting transactions,\nand some reporting features.</p>\n<p>My usage of hledger is pretty limited in the grand scheme of things,\nso I won't be able to cover every use case by myself.\nI'd love it if people gave Fruit Credits a try,\neven if just to tell me how it couldn't read their journal file.</p>\n<hr>\n<ul>\n<li><a href=\"https://flathub.org/apps/com.dz4k.FruitCredits\">get it on Flathub</a></li>\n<li><a href=\"https://codeberg.org/dz4k/fruit-credits\">get the code on Codeberg</a></li>\n<li><a href=\"https://codeberg.org/dz4k/fruit-credits/issues\">complain about it on Codeberg</a></li>\n<li><a href=\"https://fruitcredits.dz4k.com/\">look at the website on the website</a></li>\n</ul>\n","tags":["entry","computer/projects"],"date_published":"2024-11-02T23:00:51.000Z"}]}