{"version":"https://jsonfeed.org/version/1","title":"Deniz Akşimşek","icon":"https://denizaksimsek.com/assets/me.jpeg","home_page_url":"https://denizaksimsek.com/","feed_url":"https://denizaksimsek.com/feed.json","author":{"name":"Deniz Akşimşek","url":"https://denizaksimsek.com/","avatar":"https://denizaksimsek.com/assets/me.jpeg"},"items":[{"id":"/2026/psatina/","title":"Psatina: a new tiny JS framework","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Psatina: a new tiny JS framework</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Psatina: a new tiny JS framework\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2026/psatina/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Psatina: a new tiny JS framework</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Psatina: a new tiny JS framework</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>.</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=\"/assets/photos/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=&quot;{ count: 1 }&quot;&gt;\n  &lt;output&gt;[| count |]&lt;/output&gt;\n  &lt;button type=&quot;button&quot; p:on:click=&quot;update(() =&gt; count++)&quot;&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<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2\">[2]</a></sup>.\nI’m thinking about reviving my htmx clone <a href=\"https://codeberg.org/dz4k/whet\">whet</a> to pair with it.</p>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>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. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p>Not actual numbers. <a href=\"#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2026-04-16T19:06:38Z\">\n                16 Apr ’26 19:06</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"While I was building [FemtoCRM],\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. `line_items[2].quantity`) which seemed like a pain to manage[^1].\n\nI 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 [Alpine.js], 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.\n\n![GitHub screenshot: alpine (public). A user you’ve blocked has previously contributed to this repository. claude.](/assets/photos/alpine-claude.png)\n\nGreat, man. Love that for you.\n\nWell, it's not _just_ 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 collection of tiny libraries][js4k]\nand hoping a framework would arise out of that somehow.\n\nWell, it didn't, and it turns out separate tiny libraries\nget bigger when you put them together.\nOver the last week or two, built [Psatina],\na framework inspired by Vue and Alpine in 1.6 KiB (.min.br).\n\n```html\n<template p:data=\"{ count: 1 }\">\n  <output>[| count |]</output>\n  <button type=\"button\" p:on:click=\"update(() => count++)\">Increase</button>\n</template>\n```\n\nPsatina 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 [whet] to pair with it.\n\n[FemtoCRM]: https://codeberg.org/dz4k/femtocrm\n[Alpine.js]: https://alpinejs.dev\n[js4k]: https://codeberg.org/dz4k/js4k\n[whet]: https://codeberg.org/dz4k/whet\n[Psatina]: https://psatina.dz4k.com\n\n[^1]: OK, it probably wouldn't be _that_ hard.\n  But as mentioned, I like my JS components to be general-purpose.\n  For this kind of use case, the generally-applicable tool is\n  a reactive framework.\n[^2]: Not actual numbers.\n","url":"https://denizaksimsek.com/2026/psatina/","date_published":"2026-04-16T19:06:38Z"},{"id":"/2026/app-grid-folders/","title":"Pin files & folders to your app grid on GNOME","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Pin files &amp; folders to your app grid on GNOME</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Pin files &amp; folders to your app grid on GNOME\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2026/app-grid-folders/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Pin files &amp; folders to your app grid on GNOME</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Pin files &amp; folders to your app grid on GNOME</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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    &quot;&quot;&quot;Get the icon associated with a file.&quot;&quot;&quot;\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    &quot;&quot;&quot;Generate and install desktop entry for a file or folder.&quot;&quot;&quot;\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    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2026-04-16T16:00:07Z\">\n                16 Apr ’26 16:00</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"This script generates a desktop entry that opens a folder,\nwith the same icon and all.\n\n```python\n#!/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```\n","url":"https://denizaksimsek.com/2026/app-grid-folders/","date_published":"2026-04-16T16:00:07Z"},{"id":"/2026/training-data/","title":"Generative AI is not trained on \"data\"","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Generative AI is not trained on &quot;data&quot;</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Generative AI is not trained on &quot;data&quot;\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2026/training-data/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Generative AI is not trained on &quot;data&quot;</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Generative AI is not trained on &quot;data&quot;</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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<p>— <a href=\"https://www.merriam-webster.com/dictionary/data\">Merriam Webster, “data”</a></p>\n</blockquote>\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<p>— Dylan Thomas, <cite>Do Not Go Gentle into That Good Night</cite>. TODO: replace with a more topical poem.</p>\n</blockquote>\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<p>— M-W</p>\n</blockquote>\n<p>Behold, a .txt file:</p>\n<table style=\"width: 100%;\">\n  <col style=\"width: 6ch;\"><col>\n  <caption>Dylan Thomas, \"Do Not Go Gentle into That Good Night\"</caption>\n  <tr><td>0000<td>D\n  <tr><td>0001<td>o\n  <tr><td>0002<td> \n  <tr><td>0003<td>n\n  <tr><td>0004<td>o\n  <tr><td>0005<td>t\n  <tr><td>0006<td> \n  <tr><td>0007<td>g\n  <tr><td>0008<td>o\n  <tr><td colspan=2>...\n</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<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup> — you don’t pay the wind to measure its speed.</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<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</blockquote>\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.<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2\">[2]</a></sup> 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>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”,<sup class=\"footnote-ref\"><a href=\"#fn3\" id=\"fnref3\">[3]</a></sup> 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<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>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”. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p>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? <a href=\"#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p>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? <a href=\"#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2026-01-23T16:37:42Z\">\n                23 Jan ’26 16:37</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"> data\n> :   1. <mark>factual information</mark> (such as measurements or statistics) used as a basis for reasoning, discussion, or calculation\n>\n> --- [Merriam Webster, \"data\"](https://www.merriam-webster.com/dictionary/data)\n\nIs a poem factual information?\n\n> Do not go gentle into that good night,\\\n> Old age should burn and rave at close of day;\\\n> Rage, rage against the dying of the light.\n>\n> --- Dylan Thomas, <cite>Do Not Go Gentle into That Good Night</cite>. TODO: replace with a more topical poem.\n\nDoesn't look like it. But what about this?\n\n> 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.\"\n\nOh dear, that's looking pretty factual to me! See how the magical transformation is completed:\n\n> 2. information in digital form that can be transmitted or processed\n>\n> --- M-W\n\nBehold, a .txt file:\n\n<table style=\"width: 100%;\">\n  <col style=\"width: 6ch;\"><col>\n  <caption>Dylan Thomas, \"Do Not Go Gentle into That Good Night\"</caption>\n  <tr><td>0000<td>D\n  <tr><td>0001<td>o\n  <tr><td>0002<td> \n  <tr><td>0003<td>n\n  <tr><td>0004<td>o\n  <tr><td>0005<td>t\n  <tr><td>0006<td> \n  <tr><td>0007<td>g\n  <tr><td>0008<td>o\n  <tr><td colspan=2>...\n</table>\n\nAha! 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.\n\nAnd there's more! The advent of the [multimedia computer](https://en.wikipedia.org/wiki/Multimedia_computer) means I can perform the same trick on paintings, songs, speeches, movies and anything else your heart desires!\n\n\n## Maybe we shouldn't treat everything that can be saved on a computer like soil density\n\nEven though an amateur rhetorician (that's a portmanteau of \"rhetoric\" and \"magician\", I'm still working on it) like me can trivially transmute _works_ into _data_, the two have very different moral frameworks.\n\nDepending on where you live and what your ideology is, data is either unownable, or owned by the person who _collects_ 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 _producer_ of data has no rights over it[^gdpr] --- you don't pay the wind to measure its speed.\n\n[^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\".\n\nBut 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.\n\nThese are sensible if you assume that _data_ is produced involuntarily, mostly by inanimate beings, and collecting it takes effort, while _works_ are the result of intentional human labor, and are easy to \"collect\" since most creatives want their works seen.\n\nThey're also sensible if you assume that _data_ is collected by _researchers_ to advance science or provide reference material, and _works_ are collected by _publishers_ to sell for entertainment and edification.\n\nSo, 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. \n\n\n## Wait... aren't AI models data too?\n\nThe 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),\n\nBut these powerful entities, through rituals like _word vectorization_, _reinforcement learning with human feedback_, and _bribing politicians_, are now trying to transmute the database into an ownable, sellable work without turning the data inside back into works as well.\n\n> 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>\n>\n> --- [Aaron Swartz, A Programmable Web](https://upload.wikimedia.org/wikipedia/commons/3/3f/Aaron_Swartz_s_A_Programmable_Web_An_Unfinished_Work.pdf)\n\n(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.)\n\nI 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.\n\n[^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?\n\nThe ~~megacorps~~ spirits continue to make money, because they can perform the bribery rituals.\n\nI cannot. So, instead, I'm trying to cast a counterspell upon the initial data-fication spell.\n\n\n## Training materials\n\nI'm dropping the magic shtick now.\n\nThis 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.\n\nBut 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.\n\nSo, 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.\n\n[^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?\n","url":"https://denizaksimsek.com/2026/training-data/","date_published":"2026-01-23T16:37:42Z"},{"id":"/2025/tailscale/","title":"how I run Tailscale on an immutable Linux system","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>how I run Tailscale on an immutable Linux system</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"how I run Tailscale on an immutable Linux system\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/tailscale/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>how I run Tailscale on an immutable Linux system</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">how I run Tailscale on an immutable Linux system</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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 <a href=\"https://pkgs.tailscale.com/stable/#static\">https://pkgs.tailscale.com/stable/#static</a> 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 &quot;Downloading Tailscale into $tmp&quot;\ncurl &quot;https://pkgs.tailscale.com/stable/$tarball&quot; | tar -xzC &quot;$tmp&quot;\n\nsystemctl --user stop tailscaled.service\n\ntsdir=$(find &quot;$tmp&quot; -name 'tailscale_*' -type d | head -n1)\ncp &quot;$tsdir&quot;/{tailscale,tailscaled} ~/.local/bin/\n\nsystemctl --user enable tailscaled.service\nsystemctl --user start  tailscaled.service\n</code></pre>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-11-23T20:05:53Z\">\n                23 Nov ’25 20:05</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"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.\n\nYou 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.\n\n(Instructions for systemd.)\n\n\n## Create systemd unit\n\nWe're going to run `tailscaled` as a _user service_, so create a file called `tailscaled.service` in `~/.config/systemd/user/` with the following contents:\n\n```ini\n[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```\n\nI wrote this based on the unit file Tailscale ships combined with some other unit files I saw online.\n\n\n## Install Tailscale binaries\n\nGo to https://pkgs.tailscale.com/stable/#static to download the latest static binary builds of Tailscale. Extract and place the `tailscale` and `tailscaled` files in `~/.local/bin/`.\n\nI wrote a script I use that does this, and starts up the service (needs systemd unit file to be created first):\n\n```sh\n#!/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```\n","url":"https://denizaksimsek.com/2025/tailscale/","date_published":"2025-11-23T20:05:53Z"},{"id":"/2025/webdev/","title":"A web developer's feed reader","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>A web developer&#39;s feed reader</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"A web developer&#39;s feed reader\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/webdev/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>A web developer&#39;s feed reader</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">A web developer&#39;s feed reader</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-07-29T08:26:40Z\">\n                29 Jul ’25 08:26</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"- **Introducing the `<zoop>` element** --- Chrozilla Dev Blog\n- **How to magically improve every aspect of your website using `<zoop>`** --- Warm Color Site\n- **Is `<zoop>` the next big thing in web development?** --- XSLT-Tricks\n- **How to integrate `<zoop>` with Nẅxt.js** --- Vorcel\n- **`<zoop>` still not supported in iOS Safari** --- May I Use\n- **doop.js: a `<zoop>` polyfill** --- GitCub\n- **New `<zoop>` element causes some screen readers to shout slurs at user** --- Aeleveny Review\n","url":"https://denizaksimsek.com/2025/webdev/","date_published":"2025-07-29T08:26:40Z"},{"id":"/2025/retrofuture/","title":"Retrofuture: a blackletter-inspired pixel font","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Retrofuture: a blackletter-inspired pixel font</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Retrofuture: a blackletter-inspired pixel font\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/retrofuture/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Retrofuture: a blackletter-inspired pixel font</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Retrofuture: a blackletter-inspired pixel font</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <style>\n@font-face { font-family: \"Retrofuture\"; src: url(/assets/fonts/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=\"/assets/fonts/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    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-07-14T19:45:05Z\">\n                14 Jul ’25 19:45</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<style>\n@font-face { font-family: \"Retrofuture\"; src: url(/assets/fonts/Retrofuture.ttf) format(truetype) }\n</style>\n\n<figure style=\"margin: 2em 0;\">\n\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\nIN CASE OF CONFLICT, consider users over authors over implementors over specifiers over theoretical purity.\n\n</div>\n\n<figcaption><cite>Retrofuture</cite> sample. Quote: W3C, HTML Design Principles\n  § 3.2 Priority of Constituencies</figcaption>\n</figure>\n\n**[Download Retrofuture](/assets/fonts/Retrofuture.ttf){download}**\n\nAnother 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.\n\nRetrofuture replaces the wonderful [Jacquarda Bastarda](https://www.soft-type.com/jacquarda-bastarda)\non my website.\n","url":"https://denizaksimsek.com/2025/retrofuture/","date_published":"2025-07-14T19:45:05Z"},{"id":"/2025/hypersystem/","title":"Hypersystem: a pixel display font for Hypermedia Systems","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Hypersystem: a pixel display font for Hypermedia Systems</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Hypersystem: a pixel display font for Hypermedia Systems\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/hypersystem/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"hypersystem-preview.png\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Hypersystem: a pixel display font for Hypermedia Systems</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Hypersystem: a pixel display font for Hypermedia Systems</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <style>\n@font-face { font-family: \"Hypersystem\"; src: url(/assets/fonts/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=\"/assets/fonts/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=/assets/photos/hypersys-lettering-1.png><img src=/assets/photos/hypersys-lettering-1.png></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=/assets/photos/hypersys-lettering-2.png><img src=/assets/photos/hypersys-lettering-2.png></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=/assets/photos/hypersys-lettering-3.png><img src=/assets/photos/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=/assets/photos/hypersys-lettering-final.png><img src=/assets/photos/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=\"/assets/fonts/Hypersystem.ttf\" download=\"\">Download Hypersystem</a></strong></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-05-22T21:40:57Z\">\n                22 May ’25 21:40</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<style>\n@font-face { font-family: \"Hypersystem\"; src: url(/assets/fonts/Hypersystem.ttf) format(truetype) }\n</style>\n\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\nHypertexts: 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.\n\n</figure>\n\n**[Download Hypersystem](/assets/fonts/Hypersystem.ttf){download}**\n\nHypersystem is a new font I designed for the web version of Hypermedia Systems.\n\nRecently, I reworked the web page of our book Hypermedia Systems\n(<https://hypermedia.systems>). 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.\n\nAfter trying out a few alternatives, Carson suggested that I adapt the lettering\nfrom the cover of the [paperback edition]. The pixel artist we hired did an\nabsolutely fantastic job, but we decided to roll our own for the lettering.\n\n[paperback edition]: <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>\n\n<figure>\n  <div style=\"display: flex; flex-flow: row nowrap; gap: 1em;\">\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=/assets/photos/hypersys-lettering-1.png><img src=/assets/photos/hypersys-lettering-1.png></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=/assets/photos/hypersys-lettering-2.png><img src=/assets/photos/hypersys-lettering-2.png></a></figure>\n    <figure style=\"margin: 0; flex: 1 1 auto;\"><a href=/assets/photos/hypersys-lettering-3.png><img src=/assets/photos/hypersys-lettering-3.png></a></figure>\n  </div>\n<figcaption>My early attempts at Hypermedia Systems cover lettering.</figcaption>\n</figure>\n\nAfter 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.\n\n<figure>\n<a href=/assets/photos/hypersys-lettering-final.png><img src=/assets/photos/hypersys-lettering-final.png></a>\n<figcaption>The published cover.</figcaption>\n</figure>\n\nThe 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 [Caps]\nfont design tool for the Playdate console, so I decided to give a making a whole\nfont a go.\n\n[caps]: <https://play.date/caps>\n\nCaps 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 [Bits'n'Picas], a\nbitmap font tool that could both import the Playdate format and export to .ttf.\n\n[Bits'n'Picas]: <https://github.com/kreativekorp/bitsnpicas>\n\nThe font is live on <https://hypermedia.systems>, both on the landing page and\nin the content for chapter and section headings.\n\nRight now, Hypersystem supports ASCII, rudimentary Turkish, and a few extra\npunctuation characters.\n\n**[Download Hypersystem](/assets/fonts/Hypersystem.ttf){download}**\n","url":"https://denizaksimsek.com/2025/hypersystem/","image":"https://denizaksimsek.com/hypersystem-preview.png","date_published":"2025-05-22T21:40:57Z"},{"id":"/2025/version/","title":"A versioning scheme for end-user software","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>A versioning scheme for end-user software</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"A versioning scheme for end-user software\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/version/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>A versioning scheme for end-user software</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">A versioning scheme for end-user software</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>The title is wrong. It should be more like &quot;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    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-01-17T10:42:50Z\">\n                17 Jan ’25 10:42</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"The title is wrong. It should be more like \"versioning scheme for software that suffers from the [spacebar heating] problem, i.e. software for which a \"breaking change\" cannot reasonably be distinguished from a non-breaking one.\n\n\n## Background (feel free to skip)\n\nI was planning on publishing the first ever alpha release of Denizen. I typed `git tag` into my terminal and paused.\n\nI 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.\n\nI _was_ going to type `v0.0.0` 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.\n\nI started looking around online for alternative versioning systems. My conclusion was that SemVer was the worst system, except for all the others.\n\nI 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.\n\n\n## Concepts\n\nA software package under CoolVer has three kinds of published version: release, pre-release and hotfix.\n\n- Releases add, remove or alter features. They may or may not break your workflow based on your use case, environment, personality and vibes.\n\n- 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.\n\n- Prereleases are unstable testing releases in preparation for a regular release. Each prerelease may fix bugs or add/remove/alter features.\n\n\n## Scheme\n\n<figure>\n<div style=\"text-align:center;padding-block:1em\">\n\nREGULAR.HOTFIX[-PRERELEASE]\n\nwhere REGULAR = VANITY.RELEASE\n\nand PRERELEASE = CHANNEL.NUMBER\n\n</div>\n<figcaption>The CoolVer versioning scheme.</figcaption>\n</figure>\n\nREGULAR 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).\n\nVANITY 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.\n\nHOTFIX is the hotfix identifier. If the release is not a hotfix, it is 0. Otherwise, it is a number that monotonically increases **across the whole release history**. 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.\n\nPRERELEASE is for prereleases -- it consists of a CHANNEL like \"alpha\", \"beta\" or \"rc\", and a numerical identifier.\n\nMETADATA is build metadata as defined in SemVer.\n\n\n## Example\n\n| Version         | Description    |\n|-----------------|----------------|\n| `0.0.0-alpha.0` | starting a new project |\n| `0.0.0-alpha.1` | still working on it |\n| `0.0.0-beta.0`  | anyone want to try it out? |\n| `0.1.0`         | it's stable |\n| `1.0.0-rc.1`    | i think i'm ready to start promoting this, let me know if you spot any bugs |\n| `1.0.0`         | hey everyone look at my production-ready, blazing-fast, webscale project! |\n| `1.0.1`         | oops, i broke the build |\n| `1.1.0`         | added frobinator |\n| `1.1.2`         | the frobinator had a bug |\n| `1.2.0-rc.1`    | sneak preview of barbinator |\n| `1.2.0`         | add barbinator |\n| `1.2.3`         | another frobinator bug we didn't catch until now |\n| `1.1.3`         | backport the frobinator fix for the people who hate the barbinator |\n\n\n## Rationale\n\nCoolVer is syntactically compatible with SemVer and matches its sorting order. This means it can be used with most tools that expect SemVer.\n\nUsing the SemVer patch field for hotfixes, but tags for prereleases ensures that releases are sorted sensibly, and is arguably more SemVer than the `-hotfix.1` convention many projects use.\n\n[spacebar heating]: https://xkcd.com/1172/\n","url":"https://denizaksimsek.com/2025/version/","date_published":"2025-01-17T10:42:50Z"},{"id":"/2025/intl/","title":"How do I internationalize my CMS?","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>How do I internationalize my CMS?</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"How do I internationalize my CMS?\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2025/intl/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>How do I internationalize my CMS?</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">How do I internationalize my CMS?</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2025-01-16T11:02:39Z\">\n                16 Jan ’25 11:02</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"## Background\n\nDenizen 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).\n\nEvery 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.\n\n\n## Language picker\n\nRequirements: 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 (`tok`), 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'.\n\n\n## Fallback\n\nIn 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.\n\nThere's three tiers of sophistication for language matching, all of which I'd ideally like to support:\n\n- Simple BCP 47 matching, e.g. `en-US` to `en`.\n\n- Sister language matching, e.g. Dutch `nl` and Afrikaans `af`, or Turkish `tr` and Azerbaijani `az`.\n\n- Unrelated language matching. I could swear I saw Wikipedia using Turkish as a fallback for Laz `lzz`,\n  but I can't reproduce it. Nevertheless, it would be a desirable feature.\n  For context, Laz and Turkish are not related linguistically (beyond loanwords),\n  but most Laz speakers are bilingual in Turkish.\n\nHere, [CLDR](https://cldr.unicode.org/) seems to promise a fix, but I couldn't figure out how.\n\n\n## Selecting language for individual posts\n\nThe big social medias use heuristics to detect language.\nIt's expensive and goes wrong sometimes.\n\nIs 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?\n","url":"https://denizaksimsek.com/2025/intl/","date_published":"2025-01-16T11:02:39Z"},{"id":"/2024/fruit-credits/","title":"Fruit Credits: a personal accounting app based on hledger","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Fruit Credits: a personal accounting app based on hledger</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Fruit Credits: a personal accounting app based on hledger\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/fruit-credits/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Fruit Credits: a personal accounting app based on hledger</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Fruit Credits: a personal accounting app based on hledger</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <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<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>,\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>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<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>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. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-11-02T20:00:51Z\">\n                02 Nov ’24 20:00</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"I recently published the first pre-release version of [Fruit Credits] on Flathub.\n(I then immediately published two more because the reviewer discovered a bug).\n\n[Fruit Credits]: https://fruitcredits.dz4k.com\n\n\n## why: boring life stuff\n\nAfter 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 [hledger].\n\n[hledger]: https://hledger.org\n\nI'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.\n\nIt 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:\n\n  - **Everything is in one place.**\n    Most people keep multiple accounts at multiple banks.\n    Banks are somehow all shit at application engineering,\n    or even just providing usable data export.\n    Before picking up hledger, I straight-up had no idea _how much money I had_.\n\n  - **The data is queryable.**\n    I'm talking super basic stuff --\n    I've not even scratched the surface of what one can do with plain text accounting,\n    yet it's still massively valuable to ask the computer\n    \"how much did I spend on A since B?\"\n\n  - **The usual reasons.**\n    It's free software operating on free file formats.\n    I can see my account balances without popups offering me loans I can't pay back.\n\n[^1]: I'm fine -- I live with my parents, have no dependents\n  and still have some income via [Hypermedia Systems].\n  I would still very much like a job though, which is why\n  I invested in highly demanded skills as Vala programming and Haskell Flatpak packaging.\n\n[Hypermedia Systems]: https://hypermedia.systems\n\nI started by typing transactions in the ledger format directly in a text editor.\nThen, I started using `hledger add`, 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 `hledger add`\nand fumbling as I tried to undo them.\n\nI 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.\n\n\n## how: exciting computer stuff\n\nI'd heard [Tauri] 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.\n\n[Tauri]: https://tauri.app/\n\nBeing a GNOME user,\nI was inspired by the many [GNOME Circle] apps to build something with [GTK4] and [libadwaita].\nI fired up GNOME Builder and spent about 2 days paralyzed by the language selector.\nAfter looking at code examples online, I decided [Vala] was the simplest option.\n\nI 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.\n\nIt took me a while to get everything configured --\nfor example, the default Builder template has a directory for translations with [gettext],\nbut there's extra setup required to actually build and use them.\n\nHowever, between the build headaches, the programming inner loop was quite enjoyable,\nespecially with the new-ish [Blueprint] UI language.\n\n[GNOME Circle]: https://circle.gnome.org/\n[GTK4]: https://gtk.org/\n[libadwaita]: https://gnome.pages.gitlab.gnome.org/libadwaita/\n[Vala]: https://vala.dev/\n[gettext]: https://en.wikipedia.org/wiki/Gettext\n[Blueprint]: https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/\n\n\n### vala is pretty cool, imo\n\nIt 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.\n\nFor 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 `g_object_ref ()` and unref your objects manually.\n\nOne 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.\n\nVala, however, is implemented with GObject in mind from the start.\nIt has built-in syntax for features (like signals)\n\nThough 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.\n\nThe de facto debugger for Vala is [gdb], 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.\n\nWhat 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.\n\n[gdb]: https://en.wikipedia.org/wiki/GNU_Debugger\n\n\n### flatpacking haskell\n\nA 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.\n\nFlathub 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.\n\nAfter a few days of digging through outdated resources, I understood that\na tool called [cabal-flatpak] 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 [some jq and shell to work around the bugs][manifest generator].\n\nUpon submitting my app to Flathub, I found out I was something of a pioneer:\n\n> - **bbhtt:** why are these needed and why do you need to do this manually?\n> - **dz4k:** The haskell modules in the manifest are based on the output of\n>   [cabal-flatpak](https://hackage.haskell.org/package/cabal-flatpak-0.0)\n>   and the article [How to Flatpak a Haskell App into Flathub](https://medium.com/@lettier/how-to-flatpak-a-haskell-app-into-flathub-86ef6d69e94d).\n>   Are there more up-to-date resources for bundling Haskell programs?\n> - **bbhtt:** <mark>No, we don't have haskell apps. This is probably the second or third one ever.</mark>\n> {role=\"list\"}\n\n-- [Add com.dz4k.FruitCredits by dz4k · Pull Request #5731 · flathub/flathub](https://github.com/flathub/flathub/pull/5731#discussion_r1805683874)\n{.quote-attribution}\n\nfun.\n\n[cabal-flatpak]: https://hackage.haskell.org/package/cabal-flatpak\n[manifest generator]: https://github.com/flathub/com.dz4k.FruitCredits/tree/master/scripts\n\n\n## what: thermonuclear war stuff\n\nThough 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.\n\nI'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.\n\nMy 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.\n\n------\n\n- [get it on Flathub](https://flathub.org/apps/com.dz4k.FruitCredits)\n- [get the code on Codeberg](https://codeberg.org/dz4k/fruit-credits)\n- [complain about it on Codeberg](https://codeberg.org/dz4k/fruit-credits/issues)\n- [look at the website on the website](https://fruitcredits.dz4k.com/)\n","url":"https://denizaksimsek.com/2024/fruit-credits/","date_published":"2024-11-02T20:00:51Z"},{"id":"/2024/new-hypermedia-systems/","title":"Building the new Hypermedia Systems","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Building the new Hypermedia Systems</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Building the new Hypermedia Systems\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/new-hypermedia-systems/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Building the new Hypermedia Systems</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Building the new Hypermedia Systems</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><em>Clickbait title: Can you write a bestselling book in Typst?</em></p>\n<p>A shiny new edition of <cite>Hypermedia Systems</cite> is out! Apart from incorporating typo and grammar fixes sent in by readers, the book was completely redesigned inside with a new layout. To achieve a much higher fidelity of design, we ported the book to Typst — the hot new LaTeX replacement.</p>\n<h2>Why change what works?</h2>\n<p>The first print release of HS was written in AsciiDoc. It seemed like a natural choice at the time – after all, many publishing houses trust AsciiDoc for their books. We learned the hard way that those publishers also have teams of engineers developing proprietary toolchains to produce their books. Vanilla AsciiDoc doesn’t actually have a way to produce good print output:</p>\n<ul>\n<li>\n<p><code>asciidoctor-pdf</code> uses a rudimentary PDF generation library, and the output is just about readable, but not good – and you don’t have any good tools to change that.</p>\n</li>\n<li>\n<p>the venerable Pandoc can output AsciiDoc, but oddly can’t read it – so we can’t compile our book to LaTeX.</p>\n</li>\n</ul>\n<p>Your best option for producing a printable PDF, if you care at all about typesetting, is to compile to HTML, and print that to PDF. <code>asciidoctor-web-pdf</code> is a tool that wraps this workflow in a usable package, combined with Paged.js to polyfill the many features of CSS for print media that browsers don’t support. This is how we produced the first edition of HS, and it was a pain for multiple reasons.</p>\n<ul>\n<li>\n<p>Paged.js has bugs. One bug that was nasty to work around was one where using any CSS to modify the page numbers (say, resetting them after the front matter) would cause all the pages to revert to 0. I don’t know if this was fixed.</p>\n</li>\n<li>\n<p>HTML with Paged.js is slow. The book has a little over 300 pages, and each page has a perceptible delay as it gets laid out. Even worse, I had to manually scroll the browser all the way to the bottom of the book before printing, or the back half of the book would be blank. Afterwards, printing would take what felt like a solid minute, though I never measured how long: Since presumably nobody at Firefox thought printing a page to PDF would be anything but instant, there’s no progress bar or any other indicator of success beyond the PDF appearing in the folder where it’s supposed to be.</p>\n</li>\n<li>\n<p>The idiosyncrasies of this process meant that it only really worked on my machine, and I had to send PDFs back and forth with my co-authors and editor. The PDFs were absolutely massive and exceeded the Discord upload limit, so I uploaded each one to Google Drive.</p>\n</li>\n<li>\n<p>Paged.js has more bugs – asides would refuse to break across pages while code blocks were all to eager to create widows and orphans – or disappear completely – which I had to work around in a whack-a-mole fashion. <a href=\"https://github.com/bigskysoftware/hypermedia-systems-old/issues/122\">A few bugs seem to have made it into the final release</a>.</p>\n</li>\n<li>\n<p><a href=\"https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5\">How to set the background color of a table cell</a></p>\n</li>\n<li>\n<p>AsciiDoc has facilities for indexing, but doesn’t support outputting them in HTML in any way. Worse, the mechanism I could have used to do it myself was deprecated <em>and removed</em> before the replacement was implemented. Our editor ended up creating an index <strong>by hand</strong>, which was appended to the PDF.</p>\n</li>\n<li>\n<p>You go through all this effort only to be rewarded with very mediocre typesetting, since browsers use greedy algorithms for laying out text for performance reasons, yielding far worse line breaking compared to the dynamic programming algorithms of TeX.</p>\n</li>\n</ul>\n<p>Despite all this, we somehow produced a usable PDF. Most readers report being satisfied with the book’s design (Amazon’s failure to print a PDF consistently notwithstanding). The code that generated this PDF is not open source — initially, it was because we thought it might eat into print sales, but these days the real reason is that it might help someone else that tries to do this, and nobody should try to do this.</p>\n<p>(If you have any “why didn’t you just” recommendations, feel free to post them in the comments on link aggregators — they might help someone else — but don’t send them directly to me. I assure you, I’ve evaluated all options available even if I didn’t mention them here.)</p>\n<p>Speaking of link aggregators,</p>\n<h2>Typst</h2>\n<p>I first stumbled upon Typst when the author’s <a href=\"https://laurmaedje.github.io/programmable-markup-language-for-typesetting.pdf\">thesis</a> was posted on <a href=\"https://lobste.rs/s/zd8b6p/typst_programmable_markup_language_for\">the red site</a>. Enthralled by its combination of quality typesetting, lightweight markup and an ergonomic scripting language tightly integrated, I knew that if I was ever part of another book project, I would use Typst.</p>\n<p>I rewrote my <a href=\"https://cloud.dz4k.dev/s/fqfLSo4FKibzp64\">CV</a> in it, as well as some documentation for an old project. This only confirmed my initial impression — Typst is a joy to use, and the scripting language is so good I wish it was a programming language in its own right.</p>\n<p>But I couldn’t use Typst for Hypermedia Systems, because Typst didn’t support HTML output. Imagine my excitement when a while ago, I realized Pandoc had added Typst support!</p>\n<h2>Porting the manuscript</h2>\n<p>As I mentioned before, Pandoc can’t read AsciiDoc. So how can I convert the 104537 words of HS into Typst? I was ready to do it by hand, but I decided to try something else first.</p>\n<p>One of AsciiDoc’s primary output formats is DocBook XML. I don’t know what it is or who uses it, but Pandoc can read it! I converted the book to DocBook XML, and then to Typst. Except AsciiDoc was buggy and generated invalid XML, so I had to fix that first. And then massage the resulting Typst a bit more. And apply any future changes to the AsciiDoc source to the Typst version.</p>\n<p>Still better than waiting for the PDF to render, though.</p>\n<h2>The code blocks</h2>\n<p>AsciiDoc has a unique style of code blocks where you can place markers in the code that refer to explanatory notes below the block – it looks like this:</p>\n<figure><figcaption>AsciiDoc code block with callout</figcaption>\n<pre><code class=\"language-asciidoc\">[source,python]\n----\nprint(&quot;Hello, world!&quot;) &lt;1&gt;\n----\n&lt;1&gt; The canonical first program in any language.\n</code></pre>\n</figure>\n<p>If you’ve read Hypermedia Systems, now you know how we made those. Typst doesn’t have any such feature, so with the great help of the <a href=\"https://discord.gg/2uDybryKPe\">Typst Discord community</a>, I reimplemented it in about 50 lines of <a href=\"https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/code-callouts.typ\">slightly cursed Typst code</a>.</p>\n<p><img src=\"/assets/photos/typst-callouts.png\" alt=\"A code block with callouts in the new book\"></p>\n<h3>code-callouts.typ: a Hypermedia Systems literate experience</h3>\n<p>Before implementing code callouts, define a <em>label</em>, which we will use to mark code blocks we have already annotated. This is to prevent an infinite loop that occurs when we use this function in a <code>show</code> rule.</p>\n<pre><code class=\"language-typst\">#let processed-label = &lt;TypstCodeCallout-was-processed&gt;\n</code></pre>\n<p>The function <code>code-with-callouts</code> takes a code block and a function to render callouts. A simple default implementation returns the annotation number in square brackets in grayed-out sans-serif font.</p>\n<pre><code class=\"language-typst\">#let code-with-callouts(\n  it /*: content(raw) */,\n  callout-display: default-callout /*: (str) =&gt; content */\n) /*: content */ = { ... }\n</code></pre>\n<p>Begin by checking if the code block has already been processed.</p>\n<pre><code class=\"language-typst\">) /*: content */ = {\n  if it.at(&quot;label&quot;, default: none) == processed-label {\n    it\n  } else { ... }\n}\n</code></pre>\n<p>The dance with <code>it.at(&quot;label&quot;, default: none)</code> is a way to access the code block’s label without throwing an error if it has none. If it doesn’t have this label, the real fun begins.</p>\n<pre><code>let (callouts, text: new-text) = parse-callouts(it.text)\n</code></pre>\n<p><code>parse-callouts</code> is a function that extracts the callouts from the code block. We pass it the <code>text</code> of the code block – the plain, un-highlighted code as a string. It returns that text with the callout markers removed, and the callouts themselves in the form of an array of callout numbers for each line. This is in a way the meat of the whole operation, but it’s also pretty mundane string processing code, so I won’t dwell on it.</p>\n<pre><code class=\"language-typst\">#let callout-pat = regex(&quot;&lt;(\\\\d+)&gt;(\\\\n|$)&quot;)\n\n#let parse-callouts(\n  code-text /*: str */\n) /*: (callouts: array(array(int)), text: str) */ = {\n  let callouts /*: array(array(int)) */ = ()\n  let new-text = &quot;&quot;\n  for text-line in code-text.split(&quot;\\n&quot;) {\n    let match = text-line.match(callout-pat)\n    if match != none {\n      callouts.push((int(match.captures.at(0)),))\n      new-text += text-line.slice(0, match.start)\n    } else {\n      callouts.push(())\n      new-text += text-line\n    }\n    new-text += &quot;\\n&quot;\n  }\n  (callouts: callouts, text: new-text)\n}\n</code></pre>\n<p>Now that we have the callouts, we can render them. We do this by abusing show rules. A <code>raw.line</code> (object representing a line of raw text) knows its line number, so we can use that to look up the callouts for that line. We then iterate over the callouts and render them with the provided <code>callout-display</code> function.</p>\n<pre><code class=\"language-typst\">show raw.line: it =&gt; {\n  it\n  let callouts-of-line = callouts.at(it.number - 1, default: ())\n  for callout in callouts-of-line {\n    callout-display(callout)\n  }\n}\n</code></pre>\n<p>You might assume as I did that this would be the end of it and we could just return the code block, but there’s a catch. The code block still has the <code>&lt;1&gt;</code> markers in it, and we need to remove them. To do this, we create a new code block with the same attributes as the original, but with our <code>new-text</code> as the body. The fact that we create a brand new code block instead of returning the one we were given is what causes the infinite loop I mentioned earlier, so we apply the processed label.</p>\n<pre><code class=\"language-typst\">let fields = it.fields()\nlet _ = fields.remove(&quot;text&quot;)\nlet _ = fields.remove(&quot;lines&quot;)\nlet _ = fields.remove(&quot;theme&quot;)\n[#raw(..fields, new-text)#processed-label]\n</code></pre>\n<p>This is the code that powers the code blocks in the new edition of Hypermedia Systems. All you need to do is add a <code>show raw.where(block: true): code-with-callouts</code> to your Typst document, and you can have code blocks with callouts too!</p>\n<p>The actual text of the callouts is just a list written below the code block. If I was implementing this from scratch, I might have had the <code>code-with-callouts</code> accept the content of the callouts as well and match them up to the code by text labels, but AsciiDoc uses numbers and makes you match them up manually, so I did too.</p>\n<h2>The index</h2>\n<p>Typst has no indexing functionality by default, but its scripting and introspection capabilities are powerful enough to implement indexing at the library level. In fact, an indexing library already exists in the form of <a href=\"https://typst.app/universe/package/in-dexter\">in-dexter</a>, which is small enough that I made a <a href=\"https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/indexing.typ\">modified version</a> of it to have full control over the index’s appearance and to add support for hierarchical terms.</p>\n<p><img src=\"/assets/photos/typst-index.png\" alt=\"The index of the new book\"></p>\n<h2>The print book</h2>\n<p>So far, I’ve talked about the most technically interesting parts of the migration and not the most important part: the actual product. Using a capable typesetting system instead of Print to PDF meant I could tweak almost every aspect of the book’s design, and the software would actually cooperate.</p>\n<p>The new Hypermedia Systems uses indented paragraphs instead of block paragraphs. That is, the first line of a paragraph is indented, and there’s no space between paragraphs. What’s so special about that? You can definitely do that in HTML and CSS, right? Not if you want to do it well. Traditional style and my own preference dictate that indentations <em>separate</em> paragraphs, so the first paragraph after a non-paragraph element (like a heading or a list) should not be indented. This is a common typographic convention, but to do it in CSS is impossible in the general case without dozens, maybe hundreds of selectors to handle every special case. In Typst, you just give paragraphs a <code>first-line-indent</code>, and they know when to use it.</p>\n<p>It’s a similar story with emphasis. The <code>&lt;em&gt;</code> element in HTML is italicized by default, and similarly for <code>emph</code> in Typst. When emphasis is used in an element that’s already italicized, the emphasis is shown by reversing the italics and making the emphasized text upright. With CSS, you might restyle <code>&lt;em&gt;</code> in all those contexts (and remember to revert it as needed). <em>Container style queries</em> might make this easier in CSS. In Typst, it’s the default behavior.</p>\n<p>Other than styling features, Typst is also just better at typesetting, using line breaking algorithms similar to TeX. Especially compared to browsers with their greedy algorithms and weirdly small hyphenation dictionaries, Typst prose is a joy both to look at and to read.</p>\n<p>I want HTML and CSS to be as capable in print as they are on screens. Unfortunately, browsers don’t seem as interested – the <a href=\"https://drafts.csswg.org/css-page/#variable-auto-sizing\">CSS Paged Media module</a> remains largely unimplemented. While I would love a future where the same HTML code can back both online and print books (and PDF is relegated to an archival format), building separate web and print books from a Typst manuscript seems to be the best strategy for now.</p>\n<!-- TODO probably remove above paragraph -->\n<!-- TODO expand section -->\n<h2>The web book</h2>\n<p>Typst can produce a wonderful PDF for us, but we also need to publish the book online. While HTML output is a planned feature for the Typst compiler, no implementation currently exists. This is why I didn’t propose Typst while we were producing the first edition, before Pandoc gained support for reading and writing Typst.</p>\n<p>The old hypermedia.systems was built with the static site generator <a href=\"https://lume.land/\">Lume</a> with an AsciiDoc plugin I wrote. For the new book, I wanted to test out [Müteferrika][], an online book publishing toolkit I’m building. The advantage of Müteferrika is that it understands the structure of a book (parts, chapters, frontmatter etc.). The disadvantage is that it was and still is an unfinished mess, so I had to fix a lot of bugs to get it to work for Hypermedia Systems.</p>\n<p>The build process had to convert the Typst source to HTML, which I planned to do with Pandoc. Unfortunately, Pandoc doesn’t have full parity with the mainline Typst compiler, and spat out many errors when I first tried to convert the book. The fix for most of these was to replace direct property accesses with <code>at</code> calls and add null checks as needed.</p>\n<h2>Conclusion</h2>\n<p>The migration from AsciiDoc to Typst was a resounding success. I had to fudge a lot of tooling around book production, since Typst is still a young project and focused more on research papers than books, but the ecosystem is constantly growing and improving. <a href=\"https://myriad-dreamin.github.io/shiroa/\">Shiroa</a> is a promising project for publishing web books with Typst, for example.</p>\n<p>The web book is now live at <a href=\"https://hypermedia.systems/\">hypermedia.systems</a>. It has (as far as we could tell) everything the old web book had, and more. There’s a button at the bottom of each page where you can customize the colors of the website to your liking. For the print version, we’re switching from Amazon KDP to Lulu, which will hopefully result in more consistent printing.</p>\n<ul>\n<li><a href=\"https://hypermedia.systems/book/contents\" rel=\"noopener noreferrer\">Read the new Hypermedia Systems online</a></li>\n<li><a href=\"https://www.lulu.com/shop/deniz-ak%C5%9Fim%C5%9Fek-and-adam-stepinski-and-carson-gross/hypermedia-systems/ebook/product-jenyj66.html\">Get the EPUB for your e-reader</a></li>\n<li><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?q=hypermedia+systems&amp;page=1&amp;pageSize=4\">Buy the new print release on Lulu</a></li>\n<li><a href=\"https://www.amazon.com/Hypermedia-Systems-Carson-Gross-ebook/dp/B0CC315VJK\">Buy the Kindle release on Amazon</a></li>\n<li><a href=\"https://www.amazon.com/Hypermedia-Systems-Carson-Gross/dp/B0C9S88QV6\">Buy the old hardcover while it’s still available</a></li>\n<li><a href=\"https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5\">How to set the background color of a table cell</a></li>\n</ul>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-06-25T15:54:17Z\">\n                25 Jun ’24 15:54</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"_Clickbait title: Can you write a bestselling book in Typst?_\n\nA shiny new edition of <cite>Hypermedia Systems</cite> is out! Apart from incorporating typo and grammar fixes sent in by readers, the book was completely redesigned inside with a new layout. To achieve a much higher fidelity of design, we ported the book to Typst --- the hot new LaTeX replacement.\n\n\n## Why change what works?\n\nThe first print release of HS was written in AsciiDoc. It seemed like a natural choice at the time -- after all, many publishing houses trust AsciiDoc for their books. We learned the hard way that those publishers also have teams of engineers developing proprietary toolchains to produce their books. Vanilla AsciiDoc doesn't actually have a way to produce good print output:\n\n - `asciidoctor-pdf` uses a rudimentary PDF generation library, and the output is just about readable, but not good -- and you don't have any good tools to change that.\n\n - the venerable Pandoc can output AsciiDoc, but oddly can't read it -- so we can't compile our book to LaTeX.\n\nYour best option for producing a printable PDF, if you care at all about typesetting, is to compile to HTML, and print that to PDF. `asciidoctor-web-pdf` is a tool that wraps this workflow in a usable package, combined with Paged.js to polyfill the many features of CSS for print media that browsers don't support. This is how we produced the first edition of HS, and it was a pain for multiple reasons.\n\n - Paged.js has bugs. One bug that was nasty to work around was one where using any CSS to modify the page numbers (say, resetting them after the front matter) would cause all the pages to revert to 0. I don't know if this was fixed.\n\n - HTML with Paged.js is slow. The book has a little over 300 pages, and each page has a perceptible delay as it gets laid out. Even worse, I had to manually scroll the browser all the way to the bottom of the book before printing, or the back half of the book would be blank. Afterwards, printing would take what felt like a solid minute, though I never measured how long: Since presumably nobody at Firefox thought printing a page to PDF would be anything but instant, there's no progress bar or any other indicator of success beyond the PDF appearing in the folder where it's supposed to be.\n \n - The idiosyncrasies of this process meant that it only really worked on my machine, and I had to send PDFs back and forth with my co-authors and editor. The PDFs were absolutely massive and exceeded the Discord upload limit, so I uploaded each one to Google Drive. \n \n - Paged.js has more bugs -- asides would refuse to break across pages while code blocks were all to eager to create widows and orphans -- or disappear completely -- which I had to work around in a whack-a-mole fashion. [A few bugs seem to have made it into the final release](https://github.com/bigskysoftware/hypermedia-systems-old/issues/122).\n \n - [How to set the background color of a table cell](https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5)\n \n - AsciiDoc has facilities for indexing, but doesn't support outputting them in HTML in any way. Worse, the mechanism I could have used to do it myself was deprecated _and removed_ before the replacement was implemented. Our editor ended up creating an index **by hand**, which was appended to the PDF.\n \n - You go through all this effort only to be rewarded with very mediocre typesetting, since browsers use greedy algorithms for laying out text for performance reasons, yielding far worse line breaking compared to the dynamic programming algorithms of TeX.\n\nDespite all this, we somehow produced a usable PDF. Most readers report being satisfied with the book's design (Amazon's failure to print a PDF consistently notwithstanding). The code that generated this PDF is not open source --- initially, it was because we thought it might eat into print sales, but these days the real reason is that it might help someone else that tries to do this, and nobody should try to do this.\n\n(If you have any \"why didn't you just\" recommendations, feel free to post them in the comments on link aggregators --- they might help someone else --- but don't send them directly to me. I assure you, I've evaluated all options available even if I didn't mention them here.)\n\nSpeaking of link aggregators,\n\n\n## Typst\n\nI first stumbled upon Typst when the author's [thesis][] was posted on [the red site][]. Enthralled by its combination of quality typesetting, lightweight markup and an ergonomic scripting language tightly integrated, I knew that if I was ever part of another book project, I would use Typst.\n\nI rewrote my [CV](https://cloud.dz4k.dev/s/fqfLSo4FKibzp64) in it, as well as some documentation for an old project. This only confirmed my initial impression --- Typst is a joy to use, and the scripting language is so good I wish it was a programming language in its own right.\n\nBut I couldn't use Typst for Hypermedia Systems, because Typst didn't support HTML output. Imagine my excitement when a while ago, I realized Pandoc had added Typst support!\n\n\n## Porting the manuscript\n\nAs I mentioned before, Pandoc can't read AsciiDoc. So how can I convert the 104537 words of HS into Typst? I was ready to do it by hand, but I decided to try something else first.\n\nOne of AsciiDoc's primary output formats is DocBook XML. I don't know what it is or who uses it, but Pandoc can read it! I converted the book to DocBook XML, and then to Typst. Except AsciiDoc was buggy and generated invalid XML, so I had to fix that first. And then massage the resulting Typst a bit more. And apply any future changes to the AsciiDoc source to the Typst version.\n\nStill better than waiting for the PDF to render, though.\n\n\n## The code blocks\n\nAsciiDoc has a unique style of code blocks where you can place markers in the code that refer to explanatory notes below the block -- it looks like this:\n\n<figure><figcaption>AsciiDoc code block with callout</figcaption>\n\n```asciidoc\n[source,python]\n----\nprint(\"Hello, world!\") <1>\n----\n<1> The canonical first program in any language.\n```\n\n</figure>\n\nIf you've read Hypermedia Systems, now you know how we made those. Typst doesn't have any such feature, so with the great help of the [Typst Discord community][], I reimplemented it in about 50 lines of [slightly cursed Typst code][code-callouts.typ].\n\n\n![A code block with callouts in the new book](/assets/photos/typst-callouts.png)\n\n### code-callouts.typ: a Hypermedia Systems literate experience\n\nBefore implementing code callouts, define a _label_, which we will use to mark code blocks we have already annotated. This is to prevent an infinite loop that occurs when we use this function in a `show` rule.\n\n```typst\n#let processed-label = <TypstCodeCallout-was-processed>\n```\n\nThe function `code-with-callouts` takes a code block and a function to render callouts. A simple default implementation returns the annotation number in square brackets in grayed-out sans-serif font.\n\n```typst\n#let code-with-callouts(\n  it /*: content(raw) */,\n  callout-display: default-callout /*: (str) => content */\n) /*: content */ = { ... }\n```\n\nBegin by checking if the code block has already been processed. \n\n```typst\n) /*: content */ = {\n  if it.at(\"label\", default: none) == processed-label {\n    it\n  } else { ... }\n}\n```\n\nThe dance with `it.at(\"label\", default: none)` is a way to access the code block's label without throwing an error if it has none. If it doesn't have this label, the real fun begins.\n\n```\nlet (callouts, text: new-text) = parse-callouts(it.text)\n```\n\n`parse-callouts` is a function that extracts the callouts from the code block. We pass it the `text` of the code block -- the plain, un-highlighted code as a string. It returns that text with the callout markers removed, and the callouts themselves in the form of an array of callout numbers for each line. This is in a way the meat of the whole operation, but it's also pretty mundane string processing code, so I won't dwell on it.\n\n```typst\n#let callout-pat = regex(\"<(\\\\d+)>(\\\\n|$)\")\n\n#let parse-callouts(\n  code-text /*: str */\n) /*: (callouts: array(array(int)), text: str) */ = {\n  let callouts /*: array(array(int)) */ = ()\n  let new-text = \"\"\n  for text-line in code-text.split(\"\\n\") {\n    let match = text-line.match(callout-pat)\n    if match != none {\n      callouts.push((int(match.captures.at(0)),))\n      new-text += text-line.slice(0, match.start)\n    } else {\n      callouts.push(())\n      new-text += text-line\n    }\n    new-text += \"\\n\"\n  }\n  (callouts: callouts, text: new-text)\n}\n```\n\nNow that we have the callouts, we can render them. We do this by abusing show rules. A `raw.line` (object representing a line of raw text) knows its line number, so we can use that to look up the callouts for that line. We then iterate over the callouts and render them with the provided `callout-display` function.\n\n```typst\nshow raw.line: it => {\n  it\n  let callouts-of-line = callouts.at(it.number - 1, default: ())\n  for callout in callouts-of-line {\n    callout-display(callout)\n  }\n}\n```\n\nYou might assume as I did that this would be the end of it and we could just return the code block, but there's a catch. The code block still has the `<1>` markers in it, and we need to remove them. To do this, we create a new code block with the same attributes as the original, but with our `new-text` as the body. The fact that we create a brand new code block instead of returning the one we were given is what causes the infinite loop I mentioned earlier, so we apply the processed label.\n\n```typst\nlet fields = it.fields()\nlet _ = fields.remove(\"text\")\nlet _ = fields.remove(\"lines\")\nlet _ = fields.remove(\"theme\")\n[#raw(..fields, new-text)#processed-label]\n```\n\nThis is the code that powers the code blocks in the new edition of Hypermedia Systems. All you need to do is add a `show raw.where(block: true): code-with-callouts` to your Typst document, and you can have code blocks with callouts too!\n\nThe actual text of the callouts is just a list written below the code block. If I was implementing this from scratch, I might have had the `code-with-callouts` accept the content of the callouts as well and match them up to the code by text labels, but AsciiDoc uses numbers and makes you match them up manually, so I did too.\n\n\n## The index\n\nTypst has no indexing functionality by default, but its scripting and introspection capabilities are powerful enough to implement indexing at the library level. In fact, an indexing library already exists in the form of [in-dexter][], which is small enough that I made a [modified version][indexing.typ] of it to have full control over the index's appearance and to add support for hierarchical terms.\n\n![The index of the new book](/assets/photos/typst-index.png)\n\n\n## The print book\n\nSo far, I've talked about the most technically interesting parts of the migration and not the most important part: the actual product. Using a capable typesetting system instead of Print to PDF meant I could tweak almost every aspect of the book's design, and the software would actually cooperate.\n\nThe new Hypermedia Systems uses indented paragraphs instead of block paragraphs. That is, the first line of a paragraph is indented, and there's no space between paragraphs. What's so special about that? You can definitely do that in HTML and CSS, right? Not if you want to do it well. Traditional style and my own preference dictate that indentations _separate_ paragraphs, so the first paragraph after a non-paragraph element (like a heading or a list) should not be indented. This is a common typographic convention, but to do it in CSS is impossible in the general case without dozens, maybe hundreds of selectors to handle every special case. In Typst, you just give paragraphs a `first-line-indent`, and they know when to use it.\n\nIt's a similar story with emphasis. The `<em>` element in HTML is italicized by default, and similarly for `emph` in Typst. When emphasis is used in an element that's already italicized, the emphasis is shown by reversing the italics and making the emphasized text upright. With CSS, you might restyle `<em>` in all those contexts (and remember to revert it as needed). _Container style queries_ might make this easier in CSS. In Typst, it's the default behavior.\n\nOther than styling features, Typst is also just better at typesetting, using line breaking algorithms similar to TeX. Especially compared to browsers with their greedy algorithms and weirdly small hyphenation dictionaries, Typst prose is a joy both to look at and to read.\n\nI want HTML and CSS to be as capable in print as they are on screens. Unfortunately, browsers don't seem as interested -- the [CSS Paged Media module][] remains largely unimplemented. While I would love a future where the same HTML code can back both online and print books (and PDF is relegated to an archival format), building separate web and print books from a Typst manuscript seems to be the best strategy for now.\n\n<!-- TODO probably remove above paragraph -->\n<!-- TODO expand section -->\n\n\n## The web book\n\nTypst can produce a wonderful PDF for us, but we also need to publish the book online. While HTML output is a planned feature for the Typst compiler, no implementation currently exists. This is why I didn't propose Typst while we were producing the first edition, before Pandoc gained support for reading and writing Typst.\n\nThe old hypermedia.systems was built with the static site generator [Lume][] with an AsciiDoc plugin I wrote. For the new book, I wanted to test out [Müteferrika][], an online book publishing toolkit I'm building. The advantage of Müteferrika is that it understands the structure of a book (parts, chapters, frontmatter etc.). The disadvantage is that it was and still is an unfinished mess, so I had to fix a lot of bugs to get it to work for Hypermedia Systems.\n\nThe build process had to convert the Typst source to HTML, which I planned to do with Pandoc. Unfortunately, Pandoc doesn't have full parity with the mainline Typst compiler, and spat out many errors when I first tried to convert the book. The fix for most of these was to replace direct property accesses with `at` calls and add null checks as needed.\n\n\n## Conclusion\n\nThe migration from AsciiDoc to Typst was a resounding success. I had to fudge a lot of tooling around book production, since Typst is still a young project and focused more on research papers than books, but the ecosystem is constantly growing and improving. [Shiroa][] is a promising project for publishing web books with Typst, for example.\n\nThe web book is now live at [hypermedia.systems](https://hypermedia.systems). It has (as far as we could tell) everything the old web book had, and more. There's a button at the bottom of each page where you can customize the colors of the website to your liking. For the print version, we're switching from Amazon KDP to Lulu, which will hopefully result in more consistent printing.\n\n- [Read the new Hypermedia Systems online](https://hypermedia.systems/book/contents){rel=\"noopener noreferrer\"}\n- [Get the EPUB for your e-reader](https://www.lulu.com/shop/deniz-ak%C5%9Fim%C5%9Fek-and-adam-stepinski-and-carson-gross/hypermedia-systems/ebook/product-jenyj66.html)\n- [Buy the new print release on Lulu](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?q=hypermedia+systems&page=1&pageSize=4)\n- [Buy the Kindle release on Amazon](https://www.amazon.com/Hypermedia-Systems-Carson-Gross-ebook/dp/B0CC315VJK)\n- [Buy the old hardcover while it's still available](https://www.amazon.com/Hypermedia-Systems-Carson-Gross/dp/B0C9S88QV6)\n- [How to set the background color of a table cell](https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5)\n\n\n[thesis]: https://laurmaedje.github.io/programmable-markup-language-for-typesetting.pdf\n[the red site]: https://lobste.rs/s/zd8b6p/typst_programmable_markup_language_for\n[Typst Discord community]: https://discord.gg/2uDybryKPe\n[code-callouts.typ]: https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/code-callouts.typ\n[in-dexter]: https://typst.app/universe/package/in-dexter\n[indexing.typ]: https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/indexing.typ\n[Lume]: https://lume.land\n[Shiroa]: https://myriad-dreamin.github.io/shiroa/\n[CSS Paged Media module]: https://drafts.csswg.org/css-page/#variable-auto-sizing\n","url":"https://denizaksimsek.com/2024/new-hypermedia-systems/","date_published":"2024-06-25T15:54:17Z"},{"id":"/2024/nothing-kwgt/","title":"Making Kustom widgets that fit in with Nothing OS","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Making Kustom widgets that fit in with Nothing OS</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Making Kustom widgets that fit in with Nothing OS\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/nothing-kwgt/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/Screenshot_20240424-121527.png\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Making Kustom widgets that fit in with Nothing OS</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Making Kustom widgets that fit in with Nothing OS</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I’ve been using a Nothing Phone (2a) for a while now. I love the built in widgets, but there’s not enough of them, and most third-party widgets stick out like a sore thumb. Not even any of the Nothing-themed KWGT packs on the Play Store match.</p>\n<p>So I tried my own hand at it, and with not much effort, I was able to make these two:</p>\n<p><img src=\"/assets/photos/Screenshot_20240424-121527.png\" alt=\"A Nothing Phone home screen with weather, date, next event and pedometer widgets\"></p>\n<p>To my eyes, that’s an exact match! The date and next event widgets are mine.</p>\n<p>If you just want to have these widgets, you can download them. If you want to make your own, read on:</p>\n<ul>\n<li>\n<p><a href=\"https://cloud.dz4k.dev/s/y5aDJyPWwYiG2tb\">VeryFewThings Date</a></p>\n</li>\n<li>\n<p><a href=\"https://cloud.dz4k.dev/s/Zsa8Zq2wCaeH5wH\">VeryFewThings Next Event</a></p>\n</li>\n</ul>\n<p>(this is my first time using Nextcloud’s sharing links, so let me know if there’s any issues)</p>\n<p>I’ll probably be updating these widgets and this guide, as well as making more widgets. In particular, some of the formulas in the next event widget are a bit messy.</p>\n<h2>Background</h2>\n<p>The background of the widget is a Rectangle. Use formulas for the width, height and paint color (long press each property, and tap the calculator icon). The formulas are:</p>\n<ul>\n<li><strong>Width</strong>: <code>$si(rwidth)$</code></li>\n<li><strong>Height</strong>: <code>$si(rheight)$</code></li>\n<li><strong>Paint</strong>: <code>$si(syscn1, if(si(darkmode), 10, 93)$</code></li>\n</ul>\n<p>I used a corner radius of 32, though that’s my eyeball measurement and might not be exact. For 2x1 and 1x2 widgets, you can use an absurd number like 9999 to make them pill-shaped.</p>\n<h2>Layout</h2>\n<p>The widget should have 32 points of padding.</p>\n<p>Some Nothing widgets like the digital clock change based on their size. My date widget does the same thing. You can use these formulas to detect if the widget is wide or tall:</p>\n<ul>\n<li><strong>Wide</strong>: <code>$si(rwidth) &gt; 1.8 * si(rheight)$</code></li>\n<li><strong>Tall</strong>: <code>$si(rheight) &gt; 1.8 * si(rwidth)$</code></li>\n</ul>\n<p>If neither of these are true, the widget is square.</p>\n<h2>Text</h2>\n<p>Kustom can access your device’s system fonts, including Nothing’s custom fonts.</p>\n<p>Dotted: NDot (Variant: 57 for big text, 55 for small text)\nMonospace: Lettera Mono\nSerif: Ntype82\nSans-serif: Roboto (weight 300)</p>\n<p>Nothing uses the NDot font exclusively in all caps, so I recommend matching that.</p>\n<ul>\n<li><strong>Text color</strong>: <code>$si(syscn1, if(si(darkmode), 100, 0))</code></li>\n</ul>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-04-24T09:39:39Z\">\n                24 Apr ’24 09:39</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"I've been using a Nothing Phone (2a) for a while now. I love the built in widgets, but there's not enough of them, and most third-party widgets stick out like a sore thumb. Not even any of the Nothing-themed KWGT packs on the Play Store match.\n\nSo I tried my own hand at it, and with not much effort, I was able to make these two:\n\n![A Nothing Phone home screen with weather, date, next event and pedometer widgets](/assets/photos/Screenshot_20240424-121527.png)\n\nTo my eyes, that's an exact match! The date and next event widgets are mine.\n\nIf you just want to have these widgets, you can download them. If you want to make your own, read on:\n\n- [VeryFewThings Date](https://cloud.dz4k.dev/s/y5aDJyPWwYiG2tb)\n\n- [VeryFewThings Next Event](https://cloud.dz4k.dev/s/Zsa8Zq2wCaeH5wH)\n\n(this is my first time using Nextcloud's sharing links, so let me know if there's any issues)\n\nI'll probably be updating these widgets and this guide, as well as making more widgets. In particular, some of the formulas in the next event widget are a bit messy.\n\n\n## Background\n\nThe background of the widget is a Rectangle. Use formulas for the width, height and paint color (long press each property, and tap the calculator icon). The formulas are:\n\n- **Width**: `$si(rwidth)$`\n- **Height**: `$si(rheight)$`\n- **Paint**: `$si(syscn1, if(si(darkmode), 10, 93)$`\n\nI used a corner radius of 32, though that's my eyeball measurement and might not be exact. For 2x1 and 1x2 widgets, you can use an absurd number like 9999 to make them pill-shaped.\n\n\n## Layout\n\nThe widget should have 32 points of padding.\n\nSome Nothing widgets like the digital clock change based on their size. My date widget does the same thing. You can use these formulas to detect if the widget is wide or tall:\n\n- **Wide**: `$si(rwidth) > 1.8 * si(rheight)$`\n- **Tall**: `$si(rheight) > 1.8 * si(rwidth)$`\n\nIf neither of these are true, the widget is square.\n\n\n## Text\n\nKustom can access your device's system fonts, including Nothing's custom fonts.\n\nDotted: NDot (Variant: 57 for big text, 55 for small text)\nMonospace: Lettera Mono\nSerif: Ntype82\nSans-serif: Roboto (weight 300)\n\nNothing uses the NDot font exclusively in all caps, so I recommend matching that.\n\n- **Text color**: `$si(syscn1, if(si(darkmode), 100, 0))`\n","url":"https://denizaksimsek.com/2024/nothing-kwgt/","image":"https://denizaksimsek.com/assets/photos/Screenshot_20240424-121527.png","date_published":"2024-04-24T09:39:39Z"},{"id":"/2024/biber-s-day-out/","title":"Biber's day out","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Biber&#39;s day out</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Biber&#39;s day out\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/biber-s-day-out/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG_20240414_185856436.jpg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Biber&#39;s day out</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Biber&#39;s day out</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>We went on a walk in the woods today, and Biber came with us.</p>\n<p>We came across a cat that looks just like her.</p>\n<p><img src=\"/assets/photos/IMG_20240414_185845521.jpg\" alt=\"an orange tabby looking up at you\"></p>\n<p>He was a male cat. Biber soon caught up and they started to interact:</p>\n<p><img src=\"/assets/photos/IMG_20240414_185856436.jpg\" alt=\"biber and other cat stood on staring at each other\"></p>\n<p>I’m not an animal behaviorist and I couldn’t tell you if they were flirting or getting ready to fight.</p>\n<p><img src=\"/assets/photos/IMG_20240414_190258381.jpg\" alt=\"the two cats started walking in a circle still not taking their eyes off each other\"></p>\n<p><img src=\"/assets/photos/IMG_20240414_190259914.jpg\" alt=\"biber laid down on the floor at one point\"></p>\n<p><img src=\"/assets/photos/IMG_20240414_185953179.jpg\" alt=\"the other cat sitting, biber still laying down\"></p>\n<p><img src=\"/assets/photos/IMG_20240414_190052369.jpg\" alt=\"sometimes they would get further away, like 2-3 meters and keep tracking each other\"></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-04-14T17:14:47Z\">\n                14 Apr ’24 17:14</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"We went on a walk in the woods today, and Biber came with us.\n\nWe came across a cat that looks just like her.\n\n![an orange tabby looking up at you](/assets/photos/IMG_20240414_185845521.jpg)\n\nHe was a male cat. Biber soon caught up and they started to interact:\n\n![biber and other cat stood on staring at each other](/assets/photos/IMG_20240414_185856436.jpg)\n\nI'm not an animal behaviorist and I couldn't tell you if they were flirting or getting ready to fight.\n\n![the two cats started walking in a circle still not taking their eyes off each other](/assets/photos/IMG_20240414_190258381.jpg)\n\n![biber laid down on the floor at one point](/assets/photos/IMG_20240414_190259914.jpg)\n\n![the other cat sitting, biber still laying down](/assets/photos/IMG_20240414_185953179.jpg)\n\n![sometimes they would get further away, like 2-3 meters and keep tracking each other](/assets/photos/IMG_20240414_190052369.jpg)\n","url":"https://denizaksimsek.com/2024/biber-s-day-out/","image":"https://denizaksimsek.com/assets/photos/IMG_20240414_185856436.jpg","date_published":"2024-04-14T17:14:47Z"},{"id":"/2024/kill-all-distros/","title":"kill all distros","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>kill all distros</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"kill all distros\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/kill-all-distros/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>kill all distros</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">kill all distros</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>so insane to me that people advocate for distro packaging over flatpak</p>\n<p>hmm yes i love being unable to use up-and-coming software thet nobody packages</p>\n<p>yet and making it harder for new distros to get off the ground</p>\n<p>vertical monopolies are good when open source i guess</p>\n<p>the concept of software package management existing is a failure</p>\n<p>one (1) software should be expressible as one thing</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-03-22T00:57:02Z\">\n                22 Mar ’24 00:57</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"so insane to me that people advocate for distro packaging over flatpak\n\nhmm yes i love being unable to use up-and-coming software thet nobody packages \n\nyet and making it harder for new distros to get off the ground \n\nvertical monopolies are good when open source i guess\n\nthe concept of software package management existing is a failure\n\none (1) software should be expressible as one thing\n","url":"https://denizaksimsek.com/2024/kill-all-distros/","date_published":"2024-03-22T00:57:02Z"},{"id":"/2024/bamboo-paper/","title":"Why Bamboo Paper is my favorite note taking app that I never use","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Why Bamboo Paper is my favorite note taking app that I never use</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Why Bamboo Paper is my favorite note taking app that I never use\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2024/bamboo-paper/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Why Bamboo Paper is my favorite note taking app that I never use</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Why Bamboo Paper is my favorite note taking app that I never use</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><a href=\"https://www.wacom.com/en-us/products/apps-services/bamboo-paper\">Bamboo Paper</a> is an app released by Wacom in 2014, presumably to promote their Bamboo series of capacitive pens. It features 6 pens, 3 of which need to be unlocked via in-app purchases. Notes are stored in the proprietary WILL format, and can only be synced to Wacom’s own Inkspace cloud service. Otherwise, backing up notes involves exporting each notebook manually to PDF.</p>\n<p>Due to these restrictions, I don’t use it that often to actually take notes. However, I can’t think of a single note taking app that I enjoy as much.</p>\n<p>It’s not because of the minimalist interface, or the satisfying action of the pressure-sensitive brushes and pens, though I love both of those things. Only recently have I realized what draws me to this app.</p>\n<p>Notebooks in Bamboo Paper have the same size as your device’s display. When you rotate the device, the UI rotates, but the notebook itself remains in the same orientation relative to the device. Zooming in is possible, but unwieldy, and you can’t zoom out beyond 100%.</p>\n<p>All these limitations mean that writing on Bamboo Paper feels like writing on a notebook, whereas using any other app feels like playing a game where your character writes in a notebook. When penstrokes are bound to the specific location on the tablet’s front glass where I put the pen, it makes spatial reasoning more natural, and spatial reasoning is the main advantage of taking handwritten notes over textual notes using handwriting recognition.</p>\n<p>Using Bamboo Paper with floating windows of other apps in front of it, as Samsung lets me do, is the closest feeling I’ll probably get to using a Microsoft Courier – at least, it would be if Paper had drag-and-drop support. It might be a 10 year old app but it did get an update last August, so come on Wacom…</p>\n<p>Despite all my searching, I haven’t found another app on Android that imitates Paper’s model. Some apps like Samsung Notes zoom and scroll like Word documents, while other apps have an infinite canvas that makes me feel disoriented. Since I want to store my notes somewhere that’s not too likely to disappear next year, I put up with the Word doc, but I’m putting this out there in case someone points me to the perfect virtual notebook</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2024-01-20T16:44:00Z\">\n                20 Jan ’24 16:44</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"[Bamboo Paper] is an app released by Wacom in 2014, presumably to promote their Bamboo series of capacitive pens. It features 6 pens, 3 of which need to be unlocked via in-app purchases. Notes are stored in the proprietary WILL format, and can only be synced to Wacom's own Inkspace cloud service. Otherwise, backing up notes involves exporting each notebook manually to PDF.\n\n[Bamboo Paper]: https://www.wacom.com/en-us/products/apps-services/bamboo-paper\n\nDue to these restrictions, I don't use it that often to actually take notes. However, I can't think of a single note taking app that I enjoy as much.\n\nIt's not because of the minimalist interface, or the satisfying action of the pressure-sensitive brushes and pens, though I love both of those things. Only recently have I realized what draws me to this app.\n\nNotebooks in Bamboo Paper have the same size as your device's display. When you rotate the device, the UI rotates, but the notebook itself remains in the same orientation relative to the device. Zooming in is possible, but unwieldy, and you can't zoom out beyond 100%.\n\nAll these limitations mean that writing on Bamboo Paper feels like writing on a notebook, whereas using any other app feels like playing a game where your character writes in a notebook. When penstrokes are bound to the specific location on the tablet's front glass where I put the pen, it makes spatial reasoning more natural, and spatial reasoning is the main advantage of taking handwritten notes over textual notes using handwriting recognition.\n\nUsing Bamboo Paper with floating windows of other apps in front of it, as Samsung lets me do, is the closest feeling I'll probably get to using a Microsoft Courier -- at least, it would be if Paper had drag-and-drop support. It might be a 10 year old app but it did get an update last August, so come on Wacom...\n\nDespite all my searching, I haven't found another app on Android that imitates Paper's model. Some apps like Samsung Notes zoom and scroll like Word documents, while other apps have an infinite canvas that makes me feel disoriented. Since I want to store my notes somewhere that's not too likely to disappear next year, I put up with the Word doc, but I'm putting this out there in case someone points me to the perfect virtual notebook \n","url":"https://denizaksimsek.com/2024/bamboo-paper/","date_published":"2024-01-20T16:44:00Z"},{"id":"/2023/starting-denizen/","title":"Introducing Denizen","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Introducing Denizen</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Introducing Denizen\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2023/starting-denizen/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Introducing Denizen</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Introducing Denizen</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I’m building Denizen (<a href=\"https://denizen.dz4k.com/\">https://denizen.dz4k.com/</a>), a personal website system that is IndieWeb and Fediverse compatible.</p>\n<p>Source code: <a href=\"https://codeberg.org/dz4k/denizen\">https://codeberg.org/dz4k/denizen</a></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2023-10-27T13:40:38Z\">\n                27 Oct ’23 13:40</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://indieweb.social/@dz4k/111303261040511536\">\n                            Mastodon</a>,                                             <a rel=\"syndication\" href=\"https://twitter.com/DenizAksimsek/status/1717651412102836490\">\n                            Twitter</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"I'm building Denizen (<https://denizen.dz4k.com/>), a personal website system that is IndieWeb and Fediverse compatible.\n\nSource code: <https://codeberg.org/dz4k/denizen>\n","url":"https://denizaksimsek.com/2023/starting-denizen/","date_published":"2023-10-27T13:40:38Z"},{"id":"/2023/xpath/","title":"Using XPath in 2023","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Using XPath in 2023</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Using XPath in 2023\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2023/xpath/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Using XPath in 2023</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Using XPath in 2023</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>In the latest release of <a href=\"https://htmx.org/\">htmx</a>,\nyou can add event listeners to elements with <code>hx-on</code>:</p>\n<pre><code class=\"language-html\">&amp;lt;form hx-on::beforeRequest=&quot;addCsrfToken(event);&quot;&gt;\n</code></pre>\n<p>For all the other <code>hx-</code> attributes, we use CSS attribute selectors.\nHowever, with <code>hx-on</code>, the attribute name is not fixed as it contains the event.\nCSS attribute selectors support wildcards on the <em>value</em> of attributes,\nbut not the name:</p>\n<pre><code class=\"language-css\">[hx-trigger] /* common and normal */\n[href^=&quot;https://dz4k.com&quot;] /* &quot;starts with&quot; operator */\n[^ĥx-on:] /* not a thing */\n</code></pre>\n<h2><ruby>X<rt>XML</rt>Path<rt>Path Language</ruby></h2>\n<p>XPath is a query language for extracting information from XML(-like) documents.\nIts main use cases are XSLT and parsing API responses.</p>\n<p>The XPath language is significantly more expressive than CSS,\nmaking it possible to traverse the XML tree in any direction,\nfilter nodes based on arbitrary predicates,\nand select any kind of node\n(including comments, text nodes, and individual attributes).\nOur non-existent CSS attribute could be written as follows:</p>\n<pre><code class=\"language-xpath\">//@*[starts-with(name(), &quot;hx-on:&quot;)]\n</code></pre>\n<p>This post is not supposed to be an XPath tutorial, but I’ll break this one down:</p>\n<dl>\n<dt><code>//</code></dt>\n<dd>traverse the document (in CSS, this is the default)</dd>\n<dt><code>@*</code></dt>\n<dd>find any attribute (mnemonic: <strong>at</strong>-tribute)</dd>\n<dt><code>[ ... ]</code></dt>\n<dd>where…</dd>\n<dt><code>starts-with(name(), &quot;hx-on:&quot;)</code></dt>\n<dd>its name starts with <code>&quot;hx-on:&quot;</code></dd>\n</dl>\n<p>CSS selectors don’t have these kinds of features,\nand it has good reasons not to.\nCSS has strict performance requirements\n– to the point that “CSS optimization” is generally not a thing –\nand selectors that offer more control could make slow selectors possible.\nIn addition, CSS has well-defined specificity rules, whereas XPath does not.</p>\n<p>However, while these features make CSS great for stylesheets,\nCSS selectors are also the most common way to find DOM elements in\nJavaScript code and lacking in that regard.\nMany libraries which extend HTML do so by traversing the entire document\nand finding elements manually.\nThis is often not needed since, if you didn’t know,\n<strong>XPath is built into browsers.</strong></p>\n<h2>document.evaluate</h2>\n<p>The <a href=\"https://developer.mozilla.org/en-US/Web/XPath/Introduction_to_using_XPath_in_JavaScript\"><code>document.evaluate</code> API</a> is somewhat archaic,\npartly because it was designed for talking to XML APIs over <code>XMLHTTPRequest</code>.\nHere’s a DOM-friendly wrapper:</p>\n<pre><code class=\"language-ts\">function* xpath(...args) {\n  let path, root = document;\n  if (args.length &gt; 1) [root, path] = args;\n  else [path] = args;\n\n  const nodeIterator = document.evaluate(\n    path,\n    root,\n    null,\n    XPathResult.UNORDERED_NODE_ITERATOR_TYPE,\n    null,\n  );\n\n  for (\n    let node = nodeIterator.iterateNext();\n    node != null;\n    node = nodeIterator.iterateNext()\n  ) {\n    yield node;\n  }\n}\n\n// TypeScript declaration\nfunction xpath(path: string): Iterable&lt;Node&gt;;\nfunction xpath(root: Element, path: string): Iterable&lt;Node&gt;;\n</code></pre>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2023-07-16T21:10:00Z\">\n                16 Jul ’23 21:10</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"In the latest release of [htmx],\nyou can add event listeners to elements with `hx-on`:\n\n```html\n&lt;form hx-on::beforeRequest=\"addCsrfToken(event);\">\n```\n\nFor all the other `hx-` attributes, we use CSS attribute selectors.\nHowever, with `hx-on`, the attribute name is not fixed as it contains the event.\nCSS attribute selectors support wildcards on the _value_ of attributes,\nbut not the name:\n\n```css\n[hx-trigger] /* common and normal */\n[href^=\"https://dz4k.com\"] /* \"starts with\" operator */\n[^ĥx-on:] /* not a thing */\n```\n\n## <ruby>X<rt>XML</rt>Path<rt>Path Language</ruby>\n\nXPath is a query language for extracting information from XML(-like) documents.\nIts main use cases are XSLT and parsing API responses.\n\nThe XPath language is significantly more expressive than CSS,\nmaking it possible to traverse the XML tree in any direction,\nfilter nodes based on arbitrary predicates,\nand select any kind of node\n(including comments, text nodes, and individual attributes).\nOur non-existent CSS attribute could be written as follows:\n\n```xpath\n//@*[starts-with(name(), \"hx-on:\")]\n```\n\nThis post is not supposed to be an XPath tutorial, but I'll break this one down:\n\n\n`//`\n: traverse the document (in CSS, this is the default)\n\n`@*`\n: find any attribute (mnemonic: **at**-tribute)\n\n`[ ... ]`\n: where...\n\n`starts-with(name(), \"hx-on:\")`\n: its name starts with `\"hx-on:\"`\n\n\nCSS selectors don't have these kinds of features,\nand it has good reasons not to.\nCSS has strict performance requirements\n-- to the point that \"CSS optimization\" is generally not a thing --\nand selectors that offer more control could make slow selectors possible.\nIn addition, CSS has well-defined specificity rules, whereas XPath does not.\n\nHowever, while these features make CSS great for stylesheets,\nCSS selectors are also the most common way to find DOM elements in\nJavaScript code and lacking in that regard.\nMany libraries which extend HTML do so by traversing the entire document\nand finding elements manually.\nThis is often not needed since, if you didn't know,\n**XPath is built into browsers.**\n\n## document.evaluate\n\nThe [`document.evaluate` API] is somewhat archaic,\npartly because it was designed for talking to XML APIs over `XMLHTTPRequest`.\nHere's a DOM-friendly wrapper:\n\n```ts\nfunction* xpath(...args) {\n  let path, root = document;\n  if (args.length > 1) [root, path] = args;\n  else [path] = args;\n\n  const nodeIterator = document.evaluate(\n    path,\n    root,\n    null,\n    XPathResult.UNORDERED_NODE_ITERATOR_TYPE,\n    null,\n  );\n\n  for (\n    let node = nodeIterator.iterateNext();\n    node != null;\n    node = nodeIterator.iterateNext()\n  ) {\n    yield node;\n  }\n}\n\n// TypeScript declaration\nfunction xpath(path: string): Iterable<Node>;\nfunction xpath(root: Element, path: string): Iterable<Node>;\n```\n\n[htmx]: https://htmx.org\n[`document.evaluate` API]: https://developer.mozilla.org/en-US/Web/XPath/Introduction_to_using_XPath_in_JavaScript\n","url":"https://denizaksimsek.com/2023/xpath/","date_published":"2023-07-16T21:10:00Z"},{"id":"/2023/programming-on-a-tablet/","title":"Programming on an Android tablet","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Programming on an Android tablet</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Programming on an Android tablet\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2023/programming-on-a-tablet/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Programming on an Android tablet</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Programming on an Android tablet</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I recently purchased a Samsung Galaxy Tab S7 FE.\nI wasn’t planning on making it a laptop replacement,\nbut I had some experience with Android’s capabilities for development\n(from when I only had a phone)\nand the possibility was irresistible.</p>\n<p>SSH into my laptop was a no-go.\nI have no idea how to expose my laptop to the internet in a way that works,\nlet alone is safe.\nBesides, my model didn’t have mobile data support for on-the-go.\nI wanted to run a Linux dev environment on the tablet itself.</p>\n<p><img src=\"/assets/photos/tablet-programming.jpg\" alt=\"The tablet, in a magnetic stand case, with a keyboard in front of it, on a desk\"></p>\n<h2>Termux</h2>\n<blockquote>\n<p>Termux is an Android terminal emulator and Linux environment app\nthat works directly with no rooting or setup required.\nA minimal base system is installed automatically -\nadditional packages are available using the APT package manager.</p>\n</blockquote>\n<p>– <a href=\"https://termux.dev/\">https://termux.dev</a></p>\n<p>Termux is not a VM, container or emulator –\nit’s a terminal in the host system.\nBecause the places where a Linux distro would usually install software\nare write-only on an Android device,\nTermux places software and your home directory inside its data directory\n(<code>TODO</code>)\nand uses the <code>$PREFIX</code> environment variable.</p>\n<p>In my experience, this works until it doesn’t.\nSoftware in the Termux repositories works as expected,\nbut anything that needs to be built locally or piped to <code>sh</code> from an URL\nwill likely be confused by the odd directory structure.</p>\n<p>In my phone-only days, this worked fine.\nHowever, I have projects now that depend on software not packaged for Termux.</p>\n<p>Can’t I just have a container or something?</p>\n<h2>PRoot-Distro</h2>\n<blockquote>\n<p>PRoot is a user-space implementation of chroot, mount --bind, and binfmt_misc.</p>\n</blockquote>\n<p>I’m not super clear on the details,\nbut it seems that Android doesn’t let you do most of the things you need for a\ncontainer runtime.\nPRoot is a solution to that.</p>\n<p>The Termux project maintains a fork of PRoot as well as a wrapper program\ncalled PRoot-Distro.\nPRoot-Distro has an interface that will be familiar to anyone who has used\nDistrobox or toolbx:</p>\n<pre><code>proot-distro create fedora\nproot-distro login fedora\n</code></pre>\n<p>This drops you into a fully-functional Fedora shell.\nAfter my previous failed attempts, I was surprised to see how well it works:</p>\n<ul>\n<li>You can access the network\n(this didn’t work with another PRoot solution I tried –\nsomething to do with Android not allowing raw DNS traffic?)</li>\n<li>You can start a server on the container and access it from the host</li>\n<li>You can access the host Android filesystem\n(not the Termux home, the one that has your pictures and\nWhatsApp voice messages) from the container through the <code>/storage</code> directory</li>\n</ul>\n<p>One thing to note is you are still on an ARM device.\nSoftware that is not compiled for ARM will not work.\nHowever, I only hit this once while installing Deno –\nand even then, ARM builds are available through a fork.</p>\n<h2>Summary</h2>\n<ul>\n<li>\n<p>Install Termux</p>\n</li>\n<li>\n<p>Install PRoot-Distro</p>\n<pre><code>pkg install proot-distro\n</code></pre>\n</li>\n<li>\n<p>Set up a container</p>\n<pre><code>proot-distro create fedora\n</code></pre>\n</li>\n<li>\n<p>Enter your container</p>\n<pre><code>proot-distro login fedora\n</code></pre>\n</li>\n</ul>\n<h2>Hardware</h2>\n<ul>\n<li>Samsung Galaxy Tab S7 FE SM-T733</li>\n<li>Ztotop Magnetic Case</li>\n<li>Logitech K380 Bluetooth Keyboard</li>\n</ul>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2023-07-01T22:53:00Z\">\n                01 Jul ’23 22:53</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"I recently purchased a Samsung Galaxy Tab S7 FE.\nI wasn't planning on making it a laptop replacement,\nbut I had some experience with Android's capabilities for development\n(from when I only had a phone)\nand the possibility was irresistible.\n\nSSH into my laptop was a no-go.\nI have no idea how to expose my laptop to the internet in a way that works,\nlet alone is safe.\nBesides, my model didn't have mobile data support for on-the-go.\nI wanted to run a Linux dev environment on the tablet itself.\n\n![The tablet, in a magnetic stand case, with a keyboard in front of it, on a desk](/assets/photos/tablet-programming.jpg)\n\n## Termux\n\n> Termux is an Android terminal emulator and Linux environment app\n> that works directly with no rooting or setup required.\n> A minimal base system is installed automatically -\n> additional packages are available using the APT package manager.\n\n-- <https://termux.dev>\n\nTermux is not a VM, container or emulator --\nit's a terminal in the host system.\nBecause the places where a Linux distro would usually install software\nare write-only on an Android device,\nTermux places software and your home directory inside its data directory\n(`TODO`)\nand uses the `$PREFIX` environment variable.\n\nIn my experience, this works until it doesn't.\nSoftware in the Termux repositories works as expected,\nbut anything that needs to be built locally or piped to `sh` from an URL\nwill likely be confused by the odd directory structure.\n\nIn my phone-only days, this worked fine.\nHowever, I have projects now that depend on software not packaged for Termux.\n\nCan't I just have a container or something?\n\n## PRoot-Distro\n\n> PRoot is a user-space implementation of chroot, mount --bind, and binfmt_misc.\n\nI'm not super clear on the details,\nbut it seems that Android doesn't let you do most of the things you need for a\ncontainer runtime.\nPRoot is a solution to that.\n\nThe Termux project maintains a fork of PRoot as well as a wrapper program\ncalled PRoot-Distro.\nPRoot-Distro has an interface that will be familiar to anyone who has used\nDistrobox or toolbx:\n\n```\nproot-distro create fedora\nproot-distro login fedora\n```\n\nThis drops you into a fully-functional Fedora shell.\nAfter my previous failed attempts, I was surprised to see how well it works:\n\n - You can access the network\n   (this didn't work with another PRoot solution I tried --\n   something to do with Android not allowing raw DNS traffic?)\n - You can start a server on the container and access it from the host\n - You can access the host Android filesystem\n   (not the Termux home, the one that has your pictures and\n   WhatsApp voice messages) from the container through the `/storage` directory\n\nOne thing to note is you are still on an ARM device.\nSoftware that is not compiled for ARM will not work.\nHowever, I only hit this once while installing Deno --\nand even then, ARM builds are available through a fork.\n\n## Summary\n\n- Install Termux\n\n- Install PRoot-Distro\n  ```\n  pkg install proot-distro\n  ```\n\n- Set up a container\n  ```\n  proot-distro create fedora\n  ```\n\n- Enter your container\n  ```\n  proot-distro login fedora\n  ```\n\n## Hardware\n\n- Samsung Galaxy Tab S7 FE SM-T733\n- Ztotop Magnetic Case\n- Logitech K380 Bluetooth Keyboard\n","url":"https://denizaksimsek.com/2023/programming-on-a-tablet/","date_published":"2023-07-01T22:53:00Z"},{"id":"/2023/you-dont-need-modal/","title":"You don't need a modal window","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>You don&#39;t need a modal window</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"You don&#39;t need a modal window\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2023/you-dont-need-modal/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>You don&#39;t need a modal window</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">You don&#39;t need a modal window</h1>\n                \n\n                                    \n<article class=\"h-entry box crowded hover-state\" onclick=\"this.querySelector('a').click()\" lang=\"undefined\">\n            Repost of        <b class=\"list-of-links\"><a href=\"https://youdontneedamodalwindow.dev/\" class=\"p-name u-url u-uid\">\n        You don&#39;t need a modal window</a></b>\n    <span class=\"block <small>\">youdontneedamodalwindow.dev</span>\n            <blockquote class=\"p-summary\">Just make a separate page.</blockquote>\n    </article>\n                                        \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n            </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2023-05-24T05:05:00Z\">\n                24 May ’23 05:05</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"","url":"https://denizaksimsek.com/2023/you-dont-need-modal/","date_published":"2023-05-24T05:05:00Z"},{"id":"/2022/nice-try/","title":"Nice try","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Nice try</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Nice try\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/nice-try/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Nice try</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Nice try</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <article class=\"h-entry box crowded hover-state\" onclick=\"this.querySelector('a').click()\" lang=\"undefined\">\n        <b class=\"list-of-links\"><a href=\"https://lobste.rs/s/22yudo/kindelia_kind_next_gen_functional\" class=\"p-name u-url u-uid\">\n        Kind: A next-gen functional language</a></b>\n    <span class=\"block <small>\">lobste.rs</span>\n            <blockquote class=\"p-summary\">22 comments</blockquote>\n    </article>\n<p>this is a new programming language, right?\nand the people who made it are also making a blockchain thing for it to run on.\nwhich made some people, including me, sad.\nso someone laments this, and someone else comes in:</p>\n<style>\n/* TODO: move this to missing.css */\nblockquote blockquote { font-size: 1em }\n</style>\n<blockquote>\n<p>The author writes in the Kindelia Readme:</p>\n<blockquote>\n<p>There is no native coin. It is not a cryptocurrency. It is a cryptocomputer.\nWhich to me says they’re focusing on the decentralized application portion of blockchains instead of the scam coins.</p>\n</blockquote>\n</blockquote>\n<p>now, i wasn’t convinced.\nbecause, i’m no expert but, a blockchain can’t work without a currency, right?\nit’s part of the algorithmic foundation to have some token that the chain itself can reimburse participants with…</p>\n<p>so how did they do this? did they finally invent a Good Blockchain?</p>\n<p>website takes me to readme, “There is no native coin” ok but how? readme takes me to whitepaper:</p>\n<figure class=\"contents\">\n<blockquote>\n<p>As for block rewards, the same principle holds. Tokens and applications can leave rewards that only the block miner can collect. For example, <mark>Kindelia’s Genesis Token</mark>, a no-premine currency which will be deployed by the Kindelia Foundation on the first block, will include a method that mints coins once per block, following Bitcoin’s emission curve. This serves as an incentive for miners that keep the network secure. In other words, Kindelia <mark>doesn’t need a built-in token to have block rewards and miner fees</mark>. Instead, it flexibly allows users to pay fees in whatever tokens they want, and miners to collect block rewards from <mark>a constellation of user-deployed tokens, rather than a single official one</mark>.</p>\n</blockquote>\n<figcaption>\n    <a href=\"https://github.com/Kindelia/Kindelia-Chain/blob/67f86e1bf580dbf592b3cb80e8da2ec14ce01ea7/WHITEPAPER.md\"\n    ><cite>Kindelia whitepaper</cite></a>,\n    revision 67f86e1\n    (highlighting mine)\n</figcaption>\n</figure>\n<p><strong class=\"allcaps\">It’s a marketing trick.</strong>\n“No native coin” just means that the coin is pluggable.</p>\n<p><i>I’m going to commit crimes.</i></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-12-21T19:58:01Z\">\n                21 Dec ’22 19:58</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<%~ await includeFile(\"embed.eta\", { filters, url: \"https://lobste.rs/s/22yudo/kindelia_kind_next_gen_functional\" }) %>\n\n\nthis is a new programming language, right?\nand the people who made it are also making a blockchain thing for it to run on.\nwhich made some people, including me, sad.\nso someone laments this, and someone else comes in: \n\n<style>\n/* TODO: move this to missing.css */\nblockquote blockquote { font-size: 1em }\n</style>\n\n> The author writes in the Kindelia Readme:\n> > There is no native coin. It is not a cryptocurrency. It is a cryptocomputer.\n> Which to me says they’re focusing on the decentralized application portion of blockchains instead of the scam coins.\n\nnow, i wasn't convinced.\nbecause, i'm no expert but, a blockchain can't work without a currency, right?\nit's part of the algorithmic foundation to have some token that the chain itself can reimburse participants with...\n\nso how did they do this? did they finally invent a Good Blockchain?\n\nwebsite takes me to readme, \"There is no native coin\" ok but how? readme takes me to whitepaper:\n\n<figure class=\"contents\">\n\n> As for block rewards, the same principle holds. Tokens and applications can leave rewards that only the block miner can collect. For example, <mark>Kindelia's Genesis Token</mark>, a no-premine currency which will be deployed by the Kindelia Foundation on the first block, will include a method that mints coins once per block, following Bitcoin's emission curve. This serves as an incentive for miners that keep the network secure. In other words, Kindelia <mark>doesn't need a built-in token to have block rewards and miner fees</mark>. Instead, it flexibly allows users to pay fees in whatever tokens they want, and miners to collect block rewards from <mark>a constellation of user-deployed tokens, rather than a single official one</mark>.\n\n<figcaption>\n    <a href=\"https://github.com/Kindelia/Kindelia-Chain/blob/67f86e1bf580dbf592b3cb80e8da2ec14ce01ea7/WHITEPAPER.md\"\n    ><cite>Kindelia whitepaper</cite></a>,\n    revision 67f86e1\n    (highlighting mine)\n</figcaption>\n\n</figure>\n\n**It's a marketing trick.**{.allcaps}\n\"No native coin\" just means that the coin is pluggable.\n\n<i>I'm going to commit crimes.</i>\n","url":"https://denizaksimsek.com/2022/nice-try/","date_published":"2022-12-21T19:58:01Z"},{"id":"/2022/a-hypermedia-canvas/","title":"A Hypermedia Canvas","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>A Hypermedia Canvas</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"A Hypermedia Canvas\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/a-hypermedia-canvas/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>A Hypermedia Canvas</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">A Hypermedia Canvas</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>/fileshare/2022-09-30 a hypermedia canvas 2022-10-25.pdf</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-11-01T00:00:00Z\">\n                01 Nov ’22 00:00</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"/fileshare/2022-09-30 a hypermedia canvas 2022-10-25.pdf","url":"https://denizaksimsek.com/2022/a-hypermedia-canvas/","date_published":"2022-11-01T00:00:00Z"},{"id":"/2022/java-and-javascript/","title":"Similarities between Java and JavaScript","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Similarities between Java and JavaScript</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Similarities between Java and JavaScript\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/java-and-javascript/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Similarities between Java and JavaScript</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Similarities between Java and JavaScript</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>Java and JavaScript are a lot more similar than we give them credit for.</p>\n<ul>\n<li>A much-maligned language.</li>\n<li>Old, crufty APIs.</li>\n<li>Nicer today than many people remember.</li>\n<li>Complex build tools.</li>\n<li>Frameworks and metaframeworks galore.</li>\n<li>So, so much reflection.</li>\n<li>A vast universe of libraries for everything.</li>\n<li>Runs on very cursed places. (Java on SIM cards, JavaScript on spacecrafts)</li>\n<li>How do I make this an executable?</li>\n<li>Brilliant runtime.</li>\n</ul>\n<p>The main difference:\nJava programmers pretend they’re using JavaScript,\nand JavaScript programmers pretend they’re using Java.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-10-25T03:52:36Z\">\n                25 Oct ’22 03:52</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"Java and JavaScript are a lot more similar than we give them credit for.\n\n * A much-maligned language.\n * Old, crufty APIs.\n * Nicer today than many people remember.\n * Complex build tools.\n * Frameworks and metaframeworks galore.\n * So, so much reflection.\n * A vast universe of libraries for everything.\n * Runs on very cursed places. (Java on SIM cards, JavaScript on spacecrafts)\n * How do I make this an executable?\n * Brilliant runtime.\n\nThe main difference:\nJava programmers pretend they're using JavaScript,\nand JavaScript programmers pretend they're using Java.\n","url":"https://denizaksimsek.com/2022/java-and-javascript/","date_published":"2022-10-25T03:52:36Z"},{"id":"/2022/cdns-run-code/","title":"CDNs can run code now","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>CDNs can run code now</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"CDNs can run code now\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/cdns-run-code/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>CDNs can run code now</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">CDNs can run code now</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>One area where htmx apps lag behind SPAs is interaction delay.\nWhile htmx apps usually have a much improved initial load time,\nthe need for a server roundtrip for most interactions is a disadvantage.</p>\n<p>For most apps, this doesn’t matter, as they are all frontends to databases anyway.\nThus, there is always a request to the server;\nthe difference is whether the response is HTML or a proprietary JSON-based format that needs to be converted to HTML.</p>\n<p>The latency issue is even less of a problem thanks to… let’s call it Application on CDN (AoCDN).\nThe common term for this technology is <no-spoiler>“edge compute”</no-spoiler>,\nwhich I refuse to say.</p>\n<p>AoCDN is a service offered by some hosting and CDN providers.\nIt means that your code runs on several servers all across the world, close to the user.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-10-01T08:25:36Z\">\n                01 Oct ’22 08:25</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"One area where htmx apps lag behind SPAs is interaction delay.\nWhile htmx apps usually have a much improved initial load time,\nthe need for a server roundtrip for most interactions is a disadvantage.\n\nFor most apps, this doesn't matter, as they are all frontends to databases anyway.\nThus, there is always a request to the server;\nthe difference is whether the response is HTML or a proprietary JSON-based format that needs to be converted to HTML.\n\nThe latency issue is even less of a problem thanks to... let's call it Application on CDN (AoCDN).\nThe common term for this technology is <no-spoiler>\"edge compute\"</no-spoiler>,\nwhich I refuse to say.\n\nAoCDN is a service offered by some hosting and CDN providers.\nIt means that your code runs on several servers all across the world, close to the user.\n","url":"https://denizaksimsek.com/2022/cdns-run-code/","date_published":"2022-10-01T08:25:36Z"},{"id":"/2022/2022-07-19T12:49:37.803Z/","title":"Resource Traversal","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Resource Traversal</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Resource Traversal\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/2022-07-19T12:49:37.803Z/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Resource Traversal</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Resource Traversal</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <h2>Resource Expansion</h2>\n<pre><code>&lt; GET https://example.com/users/john_doe\n&gt; { &quot;name&quot;: &quot;John Doe&quot;, &quot;friends&quot;: &quot;/users/john_doe/friends&quot; }\n\n&lt; GET https://example.com/users/john_doe?expand=friends\n&gt; { &quot;name&quot;: &quot;John Doe&quot;, &quot;friends&quot;: { &quot;joe_bloggs&quot;: &quot;/users/joe_bloggs&quot; } }\n</code></pre>\n<p>Substitute the link named <code>friends</code> with the resource at that link.</p>\n<h2>Resource Traversal</h2>\n<pre><code>&lt; DELETE https://example.com/users/john_doe?traverse=friends.joe_bloggs\n&gt; { &quot;msg&quot;: &quot;Removed friend joe_bloggs&quot; }\n</code></pre>\n<p>Server will internally GET each link up to the last, and respond with a 307\nredirect to the last.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-07-19T12:49:46Z\">\n                19 Jul ’22 12:49</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"## Resource Expansion ##\n\n    < GET https://example.com/users/john_doe\n    > { \"name\": \"John Doe\", \"friends\": \"/users/john_doe/friends\" }\n    \n    < GET https://example.com/users/john_doe?expand=friends\n    > { \"name\": \"John Doe\", \"friends\": { \"joe_bloggs\": \"/users/joe_bloggs\" } }\n\nSubstitute the link named `friends` with the resource at that link.\n\n\n## Resource Traversal ##\n\n    < DELETE https://example.com/users/john_doe?traverse=friends.joe_bloggs\n    > { \"msg\": \"Removed friend joe_bloggs\" }\n\nServer will internally GET each link up to the last, and respond with a 307\nredirect to the last.\n","url":"https://denizaksimsek.com/2022/2022-07-19T12:49:37.803Z/","date_published":"2022-07-19T12:49:46Z"},{"id":"/2022/writing-things-down/","title":"Writing things down","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Writing things down</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Writing things down\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/writing-things-down/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Writing things down</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Writing things down</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><em>the Ideal Self looks on in and says: <q lang=tok style=font-style:normal>pakala</q></em></p>\n<p>There are a few intersecting online communities I lurk around that revolve around writing things down:</p>\n<ul>\n<li>notebook fans, <a href=\"https://reddit.com/r/notebooks\">r/notebooks</a> (“I can stop buying them whenever I want.”)</li>\n<li>plain text enjoyers (“Clearly the solution is to write a parser.”)</li>\n<li>“productivity” communities (“look at my Notion/Org Mode/Obsidian/… setup!” / unixporn for people with jobs)</li>\n<li>the Bullet Journal community (Plug in your own snarky comment for this one)</li>\n</ul>\n<h2>Why I want to do it</h2>\n<p>I want to write things down, I really do. I want to keep a diary, and todo lists, and kanban boards. Why? Any of the communities above would be happy to give you a laundry list of reasons to write things down, but there are two in particular that resonate with me.</p>\n<p>The first: I want to get things out of my brain. I’ve always been a forgetful person, so, I rode the productivity tool carousel.</p>\n<p>A few times, something actually worked for a bit. My best streak was keeping weekly Bullet Journal-like to-do lists throughout the last year of high school, as I was preparing for university entrance exams.</p>\n<p>That brings me to the second reason I want to write things down: keeping a log<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>. Having a record of things you did and thought seems useful in theory. Yet, I threw out the lovely little book I kept my to-dos in, and I don’t really miss them. I <em>feel</em> like I should be upset about not having it, and that does bother me, but I’m not bothered by the book itself. Why is that?</p>\n<h2>Looking upon my own works and despairing</h2>\n<p>I want to write things down quickly. I want to take notes in lectures without having to splice them with textbooks later. I want to write down an appointment before I forget, or an idea before I free-associate away from it.</p>\n<p>But I can’t bring myself to commit anything to paper. I can’t just take notes, I have to write myself a textbook. If I write down an idea, I can’t pull away without preparing a design document. Yet every sentence of such tomes would be subject to the same fear, and the result is pages half-reserved (read: empty).</p>\n<p>This leads to a conflict between the two motivations for writing things down.</p>\n<h1 lang=\"tok\">ijo nasa li pona e mi</h1>\n<p>I’ve been keeping a journal for around a year now. I write in it pretty infrequently, but it’s still helpful and I managed to actually finish a (admittedly very thin) notebook for once!</p>\n<p>The unique thing is that I write most of my entries in Toki Pona. I’ve heard of people using it in this way, and I gave it a try. It was initially just the novelty of the language that motivated me to keep going, but I was surprised to actually enjoy reading these back after a few months.</p>\n<p>The trick is that the simplicity of the language (combined with my lack of actual conversation experience) means that my entries don’t have a <em>voice</em>, that mix of word choice, syntax, and many other things that identify a particular author. They don’t have the trace of yesterday-me’s self image, which today-me finds repugnant.</p>\n<p><em>Am I genuinely going to write about cringing at my past self? Right now? This is repugnant already! Pressing through…</em></p>\n<p>That’s really it — there is a stark difference between words for the public eye and words for myself, and I can’t get myself to write the second on paper. Paper seems like another person.</p>\n<hr>\n<p>It dawns on me after writing (aha!) that I can’t write things that identify me — that when I write something for the public eye, I edit it down and remove any trace of myself I spot. That doesn’t seem healthy…</p>\n<p>I think what that makes me is <em>self-conscious</em>, but that sounds too positive to be right. Why wouldn’t you want to be conscious of yourself? At the same time, I don’t want so spend time thinking of ways to pity myself.</p>\n<p>Maybe this is something I’ll just get over. Then writing this post down would be pretty pointless…</p>\n<p><em>But I wouldn’t care!</em></p>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>There is a secret third reason why I want to write things down: notebooks and text editors are <strong>fun</strong>. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-07-16T20:57:08Z\">\n                16 Jul ’22 20:57</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"_the Ideal Self looks on in and says: <q lang=tok style=font-style:normal>pakala</q>_\n\nThere are a few intersecting online communities I lurk around that revolve around writing things down:\n\n - notebook fans, [r/notebooks] (\"I can stop buying them whenever I want.\")\n - plain text enjoyers (\"Clearly the solution is to write a parser.\")\n - \"productivity\" communities (\"look at my Notion/Org Mode/Obsidian/... setup!\" / unixporn for people with jobs)\n - the Bullet Journal community (Plug in your own snarky comment for this one)\n\n[r/notebooks]: https://reddit.com/r/notebooks\n\n\n## Why I want to do it\n\nI want to write things down, I really do. I want to keep a diary, and todo lists, and kanban boards. Why? Any of the communities above would be happy to give you a laundry list of reasons to write things down, but there are two in particular that resonate with me.\n\nThe first: I want to get things out of my brain. I've always been a forgetful person, so, I rode the productivity tool carousel.\n\nA few times, something actually worked for a bit. My best streak was keeping weekly Bullet Journal-like to-do lists throughout the last year of high school, as I was preparing for university entrance exams.\n\nThat brings me to the second reason I want to write things down: keeping a log[^1]. Having a record of things you did and thought seems useful in theory. Yet, I threw out the lovely little book I kept my to-dos in, and I don't really miss them. I _feel_ like I should be upset about not having it, and that does bother me, but I'm not bothered by the book itself. Why is that?\n\n\n## Looking upon my own works and despairing\n\nI want to write things down quickly. I want to take notes in lectures without having to splice them with textbooks later. I want to write down an appointment before I forget, or an idea before I free-associate away from it.\n\nBut I can't bring myself to commit anything to paper. I can't just take notes, I have to write myself a textbook. If I write down an idea, I can't pull away without preparing a design document. Yet every sentence of such tomes would be subject to the same fear, and the result is pages half-reserved (read: empty).\n\nThis leads to a conflict between the two motivations for writing things down.\n\n\n# ijo nasa li pona e mi {lang=tok}\n\nI've been keeping a journal for around a year now. I write in it pretty infrequently, but it's still helpful and I managed to actually finish a (admittedly very thin) notebook for once!\n\nThe unique thing is that I write most of my entries in Toki Pona. I've heard of people using it in this way, and I gave it a try. It was initially just the novelty of the language that motivated me to keep going, but I was surprised to actually enjoy reading these back after a few months.\n\nThe trick is that the simplicity of the language (combined with my lack of actual conversation experience) means that my entries don't have a _voice_, that mix of word choice, syntax, and many other things that identify a particular author. They don't have the trace of yesterday-me's self image, which today-me finds repugnant.\n\n_Am I genuinely going to write about cringing at my past self? Right now? This is repugnant already! Pressing through..._\n\nThat's really it --- there is a stark difference between words for the public eye and words for myself, and I can't get myself to write the second on paper. Paper seems like another person.\n\n\n---\n\nIt dawns on me after writing (aha!) that I can't write things that identify me --- that when I write something for the public eye, I edit it down and remove any trace of myself I spot. That doesn't seem healthy...\n\nI think what that makes me is _self-conscious_, but that sounds too positive to be right. Why wouldn't you want to be conscious of yourself? At the same time, I don't want so spend time thinking of ways to pity myself.\n\nMaybe this is something I'll just get over. Then writing this post down would be pretty pointless...\n\n_But I wouldn't care!_\n\n[^1]: There is a secret third reason why I want to write things down: notebooks and text editors are **fun**.\n","url":"https://denizaksimsek.com/2022/writing-things-down/","date_published":"2022-07-16T20:57:08Z"},{"id":"/2022/2022-06-27T18:54:05.038Z/","title":"A missing.css conundrum","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>A missing.css conundrum</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"A missing.css conundrum\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/2022-06-27T18:54:05.038Z/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>A missing.css conundrum</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">A missing.css conundrum</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I want to add a dark theme to missing.css out of the box using the <code>prefers-color-scheme</code> media query. This is a pretty straightforward thing to do, but it causes a problem when you add your own styles on top of missing.css (the expected main usage). Consider:</p>\n<ul>\n<li>You install missing.css</li>\n<li>You override the <a href=\"https://missing.style/docs/variables/#--accent\"><code>--accent</code></a> variable</li>\n</ul>\n<p>If the user has dark theme enabled system-wide, two things might happen depending on how we implement things:</p>\n<ul>\n<li>The dark theme accent color in missing.css overrides the custom accent</li>\n<li>The custom accent is used with missing.css’s dark theme colors, probably ugly and bad contrast</li>\n</ul>\n<p>Neither of these are desirable. The only way to prevent this would be to force everyone to specify their custom colors in both light and dark theme – even if they don’t want to support dark theme.</p>\n<p>As for solutions:</p>\n<ul>\n<li>One option is to require a class to enable automatic theme switching. This will work and not be much trouble, but makes missing.css less valuable as a classless CSS library.</li>\n</ul>\n<p>Thoughts?</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-06-27T18:54:22Z\">\n                27 Jun ’22 18:54</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":" I want to add a dark theme to missing.css out of the box using the `prefers-color-scheme` media query. This is a pretty straightforward thing to do, but it causes a problem when you add your own styles on top of missing.css (the expected main usage). Consider:\n\n - You install missing.css\n - You override the [`--accent`](https://missing.style/docs/variables/#--accent) variable\n\nIf the user has dark theme enabled system-wide, two things might happen depending on how we implement things:\n\n - The dark theme accent color in missing.css overrides the custom accent\n - The custom accent is used with missing.css's dark theme colors, probably ugly and bad contrast\n\nNeither of these are desirable. The only way to prevent this would be to force everyone to specify their custom colors in both light and dark theme -- even if they don't want to support dark theme.\n\nAs for solutions:\n\n - One option is to require a class to enable automatic theme switching. This will work and not be much trouble, but makes missing.css less valuable as a classless CSS library.\n\nThoughts?\n","url":"https://denizaksimsek.com/2022/2022-06-27T18:54:05.038Z/","date_published":"2022-06-27T18:54:22Z"},{"id":"/2022/hyperscript-cheatsheet/","title":"_hyperscript Cheatsheet","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>_hyperscript Cheatsheet</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"_hyperscript Cheatsheet\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/hyperscript-cheatsheet/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>_hyperscript Cheatsheet</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">_hyperscript Cheatsheet</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <style>\n:root {\n    overflow: auto;\n}\n\nheader {\n    border-bottom: 1px solid var(--faded-fg);\n}\n\n.e-content {\n    column-width: calc(var(--line-length) - var(--rhythm));\n    column-gap: var(--gap);\n    max-width: calc(2 * var(--line-length) + var(--gap));\n    padding-inline: var(--gap);\n\n    /* Full bleed */\n    width: 100vw;\n    margin-left: 50%;\n    transform: translateX(-50%);\n}\n\nsection.box {\n    display: table;\n    margin-top: 0;\n    width: 100%;\n}\n\ndl div {\n    margin-block: var(--gap);\n}\n</style>\n<section class=\"box\">\n<h2>Event listeners</h2>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>Syntax</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td></td>\n<td><strong><code>on</code></strong></td>\n<td>add event listener</td>\n</tr>\n<tr>\n<td></td>\n<td><code>every</code></td>\n<td>do not queue events</td>\n</tr>\n<tr>\n<td></td>\n<td><strong><code>mousemove</code></strong></td>\n<td>event name</td>\n</tr>\n<tr>\n<td></td>\n<td><code>(clientX, clientY)</code></td>\n<td>expose the event’s properties</td>\n</tr>\n<tr>\n<td></td>\n<td><code>[clientX &gt; 100]</code></td>\n<td>filter events</td>\n</tr>\n<tr>\n<td></td>\n<td><code>3</code></td>\n<td>only respond to 3rd click</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>3 to 10</code></td>\n<td>respond to 3rd, 4th … 10th click</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>3 and on</code></td>\n<td>respond to all clicks except 1st and 2nd</td>\n</tr>\n<tr>\n<td></td>\n<td><code>from #my-form</code></td>\n<td>element to attach listeners to, (?? me)</td>\n</tr>\n<tr>\n<td></td>\n<td><code>debounced at 200ms</code></td>\n<td>trailing debounce (200ms delay, resets on every event)</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>throttled at 200ms</code></td>\n<td>every 200ms at most regardless of the number of events</td>\n</tr>\n<tr>\n<td></td>\n<td><code>or keyup ...</code></td>\n<td>specify many events, each with its own from/debounce/…</td>\n</tr>\n<tr>\n<td></td>\n<td></td>\n<td>if events arrive while the listener is already running…</td>\n</tr>\n<tr>\n<td></td>\n<td><code>queue all</code></td>\n<td>add them to a FIFO queue</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>queue none</code></td>\n<td>discard them</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>queue first</code></td>\n<td>enqueue the first one, discard the rest</td>\n</tr>\n<tr>\n<td><em>or</em></td>\n<td><code>queue last</code></td>\n<td>enqueue the last one, discard the rest (this is the default)</td>\n</tr>\n</tbody>\n</table>\n</section>\n<section class=\"box\">\n<h2>Property access</h2>\n<ul>\n<li><code>user.data.name</code></li>\n<li><code>user’s data’s name</code></li>\n<li><code>name of data of user</code></li>\n<li><code>data.name of user</code></li>\n<li><code>user’s data.name</code></li>\n</ul>\n</section>\n<section class=\"box\">\n<h2>CSS literals</h2>\n<dl>\n<dt><code>#my-form</code></dt>\n<dd>Get element by id</dd>\n<dt><code>#{getID()}</code></dt>\n<dd>Dynamic ID</dd>\n<dt><code>.active</code></dt>\n<dd>Get elements by class</dd>\n<dt><code>.{getClass()}</code></dt>\n<dd>Dynamic class</dd>\n<dt><code>&lt;em, i /&gt;</code></dt>\n<dd>Query selector all</dd>\n<dt><code>&lt;ul:nth-child(${n}) /&gt;</code></dt>\n<dd>Dynamic selector</dd>\n</dl>\n</section>\n<section class=\"box\">\n<h2>Sigils</h2>\n<dl>\n<dt><code>foo</code></dt>\n<dd>local variable by default</dd>\n<dt><code>:foo</code></dt>\n<dd>element scoped variable, persisted. can be declared with top-level set. behaviors are isolated from one another</dd>\n<dt><code>$foo</code></dt>\n<dd>global variable</dd>\n<dt><code>@foo</code></dt>\n<dd>HTML attribute</dd>\n</dl>\n</section>\n<section class=\"box\">\n<h2>Array operations</h2>\n<p><code>first in arr</code> ≡ <code>first from arr</code>\n≡ <code>first of arr</code> ≡ <code>first arr</code></p>\n<p>also <code>random arr</code>, <code>last arr</code></p>\n</section>\n<section class=\"box\">\n<h2>Finding elements</h2>\n<dl>\n<dt><code>closest &amp;lt;section/&gt;</code></dt>\n<dd>nearest enclosing section</dd>\n<dt><code>previous &amp;lt;section/&gt; from #sec-2</code></dt>\n<dd>last section that comes before section 2 (?? me)</dd>\n<dt><code>next &amp;lt;input, button, a/&gt; from document.activeElement within #dialog with wrapping</code></dt>\n<dd>element to focus when pressing Tab in a modal dialog</dd>\n</dl>\n</section>\n<section class=\"box\">\n<h2>Other top-level features</h2>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>behavior</code></td>\n<td>Define cross-cutting behaviors that are applied to many HTML elements</td>\n</tr>\n<tr>\n<td><code>def</code></td>\n<td>Defines a function</td>\n</tr>\n<tr>\n<td><code>eventsource</code></td>\n<td>Subscribe to Server Sent Events (SSE)</td>\n</tr>\n<tr>\n<td><code>js</code></td>\n<td>Embed JavaScript code at the top level</td>\n</tr>\n<tr>\n<td><code>set</code></td>\n<td>Defines a new element-scoped variable</td>\n</tr>\n<tr>\n<td><code>init</code></td>\n<td>Initialization logic to be run when the code is first loaded</td>\n</tr>\n<tr>\n<td><code>on</code></td>\n<td>Creates an event listener \ton click log “clicked!”</td>\n</tr>\n<tr>\n<td><code>socket</code></td>\n<td>Create a Web Socket</td>\n</tr>\n<tr>\n<td><code>worker</code></td>\n<td>Create a Web Worker for asynchrnous work</td>\n</tr>\n</tbody>\n</table>\n</section>\n<section class=\"box\">\n<h2>Command index</h2>\n<dl>\n<div>\n    <dt><code>add <var>.class</var> to <var>elt</var></code>\n    <dt><code>add @<var>attribute=value</var> to <var>elt</var></code>\n    <dt><code>add { font-size: ${<var>elt</var>}px; } to <var>elt</var></code>\n    <dd>add classes/attributes/inline styles to <var>elt</var> (?? me)\n</div>\n<div>\n    <dt><code>append <var>value</var> to <var>target</var></code>\n    <dd>append to strings/arrays/elements, sets it = <var>target</var> (?? it)\n</div>\n<div>\n    <dt><code>async <var>command</var></code> | <code>async do <var>command</var>… end</code>\n    <dd>run commands in a non-blocking manner\n</div>\n<div>\n    <dt><code>call <var>expr</var> | get <var>expr</var></code>\n    <dd>sets it = <var>expr</var>\n</div>\n<div>\n    <dt><code>continue</code>\n    <dd>skips to next iteration in a loop\n</div>\n<div>\n    <dt><code>decrement <var>lvalue</var> by <var>amount</var></code>\n    <dd>sets <var>lvalue</var> = <var>lvalue</var> - <var>amount</var> (?? 1)\n</div>\n<div>\n    <dt><code>fetch <var>/url</var> with <var>option: value</var>, …</code>\n    <dt><code>fetch `/url/${<var>id</var>}/` with <var>option: value</var>, …</code>\n    <dd>makes an HTTP request, see Fetch API docs for options\n</div>\n<div>\n    <dt><code>go to url <var>/url</var> in new window</code>\n    <dt><code>go to url `/url/${<var>id</var>}/`</code>\n    <dd>navigate to a URL in the browser\n</div>\n<div>\n    <dt><code>go to top of <var>elt</var> -- top/middle/bottom </code>\n    <dt><code>go to top left of <var>elt</var> -- left/center/right</code>\n    <dt><code>go to left of <var>elt</var> smoothly -- /instantly</code>\n    <dd>scroll an element into view\n</div>\n<div>\n    <dt><code>halt the event’s default</code>\n    <dd>prevent default behavior\n</div>\n<div>\n    <dt><code>halt default</code>\n    <dd>same as above, and exits listener\n</div>\n<div>\n    <dt><code>halt the event’s bubbling</code>\n    <dd>stop event bubbling\n</div>\n<div>\n    <dt><code>halt bubbling</code>\n    <dd>same as above, and exits listener\n</div>\n<div>\n    <dt><code>halt the event</code>\n    <dd>stop both default and bubbling\n</div>\n<div>\n    <dt><code>halt</code>\n    <dd>all of the above\n</div>\n<div>\n    <dt><code>hide <var>elt</var> with strategy</code>\n    <dd>see `show`\n</div>\n<div>\n    <dt><code>if <var>cond</var> then … else … end</code>\n    <dd>if statement\n</div>\n<div>\n    <dt><code>increment</code>\n    <dd>see decrement\n</div>\n<div>\n    <dt><code>js(<var>var</var>) … end</code>\n    <dd>embed JavaScript\n</div>\n<div>\n    <dt><code>log <var>value</var> with <var>func</var></code>\n    <dd>logs the <var>value</var> to the console using <var>func</var> (?? console.log)\n</div>\n<div>\n    <dt><code>make a &lt;tag#id.class /> called <var>name</var></code>\n    <dd>creates an element with the given tag, id and classes, sets <var>name</var> (?? it) = the created element\n</div>\n<div>\n    <dt><code>make a <var>Class</var> from <var>args</var> called <var>name</var></code>\n    <dd>calls the <var>Class</var> constructor with the <var>args</var>, sets <var>name</var> (?? it) = the created object\n</div>\n<div>\n    <dt><code>put <var>rvalue</var> into <var>lvalue</var></code>\n    <dd>see set\n</div>\n<div>\n    <dt><code>put <var>content</var> into <var>elt</var> -- into/before/after/at start of/at end of</code>\n    <dd>insert content into various parts of the <var>elt</var>\n</div>\n<div>\n    <dt><code>remove .<var>class</var> from <var>elt</var></code>\n    <dd>see add\n</div>\n<div>\n    <dt><code>remove @<var>attribute</var> from <var>elt</var></code>\n    <dd>see add\n</div>\n<div>\n    <dt><code>remove <var>elt</var></code>\n    <dd>removes <var>elt</var> (?? me) from the document\n</div>\n<div>\n    <dt><code>repeat for <var>name</var> in <var>iterable</var> index <var>i</var> … end</code>\n    <dt><code>for <var>name</var> in <var>iterable</var> index <var>i</var> … end</code>\n    <dd>loop over an iterable, the loop variable is <var>name</var> (?? it)\n</div>\n<div>\n    <dt><code>repeat until event <var>e</var> from <var>elt</var> index <var>i</var> … end</code>\n    <dd>Repeat every tick until event <var>e</var> is received from <var>elt</var> (?? me)\n</div>\n<div>\n    <dt><code>repeat while <var>cond</var> | repeat until <var>cond</var> … end</code>\n    <dt><code>repeat <var>n</var> times index <var>i</var> … end</code>\n    <dt><code>repeat forever … end</code>\n    <dd>--\n</div>\n<div>\n    <dt><code>return <var>value</var> | exit</code>\n    <dd>return, see also halt\n</div>\n<div>\n    <dt><code>send    <var>evt</var>(<var>args</var>…) to <var>elt</var></code>\n    <dt><code>trigger <var>evt</var>(<var>args</var>…) on <var>elt</var></code>\n    <dd>dispatch a DOM event on <var>elt</var> (?? me)\n</div>\n<div>\n    <dt><code>set <var>lvalue</var> to <var>rvalue</var></code>\n    <dd>--\n</div>\n<div>\n    <dt><code>settle</code>\n    <dd>waits for any animations/transitions to end\n</div>\n<div>\n    <dt><code>show <var>elt</var> with <var>strategy</var> when <var>cond</var> -- strategy: display:_/visibility/opacity/…</code>\n    <dd>show <var>elt</var> (?? me) using the <var>strategy</var> (?? display:block) if <var>cond</var> (?? true) is true, else hide it\n</div>\n<div>\n    <dt><code>take .<var>class</var> from <var>eltA</var> for <var>eltB</var></code>\n    <dd>remove class from <var>eltA</var> (?? .class) and add it to <var>eltB</var> (?? me)\n</div>\n<div>\n    <dt><code>tell <var>elt</var> … end</code>\n    <dd>set <var>you</var> = elt, default to <var>you</var> over <var>me</var>\n</div>\n<div>\n    <dt><code>throw <var>exception</var></code>\n    <dd>throws an exception\n</div>\n<div>\n    <dt><code>toggle .<var>class</var> on <var>eltA</var> for <var>t</var> s </code>\n    <dt><code>toggle [@<var>attr=value</var>] until <var>evt</var> from <var>eltB</var></code>\n    <dt><code>toggle between .<var>class1</var> and .<var>class2</var> on <var>eltA</var></code>\n    <dd>toggle classes and attributes on <var>eltA</var> (?? me)\n</div>\n<div>\n    <dt><code>transition the <var>elt</var>'s <var>prop</var> to <var>value</var> … over <var>t</var> s</code>\n    <dd>Animate style properties\n</div>\n<div>\n    <dt><code>wait <var>t</var> s -- or ms</code>\n    <dd>Waits for the given duration\n</div>\n<div>\n    <dt><code>wait for <var>event</var> or <var>event2</var> or <var>t</var> s</code>\n    <dd>waits for one of the events to occur, sets it=the event\n</div>\n</dl>\n</section>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-05-06T13:14:36Z\">\n                06 May ’22 13:14</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<style>\n:root {\n    overflow: auto;\n}\n\nheader {\n    border-bottom: 1px solid var(--faded-fg);\n}\n\n.e-content {\n    column-width: calc(var(--line-length) - var(--rhythm));\n    column-gap: var(--gap);\n    max-width: calc(2 * var(--line-length) + var(--gap));\n    padding-inline: var(--gap);\n\n    /* Full bleed */\n    width: 100vw;\n    margin-left: 50%;\n    transform: translateX(-50%);\n}\n\nsection.box {\n    display: table;\n    margin-top: 0;\n    width: 100%;\n}\n\ndl div {\n    margin-block: var(--gap);\n}\n</style>\n\n<section class=\"box\">\n\n## Event listeners\n\n|      | Syntax               | Meaning                                                      |\n|------|----------------------|--------------------------------------------------------------|\n|      | **`on`**             | add event listener                                           |\n|      | `every`              | do not queue events                                          |\n|      | **`mousemove`**      | event name                                                   |\n|      | `(clientX, clientY)` | expose the event’s properties                                |\n|      | `[clientX > 100]`    | filter events                                                |\n|      | `3`                  | only respond to 3rd click                                    |\n| _or_ | `3 to 10`            | respond to 3rd, 4th … 10th click                             |\n| _or_ | `3 and on`           | respond to all clicks except 1st and 2nd                     |\n|      | `from #my-form`      | element to attach listeners to, (?? me)                      |\n|      | `debounced at 200ms` | trailing debounce (200ms delay, resets on every event)       |\n| _or_ | `throttled at 200ms` | every 200ms at most regardless of the number of events       |\n|      | `or keyup ...`       | specify many events, each with its own from/debounce/…       |\n|      |                      | if events arrive while the listener is already running…      |\n|      | `queue all`          | add them to a FIFO queue                                     |\n| _or_ | `queue none`         | discard them                                                 |\n| _or_ | `queue first`        | enqueue the first one, discard the rest                      |\n| _or_ | `queue last`         | enqueue the last one, discard the rest (this is the default) |\n\n\n</section>\n<section class=\"box\">\n\n## Property access\n\n - `user.data.name`\n - `user’s data’s name`\n - `name of data of user` \n - `data.name of user`\n - `user’s data.name`\n\n</section>\n<section class=\"box\">\n\n## CSS literals\n\n`#my-form`\n:   Get element by id\n\n`#{getID()}`\n: Dynamic ID\n\n`.active`\n: Get elements by class\n\n`.{getClass()}`\n: Dynamic class\n\n`<em, i />`\n: Query selector all\n\n`<ul:nth-child(${n}) />`\n: Dynamic selector\n\n</section>\n<section class=\"box\">\n\n## Sigils\n\n`foo`\n: local variable by default\n\n`:foo`\n: element scoped variable, persisted. can be declared with top-level set. behaviors are isolated from one another\n\n`$foo`\n: global variable\n\n`@foo`\n: HTML attribute\n\n</section>\n<section class=\"box\">\n\n## Array operations\n\n`first in arr` ≡ `first from arr`\n≡ `first of arr` ≡ `first arr`\n\nalso `random arr`, `last arr`\n\n</section>\n<section class=\"box\">\n\n## Finding elements\n\n`closest &lt;section/>`\n: nearest enclosing section\n\n`previous &lt;section/> from #sec-2`\n: last section that comes before section 2 (?? me)\n\n`next &lt;input, button, a/> from document.activeElement within #dialog with wrapping`\n: element to focus when pressing Tab in a modal dialog\n\n\n</section>\n<section class=\"box\">\n\n## Other top-level features\n\n| Feature       |   Description                                                             |\n|---------------|---------------------------------------------------------------------------|\n| `behavior`    | \tDefine cross-cutting behaviors that are applied to many HTML elements   |\n| `def`         | \tDefines a function \t                                                    |\n| `eventsource` | \tSubscribe to Server Sent Events (SSE) \t                                |\n| `js`          | \tEmbed JavaScript code at the top level \t                                |\n| `set`         | \tDefines a new element-scoped variable \t                                |\n| `init`        | \tInitialization logic to be run when the code is first loaded \t        |\n| `on`          | \tCreates an event listener \ton click log \"clicked!\"                     |\n| `socket`      | \tCreate a Web Socket \t                                                |\n| `worker`      | \tCreate a Web Worker for asynchrnous work                                |\n\n</section>\n<section class=\"box\">\n\n## Command index\n\n<dl>\n\n<div>\n    <dt><code>add <var>.class</var> to <var>elt</var></code>\n    <dt><code>add @<var>attribute=value</var> to <var>elt</var></code>\n    <dt><code>add { font-size: ${<var>elt</var>}px; } to <var>elt</var></code>\n    <dd>add classes/attributes/inline styles to <var>elt</var> (?? me)\n</div>\n\n<div>\n    <dt><code>append <var>value</var> to <var>target</var></code>\n    <dd>append to strings/arrays/elements, sets it = <var>target</var> (?? it)\n</div>\n\n<div>\n    <dt><code>async <var>command</var></code> | <code>async do <var>command</var>… end</code>\n    <dd>run commands in a non-blocking manner\n</div>\n\n<div>\n    <dt><code>call <var>expr</var> | get <var>expr</var></code>\n    <dd>sets it = <var>expr</var>\n</div>\n\n<div>\n    <dt><code>continue</code>\n    <dd>skips to next iteration in a loop\n</div>\n\n<div>\n    <dt><code>decrement <var>lvalue</var> by <var>amount</var></code>\n    <dd>sets <var>lvalue</var> = <var>lvalue</var> - <var>amount</var> (?? 1)\n</div>\n\n<div>\n    <dt><code>fetch <var>/url</var> with <var>option: value</var>, …</code>\n    <dt><code>fetch `/url/${<var>id</var>}/` with <var>option: value</var>, …</code>\n    <dd>makes an HTTP request, see Fetch API docs for options\n</div>\n\n<div>\n    <dt><code>go to url <var>/url</var> in new window</code>\n    <dt><code>go to url `/url/${<var>id</var>}/`</code>\n    <dd>navigate to a URL in the browser\n</div>\n\n<div>\n    <dt><code>go to top of <var>elt</var> -- top/middle/bottom </code>\n    <dt><code>go to top left of <var>elt</var> -- left/center/right</code>\n    <dt><code>go to left of <var>elt</var> smoothly -- /instantly</code>\n    <dd>scroll an element into view\n</div>\n\n<div>\n    <dt><code>halt the event’s default</code>\n    <dd>prevent default behavior\n</div>\n\n<div>\n    <dt><code>halt default</code>\n    <dd>same as above, and exits listener\n</div>\n\n<div>\n    <dt><code>halt the event’s bubbling</code>\n    <dd>stop event bubbling\n</div>\n\n<div>\n    <dt><code>halt bubbling</code>\n    <dd>same as above, and exits listener\n</div>\n\n<div>\n    <dt><code>halt the event</code>\n    <dd>stop both default and bubbling\n</div>\n\n<div>\n    <dt><code>halt</code>\n    <dd>all of the above\n</div>\n\n<div>\n    <dt><code>hide <var>elt</var> with strategy</code>\n    <dd>see `show`\n</div>\n\n<div>\n    <dt><code>if <var>cond</var> then … else … end</code>\n    <dd>if statement\n</div>\n\n<div>\n    <dt><code>increment</code>\n    <dd>see decrement\n</div>\n\n<div>\n    <dt><code>js(<var>var</var>) … end</code>\n    <dd>embed JavaScript\n</div>\n\n<div>\n    <dt><code>log <var>value</var> with <var>func</var></code>\n    <dd>logs the <var>value</var> to the console using <var>func</var> (?? console.log)\n</div>\n\n<div>\n    <dt><code>make a &lt;tag#id.class /> called <var>name</var></code>\n    <dd>creates an element with the given tag, id and classes, sets <var>name</var> (?? it) = the created element\n</div>\n\n<div>\n    <dt><code>make a <var>Class</var> from <var>args</var> called <var>name</var></code>\n    <dd>calls the <var>Class</var> constructor with the <var>args</var>, sets <var>name</var> (?? it) = the created object\n</div>\n\n<div>\n    <dt><code>put <var>rvalue</var> into <var>lvalue</var></code>\n    <dd>see set\n</div>\n\n<div>\n    <dt><code>put <var>content</var> into <var>elt</var> -- into/before/after/at start of/at end of</code>\n    <dd>insert content into various parts of the <var>elt</var>\n</div>\n\n<div>\n    <dt><code>remove .<var>class</var> from <var>elt</var></code>\n    <dd>see add\n</div>\n\n<div>\n    <dt><code>remove @<var>attribute</var> from <var>elt</var></code>\n    <dd>see add\n</div>\n\n<div>\n    <dt><code>remove <var>elt</var></code>\n    <dd>removes <var>elt</var> (?? me) from the document\n</div>\n\n<div>\n    <dt><code>repeat for <var>name</var> in <var>iterable</var> index <var>i</var> … end</code>\n    <dt><code>for <var>name</var> in <var>iterable</var> index <var>i</var> … end</code>\n    <dd>loop over an iterable, the loop variable is <var>name</var> (?? it)\n</div>\n\n<div>\n    <dt><code>repeat until event <var>e</var> from <var>elt</var> index <var>i</var> … end</code>\n    <dd>Repeat every tick until event <var>e</var> is received from <var>elt</var> (?? me)\n</div>\n\n<div>\n    <dt><code>repeat while <var>cond</var> | repeat until <var>cond</var> … end</code>\n    <dt><code>repeat <var>n</var> times index <var>i</var> … end</code>\n    <dt><code>repeat forever … end</code>\n    <dd>--\n</div>\n\n<div>\n    <dt><code>return <var>value</var> | exit</code>\n    <dd>return, see also halt\n</div>\n\n<div>\n    <dt><code>send    <var>evt</var>(<var>args</var>…) to <var>elt</var></code>\n    <dt><code>trigger <var>evt</var>(<var>args</var>…) on <var>elt</var></code>\n    <dd>dispatch a DOM event on <var>elt</var> (?? me)\n</div>\n\n<div>\n    <dt><code>set <var>lvalue</var> to <var>rvalue</var></code>\n    <dd>--\n</div>\n\n<div>\n    <dt><code>settle</code>\n    <dd>waits for any animations/transitions to end\n</div>\n\n<div>\n    <dt><code>show <var>elt</var> with <var>strategy</var> when <var>cond</var> -- strategy: display:_/visibility/opacity/…</code>\n    <dd>show <var>elt</var> (?? me) using the <var>strategy</var> (?? display:block) if <var>cond</var> (?? true) is true, else hide it\n</div>\n\n<div>\n    <dt><code>take .<var>class</var> from <var>eltA</var> for <var>eltB</var></code>\n    <dd>remove class from <var>eltA</var> (?? .class) and add it to <var>eltB</var> (?? me)\n</div>\n\n<div>\n    <dt><code>tell <var>elt</var> … end</code>\n    <dd>set <var>you</var> = elt, default to <var>you</var> over <var>me</var>\n</div>\n\n<div>\n    <dt><code>throw <var>exception</var></code>\n    <dd>throws an exception\n</div>\n\n<div>\n    <dt><code>toggle .<var>class</var> on <var>eltA</var> for <var>t</var> s </code>\n    <dt><code>toggle [@<var>attr=value</var>] until <var>evt</var> from <var>eltB</var></code>\n    <dt><code>toggle between .<var>class1</var> and .<var>class2</var> on <var>eltA</var></code>\n    <dd>toggle classes and attributes on <var>eltA</var> (?? me)\n</div>\n\n<div>\n    <dt><code>transition the <var>elt</var>'s <var>prop</var> to <var>value</var> … over <var>t</var> s</code>\n    <dd>Animate style properties\n</div>\n\n<div>\n    <dt><code>wait <var>t</var> s -- or ms</code>\n    <dd>Waits for the given duration\n</div>\n\n<div>\n    <dt><code>wait for <var>event</var> or <var>event2</var> or <var>t</var> s</code>\n    <dd>waits for one of the events to occur, sets it=the event\n</div>\n</dl>\n\n</section>\n","url":"https://denizaksimsek.com/2022/hyperscript-cheatsheet/","date_published":"2022-05-06T13:14:36Z"},{"id":"/2022/textmate-grammars/","title":"TextMate Grammars","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>TextMate Grammars</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"TextMate Grammars\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/textmate-grammars/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>TextMate Grammars</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">TextMate Grammars</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n            \n<article class=\"h-entry box crowded hover-state\" onclick=\"this.querySelector('a').click()\" lang=\"undefined\">\n            Bookmark of        <b class=\"list-of-links\"><a href=\"https://stackoverflow.com/questions/10834765/where-to-find-a-list-of-scopes-for-sublime2-or-textmate\" class=\"p-name u-url u-uid\">\n        Where to find a list of scopes for Sublime2 (or textMate?)</a></b>\n    <span class=\"block <small>\">stackoverflow.com</span>\n            <blockquote class=\"p-summary\">in .tmTheme files the scope key defines how a element is highlighted:\n\n    &amp;lt;dict&amp;gt;\n        &amp;lt;key&amp;gt;name&amp;lt;/key&amp;gt;\n        &amp;lt;string&amp;gt;HTML: Attribute Values&amp;lt;/string&amp;gt;\n        &amp;lt;k...</blockquote>\n    </article>\n    \n    <div class=\"e-content\">\n        <p>Thanks to Phrogz</p>\n<dl>\n<dt><code>comment</code></dt>\n<dd>\n<p>for comments.</p>\n<dl>\n<dt><code>line</code></dt>\n<dd>\n<p>line comments, we specialize further so that the type of comment start character(s) can be extracted from the scope.</p>\n<dl>\n<dt><code>double-slash</code></dt>\n<dd><code>//</code> comment</dd>\n<dt><code>double-dash</code></dt>\n<dd><code>--</code> comment</dd>\n<dt><code>number-sign</code></dt>\n<dd><code>#</code> comment</dd>\n<dt><code>percentage</code></dt>\n<dd><code>%</code> comment</dd>\n<dt><code>character</code></dt>\n<dd>other types of line comments.</dd>\n</dl>\n</dd>\n<dt><code>block</code></dt>\n<dd>\n<p>multi-line comments like <code>/* … */</code> and <code>&lt;!-- … --&gt;</code>.</p>\n<dl>\n<dt><code>documentation</code></dt>\n<dd>embedded documentation.</dd>\n</dl>\n</dd>\n</dl>\n</dd>\n<dt><code>constant</code></dt>\n<dd>\n<p>various forms of constants.</p>\n<dl>\n<dt><code>numeric</code></dt>\n<dd>\n<p>those which represent numbers, e.g. <code>42</code>, <code>1.3f</code>, <code>0x4AB1U</code>.</p>\n</dd>\n<dt><code>character</code></dt>\n<dd>\n<p>those which represent characters, e.g. <code>&amp;lt;</code>, <code>\\e</code>, <code>\\031</code>.</p>\n<dl>\n<dt><code>escape</code></dt>\n<dd>escape sequences like <code>\\e</code> would be constant.character.escape.</dd>\n</dl>\n</dd>\n<dt><code>language</code></dt>\n<dd>\n<p>constants (generally) provided by the language which are “special” like <code>true</code>, <code>false</code>, <code>nil</code>, <code>YES</code>, <code>NO</code>, etc.</p>\n</dd>\n<dt><code>other</code></dt>\n<dd>\n<p>other constants, e.g. colors in CSS.</p>\n</dd>\n</dl>\n</dd>\n<dt><code>entity</code></dt>\n<dd>\n<p>an entity refers to a larger part of the document, for example a chapter, class, function, or tag. We do not scope the entire entity as <code>entity.*</code> (we use <code>meta.*</code> for that). But we do use <code>entity.*</code> for the “placeholders” in the larger entity, e.g. if the entity is a chapter, we would use <code>entity.name.section</code> for the chapter title.</p>\n<dl>\n<dt><code>name</code></dt>\n<dd>\n<p>we are naming the larger entity.</p>\n<dl>\n<dt><code>function</code></dt>\n<dd>the name of a function.</dd>\n<dt><code>type</code></dt>\n<dd>the name of a type declaration or class.</dd>\n<dt><code>tag</code></dt>\n<dd>a tag name.</dd>\n<dt><code>section</code></dt>\n<dd>the name is the name of a section/heading.</dd>\n</dl>\n</dd>\n<dt><code>other</code></dt>\n<dd>\n<p>other entities.</p>\n<dl>\n<dt><code>inherited-class</code></dt>\n<dd>the superclass/baseclass name.</dd>\n<dt><code>attribute-name</code></dt>\n<dd>the name of an attribute (mainly in tags).</dd>\n</dl>\n</dd>\n</dl>\n</dd>\n<dt><code>invalid</code></dt>\n<dd>\n<p>stuff which is “invalid”.</p>\n<dl>\n<dt><code>illegal</code></dt>\n<dd>illegal, e.g. an ampersand or lower-than character in HTML (which is not part of an entity/tag).</dd>\n<dt><code>deprecated</code></dt>\n<dd>for deprecated stuff e.g. using an API function which is deprecated or using styling with strict HTML.</dd>\n</dl>\n</dd>\n<dt><code>keyword</code></dt>\n<dd>\n<p>keywords (when these do not fall into the other groups).</p>\n<dl>\n<dt><code>control</code></dt>\n<dd>mainly related to flow control like continue, while, return, etc.</dd>\n<dt><code>operator</code></dt>\n<dd>operators can either be textual (e.g. or) or be characters.</dd>\n<dt><code>other</code></dt>\n<dd>other keywords.</dd>\n</dl>\n</dd>\n<dt><code>markup</code></dt>\n<dd>\n<p>this is for markup languages and generally applies to larger subsets of the text.</p>\n<dl>\n<dt><code>underline</code></dt>\n<dd>\n<p>underlined text.</p>\n<dl>\n<dt><code>link</code></dt>\n<dd>this is for links, as a convenience this is derived from markup.underline so that if there is no theme rule which specifically targets markup.underline.link then it will inherit the underline style.</dd>\n</dl>\n</dd>\n<dt><code>bold</code></dt>\n<dd>\n<p>bold text (text which is strong and similar should preferably be derived from this name).</p>\n</dd>\n<dt><code>heading</code></dt>\n<dd>\n<p>a section header. Optionally provide the heading level as the next element, for example markup.heading.2.html for <code>&lt;h2&gt;…&lt;/h2&gt;</code> in HTML.</p>\n</dd>\n<dt><code>italic</code></dt>\n<dd>\n<p>italic text (text which is emphasized and similar should preferably be derived from this name).</p>\n</dd>\n<dt><code>list</code></dt>\n<dd>\n<p>list items.</p>\n<dl>\n<dt><code>numbered</code></dt>\n<dd>numbered list items.</dd>\n<dt><code>unnumbered</code></dt>\n<dd>unnumbered list items.</dd>\n</dl>\n</dd>\n<dt><code>quote</code></dt>\n<dd>\n<p>quoted (sometimes block quoted) text.</p>\n</dd>\n<dt><code>raw</code></dt>\n<dd>\n<p>text which is verbatim, e.g. code listings. Normally spell checking is disabled for markup.raw.</p>\n</dd>\n<dt><code>other</code></dt>\n<dd>\n<p>other markup constructs.</p>\n</dd>\n</dl>\n</dd>\n<dt><code>meta</code></dt>\n<dd>\n<p>the meta scope is generally used to markup larger parts of the document. For example the entire line which declares a function would be meta.function and the subsets would be storage.type, entity.name.function, variable.parameter etc. and only the latter would be styled. Sometimes the meta part of the scope will be used only to limit the more general element that is styled, most of the time meta scopes are however used in scope selectors for activation of bundle items. For example in Objective-C there is a meta scope for the interface declaration of a class and the implementation, allowing the same tab-triggers to expand differently, depending on context.</p>\n</dd>\n<dt><code>storage</code></dt>\n<dd>\n<p>things relating to “storage”.</p>\n<dl>\n<dt><code>type</code></dt>\n<dd>the type of something, class, function, int, var, etc.</dd>\n<dt><code>modifier</code></dt>\n<dd>a storage modifier like static, final, abstract, etc.</dd>\n</dl>\n</dd>\n<dt><code>string</code></dt>\n<dd>\n<p>strings.</p>\n<dl>\n<dt><code>quoted</code></dt>\n<dd>\n<p>quoted strings.</p>\n<dl>\n<dt><code>single</code></dt>\n<dd>single quoted strings: ‘foo’.</dd>\n<dt><code>double</code></dt>\n<dd>double quoted strings: “foo”.</dd>\n<dt><code>triple</code></dt>\n<dd>triple quoted strings: “”“Python”“”.</dd>\n<dt><code>other</code></dt>\n<dd>other types of quoting: $‘shell’, %s{…}.</dd>\n</dl>\n</dd>\n<dt><code>unquoted</code></dt>\n<dd>\n<p>for things like here-docs and here-strings.</p>\n</dd>\n<dt><code>interpolated</code></dt>\n<dd>\n<p>strings which are “evaluated”: date, $(pwd).</p>\n</dd>\n<dt><code>regexp</code></dt>\n<dd>\n<p>regular expressions: /(\\w+)/.</p>\n</dd>\n<dt><code>other</code></dt>\n<dd>\n<p>other types of strings (should rarely be used).</p>\n</dd>\n</dl>\n</dd>\n<dt><code>support</code></dt>\n<dd>\n<p>things provided by a framework or library should be below support.</p>\n<dl>\n<dt><code>function</code></dt>\n<dd>functions provided by the framework/library. For example NSLog in Objective-C is support.function.</dd>\n<dt><code>class</code></dt>\n<dd>when the framework/library provides classes.</dd>\n<dt><code>type</code></dt>\n<dd>types provided by the framework/library, this is probably only used for languages derived from C, which has typedef (and struct). Most other languages would introduce new types as classes.</dd>\n<dt><code>constant</code></dt>\n<dd>constants (magic values) provided by the framework/library.</dd>\n<dt><code>variable</code></dt>\n<dd>variables provided by the framework/library. For example NSApp in AppKit.</dd>\n<dt><code>other</code></dt>\n<dd>the above should be exhaustive, but for everything else use support.other.</dd>\n</dl>\n</dd>\n<dt><code>variable</code></dt>\n<dd>\n<p>variables. Not all languages allow easy identification (and thus markup) of these.</p>\n<dl>\n<dt><code>parameter</code></dt>\n<dd>when the variable is declared as the parameter.</dd>\n<dt><code>language</code></dt>\n<dd>reserved language variables like this, super, self, etc.</dd>\n<dt><code>other</code></dt>\n<dd>other variables, like $some_variables.</dd>\n</dl>\n</dd>\n</dl>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-04-16T16:02:47Z\">\n                16 Apr ’22 16:02</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"Thanks to Phrogz\n\n`comment`\n:   for comments.\n\n    `line`\n    :   line comments, we specialize further so that the type of comment start character(s) can be extracted from the scope.\n\n        `double-slash`\n        :   `//` comment\n\n        `double-dash`\n        :   `--` comment\n\n        `number-sign`\n        :   `#` comment\n\n        `percentage`\n        :   `%` comment\n\n        `character`\n        :   other types of line comments.\n\n    `block`\n    :   multi-line comments like `/* … */` and `<!-- … -->`.\n\n        `documentation`\n        :   embedded documentation.\n\n`constant`\n:   various forms of constants.\n\n    `numeric`\n    :   those which represent numbers, e.g. `42`, `1.3f`, `0x4AB1U`.\n\n    `character`\n    :   those which represent characters, e.g. `&lt;`, `\\e`, `\\031`.\n\n        `escape`\n        :   escape sequences like `\\e` would be constant.character.escape.\n\n    `language`\n    :   constants (generally) provided by the language which are “special” like `true`, `false`, `nil`, `YES`, `NO`, etc.\n\n    `other`\n    :   other constants, e.g. colors in CSS.\n\n`entity`\n:   an entity refers to a larger part of the document, for example a chapter, class, function, or tag. We do not scope the entire entity as `entity.*` (we use `meta.*` for that). But we do use `entity.*` for the “placeholders” in the larger entity, e.g. if the entity is a chapter, we would use `entity.name.section` for the chapter title.\n\n    `name`\n    :   we are naming the larger entity.\n\n        `function`\n        :   the name of a function.\n\n        `type`\n        :   the name of a type declaration or class.\n\n        `tag`\n        :   a tag name.\n\n        `section`\n        :   the name is the name of a section/heading.\n\n    `other`\n    :   other entities.\n\n        `inherited-class`\n        :   the superclass/baseclass name.\n\n        `attribute-name`\n        :   the name of an attribute (mainly in tags).\n\n`invalid`\n:   stuff which is “invalid”.\n\n    `illegal`\n    :   illegal, e.g. an ampersand or lower-than character in HTML (which is not part of an entity/tag).\n\n    `deprecated`\n    :   for deprecated stuff e.g. using an API function which is deprecated or using styling with strict HTML.\n\n`keyword`\n:   keywords (when these do not fall into the other groups).\n\n    `control`\n    :   mainly related to flow control like continue, while, return, etc.\n\n    `operator`\n    :   operators can either be textual (e.g. or) or be characters.\n\n    `other`\n    :   other keywords.\n\n`markup`\n:   this is for markup languages and generally applies to larger subsets of the text.\n\n    `underline`\n    :   underlined text.\n\n        `link`\n        :   this is for links, as a convenience this is derived from markup.underline so that if there is no theme rule which specifically targets markup.underline.link then it will inherit the underline style.\n\n    `bold`\n    :   bold text (text which is strong and similar should preferably be derived from this name).\n\n    `heading`\n    :   a section header. Optionally provide the heading level as the next element, for example markup.heading.2.html for `<h2>…</h2>` in HTML.\n\n    `italic`\n    :   italic text (text which is emphasized and similar should preferably be derived from this name).\n\n    `list`\n    :   list items.\n\n        `numbered`\n        :   numbered list items.\n\n        `unnumbered`\n        :   unnumbered list items.\n\n    `quote`\n    :   quoted (sometimes block quoted) text.\n\n    `raw`\n    :   text which is verbatim, e.g. code listings. Normally spell checking is disabled for markup.raw.\n\n    `other`\n    :   other markup constructs.\n\n`meta`\n:   the meta scope is generally used to markup larger parts of the document. For example the entire line which declares a function would be meta.function and the subsets would be storage.type, entity.name.function, variable.parameter etc. and only the latter would be styled. Sometimes the meta part of the scope will be used only to limit the more general element that is styled, most of the time meta scopes are however used in scope selectors for activation of bundle items. For example in Objective-C there is a meta scope for the interface declaration of a class and the implementation, allowing the same tab-triggers to expand differently, depending on context.\n\n`storage`\n:   things relating to “storage”.\n\n    `type`\n    :   the type of something, class, function, int, var, etc.\n\n    `modifier`\n    :   a storage modifier like static, final, abstract, etc.\n\n`string`\n:   strings.\n\n    `quoted`\n    :   quoted strings.\n\n        `single`\n        :   single quoted strings: 'foo'.\n\n        `double`\n        :   double quoted strings: \"foo\".\n\n        `triple`\n        :   triple quoted strings: \"\"\"Python\"\"\".\n\n        `other`\n        :   other types of quoting: $'shell', %s{...}.\n\n    `unquoted`\n    :   for things like here-docs and here-strings.\n\n    `interpolated`\n    :   strings which are “evaluated”: date, $(pwd).\n\n    `regexp`\n    :   regular expressions: /(\\w+)/.\n\n    `other`\n    :   other types of strings (should rarely be used).\n\n`support`\n:   things provided by a framework or library should be below support.\n\n    `function`\n    :   functions provided by the framework/library. For example NSLog in Objective-C is support.function.\n\n    `class`\n    :   when the framework/library provides classes.\n\n    `type`\n    :   types provided by the framework/library, this is probably only used for languages derived from C, which has typedef (and struct). Most other languages would introduce new types as classes.\n\n    `constant`\n    :   constants (magic values) provided by the framework/library.\n\n    `variable`\n    :   variables provided by the framework/library. For example NSApp in AppKit.\n\n    `other`\n    :   the above should be exhaustive, but for everything else use support.other.\n\n`variable`\n:   variables. Not all languages allow easy identification (and thus markup) of these.\n\n    `parameter`\n    :   when the variable is declared as the parameter.\n\n    `language`\n    :   reserved language variables like this, super, self, etc.\n\n    `other`\n    :   other variables, like $some_variables.\n","url":"https://denizaksimsek.com/2022/textmate-grammars/","summary":"Naming conventions for scopes in TextMate grammars and themes, which are also used in Sublime Text and VSCode.","date_published":"2022-04-16T16:02:47Z"},{"id":"/2022/cloudflare-internal-error/","title":"Cloudflare Pages: Failed: an internal error occurred","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Cloudflare Pages: Failed: an internal error occurred</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Cloudflare Pages: Failed: an internal error occurred\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/cloudflare-internal-error/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Cloudflare Pages: Failed: an internal error occurred</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Cloudflare Pages: Failed: an internal error occurred</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I deploy my website on Cloudflare Pages (at time of writing). My builds were\nfailing with the error message:</p>\n<pre><code>17:24:49.795\tDeploying your site to Cloudflare's global network...\n17:24:50.632\tFailed: an internal error occurred\n</code></pre>\n<p>No other information.</p>\n<p>Googling yields that many things can cause this issue. <!-- TODO: compile -->\nIn my case, it was a <strong>directory called <code>_redirects/</code> in the output folder</strong>.</p>\n<p>Cloudflare expects <code>_redirects</code> to be a file to read redirect information from.\n(Indeed, that’s what I was using it for — it was output to a directory by\naccident).</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-03-25T14:36:06Z\">\n                25 Mar ’22 14:36</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"I deploy my website on Cloudflare Pages (at time of writing). My builds were \nfailing with the error message:\n\n  ~~~\n  17:24:49.795\tDeploying your site to Cloudflare's global network...\n  17:24:50.632\tFailed: an internal error occurred\n  ~~~\n\nNo other information.\n\nGoogling yields that many things can cause this issue. <!-- TODO: compile -->\nIn my case, it was a **directory called `_redirects/` in the output folder**.\n\nCloudflare expects `_redirects` to be a file to read redirect information from.\n(Indeed, that's what I was using it for --- it was output to a directory by \naccident).\n","url":"https://denizaksimsek.com/2022/cloudflare-internal-error/","date_published":"2022-03-25T14:36:06Z"},{"id":"/2022/intentionally-unscalable/","title":"Intentionally Unscalable","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Intentionally Unscalable</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Intentionally Unscalable\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/intentionally-unscalable/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Intentionally Unscalable</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Intentionally Unscalable</h1>\n                \n\n                                                \n        </dl>\n                    <p class=\"italic\">Originally published as a <a href=\"https://gist.github.com/dz4k/c6829e42066a5fba64238a68cf41abd7\" >GitHub Gist</a> on <time>2021-11-22</time></p></p>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>_hyperscript is intentionally unscalable. We make decisions that would be\nobviously inadvisable to anyone looking to make scalable software, and dismiss\nfeatures like reactivity that seem to have proven themselves in this regard. To\nunderstand why, I examine and critique the concept of “scalable”.</p>\n<p><strong>Scalability refers to the ability of a system to handle more work given more\nresources.</strong> This definition matches both formal definitions and common use in\napplication development circles. Given this definition, we can see that:</p>\n<ul>\n<li>\n<p>Scalability is unidirectional — it only refers to scaling up, never\nscaling down. In fact, I never hear “scaling down” discussed at all.\nDeclaring it out-of-scope to ascribe this to a wider culture of growth, I’ll\njust speculate that perhaps scaling down is assumed to be trivial, or not\nnecessary as a system that handles a large amount of work can necessarily\nhandle a small amount, adequately, without modification.</p>\n</li>\n<li>\n<p>When people talk about the scalability of a tool, they are usually employing\nmetonymy.\n<x-sidenote inline=\"\">metonymy: I was excited when looking up this word on\n<a href=\"https://tureng.com/\">Tureng</a>, but “metonymy” doesn’t really have the barebones simplicity of\nthe Turkish “ad aktarması” or the weight of the Arabic loan “mecaz-ı\nmürsel”.</x-sidenote>\nThey are discussing the impact of the tool on other systems’\nscalability (will _hyperscript hinder us as we get more users/our app gets\nmore complex?) and not the scalability of the tool itself (can we get\nhyperscript to run N lines of code in T time with better hardware?)</p>\n</li>\n<li>\n<p>System, work, resources — a software development team consists of multiple\nsystems using multiple kinds of resources to do multiple kinds of work.\nWhich system is being discussed is usually clear through context, but we\nshould be aware when “scalable” is used without such context.</p>\n<table>\n<caption>The two main systems in a software organization</caption>\n<thead>\n<tr>\n<th>System</th>\n<th>More Work</th>\n<th>Resources</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Software</td>\n<td>Serve more users.</td>\n<td>Computers.</td>\n</tr>\n<tr>\n<td>Team</td>\n<td>Add complexity to the software and maintain it.</td>\n<td>Time, money.</td>\n</tr>\n<tr>\n<td class=\"responsive-table\"></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n<h2>Why is scalability desired?</h2>\n<ol>\n<li>\n<p>“As our app gets more complex, X will prevent us from growing and\nmaintaining it”.</p>\n<p>There is an assumption that your app <em>will</em> get more complex and partly that\ncomplexity is an environmental factor, as opposed to a consequence of the\nteam’s choices. Growing a codebase is adding complexity. Lamenting not being\nable to do that seems odd given the amount of lip service we pay to the\nglory of removing complexity.</p>\n</li>\n<li>\n<p>“As we get more users, the app we built with X will need rewriting”.</p>\n<p>There is an assumption that we <em>will</em> get more users. If this is true, and\nthe organization containing the team has an actual monetization\nstrategy, then it follows that we will also get more money.\n<x-sidenote inline=\"\"><strong>monetization:</strong> The way that the “we’ll need rewriting”\nargument assumes growth, but rejects relying on it reminds me of paying\npeople in shares. If you can’t trust it enough to just pay me the money\nyou’ll <i class=\"sarc\">definitely</i> make back, why should I?</x-sidenote>\nIf we need to build an app that serves N users eventually, does it not make\nsense to do so when you have F(N-ε) dollars?\n<x-sidenote inline=\"\"><strong>epsilon:</strong> I originally said this much more pithily: “if you\nare certain you’ll grow to 1 billion users, and you need an app that can\nhandle that, why build it now on a startup budget when you’re going to have\nthe budget of a 1B-user app soon”</x-sidenote>\nMy more business-savvy friend <a href=\"https://twitter.com/benpate5280\">Ben Pate</a> informs me that “many\nmillion-user budgets have been wasted doing that [building a million-user\nsystem before you have any users at all]”, and that “You won’t get a billion\nusers until you first earn a thousand”.</p>\n<p>I don’t know shit about running a company, but I reject designing our tools\nfor startups that eat some VC money, don’t generate profit <em>or</em> any benefit\nfor humanity, and get sold for the advertising value of their data.</p>\n<p>And what’s so bad about rewriting anyway? I thought programmers liked\nwriting code.\n<x-sidenote inline=\"\"><strong>liked writing code:</strong> I’m aware that some people do\nprogramming purely as a job. I sometimes envy the indifference I imagine\nthey have towards our petty squabbles.</x-sidenote>\nWe again pay lip service to how code is the easy part, and\nhow legacy code sucks… Erlang has the famous “let it crash”, anticipating\nissues and focusing on recovering from failure instead of preventing it. We\ncan apply a similar concept: <strong>“write to rewrite”</strong>.</p>\n</li>\n</ol>\n<h2>Write to rewrite</h2>\n<p>Expect that you might need to rewrite your code, and be considerate of your\nfuture self who is doing that. _hyperscript helps you do this in two ways:</p>\n<ul>\n<li>\n<p><strong>Readability over writability.</strong> The fact that _hyperscript code examples\nlook like plain English is just as much an achievement of the author as it\nis _hyperscript’s. We do not do anything smart like Natural Language\nProcessing; _hyperscript uses common and normal parsing techniques. C++,\nPerl, perhaps even Ruby have more complex grammars than _hyperscript. We\ngive you tools like the <code>prop of object</code> and <code>object's prop</code> syntax. and the\n<code>it</code> variable that might seem magical but is actually little more than the\naccumulator register in an 8-bit processor. It is up to you the programmer\nto use these tools to create readable code. The burden you take upon your\nshoulders can include (but will not be limited to):</p>\n<ul>\n<li>Choosing between <code>rates of the result's data</code>, <code>its data.rates</code>, or\n<code>result.data.rates</code>.</li>\n<li>Breaking up long expressions into statements. Besides readability and\nEnglish-like flow, this is also good for stepping through with a\ndebugger (yet another tool for a rewriter to understand the code).</li>\n<li>Reordering statements to make efficient use of <code>it</code>. If we do this\nconsistently, code that doesn’t do this will stick out as\norder-dependent and vulnerable to race conditions.</li>\n</ul>\n</li>\n<li>\n<p><strong>Locality of behavior.</strong> Ever looked through a GitHub repo, not been able\nto find the code you were looking for, eventually clone the repo locally and\nstart “Jump to Definition”-ing and “Peek References”-ing your way towards\nyour target, only to find out it’s a wrapper for yet another function? If it\nis a Java project, I usually give up before reaching any source code files\nat all. Htmx “Carson” Org has written about <a href=\"https://htmx.org/essays/locality-of-behaviour\">Locality of Behavior</a>\npreviously, so I’ll direct you there.</p>\n</li>\n</ul>\n<h2>Conclusion</h2>\n<p>I recall what I now know to be an urban legend about the great architect Mimar\nSinan. Supposedly, when the keystones of some arches in a mosque he built were\nyielding to old age, an engineer working on the restorations found a note in a\nbottle signed by Koca Mimar Sinan Ağa himself. It was a step-by-step guide on\nhow to replace the keystone without disruption to the rest of the structure,\ncomplete with drawings.</p>\n<p>Putting aside the implication that our modern architects don’t know how stone\narches are built, and that Great Sinan Agha The Architect expected such\narchitects to restore his works, this fictional Mimar Sinan clearly doesn’t know\nanything about scalability. If he did, he would predict when the Sultan would\ndie and make the mosque last exactly that long.</p>\n<blockquote>\n<p>When asked what their language is good for, many designers would say\n“everything” which really means “nothing”.</p>\n<p>– Robert Nystrom, <cite><a href=\"http://journal.stuffwithstuff.com/2010/07/23/what-i-learned-at-the-emerging-languages-camp/\">What I Learned at the Emerging Languages Camp</a></cite></p>\n</blockquote>\n<p>Nystrom said this in the context of choosing a niche for your language, but I\nbelieve it applies just as well to choosing a scale.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-02-06T14:52:45Z\">\n                06 Feb ’22 14:52</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"\\_hyperscript is intentionally unscalable. We make decisions that would be\nobviously inadvisable to anyone looking to make scalable software, and dismiss\nfeatures like reactivity that seem to have proven themselves in this regard. To\nunderstand why, I examine and critique the concept of \"scalable\".\n\n**Scalability refers to the ability of a system to handle more work given more\nresources.** This definition matches both formal definitions and common use in\napplication development circles. Given this definition, we can see that:\n\n*   Scalability is unidirectional --- it only refers to scaling up, never\n\tscaling down. In fact, I never hear \"scaling down\" discussed at all.\n\tDeclaring it out-of-scope to ascribe this to a wider culture of growth, I'll\n\tjust speculate that perhaps scaling down is assumed to be trivial, or not\n\tnecessary as a system that handles a large amount of work can necessarily\n\thandle a small amount, adequately, without modification.\n*   When people talk about the scalability of a tool, they are usually employing\n\tmetonymy. \n\t:sidenote[metonymy: I was excited when looking up this word on \n\t[Tureng][], but \"metonymy\" doesn't really have the barebones simplicity of \n\tthe Turkish \"ad aktarması\" or the weight of the Arabic loan \"mecaz-ı \n\tmürsel\".]\n\tThey are discussing the impact of the tool on other systems'\n\tscalability (will _hyperscript hinder us as we get more users/our app gets\n\tmore complex?) and not the scalability of the tool itself (can we get\n\thyperscript to run N lines of code in T time with better hardware?)\n*   System, work, resources --- a software development team consists of multiple\n\tsystems using multiple kinds of resources to do multiple kinds of work.\n\tWhich system is being discussed is usually clear through context, but we\n\tshould be aware when \"scalable\" is used without such context.\n\n\t| System   | More Work                                       | Resources       |\n\t|----------|-------------------------------------------------|-----------------|\n\t| Software | Serve more users.                               | Computers.      |\n\t| Team     | Add complexity to the software and maintain it. | Time, money.    |\n\t{.responsive-table}\n\t\n\t: The two main systems in a software organization\n\n\n## Why is scalability desired?\n\n1.  \"As our app gets more complex, X will prevent us from growing and\n\tmaintaining it\".\n\n    There is an assumption that your app _will_ get more complex and partly that\n    complexity is an environmental factor, as opposed to a consequence of the\n    team's choices. Growing a codebase is adding complexity. Lamenting not being\n    able to do that seems odd given the amount of lip service we pay to the\n    glory of removing complexity.\n\n2.\t\"As we get more users, the app we built with X will need rewriting\".\n\n\tThere is an assumption that we _will_ get more users. If this is true, and\n\tthe organization containing the team has an actual monetization\n\tstrategy, then it follows that we will also get more money.\n\t:sidenote[**monetization:** The way that the \"we'll need rewriting\" \n\targument assumes growth, but rejects relying on it reminds me of paying \n\tpeople in shares. If you can't trust it enough to just pay me the money \n\tyou'll <i class=\"sarc\">definitely</i> make back, why should I?]\n\tIf we need to build an app that serves N users eventually, does it not make \n\tsense to do so when you have F(N-&epsilon;) dollars?\n\t:sidenote[**epsilon:** I originally said this much more pithily: \"if you\n\tare certain you'll grow to 1 billion users, and you need an app that can \n\thandle that, why build it now on a startup budget when you're going to have\n\tthe budget of a 1B-user app soon\"]\n\tMy more business-savvy friend [Ben Pate][] informs me that \"many \n\tmillion-user budgets have been wasted doing that [building a million-user \n\tsystem before you have any users at all]\", and that \"You won’t get a billion\n\tusers until you first earn a thousand\".\n\n\tI don't know shit about running a company, but I reject designing our tools\n\tfor startups that eat some VC money, don't generate profit _or_ any benefit\n\tfor humanity, and get sold for the advertising value of their data.\n\n\tAnd what's so bad about rewriting anyway? I thought programmers liked\n\twriting code.\n\t:sidenote[**liked writing code:** I'm aware that some people do \n\tprogramming purely as a job. I sometimes envy the indifference I imagine \n\tthey have towards our petty squabbles.]\n\tWe again pay lip service to how code is the easy part, and\n\thow legacy code sucks... Erlang has the famous \"let it crash\", anticipating\n\tissues and focusing on recovering from failure instead of preventing it. We\n\tcan apply a similar concept: **\"write to rewrite\"**.\n\n\n## Write to rewrite\n\nExpect that you might need to rewrite your code, and be considerate of your\nfuture self who is doing that. _hyperscript helps you do this in two ways:\n\n-\t**Readability over writability.** The fact that _hyperscript code examples\n\tlook like plain English is just as much an achievement of the author as it\n\tis _hyperscript's. We do not do anything smart like Natural Language\n\tProcessing; _hyperscript uses common and normal parsing techniques. C++,\n\tPerl, perhaps even Ruby have more complex grammars than _hyperscript. We\n\tgive you tools like the `prop of object` and `object's prop` syntax. and the\n\t`it` variable that might seem magical but is actually little more than the\n\taccumulator register in an 8-bit processor. It is up to you the programmer\n\tto use these tools to create readable code. The burden you take upon your\n\tshoulders can include (but will not be limited to):\n    -   Choosing between `rates of the result's data`, `its data.rates`, or\n    \t`result.data.rates`.\n    -\tBreaking up long expressions into statements. Besides readability and\n    \tEnglish-like flow, this is also good for stepping through with a\n    \tdebugger (yet another tool for a rewriter to understand the code).\n    -\tReordering statements to make efficient use of `it`. If we do this\n    \tconsistently, code that doesn't do this will stick out as\n    \torder-dependent and vulnerable to race conditions.\n\n-\t**Locality of behavior.** Ever looked through a GitHub repo, not been able\n\tto find the code you were looking for, eventually clone the repo locally and\n\tstart \"Jump to Definition\"-ing and \"Peek References\"-ing your way towards\n\tyour target, only to find out it's a wrapper for yet another function? If it\n\tis a Java project, I usually give up before reaching any source code files\n\tat all. Htmx \"Carson\" Org has written about [Locality of Behavior][]\n\tpreviously, so I'll direct you there.\n\n\n## Conclusion\n\nI recall what I now know to be an urban legend about the great architect Mimar\nSinan. Supposedly, when the keystones of some arches in a mosque he built were\nyielding to old age, an engineer working on the restorations found a note in a\nbottle signed by Koca Mimar Sinan Ağa himself. It was a step-by-step guide on\nhow to replace the keystone without disruption to the rest of the structure,\ncomplete with drawings.\n\nPutting aside the implication that our modern architects don't know how stone\narches are built, and that Great Sinan Agha The Architect expected such\narchitects to restore his works, this fictional Mimar Sinan clearly doesn't know\nanything about scalability. If he did, he would predict when the Sultan would\ndie and make the mosque last exactly that long.\n\n> When asked what their language is good for, many designers would say\n> “everything” which really means “nothing”.\n>\n> -- Robert Nystrom, <cite>[What I Learned at the Emerging Languages Camp](http://journal.stuffwithstuff.com/2010/07/23/what-i-learned-at-the-emerging-languages-camp/)</cite>\n\nNystrom said this in the context of choosing a niche for your language, but I\nbelieve it applies just as well to choosing a scale.\n\n[Ben Pate]: https://twitter.com/benpate5280\n\n[Locality of Behavior]: https://htmx.org/essays/locality-of-behaviour\n\n[Tureng]: https://tureng.com\n\n\n","url":"https://denizaksimsek.com/2022/intentionally-unscalable/","date_published":"2022-02-06T14:52:45Z"},{"id":"/2022/nanpa-pi-ken-ala-kipisi/","title":"nanpa pi ken ala kipisi pi mute ale li lon","content_html":"<!DOCTYPE html>\n<html lang=\"tok\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>nanpa pi ken ala kipisi pi mute ale li lon</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"nanpa pi ken ala kipisi pi mute ale li lon\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2022/nanpa-pi-ken-ala-kipisi/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>nanpa pi ken ala kipisi pi mute ale li lon</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/tok\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\">jan Matelo</span>\n                        <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">nanpa pi ken ala kipisi pi mute ale li lon</h1>\n                \n\n                                                \n        </dl>\n                    <p class=\"italic\">ni li tan mi ala. mi sona ala e ni: jan seme li pali e ni.</p>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>tawa kama lon e ni, o sitelen insa e ijo jasima: nanpa pi ken ala kipisi mute pini li lon. o nimi e kulupu pi nanpa ni kepeken sitelen <var>P</var>.</p>\n<p>ni la o mute e nanpa ale pi kulupu <var>P</var>, o namako e wan. (ni li ken tan ni: kulupu <var>P</var>li suli pini). o nimi e nanpa sin ni kepeken sitelen <var>N</var>.</p>\n<p>nanpa <var>N</var>la ken tu li lon: ona li nanpa pi ken ala kipisi, anu ona li ijo tan mute pi nanpa ante pi ken ala kipisi.</p>\n<p>ken nanpa wan li ken ala tan ni: nanpa <var>N</var> li lon ala kulupu <var>P</var>. (nanpa tan kulupu <var>P</var>, la nanpa <var>N</var> li suli mute). taso ,ona li nanpa pi ken ala kipisi. kulupu <var>P</var> li wile jo e nanpa ale pi ken ala kipisi, la ni li ken ala.</p>\n<p>tenpo ni la o sitelen insa e ken nanpa tu. nanpa ni li wile lon: ona li nanpa pi ken ala kipisi, ona li ken kipisi e nanpa <var>N</var>. o nimi e ona kepeken sitelen <var>L</var>. o sona e ni: nanpa pi kulupu <var>P</var> li ken ala kipisi e nanpa <var>N</var>. (nanpa <var>p</var> li lon kulupu <var>P</var> la ona li lukin kipisi e nanpa <var>N</var> la wan li ante). tan ni, la nanpa <var>L</var> li ken ala lon kulupu <var>P</var>. taso ,ona li nanpa pi ken ala kipisi. sama tenpo pini, la ni li ken ala.</p>\n<p>ale ni, la kulupu <var>P</var> li suli pini, la ale li ijo pakala pi ken ala. tan ni, la kulupu <var>P</var> li wile kulupu pi  suli ale.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2022-01-14T21:47:00Z\">\n                &nbsp;sike 22 mun 01 suno 14 tenpo 2147</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"tawa kama lon e ni, o sitelen insa e ijo jasima: nanpa pi ken ala kipisi mute pini li lon. o nimi e kulupu pi nanpa ni kepeken sitelen <var>P</var>.\n\nni la o mute e nanpa ale pi kulupu <var>P</var>, o namako e wan. (ni li ken tan ni: kulupu <var>P</var>li suli pini). o nimi e nanpa sin ni kepeken sitelen <var>N</var>.\n\nnanpa <var>N</var>la ken tu li lon: ona li nanpa pi ken ala kipisi, anu ona li ijo tan mute pi nanpa ante pi ken ala kipisi.\n\nken nanpa wan li ken ala tan ni: nanpa <var>N</var> li lon ala kulupu <var>P</var>. (nanpa tan kulupu <var>P</var>, la nanpa <var>N</var> li suli mute). taso ,ona li nanpa pi ken ala kipisi. kulupu <var>P</var> li wile jo e nanpa ale pi ken ala kipisi, la ni li ken ala.\n\ntenpo ni la o sitelen insa e ken nanpa tu. nanpa ni li wile lon: ona li nanpa pi ken ala kipisi, ona li ken kipisi e nanpa <var>N</var>. o nimi e ona kepeken sitelen <var>L</var>. o sona e ni: nanpa pi kulupu <var>P</var> li ken ala kipisi e nanpa <var>N</var>. (nanpa <var>p</var> li lon kulupu <var>P</var> la ona li lukin kipisi e nanpa <var>N</var> la wan li ante). tan ni, la nanpa <var>L</var> li ken ala lon kulupu <var>P</var>. taso ,ona li nanpa pi ken ala kipisi. sama tenpo pini, la ni li ken ala.\n\nale ni, la kulupu <var>P</var> li suli pini, la ale li ijo pakala pi ken ala. tan ni, la kulupu <var>P</var> li wile kulupu pi  suli ale.\n","url":"https://denizaksimsek.com/2022/nanpa-pi-ken-ala-kipisi/","date_published":"2022-01-14T21:47:00Z"},{"id":"/2021/vstlbx/","title":"VSCode/Toolbx","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>VSCode/Toolbx</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"VSCode/Toolbx\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/vstlbx/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>VSCode/Toolbx</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">VSCode/Toolbx</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>A GUI script to run VSCode in a toolbox container.</p>\n<hr>\n<p>Recently, I switched to [Fedora Silverblue] as my desktop operating system. To give a basic summary, Silverblue is an operating system where the system files are immutable, and can be restored to a previous state at any time. This version of Fedora does not ship the package manager <code>dnf</code>. Instead, you are expected to install applications in one of three ways:</p>\n<ul>\n<li>\n<p>[Flatpak]. These are applications with Android-like isolation and permission management capabilities.</p>\n<p>Flatpaks are nice, but there are still some rough edges (especially with less popular applications, where the packaging is not as well-maintained) and limitations (the VSCode terminal runs inside a container, and it’s pretty inconvenient to use any of your tools).</p>\n</li>\n<li>\n<p>[RPM-OSTree] allows you to “layer” packages onto the immutable image. The package installation creates a new image such that you can restore to a previous state of your system at any point.</p>\n<p>While it works, layering packages all the time is not ideal, and best reserved for things that don’t work any other way. The docs give the <code>fish</code> shell as an example. I used it for a package that the internet told me to install to make Eduroam work. Layered packages don’t take effect until a reboot.</p>\n</li>\n<li>\n<p>[Toolboxes]! Toolbox, or Toolbx as it’s been recently renamed[^1], is a tool for setting up development containers really easily.</p>\n<p>You type something to the effect of <code>toolbox create my-project-container</code>, and you have a place where you can <code>sudo</code> without password, install whatever package with <code>dnf</code>, just trash the place. The containers use your real home directory, can access USB devices, and other things that <i>just work</i>. If anything goes wrong, you can just create a new container and start over.</p>\n</li>\n</ul>\n<h2>Toolbx</h2>\n<p>I set up toolboxes for a few projects to test the waters:</p>\n<ul>\n<li><code>www</code>: This website</li>\n<li><code>_hyperscript</code>: The hypermedia programming language</li>\n<li><code>this-week-in-htmx</code>: The weekly blog that I was running late on at the time. Sorry…</li>\n</ul>\n<p>Great, I’m all set up!</p>\n<p>So… How do I develop in here?</p>\n<p>Some DDGing led me to the amazing [<code>toolbox-vscode</code>] script. It gives you a script that you can place into <code>~/.local/bin/code</code>. The first time you run it, it sets up everything for connecting to the container via VSCode’s remote feature, and launches it. The setup needs to be performed once per container, which wasn’t the most convenient, but oh well.</p>\n<h2>In which I have to have my GUIs</h2>\n<p>Previously, I would launch VSCode and pick my project from the <kbd>Open Recent</kbd> menu. The flow of</p>\n<ul>\n<li>opening a terminal</li>\n<li>entering the toolbox</li>\n<li>entering the project folder</li>\n<li><kbd>code .</kbd></li>\n<li>now I can code, but I’m left with a useless terminal open</li>\n</ul>\n<p>was far too janky. What I wanted was:</p>\n<ul>\n<li>launch an app</li>\n<li>pick my project from a list</li>\n<li>VSCode is open to the right toolbox and project directory, and the list is out of my way</li>\n</ul>\n<p>After some relearning Bash and overengineering later, I have a script in my hands.</p>\n<details><summary><code>vstlbx</code></summary>\n<pre><code class=\"language-bash\">#!/usr/bin/env bash\n\n# Depends on: bash zenity\n\nset -e\n\nTitle=&quot;VS Toolbx&quot;\nText=&quot;Select a container to open in VS Code:&quot;\n\n## list-containers &gt; containers\nlist-containers() {\n\ttoolbox list --containers | tail -n +2 | tr -s ' +' &quot;\\t&quot; | cut -f 2\n}\n\n## containers | user-pick-container &gt; container\nuser-pick-container() {\n\tzenity \\\n\t\t--title &quot;$Title&quot; \\\n\t\t--text &quot;$Text&quot; \\\n\t\t--list \\\n  \t--column 'Name' \\\n  \t--hide-header \\\n  \t2&gt;/dev/null\n}\n\n## get-project-dir container &gt; project\nget-project-dir() {\n\tif [ &quot;$1&quot; == &quot;hyperscript&quot; ]; then\n\t\techo &quot;$HOME/Projects/_hyperscript&quot;\n\telse\n\t\techo &quot;$HOME/Projects/$1&quot;\n\tfi\n}\n\n## run-container container\nrun-container() {\n\ttoolbox run --container &quot;$1&quot; -- $(which code) $(get-project-dir &quot;$1&quot;)\n}\n\n## run-ui\nrun-ui() {\n\tcontainer=$(list-containers | user-pick-container)\n\trun-container &quot;$container&quot;\n}\n\ninstall-desktop-file() {\n\tdesktop_file=&quot;$HOME/.local/share/applications/com.dz4k.vstlbx.desktop&quot;\n\tcat &lt;&lt;-EOF &gt;&quot;$desktop_file&quot;\n\t\t[Desktop Entry]\n\t\tType=Application\n\t\tName[en_US]=VS/Toolbx\n\t\tCategories=Development;\n\t\tX-GNOME-FullName[en_US]=VS/Toolbx\n\t\tComment[en_US.UTF-8]=Attach VSCode to toolboxes\n\t\tNoDisplay=false\n\t\tExec=/var/home/deniz/Applications/vstlbx\n\t\tPath=.\n\t\tTerminal=false\n\t\tX-GNOME-UsesNotifications=false\n  \tStartupWMClass=zenity\n\t\tName[en_US.UTF-8]=VS/Toolbx\n\t\tX-GNOME-FullName[en_US.UTF-8]=VS/Toolbx\n\tEOF\n}\n\nif [ &quot;$#&quot; == 0 ]; then\n\trun-ui\n\texit 0\nfi\n\nwhile [ &quot;$#&quot; -gt 0 ]; do\n\tcase &quot;$1&quot; in\n\t\t'install-desktop')\n\t\t\tinstall-desktop-file\n\t\t;;\n\t\t'container')\n\t\t\trun-container &quot;$2&quot;\n\t\t\tshift\n\t\t;;\n\t\t'ui')\n\t\t\trun-ui\n\t\t;;\n\tesac\n\tshift\ndone\n</code></pre>\n<p><small>who needs Gists anyway</small></p>\n</details>\n<p>Here’s what the UI looks like:</p>\n<p><img src=\"/assets/photos/vstlbx.png\" alt=\"A dialog with a list of container names: fedora-toolbox-35, hyperscript, this=week-in-htmx, www\"></p>\n<p>I’m still getting myself set up, but I’m happy that I figured this part out early on.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-11-17T20:53:00Z\">\n                17 Nov ’21 20:53</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"A GUI script to run VSCode in a toolbox container.\n\n***\n\nRecently, I switched to [Fedora Silverblue] as my desktop operating system. To give a basic summary, Silverblue is an operating system where the system files are immutable, and can be restored to a previous state at any time. This version of Fedora does not ship the package manager `dnf`. Instead, you are expected to install applications in one of three ways:\n\n-   [Flatpak]. These are applications with Android-like isolation and permission management capabilities.\n\n    Flatpaks are nice, but there are still some rough edges (especially with less popular applications, where the packaging is not as well-maintained) and limitations (the VSCode terminal runs inside a container, and it's pretty inconvenient to use any of your tools).\n\n-   [RPM-OSTree] allows you to \"layer\" packages onto the immutable image. The package installation creates a new image such that you can restore to a previous state of your system at any point.\n\n    While it works, layering packages all the time is not ideal, and best reserved for things that don't work any other way. The docs give the `fish` shell as an example. I used it for a package that the internet told me to install to make Eduroam work. Layered packages don't take effect until a reboot.\n\n-   [Toolboxes]! Toolbox, or Toolbx as it's been recently renamed[^1], is a tool for setting up development containers really easily.\n\n    You type something to the effect of `toolbox create my-project-container`, and you have a place where you can `sudo` without password, install whatever package with `dnf`, just trash the place. The containers use your real home directory, can access USB devices, and other things that <i>just work</i>. If anything goes wrong, you can just create a new container and start over.\n\n## Toolbx\n\nI set up toolboxes for a few projects to test the waters:\n\n- `www`: This website\n- `_hyperscript`: The hypermedia programming language\n- `this-week-in-htmx`: The weekly blog that I was running late on at the time. Sorry...\n\nGreat, I'm all set up!\n\nSo... How do I develop in here?\n\nSome DDGing led me to the amazing [`toolbox-vscode`] script. It gives you a script that you can place into `~/.local/bin/code`. The first time you run it, it sets up everything for connecting to the container via VSCode's remote feature, and launches it. The setup needs to be performed once per container, which wasn't the most convenient, but oh well.\n\n## In which I have to have my GUIs\n\nPreviously, I would launch VSCode and pick my project from the <kbd>Open Recent</kbd> menu. The flow of\n\n- opening a terminal\n- entering the toolbox\n- entering the project folder\n- <kbd>code .</kbd>\n- now I can code, but I'm left with a useless terminal open\n\nwas far too janky. What I wanted was:\n\n- launch an app\n- pick my project from a list\n- VSCode is open to the right toolbox and project directory, and the list is out of my way\n\nAfter some relearning Bash and overengineering later, I have a script in my hands.\n\n<details><summary><code>vstlbx</code></summary>\n\n  ~~~ bash\n  #!/usr/bin/env bash\n\n  # Depends on: bash zenity\n\n  set -e\n\n  Title=\"VS Toolbx\"\n  Text=\"Select a container to open in VS Code:\"\n\n  ## list-containers > containers\n  list-containers() {\n  \ttoolbox list --containers | tail -n +2 | tr -s ' +' \"\\t\" | cut -f 2\n  }\n\n  ## containers | user-pick-container > container\n  user-pick-container() {\n  \tzenity \\\n  \t\t--title \"$Title\" \\\n  \t\t--text \"$Text\" \\\n  \t\t--list \\\n \t\t--column 'Name' \\\n \t\t--hide-header \\\n \t\t2>/dev/null\n  }\n\n  ## get-project-dir container > project\n  get-project-dir() {\n  \tif [ \"$1\" == \"hyperscript\" ]; then\n  \t\techo \"$HOME/Projects/_hyperscript\"\n  \telse\n  \t\techo \"$HOME/Projects/$1\"\n  \tfi\n  }\n\n  ## run-container container\n  run-container() {\n  \ttoolbox run --container \"$1\" -- $(which code) $(get-project-dir \"$1\")\n  }\n\n  ## run-ui\n  run-ui() {\n  \tcontainer=$(list-containers | user-pick-container)\n  \trun-container \"$container\"\n  }\n\n  install-desktop-file() {\n  \tdesktop_file=\"$HOME/.local/share/applications/com.dz4k.vstlbx.desktop\"\n  \tcat <<-EOF >\"$desktop_file\"\n  \t\t[Desktop Entry]\n  \t\tType=Application\n  \t\tName[en_US]=VS/Toolbx\n  \t\tCategories=Development;\n  \t\tX-GNOME-FullName[en_US]=VS/Toolbx\n  \t\tComment[en_US.UTF-8]=Attach VSCode to toolboxes\n  \t\tNoDisplay=false\n  \t\tExec=/var/home/deniz/Applications/vstlbx\n  \t\tPath=.\n  \t\tTerminal=false\n  \t\tX-GNOME-UsesNotifications=false\n \t\tStartupWMClass=zenity\n  \t\tName[en_US.UTF-8]=VS/Toolbx\n  \t\tX-GNOME-FullName[en_US.UTF-8]=VS/Toolbx\n  \tEOF\n  }\n\n  if [ \"$#\" == 0 ]; then\n  \trun-ui\n  \texit 0\n  fi\n\n  while [ \"$#\" -gt 0 ]; do\n  \tcase \"$1\" in\n  \t\t'install-desktop')\n  \t\t\tinstall-desktop-file\n  \t\t;;\n  \t\t'container')\n  \t\t\trun-container \"$2\"\n  \t\t\tshift\n  \t\t;;\n  \t\t'ui')\n  \t\t\trun-ui\n  \t\t;;\n  \tesac\n  \tshift\n  done\n  ~~~\n\n <small>who needs Gists anyway</small>\n\n</details>\n\nHere's what the UI looks like:\n\n![A dialog with a list of container names: fedora-toolbox-35, hyperscript, this=week-in-htmx, www](/assets/photos/vstlbx.png)\n\nI'm still getting myself set up, but I'm happy that I figured this part out early on.\n","url":"https://denizaksimsek.com/2021/vstlbx/","date_published":"2021-11-17T20:53:00Z"},{"id":"/2021/sprinkle-sharing/","title":"Sprinkle Sharing","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Sprinkle Sharing</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Sprinkle Sharing\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/sprinkle-sharing/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Sprinkle Sharing</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Sprinkle Sharing</h1>\n                \n\n                                                \n        </dl>\n                    <p class=\"italic\"><p><em>This post is directly from my notes, with no edits.</em></p>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>When people talk about code sharing between server and client, the usual suspects are Node.js and sharing model classes. However, there's often good reason to duplicate these.\n<p>A far more interesting type of code sharing to me is eliminating this duplication:\n<p>When you are writing a server-driven web app with \"sprinkles\" of JS, you want to have interactive components, but also deliver usable HTML to non-JS-enabled<sup><a class=\"fnref\" href=\"#fn1\">1</a></sup> clients. This means you are essentially writing a component with the first render and subsequent updates are written in different languages -- HTML then JS. How to make this nicer? (Existing answers follow)\n<h2>Alpine and similar</h2>\n<p>Write both in one language: \"HTML with JS inline in special attributes\".\n<h2>React and similar</h2>\n<p>Write both in one language: \"JS optionally with JSX\". Hard to set up sprinkle style, but wasn't this how it was originally created to be used?\n<ol>\n<li id=\"fn1\">\"Non-JS-enabled\" includes not only browsers where the user turned off JS, but also times when the JS didn't load, or errored due to use of unsupported modern features.\n</ol>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-09-02T19:41:00Z\">\n                02 Sep ’21 19:41</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<p>When people talk about code sharing between server and client, the usual suspects are Node.js and sharing model classes. However, there's often good reason to duplicate these.\n\n<p>A far more interesting type of code sharing to me is eliminating this duplication:\n\n<p>When you are writing a server-driven web app with \"sprinkles\" of JS, you want to have interactive components, but also deliver usable HTML to non-JS-enabled<sup><a class=\"fnref\" href=\"#fn1\">1</a></sup> clients. This means you are essentially writing a component with the first render and subsequent updates are written in different languages -- HTML then JS. How to make this nicer? (Existing answers follow)\n\n<h2>Alpine and similar</h2>\n\n<p>Write both in one language: \"HTML with JS inline in special attributes\".\n\n<h2>React and similar</h2>\n\n<p>Write both in one language: \"JS optionally with JSX\". Hard to set up sprinkle style, but wasn't this how it was originally created to be used?\n\n<ol>\n<li id=\"fn1\">\"Non-JS-enabled\" includes not only browsers where the user turned off JS, but also times when the JS didn't load, or errored due to use of unsupported modern features.\n</ol>\n\n","url":"https://denizaksimsek.com/2021/sprinkle-sharing/","date_published":"2021-09-02T19:41:00Z"},{"id":"/2021/sokaktaki-ankara/","title":"Sokaktaki Ankara: Tabela Tasarım Yarışması","content_html":"<!DOCTYPE html>\n<html lang=\"tr\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Sokaktaki Ankara: Tabela Tasarım Yarışması</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Sokaktaki Ankara: Tabela Tasarım Yarışması\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/sokaktaki-ankara/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/ank-tabela-a.jpg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Sokaktaki Ankara: Tabela Tasarım Yarışması</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/tr\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Sokaktaki Ankara: Tabela Tasarım Yarışması</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><strong><time>2021-09-08</time>:</strong> <a href=\"https://twitter.com/ankarabbld/status/1433505785573777414\">Kırmızı kazandı! (twitter)</a></p>\n<p>Ankara Büyükşehir Belediyesi sokak tabelalarının yenilenmesi için <a href=\"https://sokaktakiankara.ankara.bel.tr/\">yarışma</a> düzenledi. Halk oylamasına sunulmak üzere üç tasarım seçildi. Siz de Başkent Mobil uygulamasının <kbd><samp>Söz Hakkı</samp></kbd> bölümünden oy verebilirsiniz.</p>\n<p>Ben oyumu çoktan Eser A için kullandım. Büyük yazı kullanılmasını, metro, otobüs durağı gibi yerlerin gösterilmesini ve mimari eser künyelerinin çizim içermesini beğendim. Eser B’nin fazla dikkat çektiğini ve modası geçecek tasarım ögeleri kullandığını düşünüyorum. Eser C ise gereksiz öğelerle dolu: kimsenin göremeyeceği küçük haritalar ve kocaman “ankara” yazısı.\n<x-sidenote inline=\"\">Eser C’deki gibi bir harita, <a href=\"https://en.wikipedia.org/wiki/Legible_London\">Legible London</a> tarzı bir haritada çok daha kullanışlı olabilir.</x-sidenote></p>\n<x-fig src=\"/assets/photos/ank-tabela-a.jpg\" title=\"Eser A sokak tabelası.\"><img src=\"/assets/photos/ank-tabela-a.jpg\" alt=\"\"></x-fig>\n<p><a href=\"https://sokaktakiankara.ankara.bel.tr/sartnameekler/tabelafontyarismasartnamesi.pdf\">Yarışma şartnamesinde</a> tabelaların <i>İşçi Blokları Mahallesi, Gazi Mustafa Kemal Bulvarı, Balıkçı Caddesi, Alâ Sokak, 1923. Sokak</i> ile test edilmesi söylenmiş. Bence <a href=\"https://www.openstreetmap.org/way/25211791\">Bangabandhu Şeyh Mucibur Rahman Bulvarı</a>da eklenmeliydi.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-08-29T19:07:59Z\">\n                29 Ağu ’21 19:07</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"**<time>2021-09-08</time>:** [Kırmızı kazandı! (twitter)](https://twitter.com/ankarabbld/status/1433505785573777414)\n\nAnkara Büyükşehir Belediyesi sokak tabelalarının yenilenmesi için [yarışma][] düzenledi. Halk oylamasına sunulmak üzere üç tasarım seçildi. Siz de Başkent Mobil uygulamasının <kbd><samp>Söz Hakkı</samp></kbd> bölümünden oy verebilirsiniz.\n\nBen oyumu çoktan Eser A için kullandım. Büyük yazı kullanılmasını, metro, otobüs durağı gibi yerlerin gösterilmesini ve mimari eser künyelerinin çizim içermesini beğendim. Eser B'nin fazla dikkat çektiğini ve modası geçecek tasarım ögeleri kullandığını düşünüyorum. Eser C ise gereksiz öğelerle dolu: kimsenin göremeyeceği küçük haritalar ve kocaman \"ankara\" yazısı.\n:sidenote[Eser C'deki gibi bir harita, [Legible London][] tarzı bir haritada çok daha kullanışlı olabilir.]\n\n:: fig [![](<%= it.photo.src %>)] (<%= it.photo.src %> \"Eser A sokak tabelası.\")\n\n[Yarışma şartnamesinde][şartname] tabelaların <i>İşçi Blokları Mahallesi, Gazi Mustafa Kemal Bulvarı, Balıkçı Caddesi, Alâ Sokak, 1923. Sokak</i> ile test edilmesi söylenmiş. Bence [Bangabandhu Şeyh Mucibur Rahman Bulvarı][BŞM Rahman]da eklenmeliydi.\n\n[yarışma]: https://sokaktakiankara.ankara.bel.tr/\n[Legible London]: https://en.wikipedia.org/wiki/Legible_London\n[şartname]: https://sokaktakiankara.ankara.bel.tr/sartnameekler/tabelafontyarismasartnamesi.pdf\n[BŞM Rahman]: https://www.openstreetmap.org/way/25211791\n","url":"https://denizaksimsek.com/2021/sokaktaki-ankara/","image":"https://denizaksimsek.com/assets/photos/ank-tabela-a.jpg","date_published":"2021-08-29T19:07:59Z"},{"id":"/2021/kalama-lakimosa/","title":"kalama [_la_a_kute_ijo_moli_o_sona_a]","content_html":"<!DOCTYPE html>\n<html lang=\"tok\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>kalama [_la_a_kute_ijo_moli_o_sona_a]</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"kalama [_la_a_kute_ijo_moli_o_sona_a]\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/kalama-lakimosa/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>kalama [_la_a_kute_ijo_moli_o_sona_a]</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/tok\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\">jan Matelo</span>\n                        <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">kalama [_la_a_kute_ijo_moli_o_sona_a]</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>tenpo ni la pilin li telo:  <br>\njan ni li kama tan ma anpa: <br>\nsewi li lawa e ona pakala.</p>\n<p>sewi o pona e ona.          <br>\njan Jesu wawa o             <br>\npana e lape tawa ona.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-08-10T01:10:17Z\">\n                &nbsp;sike 21 mun 08 suno 10 tenpo 0110</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"tenpo ni la pilin li telo:  \\\njan ni li kama tan ma anpa: \\\nsewi li lawa e ona pakala.\n\nsewi o pona e ona.          \\\njan Jesu wawa o             \\\npana e lape tawa ona.\n\n","url":"https://denizaksimsek.com/2021/kalama-lakimosa/","date_published":"2021-08-10T01:10:17Z"},{"id":"/2021/hyperscript-event-delegation/","title":"Event Delegation in _hyperscript","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Event Delegation in _hyperscript</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Event Delegation in _hyperscript\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/hyperscript-event-delegation/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Event Delegation in _hyperscript</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Event Delegation in _hyperscript</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>TL;DR This is how you do it:</p>\n<pre><code class=\"language-hyperscript\">on click\n\ttell the closest &lt;li/&gt; to the target\n\t\tremove yourself\n\t\t-- do more stuff...\n\t\t-- &quot;you&quot; refers to the clicked list item\n</code></pre>\n<p>Or more concisely:</p>\n<pre><code class=\"language-hyperscript\">on click tell closest &lt;li/&gt; to target\n\tremove yourself\n</code></pre>\n<hr>\n<p>I’ve seen some people use a pattern like this:</p>\n<pre><code class=\"language-html\">&lt;ul&gt;\n\t{% for item in items %}\n\t\t&lt;li _=&quot;on click remove me&quot;&gt;{{ item }}&lt;/li&gt;\n\t{% endfor %}\n&lt;/ul&gt;\n</code></pre>\n<p>This is convenient to write if you have a server-side templating system, but\nhas a few issues:</p>\n<ul>\n<li>The code needs to be parsed as many times as there are items.</li>\n<li>The resulting HTML is bloated.</li>\n<li>If you add more items to the list dynamically on the client, you need to repeat the code there.</li>\n</ul>\n<p>The pattern for resolving this is called <dfn>event delegation</dfn>. Here’s\nhow you might do it in JavaScript:</p>\n<pre><code class=\"language-javascript\">ul.addEventListener('click', e =&gt; {\n\tconst li = e.target.closest('li')\n\tif (!li) return\n\tli.remove()\n})\n</code></pre>\n<p>We add a single event listener to the enclosing list, which finds the item\nthat was clicked and manipulates it.</p>\n<p>In _hyperscript, the <code>tell</code> command allows us to manipulate an\nelement other  than <code>me</code> conveniently, by changing the implicit\ntarget from <code>me</code> to <code>you</code>,  which refers to the “element\nbeing told”.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-05-20T12:43:49Z\">\n                20 May ’21 12:43</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"TL;DR This is how you do it:\n\n```hyperscript\non click\n\ttell the closest <li/> to the target\n\t\tremove yourself\n\t\t-- do more stuff...\n\t\t-- \"you\" refers to the clicked list item\n```\n\nOr more concisely:\n\n```hyperscript\non click tell closest <li/> to target\n\tremove yourself\n```\n\n---\n\nI've seen some people use a pattern like this:\n\n```html\n<ul>\n\t{% for item in items %}\n\t\t<li _=\"on click remove me\">{{ item }}</li>\n\t{% endfor %}\n</ul>\n```\n\nThis is convenient to write if you have a server-side templating system, but\nhas a few issues:\n\n * The code needs to be parsed as many times as there are items.\n * The resulting HTML is bloated.\n * If you add more items to the list dynamically on the client, you need to repeat the code there.\n\nThe pattern for resolving this is called <dfn>event delegation</dfn>. Here's\nhow you might do it in JavaScript:\n\n```javascript\nul.addEventListener('click', e => {\n\tconst li = e.target.closest('li')\n\tif (!li) return\n\tli.remove()\n})\n```\n\nWe add a single event listener to the enclosing list, which finds the item\nthat was clicked and manipulates it.\n\nIn _hyperscript, the <code>tell</code> command allows us to manipulate an\nelement other  than <code>me</code> conveniently, by changing the implicit\ntarget from <code>me</code> to <code>you</code>,  which refers to the \"element\nbeing told\".\n","url":"https://denizaksimsek.com/2021/hyperscript-event-delegation/","date_published":"2021-05-20T12:43:49Z"},{"id":"/2021/hypelet/","title":"Hypelet","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Hypelet</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Hypelet\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/hypelet/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Hypelet</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Hypelet</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I made a tool to make bookmarklets in <a href=\"//hyperscript.org\">_hyperscript</a>. You just write some code in the box and drag the bookmarklet into your bookmarks toolbar. Hypelet injects glue code to load hyperscript dynamically and run your code. You can get a permalink to share your bookmarklet.</p>\n<ul>\n<li>Here’s Hypelet: <a href=\"//hypelet.dz4k.com\">Hypelet</a></li>\n<li>Here’s the code for a bookmarklet to get the RSS feed of a page: <a href=\"https://hypelet.dz4k.com/#%7B%22hs%22%3A%22get%20the%20href%20of%20the%20first%20%3C%5Brel%3Dfeed%5D%2C%20%5Btype%5E%3D%5C%22application%2Frss%2Bxml%5C%22%5D%2F%3E%5Cncall%20alert(it)%22%2C%22name%22%3A%22get%20rss%20feed%22%7D\">Get RSS – Hypelet</a></li>\n</ul>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-05-01T14:46:00Z\">\n                01 May ’21 14:46</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://twitter.com/DenizAksimsek/status/1388463926174683137\">\n                            Twitter</a>,                                             <a rel=\"syndication\" href=\"https://dev.to/dz4k/hypelet-make-bookmarklets-with-hyperscript-cja\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"I made a tool to make bookmarklets in [_hyperscript](//hyperscript.org). You just write some code in the box and drag the bookmarklet into your bookmarks toolbar. Hypelet injects glue code to load hyperscript dynamically and run your code. You can get a permalink to share your bookmarklet.\n\n- Here's Hypelet: [Hypelet](//hypelet.dz4k.com)\n- Here's the code for a bookmarklet to get the RSS feed of a page: [Get RSS -- Hypelet][get-rss]\n\n[get-rss]: https://hypelet.dz4k.com/#%7B%22hs%22%3A%22get%20the%20href%20of%20the%20first%20%3C%5Brel%3Dfeed%5D%2C%20%5Btype%5E%3D%5C%22application%2Frss%2Bxml%5C%22%5D%2F%3E%5Cncall%20alert(it)%22%2C%22name%22%3A%22get%20rss%20feed%22%7D\n","url":"https://denizaksimsek.com/2021/hypelet/","summary":"I made a tool to make bookmarklets in _hyperscript.","date_published":"2021-05-01T14:46:00Z"},{"id":"/2021/the-implementation-of-hdb/","title":"The Implementation of HDB, the _hyperscript debugger","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>The Implementation of HDB, the _hyperscript debugger</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"The Implementation of HDB, the _hyperscript debugger\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/the-implementation-of-hdb/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>The Implementation of HDB, the _hyperscript debugger</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">The Implementation of HDB, the _hyperscript debugger</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <script src=\"https://unpkg.com/hyperscript.org@0.9.5\"></script>\n<script src=\"https://unpkg.com/hyperscript.org@0.9.5/dist/hdb.min.js\"></script>\n<p><strong>Update <time>2021-09-02</time>:</strong> HDB has evolved since this post was written. Though it works mostly the same way, there have been fixes and a UI redesign. Check <a href=\"https://github.com/bigskysoftware/_hyperscript\">the _hyperscript repo</a> for the up-to-date Code:</ins></p>\n<p>The 0.0.6 release of the <a href=\"https://hyperscript.org/\">_hyperscript</a> hypertext UI scripting language introduces HDB, an interactive debugging environment. In this article I discuss how the hyper-flexible hyperscript runtime allowed me to implement the first release of HDB with ease. But first, I will introduce you to what HDB is like:</p>\n<h2>The (Un)finished Product</h2>\n<p>The <code>breakpoint</code> statement stops execution and launches the HDB UI.</p>\n<x-fig title=\"Demo: The breakpoint command\">\n<p><button type=\"button\" _=\"\non click\n\tbreakpoint\n\t-- This is some sample code for you to explore HDB.\n\t-- Click 'Step Over' to move to the next command.\n\t-- Click 'Continue' to stop debugging.\n\tset my.innerHTML to 'You are stepping through!'\n\ttransition 'background-color' to red\n\twait for mouseover -- mouse over the button after you step over this\n\ttransition 'background-color' to initial\">Click me to try HDB</button></p>\n</x-fig>\n<p>You can set breakpoints conditionally:</p>\n<x-fig title=\"Demo: Conditional breakpoints\">\n<tool-bar>\n<label><input type=checkbox id=debugmode checked/> Debug Mode</label>\n<button type=\"button\" _=\"\non click\n\tif #debugmode.checked breakpoint end\n\tput 'Nothing to see here, end user' into me\">Debug, maybe</button>\n</tool-bar>\n</x-fig>\n<h2>Implementation</h2>\n<p>HDB lives in a <a href=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js\">single JavaScript file</a>.</p>\n<h3>Turning the keys</h3>\n<p>In the hyperscript runtime (which is a tree walking interpreter), each command has an <code>execute()</code> method which either returns the next command to be executed, or a <code>Promise</code> thereof. The execute method for the breakpoint command creates an HDB environment and assigns it to the global scope (usually <code>window</code>):</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L20\" title=\"hdb.js ln. 20\">\n<pre><code class=\"language-js\">var hdb = new HDB(ctx, runtime, this);\nwindow.hdb = hdb;\n</code></pre>\n</x-fig>\n<p>The <code>HDB</code> object keeps hold of the current command and context as we step through. (The context is the object holding the local variables for the hyperscript code, and some other things the runtime keeps track of). We call its <code>break()</code> method:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L35\" title=\"hdb.js ln. 35\">\n<pre><code class=\"language-js\">HDB.prototype.break = function(ctx) {\n\tvar self = this;\n\tconsole.log(&quot;%c=== HDB///_hyperscript/debugger ===&quot;, headingStyle);\n\tself.ui();\n\treturn new Promise(function (resolve, reject) {\n\t\tself.bus.addEventListener(&quot;continue&quot;, function () {\n\t\t\tif (self.ctx !== ctx) {\n\t\t\t\t// Context switch\n\t\t\t\tfor (var attr in ctx) {\n\t\t\t\t\tdelete ctx[attr];\n\t\t\t\t}\n\t\t\t\tObject.assign(ctx, self.ctx);\n\t\t\t}\n\t\t\tdelete window.hdb;\n\t\t\tresolve(self.runtime.findNext(self.cmd, self.ctx));\n\t\t}, { once: true });\n\t})\n}\n</code></pre>\n</x-fig>\n<p>There are a few things to unpack here. We call <code>self.ui()</code> to start the UI, which we’ll get to later. Remember how a command can return the next method to execute as a promise? The break method resolves after the <a href=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L10\">internal event bus</a> receives a <code>&quot;continue&quot;</code> event, whether by the user pressing “Continue” or simply reaching the end of the debugged Code:</p>\n<p>The “context switch” is the dirtiest part of it all. Because we can step out of functions, we might finish debugging session with a different context than before. In this case, we just wipe the old context and copy the current context variables over. Honestly, I thought I’d have to do a lot more of this kind of thing.</p>\n<p>Speaking of stepping out of functions…</p>\n<h3>Stepping Over and Out</h3>\n<p>Firstly, if self.cmd is null, then the previous command was the last one, so we just stop the debug process:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L58\" title=\"hdb.js ln. 58\">\n<pre><code class=\"language-js\">HDB.prototype.stepOver = function() {\n\tvar self = this;\n\tif (!self.cmd) return self.continueExec();\n</code></pre>\n</x-fig>\n<p>If not, then we do a little dance to execute the current command and get the next one:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61\" title=\"hdb.js ln. 61\">\n<pre><code class=\"language-js\">var result = self.cmd &amp;&amp; self.cmd.type === 'breakpointCommand' ?\n\tself.runtime.findNext(self.cmd, self.ctx) :\n\tself.runtime.unifiedEval(self.cmd, self.ctx);\n</code></pre>\n</x-fig>\n<p>We perform a useless check that I forgot to take out (<code>self.cmd &amp;&amp;</code>). Then, we special-case the <code>breakpoint</code> command itself and don’t execute it (nested debug sessions don’t end well…), instead finding the subsequent command ourselves with the <code>runtime.findNext()</code> in hyperscript core. Otherwise, we can execute the current command.</p>\n<p>Once we have our command result, we can step onto it:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61\" title=\"hdb.js ln. 64\">\n<pre><code class=\"language-js\">if (result.type === &quot;implicitReturn&quot;) return self.stepOut();\nif (result &amp;&amp; result.then instanceof Function) {\n\treturn result.then(function (next) {\n\t\tself.cmd = next;\n\t\tself.bus.dispatchEvent(new Event(&quot;step&quot;));\n\t\tself.logCommand();\n\t})\n} else if (result.halt_flag) {\n\tthis.bus.dispatchEvent(new Event(&quot;continue&quot;));\n} else {\n\tself.cmd = result;\n\tself.bus.dispatchEvent(new Event(&quot;step&quot;));\n\tthis.logCommand();\n}\n</code></pre>\n</x-fig>\n<p>If we returned from a function, we step out of it (discussed below). Otherwise, if the command returned a Promise, we await the next command, set <code>cmd</code> to it, notify the event bus and log it with some fancy styles. If the result was synchronous and is a <a href=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/core.js#L1221\">HALT</a>; we stop debugging (as I write this, I’m realizing I should’ve called <a href=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L54\"><code>continueExec()</code></a> here). Finally, we commit the kind of code duplication hyperscript is meant to help you avoid, to handle a synchronous result.</p>\n<p>To step out, we first get our hands on the context from which we were called:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L80\" title=\"hdb.js ln. 80\">\n<pre><code class=\"language-js\">HDB.prototype.stepOut = function() {\n\tvar self = this;\n\tif (!self.ctx.meta.caller) return self.continueExec();\n\tvar callingCmd = self.ctx.meta.callingCommand;\n\tvar oldMe = self.ctx.me;\n\tself.ctx = self.ctx.meta.caller;\n</code></pre>\n</x-fig>\n<p>Turns out _hyperscript function calls already keep hold of the caller context (<code>callingCommand</code> was added by me though). After we change context, we do something a little odd:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L92\" title=\"hdb.js ln. 92\">\n<pre><code class=\"language-js\">self.cmd = self.runtime.findNext(callingCmd, self.ctx);\nself.cmd = self.runtime.findNext(self.cmd, self.ctx);\n</code></pre>\n</x-fig>\n<p>Why do we call <code>findNext</code> twice? Consider the following hyperscript code:</p>\n<x-fig>\n<pre><code class=\"language-hyperscript\">transition 'color' to darkgray\nset name to getName()\nlog the name\n</code></pre>\n</x-fig>\n<p>We can’t execute the command to set <code>name</code> until we have the name, so when <code>getName()</code> is called, the current command is still set to the <code>transition</code>. We call <code>findNext</code> once to find the <code>set</code>, and again to find the <code>log</code>.</p>\n<p>Finally, we’re done stepping out:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L95\" title=\"hdb.js ln. 95\">\n<pre><code class=\"language-js\">self.bus.dispatchEvent(new Event('step'))\n</code></pre>\n</x-fig>\n<h3>HDB UI</h3>\n<p>What did I use to make the UI for the hyperscript debugger? Hyperscript, of course!</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L107\" title=\"hdb.js ln. 107\">\n<pre><code class=\"language-html\">&lt;div class=&quot;hdb&quot; _=&quot;--\n\ton load or step from hdb.bus send update to me\n\ton continue from hdb.bus remove #hyperscript-hdb-ui-wrapper-&quot;&gt;\n</code></pre>\n</x-fig>\n<p>There are a lot of elements listening to <code>load or step from hdb.bus</code>, so I consolidated them under <code>update from .hdb</code>. <code>#hyperscript-hdb-ui-wrapper-</code> is the element whose Shadow DOM this UI lives in — using shadow DOM to isolate the styling of the panel cost me later on, as you’ll see.</p>\n<hr>\n<p>We define some functions.</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L112\" title=\"hdb.js ln. 112\">\n<pre><code class=\"language-hyperscript\">def highlightDebugCode\n\tset start to hdb.cmd.startToken.start\n\tset end_ to hdb.cmd.endToken.end\n\tset src to hdb.cmd.programSource\n\tset beforeCmd to escapeHTML(src.substring(0, start))\n\tset cmd to escapeHTML(src.substring(start, end_))\n\tset afterCmd to escapeHTML(src.substring(end_))\n\treturn beforeCmd+&quot;&lt;u class='current'&gt;&quot;+cmd+&quot;&lt;/u&gt;&quot;+afterCmd\nend\n</code></pre>\n</x-fig>\n<p>Now, I wasn’t aware that we had <a href=\"https://hyperscript.org/expressions/string/\">template literals</a> in hyperscript at this point, so that’s for the next release. The <code>escapeHTML</code> helper might disappoint some:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L122\" title=\"hdb.js ln. 122\">\n<pre><code class=\"language-hyperscript\">def escapeHTML(unsafe)\n\tjs(unsafe) return unsafe\n\t\t.replace(/&amp;/g, &quot;&amp;amp;&quot;)\n\t\t.replace(/&lt;/g, &quot;&amp;lt;&quot;)\n\t\t.replace(/&gt;/g, &quot;&amp;gt;&quot;)\n\t\t.replace(/\\\\x22/g, &quot;&amp;quot;&quot;)\n\t\t.replace(/\\\\x27/g, &quot;&amp;#039;&quot;) end\n\treturn it\nend\n</code></pre>\n</x-fig>\n<p>Unfortunately, hyperscript’s regex syntax isn’t decided yet.</p>\n<hr>\n<p>And we have the most broken part of HDB, the prettyPrint function. If you know how to do this better, feel free to send a PR.</p>\n<p>Having defined our functions we have a simple toolbar and then the <strong>eval panel</strong>:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L158\" title=\"hdb.js ln. 158\">\n<pre><code class=\"language-html\">&lt;form class=&quot;eval-form&quot;  _=&quot;on submit\n\tcall event.preventDefault()\n\tget the first &lt;input/&gt; in me\n\tcall _hyperscript(its.value, hdb.ctx)\n\tcall prettyPrint(it)\n\tput it into the &lt;output/&gt; in me&quot;&gt;\n\n\t\t&lt;input type=&quot;text&quot; id=&quot;eval-expr&quot; placeholder=&quot;e.g. target.innerText&quot;&gt;\n\t\t&lt;button type=&quot;submit&quot;&gt;Go&lt;/button&gt;\n\t\t&lt;output id=&quot;eval-output&quot;&gt;&lt;em&gt;The value will show up here&lt;/em&gt;&lt;/output&gt;\n</code></pre>\n</x-fig>\n<p>Why do I use weird selectors like <code>&lt;input/&gt; in me</code> when these elements have good IDs? Because <code>#eval-expr</code> in hyperscript uses <code>document.querySelector</code>, which doesn’t reach Shadow DOM.</p>\n<hr>\n<p>A panel to show the code being debugged:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L170\" title=\"hdb.js ln. 170\">\n<pre><code class=\"language-html\">&lt;h3 _=&quot;on update from hdbUI\n\t\tput 'Debugging &lt;code&gt;'+hdb.cmd.parent.displayName+'&lt;/code&gt;' into me&quot;&gt;&lt;/h3&gt;\n&lt;div class=&quot;code-container&quot;&gt;\n\t&lt;pre class=&quot;code&quot; _=&quot;on update from hdbUI\n\t\t\t\t\t\t\tif hdb.cmd.programSource\n\t\t\t\t\t\t\t\tput highlightDebugCode() into my.innerHTML\n\t\t\t\t\t\t\t\tscrollIntoView({ block: 'nearest' }) the\n\t\t\t\t\t\t\t\tfirst .current in me&quot;&gt;&lt;/pre&gt;\n&lt;/div&gt;\n</code></pre>\n</x-fig>\n<hr>\n<p>Finally, a context panel that shows the local variables.</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L186\" title=\"hdb.js ln. 106\">\n<pre><code class=\"language-html\">&lt;dl class=&quot;context&quot; _=&quot;--\n\ton update from hdbUI\n\t\tset my.innerHTML to ''\n\t\trepeat for var in Object.keys(hdb.ctx) if var != 'meta'\n\t\t\tget '&lt;dt&gt;'+var+'&lt;dd&gt;'+prettyPrint(hdb.ctx[var])\n\t\t\tput it at end of me\n\t\tend\n\ton click\n\t\tget closest &lt;dt/&gt; to target\n\t\tlog hdb.ctx[its.innerText]&quot;&gt;&lt;/dl&gt;\n</code></pre>\n</x-fig>\n<p>That loop could definitely be cleaner. You can see the hidden feature where you can click a variable name to log it to the console (useful if you don’t want to rely on my super-buggy pretty printer).</p>\n<p>Some CSS later, we’re done with the UI! To avoid CSS interference from the host page, we create a wrapper and put our UI in its shadow DOM:</p>\n<x-fig src=\"https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L350\" title=\"hdb.js ln. 350\">\n<pre><code class=\"language-js\">HDB.prototype.ui = function () {\n\tvar node = document.createElement('div');\n\tvar shadow = node.attachShadow({ mode: 'open' });\n\tnode.style = 'all: initial';\n\tnode.id = 'hyperscript-hdb-ui-wrapper-';\n\tshadow.innerHTML = ui;\n\tdocument.body.appendChild(node);\n\twindow.hdbUI = shadow.querySelector('.hdb');\n\t_hyperscript.processNode(hdbUI);\n}\n</code></pre>\n</x-fig>\n<h2>The End</h2>\n<p>In just 360 lines, we have a basic debugger. This speaks volumes to the flexibility of the hyperscript runtime, and I hope HDB serves as an example of what’s possible with the hyperscript extension API. Like the rest of hyperscript, it’s in early stages of development — feedback and contributors are always welcome!</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-03-17T00:00:00Z\">\n                17 Mar ’21 00:00</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://twitter.com/DenizAksimsek/status/1372244813928660993\">\n                            Twitter</a>,                                             <a rel=\"syndication\" href=\"https://news.ycombinator.com/item?id=26494553\">\n                            Hacker News</a>,                                             <a rel=\"syndication\" href=\"https://dev.to/dz4k/the-implementation-of-hdb-the-hyperscript-debugger-5hf4\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"<script src=\"https://unpkg.com/hyperscript.org@0.9.5\"></script>\n<script src=\"https://unpkg.com/hyperscript.org@0.9.5/dist/hdb.min.js\"></script>\n\n**Update <time>2021-09-02</time>:** HDB has evolved since this post was written. Though it works mostly the same way, there have been fixes and a UI redesign. Check [the _hyperscript repo][] for the up-to-date Code:</ins>\n\nThe 0.0.6 release of the [_hyperscript] hypertext UI scripting language introduces HDB, an interactive debugging environment. In this article I discuss how the hyper-flexible hyperscript runtime allowed me to implement the first release of HDB with ease. But first, I will introduce you to what HDB is like:\n\n## The (Un)finished Product\n\nThe `breakpoint` statement stops execution and launches the HDB UI.\n\n::: fig (\"Demo: The breakpoint command\")\n<button type=\"button\" _=\"\non click\n\tbreakpoint\n\t-- This is some sample code for you to explore HDB.\n\t-- Click 'Step Over' to move to the next command.\n\t-- Click 'Continue' to stop debugging.\n\tset my.innerHTML to 'You are stepping through!'\n\ttransition 'background-color' to red\n\twait for mouseover -- mouse over the button after you step over this\n\ttransition 'background-color' to initial\">Click me to try HDB</button>\n:::\n\nYou can set breakpoints conditionally:\n\n::: fig (\"Demo: Conditional breakpoints\")\n\n<tool-bar>\n<label><input type=checkbox id=debugmode checked/> Debug Mode</label>\n<button type=\"button\" _=\"\non click\n\tif #debugmode.checked breakpoint end\n\tput 'Nothing to see here, end user' into me\">Debug, maybe</button>\n</tool-bar>\n:::\n\n## Implementation\n\nHDB lives in a [single JavaScript file][hdb-src].\n\n### Turning the keys\n\nIn the hyperscript runtime (which is a tree walking interpreter), each command has an `execute()` method which either returns the next command to be executed, or a `Promise` thereof. The execute method for the breakpoint command creates an HDB environment and assigns it to the global scope (usually `window`):\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L20 \"hdb.js ln. 20\")\n~~~js\nvar hdb = new HDB(ctx, runtime, this);\nwindow.hdb = hdb;\n~~~\n:::\n\nThe `HDB` object keeps hold of the current command and context as we step through. (The context is the object holding the local variables for the hyperscript code, and some other things the runtime keeps track of). We call its `break()` method:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L35 \"hdb.js ln. 35\")\n~~~js\nHDB.prototype.break = function(ctx) {\n\tvar self = this;\n\tconsole.log(\"%c=== HDB///_hyperscript/debugger ===\", headingStyle);\n\tself.ui();\n\treturn new Promise(function (resolve, reject) {\n\t\tself.bus.addEventListener(\"continue\", function () {\n\t\t\tif (self.ctx !== ctx) {\n\t\t\t\t// Context switch\n\t\t\t\tfor (var attr in ctx) {\n\t\t\t\t\tdelete ctx[attr];\n\t\t\t\t}\n\t\t\t\tObject.assign(ctx, self.ctx);\n\t\t\t}\n\t\t\tdelete window.hdb;\n\t\t\tresolve(self.runtime.findNext(self.cmd, self.ctx));\n\t\t}, { once: true });\n\t})\n}\n~~~\n:::\n\nThere are a few things to unpack here. We call `self.ui()` to start the UI, which we'll get to later. Remember how a command can return the next method to execute as a promise? The break method resolves after the [internal event bus][] receives a `\"continue\"` event, whether by the user pressing \"Continue\" or simply reaching the end of the debugged Code:\n\nThe \"context switch\" is the dirtiest part of it all. Because we can step out of functions, we might finish debugging session with a different context than before. In this case, we just wipe the old context and copy the current context variables over. Honestly, I thought I'd have to do a lot more of this kind of thing.\n\nSpeaking of stepping out of functions...\n\n### Stepping Over and Out\n\nFirstly, if self.cmd is null, then the previous command was the last one, so we just stop the debug process:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L58 \"hdb.js ln. 58\")\n~~~js\nHDB.prototype.stepOver = function() {\n\tvar self = this;\n\tif (!self.cmd) return self.continueExec();\n~~~\n:::\n\nIf not, then we do a little dance to execute the current command and get the next one:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61 \"hdb.js ln. 61\")\n~~~js\nvar result = self.cmd && self.cmd.type === 'breakpointCommand' ?\n\tself.runtime.findNext(self.cmd, self.ctx) :\n\tself.runtime.unifiedEval(self.cmd, self.ctx);\n~~~\n:::\n\nWe perform a useless check that I forgot to take out (`self.cmd &&`). Then, we special-case the `breakpoint` command itself and don't execute it (nested debug sessions don't end well...), instead finding the subsequent command ourselves with the `runtime.findNext()` in hyperscript core. Otherwise, we can execute the current command.\n\nOnce we have our command result, we can step onto it:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61 \"hdb.js ln. 64\")\n~~~js\nif (result.type === \"implicitReturn\") return self.stepOut();\nif (result && result.then instanceof Function) {\n\treturn result.then(function (next) {\n\t\tself.cmd = next;\n\t\tself.bus.dispatchEvent(new Event(\"step\"));\n\t\tself.logCommand();\n\t})\n} else if (result.halt_flag) {\n\tthis.bus.dispatchEvent(new Event(\"continue\"));\n} else {\n\tself.cmd = result;\n\tself.bus.dispatchEvent(new Event(\"step\"));\n\tthis.logCommand();\n}\n~~~\n:::\n\nIf we returned from a function, we step out of it (discussed below). Otherwise, if the command returned a Promise, we await the next command, set `cmd` to it, notify the event bus and log it with some fancy styles. If the result was synchronous and is a [HALT][]; we stop debugging (as I write this, I'm realizing I should've called [`continueExec()`][continue-exec] here). Finally, we commit the kind of code duplication hyperscript is meant to help you avoid, to handle a synchronous result.\n\nTo step out, we first get our hands on the context from which we were called:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L80 \"hdb.js ln. 80\")\n~~~js\nHDB.prototype.stepOut = function() {\n\tvar self = this;\n\tif (!self.ctx.meta.caller) return self.continueExec();\n\tvar callingCmd = self.ctx.meta.callingCommand;\n\tvar oldMe = self.ctx.me;\n\tself.ctx = self.ctx.meta.caller;\n~~~\n:::\n\nTurns out _hyperscript function calls already keep hold of the caller context (`callingCommand` was added by me though). After we change context, we do something a little odd:\n\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L92 \"hdb.js ln. 92\")\n~~~js\nself.cmd = self.runtime.findNext(callingCmd, self.ctx);\nself.cmd = self.runtime.findNext(self.cmd, self.ctx);\n~~~\n:::\n\nWhy do we call `findNext` twice? Consider the following hyperscript code:\n\n::: fig\n~~~hyperscript\ntransition 'color' to darkgray\nset name to getName()\nlog the name\n~~~\n:::\n\nWe can't execute the command to set `name` until we have the name, so when `getName()` is called, the current command is still set to the `transition`. We call `findNext` once to find the `set`, and again to find the `log`.\n\nFinally, we're done stepping out:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L95 \"hdb.js ln. 95\")\n~~~js\nself.bus.dispatchEvent(new Event('step'))\n~~~\n:::\n\n### HDB UI\n\nWhat did I use to make the UI for the hyperscript debugger? Hyperscript, of course!\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L107 \"hdb.js ln. 107\")\n~~~html\n<div class=\"hdb\" _=\"--\n\ton load or step from hdb.bus send update to me\n\ton continue from hdb.bus remove #hyperscript-hdb-ui-wrapper-\">\n~~~\n:::\n\nThere are a lot of elements listening to `load or step from hdb.bus`, so I consolidated them under `update from .hdb`. `#hyperscript-hdb-ui-wrapper-` is the element whose Shadow DOM this UI lives in --- using shadow DOM to isolate the styling of the panel cost me later on, as you'll see.\n\n-------------\n\nWe define some functions.\n\n::: fig(https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L112 \"hdb.js ln. 112\")\n~~~hyperscript\ndef highlightDebugCode\n\tset start to hdb.cmd.startToken.start\n\tset end_ to hdb.cmd.endToken.end\n\tset src to hdb.cmd.programSource\n\tset beforeCmd to escapeHTML(src.substring(0, start))\n\tset cmd to escapeHTML(src.substring(start, end_))\n\tset afterCmd to escapeHTML(src.substring(end_))\n\treturn beforeCmd+\"<u class='current'>\"+cmd+\"</u>\"+afterCmd\nend\n~~~\n:::\n\nNow, I wasn't aware that we had [template literals][] in hyperscript at this point, so that's for the next release. The `escapeHTML` helper might disappoint some:\n\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L122 \"hdb.js ln. 122\")\n~~~hyperscript\ndef escapeHTML(unsafe)\n\tjs(unsafe) return unsafe\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\\\\x22/g, \"&quot;\")\n\t\t.replace(/\\\\x27/g, \"&#039;\") end\n\treturn it\nend\n~~~\n:::\n\nUnfortunately, hyperscript's regex syntax isn't decided yet.\n\n------------\n\nAnd we have the most broken part of HDB, the prettyPrint function. If you know how to do this better, feel free to send a PR.\n\nHaving defined our functions we have a simple toolbar and then the **eval panel**:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L158 \"hdb.js ln. 158\")\n~~~html\n<form class=\"eval-form\"  _=\"on submit\n\tcall event.preventDefault()\n\tget the first <input/> in me\n\tcall _hyperscript(its.value, hdb.ctx)\n\tcall prettyPrint(it)\n\tput it into the <output/> in me\">\n\n\t\t<input type=\"text\" id=\"eval-expr\" placeholder=\"e.g. target.innerText\">\n\t\t<button type=\"submit\">Go</button>\n\t\t<output id=\"eval-output\"><em>The value will show up here</em></output>\n~~~\n:::\n\nWhy do I use weird selectors like `<input/> in me` when these elements have good IDs? Because `#eval-expr` in hyperscript uses `document.querySelector`, which doesn't reach Shadow DOM.\n\n------------\n\nA panel to show the code being debugged:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L170 \"hdb.js ln. 170\")\n~~~html\n<h3 _=\"on update from hdbUI\n\t\tput 'Debugging <code>'+hdb.cmd.parent.displayName+'</code>' into me\"></h3>\n<div class=\"code-container\">\n\t<pre class=\"code\" _=\"on update from hdbUI\n\t\t\t\t\t\t\tif hdb.cmd.programSource\n\t\t\t\t\t\t\t\tput highlightDebugCode() into my.innerHTML\n\t\t\t\t\t\t\t\tscrollIntoView({ block: 'nearest' }) the\n\t\t\t\t\t\t\t\tfirst .current in me\"></pre>\n</div>\n~~~\n:::\n\n------------\n\nFinally, a context panel that shows the local variables.\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L186 \"hdb.js ln. 106\")\n~~~html\n<dl class=\"context\" _=\"--\n\ton update from hdbUI\n\t\tset my.innerHTML to ''\n\t\trepeat for var in Object.keys(hdb.ctx) if var != 'meta'\n\t\t\tget '<dt>'+var+'<dd>'+prettyPrint(hdb.ctx[var])\n\t\t\tput it at end of me\n\t\tend\n\ton click\n\t\tget closest <dt/> to target\n\t\tlog hdb.ctx[its.innerText]\"></dl>\n~~~\n:::\n\nThat loop could definitely be cleaner. You can see the hidden feature where you can click a variable name to log it to the console (useful if you don't want to rely on my super-buggy pretty printer).\n\nSome CSS later, we're done with the UI! To avoid CSS interference from the host page, we create a wrapper and put our UI in its shadow DOM:\n\n::: fig (https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L350 \"hdb.js ln. 350\")\n~~~js\nHDB.prototype.ui = function () {\n\tvar node = document.createElement('div');\n\tvar shadow = node.attachShadow({ mode: 'open' });\n\tnode.style = 'all: initial';\n\tnode.id = 'hyperscript-hdb-ui-wrapper-';\n\tshadow.innerHTML = ui;\n\tdocument.body.appendChild(node);\n\twindow.hdbUI = shadow.querySelector('.hdb');\n\t_hyperscript.processNode(hdbUI);\n}\n~~~\n:::\n\n## The End\n\nIn just 360 lines, we have a basic debugger. This speaks volumes to the flexibility of the hyperscript runtime, and I hope HDB serves as an example of what's possible with the hyperscript extension API. Like the rest of hyperscript, it's in early stages of development --- feedback and contributors are always welcome!\n\n[_hyperscript]: https://hyperscript.org\n[the _hyperscript repo]: https://github.com/bigskysoftware/_hyperscript\n[hdb-src]: https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js\n[continue-exec]: https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L54\n[HALT]: https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/core.js#L1221\n[template literals]: https://hyperscript.org/expressions/string/\n[internal event bus]: https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L10\n","url":"https://denizaksimsek.com/2021/the-implementation-of-hdb/","date_published":"2021-03-17T00:00:00Z"},{"id":"/2021/hyperscript-machine/","title":"The Hyperscript Machine","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Deniz Akşimşek</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"The Hyperscript Machine\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/hyperscript-machine/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/hyperscript-machine.jpg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Deniz Akşimşek</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><a href=\"/assets/photos/hyperscript-machine.jpg\"><img src=\"/assets/photos/hyperscript-machine.jpg\" alt=\"The Hyperscript Machine -- half computer, half microwave.\" data-img-color-scheme=\"light\"></a></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-02-26T00:03:00Z\">\n                26 Feb ’21 00:03</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"[![<%= it.photo.alt %>](<%= it.photo.src %>){data-img-color-scheme=light}](<%= it.photo.src %>)\n","url":"https://denizaksimsek.com/2021/hyperscript-machine/","image":"https://denizaksimsek.com/assets/photos/hyperscript-machine.jpg","date_published":"2021-02-26T00:03:00Z"},{"id":"/2021/hyperscript-wait-for/","title":"Repost: \"hyperscript 0.0.4 beta is going to be out soon...\"","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Repost: &quot;hyperscript 0.0.4 beta is going to be out soon...&quot;</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Repost: &quot;hyperscript 0.0.4 beta is going to be out soon...&quot;\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/hyperscript-wait-for/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Repost: &quot;hyperscript 0.0.4 beta is going to be out soon...&quot;</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Repost: &quot;hyperscript 0.0.4 beta is going to be out soon...&quot;</h1>\n                \n\n                                    \n<article class=\"h-entry box crowded hover-state\" onclick=\"this.querySelector('a').click()\" lang=\"undefined\">\n            Repost of        <b class=\"list-of-links\"><a href=\"https://twitter.com/htmx_org/status/1360593828894281736\" class=\"p-name u-url u-uid\">\n        “hyperscript 0.0.4 beta is going to be out soon [...]”</a></b>\n    <span class=\"block <small>\">twitter.com</span>\n    </article>\n                                        \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <blockquote style=\"white-space: pre-wrap\">\nhyperscript 0.0.4 beta is going to be out soon\n<p>a sneak peek:</p>\n<p>the wait command can now pause evaluation until an event is received</p>\n<p>here a class is added, hyperscript waits for the transition to end, then the\nelement is removed</p>\n<p>feature suggested by <a href=\"/\">@DenizAksimsek</a></p>\n<pre class=\"language-html\">\n&lt;div _=\"on click add .fade then wait for transitionend then remove\">\n  This is a notice. Click to dismiss.\n  &lt;/div>\n</pre>\n</blockquote>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-02-13T17:45:11Z\">\n                13 Feb ’21 17:45</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"<blockquote style=\"white-space: pre-wrap\">\nhyperscript 0.0.4 beta is going to be out soon\n\na sneak peek:\n\nthe wait command can now pause evaluation until an event is received\n\nhere a class is added, hyperscript waits for the transition to end, then the\nelement is removed\n\nfeature suggested by <a href=\"/\">@DenizAksimsek</a>\n\n<pre class=\"language-html\">\n&lt;div _=\"on click add .fade then wait for transitionend then remove\">\n  This is a notice. Click to dismiss.\n  &lt;/div>\n</pre>\n</blockquote>\n","url":"https://denizaksimsek.com/2021/hyperscript-wait-for/","date_published":"2021-02-13T17:45:11Z"},{"id":"/2021/htmx-doodles/","title":"HTMX Doodles","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>HTMX Doodles</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"HTMX Doodles\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/htmx-doodles/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/htmx-doodles/hypertext-man.jpeg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>HTMX Doodles</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">HTMX Doodles</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><img src=\"/assets/htmx-doodles/hypertext-man.jpeg\" alt=\"Hypertext Man!\" data-img-color-scheme=\"light\"></p>\n<hr>\n<p><img src=\"/assets/htmx-doodles/consult-your-doctor.jpeg\" alt=\"On HTMX (see transcript)\" data-img-color-scheme=\"light\"></p>\n<details><summary>Transcript</summary>\n<h3>Server-Side App</h3>\n<ul>\n<li>Full-page reloads</li>\n<li>HTML only has GET and POST methods</li>\n<li>…and only link clicks &amp; form submissions</li>\n</ul>\n<h3>Single-Page App</h3>\n<ul>\n<li>Reinvent the accessible web</li>\n<li>Need big frameworks or complex code</li>\n<li>Probably need a server anyway for SSR</li>\n<li>The client is untrueted</li>\n</ul>\n<h3>Progressively Enhanced App</h3>\n<ul>\n<li>Tight coupling between server &amp; client code</li>\n<li>Behavior is scattered</li>\n</ul>\n<h3>htmx</h3>\n<ul>\n<li>Completing HTML with arbitrary events, replacing fragments instead of the whole page</li>\n<li>Your code runs on the server, which you trust</li>\n<li>htmx allows for <strong>locality of behavior</strong></li>\n</ul>\n<p><small>Consult your doctor to find out if htmx is right for you. Side effects may include arguing about REST on <abbr title=\"Hacker News\">HN</abbr>.</small></p>\n</details>\n<hr>\n<p><img src=\"/assets/htmx-doodles/2040.jpeg\" alt=\"2040 (See transcript)\" data-img-color-scheme=\"light\"></p>\n<details><summary>Transcript</summary>\n<p>The year is 2040.</p>\n<p>htmx is a W3C recommendation and _hyperscript is an ECMA standard. War has long ceased, API churn is nought but a distant memory.</p>\n</details>\n</div>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-01-11T00:47:00Z\">\n                11 Jan ’21 00:47</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"![Hypertext Man!](/assets/htmx-doodles/hypertext-man.jpeg){data-img-color-scheme=light}\n\n--------------------------------------------------------------------------------\n\n![On HTMX (see transcript)](/assets/htmx-doodles/consult-your-doctor.jpeg){data-img-color-scheme=light}\n\n<details><summary>Transcript</summary>\n\n### Server-Side App\n\n-\tFull-page reloads\n-\tHTML only has GET and POST methods\n-\t...and only link clicks & form submissions\n\n### Single-Page App\n\n-\tReinvent the accessible web\n-\tNeed big frameworks or complex code\n-\tProbably need a server anyway for SSR\n-\tThe client is untrueted\n\n### Progressively Enhanced App\n\n-\tTight coupling between server & client code\n-\tBehavior is scattered\n\n### htmx\n\n-\tCompleting HTML with arbitrary events, replacing fragments instead of the whole page\n-\tYour code runs on the server, which you trust\n-\thtmx allows for **locality of behavior**\n\n<small>Consult your doctor to find out if htmx is right for you. Side effects may include arguing about REST on <abbr title=\"Hacker News\">HN</abbr>.</small>\n\n</details>\n\n--------------------------------------------------------------------------------\n\n![2040 (See transcript)](/assets/htmx-doodles/2040.jpeg){data-img-color-scheme=light}\n\n<details><summary>Transcript</summary>\n\nThe year is 2040.\n\nhtmx is a W3C recommendation and _hyperscript is an ECMA standard. War has long ceased, API churn is nought but a distant memory.\n</details>\n</div>\n","url":"https://denizaksimsek.com/2021/htmx-doodles/","image":"https://denizaksimsek.com/assets/htmx-doodles/hypertext-man.jpeg","date_published":"2021-01-11T00:47:00Z"},{"id":"/2021/js-iota/","title":"Iota (from Golang) in JavaScript","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Iota (from Golang) in JavaScript</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Iota (from Golang) in JavaScript\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2021/js-iota/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Iota (from Golang) in JavaScript</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Iota (from Golang) in JavaScript</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>(<a href=\"#the-code\">Skip to code</a>)</p>\n<h2>Enums in Javascript</h2>\n<p>Currently, the ways we create enums include</p>\n<ul>\n<li>\n<p>String literals (see <code>addEventListener</code>), which can be typed with TypeScript, but look a bit ugly</p>\n<pre><code class=\"language-ts\">type Direction = 'north' | 'east' | 'south' | 'west'\n</code></pre>\n</li>\n<li>\n<p>TypeScript enums</p>\n<pre><code class=\"language-ts\">enum Direction { north, east, south, west }\n</code></pre>\n</li>\n<li>\n<p>Integer constants, IMO your best option if you’re not using TypeScript</p>\n<pre><code class=\"language-js\">const Directions = { north: 0, east: 1, south: 2, west: 3 }\n</code></pre>\n</li>\n</ul>\n<h2>Enums in Go</h2>\n<p><small data-note=1>Initially I wasn’t going to bring Go into this at all. However, it turns out <code>enum</code> is a reserved word in JS, so I went with <code>iota</code> for the name of the function, and felt the need to explain it.</small></p>\n<p><em>Go</em> doesn’t have enums, but an unusual keyword <code>iota</code>:</p>\n<pre><code class=\"language-go\">type Direction int\nconst (\n\tNorth Direction = iota\n\tEast\n\tSouth\n\tWest\n)\n</code></pre>\n<p>There’s something subtle going on here. The iota relies on a few Go features:</p>\n<ul>\n<li>When multiple const declarations are grouped together, the right hand side is implicitly repeated</li>\n<li>Iota is incremented every time it is evaluated, and reset with each const</li>\n</ul>\n<p>My JavaScript shorthand is nowhere near as magical… but it does make use of proxies.</p>\n<div id=the-code>\n<pre><code class=\"language-js\">function iota(start = 0) {\n\tlet count = start\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = count++\n\t\t}\n\t})\n}\n\nconst { north, east, south, west } = iota()\nconsole.log(north)\n</code></pre>\n</div>\n<p>So, is this function any good?</p>\n<p>For one, it lacks some of Go’s <code>iota</code> capabilities — you can’t create bitmasks with this the way you would in Go with <code>1 &lt;&lt; iota</code>. We could augment it a bit by accepting a callback:</p>\n<pre><code class=\"language-js\">function iota(cb = (i =&gt; i)) {\n\tlet count = 0\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = cb(count++)\n\t\t}\n\t})\n}\n\n// flag bits\nconst { hasPermissionFlag, userModeFlag, useLegacyProtocolFlag } = iota(i =&gt; 1 &lt;&lt; i)\nconst hasPermission = options &amp; hasPermissionFlag\n</code></pre>\n<p>I don’t think bitmasks are very common at all in JavaScript code, though.</p>\n<p>A more significant setback is that you can’t get a list of all the enum values — nothing we can’t fix:</p>\n<pre><code class=\"language-js\">function iota(start = 0) {\n\tlet count = start\n\tlet firstProp = true\n\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (firstProp) {\n\t  \t\t\tfirstProp = false\n\t  \t\t\treturn { // Enum descriptor\n\t\t\t\t\tget values() { return o }\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = count++\n\t\t}\n\t})\n}\n\nconst { Direction, north, east, south, west } = iota()\nconsole.log(Direction)\n</code></pre>\n<p>This is open for extension — we could add more methods on the enum description such as converting the integer value of a Direction to its name, or validating a Direction that we parsed from a config file.</p>\n<p>I might have a metaprogramming addiction.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2021-01-10T17:57:00Z\">\n                10 Jan ’21 17:57</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/iota-from-golang-in-javascript-b5i\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"([Skip to code](#the-code))\n\n## Enums in Javascript\n\nCurrently, the ways we create enums include\n-\tString literals (see `addEventListener`), which can be typed with TypeScript, but look a bit ugly\n\t~~~ts\n\ttype Direction = 'north' | 'east' | 'south' | 'west'\n\t~~~\n\n-\tTypeScript enums\n\t~~~ts\n\tenum Direction { north, east, south, west }\n\t~~~\n\n-\tInteger constants, IMO your best option if you're not using TypeScript\n\t~~~js\n\tconst Directions = { north: 0, east: 1, south: 2, west: 3 }\n\t~~~\n\n## Enums in Go\n\n <small data-note=1>Initially I wasn't going to bring Go into this at all. However, it turns out `enum` is a reserved word in JS, so I went with `iota` for the name of the function, and felt the need to explain it.</small>\n\n_Go_ doesn't have enums, but an unusual keyword `iota`:\n\n~~~go\ntype Direction int\nconst (\n\tNorth Direction = iota\n\tEast\n\tSouth\n\tWest\n)\n~~~\n\nThere's something subtle going on here. The iota relies on a few Go features:\n-\tWhen multiple const declarations are grouped together, the right hand side is implicitly repeated\n-\tIota is incremented every time it is evaluated, and reset with each const\n\nMy JavaScript shorthand is nowhere near as magical... but it does make use of proxies.\n\n<div id=the-code>\n\n~~~js\nfunction iota(start = 0) {\n\tlet count = start\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = count++\n\t\t}\n\t})\n}\n\nconst { north, east, south, west } = iota()\nconsole.log(north)\n~~~\n\n</div>\n\nSo, is this function any good?\n\nFor one, it lacks some of Go's `iota` capabilities --- you can't create bitmasks with this the way you would in Go with `1 << iota`. We could augment it a bit by accepting a callback:\n\n~~~js\nfunction iota(cb = (i => i)) {\n\tlet count = 0\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = cb(count++)\n\t\t}\n\t})\n}\n\n// flag bits\nconst { hasPermissionFlag, userModeFlag, useLegacyProtocolFlag } = iota(i => 1 << i)\nconst hasPermission = options & hasPermissionFlag\n~~~\n\nI don't think bitmasks are very common at all in JavaScript code, though.\n\nA more significant setback is that you can't get a list of all the enum values --- nothing we can't fix:\n\n~~~js\nfunction iota(start = 0) {\n\tlet count = start\n\tlet firstProp = true\n\n\treturn new Proxy({}, {\n\t\tget(o, prop) {\n\t\t\tif (firstProp) {\n\t  \t\t\tfirstProp = false\n\t  \t\t\treturn { // Enum descriptor\n\t\t\t\t\tget values() { return o }\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (prop in o) return o[prop]\n\t\t\telse return o[prop] = count++\n\t\t}\n\t})\n}\n\nconst { Direction, north, east, south, west } = iota()\nconsole.log(Direction)\n~~~\n\nThis is open for extension --- we could add more methods on the enum description such as converting the integer value of a Direction to its name, or validating a Direction that we parsed from a config file.\n\nI might have a metaprogramming addiction.\n\n","url":"https://denizaksimsek.com/2021/js-iota/","date_published":"2021-01-10T17:57:00Z"},{"id":"/2020/coffeescript-frontmatter/","title":"Eleventy + Markdown + CoffeeScript = ❤","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Eleventy + Markdown + CoffeeScript = ❤</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Eleventy + Markdown + CoffeeScript = ❤\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/coffeescript-frontmatter/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Eleventy + Markdown + CoffeeScript = ❤</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Eleventy + Markdown + CoffeeScript = ❤</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I’ve started using CoffeeScript instead of YAML for frontmatter, and it works like a dream. <a href=\"https://twitter.com/zachleat/status/1340057504567488513\">Endorsed by Zach Leatherman</a>.</p>\n<h2>Markdown with Frontmatter</h2>\n<p>As far as I know, Jekyll is the origin of using a YAML block at the top of a\nMarkdown file for metadata. It’s not hard to imagine why this became popular —\nassociating prose with data is a common use case, and though they have their\nissues, both Markdown and YAML look pretty.* <x-sidenote inline=\"\">“It looks pretty” is\noften what people really mean when they call something “human-readable”.</x-sidenote></p>\n<h2>Eleventy Frontmatter</h2>\n<p>Eleventy uses frontmatter blocks (delimited by <code>---</code>) for all kinds of files. Markdown posts, HTML partials and Nunjucks/EJS/Liquid/… templates can all have them. The data in the frontmatter can then be used in templates. In the following, <code>title</code> is not a reserved name:</p>\n<pre><code>---\ntitle: 'A Blog Post'\n---\n\n# {title}\n\nLorem ipsum dolor sit amet...\n\n</code></pre>\n<p>We could have used <code>name</code>, <code>heading</code> or <code>kdsad983erj33</code>.</p>\n<hr>\n<p>Eleventy’s <a href=\"https://www.11ty.dev/docs/data-computed/\">“Computed Data”</a> feature relies on functions in the frontmatter. As YAML doesn’t have functions, you are expected to use JS (well, you can use template strings I guess…):</p>\n<pre><code class=\"language-html\">---js\n{\n  title: &quot;My page title&quot;,\n  eleventyComputed: {\n    currentDate: () =&gt; new Date().toLocaleString()\n  }\n}\n---\n&lt;!doctype html&gt;\n&lt;html&gt;\n--- … --&gt;\n&lt;body&gt;\n   &lt;p&gt;This website was last generated on {{ currentDate }}&lt;/p&gt;\n  &lt;!-- … --&gt;\n</code></pre>\n<p>Computed data is cool. Among other things, it allows me to move logic away from my templates and express it in an actual programming language. However JS frontmatter is not very pretty, and I don’t want to convert my whole frontmatter just for one function.</p>\n<p>If only there was a language that had the expressive power of JS, and the visually clean data notation of YAML…</p>\n<h2>CoffeeScript All The Time!</h2>\n<p>CoffeeScript is actually mentioned in the GitHub repo for <code>gray-matter</code>, the library Eleventy uses to parse frontmatter. Compared to YAML, it’s not much more verbose:</p>\n<div id=compare class=\"-full-bleed-scroll\" style=\"\n\tpadding-inline: var(--dim-body-padding);\n\tdisplay:flex;\n\tgap: 3ch;\">\n<style>\n#compare pre {overflow: unset; margin-inline: 0}\n</style>\n<pre><code>---\ntitle: Street sign in İstanbul\ndate: 2020-10-10 19:27:39\ntags: [place, design]\nphoto: /photos/img02.jpeg\ntürkçe: /2020/istanbul-street-sign/\n---\nYAML\n</code></pre>\n<pre><code>---\ntitle: 'Street sign in İstanbul'\ndate: '2020-10-10T19:27:39+03:00'\ntags: ['place', 'design']\nphoto: '/photos/img02.jpeg'\ntürkçe: '/2020/istanbul-street-sign/'\n---\nCoffeeScript\n</code></pre>\n</div>\n<p>The CoffeeScript code above is actually fully YAML-compatible! This keeps my templates portable. And with computed data:</p>\n<pre><code class=\"language-liquid\">---\nlayout: 'layout'\neleventyComputed:\n\tsyndicationLinks: (data) -&gt;\n\t\tDEV: data.devToSyndication[data.page.url]\n---\n\n&lt;article class=&quot;h-entry&quot;&gt;\n\t&lt;!-- ... --&gt;\n\t{%for silo, link in syndicationLinks%}\n\t\t&lt;a class=&quot;u-syndication&quot; href=&quot;{{link}}&quot;&gt;{{silo}}&lt;/a&gt;\n\t\t{%if not loop.last%} | {%endif%}\n\t{%endfor%}\n</code></pre>\n<p>I think that computed frontmatter gives a nice balance of logic-less* and\nlogic-full templates. <x-sidenote inline=\"\">Logicless templates: For people who will never\nneed to make an HTML monthly calendar!</x-sidenote></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-10-16T17:12:00Z\">\n                16 Oct ’20 17:12</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/eleventy-markdown-coffeescript-p5a\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"I've started using CoffeeScript instead of YAML for frontmatter, and it works like a dream. [Endorsed by Zach Leatherman][endorsed].\n\n## Markdown with Frontmatter\n\nAs far as I know, Jekyll is the origin of using a YAML block at the top of a\nMarkdown file for metadata. It's not hard to imagine why this became popular ---\nassociating prose with data is a common use case, and though they have their\nissues, both Markdown and YAML look pretty.* :sidenote[\"It looks pretty\" is \noften what people really mean when they call something \"human-readable\".]\n\n## Eleventy Frontmatter\n\nEleventy uses frontmatter blocks (delimited by `---`) for all kinds of files. Markdown posts, HTML partials and Nunjucks/EJS/Liquid/... templates can all have them. The data in the frontmatter can then be used in templates. In the following, `title` is not a reserved name:\n\n~~~\n---\ntitle: 'A Blog Post'\n---\n\n# {title}\n\nLorem ipsum dolor sit amet...\n\n~~~\n\nWe could have used `name`, `heading` or `kdsad983erj33`.\n\n---\n\nEleventy's [\"Computed Data\"][computed-data-docs] feature relies on functions in the frontmatter. As YAML doesn't have functions, you are expected to use JS (well, you can use template strings I guess...):\n\n~~~html\n---js\n{\n  title: \"My page title\",\n  eleventyComputed: {\n    currentDate: () => new Date().toLocaleString()\n  }\n}\n---\n<!doctype html>\n<html>\n--- … -->\n<body>\n   <p>This website was last generated on {{ currentDate }}</p>\n  <!-- … -->\n~~~\n\nComputed data is cool. Among other things, it allows me to move logic away from my templates and express it in an actual programming language. However JS frontmatter is not very pretty, and I don't want to convert my whole frontmatter just for one function.\n\nIf only there was a language that had the expressive power of JS, and the visually clean data notation of YAML...\n\n## CoffeeScript All The Time!\n\nCoffeeScript is actually mentioned in the GitHub repo for `gray-matter`, the library Eleventy uses to parse frontmatter. Compared to YAML, it's not much more verbose:\n\n<div id=compare class=\"-full-bleed-scroll\" style=\"\n\tpadding-inline: var(--dim-body-padding);\n\tdisplay:flex;\n\tgap: 3ch;\">\n\n<style>\n#compare pre {overflow: unset; margin-inline: 0}\n</style>\n\n~~~\n---\ntitle: Street sign in İstanbul\ndate: 2020-10-10 19:27:39\ntags: [place, design]\nphoto: /photos/img02.jpeg\ntürkçe: /2020/istanbul-street-sign/\n---\nYAML\n~~~\n\n~~~\n---\ntitle: 'Street sign in İstanbul'\ndate: '2020-10-10T19:27:39+03:00'\ntags: ['place', 'design']\nphoto: '/photos/img02.jpeg'\ntürkçe: '/2020/istanbul-street-sign/'\n---\nCoffeeScript\n~~~\n\n</div>\n\nThe CoffeeScript code above is actually fully YAML-compatible! This keeps my templates portable. And with computed data:\n\n~~~liquid\n---\nlayout: 'layout'\neleventyComputed:\n\tsyndicationLinks: (data) ->\n\t\tDEV: data.devToSyndication[data.page.url]\n---\n\n<article class=\"h-entry\">\n\t<!-- ... -->\n\t{%for silo, link in syndicationLinks%}\n\t\t<a class=\"u-syndication\" href=\"{{link}}\">{{silo}}</a>\n\t\t{%if not loop.last%} | {%endif%}\n\t{%endfor%}\n~~~\n\nI think that computed frontmatter gives a nice balance of logic-less* and\nlogic-full templates. :sidenote[Logicless templates: For people who will never\nneed to make an HTML monthly calendar!]\n\n[endorsed]: https://twitter.com/zachleat/status/1340057504567488513\n[computed-data-docs]:\thttps://www.11ty.dev/docs/data-computed/\n[js-frontmatter-docs]:\thttps://www.11ty.dev/docs/data-frontmatter/#javascript-front-matter\n","url":"https://denizaksimsek.com/2020/coffeescript-frontmatter/","date_published":"2020-10-16T17:12:00Z"},{"id":"/2020/istanbul-sokak-tabelasi/","title":"İstanbul'da bir sokak tabelası","content_html":"<!DOCTYPE html>\n<html lang=\"tr\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>İstanbul&#39;da bir sokak tabelası</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"İstanbul&#39;da bir sokak tabelası\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/istanbul-sokak-tabelasi/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG20201007085906-02.jpeg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>İstanbul&#39;da bir sokak tabelası</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/tr\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">İstanbul&#39;da bir sokak tabelası</h1>\n                            <p>\n                🌐\n                                    <a\n                        rel=\"alternate\"\n                        lang=\"en\"\n                        hreflang=\"en\"\n                        href=\"/2020/istanbul-street-sign/\">\n                        English</a>\n                            </p>\n        \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><a href=\"/2020/ordu-street-sign/\">Ordu’daki tabelanın olduğu yazıda</a> İstanbul’a yolum düşerse oradakilerin resmini paylaşırım diye yazmıştım. Başka bir şey dileseymişim.</p>\n<p><a href=\"/assets/photos/IMG20201007085906-02.jpeg\"><img src=\"/assets/photos/IMG20201007085906-02.jpeg\" alt=\"Bir direk tepesinde ön öne gelmiş iki sokak tabelası. Her biri kırmızı ve\naltında bir ince beyaz, bir de kalın yeşil şerit var.\nÖndekinde yazan:\nKÖKNAR CADDESİ ↞↠, [beyaz şeritte:] MEHMET AKİF MAHALLESİ, [yeşil\nşeritte:] ÇEKMEKÖY (TN:40 ).\nArkadakinde yazan:\nBİL... SOKAĞI, [beyaz şeritte:] MEHMET AKİF MAHALLESİ, [yeşil şeritte:]\nÇEKMEKÖY (TN:60 )\n\"></a></p>\n<p>Pandemi yüzünden İstanbul’da gezemedim ama bu tabelaları gerçekte görme fırsatım oldu. Özel tasarım <em>Kent</em> yazıtipi ve ilçe renk kodlarını biliyordum ama tabelaların kabartmalı olduğu fotoğraflarda belli olmuyor.</p>\n<p>Bu tabelalar direk üzerinde ama bir çoğu bina duvarları üzerindeydi. Ben şahsen bunun tabelaları bulmayı zorlaştırdığını düşünüyorum. En azından Samsun’daki gibi ağaçlara çakmıyorlar.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-10-10T19:27:39Z\">\n                10 Eki ’20 19:27</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"[Ordu'daki tabelanın olduğu yazıda][ordu-street-sign] İstanbul'a yolum düşerse oradakilerin resmini paylaşırım diye yazmıştım. Başka bir şey dileseymişim.\n\n[![<%= it.photo.alt %>](<%= it.photo.src %>)](<%= it.photo.src %>)\n\nPandemi yüzünden İstanbul'da gezemedim ama bu tabelaları gerçekte görme fırsatım oldu. Özel tasarım _Kent_ yazıtipi ve ilçe renk kodlarını biliyordum ama tabelaların kabartmalı olduğu fotoğraflarda belli olmuyor.\n\nBu tabelalar direk üzerinde ama bir çoğu bina duvarları üzerindeydi. Ben şahsen bunun tabelaları bulmayı zorlaştırdığını düşünüyorum. En azından Samsun'daki gibi ağaçlara çakmıyorlar.\n\n[ordu-street-sign]:\t\t/2020/ordu-street-sign/\n","url":"https://denizaksimsek.com/2020/istanbul-sokak-tabelasi/","image":"https://denizaksimsek.com/assets/photos/IMG20201007085906-02.jpeg","date_published":"2020-10-10T19:27:39Z"},{"id":"/2020/istanbul-street-sign/","title":"Street sign in İstanbul","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Street sign in İstanbul</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Street sign in İstanbul\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/istanbul-street-sign/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG20201007085906-02.jpeg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Street sign in İstanbul</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Street sign in İstanbul</h1>\n                            <p>\n                🌐\n                                    <a\n                        rel=\"alternate\"\n                        lang=\"tr\"\n                        hreflang=\"tr\"\n                        href=\"/2020/istanbul-sokak-tabelasi/\">\n                        Türkçe</a>\n                            </p>\n        \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I mentioned these signs in <a href=\"/2020/ordu-street-sign/\">my post about the ones in Ordu</a> and said I’d post a picture if I ever went to İstanbul. I think I just wasted my wish.</p>\n<p><a href=\"/assets/photos/IMG20201007085906-02.jpeg\"><img src=\"/assets/photos/IMG20201007085906-02.jpeg\" alt=\"A post with two signs on it, one partially covering the other. The signs\nare colored red with a thin white strip, then a thick green strip at the\nbottom.\nThe front one says:\nKÖKNAR CADDESİ ↞↠, [on white strip] MEHMET AKİF MAHALLESİ, [on green\nstrip] ÇEKMEKÖY (TN:40 ).\nThe one behind says:\nBİL... SOKAĞI, [on white strip] MEHMET AKİF MAHALLESİ, [on green strip]\nÇEKMEKÖY (TN:60 )\n\"></a></p>\n<p>While I didn’t get to explore İstanbul due to the pandemic, I had an opportunity to see one of these for myself, with the custom-made <em lang=\"tr\">Kent</em> typeface and the district color codes (as you can see, I was in the lime green Çekmeköy district). When I saw one IRL, I realized that the letters are embossed — this doesn’t show through in photos.</p>\n<p>Although these two signs are on a post, many were on the sides of buildings. I don’t like this much as it makes them harder to find. At least they don’t nail them to trees like in Samsun though.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-10-10T19:27:39Z\">\n                10 Oct ’20 19:27</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"I mentioned these signs in [my post about the ones in Ordu][ordu-street-sign] and said I'd post a picture if I ever went to İstanbul. I think I just wasted my wish.\n\n[![<%= it.photo.alt %>](<%= it.photo.src %>)](<%= it.photo.src %>)\n\nWhile I didn't get to explore İstanbul due to the pandemic, I had an opportunity to see one of these for myself, with the custom-made _Kent_{lang=tr} typeface and the district color codes (as you can see, I was in the lime green Çekmeköy district). When I saw one IRL, I realized that the letters are embossed --- this doesn't show through in photos.\n\nAlthough these two signs are on a post, many were on the sides of buildings. I don't like this much as it makes them harder to find. At least they don't nail them to trees like in Samsun though.\n\n[ordu-street-sign]:\t\t/2020/ordu-street-sign/\n","url":"https://denizaksimsek.com/2020/istanbul-street-sign/","image":"https://denizaksimsek.com/assets/photos/IMG20201007085906-02.jpeg","date_published":"2020-10-10T19:27:39Z"},{"id":"/2020/devto-syndication-links/","title":"Add Syndication Links On Your Personal Site Using Dev.to API","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Add Syndication Links On Your Personal Site Using Dev.to API</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Add Syndication Links On Your Personal Site Using Dev.to API\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/devto-syndication-links/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Add Syndication Links On Your Personal Site Using Dev.to API</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Add Syndication Links On Your Personal Site Using Dev.to API</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>I recently started using the <a href=\"https://dev.to/settings/publishing-from-rss\">RSS publishing</a> option to syndicate posts from my personal site to DEV. This allows people to post comments under my posts, many of which might contain useful info. To allow any readers to easily discover and participate in these discussions, I’ve added links to the <a href=\"http://dev.to/\">dev.to</a> copies under my posts.</p>\n<p>(<a href=\"#the-code\">Skip to code</a>)</p>\n<p>I could, of course, add these links manually whenever I post, but being armed with such a powerful static site generator as Eleventy, I couldn’t possibly <em>not</em> automate it.</p>\n<h2>The API and the Data File</h2>\n<p>Eleventy users will be aware of how easy it is to gather all kinds of data from various APIs and put it in your site, all statically without client-side code. Our task here is exceptionally simple: Pick out the <code>url</code> and <code>canonical_url</code> properties for each article, and create a mapping from the latter to the former.</p>\n<pre><code class=\"language-js\">const fetch = require('node-fetch')\n\nmodule.exports = fetch('https://dev.to/api/articles?username=dza')\n    .then(res =&gt; res.json())\n    .then(articles =&gt; articles.map(\n        ({canonical_url, url}) =&gt; [canonical_url, url]))\n    .then(Object.fromEntries)\n</code></pre>\n<p><strong>Note:</strong> If you are copy-and-pasting this code, make sure to replace <code>dza</code> with your own <a href=\"http://dev.to/\">dev.to</a> username.</p>\n<p>This will give us an object like this:</p>\n<pre><code class=\"language-json\">{\n\t&quot;https://www.denizaksimsek.com/2020/css-additional-box-shadow/&quot;:\n\t    &quot;https://dev.to/dza/css-adding-additional-box-shadows-2lob&quot;,\n\t...\n}\n</code></pre>\n<p>Now let’s try using it in our templates:</p>\n<pre><code class=\"language-liquid\">{%if devToSyndication[page.url]%}\n&lt;section class=&quot;syndication-links&quot;&gt;\nThis article is syndicated to &lt;a class=&quot;u-syndication&quot;\n    href=&quot;{{devToSyndication[page.url]}}&quot;&gt;DEV&lt;/a&gt;, where you can comment on it.\n&lt;/section&gt;\n{%endif%}\n</code></pre>\n<p>Small problem: the <code>page.url</code> property Eleventy provides us is a relative URL, whereas the URLs we got from DEV are absolute.</p>\n<p>Sounds like a job for the <code>URL</code> class!</p>\n<pre><code class=\"language-js\">function makeRelativeUrl(url) {\n\tconst urlObj = new URL(url)\n\t// you might want to append url.search and url.hash too\n    // but it's unlikely, and a small amount of tech debt is\n    // good for the soul\n\treturn urlObj.pathname\n}\n\n...\n({canonical_url, url}) =&gt; [makeRelativeUrl(canonical_url), url])\n</code></pre>\n<p>Now you should see links on any post that is syndicated to DEV.</p>\n<h2 id=\"the-code\">Appendix: The Final Data File</h2>\n<pre><code class=\"language-js\">const fetch = require('node-fetch')\n\nfunction makeRelativeUrl(url) {\n\tconst urlObj = new URL(url)\n\treturn urlObj.pathname\n}\n\nmodule.exports = fetch('https://dev.to/api/articles?username=dza')\n    .then(res =&gt; res.json())\n    .then(articles =&gt; articles.map(\n        ({canonical_url, url}) =&gt; [makeRelativeUrl(canonical_url), url]))\n    .then(Object.fromEntries)\n</code></pre>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-09-17T21:10:00Z\">\n                17 Sep ’20 21:10</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/add-syndication-links-on-your-personal-site-using-dev-to-api-395\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"I recently started using the [RSS publishing][rsspub] option to syndicate posts from my personal site to DEV. This allows people to post comments under my posts, many of which might contain useful info. To allow any readers to easily discover and participate in these discussions, I've added links to the dev.to copies under my posts.\n\n([Skip to code](#the-code))\n\nI could, of course, add these links manually whenever I post, but being armed with such a powerful static site generator as Eleventy, I couldn't possibly _not_ automate it.\n\n## The API and the Data File\n\nEleventy users will be aware of how easy it is to gather all kinds of data from various APIs and put it in your site, all statically without client-side code. Our task here is exceptionally simple: Pick out the `url` and `canonical_url` properties for each article, and create a mapping from the latter to the former.\n\n~~~js\nconst fetch = require('node-fetch')\n\nmodule.exports = fetch('https://dev.to/api/articles?username=dza')\n    .then(res => res.json())\n    .then(articles => articles.map(\n        ({canonical_url, url}) => [canonical_url, url]))\n    .then(Object.fromEntries)\n~~~\n\n**Note:** If you are copy-and-pasting this code, make sure to replace `dza` with your own dev.to username.\n\nThis will give us an object like this:\n\n~~~json\n{\n\t\"https://www.denizaksimsek.com/2020/css-additional-box-shadow/\":\n\t    \"https://dev.to/dza/css-adding-additional-box-shadows-2lob\",\n\t...\n}\n~~~\n\nNow let's try using it in our templates:\n\n~~~liquid\n{%if devToSyndication[page.url]%}\n<section class=\"syndication-links\">\nThis article is syndicated to <a class=\"u-syndication\"\n    href=\"{{devToSyndication[page.url]}}\">DEV</a>, where you can comment on it.\n</section>\n{%endif%}\n~~~\n\nSmall problem: the `page.url` property Eleventy provides us is a relative URL, whereas the URLs we got from DEV are absolute.\n\nSounds like a job for the `URL` class!\n\n~~~js\nfunction makeRelativeUrl(url) {\n\tconst urlObj = new URL(url)\n\t// you might want to append url.search and url.hash too\n    // but it's unlikely, and a small amount of tech debt is\n    // good for the soul\n\treturn urlObj.pathname\n}\n\n...\n({canonical_url, url}) => [makeRelativeUrl(canonical_url), url])\n~~~\n\nNow you should see links on any post that is syndicated to DEV.\n\n## Appendix: The Final Data File {#the-code}\n\n~~~js\nconst fetch = require('node-fetch')\n\nfunction makeRelativeUrl(url) {\n\tconst urlObj = new URL(url)\n\treturn urlObj.pathname\n}\n\nmodule.exports = fetch('https://dev.to/api/articles?username=dza')\n    .then(res => res.json())\n    .then(articles => articles.map(\n        ({canonical_url, url}) => [makeRelativeUrl(canonical_url), url]))\n    .then(Object.fromEntries)\n~~~\n\n[rsspub]:  https://dev.to/settings/publishing-from-rss\n","url":"https://denizaksimsek.com/2020/devto-syndication-links/","date_published":"2020-09-17T21:10:00Z"},{"id":"/2020/cobanli-pier/","title":"Çobanlı Pier, Samsun, Turkey","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Çobanlı Pier, Samsun, Turkey</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Çobanlı Pier, Samsun, Turkey\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/cobanli-pier/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG20200913191508.jpg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Çobanlı Pier, Samsun, Turkey</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Çobanlı Pier, Samsun, Turkey</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>Nothing gets me quite like city lights reflecting off water. Though it’s usually rainwater on asphalt.</p>\n<p><a href=\"/assets/photos/IMG20200913191508.jpg\"><img src=\"/assets/photos/IMG20200913191508.jpg\" alt=\"Late at night, true to its name, the Black Sea is indistinguishable from the sky, besides the reflections of the buildings and streetlamps ahead\"></a></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-09-13T19:15:08Z\">\n                13 Sep ’20 19:15</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"Nothing gets me quite like city lights reflecting off water. Though it's usually rainwater on asphalt.\n\n[![<%= it.photo.alt %>](<%= it.photo.src %>)](<%= it.photo.src %>)\n","url":"https://denizaksimsek.com/2020/cobanli-pier/","image":"https://denizaksimsek.com/assets/photos/IMG20200913191508.jpg","date_published":"2020-09-13T19:15:08Z"},{"id":"/2020/ordu-street-sign/","title":"A street sign in Ordu","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>A street sign in Ordu</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"A street sign in Ordu\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/ordu-street-sign/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG20200903164220.jpeg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>A street sign in Ordu</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">A street sign in Ordu</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p><a href=\"/assets/photos/IMG20200903164220.jpeg\"><img src=\"/assets/photos/IMG20200903164220.jpeg\" alt=\"Old, fading street sign, with text Yeniköy Caddesi ↞ and the logo of the Metropolitan Municipality of Ordu.\"></a></p>\n<p>Street sign in Ordu. The layout seems inspired by street signs in İstanbul, but those are red and lack a garish logo at the bottom, instead featuring the names and color codes of the districts of İstanbul. I should post a photo if I end up going there for some reason. <ins>Update 06/01/2021: <a href=\"https://denizaksimsek.com/2020/istanbul-street-sign/\">I did!</a></ins></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-09-03T16:42:00Z\">\n                03 Sep ’20 16:42</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"[![<%= it.photo.alt %>](<%= it.photo.src %>)](<%= it.photo.src %>)\n\nStreet sign in Ordu. The layout seems inspired by street signs in İstanbul, but those are red and lack a garish logo at the bottom, instead featuring the names and color codes of the districts of İstanbul. I should post a photo if I end up going there for some reason. <ins>Update 06/01/2021: [I did!](https://denizaksimsek.com/2020/istanbul-street-sign/)</ins>\n","url":"https://denizaksimsek.com/2020/ordu-street-sign/","image":"https://denizaksimsek.com/assets/photos/IMG20200903164220.jpeg","date_published":"2020-09-03T16:42:00Z"},{"id":"/2020/ardesen-rize-tr/","title":"Ardeşen, Rize, Turkey","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Ardeşen, Rize, Turkey</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Ardeşen, Rize, Turkey\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/ardesen-rize-tr/\">\n    <meta property=og:type content=article>\n        \t<meta property=og:image content=\"/assets/photos/IMG20200825184721.jpg\">\n        <meta name=twitter:card content=\"summary_large_image\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Ardeşen, Rize, Turkey</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Ardeşen, Rize, Turkey</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>A few pics from there.</p>\n<p><a href=\"/assets/photos/IMG20200825184721.jpg\"><img src=\"/assets/photos/IMG20200825184721.jpg\" alt=\"Sunset over the sea, reflecting off a nearby car\"></a></p>\n<p><a href=\"/assets/photos/IMG20200902180736.jpg\"><img src=\"/assets/photos/IMG20200902180736.jpg\" alt=\"The dark clouds are painted yellow by the sun, the sea is set ablaze\"></a></p>\n<p><a href=\"/assets/photos/IMG20200902184220.jpg\"><img src=\"/assets/photos/IMG20200902184220.jpg\" alt=\"The beach is covered in rocks as far as the eyes can see, and even further\"></a></p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-09-02T22:40:22Z\">\n                02 Sep ’20 22:40</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"A few pics from there.\n\n[![<%= it.photo.alt %>](<%= it.photo.src %>)](<%= it.photo.src %>)\n\n[![The dark clouds are painted yellow by the sun, the sea is set ablaze](/assets/photos/IMG20200902180736.jpg)](/assets/photos/IMG20200902180736.jpg)\n\n[![The beach is covered in rocks as far as the eyes can see, and even further](/assets/photos/IMG20200902184220.jpg)](/assets/photos/IMG20200902184220.jpg)\n","url":"https://denizaksimsek.com/2020/ardesen-rize-tr/","image":"https://denizaksimsek.com/assets/photos/IMG20200825184721.jpg","date_published":"2020-09-02T22:40:22Z"},{"id":"/2020/css-additional-box-shadow/","title":"CSS — Adding Additional Box Shadows","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>CSS — Adding Additional Box Shadows</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"CSS — Adding Additional Box Shadows\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/css-additional-box-shadow/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>CSS — Adding Additional Box Shadows</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">CSS — Adding Additional Box Shadows</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>When you want to add a box shadow to an element, but don’t want to override any it might already have. (<a href=\"#the-code\">Jump to code</a>)</p>\n<h2>Less-than-amazing solutions</h2>\n<p>We could copy the <code>box-shadow</code> property from the element in question, paste it and append our additional shadow at the end. Of course, this requires us to know the box-shadow on the target element, which might not always be possible (say, making a reusable <code>.highlight-glow</code> class).</p>\n<p><a href=\"/stackoverflow-says\">StackOverflow says to put the shadow on an absolutely-positioned pseudo-element</a>. This leaves us with a similar problem to the one we started with — the element in question might already have styles on the <code>::before</code> or <code>::after</code> that conflicts with ours.</p>\n<h2 id=\"the-code\">My solution</h2>\n<p>Use CSS variables to apply box shadows.</p>\n<pre><code class=\"language-css\">.shadowy-figure {\n    --box-shadow: .5em .5em 1em #333;\n    box-shadow: var(--box-shadow);\n}\n</code></pre>\n<p>Later on, you can tack things on at the end of this variable, like so:</p>\n<pre><code class=\"language-css\">.highlight {\n    box-shadow: var(--box-shadow), 0 0 .1em #fff inset;\n}\n</code></pre>\n<p>One drawback is that we cannot use this technique in several classes and apply them to one element. We can solve this problem when <a href=\"/parent-var\"><code>parent-var()</code></a> is a thing in CSS — if it already is by the time you read this post, do let me know!</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-09-02T12:54:44Z\">\n                02 Sep ’20 12:54</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/css-adding-additional-box-shadows-2lob\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"When you want to add a box shadow to an element, but don't want to override any it might already have. ([Jump to code](#the-code))\n\n## Less-than-amazing solutions\n\nWe could copy the `box-shadow` property from the element in question, paste it and append our additional shadow at the end. Of course, this requires us to know the box-shadow on the target element, which might not always be possible (say, making a reusable `.highlight-glow` class).\n\n[StackOverflow says to put the shadow on an absolutely-positioned pseudo-element](stackoverflow-says). This leaves us with a similar problem to the one we started with --- the element in question might already have styles on the `::before` or `::after` that conflicts with ours.\n\n## My solution {#the-code}\n\nUse CSS variables to apply box shadows.\n\n~~~css\n.shadowy-figure {\n    --box-shadow: .5em .5em 1em #333;\n    box-shadow: var(--box-shadow);\n}\n~~~\n\nLater on, you can tack things on at the end of this variable, like so:\n\n~~~css\n.highlight {\n    box-shadow: var(--box-shadow), 0 0 .1em #fff inset;\n}\n~~~\n\nOne drawback is that we cannot use this technique in several classes and apply them to one element. We can solve this problem when [`parent-var()`](parent-var) is a thing in CSS --- if it already is by the time you read this post, do let me know!\n\n[stackoverflow-says]:  https://stackoverflow.com/a/11486224\n[parent-var]:          https://lists.w3.org/Archives/Public/www-style/2012Aug/0891.html\n","url":"https://denizaksimsek.com/2020/css-additional-box-shadow/","date_published":"2020-09-02T12:54:44Z"},{"id":"/2020/instant-edit-11ty/","title":"Tip: Instantly Edit Your Eleventy Site on Github","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Tip: Instantly Edit Your Eleventy Site on Github</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Tip: Instantly Edit Your Eleventy Site on Github\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/instant-edit-11ty/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Tip: Instantly Edit Your Eleventy Site on Github</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Tip: Instantly Edit Your Eleventy Site on Github</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>You’re looking at the blog post you made yesterday, when suddenly a typo catches your eye. What is the fastest way to fix it? (<a href=\"#the-code\">Skip to code</a>).</p>\n<p><strong>Note:</strong> This tip assumes you use GitHub. It could likely be adapted easily for other Git providers.</p>\n<p>Add the following to your base layout (note the <code>{{}}</code> and replace <code>&lt;username&gt;/&lt;repo&gt;</code> with the repo for the site):</p>\n<pre><code class=\"language-html\">&lt;script&gt;\n  addEventListener('keyup', e =&gt; {\n    if (e.shiftKey === true) {\n      switch (e.keyCode) {\n      case 69: // E\n        window.location = 'https://github.com/&lt;username&gt;/&lt;repo&gt;/edit/main/{{page.inputPath}}'\n        break\n      }\n    }\n  })\n&lt;/script&gt;\n</code></pre>\n<p>When you press <kbd>Shift+E</kbd>, the GitHub editor will open to the current page! The switch statement is there because I used to have a few more hotkeys.</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-08-17T20:12:00Z\">\n                17 Aug ’20 20:12</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/tip-instantly-edit-your-eleventy-site-on-github-5cme\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"You're looking at the blog post you made yesterday, when suddenly a typo catches your eye. What is the fastest way to fix it? ([Skip to code](#the-code)).\n\n**Note:** This tip assumes you use GitHub. It could likely be adapted easily for other Git providers.\n\nAdd the following to your base layout (note the `{{}}` and replace `<username>/<repo>` with the repo for the site):\n\n~~~html\n<script>\n  addEventListener('keyup', e => {\n    if (e.shiftKey === true) {\n      switch (e.keyCode) {\n      case 69: // E\n        window.location = 'https://github.com/<username>/<repo>/edit/main/{{page.inputPath}}'\n        break\n      }\n    }\n  })\n</script>\n~~~\n\nWhen you press <kbd>Shift+E</kbd>, the GitHub editor will open to the current page! The switch statement is there because I used to have a few more hotkeys.\n","url":"https://denizaksimsek.com/2020/instant-edit-11ty/","date_published":"2020-08-17T20:12:00Z"},{"id":"/2020/11ty-inline-google-fonts/","title":"Eleventy — Dynamically Inlining Google Fonts","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Eleventy — Dynamically Inlining Google Fonts</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Eleventy — Dynamically Inlining Google Fonts\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/11ty-inline-google-fonts/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Eleventy — Dynamically Inlining Google Fonts</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Eleventy — Dynamically Inlining Google Fonts</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <x-warning>\n<p><strong>Update <time>2021-02-07</time>:</strong> I have learned since writing this post that this is a bad idea, since Google Fonts will serve different CSS based on the browser. If you want fast loading, vendor your fonts and use <code class=\"language-html\">&lt;link rel=preload&gt;</code>.</p>\n</x-warning>\n<p>All in the name of Lighthouse scores.</p>\n<p>When you add Google Fonts to your website by copying the link from the <a href=\"http://fonts.google.com/\">fonts.google.com</a> website, you create a request chain. That is, after loading the HTML of your page, the browser then needs to request a bit of CSS from Google, and only then can it start loading the fonts themselves. A simple solution to this is to copy the CSS returned by Google and paste it into a <code>&lt;style&gt;</code> element in your page. However, I like to change fonts quite frequently, and I’d like to automate this process. This is easy with Eleventy.</p>\n<h2>A JavaScript data file</h2>\n<p>We simply fetch the CSS at build time.</p>\n<pre><code class=\"language-js\">// _data/googleFontsStylesheet.js\n\nconst fetch = require('node-fetch')\n\nconst url = 'the URL in the Google Fonts stylesheet link href'\n\nmodule.exports = fetch(url).then(res =&gt; res.text())\n</code></pre>\n<p>And include it in our base layout.</p>\n<pre><code class=\"language-liquid\">&lt;style type=&quot;text/css&quot;&gt;{{googleFontsStylesheet|safe}}&lt;/style&gt;\n</code></pre>\n<p>Enjoy your improved Lighthouse scores!</p>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-07-24T14:33:00Z\">\n                24 Jul ’20 14:33</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/eleventy-dynamically-inlining-google-fonts-2geg\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"::: Warning :::\n**Update <time>2021-02-07</time>:** I have learned since writing this post that this is a bad idea, since Google Fonts will serve different CSS based on the browser. If you want fast loading, vendor your fonts and use `<link rel=preload>`{.language-html}.\n:::::::::::::::\n\nAll in the name of Lighthouse scores.\n\nWhen you add Google Fonts to your website by copying the link from the fonts.google.com website, you create a request chain. That is, after loading the HTML of your page, the browser then needs to request a bit of CSS from Google, and only then can it start loading the fonts themselves. A simple solution to this is to copy the CSS returned by Google and paste it into a `<style>` element in your page. However, I like to change fonts quite frequently, and I'd like to automate this process. This is easy with Eleventy.\n\n## A JavaScript data file\n\nWe simply fetch the CSS at build time.\n\n~~~js\n// _data/googleFontsStylesheet.js\n\nconst fetch = require('node-fetch')\n\nconst url = 'the URL in the Google Fonts stylesheet link href'\n\nmodule.exports = fetch(url).then(res => res.text())\n~~~\n\nAnd include it in our base layout.\n\n~~~liquid\n<style type=\"text/css\">{{googleFontsStylesheet|safe}}</style>\n~~~\n\nEnjoy your improved Lighthouse scores!\n","url":"https://denizaksimsek.com/2020/11ty-inline-google-fonts/","date_published":"2020-07-24T14:33:00Z"},{"id":"/2020/11ty-layout-as-partial/","title":"Eleventy — Using A Layout As A Partial (The Most Useful Things...)","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Eleventy — Using A Layout As A Partial (The Most Useful Things...)</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Eleventy — Using A Layout As A Partial (The Most Useful Things...)\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/11ty-layout-as-partial/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Eleventy — Using A Layout As A Partial (The Most Useful Things...)</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Eleventy — Using A Layout As A Partial (The Most Useful Things...)</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <x-warning>\n<p><strong>Update 2021-02-08:</strong> This article mentions things about my website that are no longer true. The technique explained, however, still works fine.</p>\n</x-warning>\n<p>On this website, I have both a Notes page and pages for each individual note. I wanted to use the same template for both, but I ran into an issue: accessing frontmatter data.</p>\n<p>In Eleventy, frontmatter data for the current page is merged into the <a href=\"https://www.11ty.dev/docs/data-cascade/\">data cascade</a> and made available on the template global scope, whereas collection items have a <code>data</code> property. This made it hard to write templates that work with both the <code>page</code> object and collection items.</p>\n<h2>Solution</h2>\n<x-fig>\n<blockquote>\n<p>[…] the most useful things are usually cloaked in an air of nonchalance, even in documentation.</p>\n</blockquote>\n<figcaption>\n\t&mdash; <cite>YouTube user\n\t<a href=\"https://www.youtube.com/channel/UCjknfwYaYZvv94AjL10NO0Q\">Bantu Tu</a>,\n\tcommenting on\n\t<a href=\"https://www.youtube.com/watch?v=a6Q8Na575qc\">Missing Semester: Lecture 3: Editors (vim) (2020)</a>\n\t</cite>\n</figcaption>\n</x-fig>\n<p>The solution to my problem was right there in the Eleventy docs, staring at me, its description phrased such that it couldn’t possibly be of use to anyone.</p>\n<x-fig>\n<blockquote>\n<h3>Also <code>getCollectionItem</code></h3>\n<p>For completeness, a <code>getCollectionItem</code> filter is also included that fetches the current page from a collection.</p>\n</blockquote>\n<figcaption>&mdash; <cite><a href=\"https://www.11ty.dev/docs/filters/collection-items/#also-getcollectionitem\">Eleventy docs</a></cite></figcaption>\n</x-fig>\n<p>With this filter, all my problems were solved. I prepended the following to my <code>note.njk</code> template (similarly for <code>post.njk</code>):</p>\n<pre><code class=\"language-liquid\">{%set item = note | d(collections.note | getCollectionItem(page))%}\n</code></pre>\n<p>When using <code>note.njk</code> as a partial, the caller assigns the variable <code>note</code>. When using it as a layout, no such variable will exist and the default (<code>d()</code>) will be used… The beauty of this configuration is that in both situations, <code>item</code> will be a collection item with a <code>data</code> property.</p>\n<p>One caveat is that because I’m using this as a partial, I can’t use frontmatter, and therefore can’t set a layout for my layout (maybe I could use template data files…). Instead, I use Nunjucks’ builtin <a href=\"https://mozilla.github.io/nunjucks/templating.html#template-inheritance\">template inheritance</a>.</p>\n<pre><code class=\"language-liquid\">{%if not note%}\n{%extends &quot;base.njk&quot;%}\n{%endif%}\n...\n{%block content%}\n{%set item = note | d(collections.note | getCollectionItem(page))%}\n...\n{%endblock%}\n</code></pre>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-06-08T14:50:00Z\">\n                08 Jun ’20 14:50</time>\n\n    </footer>\n\n        \n            <footer class=\"box\">\n                            <p>\n                    <strong>Elsewhere:</strong>\n                                            <a rel=\"syndication\" href=\"https://dev.to/dz4k/eleventy-using-a-layout-as-a-partial-the-most-useful-things-182n\">\n                            DEV</a>                                    </p>\n            \n                                </footer>\n    </article>\n</body>\n</html>\n","content_text":"::: Warning ::: \n**Update 2021-02-08:** This article mentions things about my website that are no longer true. The technique explained, however, still works fine.\n:::::::::::::::\n\nOn this website, I have both a Notes page and pages for each individual note. I wanted to use the same template for both, but I ran into an issue: accessing frontmatter data.\n\nIn Eleventy, frontmatter data for the current page is merged into the [data cascade][] and made available on the template global scope, whereas collection items have a `data` property. This made it hard to write templates that work with both the `page` object and collection items.\n\n## Solution\n\n::: fig\n> [...] the most useful things are usually cloaked in an air of nonchalance, even in documentation.\n\n<figcaption>\n\t&mdash; <cite>YouTube user\n\t<a href=\"https://www.youtube.com/channel/UCjknfwYaYZvv94AjL10NO0Q\">Bantu Tu</a>,\n\tcommenting on\n\t<a href=\"https://www.youtube.com/watch?v=a6Q8Na575qc\">Missing Semester: Lecture 3: Editors (vim) (2020)</a>\n\t</cite>\n</figcaption>\n:::\n\nThe solution to my problem was right there in the Eleventy docs, staring at me, its description phrased such that it couldn't possibly be of use to anyone.\n\n::: fig\n> ### Also `getCollectionItem`\n>\n> For completeness, a `getCollectionItem` filter is also included that fetches the current page from a collection.\n\n<figcaption>&mdash; <cite><a href=\"https://www.11ty.dev/docs/filters/collection-items/#also-getcollectionitem\">Eleventy docs</a></cite></figcaption>\n:::\n\nWith this filter, all my problems were solved. I prepended the following to my `note.njk` template (similarly for `post.njk`):\n\n~~~liquid\n{%set item = note | d(collections.note | getCollectionItem(page))%}\n~~~\n\nWhen using `note.njk` as a partial, the caller assigns the variable `note`. When using it as a layout, no such variable will exist and the default (`d()`) will be used.. The beauty of this configuration is that in both situations, `item` will be a collection item with a `data` property.\n\nOne caveat is that because I'm using this as a partial, I can't use frontmatter, and therefore can't set a layout for my layout (maybe I could use template data files...). Instead, I use Nunjucks' builtin [template inheritance][].\n\n~~~liquid\n{%if not note%}\n{%extends \"base.njk\"%}\n{%endif%}\n...\n{%block content%}\n{%set item = note | d(collections.note | getCollectionItem(page))%}\n...\n{%endblock%}\n~~~\n\n[data cascade]: https://www.11ty.dev/docs/data-cascade/\n[Bantu Tu]: https://www.youtube.com/channel/UCjknfwYaYZvv94AjL10NO0Q\n[missing-semester]: https://www.youtube.com/watch?v=a6Q8Na575qc\n[Eleventy docs]: https://www.11ty.dev/docs/filters/collection-items/#also-getcollectionitem\n[also-getcollectionitem]: https://www.11ty.dev/docs/filters/collection-items/\\#also-getcollectionitem\n[template inheritance]: https://mozilla.github.io/nunjucks/templating.html#template-inheritance\n","url":"https://denizaksimsek.com/2020/11ty-layout-as-partial/","date_published":"2020-06-08T14:50:00Z"},{"id":"/2020/stationery-i-like/","title":"Stationery I like","content_html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n\n<title>Stationery I like</title>\n\n<link rel=\"stylesheet\" href=\"/assets/style.css\">\n<link rel=\"stylesheet\" href=\"/assets/theme-altai.css\">\n<link rel=\"stylesheet\" href=\"/assets/fonts/fonts.css\">\n\n<link rel=\"icon\" href=\"/assets/me.jpeg\">\n\n<!-- IndieAuth -->\n<link rel=\"authorization_endpoint\" href=\"https://indieauth.com/auth\">\n<link rel=\"token_endpoint\" href=\"https://tokens.indieauth.com/token\">\n\n<!-- Webmention -->\n<link rel=\"webmention\" href=\"https://webmention.io/www.denizaksimsek.com/webmention\">\n<link rel=\"pingback\" href=\"https://webmention.io/www.denizaksimsek.com/xmlrpc\">\n\n<link rel=\"alternate\" type=\"application/atom+xml\" href=\"/feed.xml\">\n<link rel=\"alternate\" type=\"application/feed+json\" href=\"/feed.json\">\n\n<!-- human.json -->\n<link rel=\"human-json\" href=\"/human.json\">\n\n    <meta property=og:title content=\"Stationery I like\">\n    <meta property=og:url content=\"https://denizaksimsek.com/2020/stationery-i-like/\">\n    <meta property=og:type content=article>\n        <meta name=twitter:card content=\"summary\">\n    <meta name=twitter:site content=@DenizAksimsek>\n    <meta name=twitter:creator content=@DenizAksimsek>\n\n<title>Stationery I like</title>\n</head>\n\n<body>\n    <article class=\"container h-entry\">\n    <header>\n        <nav class=\"container\">\n            <a href=\"/en\" class=\"p-author h-card big unlink\">\n                <img class=\"u-photo\" src=\"/assets/me.jpeg\" alt=\"\" width=\"150\" height=\"150\"\n                    style=\"height: 2em; width: auto; vertical-align: middle\">\n                <strong>\n                                            <span class=\"p-name\" lang=\"tr\">Deniz Akşimşek</span>\n                                    </strong>\n                <span lang=\"tr\" style=\"font-weight: normal\">deniz.aksimsek.tr</span>\n            </a>\n        </nav>\n                    <h1 class=\"p-name\">Stationery I like</h1>\n                \n\n                                                \n        </dl>\n            </header>\n\n    \n    <div class=\"e-content\">\n        <p>Reviews for things I used, and a wishlist I try to keep small.</p>\n<h2>Things I have used</h2>\n<h3><a href=\"https://www.uniball.com.tr/urunler/roller/uniball-air/uba-188-m\">Uni-ball Air Micro — Rollerball Pen</a></h3>\n<p>Has a tip made of some kind of plastic, super smooth to write, deep black ink with shading effect. The Micro ones have a nicer looking design.</p>\n<h3><a href=\"https://www.studioiggy.com/collections/i%CC%87nce-defterler\">Iggy Stationery Thin Notebooks</a></h3>\n<p>Great paper for the price — takes well to many pens. Subtle, sparse dot grid. Considerate design with lovely page size (bit wider than A5). Velvety cover does not go well with greasy hands. Most of the cover designs are not my style.</p>\n<h3><a href=\"http://www.smartofis.com.tr/kagit-grubu/Suni-deri-not-defteri\">Globox Leather Design Notebooks</a></h3>\n<p>These are how you know it’s Back To School season in Turkey. As far as I can find, the only place you can get these is <span lang=tr>A-101</span> (TR supermarket chain). It takes well to a lot of pens — the paper quality is amazing for the price. The plain design is quite nice in my opinion, with vibrant colors or plain black/dark blue options available. I shouldn’t praise them too much, lest they become more expensive…</p>\n <!--\n\n-   [Uni Jetstream SX-217](https://www.uniball.com.tr/urunler/jetstream/jetstream/sx-217){#uni-jetstream}\n\n    TODO: I should write a blog post about this\n -->\n<h2>Things I want (curse you, JetPens)</h2>\n<h3>Pen</h3>\n<ul>\n<li><a href=\"https://www.jetpens.com/Pilot-Multi-Ball-Rollerball-Pen-Fine-Black/pd/2760\">Pilot Multi Ball</a></li>\n<li><a href=\"https://www.jetpens.com/Kuretake-Fudegokochi-Brush-Pen-Extra-Fine-Black/pd/2659\">Kuretake Fudegokochi Brush Pen</a></li>\n<li><a href=\"https://www.wellappointeddesk.com/2019/11/pen-review-pentel-artist-brush-sign-pen/\">Pentel Artist Brush Sign Pen</a></li>\n</ul>\n<h3>Notebooks</h3>\n<ul>\n<li><a href=\"https://www.sorta.la/\">Sorta Notebinder</a></li>\n</ul>\n<h3>Pen Cases</h3>\n<ul>\n<li><a href=\"https://www.jetpens.com/Sonic-Sma-Sta-Standing-Pen-Cases/ct/4885\">Sonic Sma Sta Standing Pen Case</a></li>\n<li><a href=\"https://www.wellappointeddesk.com/2019/03/review-lihit-lab-smart-fit-carrying-pouch-a6/\">Lihit Lab Smart Fit Carrying Pouch — A6</a></li>\n<li><a href=\"https://www.jetpens.com/Nakabayashi-Lifestyle-Tool-Wall-Box-S-Kraft/pd/22304\">Nakabayashi Lifestyle Tool Wall Box</a></li>\n</ul>\n<h3>Miscellaneous</h3>\n<ul>\n<li><a href=\"https://www.jetpens.com/Raymay-Pencut-Mini-Scissors/ct/1623\">Raymay Pencut Mini Scissors</a></li>\n</ul>\n    </div>\n\n    <footer class=\"<small>\">\n        <time class=\"dt-published\" datetime=\"2020-05-22T22:10:00Z\">\n                22 May ’20 22:10</time>\n\n    </footer>\n\n        \n    </article>\n</body>\n</html>\n","content_text":"Reviews for things I used, and a wishlist I try to keep small.\n\n## Things I have used\n\n\n### [Uni-ball Air Micro --- Rollerball Pen](https://www.uniball.com.tr/urunler/roller/uniball-air/uba-188-m)\n\nHas a tip made of some kind of plastic, super smooth to write, deep black ink with shading effect. The Micro ones have a nicer looking design.\n\n### [Iggy Stationery Thin Notebooks](https://www.studioiggy.com/collections/i%CC%87nce-defterler)\n\nGreat paper for the price --- takes well to many pens. Subtle, sparse dot grid. Considerate design with lovely page size (bit wider than A5). Velvety cover does not go well with greasy hands. Most of the cover designs are not my style.\n\n### [Globox Leather Design Notebooks](http://www.smartofis.com.tr/kagit-grubu/Suni-deri-not-defteri)\n\nThese are how you know it's Back To School season in Turkey. As far as I can find, the only place you can get these is <span lang=tr>A-101</span> (TR supermarket chain). It takes well to a lot of pens --- the paper quality is amazing for the price. The plain design is quite nice in my opinion, with vibrant colors or plain black/dark blue options available. I shouldn't praise them too much, lest they become more expensive...\n\n <!--\n\n-   [Uni Jetstream SX-217](https://www.uniball.com.tr/urunler/jetstream/jetstream/sx-217){#uni-jetstream}\n\n    TODO: I should write a blog post about this\n -->\n\n## Things I want (curse you, JetPens)\n\n\n### Pen\n\n-   [Pilot Multi Ball](https://www.jetpens.com/Pilot-Multi-Ball-Rollerball-Pen-Fine-Black/pd/2760)\n-   [Kuretake Fudegokochi Brush Pen](https://www.jetpens.com/Kuretake-Fudegokochi-Brush-Pen-Extra-Fine-Black/pd/2659)\n-   [Pentel Artist Brush Sign Pen](https://www.wellappointeddesk.com/2019/11/pen-review-pentel-artist-brush-sign-pen/)\n\n### Notebooks\n\n-   [Sorta Notebinder](https://www.sorta.la/)\n\n### Pen Cases\n\n-   [Sonic Sma Sta Standing Pen Case](https://www.jetpens.com/Sonic-Sma-Sta-Standing-Pen-Cases/ct/4885)\n-   [Lihit Lab Smart Fit Carrying Pouch --- A6](https://www.wellappointeddesk.com/2019/03/review-lihit-lab-smart-fit-carrying-pouch-a6/)\n-   [Nakabayashi Lifestyle Tool Wall Box](https://www.jetpens.com/Nakabayashi-Lifestyle-Tool-Wall-Box-S-Kraft/pd/22304)\n\n### Miscellaneous\n\n-   [Raymay Pencut Mini Scissors](https://www.jetpens.com/Raymay-Pencut-Mini-Scissors/ct/1623)\n","url":"https://denizaksimsek.com/2020/stationery-i-like/","date_published":"2020-05-22T22:10:00Z"}]}