<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Deniz Akşimşek</title>
  <link href="https://dz4k.com/feed.xml" rel="self"/>
  <link href="https://dz4k.com/"/>
  <updated>2026-04-16T22:55:57Z</updated>
  <id>https://dz4k.com/</id>
  <author>
    <name>Deniz Akşimşek</name>
    <email>deniz@denizaksimsek.com</email>
  </author>
  <entry>
    <title>Psatina: a new tiny JS framework</title>
    <link href="https://dz4k.com/2026/psatina/"/>
    <updated>2026-04-16T19:06:38Z</updated>
    <id>https://dz4k.com/2026/psatina/</id>
    <content type="html">&lt;p&gt;While I was building &lt;a href=&quot;https://codeberg.org/dz4k/femtocrm&quot;&gt;FemtoCRM&lt;/a&gt;,
I needed a dynamic form for entering sale line items.
I could build some basic JS to add and remove rows from a table,
but each row’s inputs needed to have the row index in its name
(i.e. &lt;code&gt;line_items[2].quantity&lt;/code&gt;) which seemed like a pain to manage&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I reached for one of those “reactive” frameworks
where you define a template and it updates itself in response to data changes.
The obvious choice was &lt;a href=&quot;https://alpinejs.dev/&quot;&gt;Alpine.js&lt;/a&gt;, so that I could keep the sale form in the HTML template.
I don’t like having separate JS files for extremely page-specific stuff.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/alpine-claude.png&quot; alt=&quot;GitHub screenshot: alpine (public). A user you’ve blocked has previously contributed to this repository. claude.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Great, man. Love that for you.&lt;/p&gt;
&lt;p&gt;Well, it’s not &lt;em&gt;just&lt;/em&gt; that they slopped my blorbo.
I’d been trying to build a JS framework for years.
But so far, my efforts consisted of building
&lt;a href=&quot;https://codeberg.org/dz4k/js4k&quot;&gt;a collection of tiny libraries&lt;/a&gt;
and hoping a framework would arise out of that somehow.&lt;/p&gt;
&lt;p&gt;Well, it didn’t, and it turns out separate tiny libraries
get bigger when you put them together.
Over the last week or two, built &lt;a href=&quot;https://psatina.dz4k.com/&quot;&gt;Psatina&lt;/a&gt;,
a framework inspired by Vue and Alpine in 1.6 KiB (.min.br).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;template p:data=&amp;quot;{ count: 1 }&amp;quot;&amp;gt;
  &amp;lt;output&amp;gt;[| count |]&amp;lt;/output&amp;gt;
  &amp;lt;button type=&amp;quot;button&amp;quot; p:on:click=&amp;quot;update(() =&amp;gt; count++)&amp;quot;&amp;gt;Increase&amp;lt;/button&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Psatina sacrifices a lot to get its small size,
mostly in performance – I haven’t tried building a
DBMonster with it.
I consider it the 20% of Alpine I need for 80% of usecases&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.
I’m thinking about reviving my htmx clone &lt;a href=&quot;https://codeberg.org/dz4k/whet&quot;&gt;whet&lt;/a&gt; to pair with it.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;OK, it probably wouldn’t be &lt;em&gt;that&lt;/em&gt; hard.
But as mentioned, I like my JS components to be general-purpose.
For this kind of use case, the generally-applicable tool is
a reactive framework. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Not actual numbers. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>Pin files &amp; folders to your app grid on GNOME</title>
    <link href="https://dz4k.com/2026/app-grid-folders/"/>
    <updated>2026-04-16T16:00:07Z</updated>
    <id>https://dz4k.com/2026/app-grid-folders/</id>
    <content type="html">&lt;p&gt;This script generates a desktop entry that opens a folder,
with the same icon and all.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env python3

import sys, textwrap
from pathlib import Path

import gi
gi.require_version(&#39;Gtk&#39;, &#39;4.0&#39;)
from gi.repository import Gio, GLib, Gtk

def get_icon_name(path: Path):
    &amp;quot;&amp;quot;&amp;quot;Get the icon associated with a file.&amp;quot;&amp;quot;&amp;quot;
    file = Gio.File.new_for_path(path.as_posix())
    info = file.query_info(&#39;standard::icon&#39;, 0, Gio.Cancellable())
    return info.get_icon().get_names()[0]

def install_desktop_file(path: Path):
    &amp;quot;&amp;quot;&amp;quot;Generate and install desktop entry for a file or folder.&amp;quot;&amp;quot;&amp;quot;
    content = textwrap.dedent(f&#39;&#39;&#39;
      [Desktop Entry]
      Type=Application
      Version=1.0
      Name={path.name}
      Exec=xdg-open {path}
      Icon={get_icon_name(path)}
    &#39;&#39;&#39;)
    print(content)

    destpath = Path(GLib.get_user_data_dir()) / &#39;applications&#39; / (path.stem + &#39;.desktop&#39;)
    destpath.write_text(content)

if __name__ == &#39;__main__&#39;:
    install_desktop_file(Path(sys.argv[1]))
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Generative AI is not trained on &quot;data&quot;</title>
    <link href="https://dz4k.com/2026/training-data/"/>
    <updated>2026-01-23T16:37:42Z</updated>
    <id>https://dz4k.com/2026/training-data/</id>
    <content type="html">&lt;blockquote&gt;
&lt;dl&gt;
&lt;dt&gt;data&lt;/dt&gt;
&lt;dd&gt;
&lt;ol&gt;
&lt;li&gt;&lt;mark&gt;factual information&lt;/mark&gt; (such as measurements or statistics) used as a basis for reasoning, discussion, or calculation&lt;/li&gt;
&lt;/ol&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;— &lt;a href=&quot;https://www.merriam-webster.com/dictionary/data&quot;&gt;Merriam Webster, “data”&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Is a poem factual information?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not go gentle into that good night,&lt;br&gt;
Old age should burn and rave at close of day;&lt;br&gt;
Rage, rage against the dying of the light.&lt;/p&gt;
&lt;p&gt;— Dylan Thomas, &lt;cite&gt;Do Not Go Gentle into That Good Night&lt;/cite&gt;. TODO: replace with a more topical poem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Doesn’t look like it. But what about this?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;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.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh dear, that’s looking pretty factual to me! See how the magical transformation is completed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;information in digital form that can be transmitted or processed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;— M-W&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Behold, a .txt file:&lt;/p&gt;
&lt;table style=&quot;width: 100%;&quot;&gt;
  &lt;col style=&quot;width: 6ch;&quot;&gt;&lt;col&gt;
  &lt;caption&gt;Dylan Thomas, &quot;Do Not Go Gentle into That Good Night&quot;&lt;/caption&gt;
  &lt;tr&gt;&lt;td&gt;0000&lt;td&gt;D
  &lt;tr&gt;&lt;td&gt;0001&lt;td&gt;o
  &lt;tr&gt;&lt;td&gt;0002&lt;td&gt; 
  &lt;tr&gt;&lt;td&gt;0003&lt;td&gt;n
  &lt;tr&gt;&lt;td&gt;0004&lt;td&gt;o
  &lt;tr&gt;&lt;td&gt;0005&lt;td&gt;t
  &lt;tr&gt;&lt;td&gt;0006&lt;td&gt; 
  &lt;tr&gt;&lt;td&gt;0007&lt;td&gt;g
  &lt;tr&gt;&lt;td&gt;0008&lt;td&gt;o
  &lt;tr&gt;&lt;td colspan=2&gt;...
&lt;/table&gt;
&lt;p&gt;Aha! Merely by saving it in a file, I have transformed &lt;cite&gt;Do Not Go Gentle…&lt;/cite&gt; from poetry to data! It’s presented as a table, it must be so.&lt;/p&gt;
&lt;p&gt;And there’s more! The advent of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Multimedia_computer&quot;&gt;multimedia computer&lt;/a&gt; means I can perform the same trick on paintings, songs, speeches, movies and anything else your heart desires!&lt;/p&gt;
&lt;h2&gt;Maybe we shouldn’t treat everything that can be saved on a computer like soil density&lt;/h2&gt;
&lt;p&gt;Even though an amateur rhetorician (that’s a portmanteau of “rhetoric” and “magician”, I’m still working on it) like me can trivially transmute &lt;em&gt;works&lt;/em&gt; into &lt;em&gt;data&lt;/em&gt;, the two have very different moral frameworks.&lt;/p&gt;
&lt;p&gt;Depending on where you live and what your ideology is, data is either unownable, or owned by the person who &lt;em&gt;collects&lt;/em&gt; 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 &lt;em&gt;producer&lt;/em&gt; of data has no rights over it&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; — you don’t pay the wind to measure its speed.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;These are sensible if you assume that &lt;em&gt;data&lt;/em&gt; is produced involuntarily, mostly by inanimate beings, and collecting it takes effort, while &lt;em&gt;works&lt;/em&gt; are the result of intentional human labor, and are easy to “collect” since most creatives want their works seen.&lt;/p&gt;
&lt;p&gt;They’re also sensible if you assume that &lt;em&gt;data&lt;/em&gt; is collected by &lt;em&gt;researchers&lt;/em&gt; to advance science or provide reference material, and &lt;em&gt;works&lt;/em&gt; are collected by &lt;em&gt;publishers&lt;/em&gt; to sell for entertainment and edification.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;Wait… aren’t AI models data too?&lt;/h2&gt;
&lt;p&gt;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),&lt;/p&gt;
&lt;p&gt;But these powerful entities, through rituals like &lt;em&gt;word vectorization&lt;/em&gt;, &lt;em&gt;reinforcement learning with human feedback&lt;/em&gt;, and &lt;em&gt;bribing politicians&lt;/em&gt;, are now trying to transmute the database into an ownable, sellable work without turning the data inside back into works as well.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;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, &lt;data&gt;facts aren’t copyrightable&lt;/data&gt; (thanks to the landmark Supreme Court decision in Feist v. Rural Telephone Service) and &lt;mark&gt;databases are just collections of facts.&lt;/mark&gt;&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/3/3f/Aaron_Swartz_s_A_Programmable_Web_An_Unfinished_Work.pdf&quot;&gt;Aaron Swartz, A Programmable Web&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(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.)&lt;/p&gt;
&lt;p&gt;I like that facts aren’t copyrightable.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; 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.&lt;/p&gt;
&lt;p&gt;The &lt;s&gt;megacorps&lt;/s&gt; spirits continue to make money, because they can perform the bribery rituals.&lt;/p&gt;
&lt;p&gt;I cannot. So, instead, I’m trying to cast a counterspell upon the initial data-fication spell.&lt;/p&gt;
&lt;h2&gt;Training materials&lt;/h2&gt;
&lt;p&gt;I’m dropping the magic shtick now.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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”,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; 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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;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”. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;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? &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;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? &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>how I run Tailscale on an immutable Linux system</title>
    <link href="https://dz4k.com/2025/tailscale/"/>
    <updated>2025-11-23T20:05:53Z</updated>
    <id>https://dz4k.com/2025/tailscale/</id>
    <content type="html">&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;(Instructions for systemd.)&lt;/p&gt;
&lt;h2&gt;Create systemd unit&lt;/h2&gt;
&lt;p&gt;We’re going to run &lt;code&gt;tailscaled&lt;/code&gt; as a &lt;em&gt;user service&lt;/em&gt;, so create a file called &lt;code&gt;tailscaled.service&lt;/code&gt; in &lt;code&gt;~/.config/systemd/user/&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Unit]
Description=Tailscale node agent -- modified to run as user service
Documentation=https://tailscale.com/kb/
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
Restart=on-failure

# TODO: don&#39;t hardcode .local/bin, use $XDG_BIN_HOME
# problem: that&#39;s not set on most systems, and systemd env var expansion
# doesn&#39;t support default values
ExecStart=%h/.local/bin/tailscaled \
  --state=${STATE_DIRECTORY}/tailscaled.state \
  --socket=${RUNTIME_DIRECTORY}/tailscaled.sock \
  --tun=userspace-networking \
  --port=41641
ExecStopPost=%h/.local/bin/tailscaled --cleanup

RuntimeDirectory=tailscale
RuntimeDirectoryMode=0755
StateDirectory=tailscale
StateDirectoryMode=0700
CacheDirectory=tailscale
CacheDirectoryMode=0750

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wrote this based on the unit file Tailscale ships combined with some other unit files I saw online.&lt;/p&gt;
&lt;h2&gt;Install Tailscale binaries&lt;/h2&gt;
&lt;p&gt;Go to &lt;a href=&quot;https://pkgs.tailscale.com/stable/#static&quot;&gt;https://pkgs.tailscale.com/stable/#static&lt;/a&gt; to download the latest static binary builds of Tailscale. Extract and place the &lt;code&gt;tailscale&lt;/code&gt; and &lt;code&gt;tailscaled&lt;/code&gt; files in &lt;code&gt;~/.local/bin/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I wrote a script I use that does this, and starts up the service (needs systemd unit file to be created first):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;#!/usr/bin/env bash
set -Eeuo pipefail

tmp=$(mktemp -d)

tarball=$(curl -sL https://pkgs.tailscale.com/stable/?mode=json | jq -r .Tarballs.amd64)

echo &amp;quot;Downloading Tailscale into $tmp&amp;quot;
curl &amp;quot;https://pkgs.tailscale.com/stable/$tarball&amp;quot; | tar -xzC &amp;quot;$tmp&amp;quot;

systemctl --user stop tailscaled.service

tsdir=$(find &amp;quot;$tmp&amp;quot; -name &#39;tailscale_*&#39; -type d | head -n1)
cp &amp;quot;$tsdir&amp;quot;/{tailscale,tailscaled} ~/.local/bin/

systemctl --user enable tailscaled.service
systemctl --user start  tailscaled.service
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>A web developer&#39;s feed reader</title>
    <link href="https://dz4k.com/2025/webdev/"/>
    <updated>2025-07-29T08:26:40Z</updated>
    <id>https://dz4k.com/2025/webdev/</id>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Introducing the &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; element&lt;/strong&gt; — Chrozilla Dev Blog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to magically improve every aspect of your website using &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt;&lt;/strong&gt; — Warm Color Site&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; the next big thing in web development?&lt;/strong&gt; — XSLT-Tricks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to integrate &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; with Nẅxt.js&lt;/strong&gt; — Vorcel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; still not supported in iOS Safari&lt;/strong&gt; — May I Use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;doop.js: a &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; polyfill&lt;/strong&gt; — GitCub&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New &lt;code&gt;&amp;lt;zoop&amp;gt;&lt;/code&gt; element causes some screen readers to shout slurs at user&lt;/strong&gt; — Aeleveny Review&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Retrofuture: a blackletter-inspired pixel font</title>
    <link href="https://dz4k.com/2025/retrofuture/"/>
    <updated>2025-07-14T19:45:05Z</updated>
    <id>https://dz4k.com/2025/retrofuture/</id>
    <content type="html">&lt;style&gt;
@font-face { font-family: &quot;Retrofuture&quot;; src: url(/assets/fonts/Retrofuture.ttf) format(truetype) }
&lt;/style&gt;
&lt;figure style=&quot;margin: 2em 0;&quot;&gt;
&lt;div style=&quot;
  font-family: Retrofuture; hyphens: auto; font-size: 2em; color: black;
  background: lightgoldenrodyellow; padding: 0 1em;
  border: 1px solid currentcolor;&quot;&gt;
&lt;p&gt;IN CASE OF CONFLICT, consider users over authors over implementors over specifiers over theoretical purity.&lt;/p&gt;
&lt;/div&gt;
&lt;figcaption&gt;&lt;cite&gt;Retrofuture&lt;/cite&gt; sample. Quote: W3C, HTML Design Principles
  § 3.2 Priority of Constituencies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dz4k.com/assets/fonts/Retrofuture.ttf&quot; download=&quot;&quot;&gt;Download Retrofuture&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another pixel font I made. This was supposed to be a vector font. The idea was
to interpret blackletter forms with no stroke variation and a limited number of
angles. Sadly, I still can’t make vector fonts.&lt;/p&gt;
&lt;p&gt;Retrofuture replaces the wonderful &lt;a href=&quot;https://www.soft-type.com/jacquarda-bastarda&quot;&gt;Jacquarda Bastarda&lt;/a&gt;
on my website.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Hypersystem: a pixel display font for Hypermedia Systems</title>
    <link href="https://dz4k.com/2025/hypersystem/"/>
    <updated>2025-05-22T21:40:57Z</updated>
    <id>https://dz4k.com/2025/hypersystem/</id>
    <content type="html">&lt;style&gt;
@font-face { font-family: &quot;Hypersystem&quot;; src: url(/assets/fonts/Hypersystem.ttf) format(truetype) }
&lt;/style&gt;
&lt;figure style=&quot;
  font-family: Hypersystem; hyphens: auto; color: #bbf7ff; background: black;
  margin: 2em 0; padding: 0 1em; border: 1px solid currentcolor;&quot;&gt;
&lt;p&gt;Hypertexts: new forms of writing, appearing on computer screens, that will
branch or perform at the reader’s command. A hypertext is a non-sequential
piece of writing; only the computer display makes it practical.&lt;/p&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dz4k.com/assets/fonts/Hypersystem.ttf&quot; download=&quot;&quot;&gt;Download Hypersystem&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hypersystem is a new font I designed for the web version of Hypermedia Systems.&lt;/p&gt;
&lt;p&gt;Recently, I reworked the web page of our book Hypermedia Systems
(&lt;a href=&quot;https://hypermedia.systems/&quot;&gt;https://hypermedia.systems&lt;/a&gt;). I was happy with the layout, but unhappy with
how the book title looked. It was set in Jaro, a great free display font we also
used for the print release, but I didn’t think it worked to communicate the tone
of our book on the home page.&lt;/p&gt;
&lt;p&gt;After trying out a few alternatives, Carson suggested that I adapt the lettering
from the cover of the &lt;a href=&quot;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&quot;&gt;paperback edition&lt;/a&gt;. The pixel artist we hired did an
absolutely fantastic job, but we decided to roll our own for the lettering.&lt;/p&gt;
&lt;figure&gt;
  &lt;div style=&quot;display: flex; flex-flow: row nowrap; gap: 1em;&quot;&gt;
    &lt;figure style=&quot;margin: 0; flex: 1 1 auto;&quot;&gt;&lt;a href=/assets/photos/hypersys-lettering-1.png&gt;&lt;img src=/assets/photos/hypersys-lettering-1.png&gt;&lt;/a&gt;&lt;/figure&gt;
    &lt;figure style=&quot;margin: 0; flex: 1 1 auto;&quot;&gt;&lt;a href=/assets/photos/hypersys-lettering-2.png&gt;&lt;img src=/assets/photos/hypersys-lettering-2.png&gt;&lt;/a&gt;&lt;/figure&gt;
    &lt;figure style=&quot;margin: 0; flex: 1 1 auto;&quot;&gt;&lt;a href=/assets/photos/hypersys-lettering-3.png&gt;&lt;img src=/assets/photos/hypersys-lettering-3.png&gt;&lt;/a&gt;&lt;/figure&gt;
  &lt;/div&gt;
&lt;figcaption&gt;My early attempts at Hypermedia Systems cover lettering.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After trying to make off-the-shelf fonts work for a while, we eventually asked
the artist for the original PSD and I lettered in a custom title. Making it go
behind the car was Carson’s idea.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=/assets/photos/hypersys-lettering-final.png&gt;&lt;img src=/assets/photos/hypersys-lettering-final.png&gt;&lt;/a&gt;
&lt;figcaption&gt;The published cover.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The initial plan was to make an unslanted version of the lettering and put it
on the landing page as an image, but I’d recently heard about Panic’s &lt;a href=&quot;https://play.date/caps&quot;&gt;Caps&lt;/a&gt;
font design tool for the Playdate console, so I decided to give a making a whole
font a go.&lt;/p&gt;
&lt;p&gt;Caps is great, but it can only save fonts in a Playdate-specific format — a
fact I realized far too late. After much searching, I found &lt;a href=&quot;https://github.com/kreativekorp/bitsnpicas&quot;&gt;Bits’n’Picas&lt;/a&gt;, a
bitmap font tool that could both import the Playdate format and export to .ttf.&lt;/p&gt;
&lt;p&gt;The font is live on &lt;a href=&quot;https://hypermedia.systems/&quot;&gt;https://hypermedia.systems&lt;/a&gt;, both on the landing page and
in the content for chapter and section headings.&lt;/p&gt;
&lt;p&gt;Right now, Hypersystem supports ASCII, rudimentary Turkish, and a few extra
punctuation characters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dz4k.com/assets/fonts/Hypersystem.ttf&quot; download=&quot;&quot;&gt;Download Hypersystem&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>A versioning scheme for end-user software</title>
    <link href="https://dz4k.com/2025/version/"/>
    <updated>2025-01-17T10:42:50Z</updated>
    <id>https://dz4k.com/2025/version/</id>
    <content type="html">&lt;p&gt;The title is wrong. It should be more like &amp;quot;versioning scheme for software that suffers from the &lt;a href=&quot;https://xkcd.com/1172/&quot;&gt;spacebar heating&lt;/a&gt; problem, i.e. software for which a “breaking change” cannot reasonably be distinguished from a non-breaking one.&lt;/p&gt;
&lt;h2&gt;Background (feel free to skip)&lt;/h2&gt;
&lt;p&gt;I was planning on publishing the first ever alpha release of Denizen. I typed &lt;code&gt;git tag&lt;/code&gt; into my terminal and paused.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;was&lt;/em&gt; going to type &lt;code&gt;v0.0.0&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;I started looking around online for alternative versioning systems. My conclusion was that SemVer was the worst system, except for all the others.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;Concepts&lt;/h2&gt;
&lt;p&gt;A software package under CoolVer has three kinds of published version: release, pre-release and hotfix.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Releases add, remove or alter features. They may or may not break your workflow based on your use case, environment, personality and vibes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prereleases are unstable testing releases in preparation for a regular release. Each prerelease may fix bugs or add/remove/alter features.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Scheme&lt;/h2&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align:center;padding-block:1em&quot;&gt;
&lt;p&gt;REGULAR.HOTFIX[-PRERELEASE]&lt;/p&gt;
&lt;p&gt;where REGULAR = VANITY.RELEASE&lt;/p&gt;
&lt;p&gt;and PRERELEASE = CHANNEL.NUMBER&lt;/p&gt;
&lt;/div&gt;
&lt;figcaption&gt;The CoolVer versioning scheme.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;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).&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;HOTFIX is the hotfix identifier. If the release is not a hotfix, it is 0. Otherwise, it is a number that monotonically increases &lt;strong&gt;across the whole release history&lt;/strong&gt;. 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.&lt;/p&gt;
&lt;p&gt;PRERELEASE is for prereleases – it consists of a CHANNEL like “alpha”, “beta” or “rc”, and a numerical identifier.&lt;/p&gt;
&lt;p&gt;METADATA is build metadata as defined in SemVer.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.0.0-alpha.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;starting a new project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.0.0-alpha.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;still working on it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.0.0-beta.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anyone want to try it out?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;it’s stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.0.0-rc.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;i think i’m ready to start promoting this, let me know if you spot any bugs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;hey everyone look at my production-ready, blazing-fast, webscale project!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.0.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;oops, i broke the build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;added frobinator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.1.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the frobinator had a bug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.2.0-rc.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;sneak preview of barbinator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.2.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;add barbinator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.2.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;another frobinator bug we didn’t catch until now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1.1.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;backport the frobinator fix for the people who hate the barbinator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Rationale&lt;/h2&gt;
&lt;p&gt;CoolVer is syntactically compatible with SemVer and matches its sorting order. This means it can be used with most tools that expect SemVer.&lt;/p&gt;
&lt;p&gt;Using the SemVer patch field for hotfixes, but tags for prereleases ensures that releases are sorted sensibly, and is arguably more SemVer than the &lt;code&gt;-hotfix.1&lt;/code&gt; convention many projects use.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>How do I internationalize my CMS?</title>
    <link href="https://dz4k.com/2025/intl/"/>
    <updated>2025-01-16T11:02:39Z</updated>
    <id>https://dz4k.com/2025/intl/</id>
    <content type="html">&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Denizen is a CMS for personal websites.
I want to work on multi-language support –
people should be able to make websites and posts in any language they want.
(My own blog is in English, Turkish, and Toki Pona –
so this is a blocker for eating my dog food).&lt;/p&gt;
&lt;p&gt;Every aspect of this feels like something other people must have already solved,
so I wanted to publicly ask if anyone has the secret right answers.&lt;/p&gt;
&lt;h2&gt;Language picker&lt;/h2&gt;
&lt;p&gt;Requirements: Users should be able to set their site to “any language”.
I know that’s impossible to define, of course.
I’m happy to settle for “every language with an ISO-639 code”
including conlangs like Toki Pona (&lt;code&gt;tok&lt;/code&gt;), which I want for my own site.
However, I can’t find a good list anywhere.
Wikipedia has one that only lists two-letter codes,
and another that’s split across 26 pages with quite a bit of missing data.
Ideally, I could just download a big JSON file of every assigned code,
with each language’s native and English names,
as well as labels for the macrolanguages and non-language codes like ‘zxx’.&lt;/p&gt;
&lt;h2&gt;Fallback&lt;/h2&gt;
&lt;p&gt;In addition to marking up the website and the posts in it,
the language setting would be used for Denizen’s UI
including phrases in the public blog like “reply to” or “last updated”.
Since Denizen can’t possibly be translated to every ISO-639 language,
it needs a fallback mechanism.&lt;/p&gt;
&lt;p&gt;There’s three tiers of sophistication for language matching, all of which I’d ideally like to support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Simple BCP 47 matching, e.g. &lt;code&gt;en-US&lt;/code&gt; to &lt;code&gt;en&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sister language matching, e.g. Dutch &lt;code&gt;nl&lt;/code&gt; and Afrikaans &lt;code&gt;af&lt;/code&gt;, or Turkish &lt;code&gt;tr&lt;/code&gt; and Azerbaijani &lt;code&gt;az&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unrelated language matching. I could swear I saw Wikipedia using Turkish as a fallback for Laz &lt;code&gt;lzz&lt;/code&gt;,
but I can’t reproduce it. Nevertheless, it would be a desirable feature.
For context, Laz and Turkish are not related linguistically (beyond loanwords),
but most Laz speakers are bilingual in Turkish.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here, &lt;a href=&quot;https://cldr.unicode.org/&quot;&gt;CLDR&lt;/a&gt; seems to promise a fix, but I couldn’t figure out how.&lt;/p&gt;
&lt;h2&gt;Selecting language for individual posts&lt;/h2&gt;
&lt;p&gt;The big social medias use heuristics to detect language.
It’s expensive and goes wrong sometimes.&lt;/p&gt;
&lt;p&gt;Is there an alternative?
If someone uses denizen to post mixed language articles (e.g. language learning material),
will they annotate it all correctly? Can I somehow do it for them?
Can I help them? Can I encourage them?&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Fruit Credits: a personal accounting app based on hledger</title>
    <link href="https://dz4k.com/2024/fruit-credits/"/>
    <updated>2024-11-02T20:00:51Z</updated>
    <id>https://dz4k.com/2024/fruit-credits/</id>
    <content type="html">&lt;p&gt;I recently published the first pre-release version of &lt;a href=&quot;https://fruitcredits.dz4k.com/&quot;&gt;Fruit Credits&lt;/a&gt; on Flathub.
(I then immediately published two more because the reviewer discovered a bug).&lt;/p&gt;
&lt;h2&gt;why: boring life stuff&lt;/h2&gt;
&lt;p&gt;After quitting my job and realizing it would be a while before I found a new one&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;,
I realized I might need to be a bit more responsible with money.
Naturally, I downloaded &lt;a href=&quot;https://hledger.org/&quot;&gt;hledger&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’d tried to use hledger before but couldn’t make a habit of it.
I was concerned that my new attempt could be a procrastination mechanism,
but to my surprise, it was actually massively helpful.
Even after one month, I felt more in control of my life than ever before,
and it’s kept me in the black since.&lt;/p&gt;
&lt;p&gt;It was hard for me at first to see the value of keeping your own books
when banks already record and present a history of transactions.
Right now, the benefits I experience are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Everything is in one place.&lt;/strong&gt;
Most people keep multiple accounts at multiple banks.
Banks are somehow all shit at application engineering,
or even just providing usable data export.
Before picking up hledger, I straight-up had no idea &lt;em&gt;how much money I had&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The data is queryable.&lt;/strong&gt;
I’m talking super basic stuff –
I’ve not even scratched the surface of what one can do with plain text accounting,
yet it’s still massively valuable to ask the computer
“how much did I spend on A since B?”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The usual reasons.&lt;/strong&gt;
It’s free software operating on free file formats.
I can see my account balances without popups offering me loans I can’t pay back.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started by typing transactions in the ledger format directly in a text editor.
Then, I started using &lt;code&gt;hledger add&lt;/code&gt;, which has autocompletion capabilities.
Unfortunately, both of these were too clunky for me –
maybe it takes getting used to, but I kept making typos in &lt;code&gt;hledger add&lt;/code&gt;
and fumbling as I tried to undo them.&lt;/p&gt;
&lt;p&gt;I imagined a GUI for adding transactions quickly that wouldn’t require me to enter things in order.
This vision would eventually feature-creep itself into a GUI version of hledger.&lt;/p&gt;
&lt;h2&gt;how: exciting computer stuff&lt;/h2&gt;
&lt;p&gt;I’d heard &lt;a href=&quot;https://tauri.app/&quot;&gt;Tauri&lt;/a&gt; was Electron but good,
which was an attractive proposition to a web developer.
It took me about a week to give up –
it turns out making a web app look good as a desktop app is harder than Discord makes it look.&lt;/p&gt;
&lt;p&gt;Being a GNOME user,
I was inspired by the many &lt;a href=&quot;https://circle.gnome.org/&quot;&gt;GNOME Circle&lt;/a&gt; apps to build something with &lt;a href=&quot;https://gtk.org/&quot;&gt;GTK4&lt;/a&gt; and &lt;a href=&quot;https://gnome.pages.gitlab.gnome.org/libadwaita/&quot;&gt;libadwaita&lt;/a&gt;.
I fired up GNOME Builder and spent about 2 days paralyzed by the language selector.
After looking at code examples online, I decided &lt;a href=&quot;https://vala.dev/&quot;&gt;Vala&lt;/a&gt; was the simplest option.&lt;/p&gt;
&lt;p&gt;I used GNOME Builder to scaffold the app, and used it to develop for a while.
Eventually, I figured out how to build the app without it, and went back to Zed.&lt;/p&gt;
&lt;p&gt;It took me a while to get everything configured –
for example, the default Builder template has a directory for translations with &lt;a href=&quot;https://en.wikipedia.org/wiki/Gettext&quot;&gt;gettext&lt;/a&gt;,
but there’s extra setup required to actually build and use them.&lt;/p&gt;
&lt;p&gt;However, between the build headaches, the programming inner loop was quite enjoyable,
especially with the new-ish &lt;a href=&quot;https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/&quot;&gt;Blueprint&lt;/a&gt; UI language.&lt;/p&gt;
&lt;h3&gt;vala is pretty cool, imo&lt;/h3&gt;
&lt;p&gt;It seems that Rust is the recommendation for new GTK apps.
Unfortunately, I never got into Rust, and
I don’t think adding the GObject memory management model into the mix would make it grow on me.&lt;/p&gt;
&lt;p&gt;For the uninitiated, GObject is a subsystem that was originally part of GTK that
provides object-oriented programming and reference-counted memory management features in C.
Though GObject has some fun features like signals,
there’s little fun to be had in writing classes in C –
even with all the macro magic provided, class definitions are full of boilerplate code.
In addition, the reference counting mechanism requires you to &lt;code&gt;g_object_ref ()&lt;/code&gt; and unref your objects manually.&lt;/p&gt;
&lt;p&gt;One feature of GObject is the ability to generate bindings to other languages,
including ones with actual OOP features,
which allows GTK apps to be implemented in a variety of languages.
However, these bindings often suffer from various impedance mismatches
between GObject and the languages’ own object models.&lt;/p&gt;
&lt;p&gt;Vala, however, is implemented with GObject in mind from the start.
It has built-in syntax for features (like signals)&lt;/p&gt;
&lt;p&gt;Though I’ve fallen in love with Vala, I can see why other people might not enjoy it.
It has unfixed compiler bugs, and a standard library that was written for C.
Most frustratingly though, refcounting is not as ergonomic as full tracing garbage collection,
and when using C libraries (including GObject based ones!)
that expect you to be comfortable with creative usage of memory,
you can run into hard-to-debug segfaults.&lt;/p&gt;
&lt;p&gt;The de facto debugger for Vala is &lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_Debugger&quot;&gt;gdb&lt;/a&gt;, and it has no Vala support.
You have to debug through the generated C.
I had no gdb experience.
Thankfully, GLib has a built in logging framework that makes printf-style debugging quite comfortable.&lt;/p&gt;
&lt;p&gt;What draws me to Vala is the feeling of consideration that it exudes from every orifice.
I would constantly discover that whatever I was trying to do,
the Vala designers knew I’d need to do it, and had implemented a feature to make it easier.&lt;/p&gt;
&lt;h3&gt;flatpacking haskell&lt;/h3&gt;
&lt;p&gt;A GTK app written in Vala and compiled with Meson is the happiest of happy paths for Flatpak.
Unfortunately, hledger, which Fruit Credits necessarily depends on, is written in Haskell.&lt;/p&gt;
&lt;p&gt;Flathub requires not only that everything is built from source,
but also that all necessary sources can be downloaded before the build process (which is sandboxed).
This is antithetical to the model used by Haskell (and npm, and cargo, and many others)
where a build tool downloads dependencies and discovers transitive dependencies at the same time.&lt;/p&gt;
&lt;p&gt;After a few days of digging through outdated resources, I understood that
a tool called &lt;a href=&quot;https://hackage.haskell.org/package/cabal-flatpak&quot;&gt;cabal-flatpak&lt;/a&gt; was the best way to generate Flatpak manifests that
fetch and compile Haskell libraries in the right order.
Unfortunately, despite seemingly being under active maintenance, the tool had bugs,
and no discernible way to report them.
Since I don’t know Haskell, I couldn’t maintain a fork,
so I wrote &lt;a href=&quot;https://github.com/flathub/com.dz4k.FruitCredits/tree/master/scripts&quot;&gt;some jq and shell to work around the bugs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Upon submitting my app to Flathub, I found out I was something of a pioneer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul role=&quot;list&quot;&gt;
&lt;li&gt;&lt;strong&gt;bbhtt:&lt;/strong&gt; why are these needed and why do you need to do this manually?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dz4k:&lt;/strong&gt; The haskell modules in the manifest are based on the output of
&lt;a href=&quot;https://hackage.haskell.org/package/cabal-flatpak-0.0&quot;&gt;cabal-flatpak&lt;/a&gt;
and the article &lt;a href=&quot;https://medium.com/@lettier/how-to-flatpak-a-haskell-app-into-flathub-86ef6d69e94d&quot;&gt;How to Flatpak a Haskell App into Flathub&lt;/a&gt;.
Are there more up-to-date resources for bundling Haskell programs?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bbhtt:&lt;/strong&gt; &lt;mark&gt;No, we don’t have haskell apps. This is probably the second or third one ever.&lt;/mark&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p class=&quot;quote-attribution&quot;&gt;– &lt;a href=&quot;https://github.com/flathub/flathub/pull/5731#discussion_r1805683874&quot;&gt;Add com.dz4k.FruitCredits by dz4k · Pull Request #5731 · flathub/flathub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;fun.&lt;/p&gt;
&lt;h2&gt;what: thermonuclear war stuff&lt;/h2&gt;
&lt;p&gt;Though Fruit Credits is pretty barebones compared to what hledger can do,
it’s crossed from “dogfooding” to actually usable software for my use cases.
Queries and transaction input work well.&lt;/p&gt;
&lt;p&gt;I’m currently working on a setup interface to create a new ledger file from scratch,
as well as making the app more beginner-friendly in general.
I’d like to add some way of editing and deleting transactions,
and some reporting features.&lt;/p&gt;
&lt;p&gt;My usage of hledger is pretty limited in the grand scheme of things,
so I won’t be able to cover every use case by myself.
I’d love it if people gave Fruit Credits a try,
even if just to tell me how it couldn’t read their journal file.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://flathub.org/apps/com.dz4k.FruitCredits&quot;&gt;get it on Flathub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeberg.org/dz4k/fruit-credits&quot;&gt;get the code on Codeberg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeberg.org/dz4k/fruit-credits/issues&quot;&gt;complain about it on Codeberg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fruitcredits.dz4k.com/&quot;&gt;look at the website on the website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I’m fine – I live with my parents, have no dependents
and still have some income via &lt;a href=&quot;https://hypermedia.systems/&quot;&gt;Hypermedia Systems&lt;/a&gt;.
I would still very much like a job though, which is why
I invested in highly demanded skills as Vala programming and Haskell Flatpak packaging. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>Building the new Hypermedia Systems</title>
    <link href="https://dz4k.com/2024/new-hypermedia-systems/"/>
    <updated>2024-06-25T15:54:17Z</updated>
    <id>https://dz4k.com/2024/new-hypermedia-systems/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Clickbait title: Can you write a bestselling book in Typst?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A shiny new edition of &lt;cite&gt;Hypermedia Systems&lt;/cite&gt; 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.&lt;/p&gt;
&lt;h2&gt;Why change what works?&lt;/h2&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;asciidoctor-pdf&lt;/code&gt; 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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the venerable Pandoc can output AsciiDoc, but oddly can’t read it – so we can’t compile our book to LaTeX.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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. &lt;code&gt;asciidoctor-web-pdf&lt;/code&gt; 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.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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. &lt;a href=&quot;https://github.com/bigskysoftware/hypermedia-systems-old/issues/122&quot;&gt;A few bugs seem to have made it into the final release&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5&quot;&gt;How to set the background color of a table cell&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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 &lt;em&gt;and removed&lt;/em&gt; before the replacement was implemented. Our editor ended up creating an index &lt;strong&gt;by hand&lt;/strong&gt;, which was appended to the PDF.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;(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.)&lt;/p&gt;
&lt;p&gt;Speaking of link aggregators,&lt;/p&gt;
&lt;h2&gt;Typst&lt;/h2&gt;
&lt;p&gt;I first stumbled upon Typst when the author’s &lt;a href=&quot;https://laurmaedje.github.io/programmable-markup-language-for-typesetting.pdf&quot;&gt;thesis&lt;/a&gt; was posted on &lt;a href=&quot;https://lobste.rs/s/zd8b6p/typst_programmable_markup_language_for&quot;&gt;the red site&lt;/a&gt;. 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.&lt;/p&gt;
&lt;p&gt;I rewrote my &lt;a href=&quot;https://cloud.dz4k.dev/s/fqfLSo4FKibzp64&quot;&gt;CV&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;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!&lt;/p&gt;
&lt;h2&gt;Porting the manuscript&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Still better than waiting for the PDF to render, though.&lt;/p&gt;
&lt;h2&gt;The code blocks&lt;/h2&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;AsciiDoc code block with callout&lt;/figcaption&gt;
&lt;pre&gt;&lt;code class=&quot;language-asciidoc&quot;&gt;[source,python]
----
print(&amp;quot;Hello, world!&amp;quot;) &amp;lt;1&amp;gt;
----
&amp;lt;1&amp;gt; The canonical first program in any language.
&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;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 &lt;a href=&quot;https://discord.gg/2uDybryKPe&quot;&gt;Typst Discord community&lt;/a&gt;, I reimplemented it in about 50 lines of &lt;a href=&quot;https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/code-callouts.typ&quot;&gt;slightly cursed Typst code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/typst-callouts.png&quot; alt=&quot;A code block with callouts in the new book&quot;&gt;&lt;/p&gt;
&lt;h3&gt;code-callouts.typ: a Hypermedia Systems literate experience&lt;/h3&gt;
&lt;p&gt;Before implementing code callouts, define a &lt;em&gt;label&lt;/em&gt;, 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 &lt;code&gt;show&lt;/code&gt; rule.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;#let processed-label = &amp;lt;TypstCodeCallout-was-processed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function &lt;code&gt;code-with-callouts&lt;/code&gt; 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.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;#let code-with-callouts(
  it /*: content(raw) */,
  callout-display: default-callout /*: (str) =&amp;gt; content */
) /*: content */ = { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Begin by checking if the code block has already been processed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;) /*: content */ = {
  if it.at(&amp;quot;label&amp;quot;, default: none) == processed-label {
    it
  } else { ... }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The dance with &lt;code&gt;it.at(&amp;quot;label&amp;quot;, default: none)&lt;/code&gt; 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.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let (callouts, text: new-text) = parse-callouts(it.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;parse-callouts&lt;/code&gt; is a function that extracts the callouts from the code block. We pass it the &lt;code&gt;text&lt;/code&gt; 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.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;#let callout-pat = regex(&amp;quot;&amp;lt;(\\d+)&amp;gt;(\\n|$)&amp;quot;)

#let parse-callouts(
  code-text /*: str */
) /*: (callouts: array(array(int)), text: str) */ = {
  let callouts /*: array(array(int)) */ = ()
  let new-text = &amp;quot;&amp;quot;
  for text-line in code-text.split(&amp;quot;\n&amp;quot;) {
    let match = text-line.match(callout-pat)
    if match != none {
      callouts.push((int(match.captures.at(0)),))
      new-text += text-line.slice(0, match.start)
    } else {
      callouts.push(())
      new-text += text-line
    }
    new-text += &amp;quot;\n&amp;quot;
  }
  (callouts: callouts, text: new-text)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have the callouts, we can render them. We do this by abusing show rules. A &lt;code&gt;raw.line&lt;/code&gt; (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 &lt;code&gt;callout-display&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;show raw.line: it =&amp;gt; {
  it
  let callouts-of-line = callouts.at(it.number - 1, default: ())
  for callout in callouts-of-line {
    callout-display(callout)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;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 &lt;code&gt;&amp;lt;1&amp;gt;&lt;/code&gt; 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 &lt;code&gt;new-text&lt;/code&gt; 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.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typst&quot;&gt;let fields = it.fields()
let _ = fields.remove(&amp;quot;text&amp;quot;)
let _ = fields.remove(&amp;quot;lines&amp;quot;)
let _ = fields.remove(&amp;quot;theme&amp;quot;)
[#raw(..fields, new-text)#processed-label]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the code that powers the code blocks in the new edition of Hypermedia Systems. All you need to do is add a &lt;code&gt;show raw.where(block: true): code-with-callouts&lt;/code&gt; to your Typst document, and you can have code blocks with callouts too!&lt;/p&gt;
&lt;p&gt;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 &lt;code&gt;code-with-callouts&lt;/code&gt; 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.&lt;/p&gt;
&lt;h2&gt;The index&lt;/h2&gt;
&lt;p&gt;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 &lt;a href=&quot;https://typst.app/universe/package/in-dexter&quot;&gt;in-dexter&lt;/a&gt;, which is small enough that I made a &lt;a href=&quot;https://github.com/bigskysoftware/hypermedia-systems-book/blob/14921c6af8849e1340fc2534bf550a7269507e12/lib/indexing.typ&quot;&gt;modified version&lt;/a&gt; of it to have full control over the index’s appearance and to add support for hierarchical terms.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/typst-index.png&quot; alt=&quot;The index of the new book&quot;&gt;&lt;/p&gt;
&lt;h2&gt;The print book&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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 &lt;em&gt;separate&lt;/em&gt; 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 &lt;code&gt;first-line-indent&lt;/code&gt;, and they know when to use it.&lt;/p&gt;
&lt;p&gt;It’s a similar story with emphasis. The &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; element in HTML is italicized by default, and similarly for &lt;code&gt;emph&lt;/code&gt; 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 &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; in all those contexts (and remember to revert it as needed). &lt;em&gt;Container style queries&lt;/em&gt; might make this easier in CSS. In Typst, it’s the default behavior.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;I want HTML and CSS to be as capable in print as they are on screens. Unfortunately, browsers don’t seem as interested – the &lt;a href=&quot;https://drafts.csswg.org/css-page/#variable-auto-sizing&quot;&gt;CSS Paged Media module&lt;/a&gt; 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.&lt;/p&gt;
&lt;!-- TODO probably remove above paragraph --&gt;
&lt;!-- TODO expand section --&gt;
&lt;h2&gt;The web book&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The old hypermedia.systems was built with the static site generator &lt;a href=&quot;https://lume.land/&quot;&gt;Lume&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;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 &lt;code&gt;at&lt;/code&gt; calls and add null checks as needed.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;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. &lt;a href=&quot;https://myriad-dreamin.github.io/shiroa/&quot;&gt;Shiroa&lt;/a&gt; is a promising project for publishing web books with Typst, for example.&lt;/p&gt;
&lt;p&gt;The web book is now live at &lt;a href=&quot;https://hypermedia.systems/&quot;&gt;hypermedia.systems&lt;/a&gt;. 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.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hypermedia.systems/book/contents&quot; rel=&quot;noopener noreferrer&quot;&gt;Read the new Hypermedia Systems online&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lulu.com/shop/deniz-ak%C5%9Fim%C5%9Fek-and-adam-stepinski-and-carson-gross/hypermedia-systems/ebook/product-jenyj66.html&quot;&gt;Get the EPUB for your e-reader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;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;amp;page=1&amp;amp;pageSize=4&quot;&gt;Buy the new print release on Lulu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Hypermedia-Systems-Carson-Gross-ebook/dp/B0CC315VJK&quot;&gt;Buy the Kindle release on Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Hypermedia-Systems-Carson-Gross/dp/B0C9S88QV6&quot;&gt;Buy the old hardcover while it’s still available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/asciidoctor/asciidoctor/wiki/How-to-set-the-background-color-of-a-table-cell/c8b6c6b2c78e66ae11b7796b9bebb3a4f4949bb5&quot;&gt;How to set the background color of a table cell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Making Kustom widgets that fit in with Nothing OS</title>
    <link href="https://dz4k.com/2024/nothing-kwgt/"/>
    <updated>2024-04-24T09:39:39Z</updated>
    <id>https://dz4k.com/2024/nothing-kwgt/</id>
    <content type="html">&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;So I tried my own hand at it, and with not much effort, I was able to make these two:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/Screenshot_20240424-121527.png&quot; alt=&quot;A Nothing Phone home screen with weather, date, next event and pedometer widgets&quot;&gt;&lt;/p&gt;
&lt;p&gt;To my eyes, that’s an exact match! The date and next event widgets are mine.&lt;/p&gt;
&lt;p&gt;If you just want to have these widgets, you can download them. If you want to make your own, read on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.dz4k.dev/s/y5aDJyPWwYiG2tb&quot;&gt;VeryFewThings Date&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.dz4k.dev/s/Zsa8Zq2wCaeH5wH&quot;&gt;VeryFewThings Next Event&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(this is my first time using Nextcloud’s sharing links, so let me know if there’s any issues)&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Width&lt;/strong&gt;: &lt;code&gt;$si(rwidth)$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Height&lt;/strong&gt;: &lt;code&gt;$si(rheight)$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paint&lt;/strong&gt;: &lt;code&gt;$si(syscn1, if(si(darkmode), 10, 93)$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;Layout&lt;/h2&gt;
&lt;p&gt;The widget should have 32 points of padding.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wide&lt;/strong&gt;: &lt;code&gt;$si(rwidth) &amp;gt; 1.8 * si(rheight)$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tall&lt;/strong&gt;: &lt;code&gt;$si(rheight) &amp;gt; 1.8 * si(rwidth)$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If neither of these are true, the widget is square.&lt;/p&gt;
&lt;h2&gt;Text&lt;/h2&gt;
&lt;p&gt;Kustom can access your device’s system fonts, including Nothing’s custom fonts.&lt;/p&gt;
&lt;p&gt;Dotted: NDot (Variant: 57 for big text, 55 for small text)
Monospace: Lettera Mono
Serif: Ntype82
Sans-serif: Roboto (weight 300)&lt;/p&gt;
&lt;p&gt;Nothing uses the NDot font exclusively in all caps, so I recommend matching that.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Text color&lt;/strong&gt;: &lt;code&gt;$si(syscn1, if(si(darkmode), 100, 0))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Biber&#39;s day out</title>
    <link href="https://dz4k.com/2024/biber-s-day-out/"/>
    <updated>2024-04-14T17:14:47Z</updated>
    <id>https://dz4k.com/2024/biber-s-day-out/</id>
    <content type="html">&lt;p&gt;We went on a walk in the woods today, and Biber came with us.&lt;/p&gt;
&lt;p&gt;We came across a cat that looks just like her.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_185845521.jpg&quot; alt=&quot;an orange tabby looking up at you&quot;&gt;&lt;/p&gt;
&lt;p&gt;He was a male cat. Biber soon caught up and they started to interact:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_185856436.jpg&quot; alt=&quot;biber and other cat stood on staring at each other&quot;&gt;&lt;/p&gt;
&lt;p&gt;I’m not an animal behaviorist and I couldn’t tell you if they were flirting or getting ready to fight.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_190258381.jpg&quot; alt=&quot;the two cats started walking in a circle still not taking their eyes off each other&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_190259914.jpg&quot; alt=&quot;biber laid down on the floor at one point&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_185953179.jpg&quot; alt=&quot;the other cat sitting, biber still laying down&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG_20240414_190052369.jpg&quot; alt=&quot;sometimes they would get further away, like 2-3 meters and keep tracking each other&quot;&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>kill all distros</title>
    <link href="https://dz4k.com/2024/kill-all-distros/"/>
    <updated>2024-03-22T00:57:02Z</updated>
    <id>https://dz4k.com/2024/kill-all-distros/</id>
    <content type="html">&lt;p&gt;so insane to me that people advocate for distro packaging over flatpak&lt;/p&gt;
&lt;p&gt;hmm yes i love being unable to use up-and-coming software thet nobody packages&lt;/p&gt;
&lt;p&gt;yet and making it harder for new distros to get off the ground&lt;/p&gt;
&lt;p&gt;vertical monopolies are good when open source i guess&lt;/p&gt;
&lt;p&gt;the concept of software package management existing is a failure&lt;/p&gt;
&lt;p&gt;one (1) software should be expressible as one thing&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Why Bamboo Paper is my favorite note taking app that I never use</title>
    <link href="https://dz4k.com/2024/bamboo-paper/"/>
    <updated>2024-01-20T16:44:00Z</updated>
    <id>https://dz4k.com/2024/bamboo-paper/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.wacom.com/en-us/products/apps-services/bamboo-paper&quot;&gt;Bamboo Paper&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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%.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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…&lt;/p&gt;
&lt;p&gt;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&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Introducing Denizen</title>
    <link href="https://dz4k.com/2023/starting-denizen/"/>
    <updated>2023-10-27T13:40:38Z</updated>
    <id>https://dz4k.com/2023/starting-denizen/</id>
    <content type="html">&lt;p&gt;I’m building Denizen (&lt;a href=&quot;https://denizen.dz4k.com/&quot;&gt;https://denizen.dz4k.com/&lt;/a&gt;), a personal website system that is IndieWeb and Fediverse compatible.&lt;/p&gt;
&lt;p&gt;Source code: &lt;a href=&quot;https://codeberg.org/dz4k/denizen&quot;&gt;https://codeberg.org/dz4k/denizen&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Using XPath in 2023</title>
    <link href="https://dz4k.com/2023/xpath/"/>
    <updated>2023-07-16T21:10:00Z</updated>
    <id>https://dz4k.com/2023/xpath/</id>
    <content type="html">&lt;p&gt;In the latest release of &lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt;,
you can add event listeners to elements with &lt;code&gt;hx-on&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;amp;lt;form hx-on::beforeRequest=&amp;quot;addCsrfToken(event);&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For all the other &lt;code&gt;hx-&lt;/code&gt; attributes, we use CSS attribute selectors.
However, with &lt;code&gt;hx-on&lt;/code&gt;, the attribute name is not fixed as it contains the event.
CSS attribute selectors support wildcards on the &lt;em&gt;value&lt;/em&gt; of attributes,
but not the name:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;[hx-trigger] /* common and normal */
[href^=&amp;quot;https://dz4k.com&amp;quot;] /* &amp;quot;starts with&amp;quot; operator */
[^ĥx-on:] /* not a thing */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;ruby&gt;X&lt;rt&gt;XML&lt;/rt&gt;Path&lt;rt&gt;Path Language&lt;/ruby&gt;&lt;/h2&gt;
&lt;p&gt;XPath is a query language for extracting information from XML(-like) documents.
Its main use cases are XSLT and parsing API responses.&lt;/p&gt;
&lt;p&gt;The XPath language is significantly more expressive than CSS,
making it possible to traverse the XML tree in any direction,
filter nodes based on arbitrary predicates,
and select any kind of node
(including comments, text nodes, and individual attributes).
Our non-existent CSS attribute could be written as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xpath&quot;&gt;//@*[starts-with(name(), &amp;quot;hx-on:&amp;quot;)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This post is not supposed to be an XPath tutorial, but I’ll break this one down:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;//&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;traverse the document (in CSS, this is the default)&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;@*&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;find any attribute (mnemonic: &lt;strong&gt;at&lt;/strong&gt;-tribute)&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;[ ... ]&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;where…&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;starts-with(name(), &amp;quot;hx-on:&amp;quot;)&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;its name starts with &lt;code&gt;&amp;quot;hx-on:&amp;quot;&lt;/code&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;CSS selectors don’t have these kinds of features,
and it has good reasons not to.
CSS has strict performance requirements
– to the point that “CSS optimization” is generally not a thing –
and selectors that offer more control could make slow selectors possible.
In addition, CSS has well-defined specificity rules, whereas XPath does not.&lt;/p&gt;
&lt;p&gt;However, while these features make CSS great for stylesheets,
CSS selectors are also the most common way to find DOM elements in
JavaScript code and lacking in that regard.
Many libraries which extend HTML do so by traversing the entire document
and finding elements manually.
This is often not needed since, if you didn’t know,
&lt;strong&gt;XPath is built into browsers.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;document.evaluate&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/Web/XPath/Introduction_to_using_XPath_in_JavaScript&quot;&gt;&lt;code&gt;document.evaluate&lt;/code&gt; API&lt;/a&gt; is somewhat archaic,
partly because it was designed for talking to XML APIs over &lt;code&gt;XMLHTTPRequest&lt;/code&gt;.
Here’s a DOM-friendly wrapper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function* xpath(...args) {
  let path, root = document;
  if (args.length &amp;gt; 1) [root, path] = args;
  else [path] = args;

  const nodeIterator = document.evaluate(
    path,
    root,
    null,
    XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
    null,
  );

  for (
    let node = nodeIterator.iterateNext();
    node != null;
    node = nodeIterator.iterateNext()
  ) {
    yield node;
  }
}

// TypeScript declaration
function xpath(path: string): Iterable&amp;lt;Node&amp;gt;;
function xpath(root: Element, path: string): Iterable&amp;lt;Node&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Programming on an Android tablet</title>
    <link href="https://dz4k.com/2023/programming-on-a-tablet/"/>
    <updated>2023-07-01T22:53:00Z</updated>
    <id>https://dz4k.com/2023/programming-on-a-tablet/</id>
    <content type="html">&lt;p&gt;I recently purchased a Samsung Galaxy Tab S7 FE.
I wasn’t planning on making it a laptop replacement,
but I had some experience with Android’s capabilities for development
(from when I only had a phone)
and the possibility was irresistible.&lt;/p&gt;
&lt;p&gt;SSH into my laptop was a no-go.
I have no idea how to expose my laptop to the internet in a way that works,
let alone is safe.
Besides, my model didn’t have mobile data support for on-the-go.
I wanted to run a Linux dev environment on the tablet itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/tablet-programming.jpg&quot; alt=&quot;The tablet, in a magnetic stand case, with a keyboard in front of it, on a desk&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Termux&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Termux is an Android terminal emulator and Linux environment app
that works directly with no rooting or setup required.
A minimal base system is installed automatically -
additional packages are available using the APT package manager.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;– &lt;a href=&quot;https://termux.dev/&quot;&gt;https://termux.dev&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Termux is not a VM, container or emulator –
it’s a terminal in the host system.
Because the places where a Linux distro would usually install software
are write-only on an Android device,
Termux places software and your home directory inside its data directory
(&lt;code&gt;TODO&lt;/code&gt;)
and uses the &lt;code&gt;$PREFIX&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;In my experience, this works until it doesn’t.
Software in the Termux repositories works as expected,
but anything that needs to be built locally or piped to &lt;code&gt;sh&lt;/code&gt; from an URL
will likely be confused by the odd directory structure.&lt;/p&gt;
&lt;p&gt;In my phone-only days, this worked fine.
However, I have projects now that depend on software not packaged for Termux.&lt;/p&gt;
&lt;p&gt;Can’t I just have a container or something?&lt;/p&gt;
&lt;h2&gt;PRoot-Distro&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;PRoot is a user-space implementation of chroot, mount --bind, and binfmt_misc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m not super clear on the details,
but it seems that Android doesn’t let you do most of the things you need for a
container runtime.
PRoot is a solution to that.&lt;/p&gt;
&lt;p&gt;The Termux project maintains a fork of PRoot as well as a wrapper program
called PRoot-Distro.
PRoot-Distro has an interface that will be familiar to anyone who has used
Distrobox or toolbx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proot-distro create fedora
proot-distro login fedora
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This drops you into a fully-functional Fedora shell.
After my previous failed attempts, I was surprised to see how well it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can access the network
(this didn’t work with another PRoot solution I tried –
something to do with Android not allowing raw DNS traffic?)&lt;/li&gt;
&lt;li&gt;You can start a server on the container and access it from the host&lt;/li&gt;
&lt;li&gt;You can access the host Android filesystem
(not the Termux home, the one that has your pictures and
WhatsApp voice messages) from the container through the &lt;code&gt;/storage&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One thing to note is you are still on an ARM device.
Software that is not compiled for ARM will not work.
However, I only hit this once while installing Deno –
and even then, ARM builds are available through a fork.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install Termux&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install PRoot-Distro&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pkg install proot-distro
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up a container&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proot-distro create fedora
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter your container&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proot-distro login fedora
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Samsung Galaxy Tab S7 FE SM-T733&lt;/li&gt;
&lt;li&gt;Ztotop Magnetic Case&lt;/li&gt;
&lt;li&gt;Logitech K380 Bluetooth Keyboard&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>You don&#39;t need a modal window</title>
    <link href="https://dz4k.com/2023/you-dont-need-modal/"/>
    <updated>2023-05-24T05:05:00Z</updated>
    <id>https://dz4k.com/2023/you-dont-need-modal/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Nice try</title>
    <link href="https://dz4k.com/2022/nice-try/"/>
    <updated>2022-12-21T19:58:01Z</updated>
    <id>https://dz4k.com/2022/nice-try/</id>
    <content type="html">&lt;p&gt;&amp;lt;%~ await includeFile(“embed.eta”, { filters, url: “&lt;a href=&quot;https://lobste.rs/s/22yudo/kindelia_kind_next_gen_functional&quot;&gt;https://lobste.rs/s/22yudo/kindelia_kind_next_gen_functional&lt;/a&gt;” }) %&amp;gt;&lt;/p&gt;
&lt;p&gt;this is a new programming language, right?
and the people who made it are also making a blockchain thing for it to run on.
which made some people, including me, sad.
so someone laments this, and someone else comes in:&lt;/p&gt;
&lt;style&gt;
/* TODO: move this to missing.css */
blockquote blockquote { font-size: 1em }
&lt;/style&gt;
&lt;blockquote&gt;
&lt;p&gt;The author writes in the Kindelia Readme:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no native coin. It is not a cryptocurrency. It is a cryptocomputer.
Which to me says they’re focusing on the decentralized application portion of blockchains instead of the scam coins.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;now, i wasn’t convinced.
because, i’m no expert but, a blockchain can’t work without a currency, right?
it’s part of the algorithmic foundation to have some token that the chain itself can reimburse participants with…&lt;/p&gt;
&lt;p&gt;so how did they do this? did they finally invent a Good Blockchain?&lt;/p&gt;
&lt;p&gt;website takes me to readme, “There is no native coin” ok but how? readme takes me to whitepaper:&lt;/p&gt;
&lt;figure class=&quot;contents&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;As for block rewards, the same principle holds. Tokens and applications can leave rewards that only the block miner can collect. For example, &lt;mark&gt;Kindelia’s Genesis Token&lt;/mark&gt;, 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 &lt;mark&gt;doesn’t need a built-in token to have block rewards and miner fees&lt;/mark&gt;. Instead, it flexibly allows users to pay fees in whatever tokens they want, and miners to collect block rewards from &lt;mark&gt;a constellation of user-deployed tokens, rather than a single official one&lt;/mark&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;
    &lt;a href=&quot;https://github.com/Kindelia/Kindelia-Chain/blob/67f86e1bf580dbf592b3cb80e8da2ec14ce01ea7/WHITEPAPER.md&quot;
    &gt;&lt;cite&gt;Kindelia whitepaper&lt;/cite&gt;&lt;/a&gt;,
    revision 67f86e1
    (highlighting mine)
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong class=&quot;allcaps&quot;&gt;It’s a marketing trick.&lt;/strong&gt;
“No native coin” just means that the coin is pluggable.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;I’m going to commit crimes.&lt;/i&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>A Hypermedia Canvas</title>
    <link href="https://dz4k.com/2022/a-hypermedia-canvas/"/>
    <updated>2022-11-01T00:00:00Z</updated>
    <id>https://dz4k.com/2022/a-hypermedia-canvas/</id>
    <content type="html">&lt;p&gt;/fileshare/2022-09-30 a hypermedia canvas 2022-10-25.pdf&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Similarities between Java and JavaScript</title>
    <link href="https://dz4k.com/2022/java-and-javascript/"/>
    <updated>2022-10-25T03:52:36Z</updated>
    <id>https://dz4k.com/2022/java-and-javascript/</id>
    <content type="html">&lt;p&gt;Java and JavaScript are a lot more similar than we give them credit for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A much-maligned language.&lt;/li&gt;
&lt;li&gt;Old, crufty APIs.&lt;/li&gt;
&lt;li&gt;Nicer today than many people remember.&lt;/li&gt;
&lt;li&gt;Complex build tools.&lt;/li&gt;
&lt;li&gt;Frameworks and metaframeworks galore.&lt;/li&gt;
&lt;li&gt;So, so much reflection.&lt;/li&gt;
&lt;li&gt;A vast universe of libraries for everything.&lt;/li&gt;
&lt;li&gt;Runs on very cursed places. (Java on SIM cards, JavaScript on spacecrafts)&lt;/li&gt;
&lt;li&gt;How do I make this an executable?&lt;/li&gt;
&lt;li&gt;Brilliant runtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main difference:
Java programmers pretend they’re using JavaScript,
and JavaScript programmers pretend they’re using Java.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>CDNs can run code now</title>
    <link href="https://dz4k.com/2022/cdns-run-code/"/>
    <updated>2022-10-01T08:25:36Z</updated>
    <id>https://dz4k.com/2022/cdns-run-code/</id>
    <content type="html">&lt;p&gt;One area where htmx apps lag behind SPAs is interaction delay.
While htmx apps usually have a much improved initial load time,
the need for a server roundtrip for most interactions is a disadvantage.&lt;/p&gt;
&lt;p&gt;For most apps, this doesn’t matter, as they are all frontends to databases anyway.
Thus, there is always a request to the server;
the difference is whether the response is HTML or a proprietary JSON-based format that needs to be converted to HTML.&lt;/p&gt;
&lt;p&gt;The latency issue is even less of a problem thanks to… let’s call it Application on CDN (AoCDN).
The common term for this technology is &lt;no-spoiler&gt;“edge compute”&lt;/no-spoiler&gt;,
which I refuse to say.&lt;/p&gt;
&lt;p&gt;AoCDN is a service offered by some hosting and CDN providers.
It means that your code runs on several servers all across the world, close to the user.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Resource Traversal</title>
    <link href="https://dz4k.com/2022/2022-07-19T12:49:37.803Z/"/>
    <updated>2022-07-19T12:49:46Z</updated>
    <id>https://dz4k.com/2022/2022-07-19T12:49:37.803Z/</id>
    <content type="html">&lt;h2&gt;Resource Expansion&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt; GET https://example.com/users/john_doe
&amp;gt; { &amp;quot;name&amp;quot;: &amp;quot;John Doe&amp;quot;, &amp;quot;friends&amp;quot;: &amp;quot;/users/john_doe/friends&amp;quot; }

&amp;lt; GET https://example.com/users/john_doe?expand=friends
&amp;gt; { &amp;quot;name&amp;quot;: &amp;quot;John Doe&amp;quot;, &amp;quot;friends&amp;quot;: { &amp;quot;joe_bloggs&amp;quot;: &amp;quot;/users/joe_bloggs&amp;quot; } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Substitute the link named &lt;code&gt;friends&lt;/code&gt; with the resource at that link.&lt;/p&gt;
&lt;h2&gt;Resource Traversal&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt; DELETE https://example.com/users/john_doe?traverse=friends.joe_bloggs
&amp;gt; { &amp;quot;msg&amp;quot;: &amp;quot;Removed friend joe_bloggs&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Server will internally GET each link up to the last, and respond with a 307
redirect to the last.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Writing things down</title>
    <link href="https://dz4k.com/2022/writing-things-down/"/>
    <updated>2022-07-16T20:57:08Z</updated>
    <id>https://dz4k.com/2022/writing-things-down/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;the Ideal Self looks on in and says: &lt;q lang=tok style=font-style:normal&gt;pakala&lt;/q&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a few intersecting online communities I lurk around that revolve around writing things down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;notebook fans, &lt;a href=&quot;https://reddit.com/r/notebooks&quot;&gt;r/notebooks&lt;/a&gt; (“I can stop buying them whenever I want.”)&lt;/li&gt;
&lt;li&gt;plain text enjoyers (“Clearly the solution is to write a parser.”)&lt;/li&gt;
&lt;li&gt;“productivity” communities (“look at my Notion/Org Mode/Obsidian/… setup!” / unixporn for people with jobs)&lt;/li&gt;
&lt;li&gt;the Bullet Journal community (Plug in your own snarky comment for this one)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why I want to do it&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;That brings me to the second reason I want to write things down: keeping a log&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. 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 &lt;em&gt;feel&lt;/em&gt; 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?&lt;/p&gt;
&lt;h2&gt;Looking upon my own works and despairing&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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).&lt;/p&gt;
&lt;p&gt;This leads to a conflict between the two motivations for writing things down.&lt;/p&gt;
&lt;h1 lang=&quot;tok&quot;&gt;ijo nasa li pona e mi&lt;/h1&gt;
&lt;p&gt;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!&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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 &lt;em&gt;voice&lt;/em&gt;, 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.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Am I genuinely going to write about cringing at my past self? Right now? This is repugnant already! Pressing through…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;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…&lt;/p&gt;
&lt;p&gt;I think what that makes me is &lt;em&gt;self-conscious&lt;/em&gt;, 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.&lt;/p&gt;
&lt;p&gt;Maybe this is something I’ll just get over. Then writing this post down would be pretty pointless…&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But I wouldn’t care!&lt;/em&gt;&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;There is a secret third reason why I want to write things down: notebooks and text editors are &lt;strong&gt;fun&lt;/strong&gt;. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>A missing.css conundrum</title>
    <link href="https://dz4k.com/2022/2022-06-27T18:54:05.038Z/"/>
    <updated>2022-06-27T18:54:22Z</updated>
    <id>https://dz4k.com/2022/2022-06-27T18:54:05.038Z/</id>
    <content type="html">&lt;p&gt;I want to add a dark theme to missing.css out of the box using the &lt;code&gt;prefers-color-scheme&lt;/code&gt; 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:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You install missing.css&lt;/li&gt;
&lt;li&gt;You override the &lt;a href=&quot;https://missing.style/docs/variables/#--accent&quot;&gt;&lt;code&gt;--accent&lt;/code&gt;&lt;/a&gt; variable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the user has dark theme enabled system-wide, two things might happen depending on how we implement things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The dark theme accent color in missing.css overrides the custom accent&lt;/li&gt;
&lt;li&gt;The custom accent is used with missing.css’s dark theme colors, probably ugly and bad contrast&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;As for solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thoughts?&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>_hyperscript Cheatsheet</title>
    <link href="https://dz4k.com/2022/hyperscript-cheatsheet/"/>
    <updated>2022-05-06T13:14:36Z</updated>
    <id>https://dz4k.com/2022/hyperscript-cheatsheet/</id>
    <content type="html">&lt;style&gt;
:root {
    overflow: auto;
}

header {
    border-bottom: 1px solid var(--faded-fg);
}

.e-content {
    column-width: calc(var(--line-length) - var(--rhythm));
    column-gap: var(--gap);
    max-width: calc(2 * var(--line-length) + var(--gap));
    padding-inline: var(--gap);

    /* Full bleed */
    width: 100vw;
    margin-left: 50%;
    transform: translateX(-50%);
}

section.box {
    display: table;
    margin-top: 0;
    width: 100%;
}

dl div {
    margin-block: var(--gap);
}
&lt;/style&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Event listeners&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;on&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;add event listener&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;every&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;do not queue events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;mousemove&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;event name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(clientX, clientY)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;expose the event’s properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[clientX &amp;gt; 100]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;filter events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;only respond to 3rd click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3 to 10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;respond to 3rd, 4th … 10th click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3 and on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;respond to all clicks except 1st and 2nd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;from #my-form&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;element to attach listeners to, (?? me)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;debounced at 200ms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;trailing debounce (200ms delay, resets on every event)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;throttled at 200ms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;every 200ms at most regardless of the number of events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;or keyup ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;specify many events, each with its own from/debounce/…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;if events arrive while the listener is already running…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;add them to a FIFO queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;discard them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue first&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;enqueue the first one, discard the rest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;or&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue last&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;enqueue the last one, discard the rest (this is the default)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Property access&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user.data.name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user’s data’s name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name of data of user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.name of user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user’s data.name&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;CSS literals&lt;/h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;#my-form&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Get element by id&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;#{getID()}&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Dynamic ID&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;.active&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Get elements by class&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;.{getClass()}&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Dynamic class&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;&amp;lt;em, i /&amp;gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Query selector all&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;&amp;lt;ul:nth-child(${n}) /&amp;gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Dynamic selector&lt;/dd&gt;
&lt;/dl&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Sigils&lt;/h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;local variable by default&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;:foo&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;element scoped variable, persisted. can be declared with top-level set. behaviors are isolated from one another&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;$foo&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;global variable&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;@foo&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;HTML attribute&lt;/dd&gt;
&lt;/dl&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Array operations&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;first in arr&lt;/code&gt; ≡ &lt;code&gt;first from arr&lt;/code&gt;
≡ &lt;code&gt;first of arr&lt;/code&gt; ≡ &lt;code&gt;first arr&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;also &lt;code&gt;random arr&lt;/code&gt;, &lt;code&gt;last arr&lt;/code&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Finding elements&lt;/h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;closest &amp;amp;lt;section/&amp;gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;nearest enclosing section&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;previous &amp;amp;lt;section/&amp;gt; from #sec-2&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;last section that comes before section 2 (?? me)&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;next &amp;amp;lt;input, button, a/&amp;gt; from document.activeElement within #dialog with wrapping&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;element to focus when pressing Tab in a modal dialog&lt;/dd&gt;
&lt;/dl&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Other top-level features&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;behavior&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define cross-cutting behaviors that are applied to many HTML elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;def&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines a function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eventsource&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subscribe to Server Sent Events (SSE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Embed JavaScript code at the top level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines a new element-scoped variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initialization logic to be run when the code is first loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates an event listener 	on click log “clicked!”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a Web Socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;worker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a Web Worker for asynchrnous work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/section&gt;
&lt;section class=&quot;box&quot;&gt;
&lt;h2&gt;Command index&lt;/h2&gt;
&lt;dl&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;add &lt;var&gt;.class&lt;/var&gt; to &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;add @&lt;var&gt;attribute=value&lt;/var&gt; to &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;add { font-size: ${&lt;var&gt;elt&lt;/var&gt;}px; } to &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;add classes/attributes/inline styles to &lt;var&gt;elt&lt;/var&gt; (?? me)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;append &lt;var&gt;value&lt;/var&gt; to &lt;var&gt;target&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;append to strings/arrays/elements, sets it = &lt;var&gt;target&lt;/var&gt; (?? it)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;async &lt;var&gt;command&lt;/var&gt;&lt;/code&gt; | &lt;code&gt;async do &lt;var&gt;command&lt;/var&gt;… end&lt;/code&gt;
    &lt;dd&gt;run commands in a non-blocking manner
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;call &lt;var&gt;expr&lt;/var&gt; | get &lt;var&gt;expr&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;sets it = &lt;var&gt;expr&lt;/var&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;continue&lt;/code&gt;
    &lt;dd&gt;skips to next iteration in a loop
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;decrement &lt;var&gt;lvalue&lt;/var&gt; by &lt;var&gt;amount&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;sets &lt;var&gt;lvalue&lt;/var&gt; = &lt;var&gt;lvalue&lt;/var&gt; - &lt;var&gt;amount&lt;/var&gt; (?? 1)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;fetch &lt;var&gt;/url&lt;/var&gt; with &lt;var&gt;option: value&lt;/var&gt;, …&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;fetch `/url/${&lt;var&gt;id&lt;/var&gt;}/` with &lt;var&gt;option: value&lt;/var&gt;, …&lt;/code&gt;
    &lt;dd&gt;makes an HTTP request, see Fetch API docs for options
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;go to url &lt;var&gt;/url&lt;/var&gt; in new window&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;go to url `/url/${&lt;var&gt;id&lt;/var&gt;}/`&lt;/code&gt;
    &lt;dd&gt;navigate to a URL in the browser
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;go to top of &lt;var&gt;elt&lt;/var&gt; -- top/middle/bottom &lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;go to top left of &lt;var&gt;elt&lt;/var&gt; -- left/center/right&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;go to left of &lt;var&gt;elt&lt;/var&gt; smoothly -- /instantly&lt;/code&gt;
    &lt;dd&gt;scroll an element into view
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt the event’s default&lt;/code&gt;
    &lt;dd&gt;prevent default behavior
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt default&lt;/code&gt;
    &lt;dd&gt;same as above, and exits listener
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt the event’s bubbling&lt;/code&gt;
    &lt;dd&gt;stop event bubbling
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt bubbling&lt;/code&gt;
    &lt;dd&gt;same as above, and exits listener
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt the event&lt;/code&gt;
    &lt;dd&gt;stop both default and bubbling
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;halt&lt;/code&gt;
    &lt;dd&gt;all of the above
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;hide &lt;var&gt;elt&lt;/var&gt; with strategy&lt;/code&gt;
    &lt;dd&gt;see `show`
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;if &lt;var&gt;cond&lt;/var&gt; then … else … end&lt;/code&gt;
    &lt;dd&gt;if statement
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;increment&lt;/code&gt;
    &lt;dd&gt;see decrement
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;js(&lt;var&gt;var&lt;/var&gt;) … end&lt;/code&gt;
    &lt;dd&gt;embed JavaScript
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;log &lt;var&gt;value&lt;/var&gt; with &lt;var&gt;func&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;logs the &lt;var&gt;value&lt;/var&gt; to the console using &lt;var&gt;func&lt;/var&gt; (?? console.log)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;make a &amp;lt;tag#id.class /&gt; called &lt;var&gt;name&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;creates an element with the given tag, id and classes, sets &lt;var&gt;name&lt;/var&gt; (?? it) = the created element
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;make a &lt;var&gt;Class&lt;/var&gt; from &lt;var&gt;args&lt;/var&gt; called &lt;var&gt;name&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;calls the &lt;var&gt;Class&lt;/var&gt; constructor with the &lt;var&gt;args&lt;/var&gt;, sets &lt;var&gt;name&lt;/var&gt; (?? it) = the created object
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;put &lt;var&gt;rvalue&lt;/var&gt; into &lt;var&gt;lvalue&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;see set
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;put &lt;var&gt;content&lt;/var&gt; into &lt;var&gt;elt&lt;/var&gt; -- into/before/after/at start of/at end of&lt;/code&gt;
    &lt;dd&gt;insert content into various parts of the &lt;var&gt;elt&lt;/var&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;remove .&lt;var&gt;class&lt;/var&gt; from &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;see add
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;remove @&lt;var&gt;attribute&lt;/var&gt; from &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;see add
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;remove &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;removes &lt;var&gt;elt&lt;/var&gt; (?? me) from the document
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;repeat for &lt;var&gt;name&lt;/var&gt; in &lt;var&gt;iterable&lt;/var&gt; index &lt;var&gt;i&lt;/var&gt; … end&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;for &lt;var&gt;name&lt;/var&gt; in &lt;var&gt;iterable&lt;/var&gt; index &lt;var&gt;i&lt;/var&gt; … end&lt;/code&gt;
    &lt;dd&gt;loop over an iterable, the loop variable is &lt;var&gt;name&lt;/var&gt; (?? it)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;repeat until event &lt;var&gt;e&lt;/var&gt; from &lt;var&gt;elt&lt;/var&gt; index &lt;var&gt;i&lt;/var&gt; … end&lt;/code&gt;
    &lt;dd&gt;Repeat every tick until event &lt;var&gt;e&lt;/var&gt; is received from &lt;var&gt;elt&lt;/var&gt; (?? me)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;repeat while &lt;var&gt;cond&lt;/var&gt; | repeat until &lt;var&gt;cond&lt;/var&gt; … end&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;repeat &lt;var&gt;n&lt;/var&gt; times index &lt;var&gt;i&lt;/var&gt; … end&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;repeat forever … end&lt;/code&gt;
    &lt;dd&gt;--
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;return &lt;var&gt;value&lt;/var&gt; | exit&lt;/code&gt;
    &lt;dd&gt;return, see also halt
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;send    &lt;var&gt;evt&lt;/var&gt;(&lt;var&gt;args&lt;/var&gt;…) to &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;trigger &lt;var&gt;evt&lt;/var&gt;(&lt;var&gt;args&lt;/var&gt;…) on &lt;var&gt;elt&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;dispatch a DOM event on &lt;var&gt;elt&lt;/var&gt; (?? me)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;set &lt;var&gt;lvalue&lt;/var&gt; to &lt;var&gt;rvalue&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;--
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;settle&lt;/code&gt;
    &lt;dd&gt;waits for any animations/transitions to end
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;show &lt;var&gt;elt&lt;/var&gt; with &lt;var&gt;strategy&lt;/var&gt; when &lt;var&gt;cond&lt;/var&gt; -- strategy: display:_/visibility/opacity/…&lt;/code&gt;
    &lt;dd&gt;show &lt;var&gt;elt&lt;/var&gt; (?? me) using the &lt;var&gt;strategy&lt;/var&gt; (?? display:block) if &lt;var&gt;cond&lt;/var&gt; (?? true) is true, else hide it
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;take .&lt;var&gt;class&lt;/var&gt; from &lt;var&gt;eltA&lt;/var&gt; for &lt;var&gt;eltB&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;remove class from &lt;var&gt;eltA&lt;/var&gt; (?? .class) and add it to &lt;var&gt;eltB&lt;/var&gt; (?? me)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;tell &lt;var&gt;elt&lt;/var&gt; … end&lt;/code&gt;
    &lt;dd&gt;set &lt;var&gt;you&lt;/var&gt; = elt, default to &lt;var&gt;you&lt;/var&gt; over &lt;var&gt;me&lt;/var&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;throw &lt;var&gt;exception&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;throws an exception
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;toggle .&lt;var&gt;class&lt;/var&gt; on &lt;var&gt;eltA&lt;/var&gt; for &lt;var&gt;t&lt;/var&gt; s &lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;toggle [@&lt;var&gt;attr=value&lt;/var&gt;] until &lt;var&gt;evt&lt;/var&gt; from &lt;var&gt;eltB&lt;/var&gt;&lt;/code&gt;
    &lt;dt&gt;&lt;code&gt;toggle between .&lt;var&gt;class1&lt;/var&gt; and .&lt;var&gt;class2&lt;/var&gt; on &lt;var&gt;eltA&lt;/var&gt;&lt;/code&gt;
    &lt;dd&gt;toggle classes and attributes on &lt;var&gt;eltA&lt;/var&gt; (?? me)
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;transition the &lt;var&gt;elt&lt;/var&gt;&#39;s &lt;var&gt;prop&lt;/var&gt; to &lt;var&gt;value&lt;/var&gt; … over &lt;var&gt;t&lt;/var&gt; s&lt;/code&gt;
    &lt;dd&gt;Animate style properties
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;wait &lt;var&gt;t&lt;/var&gt; s -- or ms&lt;/code&gt;
    &lt;dd&gt;Waits for the given duration
&lt;/div&gt;
&lt;div&gt;
    &lt;dt&gt;&lt;code&gt;wait for &lt;var&gt;event&lt;/var&gt; or &lt;var&gt;event2&lt;/var&gt; or &lt;var&gt;t&lt;/var&gt; s&lt;/code&gt;
    &lt;dd&gt;waits for one of the events to occur, sets it=the event
&lt;/div&gt;
&lt;/dl&gt;
&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>TextMate Grammars</title>
    <link href="https://dz4k.com/2022/textmate-grammars/"/>
    <updated>2022-04-16T16:02:47Z</updated>
    <id>https://dz4k.com/2022/textmate-grammars/</id>
    <content type="html">&lt;p&gt;Thanks to Phrogz&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;comment&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;for comments.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;line&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;line comments, we specialize further so that the type of comment start character(s) can be extracted from the scope.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;double-slash&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;//&lt;/code&gt; comment&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;double-dash&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;--&lt;/code&gt; comment&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;number-sign&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;#&lt;/code&gt; comment&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;percentage&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;%&lt;/code&gt; comment&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;character&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;other types of line comments.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;block&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;multi-line comments like &lt;code&gt;/* … */&lt;/code&gt; and &lt;code&gt;&amp;lt;!-- … --&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;documentation&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;embedded documentation.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;constant&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;various forms of constants.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;numeric&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;those which represent numbers, e.g. &lt;code&gt;42&lt;/code&gt;, &lt;code&gt;1.3f&lt;/code&gt;, &lt;code&gt;0x4AB1U&lt;/code&gt;.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;character&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;those which represent characters, e.g. &lt;code&gt;&amp;amp;lt;&lt;/code&gt;, &lt;code&gt;\e&lt;/code&gt;, &lt;code&gt;\031&lt;/code&gt;.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;escape&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;escape sequences like &lt;code&gt;\e&lt;/code&gt; would be constant.character.escape.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;language&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;constants (generally) provided by the language which are “special” like &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;nil&lt;/code&gt;, &lt;code&gt;YES&lt;/code&gt;, &lt;code&gt;NO&lt;/code&gt;, etc.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;other constants, e.g. colors in CSS.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;entity&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;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 &lt;code&gt;entity.*&lt;/code&gt; (we use &lt;code&gt;meta.*&lt;/code&gt; for that). But we do use &lt;code&gt;entity.*&lt;/code&gt; for the “placeholders” in the larger entity, e.g. if the entity is a chapter, we would use &lt;code&gt;entity.name.section&lt;/code&gt; for the chapter title.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;name&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;we are naming the larger entity.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;function&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the name of a function.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;type&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the name of a type declaration or class.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;tag&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;a tag name.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;section&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the name is the name of a section/heading.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;other entities.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;inherited-class&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the superclass/baseclass name.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;attribute-name&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the name of an attribute (mainly in tags).&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;invalid&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;stuff which is “invalid”.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;illegal&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;illegal, e.g. an ampersand or lower-than character in HTML (which is not part of an entity/tag).&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;deprecated&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;for deprecated stuff e.g. using an API function which is deprecated or using styling with strict HTML.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;keyword&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;keywords (when these do not fall into the other groups).&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;control&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;mainly related to flow control like continue, while, return, etc.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;operator&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;operators can either be textual (e.g. or) or be characters.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;other keywords.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;markup&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;this is for markup languages and generally applies to larger subsets of the text.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;underline&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;underlined text.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;link&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;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.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;bold&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;bold text (text which is strong and similar should preferably be derived from this name).&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;heading&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;a section header. Optionally provide the heading level as the next element, for example markup.heading.2.html for &lt;code&gt;&amp;lt;h2&amp;gt;…&amp;lt;/h2&amp;gt;&lt;/code&gt; in HTML.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;italic&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;italic text (text which is emphasized and similar should preferably be derived from this name).&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;list&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;list items.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;numbered&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;numbered list items.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;unnumbered&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;unnumbered list items.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;quote&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;quoted (sometimes block quoted) text.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;raw&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;text which is verbatim, e.g. code listings. Normally spell checking is disabled for markup.raw.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;other markup constructs.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;meta&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;storage&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;things relating to “storage”.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;type&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the type of something, class, function, int, var, etc.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;modifier&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;a storage modifier like static, final, abstract, etc.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;string&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;strings.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;quoted&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;quoted strings.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;single&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;single quoted strings: ‘foo’.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;double&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;double quoted strings: “foo”.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;triple&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;triple quoted strings: “”“Python”“”.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;other types of quoting: $‘shell’, %s{…}.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;unquoted&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;for things like here-docs and here-strings.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;interpolated&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;strings which are “evaluated”: date, $(pwd).&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;regexp&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;regular expressions: /(\w+)/.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;other types of strings (should rarely be used).&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;support&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;things provided by a framework or library should be below support.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;function&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;functions provided by the framework/library. For example NSLog in Objective-C is support.function.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;class&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;when the framework/library provides classes.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;type&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;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.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;constant&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;constants (magic values) provided by the framework/library.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;variable&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;variables provided by the framework/library. For example NSApp in AppKit.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;the above should be exhaustive, but for everything else use support.other.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;variable&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;variables. Not all languages allow easy identification (and thus markup) of these.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;parameter&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;when the variable is declared as the parameter.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;language&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;reserved language variables like this, super, self, etc.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;other&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;other variables, like $some_variables.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content>
  </entry>
  <entry>
    <title>Cloudflare Pages: Failed: an internal error occurred</title>
    <link href="https://dz4k.com/2022/cloudflare-internal-error/"/>
    <updated>2022-03-25T14:36:06Z</updated>
    <id>https://dz4k.com/2022/cloudflare-internal-error/</id>
    <content type="html">&lt;p&gt;I deploy my website on Cloudflare Pages (at time of writing). My builds were
failing with the error message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;17:24:49.795	Deploying your site to Cloudflare&#39;s global network...
17:24:50.632	Failed: an internal error occurred
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No other information.&lt;/p&gt;
&lt;p&gt;Googling yields that many things can cause this issue. &lt;!-- TODO: compile --&gt;
In my case, it was a &lt;strong&gt;directory called &lt;code&gt;_redirects/&lt;/code&gt; in the output folder&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cloudflare expects &lt;code&gt;_redirects&lt;/code&gt; to be a file to read redirect information from.
(Indeed, that’s what I was using it for — it was output to a directory by
accident).&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Intentionally Unscalable</title>
    <link href="https://dz4k.com/2022/intentionally-unscalable/"/>
    <updated>2022-02-06T14:52:45Z</updated>
    <id>https://dz4k.com/2022/intentionally-unscalable/</id>
    <content type="html">&lt;p&gt;_hyperscript is intentionally unscalable. We make decisions that would be
obviously inadvisable to anyone looking to make scalable software, and dismiss
features like reactivity that seem to have proven themselves in this regard. To
understand why, I examine and critique the concept of “scalable”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scalability refers to the ability of a system to handle more work given more
resources.&lt;/strong&gt; This definition matches both formal definitions and common use in
application development circles. Given this definition, we can see that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Scalability is unidirectional — it only refers to scaling up, never
scaling down. In fact, I never hear “scaling down” discussed at all.
Declaring it out-of-scope to ascribe this to a wider culture of growth, I’ll
just speculate that perhaps scaling down is assumed to be trivial, or not
necessary as a system that handles a large amount of work can necessarily
handle a small amount, adequately, without modification.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When people talk about the scalability of a tool, they are usually employing
metonymy.
&lt;x-sidenote inline=&quot;&quot;&gt;metonymy: I was excited when looking up this word on
&lt;a href=&quot;https://tureng.com/&quot;&gt;Tureng&lt;/a&gt;, but “metonymy” doesn’t really have the barebones simplicity of
the Turkish “ad aktarması” or the weight of the Arabic loan “mecaz-ı
mürsel”.&lt;/x-sidenote&gt;
They are discussing the impact of the tool on other systems’
scalability (will _hyperscript hinder us as we get more users/our app gets
more complex?) and not the scalability of the tool itself (can we get
hyperscript to run N lines of code in T time with better hardware?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;System, work, resources — a software development team consists of multiple
systems using multiple kinds of resources to do multiple kinds of work.
Which system is being discussed is usually clear through context, but we
should be aware when “scalable” is used without such context.&lt;/p&gt;
&lt;table&gt;
&lt;caption&gt;The two main systems in a software organization&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;More Work&lt;/th&gt;
&lt;th&gt;Resources&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Software&lt;/td&gt;
&lt;td&gt;Serve more users.&lt;/td&gt;
&lt;td&gt;Computers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team&lt;/td&gt;
&lt;td&gt;Add complexity to the software and maintain it.&lt;/td&gt;
&lt;td&gt;Time, money.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;responsive-table&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why is scalability desired?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;“As our app gets more complex, X will prevent us from growing and
maintaining it”.&lt;/p&gt;
&lt;p&gt;There is an assumption that your app &lt;em&gt;will&lt;/em&gt; get more complex and partly that
complexity is an environmental factor, as opposed to a consequence of the
team’s choices. Growing a codebase is adding complexity. Lamenting not being
able to do that seems odd given the amount of lip service we pay to the
glory of removing complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“As we get more users, the app we built with X will need rewriting”.&lt;/p&gt;
&lt;p&gt;There is an assumption that we &lt;em&gt;will&lt;/em&gt; get more users. If this is true, and
the organization containing the team has an actual monetization
strategy, then it follows that we will also get more money.
&lt;x-sidenote inline=&quot;&quot;&gt;&lt;strong&gt;monetization:&lt;/strong&gt; The way that the “we’ll need rewriting”
argument assumes growth, but rejects relying on it reminds me of paying
people in shares. If you can’t trust it enough to just pay me the money
you’ll &lt;i class=&quot;sarc&quot;&gt;definitely&lt;/i&gt; make back, why should I?&lt;/x-sidenote&gt;
If we need to build an app that serves N users eventually, does it not make
sense to do so when you have F(N-ε) dollars?
&lt;x-sidenote inline=&quot;&quot;&gt;&lt;strong&gt;epsilon:&lt;/strong&gt; I originally said this much more pithily: “if you
are certain you’ll grow to 1 billion users, and you need an app that can
handle that, why build it now on a startup budget when you’re going to have
the budget of a 1B-user app soon”&lt;/x-sidenote&gt;
My more business-savvy friend &lt;a href=&quot;https://twitter.com/benpate5280&quot;&gt;Ben Pate&lt;/a&gt; informs me that “many
million-user budgets have been wasted doing that [building a million-user
system before you have any users at all]”, and that “You won’t get a billion
users until you first earn a thousand”.&lt;/p&gt;
&lt;p&gt;I don’t know shit about running a company, but I reject designing our tools
for startups that eat some VC money, don’t generate profit &lt;em&gt;or&lt;/em&gt; any benefit
for humanity, and get sold for the advertising value of their data.&lt;/p&gt;
&lt;p&gt;And what’s so bad about rewriting anyway? I thought programmers liked
writing code.
&lt;x-sidenote inline=&quot;&quot;&gt;&lt;strong&gt;liked writing code:&lt;/strong&gt; I’m aware that some people do
programming purely as a job. I sometimes envy the indifference I imagine
they have towards our petty squabbles.&lt;/x-sidenote&gt;
We again pay lip service to how code is the easy part, and
how legacy code sucks… Erlang has the famous “let it crash”, anticipating
issues and focusing on recovering from failure instead of preventing it. We
can apply a similar concept: &lt;strong&gt;“write to rewrite”&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Write to rewrite&lt;/h2&gt;
&lt;p&gt;Expect that you might need to rewrite your code, and be considerate of your
future self who is doing that. _hyperscript helps you do this in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Readability over writability.&lt;/strong&gt; The fact that _hyperscript code examples
look like plain English is just as much an achievement of the author as it
is _hyperscript’s. We do not do anything smart like Natural Language
Processing; _hyperscript uses common and normal parsing techniques. C++,
Perl, perhaps even Ruby have more complex grammars than _hyperscript. We
give you tools like the &lt;code&gt;prop of object&lt;/code&gt; and &lt;code&gt;object&#39;s prop&lt;/code&gt; syntax. and the
&lt;code&gt;it&lt;/code&gt; variable that might seem magical but is actually little more than the
accumulator register in an 8-bit processor. It is up to you the programmer
to use these tools to create readable code. The burden you take upon your
shoulders can include (but will not be limited to):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choosing between &lt;code&gt;rates of the result&#39;s data&lt;/code&gt;, &lt;code&gt;its data.rates&lt;/code&gt;, or
&lt;code&gt;result.data.rates&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Breaking up long expressions into statements. Besides readability and
English-like flow, this is also good for stepping through with a
debugger (yet another tool for a rewriter to understand the code).&lt;/li&gt;
&lt;li&gt;Reordering statements to make efficient use of &lt;code&gt;it&lt;/code&gt;. If we do this
consistently, code that doesn’t do this will stick out as
order-dependent and vulnerable to race conditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Locality of behavior.&lt;/strong&gt; Ever looked through a GitHub repo, not been able
to find the code you were looking for, eventually clone the repo locally and
start “Jump to Definition”-ing and “Peek References”-ing your way towards
your target, only to find out it’s a wrapper for yet another function? If it
is a Java project, I usually give up before reaching any source code files
at all. Htmx “Carson” Org has written about &lt;a href=&quot;https://htmx.org/essays/locality-of-behaviour&quot;&gt;Locality of Behavior&lt;/a&gt;
previously, so I’ll direct you there.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I recall what I now know to be an urban legend about the great architect Mimar
Sinan. Supposedly, when the keystones of some arches in a mosque he built were
yielding to old age, an engineer working on the restorations found a note in a
bottle signed by Koca Mimar Sinan Ağa himself. It was a step-by-step guide on
how to replace the keystone without disruption to the rest of the structure,
complete with drawings.&lt;/p&gt;
&lt;p&gt;Putting aside the implication that our modern architects don’t know how stone
arches are built, and that Great Sinan Agha The Architect expected such
architects to restore his works, this fictional Mimar Sinan clearly doesn’t know
anything about scalability. If he did, he would predict when the Sultan would
die and make the mosque last exactly that long.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When asked what their language is good for, many designers would say
“everything” which really means “nothing”.&lt;/p&gt;
&lt;p&gt;– Robert Nystrom, &lt;cite&gt;&lt;a href=&quot;http://journal.stuffwithstuff.com/2010/07/23/what-i-learned-at-the-emerging-languages-camp/&quot;&gt;What I Learned at the Emerging Languages Camp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nystrom said this in the context of choosing a niche for your language, but I
believe it applies just as well to choosing a scale.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>nanpa pi ken ala kipisi pi mute ale li lon</title>
    <link href="https://dz4k.com/2022/nanpa-pi-ken-ala-kipisi/"/>
    <updated>2022-01-14T21:47:00Z</updated>
    <id>https://dz4k.com/2022/nanpa-pi-ken-ala-kipisi/</id>
    <content type="html">&lt;p&gt;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 &lt;var&gt;P&lt;/var&gt;.&lt;/p&gt;
&lt;p&gt;ni la o mute e nanpa ale pi kulupu &lt;var&gt;P&lt;/var&gt;, o namako e wan. (ni li ken tan ni: kulupu &lt;var&gt;P&lt;/var&gt;li suli pini). o nimi e nanpa sin ni kepeken sitelen &lt;var&gt;N&lt;/var&gt;.&lt;/p&gt;
&lt;p&gt;nanpa &lt;var&gt;N&lt;/var&gt;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.&lt;/p&gt;
&lt;p&gt;ken nanpa wan li ken ala tan ni: nanpa &lt;var&gt;N&lt;/var&gt; li lon ala kulupu &lt;var&gt;P&lt;/var&gt;. (nanpa tan kulupu &lt;var&gt;P&lt;/var&gt;, la nanpa &lt;var&gt;N&lt;/var&gt; li suli mute). taso ,ona li nanpa pi ken ala kipisi. kulupu &lt;var&gt;P&lt;/var&gt; li wile jo e nanpa ale pi ken ala kipisi, la ni li ken ala.&lt;/p&gt;
&lt;p&gt;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 &lt;var&gt;N&lt;/var&gt;. o nimi e ona kepeken sitelen &lt;var&gt;L&lt;/var&gt;. o sona e ni: nanpa pi kulupu &lt;var&gt;P&lt;/var&gt; li ken ala kipisi e nanpa &lt;var&gt;N&lt;/var&gt;. (nanpa &lt;var&gt;p&lt;/var&gt; li lon kulupu &lt;var&gt;P&lt;/var&gt; la ona li lukin kipisi e nanpa &lt;var&gt;N&lt;/var&gt; la wan li ante). tan ni, la nanpa &lt;var&gt;L&lt;/var&gt; li ken ala lon kulupu &lt;var&gt;P&lt;/var&gt;. taso ,ona li nanpa pi ken ala kipisi. sama tenpo pini, la ni li ken ala.&lt;/p&gt;
&lt;p&gt;ale ni, la kulupu &lt;var&gt;P&lt;/var&gt; li suli pini, la ale li ijo pakala pi ken ala. tan ni, la kulupu &lt;var&gt;P&lt;/var&gt; li wile kulupu pi  suli ale.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>VSCode/Toolbx</title>
    <link href="https://dz4k.com/2021/vstlbx/"/>
    <updated>2021-11-17T20:53:00Z</updated>
    <id>https://dz4k.com/2021/vstlbx/</id>
    <content type="html">&lt;p&gt;A GUI script to run VSCode in a toolbox container.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;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 &lt;code&gt;dnf&lt;/code&gt;. Instead, you are expected to install applications in one of three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;[Flatpak]. These are applications with Android-like isolation and permission management capabilities.&lt;/p&gt;
&lt;p&gt;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).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[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.&lt;/p&gt;
&lt;p&gt;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 &lt;code&gt;fish&lt;/code&gt; 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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[Toolboxes]! Toolbox, or Toolbx as it’s been recently renamed[^1], is a tool for setting up development containers really easily.&lt;/p&gt;
&lt;p&gt;You type something to the effect of &lt;code&gt;toolbox create my-project-container&lt;/code&gt;, and you have a place where you can &lt;code&gt;sudo&lt;/code&gt; without password, install whatever package with &lt;code&gt;dnf&lt;/code&gt;, just trash the place. The containers use your real home directory, can access USB devices, and other things that &lt;i&gt;just work&lt;/i&gt;. If anything goes wrong, you can just create a new container and start over.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Toolbx&lt;/h2&gt;
&lt;p&gt;I set up toolboxes for a few projects to test the waters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;www&lt;/code&gt;: This website&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_hyperscript&lt;/code&gt;: The hypermedia programming language&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this-week-in-htmx&lt;/code&gt;: The weekly blog that I was running late on at the time. Sorry…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Great, I’m all set up!&lt;/p&gt;
&lt;p&gt;So… How do I develop in here?&lt;/p&gt;
&lt;p&gt;Some DDGing led me to the amazing [&lt;code&gt;toolbox-vscode&lt;/code&gt;] script. It gives you a script that you can place into &lt;code&gt;~/.local/bin/code&lt;/code&gt;. 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.&lt;/p&gt;
&lt;h2&gt;In which I have to have my GUIs&lt;/h2&gt;
&lt;p&gt;Previously, I would launch VSCode and pick my project from the &lt;kbd&gt;Open Recent&lt;/kbd&gt; menu. The flow of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;opening a terminal&lt;/li&gt;
&lt;li&gt;entering the toolbox&lt;/li&gt;
&lt;li&gt;entering the project folder&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;code .&lt;/kbd&gt;&lt;/li&gt;
&lt;li&gt;now I can code, but I’m left with a useless terminal open&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;was far too janky. What I wanted was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;launch an app&lt;/li&gt;
&lt;li&gt;pick my project from a list&lt;/li&gt;
&lt;li&gt;VSCode is open to the right toolbox and project directory, and the list is out of my way&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After some relearning Bash and overengineering later, I have a script in my hands.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;&lt;code&gt;vstlbx&lt;/code&gt;&lt;/summary&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash

# Depends on: bash zenity

set -e

Title=&amp;quot;VS Toolbx&amp;quot;
Text=&amp;quot;Select a container to open in VS Code:&amp;quot;

## list-containers &amp;gt; containers
list-containers() {
	toolbox list --containers | tail -n +2 | tr -s &#39; +&#39; &amp;quot;\t&amp;quot; | cut -f 2
}

## containers | user-pick-container &amp;gt; container
user-pick-container() {
	zenity \
		--title &amp;quot;$Title&amp;quot; \
		--text &amp;quot;$Text&amp;quot; \
		--list \
  	--column &#39;Name&#39; \
  	--hide-header \
  	2&amp;gt;/dev/null
}

## get-project-dir container &amp;gt; project
get-project-dir() {
	if [ &amp;quot;$1&amp;quot; == &amp;quot;hyperscript&amp;quot; ]; then
		echo &amp;quot;$HOME/Projects/_hyperscript&amp;quot;
	else
		echo &amp;quot;$HOME/Projects/$1&amp;quot;
	fi
}

## run-container container
run-container() {
	toolbox run --container &amp;quot;$1&amp;quot; -- $(which code) $(get-project-dir &amp;quot;$1&amp;quot;)
}

## run-ui
run-ui() {
	container=$(list-containers | user-pick-container)
	run-container &amp;quot;$container&amp;quot;
}

install-desktop-file() {
	desktop_file=&amp;quot;$HOME/.local/share/applications/com.dz4k.vstlbx.desktop&amp;quot;
	cat &amp;lt;&amp;lt;-EOF &amp;gt;&amp;quot;$desktop_file&amp;quot;
		[Desktop Entry]
		Type=Application
		Name[en_US]=VS/Toolbx
		Categories=Development;
		X-GNOME-FullName[en_US]=VS/Toolbx
		Comment[en_US.UTF-8]=Attach VSCode to toolboxes
		NoDisplay=false
		Exec=/var/home/deniz/Applications/vstlbx
		Path=.
		Terminal=false
		X-GNOME-UsesNotifications=false
  	StartupWMClass=zenity
		Name[en_US.UTF-8]=VS/Toolbx
		X-GNOME-FullName[en_US.UTF-8]=VS/Toolbx
	EOF
}

if [ &amp;quot;$#&amp;quot; == 0 ]; then
	run-ui
	exit 0
fi

while [ &amp;quot;$#&amp;quot; -gt 0 ]; do
	case &amp;quot;$1&amp;quot; in
		&#39;install-desktop&#39;)
			install-desktop-file
		;;
		&#39;container&#39;)
			run-container &amp;quot;$2&amp;quot;
			shift
		;;
		&#39;ui&#39;)
			run-ui
		;;
	esac
	shift
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;who needs Gists anyway&lt;/small&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;Here’s what the UI looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/vstlbx.png&quot; alt=&quot;A dialog with a list of container names: fedora-toolbox-35, hyperscript, this=week-in-htmx, www&quot;&gt;&lt;/p&gt;
&lt;p&gt;I’m still getting myself set up, but I’m happy that I figured this part out early on.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Sprinkle Sharing</title>
    <link href="https://dz4k.com/2021/sprinkle-sharing/"/>
    <updated>2021-09-02T19:41:00Z</updated>
    <id>https://dz4k.com/2021/sprinkle-sharing/</id>
    <content type="html">&lt;p&gt;When people talk about code sharing between server and client, the usual suspects are Node.js and sharing model classes. However, there&#39;s often good reason to duplicate these.
&lt;p&gt;A far more interesting type of code sharing to me is eliminating this duplication:
&lt;p&gt;When you are writing a server-driven web app with &quot;sprinkles&quot; of JS, you want to have interactive components, but also deliver usable HTML to non-JS-enabled&lt;sup&gt;&lt;a class=&quot;fnref&quot; href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 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)
&lt;h2&gt;Alpine and similar&lt;/h2&gt;
&lt;p&gt;Write both in one language: &quot;HTML with JS inline in special attributes&quot;.
&lt;h2&gt;React and similar&lt;/h2&gt;
&lt;p&gt;Write both in one language: &quot;JS optionally with JSX&quot;. Hard to set up sprinkle style, but wasn&#39;t this how it was originally created to be used?
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;&quot;Non-JS-enabled&quot; includes not only browsers where the user turned off JS, but also times when the JS didn&#39;t load, or errored due to use of unsupported modern features.
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Sokaktaki Ankara: Tabela Tasarım Yarışması</title>
    <link href="https://dz4k.com/2021/sokaktaki-ankara/"/>
    <updated>2021-08-29T19:07:59Z</updated>
    <id>https://dz4k.com/2021/sokaktaki-ankara/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;&lt;time&gt;2021-09-08&lt;/time&gt;:&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/ankarabbld/status/1433505785573777414&quot;&gt;Kırmızı kazandı! (twitter)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ankara Büyükşehir Belediyesi sokak tabelalarının yenilenmesi için &lt;a href=&quot;https://sokaktakiankara.ankara.bel.tr/&quot;&gt;yarışma&lt;/a&gt; düzenledi. Halk oylamasına sunulmak üzere üç tasarım seçildi. Siz de Başkent Mobil uygulamasının &lt;kbd&gt;&lt;samp&gt;Söz Hakkı&lt;/samp&gt;&lt;/kbd&gt; bölümünden oy verebilirsiniz.&lt;/p&gt;
&lt;p&gt;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ı.
&lt;x-sidenote inline=&quot;&quot;&gt;Eser C’deki gibi bir harita, &lt;a href=&quot;https://en.wikipedia.org/wiki/Legible_London&quot;&gt;Legible London&lt;/a&gt; tarzı bir haritada çok daha kullanışlı olabilir.&lt;/x-sidenote&gt;&lt;/p&gt;
&lt;x-fig src=&quot;https://dz4k.com/%= it.photo.src %&quot; title=&quot;Eser A sokak tabelası.&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&quot;&gt;&lt;/x-fig&gt;
&lt;p&gt;&lt;a href=&quot;https://sokaktakiankara.ankara.bel.tr/sartnameekler/tabelafontyarismasartnamesi.pdf&quot;&gt;Yarışma şartnamesinde&lt;/a&gt; tabelaların &lt;i&gt;İşçi Blokları Mahallesi, Gazi Mustafa Kemal Bulvarı, Balıkçı Caddesi, Alâ Sokak, 1923. Sokak&lt;/i&gt; ile test edilmesi söylenmiş. Bence &lt;a href=&quot;https://www.openstreetmap.org/way/25211791&quot;&gt;Bangabandhu Şeyh Mucibur Rahman Bulvarı&lt;/a&gt;da eklenmeliydi.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>kalama [_la_a_kute_ijo_moli_o_sona_a]</title>
    <link href="https://dz4k.com/2021/kalama-lakimosa/"/>
    <updated>2021-08-10T01:10:17Z</updated>
    <id>https://dz4k.com/2021/kalama-lakimosa/</id>
    <content type="html">&lt;p&gt;tenpo ni la pilin li telo:  &lt;br&gt;
jan ni li kama tan ma anpa: &lt;br&gt;
sewi li lawa e ona pakala.&lt;/p&gt;
&lt;p&gt;sewi o pona e ona.          &lt;br&gt;
jan Jesu wawa o             &lt;br&gt;
pana e lape tawa ona.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Event Delegation in _hyperscript</title>
    <link href="https://dz4k.com/2021/hyperscript-event-delegation/"/>
    <updated>2021-05-20T12:43:49Z</updated>
    <id>https://dz4k.com/2021/hyperscript-event-delegation/</id>
    <content type="html">&lt;p&gt;TL;DR This is how you do it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-hyperscript&quot;&gt;on click
	tell the closest &amp;lt;li/&amp;gt; to the target
		remove yourself
		-- do more stuff...
		-- &amp;quot;you&amp;quot; refers to the clicked list item
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or more concisely:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-hyperscript&quot;&gt;on click tell closest &amp;lt;li/&amp;gt; to target
	remove yourself
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;I’ve seen some people use a pattern like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;ul&amp;gt;
	{% for item in items %}
		&amp;lt;li _=&amp;quot;on click remove me&amp;quot;&amp;gt;{{ item }}&amp;lt;/li&amp;gt;
	{% endfor %}
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is convenient to write if you have a server-side templating system, but
has a few issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The code needs to be parsed as many times as there are items.&lt;/li&gt;
&lt;li&gt;The resulting HTML is bloated.&lt;/li&gt;
&lt;li&gt;If you add more items to the list dynamically on the client, you need to repeat the code there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The pattern for resolving this is called &lt;dfn&gt;event delegation&lt;/dfn&gt;. Here’s
how you might do it in JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;ul.addEventListener(&#39;click&#39;, e =&amp;gt; {
	const li = e.target.closest(&#39;li&#39;)
	if (!li) return
	li.remove()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add a single event listener to the enclosing list, which finds the item
that was clicked and manipulates it.&lt;/p&gt;
&lt;p&gt;In _hyperscript, the &lt;code&gt;tell&lt;/code&gt; command allows us to manipulate an
element other  than &lt;code&gt;me&lt;/code&gt; conveniently, by changing the implicit
target from &lt;code&gt;me&lt;/code&gt; to &lt;code&gt;you&lt;/code&gt;,  which refers to the “element
being told”.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Hypelet</title>
    <link href="https://dz4k.com/2021/hypelet/"/>
    <updated>2021-05-01T14:46:00Z</updated>
    <id>https://dz4k.com/2021/hypelet/</id>
    <content type="html">&lt;p&gt;I made a tool to make bookmarklets in &lt;a href=&quot;//hyperscript.org&quot;&gt;_hyperscript&lt;/a&gt;. 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.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Here’s Hypelet: &lt;a href=&quot;//hypelet.dz4k.com&quot;&gt;Hypelet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Here’s the code for a bookmarklet to get the RSS feed of a page: &lt;a href=&quot;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&quot;&gt;Get RSS – Hypelet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>The Implementation of HDB, the _hyperscript debugger</title>
    <link href="https://dz4k.com/2021/the-implementation-of-hdb/"/>
    <updated>2021-03-17T00:00:00Z</updated>
    <id>https://dz4k.com/2021/the-implementation-of-hdb/</id>
    <content type="html">&lt;script src=&quot;https://unpkg.com/hyperscript.org@0.9.5&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/hyperscript.org@0.9.5/dist/hdb.min.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;strong&gt;Update &lt;time&gt;2021-09-02&lt;/time&gt;:&lt;/strong&gt; HDB has evolved since this post was written. Though it works mostly the same way, there have been fixes and a UI redesign. Check &lt;a href=&quot;https://github.com/bigskysoftware/_hyperscript&quot;&gt;the _hyperscript repo&lt;/a&gt; for the up-to-date Code:&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;The 0.0.6 release of the &lt;a href=&quot;https://hyperscript.org/&quot;&gt;_hyperscript&lt;/a&gt; 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:&lt;/p&gt;
&lt;h2&gt;The (Un)finished Product&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;breakpoint&lt;/code&gt; statement stops execution and launches the HDB UI.&lt;/p&gt;
&lt;x-fig title=&quot;Demo: The breakpoint command&quot;&gt;
&lt;p&gt;&lt;button type=&quot;button&quot; _=&quot;
on click
	breakpoint
	-- This is some sample code for you to explore HDB.
	-- Click &#39;Step Over&#39; to move to the next command.
	-- Click &#39;Continue&#39; to stop debugging.
	set my.innerHTML to &#39;You are stepping through!&#39;
	transition &#39;background-color&#39; to red
	wait for mouseover -- mouse over the button after you step over this
	transition &#39;background-color&#39; to initial&quot;&gt;Click me to try HDB&lt;/button&gt;&lt;/p&gt;
&lt;/x-fig&gt;
&lt;p&gt;You can set breakpoints conditionally:&lt;/p&gt;
&lt;x-fig title=&quot;Demo: Conditional breakpoints&quot;&gt;
&lt;tool-bar&gt;
&lt;label&gt;&lt;input type=checkbox id=debugmode checked/&gt; Debug Mode&lt;/label&gt;
&lt;button type=&quot;button&quot; _=&quot;
on click
	if #debugmode.checked breakpoint end
	put &#39;Nothing to see here, end user&#39; into me&quot;&gt;Debug, maybe&lt;/button&gt;
&lt;/tool-bar&gt;
&lt;/x-fig&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;HDB lives in a &lt;a href=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js&quot;&gt;single JavaScript file&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Turning the keys&lt;/h3&gt;
&lt;p&gt;In the hyperscript runtime (which is a tree walking interpreter), each command has an &lt;code&gt;execute()&lt;/code&gt; method which either returns the next command to be executed, or a &lt;code&gt;Promise&lt;/code&gt; thereof. The execute method for the breakpoint command creates an HDB environment and assigns it to the global scope (usually &lt;code&gt;window&lt;/code&gt;):&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L20&quot; title=&quot;hdb.js ln. 20&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;var hdb = new HDB(ctx, runtime, this);
window.hdb = hdb;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;The &lt;code&gt;HDB&lt;/code&gt; 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 &lt;code&gt;break()&lt;/code&gt; method:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L35&quot; title=&quot;hdb.js ln. 35&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;HDB.prototype.break = function(ctx) {
	var self = this;
	console.log(&amp;quot;%c=== HDB///_hyperscript/debugger ===&amp;quot;, headingStyle);
	self.ui();
	return new Promise(function (resolve, reject) {
		self.bus.addEventListener(&amp;quot;continue&amp;quot;, function () {
			if (self.ctx !== ctx) {
				// Context switch
				for (var attr in ctx) {
					delete ctx[attr];
				}
				Object.assign(ctx, self.ctx);
			}
			delete window.hdb;
			resolve(self.runtime.findNext(self.cmd, self.ctx));
		}, { once: true });
	})
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;There are a few things to unpack here. We call &lt;code&gt;self.ui()&lt;/code&gt; 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 &lt;a href=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L10&quot;&gt;internal event bus&lt;/a&gt; receives a &lt;code&gt;&amp;quot;continue&amp;quot;&lt;/code&gt; event, whether by the user pressing “Continue” or simply reaching the end of the debugged Code:&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Speaking of stepping out of functions…&lt;/p&gt;
&lt;h3&gt;Stepping Over and Out&lt;/h3&gt;
&lt;p&gt;Firstly, if self.cmd is null, then the previous command was the last one, so we just stop the debug process:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L58&quot; title=&quot;hdb.js ln. 58&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;HDB.prototype.stepOver = function() {
	var self = this;
	if (!self.cmd) return self.continueExec();
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;If not, then we do a little dance to execute the current command and get the next one:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61&quot; title=&quot;hdb.js ln. 61&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;var result = self.cmd &amp;amp;&amp;amp; self.cmd.type === &#39;breakpointCommand&#39; ?
	self.runtime.findNext(self.cmd, self.ctx) :
	self.runtime.unifiedEval(self.cmd, self.ctx);
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;We perform a useless check that I forgot to take out (&lt;code&gt;self.cmd &amp;amp;&amp;amp;&lt;/code&gt;). Then, we special-case the &lt;code&gt;breakpoint&lt;/code&gt; command itself and don’t execute it (nested debug sessions don’t end well…), instead finding the subsequent command ourselves with the &lt;code&gt;runtime.findNext()&lt;/code&gt; in hyperscript core. Otherwise, we can execute the current command.&lt;/p&gt;
&lt;p&gt;Once we have our command result, we can step onto it:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L61&quot; title=&quot;hdb.js ln. 64&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;if (result.type === &amp;quot;implicitReturn&amp;quot;) return self.stepOut();
if (result &amp;amp;&amp;amp; result.then instanceof Function) {
	return result.then(function (next) {
		self.cmd = next;
		self.bus.dispatchEvent(new Event(&amp;quot;step&amp;quot;));
		self.logCommand();
	})
} else if (result.halt_flag) {
	this.bus.dispatchEvent(new Event(&amp;quot;continue&amp;quot;));
} else {
	self.cmd = result;
	self.bus.dispatchEvent(new Event(&amp;quot;step&amp;quot;));
	this.logCommand();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;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 &lt;code&gt;cmd&lt;/code&gt; to it, notify the event bus and log it with some fancy styles. If the result was synchronous and is a &lt;a href=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/core.js#L1221&quot;&gt;HALT&lt;/a&gt;; we stop debugging (as I write this, I’m realizing I should’ve called &lt;a href=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L54&quot;&gt;&lt;code&gt;continueExec()&lt;/code&gt;&lt;/a&gt; here). Finally, we commit the kind of code duplication hyperscript is meant to help you avoid, to handle a synchronous result.&lt;/p&gt;
&lt;p&gt;To step out, we first get our hands on the context from which we were called:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L80&quot; title=&quot;hdb.js ln. 80&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;HDB.prototype.stepOut = function() {
	var self = this;
	if (!self.ctx.meta.caller) return self.continueExec();
	var callingCmd = self.ctx.meta.callingCommand;
	var oldMe = self.ctx.me;
	self.ctx = self.ctx.meta.caller;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;Turns out _hyperscript function calls already keep hold of the caller context (&lt;code&gt;callingCommand&lt;/code&gt; was added by me though). After we change context, we do something a little odd:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L92&quot; title=&quot;hdb.js ln. 92&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;self.cmd = self.runtime.findNext(callingCmd, self.ctx);
self.cmd = self.runtime.findNext(self.cmd, self.ctx);
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;Why do we call &lt;code&gt;findNext&lt;/code&gt; twice? Consider the following hyperscript code:&lt;/p&gt;
&lt;x-fig&gt;
&lt;pre&gt;&lt;code class=&quot;language-hyperscript&quot;&gt;transition &#39;color&#39; to darkgray
set name to getName()
log the name
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;We can’t execute the command to set &lt;code&gt;name&lt;/code&gt; until we have the name, so when &lt;code&gt;getName()&lt;/code&gt; is called, the current command is still set to the &lt;code&gt;transition&lt;/code&gt;. We call &lt;code&gt;findNext&lt;/code&gt; once to find the &lt;code&gt;set&lt;/code&gt;, and again to find the &lt;code&gt;log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we’re done stepping out:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L95&quot; title=&quot;hdb.js ln. 95&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;self.bus.dispatchEvent(new Event(&#39;step&#39;))
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;h3&gt;HDB UI&lt;/h3&gt;
&lt;p&gt;What did I use to make the UI for the hyperscript debugger? Hyperscript, of course!&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L107&quot; title=&quot;hdb.js ln. 107&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;quot;hdb&amp;quot; _=&amp;quot;--
	on load or step from hdb.bus send update to me
	on continue from hdb.bus remove #hyperscript-hdb-ui-wrapper-&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;There are a lot of elements listening to &lt;code&gt;load or step from hdb.bus&lt;/code&gt;, so I consolidated them under &lt;code&gt;update from .hdb&lt;/code&gt;. &lt;code&gt;#hyperscript-hdb-ui-wrapper-&lt;/code&gt; 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.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;We define some functions.&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L112&quot; title=&quot;hdb.js ln. 112&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-hyperscript&quot;&gt;def highlightDebugCode
	set start to hdb.cmd.startToken.start
	set end_ to hdb.cmd.endToken.end
	set src to hdb.cmd.programSource
	set beforeCmd to escapeHTML(src.substring(0, start))
	set cmd to escapeHTML(src.substring(start, end_))
	set afterCmd to escapeHTML(src.substring(end_))
	return beforeCmd+&amp;quot;&amp;lt;u class=&#39;current&#39;&amp;gt;&amp;quot;+cmd+&amp;quot;&amp;lt;/u&amp;gt;&amp;quot;+afterCmd
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;Now, I wasn’t aware that we had &lt;a href=&quot;https://hyperscript.org/expressions/string/&quot;&gt;template literals&lt;/a&gt; in hyperscript at this point, so that’s for the next release. The &lt;code&gt;escapeHTML&lt;/code&gt; helper might disappoint some:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L122&quot; title=&quot;hdb.js ln. 122&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-hyperscript&quot;&gt;def escapeHTML(unsafe)
	js(unsafe) return unsafe
		.replace(/&amp;amp;/g, &amp;quot;&amp;amp;amp;&amp;quot;)
		.replace(/&amp;lt;/g, &amp;quot;&amp;amp;lt;&amp;quot;)
		.replace(/&amp;gt;/g, &amp;quot;&amp;amp;gt;&amp;quot;)
		.replace(/\\x22/g, &amp;quot;&amp;amp;quot;&amp;quot;)
		.replace(/\\x27/g, &amp;quot;&amp;amp;#039;&amp;quot;) end
	return it
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;Unfortunately, hyperscript’s regex syntax isn’t decided yet.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Having defined our functions we have a simple toolbar and then the &lt;strong&gt;eval panel&lt;/strong&gt;:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L158&quot; title=&quot;hdb.js ln. 158&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;form class=&amp;quot;eval-form&amp;quot;  _=&amp;quot;on submit
	call event.preventDefault()
	get the first &amp;lt;input/&amp;gt; in me
	call _hyperscript(its.value, hdb.ctx)
	call prettyPrint(it)
	put it into the &amp;lt;output/&amp;gt; in me&amp;quot;&amp;gt;

		&amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;eval-expr&amp;quot; placeholder=&amp;quot;e.g. target.innerText&amp;quot;&amp;gt;
		&amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Go&amp;lt;/button&amp;gt;
		&amp;lt;output id=&amp;quot;eval-output&amp;quot;&amp;gt;&amp;lt;em&amp;gt;The value will show up here&amp;lt;/em&amp;gt;&amp;lt;/output&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;Why do I use weird selectors like &lt;code&gt;&amp;lt;input/&amp;gt; in me&lt;/code&gt; when these elements have good IDs? Because &lt;code&gt;#eval-expr&lt;/code&gt; in hyperscript uses &lt;code&gt;document.querySelector&lt;/code&gt;, which doesn’t reach Shadow DOM.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;A panel to show the code being debugged:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L170&quot; title=&quot;hdb.js ln. 170&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;h3 _=&amp;quot;on update from hdbUI
		put &#39;Debugging &amp;lt;code&amp;gt;&#39;+hdb.cmd.parent.displayName+&#39;&amp;lt;/code&amp;gt;&#39; into me&amp;quot;&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;code-container&amp;quot;&amp;gt;
	&amp;lt;pre class=&amp;quot;code&amp;quot; _=&amp;quot;on update from hdbUI
							if hdb.cmd.programSource
								put highlightDebugCode() into my.innerHTML
								scrollIntoView({ block: &#39;nearest&#39; }) the
								first .current in me&amp;quot;&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;hr&gt;
&lt;p&gt;Finally, a context panel that shows the local variables.&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L186&quot; title=&quot;hdb.js ln. 106&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;dl class=&amp;quot;context&amp;quot; _=&amp;quot;--
	on update from hdbUI
		set my.innerHTML to &#39;&#39;
		repeat for var in Object.keys(hdb.ctx) if var != &#39;meta&#39;
			get &#39;&amp;lt;dt&amp;gt;&#39;+var+&#39;&amp;lt;dd&amp;gt;&#39;+prettyPrint(hdb.ctx[var])
			put it at end of me
		end
	on click
		get closest &amp;lt;dt/&amp;gt; to target
		log hdb.ctx[its.innerText]&amp;quot;&amp;gt;&amp;lt;/dl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;p&gt;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).&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;x-fig src=&quot;https://github.com/bigskysoftware/_hyperscript/blob/7740c7eccfe3fe4f09443ec0adb961c72eb27a7b/src/lib/hdb.js#L350&quot; title=&quot;hdb.js ln. 350&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;HDB.prototype.ui = function () {
	var node = document.createElement(&#39;div&#39;);
	var shadow = node.attachShadow({ mode: &#39;open&#39; });
	node.style = &#39;all: initial&#39;;
	node.id = &#39;hyperscript-hdb-ui-wrapper-&#39;;
	shadow.innerHTML = ui;
	document.body.appendChild(node);
	window.hdbUI = shadow.querySelector(&#39;.hdb&#39;);
	_hyperscript.processNode(hdbUI);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/x-fig&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;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!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title></title>
    <link href="https://dz4k.com/2021/hyperscript-machine/"/>
    <updated>2021-02-26T00:03:00Z</updated>
    <id>https://dz4k.com/2021/hyperscript-machine/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot; data-img-color-scheme=&quot;light&quot;&gt;&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Repost: &quot;hyperscript 0.0.4 beta is going to be out soon...&quot;</title>
    <link href="https://dz4k.com/2021/hyperscript-wait-for/"/>
    <updated>2021-02-13T17:45:11Z</updated>
    <id>https://dz4k.com/2021/hyperscript-wait-for/</id>
    <content type="html">&lt;blockquote style=&quot;white-space: pre-wrap&quot;&gt;
hyperscript 0.0.4 beta is going to be out soon
&lt;p&gt;a sneak peek:&lt;/p&gt;
&lt;p&gt;the wait command can now pause evaluation until an event is received&lt;/p&gt;
&lt;p&gt;here a class is added, hyperscript waits for the transition to end, then the
element is removed&lt;/p&gt;
&lt;p&gt;feature suggested by &lt;a href=&quot;https://dz4k.com/&quot;&gt;@DenizAksimsek&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;
&amp;lt;div _=&quot;on click add .fade then wait for transitionend then remove&quot;&gt;
  This is a notice. Click to dismiss.
  &amp;lt;/div&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;</content>
  </entry>
  <entry>
    <title>HTMX Doodles</title>
    <link href="https://dz4k.com/2021/htmx-doodles/"/>
    <updated>2021-01-11T00:47:00Z</updated>
    <id>https://dz4k.com/2021/htmx-doodles/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/htmx-doodles/hypertext-man.jpeg&quot; alt=&quot;Hypertext Man!&quot; data-img-color-scheme=&quot;light&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/htmx-doodles/consult-your-doctor.jpeg&quot; alt=&quot;On HTMX (see transcript)&quot; data-img-color-scheme=&quot;light&quot;&gt;&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Transcript&lt;/summary&gt;
&lt;h3&gt;Server-Side App&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Full-page reloads&lt;/li&gt;
&lt;li&gt;HTML only has GET and POST methods&lt;/li&gt;
&lt;li&gt;…and only link clicks &amp;amp; form submissions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Single-Page App&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reinvent the accessible web&lt;/li&gt;
&lt;li&gt;Need big frameworks or complex code&lt;/li&gt;
&lt;li&gt;Probably need a server anyway for SSR&lt;/li&gt;
&lt;li&gt;The client is untrueted&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Progressively Enhanced App&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Tight coupling between server &amp;amp; client code&lt;/li&gt;
&lt;li&gt;Behavior is scattered&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;htmx&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Completing HTML with arbitrary events, replacing fragments instead of the whole page&lt;/li&gt;
&lt;li&gt;Your code runs on the server, which you trust&lt;/li&gt;
&lt;li&gt;htmx allows for &lt;strong&gt;locality of behavior&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;small&gt;Consult your doctor to find out if htmx is right for you. Side effects may include arguing about REST on &lt;abbr title=&quot;Hacker News&quot;&gt;HN&lt;/abbr&gt;.&lt;/small&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://dz4k.com/assets/htmx-doodles/2040.jpeg&quot; alt=&quot;2040 (See transcript)&quot; data-img-color-scheme=&quot;light&quot;&gt;&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Transcript&lt;/summary&gt;
&lt;p&gt;The year is 2040.&lt;/p&gt;
&lt;p&gt;htmx is a W3C recommendation and _hyperscript is an ECMA standard. War has long ceased, API churn is nought but a distant memory.&lt;/p&gt;
&lt;/details&gt;
&lt;/div&gt;</content>
  </entry>
  <entry>
    <title>Iota (from Golang) in JavaScript</title>
    <link href="https://dz4k.com/2021/js-iota/"/>
    <updated>2021-01-10T17:57:00Z</updated>
    <id>https://dz4k.com/2021/js-iota/</id>
    <content type="html">&lt;p&gt;(&lt;a href=&quot;#the-code&quot;&gt;Skip to code&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;Enums in Javascript&lt;/h2&gt;
&lt;p&gt;Currently, the ways we create enums include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;String literals (see &lt;code&gt;addEventListener&lt;/code&gt;), which can be typed with TypeScript, but look a bit ugly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type Direction = &#39;north&#39; | &#39;east&#39; | &#39;south&#39; | &#39;west&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TypeScript enums&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;enum Direction { north, east, south, west }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integer constants, IMO your best option if you’re not using TypeScript&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Directions = { north: 0, east: 1, south: 2, west: 3 }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Enums in Go&lt;/h2&gt;
&lt;p&gt;&lt;small data-note=1&gt;Initially I wasn’t going to bring Go into this at all. However, it turns out &lt;code&gt;enum&lt;/code&gt; is a reserved word in JS, so I went with &lt;code&gt;iota&lt;/code&gt; for the name of the function, and felt the need to explain it.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Go&lt;/em&gt; doesn’t have enums, but an unusual keyword &lt;code&gt;iota&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;type Direction int
const (
	North Direction = iota
	East
	South
	West
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s something subtle going on here. The iota relies on a few Go features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When multiple const declarations are grouped together, the right hand side is implicitly repeated&lt;/li&gt;
&lt;li&gt;Iota is incremented every time it is evaluated, and reset with each const&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My JavaScript shorthand is nowhere near as magical… but it does make use of proxies.&lt;/p&gt;
&lt;div id=the-code&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function iota(start = 0) {
	let count = start
	return new Proxy({}, {
		get(o, prop) {
			if (prop in o) return o[prop]
			else return o[prop] = count++
		}
	})
}

const { north, east, south, west } = iota()
console.log(north)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So, is this function any good?&lt;/p&gt;
&lt;p&gt;For one, it lacks some of Go’s &lt;code&gt;iota&lt;/code&gt; capabilities — you can’t create bitmasks with this the way you would in Go with &lt;code&gt;1 &amp;lt;&amp;lt; iota&lt;/code&gt;. We could augment it a bit by accepting a callback:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function iota(cb = (i =&amp;gt; i)) {
	let count = 0
	return new Proxy({}, {
		get(o, prop) {
			if (prop in o) return o[prop]
			else return o[prop] = cb(count++)
		}
	})
}

// flag bits
const { hasPermissionFlag, userModeFlag, useLegacyProtocolFlag } = iota(i =&amp;gt; 1 &amp;lt;&amp;lt; i)
const hasPermission = options &amp;amp; hasPermissionFlag
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don’t think bitmasks are very common at all in JavaScript code, though.&lt;/p&gt;
&lt;p&gt;A more significant setback is that you can’t get a list of all the enum values — nothing we can’t fix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function iota(start = 0) {
	let count = start
	let firstProp = true

	return new Proxy({}, {
		get(o, prop) {
			if (firstProp) {
	  			firstProp = false
	  			return { // Enum descriptor
					get values() { return o }
				}
			}
			if (prop in o) return o[prop]
			else return o[prop] = count++
		}
	})
}

const { Direction, north, east, south, west } = iota()
console.log(Direction)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;I might have a metaprogramming addiction.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Eleventy + Markdown + CoffeeScript = ❤</title>
    <link href="https://dz4k.com/2020/coffeescript-frontmatter/"/>
    <updated>2020-10-16T17:12:00Z</updated>
    <id>https://dz4k.com/2020/coffeescript-frontmatter/</id>
    <content type="html">&lt;p&gt;I’ve started using CoffeeScript instead of YAML for frontmatter, and it works like a dream. &lt;a href=&quot;https://twitter.com/zachleat/status/1340057504567488513&quot;&gt;Endorsed by Zach Leatherman&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Markdown with Frontmatter&lt;/h2&gt;
&lt;p&gt;As far as I know, Jekyll is the origin of using a YAML block at the top of a
Markdown file for metadata. It’s not hard to imagine why this became popular —
associating prose with data is a common use case, and though they have their
issues, both Markdown and YAML look pretty.* &lt;x-sidenote inline=&quot;&quot;&gt;“It looks pretty” is
often what people really mean when they call something “human-readable”.&lt;/x-sidenote&gt;&lt;/p&gt;
&lt;h2&gt;Eleventy Frontmatter&lt;/h2&gt;
&lt;p&gt;Eleventy uses frontmatter blocks (delimited by &lt;code&gt;---&lt;/code&gt;) 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, &lt;code&gt;title&lt;/code&gt; is not a reserved name:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: &#39;A Blog Post&#39;
---

# {title}

Lorem ipsum dolor sit amet...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could have used &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;heading&lt;/code&gt; or &lt;code&gt;kdsad983erj33&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Eleventy’s &lt;a href=&quot;https://www.11ty.dev/docs/data-computed/&quot;&gt;“Computed Data”&lt;/a&gt; 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…):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;---js
{
  title: &amp;quot;My page title&amp;quot;,
  eleventyComputed: {
    currentDate: () =&amp;gt; new Date().toLocaleString()
  }
}
---
&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
--- … --&amp;gt;
&amp;lt;body&amp;gt;
   &amp;lt;p&amp;gt;This website was last generated on {{ currentDate }}&amp;lt;/p&amp;gt;
  &amp;lt;!-- … --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;If only there was a language that had the expressive power of JS, and the visually clean data notation of YAML…&lt;/p&gt;
&lt;h2&gt;CoffeeScript All The Time!&lt;/h2&gt;
&lt;p&gt;CoffeeScript is actually mentioned in the GitHub repo for &lt;code&gt;gray-matter&lt;/code&gt;, the library Eleventy uses to parse frontmatter. Compared to YAML, it’s not much more verbose:&lt;/p&gt;
&lt;div id=compare class=&quot;-full-bleed-scroll&quot; style=&quot;
	padding-inline: var(--dim-body-padding);
	display:flex;
	gap: 3ch;&quot;&gt;
&lt;style&gt;
#compare pre {overflow: unset; margin-inline: 0}
&lt;/style&gt;
&lt;pre&gt;&lt;code&gt;---
title: Street sign in İstanbul
date: 2020-10-10 19:27:39
tags: [place, design]
photo: /photos/img02.jpeg
türkçe: /2020/istanbul-street-sign/
---
YAML
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;---
title: &#39;Street sign in İstanbul&#39;
date: &#39;2020-10-10T19:27:39+03:00&#39;
tags: [&#39;place&#39;, &#39;design&#39;]
photo: &#39;/photos/img02.jpeg&#39;
türkçe: &#39;/2020/istanbul-street-sign/&#39;
---
CoffeeScript
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The CoffeeScript code above is actually fully YAML-compatible! This keeps my templates portable. And with computed data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-liquid&quot;&gt;---
layout: &#39;layout&#39;
eleventyComputed:
	syndicationLinks: (data) -&amp;gt;
		DEV: data.devToSyndication[data.page.url]
---

&amp;lt;article class=&amp;quot;h-entry&amp;quot;&amp;gt;
	&amp;lt;!-- ... --&amp;gt;
	{%for silo, link in syndicationLinks%}
		&amp;lt;a class=&amp;quot;u-syndication&amp;quot; href=&amp;quot;{{link}}&amp;quot;&amp;gt;{{silo}}&amp;lt;/a&amp;gt;
		{%if not loop.last%} | {%endif%}
	{%endfor%}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think that computed frontmatter gives a nice balance of logic-less* and
logic-full templates. &lt;x-sidenote inline=&quot;&quot;&gt;Logicless templates: For people who will never
need to make an HTML monthly calendar!&lt;/x-sidenote&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>İstanbul&#39;da bir sokak tabelası</title>
    <link href="https://dz4k.com/2020/istanbul-sokak-tabelasi/"/>
    <updated>2020-10-10T19:27:39Z</updated>
    <id>https://dz4k.com/2020/istanbul-sokak-tabelasi/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://dz4k.com/2020/ordu-street-sign/&quot;&gt;Ordu’daki tabelanın olduğu yazıda&lt;/a&gt; İstanbul’a yolum düşerse oradakilerin resmini paylaşırım diye yazmıştım. Başka bir şey dileseymişim.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pandemi yüzünden İstanbul’da gezemedim ama bu tabelaları gerçekte görme fırsatım oldu. Özel tasarım &lt;em&gt;Kent&lt;/em&gt; yazıtipi ve ilçe renk kodlarını biliyordum ama tabelaların kabartmalı olduğu fotoğraflarda belli olmuyor.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Street sign in İstanbul</title>
    <link href="https://dz4k.com/2020/istanbul-street-sign/"/>
    <updated>2020-10-10T19:27:39Z</updated>
    <id>https://dz4k.com/2020/istanbul-street-sign/</id>
    <content type="html">&lt;p&gt;I mentioned these signs in &lt;a href=&quot;https://dz4k.com/2020/ordu-street-sign/&quot;&gt;my post about the ones in Ordu&lt;/a&gt; and said I’d post a picture if I ever went to İstanbul. I think I just wasted my wish.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;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 &lt;em lang=&quot;tr&quot;&gt;Kent&lt;/em&gt; 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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Add Syndication Links On Your Personal Site Using Dev.to API</title>
    <link href="https://dz4k.com/2020/devto-syndication-links/"/>
    <updated>2020-09-17T21:10:00Z</updated>
    <id>https://dz4k.com/2020/devto-syndication-links/</id>
    <content type="html">&lt;p&gt;I recently started using the &lt;a href=&quot;https://dev.to/settings/publishing-from-rss&quot;&gt;RSS publishing&lt;/a&gt; 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 &lt;a href=&quot;http://dev.to/&quot;&gt;dev.to&lt;/a&gt; copies under my posts.&lt;/p&gt;
&lt;p&gt;(&lt;a href=&quot;#the-code&quot;&gt;Skip to code&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;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 &lt;em&gt;not&lt;/em&gt; automate it.&lt;/p&gt;
&lt;h2&gt;The API and the Data File&lt;/h2&gt;
&lt;p&gt;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 &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;canonical_url&lt;/code&gt; properties for each article, and create a mapping from the latter to the former.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const fetch = require(&#39;node-fetch&#39;)

module.exports = fetch(&#39;https://dev.to/api/articles?username=dza&#39;)
    .then(res =&amp;gt; res.json())
    .then(articles =&amp;gt; articles.map(
        ({canonical_url, url}) =&amp;gt; [canonical_url, url]))
    .then(Object.fromEntries)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are copy-and-pasting this code, make sure to replace &lt;code&gt;dza&lt;/code&gt; with your own &lt;a href=&quot;http://dev.to/&quot;&gt;dev.to&lt;/a&gt; username.&lt;/p&gt;
&lt;p&gt;This will give us an object like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
	&amp;quot;https://www.denizaksimsek.com/2020/css-additional-box-shadow/&amp;quot;:
	    &amp;quot;https://dev.to/dza/css-adding-additional-box-shadows-2lob&amp;quot;,
	...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s try using it in our templates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-liquid&quot;&gt;{%if devToSyndication[page.url]%}
&amp;lt;section class=&amp;quot;syndication-links&amp;quot;&amp;gt;
This article is syndicated to &amp;lt;a class=&amp;quot;u-syndication&amp;quot;
    href=&amp;quot;{{devToSyndication[page.url]}}&amp;quot;&amp;gt;DEV&amp;lt;/a&amp;gt;, where you can comment on it.
&amp;lt;/section&amp;gt;
{%endif%}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Small problem: the &lt;code&gt;page.url&lt;/code&gt; property Eleventy provides us is a relative URL, whereas the URLs we got from DEV are absolute.&lt;/p&gt;
&lt;p&gt;Sounds like a job for the &lt;code&gt;URL&lt;/code&gt; class!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function makeRelativeUrl(url) {
	const urlObj = new URL(url)
	// you might want to append url.search and url.hash too
    // but it&#39;s unlikely, and a small amount of tech debt is
    // good for the soul
	return urlObj.pathname
}

...
({canonical_url, url}) =&amp;gt; [makeRelativeUrl(canonical_url), url])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should see links on any post that is syndicated to DEV.&lt;/p&gt;
&lt;h2 id=&quot;the-code&quot;&gt;Appendix: The Final Data File&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const fetch = require(&#39;node-fetch&#39;)

function makeRelativeUrl(url) {
	const urlObj = new URL(url)
	return urlObj.pathname
}

module.exports = fetch(&#39;https://dev.to/api/articles?username=dza&#39;)
    .then(res =&amp;gt; res.json())
    .then(articles =&amp;gt; articles.map(
        ({canonical_url, url}) =&amp;gt; [makeRelativeUrl(canonical_url), url]))
    .then(Object.fromEntries)
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Çobanlı Pier, Samsun, Turkey</title>
    <link href="https://dz4k.com/2020/cobanli-pier/"/>
    <updated>2020-09-13T19:15:08Z</updated>
    <id>https://dz4k.com/2020/cobanli-pier/</id>
    <content type="html">&lt;p&gt;Nothing gets me quite like city lights reflecting off water. Though it’s usually rainwater on asphalt.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot;&gt;&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>A street sign in Ordu</title>
    <link href="https://dz4k.com/2020/ordu-street-sign/"/>
    <updated>2020-09-03T16:42:00Z</updated>
    <id>https://dz4k.com/2020/ordu-street-sign/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;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. &lt;ins&gt;Update 06/01/2021: &lt;a href=&quot;https://denizaksimsek.com/2020/istanbul-street-sign/&quot;&gt;I did!&lt;/a&gt;&lt;/ins&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Ardeşen, Rize, Turkey</title>
    <link href="https://dz4k.com/2020/ardesen-rize-tr/"/>
    <updated>2020-09-02T22:40:22Z</updated>
    <id>https://dz4k.com/2020/ardesen-rize-tr/</id>
    <content type="html">&lt;p&gt;A few pics from there.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot;&gt;&lt;img src=&quot;https://dz4k.com/%25=%20it.photo.src%20%25&quot; alt=&quot;&amp;lt;%= it.photo.alt %&amp;gt;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/assets/photos/IMG20200902180736.jpg&quot;&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG20200902180736.jpg&quot; alt=&quot;The dark clouds are painted yellow by the sun, the sea is set ablaze&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/assets/photos/IMG20200902184220.jpg&quot;&gt;&lt;img src=&quot;https://dz4k.com/assets/photos/IMG20200902184220.jpg&quot; alt=&quot;The beach is covered in rocks as far as the eyes can see, and even further&quot;&gt;&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>CSS — Adding Additional Box Shadows</title>
    <link href="https://dz4k.com/2020/css-additional-box-shadow/"/>
    <updated>2020-09-02T12:54:44Z</updated>
    <id>https://dz4k.com/2020/css-additional-box-shadow/</id>
    <content type="html">&lt;p&gt;When you want to add a box shadow to an element, but don’t want to override any it might already have. (&lt;a href=&quot;#the-code&quot;&gt;Jump to code&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;Less-than-amazing solutions&lt;/h2&gt;
&lt;p&gt;We could copy the &lt;code&gt;box-shadow&lt;/code&gt; 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 &lt;code&gt;.highlight-glow&lt;/code&gt; class).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dz4k.com/stackoverflow-says&quot;&gt;StackOverflow says to put the shadow on an absolutely-positioned pseudo-element&lt;/a&gt;. This leaves us with a similar problem to the one we started with — the element in question might already have styles on the &lt;code&gt;::before&lt;/code&gt; or &lt;code&gt;::after&lt;/code&gt; that conflicts with ours.&lt;/p&gt;
&lt;h2 id=&quot;the-code&quot;&gt;My solution&lt;/h2&gt;
&lt;p&gt;Use CSS variables to apply box shadows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.shadowy-figure {
    --box-shadow: .5em .5em 1em #333;
    box-shadow: var(--box-shadow);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Later on, you can tack things on at the end of this variable, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.highlight {
    box-shadow: var(--box-shadow), 0 0 .1em #fff inset;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One drawback is that we cannot use this technique in several classes and apply them to one element. We can solve this problem when &lt;a href=&quot;https://dz4k.com/parent-var&quot;&gt;&lt;code&gt;parent-var()&lt;/code&gt;&lt;/a&gt; is a thing in CSS — if it already is by the time you read this post, do let me know!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Tip: Instantly Edit Your Eleventy Site on Github</title>
    <link href="https://dz4k.com/2020/instant-edit-11ty/"/>
    <updated>2020-08-17T20:12:00Z</updated>
    <id>https://dz4k.com/2020/instant-edit-11ty/</id>
    <content type="html">&lt;p&gt;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? (&lt;a href=&quot;#the-code&quot;&gt;Skip to code&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This tip assumes you use GitHub. It could likely be adapted easily for other Git providers.&lt;/p&gt;
&lt;p&gt;Add the following to your base layout (note the &lt;code&gt;{{}}&lt;/code&gt; and replace &lt;code&gt;&amp;lt;username&amp;gt;/&amp;lt;repo&amp;gt;&lt;/code&gt; with the repo for the site):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script&amp;gt;
  addEventListener(&#39;keyup&#39;, e =&amp;gt; {
    if (e.shiftKey === true) {
      switch (e.keyCode) {
      case 69: // E
        window.location = &#39;https://github.com/&amp;lt;username&amp;gt;/&amp;lt;repo&amp;gt;/edit/main/{{page.inputPath}}&#39;
        break
      }
    }
  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you press &lt;kbd&gt;Shift+E&lt;/kbd&gt;, the GitHub editor will open to the current page! The switch statement is there because I used to have a few more hotkeys.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Eleventy — Dynamically Inlining Google Fonts</title>
    <link href="https://dz4k.com/2020/11ty-inline-google-fonts/"/>
    <updated>2020-07-24T14:33:00Z</updated>
    <id>https://dz4k.com/2020/11ty-inline-google-fonts/</id>
    <content type="html">&lt;x-warning&gt;
&lt;p&gt;&lt;strong&gt;Update &lt;time&gt;2021-02-07&lt;/time&gt;:&lt;/strong&gt; 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 &lt;code class=&quot;language-html&quot;&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/x-warning&gt;
&lt;p&gt;All in the name of Lighthouse scores.&lt;/p&gt;
&lt;p&gt;When you add Google Fonts to your website by copying the link from the &lt;a href=&quot;http://fonts.google.com/&quot;&gt;fonts.google.com&lt;/a&gt; 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 &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 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.&lt;/p&gt;
&lt;h2&gt;A JavaScript data file&lt;/h2&gt;
&lt;p&gt;We simply fetch the CSS at build time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// _data/googleFontsStylesheet.js

const fetch = require(&#39;node-fetch&#39;)

const url = &#39;the URL in the Google Fonts stylesheet link href&#39;

module.exports = fetch(url).then(res =&amp;gt; res.text())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And include it in our base layout.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-liquid&quot;&gt;&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;{{googleFontsStylesheet|safe}}&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enjoy your improved Lighthouse scores!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Eleventy — Using A Layout As A Partial (The Most Useful Things...)</title>
    <link href="https://dz4k.com/2020/11ty-layout-as-partial/"/>
    <updated>2020-06-08T14:50:00Z</updated>
    <id>https://dz4k.com/2020/11ty-layout-as-partial/</id>
    <content type="html">&lt;x-warning&gt;
&lt;p&gt;&lt;strong&gt;Update 2021-02-08:&lt;/strong&gt; This article mentions things about my website that are no longer true. The technique explained, however, still works fine.&lt;/p&gt;
&lt;/x-warning&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;In Eleventy, frontmatter data for the current page is merged into the &lt;a href=&quot;https://www.11ty.dev/docs/data-cascade/&quot;&gt;data cascade&lt;/a&gt; and made available on the template global scope, whereas collection items have a &lt;code&gt;data&lt;/code&gt; property. This made it hard to write templates that work with both the &lt;code&gt;page&lt;/code&gt; object and collection items.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;x-fig&gt;
&lt;blockquote&gt;
&lt;p&gt;[…] the most useful things are usually cloaked in an air of nonchalance, even in documentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;
	&amp;mdash; &lt;cite&gt;YouTube user
	&lt;a href=&quot;https://www.youtube.com/channel/UCjknfwYaYZvv94AjL10NO0Q&quot;&gt;Bantu Tu&lt;/a&gt;,
	commenting on
	&lt;a href=&quot;https://www.youtube.com/watch?v=a6Q8Na575qc&quot;&gt;Missing Semester: Lecture 3: Editors (vim) (2020)&lt;/a&gt;
	&lt;/cite&gt;
&lt;/figcaption&gt;
&lt;/x-fig&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;x-fig&gt;
&lt;blockquote&gt;
&lt;h3&gt;Also &lt;code&gt;getCollectionItem&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;For completeness, a &lt;code&gt;getCollectionItem&lt;/code&gt; filter is also included that fetches the current page from a collection.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;&amp;mdash; &lt;cite&gt;&lt;a href=&quot;https://www.11ty.dev/docs/filters/collection-items/#also-getcollectionitem&quot;&gt;Eleventy docs&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;
&lt;/x-fig&gt;
&lt;p&gt;With this filter, all my problems were solved. I prepended the following to my &lt;code&gt;note.njk&lt;/code&gt; template (similarly for &lt;code&gt;post.njk&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-liquid&quot;&gt;{%set item = note | d(collections.note | getCollectionItem(page))%}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using &lt;code&gt;note.njk&lt;/code&gt; as a partial, the caller assigns the variable &lt;code&gt;note&lt;/code&gt;. When using it as a layout, no such variable will exist and the default (&lt;code&gt;d()&lt;/code&gt;) will be used… The beauty of this configuration is that in both situations, &lt;code&gt;item&lt;/code&gt; will be a collection item with a &lt;code&gt;data&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;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 &lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#template-inheritance&quot;&gt;template inheritance&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-liquid&quot;&gt;{%if not note%}
{%extends &amp;quot;base.njk&amp;quot;%}
{%endif%}
...
{%block content%}
{%set item = note | d(collections.note | getCollectionItem(page))%}
...
{%endblock%}
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Stationery I like</title>
    <link href="https://dz4k.com/2020/stationery-i-like/"/>
    <updated>2020-05-22T22:10:00Z</updated>
    <id>https://dz4k.com/2020/stationery-i-like/</id>
    <content type="html">&lt;p&gt;Reviews for things I used, and a wishlist I try to keep small.&lt;/p&gt;
&lt;h2&gt;Things I have used&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.uniball.com.tr/urunler/roller/uniball-air/uba-188-m&quot;&gt;Uni-ball Air Micro — Rollerball Pen&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.studioiggy.com/collections/i%CC%87nce-defterler&quot;&gt;Iggy Stationery Thin Notebooks&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://www.smartofis.com.tr/kagit-grubu/Suni-deri-not-defteri&quot;&gt;Globox Leather Design Notebooks&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;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 &lt;span lang=tr&gt;A-101&lt;/span&gt; (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…&lt;/p&gt;
 &lt;!--

-   [Uni Jetstream SX-217](https://www.uniball.com.tr/urunler/jetstream/jetstream/sx-217){#uni-jetstream}

    TODO: I should write a blog post about this
 --&gt;
&lt;h2&gt;Things I want (curse you, JetPens)&lt;/h2&gt;
&lt;h3&gt;Pen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetpens.com/Pilot-Multi-Ball-Rollerball-Pen-Fine-Black/pd/2760&quot;&gt;Pilot Multi Ball&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetpens.com/Kuretake-Fudegokochi-Brush-Pen-Extra-Fine-Black/pd/2659&quot;&gt;Kuretake Fudegokochi Brush Pen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wellappointeddesk.com/2019/11/pen-review-pentel-artist-brush-sign-pen/&quot;&gt;Pentel Artist Brush Sign Pen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Notebooks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sorta.la/&quot;&gt;Sorta Notebinder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Pen Cases&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetpens.com/Sonic-Sma-Sta-Standing-Pen-Cases/ct/4885&quot;&gt;Sonic Sma Sta Standing Pen Case&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wellappointeddesk.com/2019/03/review-lihit-lab-smart-fit-carrying-pouch-a6/&quot;&gt;Lihit Lab Smart Fit Carrying Pouch — A6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetpens.com/Nakabayashi-Lifestyle-Tool-Wall-Box-S-Kraft/pd/22304&quot;&gt;Nakabayashi Lifestyle Tool Wall Box&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Miscellaneous&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetpens.com/Raymay-Pencut-Mini-Scissors/ct/1623&quot;&gt;Raymay Pencut Mini Scissors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
</feed>
